JavaScript is required. Please enable it to continue.
Your browser lacks required capabilities. Please upgrade it or switch to another to continue.
Loading…
{ "name": "Two Weeks", "ifid": "2e571b5f-6fca-4b1a-85fd-2bb3c587900e", "format": "SugarCube", "format-version": "2.36.1", "start": "Start" }
<<set $player = { "name": "Lily", "portrait": "player.jpg", "current_location": "", "core_traits": {"boldness": 0, "energy": 100}, "flags": {"game_started": false, "arrival_complete": false, "welcome_dinner_complete": false, "old_photos_complete": false, "sleepless_night_complete": false, "madison_calls_complete": false, "rainy_day_complete": false, "the_couch_complete": false, "confession_complete": false, "almost_kiss_complete": false, "real_talk_complete": false, "first_kiss_done": false, "what_are_we_doing_done": false, "going_further_complete": false, "first_night_complete": false, "morning_after_complete": false, "cant_stay_away_complete": false, "madison_arrived": false, "stolen_moment_complete": false, "night_before_complete": false, "wedding_morning_done": false, "ending_seen": false, "lingering_touch_unlock": false, "flirt_unlock": false, "kiss_unlock": false, "grind_unlock": false, "cant_keep_hands_off_complete": false, "intimacy_unlock": false, "manual_unlock": false, "oral_unlock": false, "sex_unlock": false, "ethan_comfortable": false, "ethan_interested": false, "ethan_vulnerable": false, "ethan_intimate": false} }>><<set $npcs = {"ce2edb41-3ab6-4894-a8d5-1ae31ce8bd05": {"name": "Ethan", "portrait": "ethan.jpg", "core_traits": {"love": 0, "trust": 0, "corruption": 0}, "flags": {}, "schedule": []}, "add8ff44-d4d4-41f2-b44b-274b91bad4cf": {"name": "Madison", "portrait": "madison.jpg", "core_traits": {}, "flags": {}, "schedule": []}}>><<set $flags = { "game_started": true, "debug_mode": false }>><<set $flags_meta = {}>><<set $game_state = { "current_canvas": "", "visited_locations": [], "visited_nodes": [], "trigger_history": {}, "activity_trigger_history": {}, "visited_choices": {}, "time_state": { "current_hour": 8, "current_minute": 0, "current_day": "Monday", "current_week": 1, "day": 1 } }>><<nobr>> <div class="game-intro"> <h1>Two Weeks</h1> <p class="game-description">You return home after two years for your step-brother Ethan's wedding. The feelings you buried — the ones that made you leave in the first place — never went away. And neither did his. You have exactly 14 days before he says "I do" to someone else. Every moment is a choice between what's right and what you truly want. A single-NPC forbidden romance driven by a ticking clock, dual taboo (step-sibling + engaged), and the question: is love worth the cost of destroying everything else?</p> <div class="developer-intro"> <p class="developer-about">We're a small indie studio crafting intimate, story-driven experiences. Every game is made with care, and your support helps us keep creating. If you enjoy our work, consider supporting us!</p> <p class="support-link">👉 <a href="https://www.patreon.com/cw/nutgames844" target="_blank" rel="noopener">Support us on Patreon</a></p> </div> <div class="age-gate"> <p class="age-warning">⚠️ This game contains adult content intended for players 18 years of age or older.</p> <div class="age-buttons"> [[✓ I am 18 or older - Enter Game->StartingCanvas_scene_arrival_Node_1]] [[✗ I am NOT 18 or older->AgeBlocked]] </div> </div> <div class="developer-footer"> <p class="developer-credit">Developed by <strong>NutGames</strong></p> <p class="support-link">👉 <a href="https://www.patreon.com/cw/nutgames844" target="_blank" rel="noopener">Support us on Patreon</a></p> </div> </div> <</nobr>>
<div class="blocked-page"> <h2>Access Denied</h2> <p>This content is not available for you.</p> <p>Please close this page.</p> </div>
<!-- Time Display Widgets --> <<widget "timeFormatted">> <<set _hour to $game_state.time_state.current_hour>> <<set _minute to $game_state.time_state.current_minute>> <<set _displayHour to (_hour is 0 ? 12 : (_hour > 12 ? _hour - 12 : _hour))>> <<set _ampm to (_hour < 12 ? "AM" : "PM")>> <<set _minuteStr to (_minute < 10 ? "0" + _minute : _minute)>> <<print _displayHour + ":" + _minuteStr + " " + _ampm>> <</widget>> <<widget "timeDisplay">> <div id="time-widget" class="time-display"> <div class="time-line"> <span id="time-display"><<timeFormatted>></span> | <span id="current-day"><<print $game_state.time_state.current_day>></span> </div> <div class="control-line"> <button class="time-btn" onclick="advanceTime(10)" title="Advance 10 minutes">></button> | <button class="time-btn" onclick="advanceTime(60)" title="Advance 1 hour">>></button> | <button class="time-btn" onclick="advanceTime(1440)" title="Advance 1 day">>>>>></button> </div> </div> <</widget>> <<widget "sidebarItems">> <<if setup.sidebar_items && setup.sidebar_items.length > 0>> <div id="sidebar-items-widget"> <<for _si to 0; _si lt setup.sidebar_items.length; _si++>> <<set _item to setup.sidebar_items[_si]>> <<if _item.type is "countdown">> <<set _daysLeft to _item.total_days - $game_state.time_state.day + 1>> <div class="sidebar-item countdown-item" id="sidebar-countdown-<<print _si>>"> <<if _daysLeft gt 1>> <<print _daysLeft + " " + _item.label>> <<elseif _daysLeft is 1>> <<print "Tomorrow is " + _item.label.replace("days until ", "")>> <<elseif _daysLeft is 0>> <<print "Today is " + _item.label.replace("days until ", "")>> <<else>> <<print _item.label.replace("days until ", "") + " has passed">> <</if>> </div> <<elseif _item.type is "hint">> <<set _hintText to setup.getSidebarHint()>> <<if _hintText>> <div class="sidebar-item hint-item" id="sidebar-hint-<<print _si>>"> <<print _hintText>> </div> <</if>> <<elseif _item.type is "trait_bar">> <<set _traitKey to _item.trait>> <<set _traitVal to ($player && $player.core_traits) ? ($player.core_traits[_traitKey] || 0) : 0>> <<set _traitMax to _item.max || 100>> <<set _traitLabel to _item.label || _traitKey>> <<set _traitPct to Math.max(0, Math.min(100, (_traitVal / _traitMax) * 100))>> <div class="sidebar-item trait-bar-item" id="sidebar-trait-bar-<<print _si>>"> <div class="trait-bar-label"><<print _traitLabel>>: <<print Math.floor(_traitVal)>> / <<print _traitMax>></div> <div class="trait-bar-bg"> <div class="trait-bar-fill" style="width: <<print _traitPct>>%"></div> </div> </div> <</if>> <</for>> </div> <</if>> <</widget>> <<widget "playerTraits">> <div id="traits-widget" class="traits-display"> <div class="traits-header">Traits</div> <<if $player and $player.core_traits and Object.keys($player.core_traits).length > 0>> <<set _keys to Object.keys($player.core_traits).sort()>> <ul class="traits-list"> <<for _i to 0; _i lt _keys.length; _i++>> <<set _k to _keys[_i]>> <li class="trait-item"> <span class="trait-name"><<print _k>></span> <span class="trait-value"><<print $player.core_traits[_k]>></span> </li> <</for>> </ul> <<else>> <div class="no-traits">No traits</div> <</if>> <div class="traits-hint">Updates after each choice.</div> </div> <</widget>> <<widget "playerFlags">> <div id="flags-widget" class="traits-display"> <div class="traits-header">Flags</div> <<if $player and $player.flags and Object.keys($player.flags).length > 0>> <<set _fkeys to Object.keys($player.flags).sort()>> <ul class="traits-list"> <<for _j to 0; _j lt _fkeys.length; _j++>> <<set _fk to _fkeys[_j]>> <li class="trait-item"> <span class="trait-name"><<print _fk>></span> <span class="trait-value"><<print $player.flags[_fk] ? '✔' : '✖'>></span> </li> <</for>> </ul> <<else>> <div class="no-traits">No flags</div> <</if>> <div class="traits-hint">Set by choices and story logic.</div> </div> <</widget>> <<widget "questsButton">> <!-- Debug/legacy quest view - shows raw mechanics --> <div id="quests-btn-widget"> <<if passage() isnot "QuestsPage">><<link "📋 Quests" "QuestsPage">><</link>><<else>>📋 Quests<</if>> </div> <</widget>> <<widget "missingMediaButton">> <<if $flags.debug_mode>> <div id="missing-media-btn-widget" style="margin-bottom:8px;"> <<if passage() isnot "MissingMediaPage">><<link "⚠️ Missing Media" "MissingMediaPage">><</link>><<else>>⚠️ Missing Media<</if>> </div> <</if>> <</widget>> <<widget "journalButton">> <!-- Guide - simplified what's next help --> <div id="journal-btn-widget"> <<if passage() isnot "QuestsPage">><<link "📖 Guide" "QuestsPage">><</link>><<else>>📖 Guide<</if>> </div> <</widget>> <<widget "statsButton">> <div id="stats-btn-widget"> <<if passage() isnot "StatsPage">><<link "📊 Stats" "StatsPage">><</link>><<else>>📊 Stats<</if>> </div> <</widget>> <<widget "scheduleButton">> <<set _npcsWithSchedules to setup.getNpcsWithSchedules()>> <<set _soloActivities to setup.getSoloActivitiesForToday()>> <<if _npcsWithSchedules.length > 0 || _soloActivities.length > 0>> <div id="schedule-btn-widget"> <<if passage() isnot "SchedulePage">><<link "📅 Schedules" "SchedulePage">><</link>><<else>>📅 Schedules<</if>> </div> <</if>> <</widget>> <<widget "patreonButton">> <div id="patreon-btn-widget"> <a href="https://www.patreon.com/cw/nutgames844" target="_blank" rel="noopener" class="patreon-link"> <svg class="patreon-icon" viewBox="0 0 24 24" width="16" height="16"> <path fill="currentColor" d="M15.386.524c-4.764 0-8.64 3.876-8.64 8.64 0 4.75 3.876 8.613 8.64 8.613 4.75 0 8.614-3.864 8.614-8.613C24 4.4 20.136.524 15.386.524M.003 23.537h4.22V.524H.003"/> </svg> Support Us </a> </div> <</widget>> <<widget "flagsButton">> <div id="flags-btn-widget"> <<if passage() isnot "FlagsPage">><<link "🚩 Flags" "FlagsPage">><</link>><<else>>🚩 Flags<</if>> </div> <</widget>>
<<missingMediaButton>> <<timeDisplay>> <<sidebarItems>> <<journalButton>> <<statsButton>> <<scheduleButton>> <<flagsButton>> <<playerTraits>> <<patreonButton>> <style> #time-widget { background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 4px; padding: 8px; margin-bottom: 10px; font-family: 'Courier New', monospace; font-size: 14px; line-height: 1.2; color: #212529; } .time-line { text-align: center; margin-bottom: 4px; font-weight: bold; color: #000000; } .control-line { text-align: center; font-size: 12px; } .time-btn { background: #e9ecef; border: 1px solid #ced4da; border-radius: 3px; color: #495057; cursor: pointer; padding: 3px 6px; font-size: 12px; font-family: 'Courier New', monospace; font-weight: bold; } .time-btn:hover { background: #dee2e6; color: #000000; border-color: #adb5bd; } #sidebar-items-widget { margin-bottom: 8px; } .sidebar-item { text-align: center; padding: 6px 8px; font-size: 13px; font-family: 'Courier New', monospace; line-height: 1.3; } .countdown-item { background: #fff3cd; border: 1px solid #ffc107; border-radius: 4px; color: #856404; font-weight: bold; } .hint-item { background: #d4edda; border: 1px solid #28a745; border-radius: 4px; color: #155724; font-style: italic; } #traits-widget { background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 4px; padding: 8px; margin-top: 8px; font-family: 'Courier New', monospace; font-size: 13px; color: #212529; } #flags-widget { background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 4px; padding: 8px; margin-top: 8px; font-family: 'Courier New', monospace; font-size: 13px; color: #212529; } .traits-header { font-weight: bold; margin-bottom: 6px; } .traits-list { list-style: none; padding-left: 0; margin: 0; } .trait-item { display: flex; justify-content: space-between; border-bottom: 1px dashed #e2e3e5; padding: 2px 0; } .trait-name { color: #495057; } .trait-value { color: #0d6efd; font-weight: bold; } .no-traits { font-style: italic; color: #6c757d; } .traits-hint { margin-top: 6px; font-size: 11px; color: #6c757d; } #time-display, #current-day { font-family: 'Courier New', monospace; color: #000000; } #quests-btn-widget { background: #e7f3ff; border: 1px solid #007bff; border-radius: 4px; padding: 6px 8px; margin-bottom: 8px; text-align: center; } #quests-btn-widget a { color: #007bff; text-decoration: none; font-weight: bold; } #quests-btn-widget a:hover { color: #0056b3; text-decoration: underline; } #journal-btn-widget { background: linear-gradient(180deg, #fdf6e3 0%, #f5e6d3 100%); border: 1px solid #c9b896; border-radius: 4px; padding: 6px 8px; margin-bottom: 8px; text-align: center; } #journal-btn-widget a { color: #6d5a40; text-decoration: none; font-weight: bold; font-family: 'Georgia', serif; } #journal-btn-widget a:hover { color: #4a3f30; text-decoration: underline; } #stats-btn-widget { background: #f0f7e6; border: 1px solid #28a745; border-radius: 4px; padding: 6px 8px; margin-bottom: 8px; text-align: center; } #stats-btn-widget a { color: #28a745; text-decoration: none; font-weight: bold; } #stats-btn-widget a:hover { color: #1e7e34; text-decoration: underline; } #schedule-btn-widget { background: #e8f4fc; border: 1px solid #3b82f6; border-radius: 4px; padding: 6px 8px; margin-bottom: 8px; text-align: center; } #schedule-btn-widget a { color: #3b82f6; text-decoration: none; font-weight: bold; } #schedule-btn-widget a:hover { color: #2563eb; text-decoration: underline; } /* Flags Button in Sidebar */ #flags-btn-widget { background: #fff3cd; border: 1px solid #ffc107; border-radius: 4px; padding: 6px 8px; margin-bottom: 8px; text-align: center; } #flags-btn-widget a { color: #856404; text-decoration: none; font-weight: bold; } #flags-btn-widget a:hover { color: #5a4203; text-decoration: underline; } /* Patreon Button in Sidebar */ #patreon-btn-widget { margin: 15px 0 0 0; padding: 15px 0 0 0; border-top: 1px solid #444; text-align: center; } /* Reduce gap between StoryCaption and Save/Restart menu */ #story-caption { margin-bottom: 0 !important; padding-bottom: 0 !important; } #menu-story { margin-top: 8px !important; } .patreon-link { display: inline-flex; align-items: center; gap: 6px; padding: 8px 12px; background: #f96854; color: #fff !important; border-radius: 4px; text-decoration: none; font-size: 0.9em; font-weight: bold; } .patreon-link:hover { background: #e85a47; text-decoration: none !important; } .patreon-icon { flex-shrink: 0; } .stats-card { background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 6px; padding: 12px; margin-bottom: 12px; } .stats-name { font-weight: bold; font-size: 1.2em; color: #212529; margin-bottom: 8px; border-bottom: 1px solid #dee2e6; padding-bottom: 6px; } .stats-traits { margin-top: 8px; } .stats-trait-item { display: flex; justify-content: space-between; padding: 4px 8px; border-bottom: 1px dashed #e2e3e5; color: #343a40; } .stats-trait-value { font-weight: bold; color: #0d6efd; } /* Story Arc Help Page Styles */ .chapter-context { background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); padding: 12px 16px; border-radius: 8px; margin-bottom: 16px; display: flex; justify-content: space-between; align-items: center; } .chapter-name { font-size: 1.1em; color: #e0e0e0; font-weight: bold; } .chapter-mood { font-size: 0.9em; color: #a0a0a0; font-style: italic; } .available-section h3 { font-size: 1em; color: #495057; margin: 0 0 10px 0; border-bottom: 1px solid #dee2e6; padding-bottom: 6px; } .help-card { background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 6px; padding: 12px 16px; margin-bottom: 10px; } .node-name { font-weight: bold; color: #212529; } .node-hint { color: #6c757d; font-size: 0.9em; margin-top: 4px; } .narrative-hint { background: #fff3cd; border: 1px solid #ffc107; border-radius: 6px; padding: 12px; margin: 16px 0; } .narrative-hint p { margin: 0; color: #856404; } .progress-summary { text-align: center; color: #6c757d; font-size: 0.9em; margin-top: 16px; padding-top: 12px; border-top: 1px solid #dee2e6; } .no-quests-msg { font-style: italic; color: #6c757d; padding: 10px; } /* Quest Page - Simplified One Activity Per NPC */ .npc-section { margin-bottom: 20px; padding: 16px; background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); border-radius: 10px; border: 1px solid #dee2e6; } .npc-name { font-size: 1.2em; color: #2c3e50; margin: 0 0 12px 0; border-bottom: 2px solid #3498db; padding-bottom: 8px; font-weight: 600; } .guide-hint { font-size: 0.85em; color: #f0c040; font-style: italic; } .quest-available { color: #155724; font-weight: 500; padding: 10px 14px; background: rgba(40, 167, 69, 0.1); border-radius: 6px; border-left: 4px solid #28a745; } .quest-locked { color: #856404; font-weight: 500; padding: 10px 14px; background: rgba(255, 193, 7, 0.15); border-radius: 6px; border-left: 4px solid #ffc107; } .quest-conditions { color: #721c24; font-weight: 500; padding: 10px 14px; background: rgba(220, 53, 69, 0.1); border-radius: 6px; border-left: 4px solid #dc3545; } .quest-waiting { color: #856404; font-weight: 500; padding: 10px 14px; background: rgba(255, 193, 7, 0.15); border-radius: 6px; border-left: 4px solid #ffc107; font-style: italic; } .dev-canvas-info { background: #17a2b8; color: white; padding: 6px 10px; border-radius: 4px; margin-bottom: 10px; font-size: 11px; font-family: 'Courier New', monospace; } .quest-complete { color: #28a745; font-style: italic; padding: 10px 14px; text-align: center; } .no-quests { color: #6c757d; font-style: italic; padding: 12px; text-align: center; } /* Trait Requirement Link */ .trait-requirement-link { color: #721c24; text-decoration: underline; cursor: pointer; } .trait-requirement-link:hover { color: #a71d2a; } /* Trait Activities Modal Overlay */ .trait-modal-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.6); display: flex; align-items: center; justify-content: center; z-index: 10000; } /* Modal Container */ .trait-modal { background: #fff; border-radius: 12px; max-width: 400px; width: 90%; max-height: 80vh; overflow: hidden; box-shadow: 0 10px 40px rgba(0,0,0,0.3); } .trait-modal-header { display: flex; justify-content: space-between; align-items: center; padding: 16px 20px; background: #f8f9fa; border-bottom: 1px solid #dee2e6; } .trait-modal-header h3 { margin: 0; font-size: 1.1em; color: #333; } .trait-modal-close { font-size: 24px; cursor: pointer; color: #666; line-height: 1; } .trait-modal-close:hover { color: #333; } .trait-modal-progress { padding: 12px 20px; background: rgba(220, 53, 69, 0.1); color: #721c24; font-weight: 500; text-align: center; } .trait-modal-body { padding: 16px 20px; max-height: 50vh; overflow-y: auto; } /* Activity List */ .trait-activity-list { list-style: none; padding: 0; margin: 0; } .trait-activity-list li { padding: 12px; border-bottom: 1px solid #eee; } .trait-activity-list li:last-child { border-bottom: none; } .activity-name { font-weight: 600; color: #333; } .activity-bonus { float: right; color: #28a745; font-weight: 500; } .activity-hint { margin-top: 4px; color: #666; font-size: 0.9em; clear: both; } /* Effect Toast Notification */ .effect-toast { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background: rgba(0, 0, 0, 0.85); color: #4ade80; padding: 10px 20px; border-radius: 6px; font-size: 14px; z-index: 9999; white-space: nowrap; } /* Game Intro - Top Left Aligned */ .game-intro { text-align: left; } /* Age Gate Section */ .age-gate { background: #fff3cd; border: 1px solid #ffc107; border-radius: 8px; padding: 20px; margin: 20px 0; text-align: center; } .age-warning { font-weight: bold; color: #856404; margin-bottom: 15px; } .age-buttons { display: flex; flex-direction: column; gap: 10px; align-items: center; } /* Developer Intro Section (above age gate) */ .developer-intro { text-align: left; color: #555; margin: 20px 0; } /* Developer Footer Section (below age gate) */ .developer-footer { margin-top: 30px; padding-top: 20px; border-top: 1px solid #dee2e6; text-align: center; color: #6c757d; font-size: 0.9em; } .developer-intro a, .developer-footer a { color: #f96854; text-decoration: none; } .developer-intro a:hover, .developer-footer a:hover { text-decoration: underline; } .developer-about { line-height: 1.5; margin: 10px 0; } .support-link { margin-top: 8px; } .developer-footer .support-link { font-weight: bold; } /* Age Blocked Page */ .blocked-page { text-align: center; padding: 50px; color: #721c24; background: #f8d7da; border-radius: 8px; margin: 50px auto; max-width: 400px; } /* Override SugarCube default vertical centering */ #story { align-items: flex-start !important; justify-content: flex-start !important; padding-top: 1em !important; } .passage { margin-top: 0 !important; padding-top: 0 !important; } /* New/Unvisited Canvas Highlight */ .new-canvas a { color: #28a745 !important; font-weight: 600; } .new-canvas a::before { content: "✨ "; } /* Unlocked conditional choice highlight (in passages) */ .unlocked-choice a, .unlocked-choice .link-internal { color: #f59e0b !important; font-weight: 600; } .unlocked-choice a::before, .unlocked-choice .link-internal::before { content: "🔓 "; } /* Unlocked choice canvas link highlight (in location activity list) */ .unlocked-choice-canvas a { color: #f59e0b !important; font-weight: 600; } .unlocked-choice-canvas a::before { content: "🔓 "; } /* Navigation New Content Indicator */ .nav-new { color: #ffc107; font-weight: bold; font-size: 1.1em; } /* Navigation Unlocked Choice Indicators */ .nav-unlocked { color: #f59e0b; font-weight: bold; font-size: 1.0em; } .nav-unlocked-badge { font-size: 0.65em; padding: 2px 5px; } /* Navigation NPC Portrait Indicator */ .nav-npc-portrait { width: 24px; height: 24px; border-radius: 50%; object-fit: cover; vertical-align: middle; margin-left: 4px; border: 2px solid #ccc; } /* =============================================== VISUAL NAVIGATION GRID =============================================== */ .location-nav-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; margin: 8px 0; } @media (min-width: 768px) { .location-nav-grid { grid-template-columns: repeat(3, 1fr); } } @media (min-width: 1024px) { .location-nav-grid { grid-template-columns: repeat(4, 1fr); } } /* Location Card */ .location-card { display: flex; flex-direction: column; background: #2a2a2a; border-radius: 8px; overflow: hidden; text-decoration: none !important; color: inherit !important; transition: transform 0.2s ease, box-shadow 0.2s ease; cursor: pointer; } .location-card:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); } /* Card Image Area (5:2 aspect ratio - more compact) */ .location-card-image { width: 100%; padding-bottom: 40%; /* 5:2 ratio */ background-size: cover; background-position: center; position: relative; background-color: #1a1a1a; } /* Placeholder silhouette for locations without images */ .location-card-placeholder { display: flex; align-items: center; justify-content: center; background: linear-gradient(135deg, #2d2d3a 0%, #1a1a2a 100%); } .location-card-placeholder svg { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 60%; height: 60%; opacity: 0.5; } /* Card Content Area */ .location-card-content { padding: 6px 8px; display: flex; flex-direction: column; gap: 2px; } .location-card-name { font-weight: 600; font-size: 0.9em; line-height: 1.2; color: #e0e0e0; } /* Indicators Row */ .location-card-indicators { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; } /* NEW badge (replaces ! indicator in visual mode) */ .nav-new-badge { background: #ffc107; color: #000; font-size: 0.65em; font-weight: bold; padding: 2px 5px; border-radius: 3px; text-transform: uppercase; } /* NPC portrait badges (smaller version for cards) */ .nav-npc-badge { width: 20px; height: 20px; border-radius: 50%; object-fit: cover; border: 1px solid #666; } /* Exit Links Section (text-only, below grid) */ .location-nav-exits { margin-top: 12px; padding-top: 12px; border-top: 1px solid #444; } .location-nav-exits a { color: #aaa; } .location-nav-exits a:hover { color: #fff; } /* ===== NPC Portrait System (Location Interaction) ===== */ /* Grid of clickable NPC portraits shown at locations */ .location-npcs { display: flex; gap: 1.5rem; justify-content: center; padding: 1.5rem 0; flex-wrap: wrap; } .npc-portrait-card { text-align: center; cursor: pointer; transition: transform 0.2s ease; position: relative; } .npc-portrait-card:hover { transform: scale(1.08); } .npc-portrait-link { text-decoration: none !important; color: inherit !important; display: flex; flex-direction: column; align-items: center; gap: 0.4rem; } .npc-portrait-img { width: 80px; height: 80px; border-radius: 50%; object-fit: cover; border: 3px solid #555; transition: border-color 0.2s ease, box-shadow 0.2s ease; } .npc-portrait-card:hover .npc-portrait-img { border-color: #ccc; box-shadow: 0 0 12px rgba(200, 200, 200, 0.2); } /* Initial letter fallback for missing portraits */ .npc-portrait-placeholder { width: 80px; height: 80px; border-radius: 50%; background: linear-gradient(135deg, #3a3a4a 0%, #2a2a3a 100%); display: flex; align-items: center; justify-content: center; font-size: 1.8rem; font-weight: 600; color: #ccc; border: 3px solid #555; transition: border-color 0.2s ease, box-shadow 0.2s ease; } .npc-portrait-card:hover .npc-portrait-placeholder { border-color: #ccc; box-shadow: 0 0 12px rgba(200, 200, 200, 0.2); } .npc-portrait-name { display: block; font-size: 0.85rem; color: #bbb; line-height: 1.2; } /* NEW / unlocked indicators on portraits */ .npc-portrait-new .npc-portrait-img, .npc-portrait-new .npc-portrait-placeholder { border-color: #ffc107; } .npc-portrait-unlocked .npc-portrait-img, .npc-portrait-unlocked .npc-portrait-placeholder { border-color: #28a745; } .npc-badge { position: absolute; top: -4px; right: -4px; font-size: 0.6rem; font-weight: bold; padding: 2px 5px; border-radius: 8px; text-transform: uppercase; } .npc-badge-new { background: #ffc107; color: #000; } .npc-badge-unlocked { background: #28a745; color: #fff; } /* Solo activity buttons (activities with no NPC) */ .location-solo-activities { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid #333; } .solo-activity-btn { display: inline-block; padding: 0.4rem 1rem; margin: 0.3rem 0; background: #2a2a2a; border-radius: 4px; color: #ccc !important; text-decoration: none !important; transition: background 0.2s ease; } .solo-activity-btn:hover { background: #3a3a3a; color: #fff !important; } .solo-activity-new { border-left: 3px solid #ffc107; } /* ===== Cost-blocked states ===== */ /* Blocked NPC portraits (greyed out) */ .npc-portrait-blocked .npc-portrait-img, .npc-portrait-blocked .npc-portrait-placeholder { filter: grayscale(70%) brightness(0.6); border-color: #444; } .npc-portrait-blocked .npc-portrait-name { color: #666; } .npc-portrait-blocked:hover { transform: scale(1.04); } /* Cost badge on portraits */ .npc-cost-badge { background: #dc3545; color: #fff; font-size: 0.55rem; position: absolute; bottom: 18px; left: 50%; transform: translateX(-50%); white-space: nowrap; padding: 1px 6px; border-radius: 8px; top: auto; right: auto; } /* Blocked solo activities */ .solo-activity-blocked { opacity: 0.5; } .solo-activity-blocked:hover { opacity: 0.7; } .solo-cost-tag { font-size: 0.75em; color: #dc3545; } /* Cost-blocked passage message */ .cost-blocked-message { text-align: center; padding: 2rem; color: #999; font-style: italic; } .cost-blocked-message p { margin-bottom: 1rem; } /* ===== Sidebar trait bar ===== */ .trait-bar-item { margin-top: 0.5rem; } .trait-bar-label { font-size: 0.75rem; color: #ccc; margin-bottom: 3px; } .trait-bar-bg { height: 8px; background: #1a1a2a; border-radius: 4px; overflow: hidden; } .trait-bar-fill { height: 100%; background: linear-gradient(90deg, #ffc107, #28a745); border-radius: 4px; transition: width 0.3s ease; } /* Dev Mode Controls */ .dev-adj-btn { background: #6c757d; color: white; border: none; border-radius: 3px; width: 20px; height: 20px; font-size: 14px; font-weight: bold; cursor: pointer; padding: 0; line-height: 1; vertical-align: middle; } .dev-adj-btn:hover { background: #5a6268; } .trait-controls { display: flex; align-items: center; gap: 4px; } .trait-controls .trait-value { min-width: 24px; text-align: center; } /* Portrait System */ .portrait { width: 40px; height: 40px; border-radius: 50%; overflow: hidden; flex-shrink: 0; background: #e9ecef; display: flex; align-items: center; justify-content: center; } .portrait img { width: 100%; height: 100%; object-fit: cover; } .portrait-placeholder svg { width: 70%; height: 70%; } /* Dialog Block with Portrait */ .dialog-block { display: flex; align-items: center; gap: 12px; margin: 10px 0; padding: 10px; border-radius: 8px; color: #212529; } .dialog-player { border-left: 4px solid #007bff; background: #e7f3ff; } .dialog-player strong { color: #0056b3; } .dialog-npc { border-left: 4px solid #6c757d; background: #f8f9fa; } .dialog-npc strong { color: #495057; } .dialog-content { flex: 1; } /* Stats Card Portrait (larger) */ .stats-portrait { width: 80px; height: 100px; border-radius: 8px; overflow: hidden; background: #e9ecef; flex-shrink: 0; display: flex; align-items: center; justify-content: center; } .stats-portrait img { width: 100%; height: 100%; object-fit: cover; } .stats-portrait svg { width: 60%; height: 60%; } /* Stats Card with Portrait Layout */ .stats-card-with-portrait { display: flex; gap: 12px; align-items: flex-start; } .stats-info { flex: 1; min-width: 0; } /* Wardrobe Page */ .wardrobe-page { margin: 12px 0; background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 10px; padding: 16px; } .wardrobe-table { width: 100%; border-collapse: collapse; } .wardrobe-row { border-bottom: 1px solid #dee2e6; } .wardrobe-row:last-child { border-bottom: none; } .wardrobe-row-disabled { opacity: 0.45; } .wardrobe-slot-label { padding: 12px 8px; font-weight: 700; color: #333; width: 90px; vertical-align: middle; font-size: 0.85em; text-transform: uppercase; letter-spacing: 0.5px; } .wardrobe-slot-items { padding: 10px 4px; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; } .wardrobe-item { position: relative; width: 76px; height: 76px; border: 2px solid #ccc; border-radius: 8px; cursor: pointer; overflow: hidden; display: flex; align-items: center; justify-content: center; background: #fff; transition: border-color 0.15s, transform 0.15s, box-shadow 0.15s; } .wardrobe-item:hover { border-color: #888; transform: translateY(-1px); box-shadow: 0 2px 6px rgba(0,0,0,0.12); } .wardrobe-item-equipped { border-color: #28a745; background: #e8f5e9; box-shadow: 0 0 0 2px rgba(40, 167, 69, 0.25); } .wardrobe-item-equipped:hover { border-color: #218838; } .wardrobe-item-locked { opacity: 0.5; cursor: not-allowed; filter: grayscale(50%); } .wardrobe-item-locked:hover { transform: none; box-shadow: none; border-color: #ccc; } .wardrobe-item-img { width: 100%; height: 100%; object-fit: cover; } .wardrobe-item-text { font-size: 0.8em; text-align: center; padding: 6px; word-break: break-word; color: #333; line-height: 1.2; } .wardrobe-badge-equipped { position: absolute; top: 2px; right: 2px; background: #28a745; color: #fff; font-size: 11px; width: 18px; height: 18px; border-radius: 50%; display: flex; align-items: center; justify-content: center; } .wardrobe-badge-locked { position: absolute; bottom: 2px; right: 2px; font-size: 13px; } .wardrobe-unequip-btn { background: none; border: 1px solid #6c757d; color: #6c757d; width: 28px; height: 28px; border-radius: 50%; cursor: pointer; font-size: 16px; line-height: 1; display: flex; align-items: center; justify-content: center; margin-left: auto; transition: background 0.15s, color 0.15s; } .wardrobe-unequip-btn:hover { background: #dc3545; border-color: #dc3545; color: #fff; } .wardrobe-disabled-hint, .wardrobe-empty-hint { color: #aaa; font-style: italic; font-size: 0.85em; } .entry-blocked { color: #721c24; font-weight: 500; } .entry-requirements { color: #856404; background: #fff3cd; padding: 8px 12px; border-radius: 6px; font-size: 0.9em; } #wardrobe-btn-widget button { background: #f8f9fa; border: 1px solid #dee2e6; color: #333; padding: 6px 12px; border-radius: 6px; cursor: pointer; width: 100%; font-size: 0.95em; margin: 4px 0; } #wardrobe-btn-widget button:hover { background: #e9ecef; border-color: #adb5bd; } /* Shop page styles */ .shop-page { margin: 12px 0; } .shop-money { background: #e8f5e9; border: 1px solid #c8e6c9; border-radius: 8px; padding: 10px 16px; font-size: 1.05em; font-weight: 600; color: #2e7d32; margin-bottom: 16px; display: inline-block; } .shop-tier { margin-bottom: 20px; border: 1px solid #dee2e6; border-radius: 10px; overflow: hidden; background: #f8f9fa; } .shop-tier-locked { opacity: 0.55; } .shop-tier-header { background: #343a40; color: #fff; padding: 10px 16px; font-weight: 700; font-size: 0.95em; display: flex; justify-content: space-between; align-items: center; } .shop-tier-locked .shop-tier-header { background: #6c757d; } .shop-tier-req { font-weight: 400; font-size: 0.85em; opacity: 0.8; } .shop-tier-locked-msg { padding: 12px 16px; color: #856404; background: #fff3cd; font-style: italic; font-size: 0.9em; text-align: center; } .shop-items { padding: 12px; display: flex; flex-direction: column; gap: 12px; } .shop-slot-group { margin-bottom: 4px; } .shop-slot-label { font-weight: 700; font-size: 0.8em; text-transform: uppercase; letter-spacing: 0.5px; color: #495057; padding: 4px 0; border-bottom: 1px solid #e9ecef; margin-bottom: 6px; } .shop-slot-items { display: flex; flex-direction: column; gap: 6px; } .shop-item { display: flex; align-items: center; gap: 12px; background: #fff; border: 1px solid #dee2e6; border-radius: 8px; padding: 10px 14px; transition: border-color 0.15s; } .shop-item-thumb { width: 56px; height: 56px; border: 2px solid #ccc; border-radius: 8px; overflow: hidden; display: flex; align-items: center; justify-content: center; background: #f8f9fa; flex-shrink: 0; } .shop-item-thumb-img { width: 100%; height: 100%; object-fit: cover; } .shop-item-thumb-text { font-size: 0.7em; text-align: center; padding: 4px; word-break: break-word; color: #666; line-height: 1.2; } .shop-item:hover { border-color: #adb5bd; } .shop-item-owned { border-color: #28a745; background: #e8f5e9; } .shop-item-unaffordable { opacity: 0.55; } .shop-item-info { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; } .shop-item-name { font-weight: 600; color: #212529; font-size: 0.95em; } .shop-item-action { display: flex; align-items: center; gap: 10px; } .shop-item-price { font-weight: 600; color: #495057; font-size: 0.95em; } .shop-item-owned-badge { color: #28a745; font-weight: 600; font-size: 0.9em; } .shop-item-cant-afford { color: #dc3545; font-size: 0.85em; font-style: italic; } .shop-buy-btn { background: #007bff; color: #fff; border: none; padding: 6px 16px; border-radius: 6px; cursor: pointer; font-size: 0.9em; font-weight: 600; transition: background 0.15s; } .shop-buy-btn:hover { background: #0056b3; } /* Rent page styles */ .rent-title { color: #ffffff; margin-bottom: 16px; font-size: 1.4em; } .rent-choices { margin: 16px 0; display: flex; flex-direction: column; gap: 8px; } .rent-choices a { display: block; padding: 10px 16px; background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 6px; color: #212529; text-decoration: none; font-weight: 500; transition: background 0.15s, border-color 0.15s; } .rent-choices a:hover { background: #e9ecef; border-color: #adb5bd; } .rent-balance { margin-top: 12px; padding: 8px 12px; background: rgba(0,0,0,0.04); border-radius: 6px; font-size: 0.95em; } .game-over-text { text-align: center; font-size: 1.5em; font-weight: bold; color: #dc2626; margin: 20px 0; letter-spacing: 2px; } </style>
<<nobr>> <h2>What's Next</h2> <<set _helpData = setup.help_data || {}>> <<set _hasPlayer = _helpData.player && _helpData.player.activities && _helpData.player.activities.some(function(a) { return a.node_id; })>> <<set _hasNpcs = _helpData.npcs && Object.keys(_helpData.npcs).length > 0>> <<if _hasPlayer>> <div class="npc-section"> <h3 class="npc-name"><<print _helpData.player.name>></h3> <<set _next = setup.getNextActivity("_player_")>> <<if _next === null>> <div class="quest-complete">✓ All activities completed!</div> <<elseif _next.isStartingCanvas>> <div class="quest-available">→ Start the game</div> <<elseif _next.traitConditionsNotMet>> <div class="quest-conditions"> 🔒 <<print setup.formatCanvasConditions(_next.canvasConditions)>> </div> <<elseif _next.flagConditionsNotMet>> <div class="quest-available"> → <<print setup.formatFlagHint(_next.flagHint, _helpData.player.name)>> </div> <<elseif _next.daysConditionsNotMet>> <div class="quest-waiting"> ⏳ <<if _next.daysRemaining === 1>>Come back tomorrow<<else>>Wait <<print _next.daysRemaining>> more days<</if>> </div> <<elseif _next.conditionsNotMet>> <div class="quest-conditions"> 🔒 <<print setup.formatCanvasConditions(_next.canvasConditions)>> </div> <<elseif _next.isLocked>> <div class="quest-locked"> 🔒 <<print setup.formatTraitRequirements(_next.missingTraits)>> </div> <<else>> <div class="quest-available"> → <<print setup.formatActivityHint(_next.activity)>> </div> <</if>> </div> <</if>> <<if _hasNpcs>> <<for _npcId, _npcData range _helpData.npcs>> <div class="npc-section"> <h3 class="npc-name"><<print _npcData.name>></h3> <<set _next = setup.getNextActivity(_npcId)>> <<if _next === null>> <div class="quest-complete">✓ All activities completed!</div> <<elseif _next.isStartingCanvas>> <div class="quest-available">→ Start the game</div> <<elseif _next.traitConditionsNotMet>> <div class="quest-conditions"> 🔒 <<print setup.formatCanvasConditions(_next.canvasConditions)>> </div> <<elseif _next.flagConditionsNotMet>> <div class="quest-available"> → <<print setup.formatFlagHint(_next.flagHint, _npcData.name)>> </div> <<elseif _next.daysConditionsNotMet>> <div class="quest-waiting"> ⏳ <<if _next.daysRemaining === 1>>Come back tomorrow<<else>>Wait <<print _next.daysRemaining>> more days<</if>> </div> <<elseif _next.conditionsNotMet>> <div class="quest-conditions"> 🔒 <<print setup.formatCanvasConditions(_next.canvasConditions)>> </div> <<elseif _next.isLocked>> <div class="quest-locked"> 🔒 <<print setup.formatTraitRequirements(_next.missingTraits)>> </div> <<else>> <div class="quest-available"> → <<print setup.formatActivityHint(_next.activity)>> </div> <</if>> </div> <</for>> <</if>> <<if !_hasPlayer && !_hasNpcs>> <div class="no-quests">No activities available.</div> <</if>> <</nobr>> <<link "← Back">><<run Engine.play(State.variables.last_game_passage || "Navigation")>><</link>>
<<set _position = setup.detectStoryPosition()>> <<set _hint = setup.generateNarrativeHint()>> <div class="journal-container"> <div class="journal-header"> <<if _position.current_chapter>> <h2 class="journal-chapter-title"><<print _position.current_chapter.name>></h2> <<if _position.current_chapter.mood>> <div class="journal-chapter-mood <<print _position.current_chapter.mood>>"> <<print _position.current_chapter.description || "">> </div> <</if>> <<else>> <h2 class="journal-chapter-title">Your Story</h2> <</if>> <<if _position.progress_percent > 0>> <div class="journal-progress"> <div class="journal-progress-bar"> <div class="journal-progress-fill" style="width: <<print _position.progress_percent>>%;"></div> </div> </div> <</if>> </div> <!-- Memories (completed story moments) --> <<if _position.completed_nodes.length > 0>> <div class="journal-section"> <div class="journal-section-title">Memories</div> <<for _i, _node range _position.completed_nodes>> <div class="journal-memory <<if _node.is_milestone>>milestone<</if>>"> <div class="journal-memory-title"><<print _node.name>></div> <<if _node.journal_entry>> <<print _node.journal_entry>> <</if>> </div> <</for>> </div> <</if>> <!-- Active story threads (groups in progress) --> <<if _position.active_groups.length > 0>> <<set _incompleteGroups = _position.active_groups.filter(function(g) { return !g.isComplete && g.completed > 0; })>> <<if _incompleteGroups.length > 0>> <div class="journal-section"> <div class="journal-section-title">Unfolding</div> <<for _i, _group range _incompleteGroups>> <div class="journal-group"> <div class="journal-group-name"><<print _group.name>></div> <div class="journal-group-progress"> <<print _group.description>> </div> </div> <</for>> </div> <</if>> <</if>> <!-- Relationship states (NPC emotions) --> <<if Object.keys($npcs).length > 0>> <div class="journal-section"> <div class="journal-section-title">Connections</div> <<for _npcId range Object.keys($npcs)>> <<set _state = setup.interpretNpcState(_npcId)>> <<if _state.npc_name>> <div class="journal-npc"> <div class="journal-npc-name"><<print _state.npc_name>></div> <div class="journal-npc-emotion"><<print _state.description>></div> <<if _state.relationship_summary && _state.relationship_summary !== _state.description>> <div class="journal-npc-summary"><<print _state.relationship_summary>></div> <</if>> </div> <</if>> <</for>> </div> <</if>> <!-- Subtle narrative hint (only when needed) --> <<if _hint.hint_type !== "none" && _hint.text>> <div class="journal-hint <<print _hint.hint_type>>"> <<print _hint.text>> </div> <</if>> <!-- Empty state --> <<if _position.completed_nodes.length === 0 && Object.keys($npcs).length === 0>> <div class="journal-empty"> Your story is just beginning... </div> <</if>> <div class="journal-back-link"> [[Return to your story->Navigation]] </div> </div>
<<nobr>> <h2>All Stats</h2> <!-- Player Stats --> <div class="stats-card stats-card-with-portrait"> <div class="stats-portrait"> <<if $player.portrait>> <img @src="'./videos/' + $player.portrait" @alt="$player.name" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';"> <<else>> <svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg> <</if>> </div> <div class="stats-info"> <div class="stats-name">You (<<print $player.name>>)</div> <div class="stats-traits"> <<if $player && $player.core_traits && Object.keys($player.core_traits).length > 0>> <<for _tk, _tv range $player.core_traits>> <div class="stats-trait-item"> <span><<print _tk>></span> <span class="stats-trait-value"><<print _tv>></span> </div> <</for>> <<else>> <em>No traits</em> <</if>> </div> </div> </div> <!-- NPC Stats --> <<if Object.keys($npcs).length > 0>> <<for _npcId, _npc range $npcs>> <div class="stats-card stats-card-with-portrait"> <div class="stats-portrait"> <<if _npc.portrait>> <img @src="'./videos/' + _npc.portrait" @alt="_npc.name" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';"> <<else>> <svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg> <</if>> </div> <div class="stats-info"> <div class="stats-name"><<print _npc.name>></div> <div class="stats-traits"> <<if _npc.core_traits && Object.keys(_npc.core_traits).length > 0>> <<for _tk, _tv range _npc.core_traits>> <div class="stats-trait-item"> <span><<print _tk>></span> <span class="stats-trait-value"><<print _tv>></span> </div> <</for>> <<else>> <em>No traits</em> <</if>> </div> </div> </div> <</for>> <<else>> <p><em>No NPCs found.</em></p> <</if>> <</nobr>> <<link "← Back">><<run Engine.play(State.variables.last_game_passage || "Navigation")>><</link>>
<<nobr>> <<set _currentDay to $game_state.time_state.current_day>> <h2>📅 <<print _currentDay>>'s Schedules</h2> <p class="current-time">Current Time: <<print setup.formatTime($game_state.time_state.current_hour, $game_state.time_state.current_minute)>></p> <<set _npcsWithSchedules to setup.getNpcsWithSchedules()>> <<if _npcsWithSchedules.length === 0>> <p><em>No NPCs have schedules defined.</em></p> <<else>> <<for _npcInfo range _npcsWithSchedules>> <<set _npcId to _npcInfo.id>> <<set _npcName to _npcInfo.name>> <<set _currentLoc to setup.getNpcLocation(_npcId)>> <<set _todaySchedule to setup.getTodayScheduleSorted(_npcId)>> <<set _dayIndex to ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"].indexOf(_currentDay)>> <div class="schedule-card"> <div class="schedule-header"> <span class="npc-name"><<print _npcName>></span> <<if _currentLoc>> <<set _locName to (setup.locations[_currentLoc.location] && setup.locations[_currentLoc.location].name) || _currentLoc.location>> <span class="now-badge">NOW: <<print _locName>></span> <</if>> </div> <<if _currentLoc && _currentLoc.activity>> <div class="current-activity">"<<print _currentLoc.activity>>"</div> <</if>> <<if _todaySchedule.length === 0>> <p><em>No schedule entries for <<print _currentDay>>.</em></p> <<else>> <p class="schedule-day-info"><<print _todaySchedule.length>> schedule entries for <<print _currentDay>></p> <table class="schedule-table"> <thead><tr><th>Time</th><th>Location</th><th>Activity</th></tr></thead> <tbody> <<for _sch range _todaySchedule>> <<set _isCurrent to setup.isCurrentTimeSlot(_sch.start_time, _sch.end_time)>> <<set _rowClass to _isCurrent ? "current-slot" : "">> <<set _schLocName to (setup.locations[_sch.location] && setup.locations[_sch.location].name) || _sch.location>> <tr class="<<print _rowClass>>"> <td><<if _isCurrent>>▶ <</if>><<print _sch.start_time>>-<<print _sch.end_time || "?">></td> <td><<print _schLocName>></td> <td><<print _sch.activity>></td> </tr> <</for>> </tbody> </table> <</if>> </div> <</for>> <</if>> <<set _soloActivities to setup.getSoloActivitiesForToday()>> <<if _soloActivities.length > 0>> <div class="solo-activities-section schedule-card"> <h3>Your Activities</h3> <table class="schedule-table"> <thead><tr><th>Time</th><th>Location</th><th>Activity</th></tr></thead> <tbody> <<for _act range _soloActivities>> <<set _rowClass to _act.isCurrent ? "current-slot" : "">> <tr class="<<print _rowClass>>"> <td><<if _act.isCurrent>>▶ <</if>><<print _act.startTime>>-<<print _act.endTime || "?">></td> <td><<print _act.locationName>></td> <td><<print _act.name>></td> </tr> <</for>> </tbody> </table> </div> <</if>> <<link "← Back">><<run Engine.play(State.variables.last_game_passage || "Navigation")>><</link>> <</nobr>> <style> .current-time { color: #334155; font-size: 0.9em; margin: 0 0 10px 0; } .schedule-card { background: #ffffff; border: 1px solid #e2e8f0; border-radius: 8px; padding: 12px; margin-bottom: 12px; } .schedule-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; } .npc-name { font-size: 1.1em; font-weight: bold; color: #1e293b; } .now-badge { background: #22c55e; color: white; padding: 3px 8px; border-radius: 10px; font-size: 0.8em; font-weight: bold; } .current-activity { color: #475569; font-style: italic; margin-bottom: 6px; font-size: 0.85em; } .schedule-day-info { color: #64748b; font-size: 0.8em; margin: 0 0 6px 0; padding: 2px 6px; background: #f1f5f9; border-radius: 3px; display: inline-block; } .schedule-table { width: 100%; border-collapse: collapse; font-size: 0.85em; color: #1e293b; } .schedule-table th { text-align: left; padding: 4px 8px; background: #f1f5f9; border-bottom: 2px solid #e2e8f0; color: #334155; font-weight: 600; } .schedule-table td { padding: 4px 8px; border-bottom: 1px solid #e2e8f0; color: #334155; } .schedule-table tr.current-slot { background: #fef3c7; font-weight: 500; } .schedule-table tr.current-slot td:first-child { color: #d97706; } .solo-activities-section { margin-top: 18px; } .solo-activities-section h3 { color: #1e293b; margin: 0 0 8px 0; } </style>
<<nobr>> <h2>🚩 Game Flags</h2> <div class="flags-container"> <<if $flags && Object.keys($flags).length > 0>> <div class="flags-section"> <h3>Story Flags</h3> <<set _gkeys to Object.keys($flags).sort()>> <ul class="flags-list"> <<for _i to 0; _i lt _gkeys.length; _i++>> <<set _gk to _gkeys[_i]>> <li class="flag-item"> <span class="flag-name"><<print _gk>></span> <span class="flag-value <<print $flags[_gk] ? 'flag-true' : 'flag-false'>>"><<print $flags[_gk] ? '✔' : '✖'>></span> </li> <</for>> </ul> </div> <<else>> <div class="no-flags">No story flags set.</div> <</if>> <<if $player && $player.flags && Object.keys($player.flags).length > 0>> <div class="flags-section"> <h3>Player Flags</h3> <<set _pkeys to Object.keys($player.flags).sort()>> <ul class="flags-list"> <<for _j to 0; _j lt _pkeys.length; _j++>> <<set _pk to _pkeys[_j]>> <li class="flag-item"> <span class="flag-name"><<print _pk>></span> <span class="flag-value <<print $player.flags[_pk] ? 'flag-true' : 'flag-false'>>"><<print $player.flags[_pk] ? '✔' : '✖'>></span> </li> <</for>> </ul> </div> <</if>> </div> <<link "← Back">><<run Engine.play(State.variables.last_game_passage || "Navigation")>><</link>> <</nobr>> <style> .flags-container { background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 8px; padding: 16px; margin: 16px 0; } .flags-section { margin-bottom: 20px; } .flags-section:last-child { margin-bottom: 0; } .flags-section h3 { color: #495057; border-bottom: 2px solid #dee2e6; padding-bottom: 8px; margin: 0 0 12px 0; } .flags-list { list-style: none; padding: 0; margin: 0; } .flag-item { display: flex; justify-content: space-between; padding: 6px 0; border-bottom: 1px solid #e9ecef; } .flag-item:last-child { border-bottom: none; } .flag-name { font-weight: 500; color: #212529; } .flag-value { font-family: monospace; } .flag-true { color: #000000; } .flag-false { color: #000000; } .no-flags { color: #6c757d; font-style: italic; padding: 12px; background: #fff; border-radius: 4px; } </style>
<h1>Before You Begin</h1><p>Two Weeks is an interactive story about forbidden feelings, impossible choices, and a clock that won't stop ticking. You have fourteen days before Ethan's wedding. What happens in those days is up to you.</p><p>Explore the house, spend time with Ethan through daily activities, and make choices that shape your relationship. New story events unlock as your bond deepens. Check the Guide at the top of the screen anytime to see what's available.</p><p>Five stats shape your story:</p><p>Love — how deeply Ethan feels for you. Built through emotional choices and time together. Trust — his belief that what you have is real, not just desire. Built through bonding activities and honest moments. Corruption — how far you've both crossed the line. Built through physical choices and daring actions. Energy — your daily stamina. Sleep restores it, activities spend it.</p><p>This game has multiple endings. For Ethan to choose you over the wedding: Love 90+, Trust 75+, and Corruption 85+. He needs to love you deeply, trust what you have is real, and have crossed enough lines that there's no going back. Balance emotional and physical choices — lean too far either way and you'll miss the best ending.</p><p>The wedding is in fourteen days. Time advances with every action. If the clock runs out before you've seen the story through, the wedding happens without you getting a say.</p> <<set $game_state.current_canvas = "458eca00-e0f2-49db-a504-674c8682bab8">> <<set $game_state.current_node = "87254a4c-d82f-4ee3-b623-72a716ed0e63">> <<script>>setup.markCanvasTriggered("458eca00-e0f2-49db-a504-674c8682bab8");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_arrival.before_you_begin") === -1) { vn.push("scene_arrival.before_you_begin"); }<</script>> <<nobr>> <<link "Begin" "StartingCanvas_scene_arrival_Node_2">><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<h1>Two Weeks</h1><p>The taxi pulls away and you're standing on the sidewalk with your suitcase, staring at a house you haven't seen in two years. Same white shutters. Same oak tree in the front yard, taller now. Same crack in the second porch step that Dad always said he'd fix.</p><p>You left for college across the country. Told everyone it was for the program. Told yourself it was for the adventure. The truth was simpler and uglier: you couldn't keep living in a house with him and pretending you didn't feel what you felt.</p><p>Running was easier than staying and aching.</p><img src="./videos/locations/home_exterior.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Two years of careful distance. Two years of short phone calls and shorter visits. And now you're back because your step-brother is getting married in fourteen days and you couldn't think of a good enough excuse not to come.</p><p>Your heart is pounding. You tell yourself it's just nerves.</p> <<set $game_state.current_canvas = "458eca00-e0f2-49db-a504-674c8682bab8">> <<set $game_state.current_node = "c85a9cf4-8255-4601-916e-cc5aab64eb93">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_arrival.the_return") === -1) { vn.push("scene_arrival.the_return"); }<</script>> <<nobr>> <<link "Walk to the door" "StartingCanvas_scene_arrival_Node_3">><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>The door opens before you reach it. And there he is.</p><p>Taller than you remembered. Or maybe you'd been trying to make him smaller in your memory. Warm brown eyes. That easy, lopsided smile that always made you feel like you were the only person in the room. He's broader now — athletic, filled out. Not the lanky stepbrother you left behind.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Hey, stranger.</div></div><p>He pulls you into a hug. His arms tighten a moment too long. You breathe him in without meaning to — cedar and coffee and something that's just him. Two years of careful distance collapse in three seconds.</p><img src="./videos/story/scene_arrival_ethan.gif" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> God, it's good to see you. I wasn't sure you'd actually come.</div></div><p>Your heart is racing and you are actively trying to ignore the reason why.</p> <<set $game_state.current_canvas = "458eca00-e0f2-49db-a504-674c8682bab8">> <<set $game_state.current_node = "4bb37ca1-cab5-4194-bccf-1120c80d0525">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_arrival.ethan") === -1) { vn.push("scene_arrival.ethan"); }<</script>> <<nobr>> <<link "I missed you." "StartingCanvas_scene_arrival_Node_4">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>He carries your suitcase inside like it weighs nothing. The house smells like fresh coffee and something baking. He's been preparing.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Madison's away finishing up some work stuff. Won't be here until right before the wedding. So it's just us for the next couple weeks.</div></div><p>Just us. The weight of those two words hangs in the air between you.</p><p>He shows you to your old room — the guest room that was once yours. Some of your old things are still here. Posters you left behind, books on the shelf. He didn't change it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I kept meaning to turn it into an office but... I don't know. Didn't feel right.</div></div><p>Fourteen days. Just the two of you. In the house where everything started.</p> <<set $game_state.current_canvas = "458eca00-e0f2-49db-a504-674c8682bab8">> <<set $game_state.current_node = "b112fec6-c0bd-4078-9fbb-505bbd2984eb">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_arrival.the_situation") === -1) { vn.push("scene_arrival.the_situation"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "game_started");setup.applyAndNotifyFlag("player", null, "arrival_complete");<</script>> [[Settle in->Location_Home]]
<h2>Navigation</h2> <p>Where would you like to go?</p> <div class="location-list"> [[Home->Location_Home]]<br> [[Your Old Room->Location_Your_Old_Room]]<br> [[Ethan's Bedroom->Location_Ethan's_Bedroom]]<br> [[Bathroom->Location_Bathroom]]<br> [[Living Room->Location_Living_Room]]<br> [[Kitchen->Location_Kitchen]]<br> [[Backyard & Pool->Location_Backyard_&_Pool]]<br> [[Garage->Location_Garage]]<br> </div> <!-- LOCATION PASSAGES -->
<<nobr>> <<set $player.current_location = "bb62b171-50fe-497c-9925-1bc99caa113b">> <<if not $game_state.visited_locations.includes("bb62b171-50fe-497c-9925-1bc99caa113b")>> <<set $game_state.visited_locations.push("bb62b171-50fe-497c-9925-1bc99caa113b")>> <</if>> <</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("bb62b171-50fe-497c-9925-1bc99caa113b")>><<if _autoFire>><<goto _autoFire>><<else>><h2>Home</h2> <p>The central hallway of the house. Family photos line the walls — including several of you and Ethan as teenagers, standing too close at someone's birthday party. Doors branch off to every room. Shoes by the front door, keys on the hook, the smell of whatever Ethan last cooked still lingering.</p> <<= setup.renderNpcPortraits("bb62b171-50fe-497c-9925-1bc99caa113b")>> <<= setup.renderSoloActivities("bb62b171-50fe-497c-9925-1bc99caa113b")>> <div class="location-navigation"> <<nobr>><div class="location-nav-grid"><a class="location-card link-internal" data-passage="Location_Your_Old_Room"><div class="location-card-image" style="background-image: url('./videos/locations/player_room.jpg')"></div><div class="location-card-content"><span class="location-card-name">Your Old Room</span><div class="location-card-indicators"><<if setup.locationHasNewCanvases("b87d311b-c1a9-48f1-8201-d01af9f70322")>><span class="nav-new-badge">NEW</span><</if>><<if setup.locationHasNewUnlockedChoices("b87d311b-c1a9-48f1-8201-d01af9f70322")>><span class="nav-unlocked-badge">🔓</span><</if>><<for _npc range setup.getNpcsWithCanvasesAtLocation("b87d311b-c1a9-48f1-8201-d01af9f70322")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><a class="location-card link-internal" data-passage="Location_Ethan's_Bedroom"><div class="location-card-image" style="background-image: url('./videos/locations/ethan_room.jpg')"></div><div class="location-card-content"><span class="location-card-name">Ethan's Bedroom</span><div class="location-card-indicators"><<if setup.locationHasNewCanvases("d1173eac-d755-49ee-9b21-d6ba07fa7ff4")>><span class="nav-new-badge">NEW</span><</if>><<if setup.locationHasNewUnlockedChoices("d1173eac-d755-49ee-9b21-d6ba07fa7ff4")>><span class="nav-unlocked-badge">🔓</span><</if>><<for _npc range setup.getNpcsWithCanvasesAtLocation("d1173eac-d755-49ee-9b21-d6ba07fa7ff4")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><a class="location-card link-internal" data-passage="Location_Bathroom"><div class="location-card-image" style="background-image: url('./videos/locations/bathroom.jpg')"></div><div class="location-card-content"><span class="location-card-name">Bathroom</span><div class="location-card-indicators"><<if setup.locationHasNewCanvases("0a1374be-33f1-4de0-aea2-eedb80a20f82")>><span class="nav-new-badge">NEW</span><</if>><<if setup.locationHasNewUnlockedChoices("0a1374be-33f1-4de0-aea2-eedb80a20f82")>><span class="nav-unlocked-badge">🔓</span><</if>><<for _npc range setup.getNpcsWithCanvasesAtLocation("0a1374be-33f1-4de0-aea2-eedb80a20f82")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><a class="location-card link-internal" data-passage="Location_Living_Room"><div class="location-card-image" style="background-image: url('./videos/locations/living_room.jpg')"></div><div class="location-card-content"><span class="location-card-name">Living Room</span><div class="location-card-indicators"><<if setup.locationHasNewCanvases("ee621e08-56de-4263-b937-035ccee64ecb")>><span class="nav-new-badge">NEW</span><</if>><<if setup.locationHasNewUnlockedChoices("ee621e08-56de-4263-b937-035ccee64ecb")>><span class="nav-unlocked-badge">🔓</span><</if>><<for _npc range setup.getNpcsWithCanvasesAtLocation("ee621e08-56de-4263-b937-035ccee64ecb")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><a class="location-card link-internal" data-passage="Location_Kitchen"><div class="location-card-image" style="background-image: url('./videos/locations/kitchen.jpg')"></div><div class="location-card-content"><span class="location-card-name">Kitchen</span><div class="location-card-indicators"><<if setup.locationHasNewCanvases("027f175f-d348-40c8-8a4b-fcf1820649c8")>><span class="nav-new-badge">NEW</span><</if>><<if setup.locationHasNewUnlockedChoices("027f175f-d348-40c8-8a4b-fcf1820649c8")>><span class="nav-unlocked-badge">🔓</span><</if>><<for _npc range setup.getNpcsWithCanvasesAtLocation("027f175f-d348-40c8-8a4b-fcf1820649c8")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><a class="location-card link-internal" data-passage="Location_Backyard_&_Pool"><div class="location-card-image" style="background-image: url('./videos/locations/backyard.jpg')"></div><div class="location-card-content"><span class="location-card-name">Backyard & Pool</span><div class="location-card-indicators"><<if setup.locationHasNewCanvases("80c9d208-8df9-4941-8052-cc6f7c2f08c1")>><span class="nav-new-badge">NEW</span><</if>><<if setup.locationHasNewUnlockedChoices("80c9d208-8df9-4941-8052-cc6f7c2f08c1")>><span class="nav-unlocked-badge">🔓</span><</if>><<for _npc range setup.getNpcsWithCanvasesAtLocation("80c9d208-8df9-4941-8052-cc6f7c2f08c1")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><a class="location-card link-internal" data-passage="Location_Garage"><div class="location-card-image" style="background-image: url('./videos/locations/garage.jpg')"></div><div class="location-card-content"><span class="location-card-name">Garage</span><div class="location-card-indicators"><<if setup.locationHasNewCanvases("98b47799-8366-492f-9800-f50b6e09aff7")>><span class="nav-new-badge">NEW</span><</if>><<if setup.locationHasNewUnlockedChoices("98b47799-8366-492f-9800-f50b6e09aff7")>><span class="nav-unlocked-badge">🔓</span><</if>><<for _npc range setup.getNpcsWithCanvasesAtLocation("98b47799-8366-492f-9800-f50b6e09aff7")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a></div><</nobr>> </div> <</if>>
<<nobr>> <<set $player.current_location = "b87d311b-c1a9-48f1-8201-d01af9f70322">> <<if not $game_state.visited_locations.includes("b87d311b-c1a9-48f1-8201-d01af9f70322")>> <<set $game_state.visited_locations.push("b87d311b-c1a9-48f1-8201-d01af9f70322")>> <</if>> <</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("b87d311b-c1a9-48f1-8201-d01af9f70322")>><<if _autoFire>><<goto _autoFire>><<else>><h2>Your Old Room</h2> <p>The guest room that was once yours. Some of your old things are still here — posters you left behind, books on the shelf. The bed is smaller than you remembered. Window overlooks the backyard pool.</p> <<= setup.renderNpcPortraits("b87d311b-c1a9-48f1-8201-d01af9f70322")>> <<= setup.renderSoloActivities("b87d311b-c1a9-48f1-8201-d01af9f70322")>> <div class="location-navigation"> <div class="location-nav-exits"> [[Leave Your Old Room->Location_Home]]<br> </div> </div> <</if>>
<<nobr>> <<set $player.current_location = "d1173eac-d755-49ee-9b21-d6ba07fa7ff4">> <<if not $game_state.visited_locations.includes("d1173eac-d755-49ee-9b21-d6ba07fa7ff4")>> <<set $game_state.visited_locations.push("d1173eac-d755-49ee-9b21-d6ba07fa7ff4")>> <</if>> <</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("d1173eac-d755-49ee-9b21-d6ba07fa7ff4")>><<if _autoFire>><<goto _autoFire>><<else>><h2>Ethan's Bedroom</h2> <p>Master bedroom with a king bed. Madison's presence is visible — her things on the dresser, their engagement photo on the nightstand. Entering feels like crossing a line.</p> <<= setup.renderNpcPortraits("d1173eac-d755-49ee-9b21-d6ba07fa7ff4")>> <<= setup.renderSoloActivities("d1173eac-d755-49ee-9b21-d6ba07fa7ff4")>> <div class="location-navigation"> <div class="location-nav-exits"> [[Leave Ethan's Bedroom->Location_Home]]<br> </div> </div> <</if>>
<<nobr>> <<set $player.current_location = "0a1374be-33f1-4de0-aea2-eedb80a20f82">> <<if not $game_state.visited_locations.includes("0a1374be-33f1-4de0-aea2-eedb80a20f82")>> <<set $game_state.visited_locations.push("0a1374be-33f1-4de0-aea2-eedb80a20f82")>> <</if>> <</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("0a1374be-33f1-4de0-aea2-eedb80a20f82")>><<if _autoFire>><<goto _autoFire>><<else>><h2>Bathroom</h2> <p>Shared bathroom with a large mirror, walk-in shower with glass door. Acoustics carry — you can hear when someone's in here. One bathroom for two people means timing matters.</p> <<= setup.renderNpcPortraits("0a1374be-33f1-4de0-aea2-eedb80a20f82")>> <<= setup.renderSoloActivities("0a1374be-33f1-4de0-aea2-eedb80a20f82")>> <div class="location-navigation"> <div class="location-nav-exits"> [[Leave Bathroom->Location_Home]]<br> </div> </div> <</if>>
<<nobr>> <<set $player.current_location = "ee621e08-56de-4263-b937-035ccee64ecb">> <<if not $game_state.visited_locations.includes("ee621e08-56de-4263-b937-035ccee64ecb")>> <<set $game_state.visited_locations.push("ee621e08-56de-4263-b937-035ccee64ecb")>> <</if>> <</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("ee621e08-56de-4263-b937-035ccee64ecb")>><<if _autoFire>><<goto _autoFire>><<else>><h2>Living Room</h2> <p>Comfortable space with a large sectional couch, flatscreen TV, and soft lighting. The couch is notably oversized — easy to end up sitting close. Wedding planning materials scattered on the coffee table.</p> <<= setup.renderNpcPortraits("ee621e08-56de-4263-b937-035ccee64ecb")>> <<= setup.renderSoloActivities("ee621e08-56de-4263-b937-035ccee64ecb")>> <div class="location-navigation"> <div class="location-nav-exits"> [[Leave Living Room->Location_Home]]<br> </div> </div> <</if>>
<<nobr>> <<set $player.current_location = "027f175f-d348-40c8-8a4b-fcf1820649c8">> <<if not $game_state.visited_locations.includes("027f175f-d348-40c8-8a4b-fcf1820649c8")>> <<set $game_state.visited_locations.push("027f175f-d348-40c8-8a4b-fcf1820649c8")>> <</if>> <</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("027f175f-d348-40c8-8a4b-fcf1820649c8")>><<if _autoFire>><<goto _autoFire>><<else>><h2>Kitchen</h2> <p>Open-plan modern kitchen with a large island and breakfast bar. Morning light floods through windows. Coffee maker prominently featured. Connected to the living area through an open archway.</p> <<= setup.renderNpcPortraits("027f175f-d348-40c8-8a4b-fcf1820649c8")>> <<= setup.renderSoloActivities("027f175f-d348-40c8-8a4b-fcf1820649c8")>> <div class="location-navigation"> <div class="location-nav-exits"> [[Leave Kitchen->Location_Home]]<br> </div> </div> <</if>>
<<nobr>> <<set $player.current_location = "80c9d208-8df9-4941-8052-cc6f7c2f08c1">> <<if not $game_state.visited_locations.includes("80c9d208-8df9-4941-8052-cc6f7c2f08c1")>> <<set $game_state.visited_locations.push("80c9d208-8df9-4941-8052-cc6f7c2f08c1")>> <</if>> <</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("80c9d208-8df9-4941-8052-cc6f7c2f08c1")>><<if _autoFire>><<goto _autoFire>><<else>><h2>Backyard & Pool</h2> <p>Well-maintained backyard with an in-ground pool, lounge chairs, and a covered patio area. Privacy fence ensures neighbors can't see. The pool has underwater lights for night swimming.</p> <<= setup.renderNpcPortraits("80c9d208-8df9-4941-8052-cc6f7c2f08c1")>> <<= setup.renderSoloActivities("80c9d208-8df9-4941-8052-cc6f7c2f08c1")>> <div class="location-navigation"> <div class="location-nav-exits"> [[Leave Backyard & Pool->Location_Home]]<br> </div> </div> <</if>>
<<nobr>> <<set $player.current_location = "98b47799-8366-492f-9800-f50b6e09aff7">> <<if not $game_state.visited_locations.includes("98b47799-8366-492f-9800-f50b6e09aff7")>> <<set $game_state.visited_locations.push("98b47799-8366-492f-9800-f50b6e09aff7")>> <</if>> <</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("98b47799-8366-492f-9800-f50b6e09aff7")>><<if _autoFire>><<goto _autoFire>><<else>><h2>Garage</h2> <p>Attached garage used partly for storage. Old boxes contain family memories — photo albums, childhood items, mementos from when you were growing up together. Dusty, dim, private.</p> <<= setup.renderNpcPortraits("98b47799-8366-492f-9800-f50b6e09aff7")>> <<= setup.renderSoloActivities("98b47799-8366-492f-9800-f50b6e09aff7")>> <div class="location-navigation"> <div class="location-nav-exits"> [[Leave Garage->Location_Home]]<br> </div> </div> <</if>> <!-- STORY CANVAS PASSAGES -->
<<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <p>He whispers so Madison won't hear through the bedroom window. The stars feel like witnesses to something they shouldn't see.</p> <<else>> <p>Blanket on the grass. Stars out. He points at constellations and gets every name wrong. You correct him. He claims he's been saying that the whole time.</p><img src="./videos/activities/bonding_stargazing_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> That one's definitely Orion.</div></div><p>It's not. The sky is infinite and the moment is small and perfect.</p> <</if>> <<set $game_state.current_canvas = "6cacd209-413f-430c-8beb-bf5e650f64fe">> <<set $game_state.current_node = "63f35c88-ecb3-4103-9d19-0cc4162b80a6">> <<script>>setup.markCanvasTriggered("6cacd209-413f-430c-8beb-bf5e650f64fe");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("bonding_stargazing.base") === -1) { vn.push("bonding_stargazing.base"); }<</script>> <<nobr>> <<link "Watch the stars in comfortable silence." "Location_Backyard_&_Pool">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(45);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_ethan", "trait_key": "love", "operator": "gte", "value": 30}]})>> <span @class="setup.isChoiceVisited('6cacd209-413f-430c-8beb-bf5e650f64fe:cc0') ? '' : 'unlocked-choice'"> <<link "Ask what he wanted to be when he grew up." "Canvas_bonding_stargazing_Node_2">><<script>>setup.markChoiceVisited("6cacd209-413f-430c-8beb-bf5e650f64fe:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_ethan", "trait_key": "love", "operator": "gte", "value": 55}]})>> <span @class="setup.isChoiceVisited('6cacd209-413f-430c-8beb-bf5e650f64fe:cc1') ? '' : 'unlocked-choice'"> <<link "Just lie here. No words needed." "Canvas_bonding_stargazing_Node_3">><<script>>setup.markChoiceVisited("6cacd209-413f-430c-8beb-bf5e650f64fe:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <</nobr>>
<p>He asks what you wanted to be when you were a kid. You tell him. He remembers — he always remembers.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I wanted to be someone who didn't hurt people.</div></div><p>The quiet after that sentence goes on forever. A shooting star crosses the sky and neither of you makes a wish.</p> <<set $game_state.current_canvas = "6cacd209-413f-430c-8beb-bf5e650f64fe">> <<set $game_state.current_node = "0c96074d-0a1d-4f1c-a12f-b97e1928c93c">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("bonding_stargazing.t2") === -1) { vn.push("bonding_stargazing.t2"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Let the silence be->Location_Backyard_&_Pool]]
<p>No words left. Just two people lying on the grass, shoulders touching, watching the same sky. His hand finds yours. Not desire. Just presence.</p><p>Just: I'm here. You're here. That's enough tonight.</p><p>The dew starts to fall. Neither of you moves.</p> <<set $game_state.current_canvas = "6cacd209-413f-430c-8beb-bf5e650f64fe">> <<set $game_state.current_node = "3424d440-c333-456f-80ae-0e1df6d87b90">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("bonding_stargazing.t3") === -1) { vn.push("bonding_stargazing.t3"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Stay until the dew falls->Location_Backyard_&_Pool]]
<<set _costs to [{"trait": "energy", "value": 10}]>>\ <<if setup.checkCostsAffordable(_costs)>>\ <<script>>setup.deductCosts("4fce956c-10d1-4ece-b879-939c3a338f2c");<</script>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <p>You find a prom photo buried in the boxes. His arm around you, your head on his shoulder.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> We look happy.</div></div><p>Present tense.</p> <<else>> <p>Boxes of old stuff. School projects, faded ribbons, a science fair trophy with both your names on it. You sit on the concrete floor and sort through a childhood neither of you has touched in years.</p><img src="./videos/activities/bonding_garage_memories_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Oh my god. Your macaroni art phase.</div></div> <</if>> <<set $game_state.current_canvas = "4fce956c-10d1-4ece-b879-939c3a338f2c">> <<set $game_state.current_node = "b9c10166-2278-429a-b976-0ac7824c39ca">> <<script>>setup.markCanvasTriggered("4fce956c-10d1-4ece-b879-939c3a338f2c");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("bonding_garage_memories.base") === -1) { vn.push("bonding_garage_memories.base"); }<</script>> <<nobr>> <<link "Laugh about old school photos. Put the boxes back." "Location_Garage">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(45);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_ethan", "trait_key": "love", "operator": "gte", "value": 30}]})>> <span @class="setup.isChoiceVisited('4fce956c-10d1-4ece-b879-939c3a338f2c:cc0') ? '' : 'unlocked-choice'"> <<link "Look at what he kept." "Canvas_bonding_garage_memories_Node_2">><<script>>setup.markChoiceVisited("4fce956c-10d1-4ece-b879-939c3a338f2c:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_ethan", "trait_key": "love", "operator": "gte", "value": 55}]})>> <span @class="setup.isChoiceVisited('4fce956c-10d1-4ece-b879-939c3a338f2c:cc1') ? '' : 'unlocked-choice'"> <<link "Sit with the memories a while." "Canvas_bonding_garage_memories_Node_3">><<script>>setup.markChoiceVisited("4fce956c-10d1-4ece-b879-939c3a338f2c:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <</nobr>> <<else>> <div class="cost-blocked-message"> <p><<= setup.getCostBlockedMessage(_costs)>></p> </div> [[Back->Location_Garage]] <</if>>
<p>He finds the birthday card you made him when you were twelve. Crayon hearts and misspelled words. He reads it out loud and his voice cracks on 'your the best brother ever.'</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> You spelled 'you're' wrong.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> I was twelve.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Still wrong.</div></div><p>But he's smiling. And he puts the card back in the box carefully, like it matters.</p> <<set $game_state.current_canvas = "4fce956c-10d1-4ece-b879-939c3a338f2c">> <<set $game_state.current_node = "187f1f4f-4416-421e-9d05-5c23b7f3411a">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("bonding_garage_memories.t2") === -1) { vn.push("bonding_garage_memories.t2"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Keep looking->Location_Garage]]
<p>Sitting among the boxes, dusty and quiet. The garage smells like old cardboard and motor oil and time.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> We were a family before we were anything else.</div></div><p>It's the truest thing either of you has said. Some bonds predate desire. This one does. Whatever happens with the wedding, with Madison, with the future — this is the foundation underneath everything.</p> <<set $game_state.current_canvas = "4fce956c-10d1-4ece-b879-939c3a338f2c">> <<set $game_state.current_node = "f071b56c-4097-47a3-bea7-5ed2b26e218a">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("bonding_garage_memories.t3") === -1) { vn.push("bonding_garage_memories.t3"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Close the boxes gently->Location_Garage]]
<<set _costs to [{"trait": "energy", "value": 15}]>>\ <<if setup.checkCostsAffordable(_costs)>>\ <<script>>setup.deductCosts("66f79d38-8d20-4c09-b03f-1767a69e4d5b");<</script>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <p>Between rounds, his hand rests on your knee. Player one, player two. The game has changed but neither of you pauses it.</p> <<else>> <p>He digs the old console out of the TV stand. Same controllers, same cartridge. You sit cross-legged on the floor like you're fourteen again, elbowing each other for advantage.</p><img src="./videos/activities/bonding_video_games_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I call player one.</div></div><p>Some arguments never change.</p> <</if>> <<set $game_state.current_canvas = "66f79d38-8d20-4c09-b03f-1767a69e4d5b">> <<set $game_state.current_node = "4a07b5be-0239-4f08-b928-e02728c88a43">> <<script>>setup.markCanvasTriggered("66f79d38-8d20-4c09-b03f-1767a69e4d5b");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("bonding_video_games.base") === -1) { vn.push("bonding_video_games.base"); }<</script>> <<nobr>> <<link "Play a few rounds. Trash-talk like old times." "Location_Living_Room">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(45);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_ethan", "trait_key": "love", "operator": "gte", "value": 30}]})>> <span @class="setup.isChoiceVisited('66f79d38-8d20-4c09-b03f-1767a69e4d5b:cc0') ? '' : 'unlocked-choice'"> <<link "Call him out for letting you win." "Canvas_bonding_video_games_Node_2">><<script>>setup.markChoiceVisited("66f79d38-8d20-4c09-b03f-1767a69e4d5b:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_ethan", "trait_key": "love", "operator": "gte", "value": 55}]})>> <span @class="setup.isChoiceVisited('66f79d38-8d20-4c09-b03f-1767a69e4d5b:cc1') ? '' : 'unlocked-choice'"> <<link "Pause the game. Just talk." "Canvas_bonding_video_games_Node_3">><<script>>setup.markChoiceVisited("66f79d38-8d20-4c09-b03f-1767a69e4d5b:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <</nobr>> <<else>> <div class="cost-blocked-message"> <p><<= setup.getCostBlockedMessage(_costs)>></p> </div> [[Back->Location_Living_Room]] <</if>>
<p>He lets you win. You call him out. He grins — that same grin from when you were kids.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I have no idea what you're talking about. You're just naturally gifted.</div></div><p>For a moment there's no wedding, no Madison, no complicated feelings. Just two people who grew up together, still keeping score.</p> <<set $game_state.current_canvas = "66f79d38-8d20-4c09-b03f-1767a69e4d5b">> <<set $game_state.current_node = "24746e06-0781-4ba8-a965-f4be2fdd1fb6">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("bonding_video_games.t2") === -1) { vn.push("bonding_video_games.t2"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Save the game->Location_Living_Room]]
<p>The game is paused. Controllers on the carpet. He's talking about the summer you both learned to play this — before everything got complicated.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I miss when things were simple. When we were just... us.</div></div><p>His voice is soft. You realize this is the version of him that existed before obligation. Before he learned to want things he wasn't supposed to have.</p> <<set $game_state.current_canvas = "66f79d38-8d20-4c09-b03f-1767a69e4d5b">> <<set $game_state.current_node = "acd61245-7711-44fa-a1fa-b36f34d330b5">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("bonding_video_games.t3") === -1) { vn.push("bonding_video_games.t3"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Remember this feeling->Location_Living_Room]]
<<set _costs to [{"trait": "energy", "value": 15}]>>\ <<if setup.checkCostsAffordable(_costs)>>\ <<script>>setup.deductCosts("8bdb96a2-e180-476c-860b-2bd07495b70f");<</script>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <p>Madison asks for the recipe card. 'Family tradition!' she says brightly. He hands it to her. You watch your mother's handwriting leave his hands.</p> <<else>> <p>He finds the old recipe card in the drawer. Mom's handwriting, faded but legible. You make it together — measuring flour, arguing about seasoning. The kitchen smells like childhood.</p><img src="./videos/activities/bonding_moms_recipe_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> She always added too much cinnamon. On purpose, I think.</div></div> <</if>> <<set $game_state.current_canvas = "8bdb96a2-e180-476c-860b-2bd07495b70f">> <<set $game_state.current_node = "d3cd51b1-b142-47ea-be41-d31e03b35710">> <<script>>setup.markCanvasTriggered("8bdb96a2-e180-476c-860b-2bd07495b70f");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("bonding_moms_recipe.base") === -1) { vn.push("bonding_moms_recipe.base"); }<</script>> <<nobr>> <<link "Finish cooking. Eat together." "Location_Kitchen">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(45);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_ethan", "trait_key": "love", "operator": "gte", "value": 30}]})>> <span @class="setup.isChoiceVisited('8bdb96a2-e180-476c-860b-2bd07495b70f:cc0') ? '' : 'unlocked-choice'"> <<link "Ask about the first time she made this." "Canvas_bonding_moms_recipe_Node_2">><<script>>setup.markChoiceVisited("8bdb96a2-e180-476c-860b-2bd07495b70f:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_ethan", "trait_key": "love", "operator": "gte", "value": 55}]})>> <span @class="setup.isChoiceVisited('8bdb96a2-e180-476c-860b-2bd07495b70f:cc1') ? '' : 'unlocked-choice'"> <<link "Stay in this moment a while." "Canvas_bonding_moms_recipe_Node_3">><<script>>setup.markChoiceVisited("8bdb96a2-e180-476c-860b-2bd07495b70f:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <</nobr>> <<else>> <div class="cost-blocked-message"> <p><<= setup.getCostBlockedMessage(_costs)>></p> </div> [[Back->Location_Kitchen]] <</if>>
<p>He tells you about the Thanksgiving she first made this. Before the divorce. Before you were step-siblings. When you were just two kids who liked the same dessert.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> You stole the last piece. I cried. Mom made another one just for me.</div></div><p>He laughs. You remember it differently but you let him have his version.</p> <<set $game_state.current_canvas = "8bdb96a2-e180-476c-860b-2bd07495b70f">> <<set $game_state.current_node = "032d9a34-7781-42fa-87f1-d9755b0ec82b">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("bonding_moms_recipe.t2") === -1) { vn.push("bonding_moms_recipe.t2"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Save the leftovers->Location_Kitchen]]
<p>You're covered in flour and laughing. He wipes your cheek with his thumb and doesn't move his hand.</p><p>This isn't desire — it's something older and deeper. The feeling of belonging somewhere. Of being known by someone who remembers the version of you that existed before you learned to hide.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> We should make this more often.</div></div><p>He means the recipe. He also means this.</p> <<set $game_state.current_canvas = "8bdb96a2-e180-476c-860b-2bd07495b70f">> <<set $game_state.current_node = "8d22d9dc-3883-4587-af16-4b70fa385823">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("bonding_moms_recipe.t3") === -1) { vn.push("bonding_moms_recipe.t3"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[This is enough->Location_Kitchen]]
<p>You sink into the couch cushions and stare at the ceiling fan making slow circles. No phone, no journal, no thinking about what any of this means. Just sitting. The house creaks. Somewhere a clock ticks. It's enough.</p> <<set $game_state.current_canvas = "717fde0a-44e7-48a1-8845-88766d8bc293">> <<set $game_state.current_node = "138f350c-b276-4210-8bd9-eefee2b3d0f6">> <<script>>setup.markCanvasTriggered("717fde0a-44e7-48a1-8845-88766d8bc293");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("solo_relax.base") === -1) { vn.push("solo_relax.base"); }<</script>> <<nobr>> <<link "Just a few minutes" "Location_Living_Room">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", 8.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(15);<</script>><</link>><br> <<link "Lose track of time" "Location_Living_Room">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", 12.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(30);<</script>><</link>><br> <</nobr>>
<p>You rummage through the fridge. Leftover pasta, a half-eaten pie, cold coffee you reheat in the microwave. Standing at the counter eating out of the container like a civilized adult.</p> <<set $game_state.current_canvas = "6bb87f19-ccb3-4178-979c-3f314259240b">> <<set $game_state.current_node = "6d63abdd-6a22-4456-885a-adafe230666a">> <<script>>setup.markCanvasTriggered("6bb87f19-ccb3-4178-979c-3f314259240b");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("solo_snack.base") === -1) { vn.push("solo_snack.base"); }<</script>> <<nobr>> <<link "Quick coffee" "Location_Kitchen">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", 5.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(10);<</script>><</link>><br> <<link "Proper snack" "Location_Kitchen">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", 8.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(20);<</script>><</link>><br> <</nobr>>
<p>You lie back on the bed and stare at the ceiling. Not sleeping. Just letting your body be still for a minute. The fan hums. Light moves across the wall. Your thoughts slow down enough to breathe.</p> <<set $game_state.current_canvas = "f5fd89ae-3147-49f4-8c82-e06890fce59a">> <<set $game_state.current_node = "6659b2d1-a02f-4571-b58c-12b05b39a3a5">> <<script>>setup.markCanvasTriggered("f5fd89ae-3147-49f4-8c82-e06890fce59a");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("solo_lie_down.base") === -1) { vn.push("solo_lie_down.base"); }<</script>> <<nobr>> <<link "Just close your eyes" "Location_Your_Old_Room">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", 10.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(15);<</script>><</link>><br> <<link "Drift for a while" "Location_Your_Old_Room">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", 15.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(30);<</script>><</link>><br> <</nobr>>
<<set _costs to [{"trait": "energy", "value": 20}]>>\ <<if setup.checkCostsAffordable(_costs)>>\ <<script>>setup.deductCosts("dc3c5b20-91cf-43c7-bc9b-d01a348cb52d");<</script>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <p>You wrote his name. Crossed it out. Wrote it again. The pen knows what you won't say out loud.</p> <<else>> <p>Writing in the journal you found while unpacking. Processing the day. You write about the house, the weather, the food. You don't write about the way his hand felt on your back. But you think about it for the rest of the page.</p> <</if>> <<set $game_state.current_canvas = "dc3c5b20-91cf-43c7-bc9b-d01a348cb52d">> <<set $game_state.current_node = "c3aa60e0-832a-478c-98ac-8975e16cc410">> <<script>>setup.markCanvasTriggered("dc3c5b20-91cf-43c7-bc9b-d01a348cb52d");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("solo_journal.base") === -1) { vn.push("solo_journal.base"); }<</script>> <<script>>advanceTime(30);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Close the journal->Location_Your_Old_Room]] <<else>> <div class="cost-blocked-message"> <p><<= setup.getCostBlockedMessage(_costs)>></p> </div> [[Back->Location_Your_Old_Room]] <</if>>
<<set _costs to [{"trait": "energy", "value": 5}]>>\ <<if setup.checkCostsAffordable(_costs)>>\ <<script>>setup.deductCosts("252b5b0f-1b46-48cc-babc-1a07029f4948");<</script>> <p>Walking through the house. Family photos in the hallway — you and Ethan at someone's birthday, standing too close. Peeking into rooms. The house is a museum of your shared childhood. You pass his door. It's open a crack. You keep walking.</p> <<set $game_state.current_canvas = "252b5b0f-1b46-48cc-babc-1a07029f4948">> <<set $game_state.current_node = "d2dc953b-f3a4-4902-8875-5a5f6a3bdf9a">> <<script>>setup.markCanvasTriggered("252b5b0f-1b46-48cc-babc-1a07029f4948");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("solo_wander.base") === -1) { vn.push("solo_wander.base"); }<</script>> <<script>>advanceTime(15);<</script>> [[Return to your room->Location_Home]] <<else>> <div class="cost-blocked-message"> <p><<= setup.getCostBlockedMessage(_costs)>></p> </div> [[Back->Location_Home]] <</if>>
<<set _costs to [{"trait": "energy", "value": 5}]>>\ <<if setup.checkCostsAffordable(_costs)>>\ <<script>>setup.deductCosts("201a0804-38ce-451d-810b-ad11af896714");<</script>> <p>Swimming laps alone. The pool is different when he's not here — peaceful, meditative. The sun on your skin. Floating and thinking. The water holds you up when everything else feels heavy.</p> <<set $game_state.current_canvas = "201a0804-38ce-451d-810b-ad11af896714">> <<set $game_state.current_node = "49c70bd5-5287-4c8b-8419-53eb858a0ec2">> <<script>>setup.markCanvasTriggered("201a0804-38ce-451d-810b-ad11af896714");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("solo_swim_alone.base") === -1) { vn.push("solo_swim_alone.base"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "energy", "add", 10.0, true, null);setup.showEffectNotification();<</script>> [[Dry off->Location_Backyard_&_Pool]] <<else>> <div class="cost-blocked-message"> <p><<= setup.getCostBlockedMessage(_costs)>></p> </div> [[Back->Location_Backyard_&_Pool]] <</if>>
<<set _costs to [{"trait": "energy", "value": 15}]>>\ <<if setup.checkCostsAffordable(_costs)>>\ <<script>>setup.deductCosts("1a705540-f1a4-4bf9-b59e-5f8b69490275");<</script>> <p>Scrolling social media, texting friends, avoiding what you should be thinking about. Your friend asks how the trip is. 'Fine,' you type. Delete it. 'Complicated.' Delete that too. 'Fine.'</p> <<set $game_state.current_canvas = "1a705540-f1a4-4bf9-b59e-5f8b69490275">> <<set $game_state.current_node = "ab768202-b289-4057-968c-327344b3afc9">> <<script>>setup.markCanvasTriggered("1a705540-f1a4-4bf9-b59e-5f8b69490275");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("solo_phone_scroll.base") === -1) { vn.push("solo_phone_scroll.base"); }<</script>> <<script>>advanceTime(30);<</script>> [[Put the phone down->Location_Your_Old_Room]] <<else>> <div class="cost-blocked-message"> <p><<= setup.getCostBlockedMessage(_costs)>></p> </div> [[Back->Location_Your_Old_Room]] <</if>>
<p>Unpacking in your old room. You find things you left behind — an old journal, a photo strip from the mall with Ethan. His arm around you, both of you laughing. You were sixteen. You put it face-down on the nightstand.</p> <<set $game_state.current_canvas = "07461032-727f-45e0-bfaa-5bb2bd94d991">> <<set $game_state.current_node = "dee99e95-1fe4-4d2a-aef4-6f130628ce81">> <<script>>setup.markCanvasTriggered("07461032-727f-45e0-bfaa-5bb2bd94d991");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("solo_unpack.base") === -1) { vn.push("solo_unpack.base"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Settle in->Location_Your_Old_Room]]
<<set _costs to [{"trait": "energy", "value": 10}]>>\ <<if setup.checkCostsAffordable(_costs)>>\ <<script>>setup.deductCosts("4b449268-b25c-476a-9150-c3b066e1ee6e");<</script>> <p>Mirror, makeup, clothes. Choosing what to wear — knowing who will see. You spend more time getting ready than you used to. You tell yourself it's just because you're on vacation.</p> <<set $game_state.current_canvas = "4b449268-b25c-476a-9150-c3b066e1ee6e">> <<set $game_state.current_node = "a14e48b7-9216-4c90-af8e-d55f1435c7f2">> <<script>>setup.markCanvasTriggered("4b449268-b25c-476a-9150-c3b066e1ee6e");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("solo_get_ready.base") === -1) { vn.push("solo_get_ready.base"); }<</script>> <<nobr>> <<link "Keep it casual" "Location_Bathroom">><<script>>advanceTime(15);<</script>><</link>><br> <<link "Put in effort today" "Location_Bathroom">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(30);<</script>><</link>><br> <</nobr>> <<else>> <div class="cost-blocked-message"> <p><<= setup.getCostBlockedMessage(_costs)>></p> </div> [[Back->Location_Bathroom]] <</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <p>Hot water runs over skin that still remembers his hands. The steam doesn't wash away the feeling. You don't want it to.</p> <<else>> <p>Hot water, steam on the mirror. A moment to decompress and process everything that's happening. The shared bathroom means you can hear him in the hallway sometimes.</p> <</if>> <<set $game_state.current_canvas = "49b410cf-8a36-48d1-a4d0-0eceb14a59bd">> <<set $game_state.current_node = "67fd3dc1-4b67-4cc0-9608-793886996ab1">> <<script>>setup.markCanvasTriggered("49b410cf-8a36-48d1-a4d0-0eceb14a59bd");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("solo_shower.base") === -1) { vn.push("solo_shower.base"); }<</script>> <<nobr>> <<link "Quick rinse" "Location_Bathroom">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", 5.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(15);<</script>><</link>><br> <<link "Long shower, let the water run" "Location_Bathroom">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", 10.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(30);<</script>><</link>><br> <</nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <p>The sheets smell like you and faintly like him. You press your face into the pillow and breathe.</p> <<else>> <p>You lie down on the bed. The familiar ceiling, the old posters. So much has changed, but this room feels like a time capsule. Your eyes are heavy.</p> <</if>> <<set $game_state.current_canvas = "3d11eb3d-2343-4aa8-860d-df3a1fdfa5e4">> <<set $game_state.current_node = "27aa459c-8288-4cce-9b48-833077026ef7">> <<script>>setup.markCanvasTriggered("3d11eb3d-2343-4aa8-860d-df3a1fdfa5e4");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("solo_sleep.base") === -1) { vn.push("solo_sleep.base"); }<</script>> <<nobr>> <<link "Take a short nap (1 hour)" "Location_Your_Old_Room">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", 20.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(60);<</script>><</link>><br> <<link "Sleep until morning" "Location_Your_Old_Room">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "set", 100.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(480);<</script>><</link>><br> <</nobr>>
<<set _costs to [{"trait": "energy", "value": 10}]>>\ <<if setup.checkCostsAffordable(_costs)>>\ <<script>>setup.deductCosts("c04ca6ec-d587-41b7-be7b-072a67e0077a");<</script>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <p>Madison's voice from his room: 'Coming to bed?' He looks at you. The hallway feels like a canyon.</p><img src="./videos/activities/hallway_doorway_flirt.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <p>Standing between two doors. His and yours. The goodnight takes longer every night.</p><img src="./videos/activities/hallway_doorway_flirt.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <p>The hug lasts too long. He pulls back. Something unfinished in his eyes.</p><img src="./videos/activities/hallway_doorway_flirt.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<else>> <p>Bedroom doors ten feet apart. The goodnight ritual. Neither wants to be the first through their door.</p><img src="./videos/activities/hallway_doorway_flirt.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <</if>> <<set $game_state.current_canvas = "c04ca6ec-d587-41b7-be7b-072a67e0077a">> <<set $game_state.current_node = "27aa28b9-ea3c-499a-a23e-e5b9f7c89627">> <<script>>setup.markCanvasTriggered("c04ca6ec-d587-41b7-be7b-072a67e0077a");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_goodnight.base") === -1) { vn.push("activity_goodnight.base"); }<</script>> <<nobr>> <<link "Talk with him" "Canvas_activity_goodnight_Node_2">><<script>>advanceTime(3);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "lingering_touch_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('c04ca6ec-d587-41b7-be7b-072a67e0077a:cc0') ? '' : 'unlocked-choice'"> <<link "Get closer" "Canvas_activity_goodnight_Node_3">><<script>>setup.markChoiceVisited("c04ca6ec-d587-41b7-be7b-072a67e0077a:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<link "Quick goodnight. Separate doors." "Location_Home">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(45);<</script>><</link>><br> <</nobr>> <<else>> <div class="cost-blocked-message"> <p><<= setup.getCostBlockedMessage(_costs)>></p> </div> [[Back->Location_Home]] <</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I should go in. She's waiting.</div></div><p>He doesn't move. You don't move. The hallway holds its breath.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> I don't want tonight to end.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Then don't let it.</div></div> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Goodnight.</div></div><p>He says it but doesn't turn away. Neither do you.</p> <<else>> <p>A brief hug.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Sleep well.</div></div><p>He smells like soap and something warm. You pull back before it gets complicated.</p> <</if>> <<set $game_state.current_canvas = "c04ca6ec-d587-41b7-be7b-072a67e0077a">> <<set $game_state.current_node = "f53e3c24-a3f2-461e-843d-0cc8aaf15a4b">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_goodnight.emotional") === -1) { vn.push("activity_goodnight.emotional"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Your door. Close it.->Location_Home]]
<p>Words can wait.</p> <<set $game_state.current_canvas = "c04ca6ec-d587-41b7-be7b-072a67e0077a">> <<set $game_state.current_node = "cc2d249d-40d8-49cd-8106-1dbd71d60907">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_goodnight.physical") === -1) { vn.push("activity_goodnight.physical"); }<</script>> <<nobr>> <<link "Let the hug linger. Press closer." "Canvas_activity_goodnight_Node_4">><<script>>advanceTime(3);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "flirt_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('c04ca6ec-d587-41b7-be7b-072a67e0077a:cc1') ? '' : 'unlocked-choice'"> <<link "'What if I can't sleep?' Watch his face." "Canvas_activity_goodnight_Node_5">><<script>>setup.markChoiceVisited("c04ca6ec-d587-41b7-be7b-072a67e0077a:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "kiss_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('c04ca6ec-d587-41b7-be7b-072a67e0077a:cc2') ? '' : 'unlocked-choice'"> <<link "Pin him against his door." "Canvas_activity_goodnight_Node_6">><<script>>setup.markChoiceVisited("c04ca6ec-d587-41b7-be7b-072a67e0077a:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "grind_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('c04ca6ec-d587-41b7-be7b-072a67e0077a:cc3') ? '' : 'unlocked-choice'"> <<link "Pressed against the door. Grind. Don't open it." "Canvas_activity_goodnight_Node_7">><<script>>setup.markChoiceVisited("c04ca6ec-d587-41b7-be7b-072a67e0077a:cc3");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "manual_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('c04ca6ec-d587-41b7-be7b-072a67e0077a:cc4') ? '' : 'unlocked-choice'"> <<link "Between his body and the wood. His hand finds you." "Canvas_activity_goodnight_Node_8">><<script>>setup.markChoiceVisited("c04ca6ec-d587-41b7-be7b-072a67e0077a:cc4");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "oral_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('c04ca6ec-d587-41b7-be7b-072a67e0077a:cc5') ? '' : 'unlocked-choice'"> <<link "Just inside the door. Kneel." "Canvas_activity_goodnight_Node_9">><<script>>setup.markChoiceVisited("c04ca6ec-d587-41b7-be7b-072a67e0077a:cc5");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "sex_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('c04ca6ec-d587-41b7-be7b-072a67e0077a:cc6') ? '' : 'unlocked-choice'"> <<link "Whose room? Does it matter?" "Canvas_activity_goodnight_Node_10">><<script>>setup.markChoiceVisited("c04ca6ec-d587-41b7-be7b-072a67e0077a:cc6");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <</nobr>>
<p>The hug goes on. His arms tighten. Your face presses into his neck. He smells like soap and something that's just him. He pulls back — looks at you. A beat too long. Releases you like it costs something.</p><img src="./videos/activities/goodnight_t2.gif" alt="Lingering hug in hallway, reluctant release, intense eye contact" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "c04ca6ec-d587-41b7-be7b-072a67e0077a">> <<set $game_state.current_node = "88fe0c9c-080c-4cf0-a680-f14ed944009e">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_goodnight.t2") === -1) { vn.push("activity_goodnight.t2"); }<</script>> <<script>>advanceTime(15);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Goodnight->Location_Home]]
<div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> What if I can't sleep?</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Then come find me.</div></div><p>His door closes. The invitation hangs in the hallway air. You stand between the two doors. Your room is safety. His room is everything else.</p><img src="./videos/activities/goodnight_t3.jpg" alt="Hallway between two doors, invitation, standing alone deciding" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "c04ca6ec-d587-41b7-be7b-072a67e0077a">> <<set $game_state.current_node = "b1a4b53f-1067-4919-9849-189efeee0f14">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_goodnight.t3") === -1) { vn.push("activity_goodnight.t3"); }<</script>> <<script>>advanceTime(15);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Your door. For now.->Location_Home]]
<p>He pins you against your bedroom door.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> We should stop.</div></div><p>Your hands pull him closer.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> We should.</div></div><p>His mouth finds your neck. The door handle digs into your back. Neither of you reaches for it.</p><img src="./videos/activities/goodnight_t4.gif" alt="Pinned against bedroom door, kissing neck, hallway at night" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "c04ca6ec-d587-41b7-be7b-072a67e0077a">> <<set $game_state.current_node = "2a63111e-ccb4-4090-8ea0-a86fc744a5f6">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_goodnight.t4") === -1) { vn.push("activity_goodnight.t4"); }<</script>> <<script>>advanceTime(15);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Separate doors. Barely.->Location_Home]]
<p>His body pins you against the door — chest, hips, thigh between yours. You rock against him through thin sleep shorts, feeling every ridge, every twitch. His hands grip your hips and pull you tighter, setting a slow rhythm that makes the door creak.</p><video src="./videos/activities/goodnight_t5.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> Don't open the door.</div></div><p>He groans against your neck. Neither of you opens it. The hallway gets all of you tonight.</p> <<set $game_state.current_canvas = "c04ca6ec-d587-41b7-be7b-072a67e0077a">> <<set $game_state.current_node = "77dfdc08-e7ea-4ded-aba1-5757dced3c8b">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_goodnight.t5") === -1) { vn.push("activity_goodnight.t5"); }<</script>> <<script>>advanceTime(30);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Peel apart. Breathless.->Location_Home]]
<p>Pinned between his body and the wood, his hand finds you without asking. You nod anyway.</p><img src="./videos/activities/goodnight_t6.gif" alt="Manual stimulation against bedroom door at night" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "c04ca6ec-d587-41b7-be7b-072a67e0077a">> <<set $game_state.current_node = "5c24518b-33ba-42a6-9ff1-f7185bccfacd">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_goodnight.t6") === -1) { vn.push("activity_goodnight.t6"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Keys jingle down the hall->Location_Home]]
<p>Inside the door, you sink to your knees before it clicks shut. He exhales your name like relief.</p><video src="./videos/activities/goodnight_t7.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "c04ca6ec-d587-41b7-be7b-072a67e0077a">> <<set $game_state.current_node = "b738aea6-2ba6-4af2-aabd-109f6d58b7f8">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_goodnight.t7") === -1) { vn.push("activity_goodnight.t7"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Stand, dizzy->Location_Home]]
<p>You're not sure whose room it is when you finally break apart. It doesn't matter. The door clicks. The world narrows.</p><video src="./videos/activities/goodnight_t8.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "c04ca6ec-d587-41b7-be7b-072a67e0077a">> <<set $game_state.current_node = "13760d64-6325-437b-8844-ca4e463fa06b">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_goodnight.t8") === -1) { vn.push("activity_goodnight.t8"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 2.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Lights off->Location_Home]]
<<set _costs to [{"trait": "energy", "value": 20}]>>\ <<if setup.checkCostsAffordable(_costs)>>\ <<script>>setup.deductCosts("2e40eb3b-2742-4d16-8864-0fc7e6becf5b");<</script>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <p>He checks the hallway twice before closing the kitchen door. She's asleep upstairs.</p><img src="./videos/activities/cant_sleep.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The click of the latch sounds like a confession.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <p>He's leaning against the counter, waiting. Boxers and nothing else. Not pretending anymore.</p><img src="./videos/activities/cant_sleep.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <p>Both can't sleep. He's in boxers. You're in a nightgown. Eyes meet over the fridge light.</p><img src="./videos/activities/cant_sleep.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<else>> <p>Both can't sleep. The kitchen at 1 AM feels different — smaller, more intimate. Eyes meet over the fridge light.</p><img src="./videos/activities/cant_sleep.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Both pretend not to notice. Both notice everything. The fridge hums.</p> <</if>> <<set $game_state.current_canvas = "2e40eb3b-2742-4d16-8864-0fc7e6becf5b">> <<set $game_state.current_node = "f52d395a-8ebb-4500-aeee-aafe8f094a15">> <<script>>setup.markCanvasTriggered("2e40eb3b-2742-4d16-8864-0fc7e6becf5b");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_late_night_kitchen.base") === -1) { vn.push("activity_late_night_kitchen.base"); }<</script>> <<nobr>> <<link "Talk with him" "Canvas_activity_late_night_kitchen_Node_2">><<script>>advanceTime(3);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "flirt_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('2e40eb3b-2742-4d16-8864-0fc7e6becf5b:cc0') ? '' : 'unlocked-choice'"> <<link "Get closer" "Canvas_activity_late_night_kitchen_Node_3">><<script>>setup.markChoiceVisited("2e40eb3b-2742-4d16-8864-0fc7e6becf5b:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<link "Midnight snack. Back to bed." "Location_Kitchen">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(45);<</script>><</link>><br> <</nobr>> <<else>> <div class="cost-blocked-message"> <p><<= setup.getCostBlockedMessage(_costs)>></p> </div> [[Back->Location_Kitchen]] <</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> What happens when she finds out?</div></div><p>His hand tightens on his glass.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I don't know.</div></div><p>The honesty is the loudest thing in the room.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I can't stop thinking about you.</div></div><p>He says it to the countertop. The confession fills the dark.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Can't sleep either?</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> It's not the coffee.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> No. It's not.</div></div> <<else>> <p>Midnight snacking in near-darkness. The fridge hum is the loudest thing. Both pretending this is about insomnia.</p> <</if>> <<set $game_state.current_canvas = "2e40eb3b-2742-4d16-8864-0fc7e6becf5b">> <<set $game_state.current_node = "d7e154a5-14bd-409c-8e9c-518be5bd7d0f">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_late_night_kitchen.emotional") === -1) { vn.push("activity_late_night_kitchen.emotional"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Back upstairs->Location_Kitchen]]
<p>Words can wait.</p> <<set $game_state.current_canvas = "2e40eb3b-2742-4d16-8864-0fc7e6becf5b">> <<set $game_state.current_node = "da5799c3-982b-437d-b91b-ab4ef4ea105e">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_late_night_kitchen.physical") === -1) { vn.push("activity_late_night_kitchen.physical"); }<</script>> <<nobr>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "flirt_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('2e40eb3b-2742-4d16-8864-0fc7e6becf5b:cc1') ? '' : 'unlocked-choice'"> <<link "Cross the three feet between you." "Canvas_activity_late_night_kitchen_Node_4">><<script>>setup.markChoiceVisited("2e40eb3b-2742-4d16-8864-0fc7e6becf5b:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "kiss_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('2e40eb3b-2742-4d16-8864-0fc7e6becf5b:cc2') ? '' : 'unlocked-choice'"> <<link "'I keep thinking about you.' Close the distance." "Canvas_activity_late_night_kitchen_Node_5">><<script>>setup.markChoiceVisited("2e40eb3b-2742-4d16-8864-0fc7e6becf5b:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "grind_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('2e40eb3b-2742-4d16-8864-0fc7e6becf5b:cc3') ? '' : 'unlocked-choice'"> <<link "His thigh between yours against the counter." "Canvas_activity_late_night_kitchen_Node_6">><<script>>setup.markChoiceVisited("2e40eb3b-2742-4d16-8864-0fc7e6becf5b:cc3");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "manual_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('2e40eb3b-2742-4d16-8864-0fc7e6becf5b:cc4') ? '' : 'unlocked-choice'"> <<link "Brace on the counter. Let his hand find you." "Canvas_activity_late_night_kitchen_Node_7">><<script>>setup.markChoiceVisited("2e40eb3b-2742-4d16-8864-0fc7e6becf5b:cc4");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "oral_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('2e40eb3b-2742-4d16-8864-0fc7e6becf5b:cc5') ? '' : 'unlocked-choice'"> <<link "Sink to your knees on the tile." "Canvas_activity_late_night_kitchen_Node_8">><<script>>setup.markChoiceVisited("2e40eb3b-2742-4d16-8864-0fc7e6becf5b:cc5");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "sex_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('2e40eb3b-2742-4d16-8864-0fc7e6becf5b:cc6') ? '' : 'unlocked-choice'"> <<link "He lifts you onto the counter." "Canvas_activity_late_night_kitchen_Node_9">><<script>>setup.markChoiceVisited("2e40eb3b-2742-4d16-8864-0fc7e6becf5b:cc6");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if not (setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "flirt_unlock", "operator": "is_true"}]}) or setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "kiss_unlock", "operator": "is_true"}]}) or setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "grind_unlock", "operator": "is_true"}]}) or setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "manual_unlock", "operator": "is_true"}]}) or setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "oral_unlock", "operator": "is_true"}]}) or setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "sex_unlock", "operator": "is_true"}]}))>> No available choices<br> [[Continue->Location_Kitchen]] <</if>> <</nobr>>
<div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> Can't sleep either?</div></div><p>You both know why neither can sleep. It's not the coffee. He leans against the counter. You lean against the opposite one. Three feet of kitchen floor between you. It might as well be an ocean. Or nothing.</p><img src="./videos/activities/late_night_kitchen_t3.jpg" alt="Leaning on opposite kitchen counters, midnight tension, loaded silence" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "2e40eb3b-2742-4d16-8864-0fc7e6becf5b">> <<set $game_state.current_node = "9d702dd4-4402-4cd6-8c11-64ad40a82a2a">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_late_night_kitchen.t3") === -1) { vn.push("activity_late_night_kitchen.t3"); }<</script>> <<script>>advanceTime(30);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Warm milk helps->Location_Kitchen]]
<div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> I keep thinking about you.</div></div><p>Whispered into the dark kitchen. You cross the three feet between you. His back against the counter. Your hands on his chest. Heartbeat under your palms.</p><img src="./videos/activities/late_night_kitchen_t4.jpg" alt="Crossing kitchen to him, hands on chest, whispered confession, dark" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "2e40eb3b-2742-4d16-8864-0fc7e6becf5b">> <<set $game_state.current_node = "f1e824ee-4463-4dc0-a9a3-6cb4fc6510c8">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_late_night_kitchen.t4") === -1) { vn.push("activity_late_night_kitchen.t4"); }<</script>> <<script>>advanceTime(30);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Go upstairs->Location_Kitchen]]
<p>He backs you against the fridge. Magnets scatter. His thigh slides between yours and you grind against it in the dark — slow, desperate, clothes damp with sweat. His hands grip your hips, guiding the rhythm.</p><video src="./videos/activities/late_night_kitchen_t5.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>The fridge hums against your back. He breathes your name into your hair. Everything stays on. Everything still counts.</p> <<set $game_state.current_canvas = "2e40eb3b-2742-4d16-8864-0fc7e6becf5b">> <<set $game_state.current_node = "3cecdf70-a78e-47d9-8a8c-e13c92ef437e">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_late_night_kitchen.t5") === -1) { vn.push("activity_late_night_kitchen.t5"); }<</script>> <<script>>advanceTime(30);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Peel apart in the dark->Location_Kitchen]]
<p>You brace on the counter as his hand works you open. The kitchen feels charged at this hour — breath, touch, the hum of the fridge.</p><video src="./videos/activities/late_night_kitchen_t6.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "2e40eb3b-2742-4d16-8864-0fc7e6becf5b">> <<set $game_state.current_node = "5e6f76c8-c13b-4faa-a799-7407e5d5c57b">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_late_night_kitchen.t6") === -1) { vn.push("activity_late_night_kitchen.t6"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Catch your breath->Location_Kitchen]]
<p>You sink to your knees on the tile. He tastes like salt and sleep. His hand finds your hair; he bites his lip to stay quiet.</p><video src="./videos/activities/late_night_kitchen_t7.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "2e40eb3b-2742-4d16-8864-0fc7e6becf5b">> <<set $game_state.current_node = "f6cc8b36-5c47-432a-82bd-e72aeeb017ff">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_late_night_kitchen.t7") === -1) { vn.push("activity_late_night_kitchen.t7"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Stand up slowly->Location_Kitchen]]
<p>He lifts you onto the counter and slides inside you. You try to be quiet; you fail, breath catching as you cling to his shoulders.</p><video src="./videos/activities/late_night_kitchen_t8.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "2e40eb3b-2742-4d16-8864-0fc7e6becf5b">> <<set $game_state.current_node = "77cc3ec2-7d7e-43b8-a9d1-9db1d4edfd57">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_late_night_kitchen.t8") === -1) { vn.push("activity_late_night_kitchen.t8"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 2.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Sneak back upstairs->Location_Kitchen]]
<<set _costs to [{"trait": "energy", "value": 20}]>>\ <<if setup.checkCostsAffordable(_costs)>>\ <<script>>setup.deductCosts("fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39");<</script>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <p>He brings the bottle outside after Madison falls asleep. You're already waiting.</p><img src="./videos/activities/wine_talk_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The patio is your sanctuary now. The stars are the same. Everything else has changed.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <p>Two glasses. No pretense. The wine is an excuse but not the reason.</p><img src="./videos/activities/wine_talk_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <p>A bottle on the patio table. Stars overhead. The wine loosens what the daylight holds tight.</p><img src="./videos/activities/wine_talk_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<else>> <p>A bottle of wine on the patio table. Two glasses. Stars overhead, privacy fence all around.</p><img src="./videos/activities/wine_talk_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The wine loosens tongues and the night air makes everything feel confessional.</p> <</if>> <<set $game_state.current_canvas = "fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39">> <<set $game_state.current_node = "951f9811-963a-4a50-9d0b-f8adda66cfee">> <<script>>setup.markCanvasTriggered("fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wine_talk.base") === -1) { vn.push("activity_wine_talk.base"); }<</script>> <<nobr>> <<link "Talk with him" "Canvas_activity_wine_talk_Node_2">><<script>>advanceTime(3);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "lingering_touch_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39:cc0') ? '' : 'unlocked-choice'"> <<link "Get closer" "Canvas_activity_wine_talk_Node_3">><<script>>setup.markChoiceVisited("fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<link "One glass, stargazing." "Location_Backyard_&_Pool">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(45);<</script>><</link>><br> <</nobr>> <<else>> <div class="cost-blocked-message"> <p><<= setup.getCostBlockedMessage(_costs)>></p> </div> [[Back->Location_Backyard_&_Pool]] <</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> How many more nights do we have?</div></div><p>The wine makes you brave enough to count. The stars don't answer.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> That summer — what would have happened if I'd stayed?</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Everything. Everything would have happened.</div></div> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> I need to tell you something.</div></div><p>The wine gives you courage the daylight won't. He sets his glass down.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Tell me.</div></div> <<else>> <p>Stars and wine. He asks about your life. You ask about his. The answers get real.</p> <</if>> <<set $game_state.current_canvas = "fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39">> <<set $game_state.current_node = "174901c8-c112-4e6e-8db8-4bcde8291091">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wine_talk.emotional") === -1) { vn.push("activity_wine_talk.emotional"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Cork the bottle->Location_Backyard_&_Pool]]
<p>Words can wait.</p> <<set $game_state.current_canvas = "fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39">> <<set $game_state.current_node = "79f5814b-9e86-40df-9efc-415543312d19">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wine_talk.physical") === -1) { vn.push("activity_wine_talk.physical"); }<</script>> <<nobr>> <<link "Second glass. Let the conversation drift." "Canvas_activity_wine_talk_Node_4">><<script>>advanceTime(3);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "flirt_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39:cc1') ? '' : 'unlocked-choice'"> <<link "Liquid courage. Say what you mean." "Canvas_activity_wine_talk_Node_5">><<script>>setup.markChoiceVisited("fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "kiss_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39:cc2') ? '' : 'unlocked-choice'"> <<link "Lean across the table. We shouldn't." "Canvas_activity_wine_talk_Node_6">><<script>>setup.markChoiceVisited("fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "grind_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39:cc3') ? '' : 'unlocked-choice'"> <<link "Straddle him on the patio chair." "Canvas_activity_wine_talk_Node_7">><<script>>setup.markChoiceVisited("fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39:cc3");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "manual_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39:cc4') ? '' : 'unlocked-choice'"> <<link "His hand under your dress. The night keeps secrets." "Canvas_activity_wine_talk_Node_8">><<script>>setup.markChoiceVisited("fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39:cc4");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "oral_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39:cc5') ? '' : 'unlocked-choice'"> <<link "Slide off the lounger." "Canvas_activity_wine_talk_Node_9">><<script>>setup.markChoiceVisited("fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39:cc5");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "sex_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39:cc6') ? '' : 'unlocked-choice'"> <<link "Pull him down onto the lounger." "Canvas_activity_wine_talk_Node_10">><<script>>setup.markChoiceVisited("fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39:cc6");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <</nobr>>
<p>Second glass. The conversation drifts into dangerous territory.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Do you ever think about that summer?</div></div><p>He doesn't finish. Doesn't need to. The stars are impossibly bright and the wine is warm in your chest.</p><img src="./videos/activities/wine_talk_t2.jpg" alt="Second glass of wine, deep conversation under stars" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39">> <<set $game_state.current_node = "e7a940d8-d8e1-4303-9cfe-417eff0ba0cb">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wine_talk.t2") === -1) { vn.push("activity_wine_talk.t2"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Finish the bottle another night->Location_Backyard_&_Pool]]
<div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> I need to tell you something.</div></div><p>The wine gives you courage the daylight won't. He sets his glass down. Turns to face you.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Tell me.</div></div><p>The night is patient. The words come out messier than planned.</p><img src="./videos/activities/wine_talk_t3.jpg" alt="Confession moment on patio, setting wine glass down, turning to face" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39">> <<set $game_state.current_node = "e2919b00-3c48-4f50-a123-26901c7afae8">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wine_talk.t3") === -1) { vn.push("activity_wine_talk.t3"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Cork the bottle->Location_Backyard_&_Pool]]
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> We shouldn't.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> I know.</div></div><p>But you're already leaning across the patio table. His hand catches your chin. Thumb on your lower lip. Stars above. Privacy fence around. Nobody but the night.</p><img src="./videos/activities/wine_talk_t4.jpg" alt="Leaning across table, chin touch, intimate patio moment under stars" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39">> <<set $game_state.current_node = "a2d36278-e831-4ef1-bee9-b674502b6d48">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wine_talk.t4") === -1) { vn.push("activity_wine_talk.t4"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Head inside->Location_Backyard_&_Pool]]
<p>You move from your chair to his. Straddle him on the patio lounger, dress riding up your thighs. His hands grip your hips through the fabric as you rock slowly. Wine-warm skin, the creak of wicker, stars wheeling above you both.</p><img src="./videos/activities/wine_talk_t5.gif" alt="Straddling on patio chair, grinding through clothes, night" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> The neighbors—</div></div><p>You roll your hips and his objection dies. The privacy fence earns its name.</p> <<set $game_state.current_canvas = "fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39">> <<set $game_state.current_node = "fe9b82a4-c927-4050-8b01-5688e15a0a36">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wine_talk.t5") === -1) { vn.push("activity_wine_talk.t5"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Climb off. Pour another glass.->Location_Backyard_&_Pool]]
<p>On the lounger, his hand slips under your dress. The night keeps your secret.</p><video src="./videos/activities/wine_talk_t6.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39">> <<set $game_state.current_node = "d08836ea-414a-460a-b51e-90133ad85350">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wine_talk.t6") === -1) { vn.push("activity_wine_talk.t6"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Top off the wine->Location_Backyard_&_Pool]]
<p>You slide off the lounger and take him in your mouth. The fence, the stars, the two of you — everything feels far away.</p><video src="./videos/activities/wine_talk_t7.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39">> <<set $game_state.current_node = "f8a74b8c-3c30-4c22-a2ac-7cac575e8053">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wine_talk.t7") === -1) { vn.push("activity_wine_talk.t7"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Sit back up->Location_Backyard_&_Pool]]
<p>You turn and pull him down with you on the lounger. The cushion creaks; your breath does too.</p><video src="./videos/activities/wine_talk_t8.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "fd2540ac-d4f3-4e0b-97aa-fdc57aca4e39">> <<set $game_state.current_node = "98bec265-a1d1-4c9d-9467-96a7ba51f26f">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wine_talk.t8") === -1) { vn.push("activity_wine_talk.t8"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 2.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Gather the glasses->Location_Backyard_&_Pool]]
<<set _costs to [{"trait": "energy", "value": 15}]>>\ <<if setup.checkCostsAffordable(_costs)>>\ <<script>>setup.deductCosts("2a315a4c-e29d-4f64-b15d-05522c7aad46");<</script>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <p>Madison sits between you on the couch. His hand finds yours behind her back.</p><img src="./videos/activities/movie_night_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <p>No popcorn bowl between you. You sit against him from the start. His arm is automatic now.</p><img src="./videos/activities/movie_night_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <p>The popcorn bowl is between you. A buffer. Both pretending you need it.</p><img src="./videos/activities/movie_night_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<else>> <p>Big couch, blanket draped over the armrest, popcorn between you. Neither of you is really watching.</p><img src="./videos/activities/movie_night_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <</if>> <<set $game_state.current_canvas = "2a315a4c-e29d-4f64-b15d-05522c7aad46">> <<set $game_state.current_node = "394ea85f-b491-49c3-880d-f7dec546b772">> <<script>>setup.markCanvasTriggered("2a315a4c-e29d-4f64-b15d-05522c7aad46");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_movie_night.base") === -1) { vn.push("activity_movie_night.base"); }<</script>> <<nobr>> <<link "Talk with him" "Canvas_activity_movie_night_Node_2">><<script>>advanceTime(3);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "lingering_touch_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('2a315a4c-e29d-4f64-b15d-05522c7aad46:cc0') ? '' : 'unlocked-choice'"> <<link "Get closer" "Canvas_activity_movie_night_Node_3">><<script>>setup.markChoiceVisited("2a315a4c-e29d-4f64-b15d-05522c7aad46:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<link "Watch the movie." "Location_Living_Room">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(45);<</script>><</link>><br> <</nobr>> <<else>> <div class="cost-blocked-message"> <p><<= setup.getCostBlockedMessage(_costs)>></p> </div> [[Back->Location_Living_Room]] <</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <p>Madison falls asleep on his shoulder. He looks at you over her head. Nobody watches the movie.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> We used to watch this exact one. Remember?</div></div><p>You remember. You remember everything about that summer.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> This is nice.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> Yeah. It is.</div></div> <<else>> <p>The movie fills the silence. You argue about the plot like you used to. His laugh is the same.</p> <</if>> <<set $game_state.current_canvas = "2a315a4c-e29d-4f64-b15d-05522c7aad46">> <<set $game_state.current_node = "6045aad8-c980-4214-a0ff-cfc97ca71f6a">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_movie_night.emotional") === -1) { vn.push("activity_movie_night.emotional"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Credits roll->Location_Living_Room]]
<p>Words can wait.</p> <<set $game_state.current_canvas = "2a315a4c-e29d-4f64-b15d-05522c7aad46">> <<set $game_state.current_node = "ef59501e-7348-4cad-8902-662c70ce1c68">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_movie_night.physical") === -1) { vn.push("activity_movie_night.physical"); }<</script>> <<nobr>> <<link "Scoot past the popcorn bowl. Closer." "Canvas_activity_movie_night_Node_4">><<script>>advanceTime(3);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "flirt_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('2a315a4c-e29d-4f64-b15d-05522c7aad46:cc1') ? '' : 'unlocked-choice'"> <<link "Head on his shoulder. Let his arm settle." "Canvas_activity_movie_night_Node_5">><<script>>setup.markChoiceVisited("2a315a4c-e29d-4f64-b15d-05522c7aad46:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "kiss_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('2a315a4c-e29d-4f64-b15d-05522c7aad46:cc2') ? '' : 'unlocked-choice'"> <<link "His hand finds your thigh under the blanket." "Canvas_activity_movie_night_Node_6">><<script>>setup.markChoiceVisited("2a315a4c-e29d-4f64-b15d-05522c7aad46:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "grind_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('2a315a4c-e29d-4f64-b15d-05522c7aad46:cc3') ? '' : 'unlocked-choice'"> <<link "Under the blanket. Grind against his thigh." "Canvas_activity_movie_night_Node_7">><<script>>setup.markChoiceVisited("2a315a4c-e29d-4f64-b15d-05522c7aad46:cc3");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "manual_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('2a315a4c-e29d-4f64-b15d-05522c7aad46:cc4') ? '' : 'unlocked-choice'"> <<link "The blanket becomes a tent for two." "Canvas_activity_movie_night_Node_8">><<script>>setup.markChoiceVisited("2a315a4c-e29d-4f64-b15d-05522c7aad46:cc4");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "oral_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('2a315a4c-e29d-4f64-b15d-05522c7aad46:cc5') ? '' : 'unlocked-choice'"> <<link "Slide down the couch under the blanket." "Canvas_activity_movie_night_Node_9">><<script>>setup.markChoiceVisited("2a315a4c-e29d-4f64-b15d-05522c7aad46:cc5");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "sex_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('2a315a4c-e29d-4f64-b15d-05522c7aad46:cc6') ? '' : 'unlocked-choice'"> <<link "Climb onto his lap. Blanket falls away." "Canvas_activity_movie_night_Node_10">><<script>>setup.markChoiceVisited("2a315a4c-e29d-4f64-b15d-05522c7aad46:cc6");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <</nobr>>
<p>You shiver — barely convincing — and scoot past the popcorn bowl. Your thigh presses against his. Heat radiates through the blanket. He doesn't move away. The movie continues. Neither of you could name the scene.</p><img src="./videos/activities/movie_night_t2.jpg" alt="Scooting closer on couch under blanket, thighs touching" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "2a315a4c-e29d-4f64-b15d-05522c7aad46">> <<set $game_state.current_node = "48e76d17-1308-420d-9d98-88fcd08d2d89">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_movie_night.t2") === -1) { vn.push("activity_movie_night.t2"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Movie ends->Location_Living_Room]]
<p>Your head drifts to his shoulder. His arm settles around you — natural as breathing. His fingers trace absent patterns on your arm.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Just like we used to.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> It doesn't feel like it used to.</div></div><p>No. It doesn't.</p><img src="./videos/activities/movie_night_cuddle.jpg" alt="Head on shoulder, arm around her, fingers tracing arm" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "2a315a4c-e29d-4f64-b15d-05522c7aad46">> <<set $game_state.current_node = "ea7820c8-9965-40cb-94bc-38f2b8855146">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_movie_night.t3") === -1) { vn.push("activity_movie_night.t3"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Credits roll->Location_Living_Room]]
<p>Under the blanket, his hand finds your thigh. Innocent at first. Then higher. Your hand finds his. Intertwines. Then moves to his leg. The movie is background noise. Both of your breathing has changed.</p><img src="./videos/activities/movie_night_t4.jpg" alt="Under blanket hands on thigh, tension, heavy breathing" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "2a315a4c-e29d-4f64-b15d-05522c7aad46">> <<set $game_state.current_node = "50a92aab-9990-4c66-bb7f-448b46bcca15">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_movie_night.t4") === -1) { vn.push("activity_movie_night.t4"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Pull the blanket tighter->Location_Living_Room]]
<p>Under the blanket, you shift onto his lap. The movie plays to an audience of nobody. His hands settle on your hips as you grind down slowly — the friction of cotton on cotton, his breath sharp against your ear.</p><video src="./videos/activities/movie_night_t5.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>The blanket hides everything. The couch springs protest softly. He bites his lip and grips your thighs harder.</p> <<set $game_state.current_canvas = "2a315a4c-e29d-4f64-b15d-05522c7aad46">> <<set $game_state.current_node = "4866c680-3404-4300-8a19-d26d0358ffc0">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_movie_night.t5") === -1) { vn.push("activity_movie_night.t5"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Shift back to your side. Barely.->Location_Living_Room]]
<p>The blanket becomes a tent for two. His hand finds a rhythm, and your breath stutters as the movie drones on, forgotten.</p><video src="./videos/activities/movie_night_t6.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "2a315a4c-e29d-4f64-b15d-05522c7aad46">> <<set $game_state.current_node = "0a1c96c0-cfd2-4ed2-90df-d2e0967f7b4e">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_movie_night.t6") === -1) { vn.push("activity_movie_night.t6"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Let the credits roll->Location_Living_Room]]
<p>You slide down the couch and take him in your mouth. The blanket hides you; his hand curls in your hair, eyes on the ceiling, stunned and grateful.</p><video src="./videos/activities/movie_night_t7.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "2a315a4c-e29d-4f64-b15d-05522c7aad46">> <<set $game_state.current_node = "3e4cbbd3-8b80-4620-9eb8-6a9e191c184b">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_movie_night.t7") === -1) { vn.push("activity_movie_night.t7"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Straighten the blanket->Location_Living_Room]]
<p>You climb back onto his lap and guide him inside you. Breathless and close, you try to be quiet and fail, laughing softly against his mouth.</p><video src="./videos/activities/movie_night_t8.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "2a315a4c-e29d-4f64-b15d-05522c7aad46">> <<set $game_state.current_node = "22f8b251-9388-4492-91ff-08fa5bc74755">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_movie_night.t8") === -1) { vn.push("activity_movie_night.t8"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 2.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Find the remote eventually->Location_Living_Room]]
<<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <p>Three place settings. Madison talks about the honeymoon. Bali. She's excited. You drink faster.</p><img src="./videos/activities/dinner_ethan_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <p>Wine with dinner. The conversation dives straight in. His foot finds yours from the first pour.</p><img src="./videos/activities/dinner_ethan_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <p>Second glass. He asks about your life. You ask about the wedding. Both deflecting.</p><img src="./videos/activities/dinner_ethan_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<else>> <p>Dinner conversation. He asks about your dating life, you deflect. Normal family dinner with undercurrents.</p><img src="./videos/activities/dinner_ethan_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Wine with dinner. Elbows on the table, leaning in.</p> <</if>> <<set $game_state.current_canvas = "3d98f520-fc3f-4d4a-b7a8-4220e255fbc3">> <<set $game_state.current_node = "1f08b0b3-f027-4384-aaee-f3d1745c0899">> <<script>>setup.markCanvasTriggered("3d98f520-fc3f-4d4a-b7a8-4220e255fbc3");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_dinner_ethan.base") === -1) { vn.push("activity_dinner_ethan.base"); }<</script>> <<nobr>> <<link "Talk with him" "Canvas_activity_dinner_ethan_Node_2">><<script>>advanceTime(3);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "lingering_touch_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('3d98f520-fc3f-4d4a-b7a8-4220e255fbc3:cc0') ? '' : 'unlocked-choice'"> <<link "Get closer" "Canvas_activity_dinner_ethan_Node_3">><<script>>setup.markChoiceVisited("3d98f520-fc3f-4d4a-b7a8-4220e255fbc3:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<link "Pleasant dinner conversation." "Location_Kitchen">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", 10.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(45);<</script>><</link>><br> <</nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <p>Madison clears the plates. He looks at you over her shoulder. You look away first.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> Why her?</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Because she was safe. Because she wasn't you.</div></div> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> You know why I left.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I know why you left.</div></div><p>The truth is a third guest at the table.</p> <<else>> <p>Wine loosens the conversation. He asks why you stayed away so long. You give the rehearsed answer.</p> <</if>> <<set $game_state.current_canvas = "3d98f520-fc3f-4d4a-b7a8-4220e255fbc3">> <<set $game_state.current_node = "8955cea9-6076-4268-a0e2-1181e54df4e4">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_dinner_ethan.emotional") === -1) { vn.push("activity_dinner_ethan.emotional"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 2.0, true, null);setup.applyAndNotifyTrait("player", null, "energy", "add", 10.0, true, null);setup.showEffectNotification();<</script>> [[Clear the table->Location_Kitchen]]
<p>Words can wait.</p> <<set $game_state.current_canvas = "3d98f520-fc3f-4d4a-b7a8-4220e255fbc3">> <<set $game_state.current_node = "1c1b8167-55ff-4899-9091-f4ea73958f1c">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_dinner_ethan.physical") === -1) { vn.push("activity_dinner_ethan.physical"); }<</script>> <<nobr>> <<link "Pour a second glass. Lean closer." "Canvas_activity_dinner_ethan_Node_4">><<script>>advanceTime(3);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "flirt_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('3d98f520-fc3f-4d4a-b7a8-4220e255fbc3:cc1') ? '' : 'unlocked-choice'"> <<link "'You know why.' Let the words hang." "Canvas_activity_dinner_ethan_Node_5">><<script>>setup.markChoiceVisited("3d98f520-fc3f-4d4a-b7a8-4220e255fbc3:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "kiss_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('3d98f520-fc3f-4d4a-b7a8-4220e255fbc3:cc2') ? '' : 'unlocked-choice'"> <<link "Your bare foot finds his calf. Slides up." "Canvas_activity_dinner_ethan_Node_6">><<script>>setup.markChoiceVisited("3d98f520-fc3f-4d4a-b7a8-4220e255fbc3:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "grind_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('3d98f520-fc3f-4d4a-b7a8-4220e255fbc3:cc3') ? '' : 'unlocked-choice'"> <<link "His lap under the table. Move against him." "Canvas_activity_dinner_ethan_Node_7">><<script>>setup.markChoiceVisited("3d98f520-fc3f-4d4a-b7a8-4220e255fbc3:cc3");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "manual_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('3d98f520-fc3f-4d4a-b7a8-4220e255fbc3:cc4') ? '' : 'unlocked-choice'"> <<link "Guide his hand under the tablecloth." "Canvas_activity_dinner_ethan_Node_8">><<script>>setup.markChoiceVisited("3d98f520-fc3f-4d4a-b7a8-4220e255fbc3:cc4");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "oral_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('3d98f520-fc3f-4d4a-b7a8-4220e255fbc3:cc5') ? '' : 'unlocked-choice'"> <<link "Slide under the tablecloth." "Canvas_activity_dinner_ethan_Node_9">><<script>>setup.markChoiceVisited("3d98f520-fc3f-4d4a-b7a8-4220e255fbc3:cc5");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "sex_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('3d98f520-fc3f-4d4a-b7a8-4220e255fbc3:cc6') ? '' : 'unlocked-choice'"> <<link "Sweep the plates aside." "Canvas_activity_dinner_ethan_Node_10">><<script>>setup.markChoiceVisited("3d98f520-fc3f-4d4a-b7a8-4220e255fbc3:cc6");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <</nobr>>
<p>Second glass loosens things. He asks why you stayed away. The answer is on the tip of your tongue. You hold it back — barely.</p><img src="./videos/activities/dinner_ethan_t2.jpg" alt="Deep dinner conversation, wine, loaded questions" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "3d98f520-fc3f-4d4a-b7a8-4220e255fbc3">> <<set $game_state.current_node = "e829a68e-2d0e-49b2-8a55-658f62317d58">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_dinner_ethan.t2") === -1) { vn.push("activity_dinner_ethan.t2"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Clear the plates->Location_Kitchen]]
<div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> You know why.</div></div><p>His fork stops. The air thickens. Two words that change the entire dinner from catch-up to confession.</p><img src="./videos/activities/dinner_ethan_t3.jpg" alt="Fork stopping, intense eye contact across dinner table" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "3d98f520-fc3f-4d4a-b7a8-4220e255fbc3">> <<set $game_state.current_node = "07612059-a4de-4bfe-a603-011638dc293f">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_dinner_ethan.t3") === -1) { vn.push("activity_dinner_ethan.t3"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Finish dinner->Location_Kitchen]]
<p>Your bare foot finds his calf under the table. Slides up. He grips the edge of the table. His eyes widen. Neither of you breaks eye contact.</p><video src="./videos/activities/dinner_ethan_t4.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "3d98f520-fc3f-4d4a-b7a8-4220e255fbc3">> <<set $game_state.current_node = "b4201ab2-c92a-4066-938f-b66cf9d4c741">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_dinner_ethan.t4") === -1) { vn.push("activity_dinner_ethan.t4"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Dessert?->Location_Kitchen]]
<p>You slide out of your chair and onto his lap. The table hides you from the waist down. You rock against him — slow, deliberate, maintaining eye contact while he tries to hold a straight face.</p><video src="./videos/activities/dinner_ethan_t5.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> The food's getting cold.</div></div><p>His hands say otherwise. They grip your waist and press you down harder.</p> <<set $game_state.current_canvas = "3d98f520-fc3f-4d4a-b7a8-4220e255fbc3">> <<set $game_state.current_node = "8181b017-3e18-4e04-9c29-cce616668417">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_dinner_ethan.t5") === -1) { vn.push("activity_dinner_ethan.t5"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Back to your chair. Compose yourself.->Location_Kitchen]]
<p>You lace fingers under the tablecloth and then guide him to your thigh. He doesn't look up from his plate; neither do you.</p><video src="./videos/activities/dinner_ethan_t6.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "3d98f520-fc3f-4d4a-b7a8-4220e255fbc3">> <<set $game_state.current_node = "b7aca503-0bd7-4855-b64b-e793ca72a949">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_dinner_ethan.t6") === -1) { vn.push("activity_dinner_ethan.t6"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Clear plates slowly->Location_Kitchen]]
<p>You slide under the tablecloth and make dinner irrelevant. The silverware rattles once; you both freeze and then laugh.</p><video src="./videos/activities/dinner_ethan_t7.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "3d98f520-fc3f-4d4a-b7a8-4220e255fbc3">> <<set $game_state.current_node = "545245dc-06bc-4f3e-a5c5-f604e4e35235">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_dinner_ethan.t7") === -1) { vn.push("activity_dinner_ethan.t7"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Set the forks straight->Location_Kitchen]]
<p>You sweep plates aside and pull him over you on the table. It's messy and perfect and neither of you cares.</p><video src="./videos/activities/dinner_ethan_t8.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "3d98f520-fc3f-4d4a-b7a8-4220e255fbc3">> <<set $game_state.current_node = "03b42964-07e2-4e36-ac2d-34bdb547913d">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_dinner_ethan.t8") === -1) { vn.push("activity_dinner_ethan.t8"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 2.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Dishwasher later->Location_Kitchen]]
<<set _costs to [{"trait": "energy", "value": 20}]>>\ <<if setup.checkCostsAffordable(_costs)>>\ <<script>>setup.deductCosts("d211137b-6ff7-486c-9dbe-9e5dfe768793");<</script>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <p>Madison asks to join. He says it's a 'sibling thing.' She smiles and goes to the living room.</p><img src="./videos/activities/cooking_together_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>You can hear her humming from the next room.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <p>He chops while you stir. His hip against yours at the counter.</p><img src="./videos/activities/cooking_together_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> You're doing it wrong.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> Show me then.</div></div> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <p>Mom's old recipe. His hands guide yours on the knife. He doesn't need to.</p><img src="./videos/activities/cooking_together_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<else>> <p>Making mom's old recipe together. Nostalgia, laughter over shared memories.</p><img src="./videos/activities/cooking_together_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> You're doing it wrong.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> Show me then.</div></div> <</if>> <<set $game_state.current_canvas = "d211137b-6ff7-486c-9dbe-9e5dfe768793">> <<set $game_state.current_node = "fce4002f-d3df-441a-a810-e625917efa3f">> <<script>>setup.markCanvasTriggered("d211137b-6ff7-486c-9dbe-9e5dfe768793");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_cooking_together.base") === -1) { vn.push("activity_cooking_together.base"); }<</script>> <<nobr>> <<link "Talk with him" "Canvas_activity_cooking_together_Node_2">><<script>>advanceTime(3);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "lingering_touch_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('d211137b-6ff7-486c-9dbe-9e5dfe768793:cc0') ? '' : 'unlocked-choice'"> <<link "Get closer" "Canvas_activity_cooking_together_Node_3">><<script>>setup.markChoiceVisited("d211137b-6ff7-486c-9dbe-9e5dfe768793:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<link "Cook together, swap stories." "Location_Kitchen">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(45);<</script>><</link>><br> <</nobr>> <<else>> <div class="cost-blocked-message"> <p><<= setup.getCostBlockedMessage(_costs)>></p> </div> [[Back->Location_Kitchen]] <</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <p>She's in the next room. You can hear her humming.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> She likes your cooking.</div></div> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> Remember the smoke alarm?</div></div><p>He laughs. You laugh. The kitchen smells like then and now.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <p>He tells you about learning this recipe after you left.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I wanted to remember.</div></div> <<else>> <p>Easy cooking banter. He burns the garlic. You rescue it. Old rhythm.</p> <</if>> <<set $game_state.current_canvas = "d211137b-6ff7-486c-9dbe-9e5dfe768793">> <<set $game_state.current_node = "28446fd2-54e6-4140-8ce3-544e3cecba9f">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_cooking_together.emotional") === -1) { vn.push("activity_cooking_together.emotional"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Plate up dinner->Location_Kitchen]]
<p>Words can wait.</p> <<set $game_state.current_canvas = "d211137b-6ff7-486c-9dbe-9e5dfe768793">> <<set $game_state.current_node = "afbbeaca-53c6-4c13-b179-debdd57cc3d6">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_cooking_together.physical") === -1) { vn.push("activity_cooking_together.physical"); }<</script>> <<nobr>> <<link "Let him stand behind you at the cutting board." "Canvas_activity_cooking_together_Node_4">><<script>>advanceTime(3);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "flirt_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('d211137b-6ff7-486c-9dbe-9e5dfe768793:cc1') ? '' : 'unlocked-choice'"> <<link "Taste from the same spoon. Watch his eyes." "Canvas_activity_cooking_together_Node_5">><<script>>setup.markChoiceVisited("d211137b-6ff7-486c-9dbe-9e5dfe768793:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "kiss_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('d211137b-6ff7-486c-9dbe-9e5dfe768793:cc2') ? '' : 'unlocked-choice'"> <<link "Sauce on your lip. Let him get it." "Canvas_activity_cooking_together_Node_6">><<script>>setup.markChoiceVisited("d211137b-6ff7-486c-9dbe-9e5dfe768793:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "grind_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('d211137b-6ff7-486c-9dbe-9e5dfe768793:cc3') ? '' : 'unlocked-choice'"> <<link "Press back against him at the stove. Move slowly." "Canvas_activity_cooking_together_Node_7">><<script>>setup.markChoiceVisited("d211137b-6ff7-486c-9dbe-9e5dfe768793:cc3");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "manual_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('d211137b-6ff7-486c-9dbe-9e5dfe768793:cc4') ? '' : 'unlocked-choice'"> <<link "His hand follows the trail of your skin." "Canvas_activity_cooking_together_Node_8">><<script>>setup.markChoiceVisited("d211137b-6ff7-486c-9dbe-9e5dfe768793:cc4");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "oral_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('d211137b-6ff7-486c-9dbe-9e5dfe768793:cc5') ? '' : 'unlocked-choice'"> <<link "Slide to your knees on the tile." "Canvas_activity_cooking_together_Node_9">><<script>>setup.markChoiceVisited("d211137b-6ff7-486c-9dbe-9e5dfe768793:cc5");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "sex_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('d211137b-6ff7-486c-9dbe-9e5dfe768793:cc6') ? '' : 'unlocked-choice'"> <<link "He lifts you onto the counter." "Canvas_activity_cooking_together_Node_10">><<script>>setup.markChoiceVisited("d211137b-6ff7-486c-9dbe-9e5dfe768793:cc6");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <</nobr>>
<p>He stands behind you at the chopping board. His hands over yours, guiding the knife. His chest warm against your back. You can feel his heartbeat.</p><img src="./videos/activities/cooking_together_t2.jpg" alt="Guiding hands while cooking, standing behind, close" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "d211137b-6ff7-486c-9dbe-9e5dfe768793">> <<set $game_state.current_node = "af451fed-ab37-4e97-8617-f6ecdc9c2d48">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_cooking_together.t2") === -1) { vn.push("activity_cooking_together.t2"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Keep cooking->Location_Kitchen]]
<p>He holds the spoon up. You lean forward, lips parting. His eyes on your mouth. You taste.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> Perfect.</div></div><p>The word means more than the food.</p><img src="./videos/activities/cooking_together_t3.jpg" alt="Taste-testing from same spoon, intimate, kitchen" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "d211137b-6ff7-486c-9dbe-9e5dfe768793">> <<set $game_state.current_node = "e4d53dc0-00ed-4277-8e4e-02e99432c0f2">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_cooking_together.t3") === -1) { vn.push("activity_cooking_together.t3"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Plate up dinner->Location_Kitchen]]
<p>Sauce on your lip. His thumb comes up to wipe it. Lingers. His eyes drop from the sauce to your lips to your eyes. The kitchen goes very quiet.</p><img src="./videos/activities/cooking_together_t4.jpg" alt="Wiping sauce from lip with thumb, lingering, close faces" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "d211137b-6ff7-486c-9dbe-9e5dfe768793">> <<set $game_state.current_node = "84a13792-feff-46d5-9596-78a09b3f93f9">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_cooking_together.t4") === -1) { vn.push("activity_cooking_together.t4"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Serve dinner->Location_Kitchen]]
<p>You press your back against him while he stirs. He freezes. You roll your hips slowly — once, twice. His free hand drops to your hip and pulls you flush against him. The spoon goes still in the pot.</p><img src="./videos/activities/cooking_together_t5.gif" alt="Grinding against him from behind at stove, clothed, kitchen" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Something's burning.</div></div><p>Neither of you checks the stove.</p> <<set $game_state.current_canvas = "d211137b-6ff7-486c-9dbe-9e5dfe768793">> <<set $game_state.current_node = "1ce132e7-467d-45e4-8e0a-50d7285445d7">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_cooking_together.t5") === -1) { vn.push("activity_cooking_together.t5"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Turn down the heat. The stove, not you.->Location_Kitchen]]
<p>He licks a dot of sauce from your finger, then follows the trail of your skin further. You brace against the counter as your focus shifts off the stove.</p><video src="./videos/activities/cooking_together_t6.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "d211137b-6ff7-486c-9dbe-9e5dfe768793">> <<set $game_state.current_node = "712181ec-ffd8-4a10-ab3e-ca7aebff542d">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_cooking_together.t6") === -1) { vn.push("activity_cooking_together.t6"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Turn off a burner->Location_Kitchen]]
<p>You slide to your knees on the tile. He leans back on the counter, eyes closing as your mouth replaces any thought of dinner.</p><video src="./videos/activities/cooking_together_t7.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "d211137b-6ff7-486c-9dbe-9e5dfe768793">> <<set $game_state.current_node = "4ba11afe-cbdc-4bcc-9fcc-161137e49b14">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_cooking_together.t7") === -1) { vn.push("activity_cooking_together.t7"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Stir later->Location_Kitchen]]
<p>He lifts you onto the counter and slides into you. The pot sizzles on an empty burner; you both start laughing and turn it off between kisses.</p><video src="./videos/activities/cooking_together_t8.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "d211137b-6ff7-486c-9dbe-9e5dfe768793">> <<set $game_state.current_node = "323cc14f-29de-4064-903b-929836be1b87">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_cooking_together.t8") === -1) { vn.push("activity_cooking_together.t8"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 2.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Order takeout instead->Location_Kitchen]]
<<set _costs to [{"trait": "energy", "value": 20}]>>\ <<if setup.checkCostsAffordable(_costs)>>\ <<script>>setup.deductCosts("29919107-422e-4040-b02a-969191e0b3bd");<</script>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <p>Madison reviews your seating chart suggestions.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/madison.jpg" alt="Madison" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Madison:</strong> You're so helpful!</div></div><p>The gratitude is genuine. You want to disappear.</p><img src="./videos/activities/wedding_planning_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <p>The invitation says 'Ethan & Madison' in the font you helped pick. Your handwriting is on the envelopes.</p><img src="./videos/activities/wedding_planning_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <p>He passes you invitation samples. Your fingers overlap on the paper. The embossed names are a knife between you.</p><img src="./videos/activities/wedding_planning_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<else>> <p>Seating charts, RSVPs, invitation samples spread across the patio table. You're being a good sister.</p><img src="./videos/activities/wedding_planning_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The irony is not lost on either of you.</p> <</if>> <<set $game_state.current_canvas = "29919107-422e-4040-b02a-969191e0b3bd">> <<set $game_state.current_node = "d887c4e0-fc60-451c-bbe2-e4bcd64b3f11">> <<script>>setup.markCanvasTriggered("29919107-422e-4040-b02a-969191e0b3bd");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wedding_planning.base") === -1) { vn.push("activity_wedding_planning.base"); }<</script>> <<nobr>> <<link "Talk with him" "Canvas_activity_wedding_planning_Node_2">><<script>>advanceTime(3);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "lingering_touch_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('29919107-422e-4040-b02a-969191e0b3bd:cc0') ? '' : 'unlocked-choice'"> <<link "Get closer" "Canvas_activity_wedding_planning_Node_3">><<script>>setup.markChoiceVisited("29919107-422e-4040-b02a-969191e0b3bd:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<link "Help with RSVPs. Be a good sister." "Location_Backyard_&_Pool">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(45);<</script>><</link>><br> <</nobr>> <<else>> <div class="cost-blocked-message"> <p><<= setup.getCostBlockedMessage(_costs)>></p> </div> [[Back->Location_Backyard_&_Pool]] <</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <p>Madison asks your opinion on vows. He leaves the room. You help her anyway.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> Does she make you happy?</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> She makes things... simple.</div></div> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <p>You trace 'Ethan & Madison' with your fingertip. He watches your hand. Neither speaks.</p> <<else>> <p>You help sort RSVPs. His hand brushes yours over a stack.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Thanks for doing this.</div></div> <</if>> <<set $game_state.current_canvas = "29919107-422e-4040-b02a-969191e0b3bd">> <<set $game_state.current_node = "50679b1a-88c7-41dd-9960-989185e290cc">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wedding_planning.emotional") === -1) { vn.push("activity_wedding_planning.emotional"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Back to the plans->Location_Backyard_&_Pool]]
<p>Words can wait.</p> <<set $game_state.current_canvas = "29919107-422e-4040-b02a-969191e0b3bd">> <<set $game_state.current_node = "ee493d5f-b04a-44eb-a9c9-69331c5da1ca">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wedding_planning.physical") === -1) { vn.push("activity_wedding_planning.physical"); }<</script>> <<nobr>> <<link "Let your fingers overlap on the invitation." "Canvas_activity_wedding_planning_Node_4">><<script>>advanceTime(3);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "flirt_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('29919107-422e-4040-b02a-969191e0b3bd:cc1') ? '' : 'unlocked-choice'"> <<link "Ask what kind of wedding he really wants." "Canvas_activity_wedding_planning_Node_5">><<script>>setup.markChoiceVisited("29919107-422e-4040-b02a-969191e0b3bd:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "kiss_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('29919107-422e-4040-b02a-969191e0b3bd:cc2') ? '' : 'unlocked-choice'"> <<link "He puts his head in his hands. Take his hand." "Canvas_activity_wedding_planning_Node_6">><<script>>setup.markChoiceVisited("29919107-422e-4040-b02a-969191e0b3bd:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "grind_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('29919107-422e-4040-b02a-969191e0b3bd:cc3') ? '' : 'unlocked-choice'"> <<link "Slide onto his lap. Roll your hips." "Canvas_activity_wedding_planning_Node_7">><<script>>setup.markChoiceVisited("29919107-422e-4040-b02a-969191e0b3bd:cc3");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "manual_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('29919107-422e-4040-b02a-969191e0b3bd:cc4') ? '' : 'unlocked-choice'"> <<link "Under the table, under the RSVPs." "Canvas_activity_wedding_planning_Node_8">><<script>>setup.markChoiceVisited("29919107-422e-4040-b02a-969191e0b3bd:cc4");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "oral_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('29919107-422e-4040-b02a-969191e0b3bd:cc5') ? '' : 'unlocked-choice'"> <<link "Kneel between his chair and the table." "Canvas_activity_wedding_planning_Node_9">><<script>>setup.markChoiceVisited("29919107-422e-4040-b02a-969191e0b3bd:cc5");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "sex_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('29919107-422e-4040-b02a-969191e0b3bd:cc6') ? '' : 'unlocked-choice'"> <<link "Sweep the invitations aside." "Canvas_activity_wedding_planning_Node_10">><<script>>setup.markChoiceVisited("29919107-422e-4040-b02a-969191e0b3bd:cc6");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <</nobr>>
<p>Your fingers overlap on a sample. He doesn't pull away. You trace the embossed 'Ethan & Madison' with your fingertip. The name is a knife between you.</p><img src="./videos/activities/wedding_planning_t2.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "29919107-422e-4040-b02a-969191e0b3bd">> <<set $game_state.current_node = "419b3a25-229f-4017-b1db-2f1793bafa3f">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wedding_planning.t2") === -1) { vn.push("activity_wedding_planning.t2"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Keep planning->Location_Backyard_&_Pool]]
<p>His eyes meet yours. Long pause.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Small. Private. Someone who...</div></div><p>He doesn't finish. Doesn't need to.</p><img src="./videos/activities/wedding_planning_t3.jpg" alt="Loaded eye contact, unfinished sentence, emotional" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "29919107-422e-4040-b02a-969191e0b3bd">> <<set $game_state.current_node = "7e82faa4-0ef2-4d54-86b1-470bfb4a6c78">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wedding_planning.t3") === -1) { vn.push("activity_wedding_planning.t3"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Change the subject->Location_Backyard_&_Pool]]
<p>He drops his pen. Puts his head in his hands. You take his hand. Both know he's not talking about centerpieces. Forehead to forehead.</p><img src="./videos/activities/wedding_planning_t4.jpg" alt="Comfort, foreheads touching, emotional, wedding plans visible" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "29919107-422e-4040-b02a-969191e0b3bd">> <<set $game_state.current_node = "918e5baa-5e2c-406f-ac52-005d194d9dc3">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wedding_planning.t4") === -1) { vn.push("activity_wedding_planning.t4"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Give him space->Location_Backyard_&_Pool]]
<p>You slide onto his lap at the dining table. RSVPs scatter. His hands grip your thighs through your sundress as you roll your hips against him — slow, deliberate, while seating charts flutter to the floor.</p><video src="./videos/activities/wedding_planning_t5.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>His fiancée's handwriting is on every card. You grind harder. He lets you.</p> <<set $game_state.current_canvas = "29919107-422e-4040-b02a-969191e0b3bd">> <<set $game_state.current_node = "771f32e4-e6ef-4ab7-8710-60b48ffe53c7">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wedding_planning.t5") === -1) { vn.push("activity_wedding_planning.t5"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Pick up the RSVPs.->Location_Backyard_&_Pool]]
<p>He slides a stack of RSVP cards aside and finds you under the table edge. Your breath hitches; the plans blur.</p><video src="./videos/activities/wedding_planning_t6.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "29919107-422e-4040-b02a-969191e0b3bd">> <<set $game_state.current_node = "4e38b1f7-c877-4703-bd4c-09ad4bacdea0">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wedding_planning.t6") === -1) { vn.push("activity_wedding_planning.t6"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Straighten the stack->Location_Backyard_&_Pool]]
<p>You kneel between his knees as he slides back the chair. Paper rustles above; something else happens below.</p><video src="./videos/activities/wedding_planning_t7.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "29919107-422e-4040-b02a-969191e0b3bd">> <<set $game_state.current_node = "c84eee92-a0a1-4562-ba61-c34962fcaaa9">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wedding_planning.t7") === -1) { vn.push("activity_wedding_planning.t7"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Push in the chair->Location_Backyard_&_Pool]]
<p>Invitations scatter to the floor as he sweeps the table. You climb up and pull him with you, paper crunching underfoot.</p><video src="./videos/activities/wedding_planning_t8.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "29919107-422e-4040-b02a-969191e0b3bd">> <<set $game_state.current_node = "9ca9f176-8365-465f-afc9-fad2e8240f6d">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wedding_planning.t8") === -1) { vn.push("activity_wedding_planning.t8"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 2.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Gather the cards later->Location_Backyard_&_Pool]]
<<set _costs to [{"trait": "energy", "value": 30}]>>\ <<if setup.checkCostsAffordable(_costs)>>\ <<script>>setup.deductCosts("6b9f0235-347e-4ea5-8000-fc1f55f5d973");<</script>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <p>Madison sunbathes on the far lounger. He's in the pool. You're in a bikini. The geometry is unbearable.</p><img src="./videos/activities/pool_time_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>She adjusts her sunglasses. He treads water. You stand at the edge of everything.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <p>He's already in the water when you come out. Watches you walk to the edge.</p><img src="./videos/activities/pool_time_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Took you long enough.</div></div> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <p>Summer heat. The pool shimmers. You catch him looking at you in your swimsuit. He doesn't pretend he wasn't.</p><img src="./videos/activities/pool_time_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<else>> <p>Summer heat. The pool shimmers. You're in a swimsuit, he's in board shorts. The privacy fence means no one can see.</p><img src="./videos/activities/pool_time_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> The water's perfect.</div></div> <</if>> <<set $game_state.current_canvas = "6b9f0235-347e-4ea5-8000-fc1f55f5d973">> <<set $game_state.current_node = "3c3e3d77-b760-4f84-bea9-1d4e5dfe6cd3">> <<script>>setup.markCanvasTriggered("6b9f0235-347e-4ea5-8000-fc1f55f5d973");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_pool_time.base") === -1) { vn.push("activity_pool_time.base"); }<</script>> <<nobr>> <<link "Talk with him" "Canvas_activity_pool_time_Node_2">><<script>>advanceTime(3);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "lingering_touch_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('6b9f0235-347e-4ea5-8000-fc1f55f5d973:cc0') ? '' : 'unlocked-choice'"> <<link "Get closer" "Canvas_activity_pool_time_Node_3">><<script>>setup.markChoiceVisited("6b9f0235-347e-4ea5-8000-fc1f55f5d973:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<link "Swim laps, lounge on chairs." "Location_Backyard_&_Pool">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(45);<</script>><</link>><br> <</nobr>> <<else>> <div class="cost-blocked-message"> <p><<= setup.getCostBlockedMessage(_costs)>></p> </div> [[Back->Location_Backyard_&_Pool]] <</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <p>Madison waves from her lounger. You wave back. The smile costs everything.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <p>Floating on your backs, staring at the sky.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> Do you ever think about what happens after I leave?</div></div> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <p>He tosses you a towel. His hand stays on yours a beat too long.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> You're getting burned.</div></div> <<else>> <p>You float. He floats. The water holds you both up. Easy silence.</p> <</if>> <<set $game_state.current_canvas = "6b9f0235-347e-4ea5-8000-fc1f55f5d973">> <<set $game_state.current_node = "28f7e84b-5951-454c-baf5-5f8ede4ab927">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_pool_time.emotional") === -1) { vn.push("activity_pool_time.emotional"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Dry off->Location_Backyard_&_Pool]]
<p>Words can wait.</p> <<set $game_state.current_canvas = "6b9f0235-347e-4ea5-8000-fc1f55f5d973">> <<set $game_state.current_node = "095f1b1c-2249-45c1-bab5-49895955ad10">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_pool_time.physical") === -1) { vn.push("activity_pool_time.physical"); }<</script>> <<nobr>> <<link "Let him put sunscreen on your back." "Canvas_activity_pool_time_Node_4">><<script>>advanceTime(3);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "flirt_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('6b9f0235-347e-4ea5-8000-fc1f55f5d973:cc1') ? '' : 'unlocked-choice'"> <<link "Wrap your legs around him in the water." "Canvas_activity_pool_time_Node_5">><<script>>setup.markChoiceVisited("6b9f0235-347e-4ea5-8000-fc1f55f5d973:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "kiss_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('6b9f0235-347e-4ea5-8000-fc1f55f5d973:cc2') ? '' : 'unlocked-choice'"> <<link "Night swimming. Underwater lights." "Canvas_activity_pool_time_Node_6">><<script>>setup.markChoiceVisited("6b9f0235-347e-4ea5-8000-fc1f55f5d973:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "grind_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('6b9f0235-347e-4ea5-8000-fc1f55f5d973:cc3') ? '' : 'unlocked-choice'"> <<link "Wrap around him in the water. Grind through thin fabric." "Canvas_activity_pool_time_Node_7">><<script>>setup.markChoiceVisited("6b9f0235-347e-4ea5-8000-fc1f55f5d973:cc3");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "manual_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('6b9f0235-347e-4ea5-8000-fc1f55f5d973:cc4') ? '' : 'unlocked-choice'"> <<link "On the lounger. Under the bikini." "Canvas_activity_pool_time_Node_8">><<script>>setup.markChoiceVisited("6b9f0235-347e-4ea5-8000-fc1f55f5d973:cc4");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "oral_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('6b9f0235-347e-4ea5-8000-fc1f55f5d973:cc5') ? '' : 'unlocked-choice'"> <<link "Slide down between his legs on the lounger." "Canvas_activity_pool_time_Node_9">><<script>>setup.markChoiceVisited("6b9f0235-347e-4ea5-8000-fc1f55f5d973:cc5");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "sex_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('6b9f0235-347e-4ea5-8000-fc1f55f5d973:cc6') ? '' : 'unlocked-choice'"> <<link "In the water. Quiet splashes." "Canvas_activity_pool_time_Node_10">><<script>>setup.markChoiceVisited("6b9f0235-347e-4ea5-8000-fc1f55f5d973:cc6");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <</nobr>>
<p>His hands on your back. Slow — shoulders, spine, the small of your back. Fingertips at the bikini line. You arch into his touch.</p><img src="./videos/activities/pool_time_t2.jpg" alt="Applying sunscreen on back, lingering hands, pool" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "6b9f0235-347e-4ea5-8000-fc1f55f5d973">> <<set $game_state.current_node = "58ce5107-7d74-4537-a97a-d6016b57523f">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_pool_time.t2") === -1) { vn.push("activity_pool_time.t2"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Go for a swim->Location_Backyard_&_Pool]]
<p>Splash war escalates. You wrap your legs around him for 'balance.' His hands on your thighs, holding you up. Wet skin on wet skin. Faces inches apart.</p><img src="./videos/activities/pool_time_t3.jpg" alt="Pool play, bodies pressed together, wet, playful" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>'Balance' is a fiction and you both know it.</p> <<set $game_state.current_canvas = "6b9f0235-347e-4ea5-8000-fc1f55f5d973">> <<set $game_state.current_node = "19adde90-361d-4e63-86cb-2cfeab40a834">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_pool_time.t3") === -1) { vn.push("activity_pool_time.t3"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Dry off->Location_Backyard_&_Pool]]
<p>Night. Pool lights shimmer blue beneath the surface. He pulls you against him. Water provides cover. Your bikini top loosens. His fingers find skin.</p><img src="./videos/activities/pool_time_t4.jpg" alt="Night pool, underwater touching, intimate, blue lights" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> Anyone could see us.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> No they can't.</div></div> <<set $game_state.current_canvas = "6b9f0235-347e-4ea5-8000-fc1f55f5d973">> <<set $game_state.current_node = "d0c0680d-cbfa-4f0a-89d7-efdaa13fc3df">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_pool_time.t4") === -1) { vn.push("activity_pool_time.t4"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Get out of the pool->Location_Backyard_&_Pool]]
<p>You wrap your legs around him in the shallow end. Wet skin on wet skin, thin swimwear the only barrier. His hands grip under your thighs as you grind against him — the water making everything slow, weightless, electric.</p><video src="./videos/activities/pool_time_t5.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>Water laps at the pool edge. His boardshorts hide nothing. Your bikini bottom hides less.</p> <<set $game_state.current_canvas = "6b9f0235-347e-4ea5-8000-fc1f55f5d973">> <<set $game_state.current_node = "9b6f9a95-6646-4de8-ba9a-f4da160e8841">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_pool_time.t5") === -1) { vn.push("activity_pool_time.t5"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Unwrap. Float apart.->Location_Backyard_&_Pool]]
<p>On the lounger, his hand slips under your bikini bottom. The night air is warm; your skin warmer.</p><video src="./videos/activities/pool_time_t6.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "6b9f0235-347e-4ea5-8000-fc1f55f5d973">> <<set $game_state.current_node = "a34a96f3-2aed-43d0-b0c9-730ea298ecf2">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_pool_time.t6") === -1) { vn.push("activity_pool_time.t6"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Adjust your suit->Location_Backyard_&_Pool]]
<p>You slide down between his legs on the lounger, the fence hiding you from the world. Stars watch without judging.</p><video src="./videos/activities/pool_time_t7.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "6b9f0235-347e-4ea5-8000-fc1f55f5d973">> <<set $game_state.current_node = "811ea051-4b41-4fed-a597-db366eab779b">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_pool_time.t7") === -1) { vn.push("activity_pool_time.t7"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Catch your breath->Location_Backyard_&_Pool]]
<p>In the water or on the lounger, you take him inside you. Quiet splashes, quiet gasps, a private universe in your backyard.</p><video src="./videos/activities/pool_time_t8.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "6b9f0235-347e-4ea5-8000-fc1f55f5d973">> <<set $game_state.current_node = "24f5e697-5167-48bd-811b-6e7ab3f36c9d">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_pool_time.t8") === -1) { vn.push("activity_pool_time.t8"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 2.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Wrap in a towel->Location_Backyard_&_Pool]]
<<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <p>He made lunch for three. Madison's plate is at the far end. You sit where you always sit — next to him.</p><img src="./videos/activities/lunch_together_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> She likes your grilled cheese recipe.</div></div><p>You smile. The cheese tastes like chalk in your mouth.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <p>Lunch for two without being asked. This is routine now. His knee against yours is deliberate.</p><img src="./videos/activities/lunch_together_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Same as yesterday?</div></div><p>He means the sandwich. He also means everything else.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <p>Grilled cheese, extra sharp. He remembered. His eyes linger on your mouth when you take the first bite.</p><img src="./videos/activities/lunch_together_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The kitchen feels different now. Every shared meal is a small confession.</p> <<else>> <p>Making sandwiches together. He remembers your favorite — grilled cheese, extra sharp cheddar.</p><img src="./videos/activities/lunch_together_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Their knees touch under the breakfast bar. Neither moves away.</p> <</if>> <<set $game_state.current_canvas = "3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054">> <<set $game_state.current_node = "1138eb8c-ff8f-4a8c-b1db-f7d5fbf0c224">> <<script>>setup.markCanvasTriggered("3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_lunch_together.base") === -1) { vn.push("activity_lunch_together.base"); }<</script>> <<nobr>> <<link "Talk with him" "Canvas_activity_lunch_together_Node_2">><<script>>advanceTime(3);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "lingering_touch_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054:cc0') ? '' : 'unlocked-choice'"> <<link "Get closer" "Canvas_activity_lunch_together_Node_3">><<script>>setup.markChoiceVisited("3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<link "Casual lunch." "Location_Kitchen">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", 5.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(45);<</script>><</link>><br> <</nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <p>Madison texts him during lunch. He puts the phone face-down.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> It's nothing.</div></div><p>Everything is nothing now. You eat in silence.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <p>You talk about small things. Every mundane word carries the weight of what you can't say in daylight.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> This is nice. Just... this.</div></div> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <p>He asks how you're sleeping. You both know the answer.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> Better than I expected.</div></div> <<else>> <p>Easy lunch conversation. He asks about your friends, your job.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> You always were a terrible liar.</div></div> <</if>> <<set $game_state.current_canvas = "3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054">> <<set $game_state.current_node = "cc955245-1cf0-4a7b-bd4b-17c9a981dc02">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_lunch_together.emotional") === -1) { vn.push("activity_lunch_together.emotional"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 2.0, true, null);setup.applyAndNotifyTrait("player", null, "energy", "add", 5.0, true, null);setup.showEffectNotification();<</script>> [[Clear the plates->Location_Kitchen]]
<p>Words can wait.</p> <<set $game_state.current_canvas = "3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054">> <<set $game_state.current_node = "44778aee-fd8f-496f-9681-dbbbdadfe20e">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_lunch_together.physical") === -1) { vn.push("activity_lunch_together.physical"); }<</script>> <<nobr>> <<link "Let your knee press against his. Leave it there." "Canvas_activity_lunch_together_Node_4">><<script>>advanceTime(3);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "flirt_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054:cc1') ? '' : 'unlocked-choice'"> <<link "Offer a bite from your fork. Watch his eyes." "Canvas_activity_lunch_together_Node_5">><<script>>setup.markChoiceVisited("3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "kiss_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054:cc2') ? '' : 'unlocked-choice'"> <<link "Hop onto the counter. Pull him close." "Canvas_activity_lunch_together_Node_6">><<script>>setup.markChoiceVisited("3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "grind_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054:cc3') ? '' : 'unlocked-choice'"> <<link "Straddle his lap on the stool." "Canvas_activity_lunch_together_Node_7">><<script>>setup.markChoiceVisited("3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054:cc3");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "manual_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054:cc4') ? '' : 'unlocked-choice'"> <<link "Take his hand under the counter edge." "Canvas_activity_lunch_together_Node_8">><<script>>setup.markChoiceVisited("3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054:cc4");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "oral_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054:cc5') ? '' : 'unlocked-choice'"> <<link "Kneel on the kitchen mat." "Canvas_activity_lunch_together_Node_9">><<script>>setup.markChoiceVisited("3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054:cc5");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "sex_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054:cc6') ? '' : 'unlocked-choice'"> <<link "Pull him between your knees. Now." "Canvas_activity_lunch_together_Node_10">><<script>>setup.markChoiceVisited("3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054:cc6");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <</nobr>>
<p>Knees pressed together under the counter. The warmth of his leg against yours. He offers a bite from his plate. You take it.</p><img src="./videos/activities/lunch_together_t2.jpg" alt="Legs touching under counter, sharing food" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054">> <<set $game_state.current_node = "73a35acf-c633-497d-9ff9-ddb8c5186915">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_lunch_together.t2") === -1) { vn.push("activity_lunch_together.t2"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Clear the plates->Location_Kitchen]]
<p>He offers a bite from his fork. You lean forward, eyes on his. Take it slowly. His eyes drop to your lips. The air between you thickens.</p><img src="./videos/activities/lunch_together_t3.jpg" alt="Feeding from fork, eye contact, intimate" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054">> <<set $game_state.current_node = "a0ff500f-bfb0-4f50-9922-61fb789aa0f1">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_lunch_together.t3") === -1) { vn.push("activity_lunch_together.t3"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Clear the plates->Location_Kitchen]]
<p>You hop onto the counter. He moves between your legs. Hands on your thighs. The sandwich is irrelevant. His mouth finds yours.</p><video src="./videos/activities/lunch_together_t4.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054">> <<set $game_state.current_node = "16de33c4-89e0-4299-86b4-4a7eba9a3fea">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_lunch_together.t4") === -1) { vn.push("activity_lunch_together.t4"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Eventually eat->Location_Kitchen]]
<p>You straddle his lap on the kitchen stool. Your skirt rides up. His hands grip the outside of your thighs as you rock against him — slow circles, lunch forgotten, the stool creaking under both of you.</p><video src="./videos/activities/lunch_together_t5.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> The sandwich—</div></div><p>You grind down harder. The sandwich can wait.</p> <<set $game_state.current_canvas = "3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054">> <<set $game_state.current_node = "1eef3d81-26e3-4261-9519-9942a9c309c5">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_lunch_together.t5") === -1) { vn.push("activity_lunch_together.t5"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Climb off. Fix your skirt.->Location_Kitchen]]
<p>You perch on the stool and take his hand under the counter edge. No one would know — if anyone were here to see.</p><video src="./videos/activities/lunch_together_t6.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054">> <<set $game_state.current_node = "37db03f5-56fe-417d-a10d-4d8c48fae12f">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_lunch_together.t6") === -1) { vn.push("activity_lunch_together.t6"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Finish lunch eventually->Location_Kitchen]]
<p>You kneel on the kitchen mat, bracing one hand on the cabinet door as you make quick work of his resolve.</p><video src="./videos/activities/lunch_together_t7.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054">> <<set $game_state.current_node = "d1171ea1-ae3c-419d-bf6e-0b1c858916c2">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_lunch_together.t7") === -1) { vn.push("activity_lunch_together.t7"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Stand and grin->Location_Kitchen]]
<p>You pull him between your knees and the world tilts. It's fast, hungry, and reckless in the middle of the day.</p><video src="./videos/activities/lunch_together_t8.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "3b87dda3-aeb5-4ba3-b91b-d9a73b5ce054">> <<set $game_state.current_node = "d46356dc-25de-4beb-887c-b07fd1e6bb8c">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_lunch_together.t8") === -1) { vn.push("activity_lunch_together.t8"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 2.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Wipe the counter->Location_Kitchen]]
<<set _costs to [{"trait": "energy", "value": 25}]>>\ <<if setup.checkCostsAffordable(_costs)>>\ <<script>>setup.deductCosts("c449f114-b3ac-4279-8a5c-5220fcb4a37c");<</script>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <p>Madison's dry cleaning hangs on the door handle. You fold it without being asked. He watches you fold his fiancée's clothes and something in his face breaks a little.</p><img src="./videos/activities/helping_chores_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> You don't have to do that.</div></div><p>But you do it anyway. Because what else can you do.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <p>You're folding his shirts. He comes up behind you and wraps his arms around your waist.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> You don't have to do this.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> I want to.</div></div><p>Neither of you means the laundry.</p><img src="./videos/activities/helping_chores_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The house smells like detergent and him. Domestic in a way that aches.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <p>Laundry, dishes, the domestic dance. His hand brushes yours at the sink more than necessary. The house feels smaller since last night.</p><img src="./videos/activities/helping_chores_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Thanks for helping.</div></div><p>His eyes say more than thanks. Your hips bump at the sink. Neither apologizes.</p> <<else>> <p>Laundry, dishes, general tidying. Domestic teamwork. You're helping because you're a guest who doesn't want to be useless. He appreciates it.</p><img src="./videos/activities/helping_chores_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Easy conversation about old times while folding towels. He washes, you dry. Your hips bump at the sink.</p> <</if>> <<set $game_state.current_canvas = "c449f114-b3ac-4279-8a5c-5220fcb4a37c">> <<set $game_state.current_node = "a5d3ac91-0e9b-42a1-97fe-cb18cea06376">> <<script>>setup.markCanvasTriggered("c449f114-b3ac-4279-8a5c-5220fcb4a37c");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_helping_chores.base") === -1) { vn.push("activity_helping_chores.base"); }<</script>> <<nobr>> <<link "Talk while you work" "Canvas_activity_helping_chores_Node_2">><<script>>advanceTime(3);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "lingering_touch_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('c449f114-b3ac-4279-8a5c-5220fcb4a37c:cc0') ? '' : 'unlocked-choice'"> <<link "Get closer" "Canvas_activity_helping_chores_Node_3">><<script>>setup.markChoiceVisited("c449f114-b3ac-4279-8a5c-5220fcb4a37c:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<link "Fold laundry, easy conversation." "Location_Living_Room">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(45);<</script>><</link>><br> <</nobr>> <<else>> <div class="cost-blocked-message"> <p><<= setup.getCostBlockedMessage(_costs)>></p> </div> [[Back->Location_Living_Room]] <</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <p>She left her wedding shoes by the door. You almost tripped on them this morning.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I saw.</div></div><p>He doesn't apologize for them being there. He doesn't move them. The shoes sit between you like everything else unsaid.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <p>Folding towels side by side. The routine feels dangerously domestic. Like this could be your life.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> What would mornings look like if things were different?</div></div><p>He stops folding. Stares at the towel in his hands like it holds the answer. It doesn't. But the question hangs in the warm air between you.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <p>You sort laundry in comfortable silence. His shirt is warm from the dryer.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> It's nice having help.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> It's nice being here.</div></div><p>Both of you meaning more than the words carry.</p> <<else>> <p>Easy banter while you work. He tells you about the neighbor's cat that keeps getting into the yard. You tell him about your apartment's terrible plumbing.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Sounds like you need a handyman.</div></div><p>The offer hangs in the air, half-joke, half-something else.</p> <</if>> <<set $game_state.current_canvas = "c449f114-b3ac-4279-8a5c-5220fcb4a37c">> <<set $game_state.current_node = "d7e10b64-a6ff-4612-99c5-436743e4b304">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_helping_chores.emotional") === -1) { vn.push("activity_helping_chores.emotional"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Back to folding->Location_Living_Room]]
<p>The chores can wait.</p> <<set $game_state.current_canvas = "c449f114-b3ac-4279-8a5c-5220fcb4a37c">> <<set $game_state.current_node = "355afc2a-282a-407e-9de9-367e7b111387">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_helping_chores.physical") === -1) { vn.push("activity_helping_chores.physical"); }<</script>> <<nobr>> <<link "Hold his shirt to your face. Breathe in." "Canvas_activity_helping_chores_Node_4">><<script>>advanceTime(3);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "flirt_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('c449f114-b3ac-4279-8a5c-5220fcb4a37c:cc1') ? '' : 'unlocked-choice'"> <<link "Flick him with the dish towel. Let him chase you." "Canvas_activity_helping_chores_Node_5">><<script>>setup.markChoiceVisited("c449f114-b3ac-4279-8a5c-5220fcb4a37c:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "kiss_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('c449f114-b3ac-4279-8a5c-5220fcb4a37c:cc2') ? '' : 'unlocked-choice'"> <<link "Stay at the sink. Let him come up behind you." "Canvas_activity_helping_chores_Node_6">><<script>>setup.markChoiceVisited("c449f114-b3ac-4279-8a5c-5220fcb4a37c:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "grind_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('c449f114-b3ac-4279-8a5c-5220fcb4a37c:cc3') ? '' : 'unlocked-choice'"> <<link "Pin him against the washing machine. Grind." "Canvas_activity_helping_chores_Node_7">><<script>>setup.markChoiceVisited("c449f114-b3ac-4279-8a5c-5220fcb4a37c:cc3");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "manual_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('c449f114-b3ac-4279-8a5c-5220fcb4a37c:cc4') ? '' : 'unlocked-choice'"> <<link "Pull him to the couch. Hide under the laundry." "Canvas_activity_helping_chores_Node_8">><<script>>setup.markChoiceVisited("c449f114-b3ac-4279-8a5c-5220fcb4a37c:cc4");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "oral_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('c449f114-b3ac-4279-8a5c-5220fcb4a37c:cc5') ? '' : 'unlocked-choice'"> <<link "Kneel on the rug. Kiss lower." "Canvas_activity_helping_chores_Node_9">><<script>>setup.markChoiceVisited("c449f114-b3ac-4279-8a5c-5220fcb4a37c:cc5");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "sex_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('c449f114-b3ac-4279-8a5c-5220fcb4a37c:cc6') ? '' : 'unlocked-choice'"> <<link "Push him into the cushions." "Canvas_activity_helping_chores_Node_10">><<script>>setup.markChoiceVisited("c449f114-b3ac-4279-8a5c-5220fcb4a37c:cc6");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <</nobr>>
<p>You hold one of his shirts — press it to your nose.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> Still use the same detergent.</div></div><p>He watches you inhale. Doesn't say anything. Doesn't need to.</p><img src="./videos/activities/helping_chores_t2.jpg" alt="Smelling his shirt, intimate domestic moment" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "c449f114-b3ac-4279-8a5c-5220fcb4a37c">> <<set $game_state.current_node = "52273708-c8a0-4b2e-9f0f-e33bf36378c4">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_helping_chores.t2") === -1) { vn.push("activity_helping_chores.t2"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Keep folding->Location_Living_Room]]
<p>Dish towel flick fight. He chases you around the island. You squeal. He catches you from behind. Both breathing hard. His arms around you. Neither moves.</p><img src="./videos/activities/helping_chores_t3.gif" alt="Playful chasing, catching from behind, laughing" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Then you squirm free, laughing.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> You cheat.</div></div> <<set $game_state.current_canvas = "c449f114-b3ac-4279-8a5c-5220fcb4a37c">> <<set $game_state.current_node = "1efc7bf5-d260-43b3-a811-c05ba2927558">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_helping_chores.t3") === -1) { vn.push("activity_helping_chores.t3"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Back to chores->Location_Living_Room]]
<p>You're at the sink. He comes up behind you, reaching for the faucet. Doesn't step back. His breath on your neck. Hands sliding from the faucet to your waist.</p><img src="./videos/activities/helping_chores_t4.jpg" alt="Behind her at the sink, kissing her neck, intimate" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> You missed a spot.</div></div><p>His lips find your shoulder. The dishes can wait.</p> <<set $game_state.current_canvas = "c449f114-b3ac-4279-8a5c-5220fcb4a37c">> <<set $game_state.current_node = "bbda83fe-a4de-4381-920e-0925b841f6a9">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_helping_chores.t4") === -1) { vn.push("activity_helping_chores.t4"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Finish the dishes->Location_Living_Room]]
<p>You back him against the washing machine mid-cycle. The vibration hums through both of you as you press your hips into his. His hands find your waist through your t-shirt, pulling you into the rhythm.</p><video src="./videos/activities/helping_chores_t5.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>Clean laundry tumbles to the floor. Neither of you picks it up. The spin cycle does the work for you.</p> <<set $game_state.current_canvas = "c449f114-b3ac-4279-8a5c-5220fcb4a37c">> <<set $game_state.current_node = "fd85fc14-5fe1-4435-a896-520777f1ec4f">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_helping_chores.t5") === -1) { vn.push("activity_helping_chores.t5"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Step back. Refold everything.->Location_Living_Room]]
<p>You tug him to the couch, a shirt draped over your lap like camouflage. His hand disappears under the fabric and you forget what chore you were doing.</p><video src="./videos/activities/helping_chores_t6.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "c449f114-b3ac-4279-8a5c-5220fcb4a37c">> <<set $game_state.current_node = "f9f2c3db-c412-438d-a62b-f2b04f30cc0b">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_helping_chores.t6") === -1) { vn.push("activity_helping_chores.t6"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Half-folded, fully distracted->Location_Living_Room]]
<p>You kneel on the rug and kiss lower until talking stops. The TV hums to itself, ignored.</p><video src="./videos/activities/helping_chores_t7.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "c449f114-b3ac-4279-8a5c-5220fcb4a37c">> <<set $game_state.current_node = "22d266cd-adee-408b-8b22-11a220a622f9">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_helping_chores.t7") === -1) { vn.push("activity_helping_chores.t7"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Stand, breathless->Location_Living_Room]]
<p>He pushes you back into the cushions and the world narrows to you and him. The laundry can absolutely wait.</p><video src="./videos/activities/helping_chores_t8.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "c449f114-b3ac-4279-8a5c-5220fcb4a37c">> <<set $game_state.current_node = "43149d74-3c00-48fa-b597-adf2542dd882">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_helping_chores.t8") === -1) { vn.push("activity_helping_chores.t8"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 2.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Fold later->Location_Living_Room]]
<<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <p>He's on the phone when you walk in. Hangs up fast. 'That was Madison.' One mug on the counter. Yours isn't made.</p><img src="./videos/activities/breakfast_ethan_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> She's asking about the seating chart.</div></div><p>You make your own coffee. The silence has a different weight now.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <p>He made your favorite. Doesn't say why. Your mug is exactly right — he's memorized it. Sits closer than he used to.</p><img src="./videos/activities/breakfast_ethan_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Couldn't sleep after you left.</div></div><p>His knee finds yours under the counter. Neither of you pretends it's an accident anymore.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <p>He's already looking when you walk in. Looks away too fast. Your mug is ready — made exactly how you like it.</p><img src="./videos/activities/breakfast_ethan_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Hey.</div></div><p>Just 'hey.' But the way he says it — like the word is carrying everything he can't say out loud.</p> <<else>> <p>Morning light through the kitchen window. He's at the counter, coffee already made. Two mugs. He remembered how you take yours.</p><img src="./videos/activities/breakfast_ethan_base.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Morning. Sleep okay?</div></div><p>Cereal, toast, the easy rhythm of two people who know each other's habits.</p> <</if>> <<set $game_state.current_canvas = "1767ce5e-3704-422d-aa53-4f769466789c">> <<set $game_state.current_node = "e419887b-ca93-4d9e-b9c1-1274faac75f3">> <<script>>setup.markCanvasTriggered("1767ce5e-3704-422d-aa53-4f769466789c");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_breakfast_ethan.base") === -1) { vn.push("activity_breakfast_ethan.base"); }<</script>> <<nobr>> <<link "Talk with him over coffee" "Canvas_activity_breakfast_ethan_Node_2">><<script>>advanceTime(3);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "lingering_touch_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('1767ce5e-3704-422d-aa53-4f769466789c:cc0') ? '' : 'unlocked-choice'"> <<link "Get closer" "Canvas_activity_breakfast_ethan_Node_3">><<script>>setup.markChoiceVisited("1767ce5e-3704-422d-aa53-4f769466789c:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<link "Just eat. Easy silence." "Location_Kitchen">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 3.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", 5.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(45);<</script>><</link>><br> <</nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "madison_arrived", "operator": "is_true"}]})>> <p>You stir your coffee longer than necessary. The question sits between you like a third person at the table.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> How's the planning going?</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Fine. It's fine.</div></div><p>'Fine' is doing a lot of heavy lifting in that sentence. You don't push. The silence says what neither of you will.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_night_complete", "operator": "is_true"}]})>> <p>The coffee gets cold while you talk. Really talk. About what this is, what it means, whether either of you can stop.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I keep thinking about what happens when you leave.</div></div><p>You don't have an answer. Neither does he. But the honesty feels like its own kind of intimacy.</p> <<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "first_kiss_done", "operator": "is_true"}]})>> <p>You talk around it for ten minutes before he sets down his mug.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Are we going to pretend last night didn't happen?</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> Do you want to pretend?</div></div><p>He looks at you. Really looks.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> No.</div></div><p>The word is quiet but it fills the kitchen.</p> <<else>> <p>Easy conversation about nothing important. Old memories, shared jokes, the kind of talk that flows between people with history.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Remember when Mom burned the turkey that one Thanksgiving?</div></div><p>You laugh. He laughs. For a moment it's just two people who grew up together, catching up. Almost simple.</p> <</if>> <<set $game_state.current_canvas = "1767ce5e-3704-422d-aa53-4f769466789c">> <<set $game_state.current_node = "d655bee5-4c66-4528-9e98-a3f52f826a4b">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_breakfast_ethan.emotional") === -1) { vn.push("activity_breakfast_ethan.emotional"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "trust", "add", 2.0, true, null);setup.applyAndNotifyTrait("player", null, "energy", "add", 5.0, true, null);setup.showEffectNotification();<</script>> [[Finish your coffee->Location_Kitchen]]
<p>The coffee can wait.</p> <<set $game_state.current_canvas = "1767ce5e-3704-422d-aa53-4f769466789c">> <<set $game_state.current_node = "12e7d686-018c-4a39-ab35-f38210bf858b">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_breakfast_ethan.physical") === -1) { vn.push("activity_breakfast_ethan.physical"); }<</script>> <<nobr>> <<link "Sit closer than necessary. Let your knee touch his." "Canvas_activity_breakfast_ethan_Node_4">><<script>>advanceTime(3);<</script>><</link>><br> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "flirt_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('1767ce5e-3704-422d-aa53-4f769466789c:cc1') ? '' : 'unlocked-choice'"> <<link "\"You always look good in the morning.\" Hold eye contact." "Canvas_activity_breakfast_ethan_Node_5">><<script>>setup.markChoiceVisited("1767ce5e-3704-422d-aa53-4f769466789c:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "kiss_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('1767ce5e-3704-422d-aa53-4f769466789c:cc2') ? '' : 'unlocked-choice'"> <<link "Come up behind him. Arms around his waist. Kiss his neck." "Canvas_activity_breakfast_ethan_Node_6">><<script>>setup.markChoiceVisited("1767ce5e-3704-422d-aa53-4f769466789c:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "grind_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('1767ce5e-3704-422d-aa53-4f769466789c:cc3') ? '' : 'unlocked-choice'"> <<link "Press against him at the counter. Rock your hips." "Canvas_activity_breakfast_ethan_Node_7">><<script>>setup.markChoiceVisited("1767ce5e-3704-422d-aa53-4f769466789c:cc3");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "manual_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('1767ce5e-3704-422d-aa53-4f769466789c:cc4') ? '' : 'unlocked-choice'"> <<link "His hand under the table. The cereal can wait." "Canvas_activity_breakfast_ethan_Node_8">><<script>>setup.markChoiceVisited("1767ce5e-3704-422d-aa53-4f769466789c:cc4");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "oral_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('1767ce5e-3704-422d-aa53-4f769466789c:cc5') ? '' : 'unlocked-choice'"> <<link "Kneel on cool tile. Morning takes a turn." "Canvas_activity_breakfast_ethan_Node_9">><<script>>setup.markChoiceVisited("1767ce5e-3704-422d-aa53-4f769466789c:cc5");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "sex_unlock", "operator": "is_true"}]})>> <span @class="setup.isChoiceVisited('1767ce5e-3704-422d-aa53-4f769466789c:cc6') ? '' : 'unlocked-choice'"> <<link "Counter. Now." "Canvas_activity_breakfast_ethan_Node_10">><<script>>setup.markChoiceVisited("1767ce5e-3704-422d-aa53-4f769466789c:cc6");<</script>><<script>>advanceTime(3);<</script>><</link>><br> </span> <</if>> <</nobr>>
<p>Your knees touch under the breakfast bar. Neither moves away. His hand finds yours reaching for the jam. Fingers brush. Linger.</p><img src="./videos/activities/breakfast_ethan_t2.jpg" alt="Accidental hand touch over breakfast, fingers lingering" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>He's not sorry. Neither are you.</p> <<set $game_state.current_canvas = "1767ce5e-3704-422d-aa53-4f769466789c">> <<set $game_state.current_node = "3490a5a7-3676-4373-add7-332720508666">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_breakfast_ethan.t2") === -1) { vn.push("activity_breakfast_ethan.t2"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Finish breakfast->Location_Kitchen]]
<p>You catch him watching you stretch. He quickly looks away, ears red. You take your time reaching for things on high shelves. The kitchen feels very small.</p><img src="./videos/activities/breakfast_ethan_flirt.jpg" alt="Standing too close in kitchen, reaching past her" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Need help with that?</div></div><p>He's already behind you.</p> <<set $game_state.current_canvas = "1767ce5e-3704-422d-aa53-4f769466789c">> <<set $game_state.current_node = "5b1b1b85-8e4c-4b45-ba14-78a84e1fb107">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_breakfast_ethan.t3") === -1) { vn.push("activity_breakfast_ethan.t3"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 2.0, true, null);setup.showEffectNotification();<</script>> [[Finish breakfast->Location_Kitchen]]
<p>You wrap your arms around him from behind at the counter. He leans back. Turns around. His hands on your waist. Kisses that taste like coffee.</p><video src="./videos/activities/breakfast_ethan_t4.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> We should eat.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> Later.</div></div> <<set $game_state.current_canvas = "1767ce5e-3704-422d-aa53-4f769466789c">> <<set $game_state.current_node = "03b80c97-897b-41a4-a2c5-62f5b0c0b003">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_breakfast_ethan.t4") === -1) { vn.push("activity_breakfast_ethan.t4"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Continue your morning->Location_Kitchen]]
<p>You press your hips back against him at the counter. Pajama pants on boxers. He inhales sharply. His hands find your hip bones and pull you tighter, matching your slow grind with his own. Coffee drips forgotten into the pot.</p><img src="./videos/activities/breakfast_ethan_t5.gif" alt="Morning grinding at kitchen counter, pajamas, from behind" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> The coffee—</div></div><p>You push back harder. The coffee can burn.</p> <<set $game_state.current_canvas = "1767ce5e-3704-422d-aa53-4f769466789c">> <<set $game_state.current_node = "138ee46c-5b9b-4fc7-90a4-8ed078b263f5">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_breakfast_ethan.t5") === -1) { vn.push("activity_breakfast_ethan.t5"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Pour the coffee. Eventually.->Location_Kitchen]]
<p>Milk sweats on the carton while his hand makes you forget it exists. The kitchen feels indecent in the morning light — in a way you love.</p><video src="./videos/activities/breakfast_ethan_t6.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "1767ce5e-3704-422d-aa53-4f769466789c">> <<set $game_state.current_node = "1feddbc7-f332-4250-ac7f-34510c31662f">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_breakfast_ethan.t6") === -1) { vn.push("activity_breakfast_ethan.t6"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Toast pops eventually->Location_Kitchen]]
<p>You sink to your knees on cool tile. He tastes like coffee and him. His fingers curl in your hair as he tries to stay quiet and fails a little.</p><video src="./videos/activities/breakfast_ethan_t7.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "1767ce5e-3704-422d-aa53-4f769466789c">> <<set $game_state.current_node = "5c30dbe0-5b13-4f6f-afee-d718df82fb63">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_breakfast_ethan.t7") === -1) { vn.push("activity_breakfast_ethan.t7"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 1.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Wipe your mouth, grin->Location_Kitchen]]
<p>He lifts you onto the counter again, this time with no hesitation. You meet him eagerly, the kitchen your accomplice.</p><video src="./videos/activities/breakfast_ethan_t8.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>The toast is charcoal by the time you're done. He makes a new batch, grinning.</p> <<set $game_state.current_canvas = "1767ce5e-3704-422d-aa53-4f769466789c">> <<set $game_state.current_node = "a0c30b56-1e12-4833-9976-6410527c1ff9">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_breakfast_ethan.t8") === -1) { vn.push("activity_breakfast_ethan.t8"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 2.0, true, null);setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);setup.showEffectNotification();<</script>> [[Make new toast->Location_Kitchen]]
<h1>The Wedding</h1><p>The wedding is beautiful. Everyone says so. The flowers, the venue, the weather — all of it perfect.</p><p>You sit in the crowd and watch it happen. Madison walks the aisle. He waits at the altar. The officiant speaks. The vows are said.</p><p>He doesn't look at you. Not once. Not during the vows. Not during the ring. Not during the kiss.</p><p>You're not sure if that makes it easier or harder.</p><p>When did it happen — or rather, when didn't it? Two weeks in the same house. Every almost. Every not-quite. The distance never closed and now it never will.</p><p>The ceremony ends. Everyone applauds. You clap too, because what else is there to do?</p> <<set $game_state.current_canvas = "8d5c8ef5-2409-455d-939b-649ee9b0258e">> <<set $game_state.current_node = "dee3a347-4a4a-43e1-b669-86657e07380d">> <<script>>setup.markCanvasTriggered("8d5c8ef5-2409-455d-939b-649ee9b0258e");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("ending_what_could_have_been.the_ceremony") === -1) { vn.push("ending_what_could_have_been.the_ceremony"); }<</script>> <<script>>advanceTime(3);<</script>> [[Go to the reception->Canvas_ending_what_could_have_been_Node_2]]
<h1>What Could Have Been</h1><p>They came close. So close. But neither was brave enough to cross the final line.</p><p>The wedding happens. It's beautiful. He's distant but committed. She's present but already leaving.</p><p>At the reception, you find a quiet corner. He finds you there.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Maybe in another life.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> Maybe.</div></div><img src="./videos/story/ending_what_could_have_been.jpg" alt="Airport departure, looking back once, melancholy and regret" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>You leave the next morning. Early flight. No fanfare. He drives you to the airport. Neither of you speaks.</p><p>At the gate, you almost say it. Almost. The words are right there.</p><p>But you board the plane. And the words stay unspoken. And the story ends the way most love stories end — not with a bang, but with a what-if that echoes forever.</p><p>The distance won. Two weeks in the same house, and neither of you found the courage to close the gap. The moments were there — but you let them pass.</p><p>Next time: don't hold back. Spend more time with Ethan — every meal, every evening, every quiet moment. Balance emotional and physical choices. Build trust through bonding activities. The best ending needs deep love, real trust, and enough corruption that going back to normal is impossible.</p> <<set $game_state.current_canvas = "8d5c8ef5-2409-455d-939b-649ee9b0258e">> <<set $game_state.current_node = "da91d24b-9316-4de6-bbc6-a5c4b827497c">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("ending_what_could_have_been.ending") === -1) { vn.push("ending_what_could_have_been.ending"); }<</script>> <<script>>advanceTime(3);<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "ending_seen");<</script>> <div class='game-end'><h2>The End</h2><<link 'Play Again'>><<run Engine.restart()>><</link>></div>
<h1>The Wedding</h1><p>You take your seat. Third row, aisle side. Your hands won't stop shaking so you sit on them like a child.</p><p>The music starts. Madison walks the aisle in white. She's radiant. She deserves this — all of it. The thought makes your throat close.</p><p>He's at the altar. The officiant speaks. And then — just for a second — his eyes find yours.</p><p>Everything you built over fourteen days is in that look. Every late-night conversation. Every accidental touch that wasn't accidental. Every time one of you almost said the thing that would have changed everything.</p><p>Almost. The cruelest word in any language.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I do.</div></div><p>He doesn't look at you again. Not during the kiss. Not during the applause. Not when they walk back up the aisle together, married, finished.</p> <<set $game_state.current_canvas = "856259c2-361b-49a3-a3e4-a21f70a5e07b">> <<set $game_state.current_node = "4e974830-cf38-415f-84eb-cebf01de0054">> <<script>>setup.markCanvasTriggered("856259c2-361b-49a3-a3e4-a21f70a5e07b");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("ending_one_last_night.the_ceremony") === -1) { vn.push("ending_one_last_night.the_ceremony"); }<</script>> <<script>>advanceTime(3);<</script>> [[Stay for the reception->Canvas_ending_one_last_night_Node_2]]
<h1>One Last Night</h1><p>He goes through with it. You never pushed far enough — kept things emotional, kept things safe. But the night before, he came to you one last time.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I love you. I'll always love you. But I can't destroy everything.</div></div><p>You watch from the crowd. Their vows. The ring. The kiss. His eyes find yours once — just once — across the room. A lifetime in a glance.</p><img src="./videos/story/ending_one_last_night.jpg" alt="Watching wedding from crowd with hidden tears, eye contact across room" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>You leave before the reception. You don't say goodbye. The taxi comes and you get in and you don't look back because if you look back you will break apart.</p><p>At the airport, your phone buzzes. One message from him: 'I'm sorry. For all of it. Except the parts with you.'</p><p>You board the plane. You fly away. You don't come back.</p><p>Some love stories end with sacrifice.</p><p>He loved you. That was never the question. But love wasn't enough — not without crossing enough lines together, not without the physical truth of what you were to each other making denial impossible.</p><p>Next time: don't hold back physically. Build corruption through daring choices and the journal. The best ending needs love, trust, and corruption all running high — emotional depth alone won't free him.</p> <<set $game_state.current_canvas = "856259c2-361b-49a3-a3e4-a21f70a5e07b">> <<set $game_state.current_node = "9e61577e-d1a2-4ec2-a778-cc7d6e947f87">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("ending_one_last_night.ending") === -1) { vn.push("ending_one_last_night.ending"); }<</script>> <<script>>advanceTime(3);<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "ending_seen");<</script>> <div class='game-end'><h2>The End</h2><<link 'Play Again'>><<run Engine.restart()>><</link>></div>
<h1>The Wedding</h1><p>The ceremony is perfect. Of course it is. Madison walks the aisle in white, radiant, every inch the bride. The flowers, the music, the soft gasps from the crowd — all of it choreographed, all of it beautiful.</p><p>He stands at the altar. Composed. Steady. The vows come — 'to have and to hold, in sickness and in health' — and his voice doesn't break.</p><p>But you see it. The way his jaw tightens. The way his eyes sweep the crowd once — just once — and find you before snapping back to Madison. The way his hands shake when he slides on the ring.</p><p>He says 'I do.' Madison says 'I do.' Everyone cheers. You clap with the rest of them, your smile a perfect mask.</p><p>You know something they don't. And that knowledge sits in your chest like a stone.</p> <<set $game_state.current_canvas = "1467cff5-632e-42f1-9fdb-5636f83845b3">> <<set $game_state.current_node = "eab61add-dd54-421b-b830-631a5d7a7d52">> <<script>>setup.markCanvasTriggered("1467cff5-632e-42f1-9fdb-5636f83845b3");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("ending_the_arrangement.the_ceremony") === -1) { vn.push("ending_the_arrangement.the_ceremony"); }<</script>> <<script>>advanceTime(3);<</script>> [[Watch the reception begin->Canvas_ending_the_arrangement_Node_2]]
<h1>The Arrangement</h1><p>He marries Madison. Of course he does. The desire was there but something was missing — the trust to believe you could be more than a secret. You watch from the crowd with a smile that could cut glass.</p><p>The reception is beautiful. The speeches are warm. Madison glows. He plays the part perfectly.</p><p>Three weeks later, your phone buzzes. Unknown number.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I can't leave her. But I can't leave you either.</div></div><p>It's wrong. You both know it's wrong. You've always known.</p><img src="./videos/story/ending_the_arrangement.jpg" alt="Secret message on phone, hidden relationship continuing, bittersweet" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>But when has that ever stopped you?</p><p>The arrangement begins. Stolen weekends. Burner phones. A love that lives in the shadows because neither of you is strong enough to let it die — or brave enough to bring it into the light.</p><p>The love was real. The connection was deep. But without enough trust to believe in a future together, duty held him in place. You pushed him far — but not quite far enough to pull him free before the ring went on.</p><p>Next time: balance everything. Build love through emotional choices, earn trust through bonding, and don't shy away from physical escalation. The best ending needs love, trust, and corruption all running high.</p> <<set $game_state.current_canvas = "1467cff5-632e-42f1-9fdb-5636f83845b3">> <<set $game_state.current_node = "26281c5e-b88a-4cd8-a213-1466bb5182c4">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("ending_the_arrangement.ending") === -1) { vn.push("ending_the_arrangement.ending"); }<</script>> <<script>>advanceTime(3);<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "ending_seen");<</script>> <div class='game-end'><h2>The End</h2><<link 'Play Again'>><<run Engine.restart()>><</link>></div>
<h1>He Chooses You</h1><p>The ceremony begins. Flowers. Music. Madison walks down the aisle in white. Everyone watches. Everyone smiles.</p><p>He stands at the altar. The officiant speaks. 'Do you take this woman...'</p><p>And he looks at you. Sitting in the third row. Hands in your lap. Heart in your throat.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I can't do this.</div></div><p>Chaos. Madison's tears. Her mother's scream. His father's face. Whispers turning to shouts. The destruction of a life carefully built.</p><img src="./videos/story/ending_he_chooses_you.jpg" alt="Wedding interrupted, man walking away from altar, aftermath chaos" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>When the dust settles — hours, days, a lifetime later — he's beside you. Bags packed. Nowhere to go. Scandal at his back and an uncertain future ahead.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I choose you. I choose us.</div></div><p>It won't be easy. His family may never forgive him. Madison certainly won't. The whispers will follow you both for years.</p><p>But his hand is in yours. And for the first time in your life, you're not running.</p><p>You did it. You were brave enough to let it happen — bold enough to stand in the fire and not look away. He saw that in you. He chose that.</p><p>It took everything: the late nights, the stolen moments, the courage to push past every safe boundary. Love ran deep between you, trust anchored what you built, and neither of you flinched when it mattered most.</p><p>Not every love story ends like this. Yours did.</p> <<set $game_state.current_canvas = "21fd6c01-6552-4f23-8a4f-ac1632ffea16">> <<set $game_state.current_node = "46c84023-5f89-4b88-88f2-9e4a7e1fc9d3">> <<script>>setup.markCanvasTriggered("21fd6c01-6552-4f23-8a4f-ac1632ffea16");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("ending_he_chooses_you.ending") === -1) { vn.push("ending_he_chooses_you.ending"); }<</script>> <<script>>advanceTime(3);<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "ending_seen");<</script>> <div class='game-end'><h2>The End</h2><<link 'Play Again'>><<run Engine.restart()>><</link>></div>
<p>The house is chaos. Florists. A caterer. Madison's mother on the phone. Someone asking about centerpieces.</p><p>He finds you in the kitchen during a brief gap. Everyone else is upstairs. His hand finds yours under the counter.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Whatever happens today... I need you to know... Thank you. For everything.</div></div><img src="./videos/story/scene_wedding_morning_the_day.jpg" alt="Stolen hand-holding amid wedding chaos, intense brief moment" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>You don't know what that means yet. His eyes are red. He didn't sleep either.</p><p>Someone calls his name from upstairs. He squeezes your hand once, hard, then lets go.</p> <<set $game_state.current_canvas = "6bb7047c-d52d-4d93-aaf7-e6ea9144260c">> <<set $game_state.current_node = "f503a95f-b6ee-41cd-8ed4-d912da77f16b">> <<script>>setup.markCanvasTriggered("6bb7047c-d52d-4d93-aaf7-e6ea9144260c");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_wedding_morning.the_day") === -1) { vn.push("scene_wedding_morning.the_day"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "wedding_morning_done");<</script>> [[Let him go->Location_Kitchen]]
<p>Tomorrow he marries Madison.</p><p>Tonight the door opens in darkness. You know it's him before he speaks. You've been waiting. You've been hoping. You've been dreading.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I had to see you.</div></div><p>His kiss tastes like tears.</p><img src="./videos/story/scene_night_before_wedding_n1_door.gif" alt="Desperate embrace in doorway at night, emotional intensity, tears" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "22e1ab87-5b5c-4da0-ba96-8c1ce2eaee78">> <<set $game_state.current_node = "30bd4d59-329d-44f3-a4b1-efc0a2007086">> <<script>>setup.markCanvasTriggered("22e1ab87-5b5c-4da0-ba96-8c1ce2eaee78");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_night_before_wedding.n1_door") === -1) { vn.push("scene_night_before_wedding.n1_door"); }<</script>> <<nobr>> <<link "Pull him inside" "Canvas_scene_night_before_wedding_Node_2">><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>Slowly. Memorizing. Every button, every inch of skin. Like he's trying to commit you to permanent memory.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I want to remember everything.</div></div><video src="./videos/story/scene_night_before_wedding_n2_undressing.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "22e1ab87-5b5c-4da0-ba96-8c1ce2eaee78">> <<set $game_state.current_node = "7c71c464-6298-42b1-97ed-431287868cc5">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_night_before_wedding.n2_undressing") === -1) { vn.push("scene_night_before_wedding.n2_undressing"); }<</script>> <<nobr>> <<link "Let him lay you down" "Canvas_scene_night_before_wedding_Node_3">><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>He kisses his way down your body. No rush tonight. Slow, devoted, like a prayer.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I want to remember every part of you.</div></div><video src="./videos/story/scene_night_before_wedding_n3_worship.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "22e1ab87-5b5c-4da0-ba96-8c1ce2eaee78">> <<set $game_state.current_node = "57f87bb3-2813-4d29-b5f1-c6ab88615041">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_night_before_wedding.n3_worship") === -1) { vn.push("scene_night_before_wedding.n3_worship"); }<</script>> <<nobr>> <<link "Pull him up to you" "Canvas_scene_night_before_wedding_Node_4">><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>When he enters you, you're both crying. The weight of everything you can't have. But right now, in this moment, he's yours.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I love you. I've always loved you.</div></div><p>Not in the heat of passion — in the weight of loss. He says it like a fact. Like gravity. Like something that was always true and will always be true regardless of what tomorrow brings.</p><video src="./videos/story/scene_night_before_wedding_n4_together.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "22e1ab87-5b5c-4da0-ba96-8c1ce2eaee78">> <<set $game_state.current_node = "bde2921a-b005-4d55-bea1-e6d15df3c5f0">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_night_before_wedding.n4_together") === -1) { vn.push("scene_night_before_wedding.n4_together"); }<</script>> <<nobr>> <<link "Hold him closer" "Canvas_scene_night_before_wedding_Node_5">><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>The pace builds. Gentle becomes urgent. Tenderness gives way to need.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> Don't hold back. Not tonight.</div></div><p>You pull him deeper. Nails down his back. He gasps your name.</p><video src="./videos/story/scene_night_before_wedding_n5_deeper.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "22e1ab87-5b5c-4da0-ba96-8c1ce2eaee78">> <<set $game_state.current_node = "48d7d134-c6c5-42ea-95cd-811685b9ec84">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_night_before_wedding.n5_deeper") === -1) { vn.push("scene_night_before_wedding.n5_deeper"); }<</script>> <<nobr>> <<link "Take control" "Canvas_scene_night_before_wedding_Node_6">><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>You push him onto his back. You need to feel him, control this, make it yours. His hands grip your hips. His eyes never leave yours.</p><video src="./videos/story/scene_night_before_wedding_n6_control.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>Everything narrows to this — two bodies, one heartbeat, a love that will outlast whatever happens tomorrow.</p> <<set $game_state.current_canvas = "22e1ab87-5b5c-4da0-ba96-8c1ce2eaee78">> <<set $game_state.current_node = "238001e3-5ad1-4a08-8457-d2325544ab7c">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_night_before_wedding.n6_control") === -1) { vn.push("scene_night_before_wedding.n6_control"); }<</script>> <<nobr>> <<link "Feel it building" "Canvas_scene_night_before_wedding_Node_7">><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I'm yours. Whatever happens tomorrow, I'm yours tonight.</div></div><p>Climax. The world whites out. When it returns, you're tangled together, breathing hard, holding on.</p><p>He cleans you gently, tenderly. Then you lie together in the dark.</p><img src="./videos/story/scene_night_before_wedding_n7_finish.gif" alt="Post-sex afterglow, naked and tangled together in bed, holding each other" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> Don't marry her.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Don't ask me that.</div></div><p>But he's here, isn't he?</p><p>You stay together until the sky lightens. No more words. Just holding on to what little time you have.</p> <<set $game_state.current_canvas = "22e1ab87-5b5c-4da0-ba96-8c1ce2eaee78">> <<set $game_state.current_node = "f9631eda-0a3b-4d18-b3da-72a2c53b4249">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_night_before_wedding.n7_finish") === -1) { vn.push("scene_night_before_wedding.n7_finish"); }<</script>> <<nobr>> <<link "I'll always love you. Whatever you decide." "Location_Your_Old_Room">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.applyAndNotifyFlag("player", null, "night_before_complete");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(300);<</script>><</link>><br> <</nobr>>
<p>Madison is on the phone in the kitchen. Or maybe the shower. It doesn't matter where — just that she's not here.</p><p>He finds you in the garage. Grabs your hand. Pulls you behind the shelves.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I need you. I can't — one more time before —</div></div><p>He doesn't finish. He doesn't need to.</p><p>Desperate. Guilty. Unable to stop. His hand over your mouth because Madison is somewhere in the house.</p><img src="./videos/story/scene_stolen_moment_hidden.jpg" alt="Quick desperate sex in garage, forbidden, hand covering her mouth" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Every sound from inside the house is a threat. The thrill of almost getting caught. The terrifying freedom of not caring.</p> <<set $game_state.current_canvas = "0a5767c4-1252-4b9d-849f-afc7cf88c8ce">> <<set $game_state.current_node = "9a2e530d-73a0-4ea6-9e1e-1a99f765bb22">> <<script>>setup.markCanvasTriggered("0a5767c4-1252-4b9d-849f-afc7cf88c8ce");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_stolen_moment.hidden") === -1) { vn.push("scene_stolen_moment.hidden"); }<</script>> <<nobr>> <<link "We're insane. (But don't stop)" "Canvas_scene_stolen_moment_Node_2">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>Madison's voice calling his name from inside. The spell shatters.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Coming!</div></div><p>He looks at you one last time before going inside. That look says everything words can't: I'm sorry. I love you. I don't know how to fix this.</p> <<set $game_state.current_canvas = "0a5767c4-1252-4b9d-849f-afc7cf88c8ce">> <<set $game_state.current_node = "80b66704-1fff-486e-b7cf-66807a9e3f18">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_stolen_moment.after") === -1) { vn.push("scene_stolen_moment.after"); }<</script>> <<script>>advanceTime(30);<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "stolen_moment_complete");<</script>> [[Wait. Then go inside separately.->Location_Garage]]
<p>Sound of a car in the driveway. Your heart stops.</p><p>Madison.</p><p>She's polished, put-together, excited. She practically bounces through the door with a suitcase and a garment bag.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/madison.jpg" alt="Madison" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Madison:</strong> Surprise! I finished early!</div></div><p>You watch Ethan perform. The hug. The smile. The 'I'm so glad you're here.' Over Madison's shoulder, his eyes find yours. Apology. Fear. Longing.</p><img src="./videos/story/scene_madison_arrives_arrival.jpg" alt="Fiancee arriving home, couple embrace, third person watching with hidden emotion" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/madison.jpg" alt="Madison" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Madison:</strong> You must be the step-sister! I've heard so much about you.</div></div><p>She's warm. Genuine. She hugs you like a friend. She smells like expensive perfume and a clear conscience.</p> <<set $game_state.current_canvas = "d59480ca-2ae6-4e25-a0ce-9a01a146a4e3">> <<set $game_state.current_node = "9e0c6284-6457-499b-9ea2-c9182e83ed21">> <<script>>setup.markCanvasTriggered("d59480ca-2ae6-4e25-a0ce-9a01a146a4e3");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_madison_arrives.arrival") === -1) { vn.push("scene_madison_arrives.arrival"); }<</script>> <<nobr>> <<link "All good things, I hope." "Canvas_scene_madison_arrives_Node_2">><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>Madison is nice. Genuinely, disarmingly nice. She asks about your life, your work, your flight. She makes you tea without being asked.</p><p>That makes it so much worse.</p><p>The abstract has become concrete. She's not a name on a phone screen anymore. She's a person. A person who doesn't deserve what you've done.</p> <<set $game_state.current_canvas = "d59480ca-2ae6-4e25-a0ce-9a01a146a4e3">> <<set $game_state.current_node = "63fef3cf-dde5-4582-a6c4-2b13cec073cf">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_madison_arrives.new_reality") === -1) { vn.push("scene_madison_arrives.new_reality"); }<</script>> <<script>>advanceTime(30);<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "madison_arrived");<</script>> [[Retreat to your room->Location_Home]]
<p>You tried to be normal today. Failed. Every look is loaded. Every accidental touch sends electricity through both of you.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I can't stop thinking about last night.</div></div><p>He initiates. That's the difference. He comes to you, not the other way around. His resistance broke on its own — desire stronger than duty.</p><p>He glances down the hall — empty — and then his mouth is on yours. You stumble backward. Your back hits the wall. Fast, desperate, addicted.</p><video src="./videos/story/scene_cant_stay_away_hallway.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> My room. Ten minutes. I'm not done with you.</div></div><p>Every creaking board in this house is a risk. Every doorway a potential witness. The danger makes it more intense, not less.</p> <<set $game_state.current_canvas = "00cf792b-24a9-40c4-a16e-ad78c967f92e">> <<set $game_state.current_node = "1409bac2-d924-4672-989b-b3a78c9cde32">> <<script>>setup.markCanvasTriggered("00cf792b-24a9-40c4-a16e-ad78c967f92e");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_cant_stay_away.hallway") === -1) { vn.push("scene_cant_stay_away.hallway"); }<</script>> <<script>>advanceTime(90);<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "cant_stay_away_complete");<</script>> [[Follow him->Location_Home]]
<p>Dawn light through the curtains. His arm around you. For a moment — just this. Just warmth and skin and the slow rhythm of his breathing.</p><p>Morning intimacy — lazy, half-asleep, tender. A continuation of the night before. He's gentle, present, lost in you.</p><video src="./videos/story/scene_morning_after_dawn.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>Then it's over and the weight lands. You can see it hit him — his eyes change, the softness replaced by something heavier. He's lying next to his step-sister, in the house where they grew up, four days before his wedding.</p> <<set $game_state.current_canvas = "2b1846e5-50c0-4114-bfba-0f187ae7172f">> <<set $game_state.current_node = "8203aae6-ed08-46c1-b17c-2af3a09bd5b9">> <<script>>setup.markCanvasTriggered("2b1846e5-50c0-4114-bfba-0f187ae7172f");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_morning_after.dawn") === -1) { vn.push("scene_morning_after.dawn"); }<</script>> <<nobr>> <<link "I don't regret this. Do you?" "Canvas_scene_morning_after_Node_2">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> We should talk. Later. About everything.</div></div><p>But not now. Now he gets dressed without meeting your eyes. He flinches when his hand brushes yours. The weight of what they've done is a living thing between them.</p><p>At the door, he pauses. Doesn't turn around. Then he's gone, and you're alone with the impression of his body on the sheets.</p> <<set $game_state.current_canvas = "2b1846e5-50c0-4114-bfba-0f187ae7172f">> <<set $game_state.current_node = "8d620ec5-3c69-4748-a291-8b51de4d4fd2">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_morning_after.lingering") === -1) { vn.push("scene_morning_after.lingering"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "morning_after_complete");setup.applyAndNotifyFlag("player", null, "sex_unlock");<</script>> [[Get up->Location_Your_Old_Room]]
<p>The goodnight at the door doesn't end with goodnight.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> Stay.</div></div><p>One word. Everything changes. The door clicks shut.</p><p>His hands shake — not from nerves but from how long he's wanted this. You pull him closer. Years of suppression breaking.</p><p>Tender and desperate at once. His hands tracing paths he's memorized in his imagination. Your name on his lips like a prayer.</p><video src="./videos/story/scene_first_night_stay.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> God, you're beautiful.</div></div><p>This isn't just physical. Eye contact throughout. His name on your lips. Emotional intimacy matching every physical moment.</p> <<set $game_state.current_canvas = "97550b02-646d-4e96-884e-c6f6b996a77e">> <<set $game_state.current_node = "0071c9a3-aadd-4cfd-832d-deb365b7be71">> <<script>>setup.markCanvasTriggered("97550b02-646d-4e96-884e-c6f6b996a77e");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_first_night.stay") === -1) { vn.push("scene_first_night.stay"); }<</script>> <<nobr>> <<link "I've wanted this for so long." "Canvas_scene_first_night_Node_2">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>In the quiet after, everything feels different. Clearer. More complicated. You lie tangled together, his arm around you, his breath slowing against your neck.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> No regrets?</div></div><p>You answer with a kiss. He pulls you closer and you fall asleep like that — intertwined, sharing a pillow, sharing a secret that will reshape both your lives.</p> <<set $game_state.current_canvas = "97550b02-646d-4e96-884e-c6f6b996a77e">> <<set $game_state.current_node = "009a377a-1dd5-4444-b82c-7345cd1e662b">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_first_night.aftermath") === -1) { vn.push("scene_first_night.aftermath"); }<</script>> <<script>>advanceTime(360);<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "first_night_complete");setup.applyAndNotifyFlag("player", null, "intimacy_unlock");setup.applyAndNotifyFlag("player", null, "ethan_intimate");<</script>> [[Fall asleep together->Location_Your_Old_Room]]
<p>The same couch. The same blanket. But nothing is the same.</p><p>His hand finds yours under the blanket — the way it did weeks ago, when touching fingers felt revolutionary. Now his thumb traces your palm and you both know it isn't going to stop at hands.</p><p>The TV is on. Neither of you is watching.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Come here.</div></div><p>You swing your leg over his lap. Face to face. His hands on your hips. The blanket falls to the floor and neither of you reaches for it.</p> <<set $game_state.current_canvas = "666537f7-7273-4689-a46f-18b2b72cae3e">> <<set $game_state.current_node = "cd31d7ae-870a-4d10-9c87-a75d7eebf14b">> <<script>>setup.markCanvasTriggered("666537f7-7273-4689-a46f-18b2b72cae3e");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_going_further.the_couch_again") === -1) { vn.push("scene_going_further.the_couch_again"); }<</script>> <<nobr>> <<link "Let it happen. See where this goes." "Canvas_scene_going_further_Node_2">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>Mouths travel. Past collarbones. Past the edges of fabric. Shirts come off — not thrown, but peeled away, slowly, like unwrapping something precious.</p><p>He kisses down your stomach. Your fingers curl in his hair. The world shrinks to the couch, to his mouth, to the sound you make when he reaches his destination.</p><video src="./videos/story/scene_going_further_taste.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>When he comes back up, you push him onto his back. Fair is fair. His head falls back. His hand finds your hair. The couch creaks. You don't care.</p><video src="./videos/story/scene_going_further_taste2.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>Not sex. Not yet. But this — mouths and hands and the raw honesty of wanting someone without any barriers left — is its own kind of crossing.</p> <<set $game_state.current_canvas = "666537f7-7273-4689-a46f-18b2b72cae3e">> <<set $game_state.current_node = "8142a458-7f19-461b-84bb-205e2f8c3034">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_going_further.taste") === -1) { vn.push("scene_going_further.taste"); }<</script>> <<nobr>> <<link "Stay here tonight. Just like this." "Canvas_scene_going_further_Node_3">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>You lie on the couch tangled together, half-dressed, the TV casting blue light over bare skin. His fingers draw patterns on your shoulder.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I don't think I can go back to just coffee and conversation.</div></div><p>You can't either. And the wedding is getting closer.</p> <<set $game_state.current_canvas = "666537f7-7273-4689-a46f-18b2b72cae3e">> <<set $game_state.current_node = "aa62eaa3-6bfd-4316-9eeb-359116e373af">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_going_further.after") === -1) { vn.push("scene_going_further.after"); }<</script>> <<script>>advanceTime(120);<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "going_further_complete");setup.applyAndNotifyFlag("player", null, "oral_unlock");<</script>> [[Eventually, separate rooms->Location_Living_Room]]
<p>Morning. Coffee. The weight of last night hanging in the air like smoke.</p><p>He's already there when you come down. Already poured your coffee. He knows how you take it — black, one sugar. Always has.</p><p>He crosses to you. Takes the coffee from your hands. Sets it on the counter.</p><p>And kisses you. Soft. Deliberate. Morning breath and all.</p><img src="./videos/story/scene_what_are_we_doing_morning.gif" alt="Morning kitchen kiss, tender and unhurried, coffee on counter" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I don't want to pretend that didn't happen.</div></div> <<set $game_state.current_canvas = "6b523d9f-a9a3-4e93-9bee-f243b083f9b4">> <<set $game_state.current_node = "6ca5cf21-8c09-49e4-b90d-06e3c66a2cd5">> <<script>>setup.markCanvasTriggered("6b523d9f-a9a3-4e93-9bee-f243b083f9b4");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_what_are_we_doing.morning") === -1) { vn.push("scene_what_are_we_doing.morning"); }<</script>> <<nobr>> <<link "Neither do I." "Canvas_scene_what_are_we_doing_Node_2">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>The kiss changes. Deeper. His hands slide from your face to your waist. Yours find the hem of his shirt.</p><p>He lifts you onto the counter — the same counter where he makes your coffee every morning. Your legs wrap around him. His mouth moves to your neck.</p><p>His hand slides under your shirt, tracing upward. Then downward. Past your waistband. Your breath catches.</p><video src="./videos/story/scene_what_are_we_doing_escalation.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>You reach for him. Find him hard through his shorts. His forehead drops to your shoulder as your hand wraps around him.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> God...</div></div><p>It's clumsy and perfect. Morning light through the kitchen window. Coffee going cold. His breath ragged against your neck as your hand moves. Years of wanting condensed into this — the first time you touch each other and mean it.</p> <<set $game_state.current_canvas = "6b523d9f-a9a3-4e93-9bee-f243b083f9b4">> <<set $game_state.current_node = "ecbd5cf3-4fcb-4f26-af5b-3a515d753ea8">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_what_are_we_doing.escalation") === -1) { vn.push("scene_what_are_we_doing.escalation"); }<</script>> <<nobr>> <<link "Don't stop looking at me." "Canvas_scene_what_are_we_doing_Node_3">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>You lean against each other, breathing hard. The counter is cold under your thighs. His hand is still on your hip, thumb tracing circles.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> So that's what we're doing.</div></div><p>He says it with a half-laugh, half-wonder. Like he can't believe it's real. Neither can you.</p><p>The coffee is stone cold. He makes a fresh pot. You sit at the counter and watch him, and everything — every small domestic motion — is charged now. Electric. Inevitable.</p> <<set $game_state.current_canvas = "6b523d9f-a9a3-4e93-9bee-f243b083f9b4">> <<set $game_state.current_node = "3fd8cecb-011c-4397-9590-2b329036e27b">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_what_are_we_doing.new_normal") === -1) { vn.push("scene_what_are_we_doing.new_normal"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "what_are_we_doing_done");setup.applyAndNotifyFlag("player", null, "manual_unlock");<</script>> [[Start the day->Location_Kitchen]]
<p>You pass him in the upstairs hallway. Same hallway, same hour, same route to your separate rooms. But since the kiss, nothing is the same.</p><p>His arm brushes yours. Neither of you moves away. The hallway narrows to the width of the breath between you.</p><p>He stops walking. You stop walking. The house ticks around you — radiator, settling wood, clock downstairs. His eyes drop to your mouth. Your pulse hammers in your throat.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I keep trying to walk past you.</div></div><p>His hand finds your hip. Thumb on the bone, fingers curling into the fabric of your shirt. Not pulling. Holding. Like he's anchoring himself to the decision he's about to make.</p><video src="./videos/story/scene_cant_keep_hands_off_hallway.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video> <<set $game_state.current_canvas = "6b485769-8efb-4230-b5ed-b05887a735ee">> <<set $game_state.current_node = "574510ae-5d9a-45c4-99b7-d3f68dcb00d6">> <<script>>setup.markCanvasTriggered("6b485769-8efb-4230-b5ed-b05887a735ee");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_cant_keep_hands_off.hallway_heat") === -1) { vn.push("scene_cant_keep_hands_off.hallway_heat"); }<</script>> <<nobr>> <<link "Pull him closer. Feel everything." "Canvas_scene_cant_keep_hands_off_Node_2">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>Your back hits the wall. His body follows — chest to chest, hips to hips. The kiss is immediate, open-mouthed, graceless. His thigh presses between yours and you roll against it without thinking.</p><p>His hands grip your waist and pull you tighter. You feel him hard against your hip through thin cotton. Your fingers curl into his shirt, bunching fabric, pulling him impossibly closer. Every slow grind sends heat pooling low in your stomach.</p><p>Clothes stay on. Barely. His hand slides up under the hem of your shirt, palm flat on your ribs, but he doesn't go further. Fingers trace the underwire of your bra through fabric. You arch into his touch and his breath catches against your mouth.</p><img src="./videos/story/scene_cant_keep_hands_off_grinding.gif" alt="Grinding against wall in hallway, fully clothed, desperate and urgent" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> We should—</div></div><p>You silence him with your mouth. Roll your hips again. He groans against your lips and matches the rhythm. The hallway fills with the sound of breathing and fabric shifting.</p> <<set $game_state.current_canvas = "6b485769-8efb-4230-b5ed-b05887a735ee">> <<set $game_state.current_node = "430b25a5-9e62-4ed9-8281-131fcd96f33e">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_cant_keep_hands_off.grinding") === -1) { vn.push("scene_cant_keep_hands_off.grinding"); }<</script>> <<nobr>> <<link "Don't stop." "Canvas_scene_cant_keep_hands_off_Node_3">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>You pull apart slowly. His forehead drops to your shoulder. Both of you breathing hard. Shirts rumpled, skin flushed, but every button still fastened.</p><p>His hand is still on your hip. Yours is still fisted in his shirt. The hallway feels ten degrees warmer than it did five minutes ago.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> That's getting harder to stop.</div></div><p>He straightens your shirt where it rode up. The tenderness after the urgency undoes you more than the grinding did. He steps back. One step. Two. The distance between your doors has never felt so small.</p> <<set $game_state.current_canvas = "6b485769-8efb-4230-b5ed-b05887a735ee">> <<set $game_state.current_node = "3e8145d1-07fa-4cd9-8a0f-4251ddefd019">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_cant_keep_hands_off.aftermath") === -1) { vn.push("scene_cant_keep_hands_off.aftermath"); }<</script>> <<script>>advanceTime(30);<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "cant_keep_hands_off_complete");setup.applyAndNotifyFlag("player", null, "grind_unlock");<</script>> [[Separate doors. Somehow.->Location_Home]]
<p>No more interruptions. No more excuses. It happens like gravity.</p><p>One moment you're talking — something about nothing, filling silence with noise. The next, silence. His eyes on your mouth. Your breath catching.</p><p>Then his mouth is on yours.</p><p>The kiss starts tentative — a question. Then desperate. Years of suppression breaking like a dam. His hands in your hair, your back against the wall, pulling each other closer and closer and it's still not close enough.</p><img src="./videos/activities/passionate_kiss.jpg" alt="Passionate first kiss, tentative then desperate, years of longing released" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Everything you've both been fighting falls away in an instant.</p> <<set $game_state.current_canvas = "96cdf6ea-e12d-4d2f-8912-9ca30ff5437b">> <<set $game_state.current_node = "2ab57a06-54f0-420d-aa03-ce4c84e15875">> <<script>>setup.markCanvasTriggered("96cdf6ea-e12d-4d2f-8912-9ca30ff5437b");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_first_kiss.inevitable") === -1) { vn.push("scene_first_kiss.inevitable"); }<</script>> <<nobr>> <<link "Don't stop." "Canvas_scene_first_kiss_Node_2">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>When you finally break apart, the world has shifted on its axis. The room is the same. Everything else is different.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I've wanted to do that for years.</div></div><p>His forehead rests against yours. His breath is ragged. Neither of you is pretending anymore.</p> <<set $game_state.current_canvas = "96cdf6ea-e12d-4d2f-8912-9ca30ff5437b">> <<set $game_state.current_node = "d8e2ad89-8ad4-4611-ab54-71eac486986e">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_first_kiss.after_the_kiss") === -1) { vn.push("scene_first_kiss.after_the_kiss"); }<</script>> <<script>>advanceTime(30);<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "first_kiss_done");setup.applyAndNotifyFlag("player", null, "kiss_unlock");<</script>> [[Say goodnight->Location_Living_Room]]
<p>You find him sitting on your bed in the dark. He's been waiting. Or maybe he's been trying to leave and couldn't.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> We need to talk about this.</div></div><p>The wedding is in a week. Madison. The family. The impossibility of what he's feeling. Everything he's been pushing down comes flooding up at once.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I'm supposed to be getting married.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> I know.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I don't know if I can.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> I know.</div></div><p>His voice breaks.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> What do you want?</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I want you. I hate that I want you.</div></div><img src="./videos/story/scene_real_talk_in_the_dark.jpg" alt="Emotional breakdown, holding someone who is crying, comfort not seduction" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>He's not performing anymore. The protective older brother, the dutiful fiance, the man who has it all together — all masks, and they've all fallen off at once.</p> <<set $game_state.current_canvas = "8afc4ce8-e750-4025-92f0-9327b0f035f8">> <<set $game_state.current_node = "0fa23dd2-9a87-4f4d-a899-06a65854bb86">> <<script>>setup.markCanvasTriggered("8afc4ce8-e750-4025-92f0-9327b0f035f8");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_real_talk.in_the_dark") === -1) { vn.push("scene_real_talk.in_the_dark"); }<</script>> <<nobr>> <<link "We'll figure it out. Together." "Canvas_scene_real_talk_Node_2">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>Whatever was said, the air feels different. Raw. Like a wound that's been lanced — it hurts more now, but at least it's honest.</p><p>He doesn't stay. But when he leaves, he pauses at the door. His hand on the frame. He doesn't look back.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I'm sorry. For all of this.</div></div><p>The door clicks shut. You stare at the ceiling until dawn.</p> <<set $game_state.current_canvas = "8afc4ce8-e750-4025-92f0-9327b0f035f8">> <<set $game_state.current_node = "141f90e6-a34a-4cc5-83fd-5cd96c5a01dc">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_real_talk.aftermath") === -1) { vn.push("scene_real_talk.aftermath"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "real_talk_complete");setup.applyAndNotifyFlag("player", null, "ethan_vulnerable");<</script>> [[Try to sleep->Location_Your_Old_Room]]
<p>Close. Too close. You can feel the heat radiating off his skin.</p><p>His hand comes up to your face. Thumb tracing your cheekbone. His breath mixing with yours. The tilt of heads beginning.</p><img src="./videos/story/scene_almost_kiss_the_moment.gif" alt="Faces inches apart, almost kissing, intense anticipation then interrupted" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Then — his phone. Vibrating on the patio table. The spell shatters. You spring apart like guilty teenagers caught by the porch light.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I should...</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> Yeah.</div></div><p>But you both know. There's no going back from what almost just happened.</p> <<set $game_state.current_canvas = "d22394d1-d096-49be-96a2-3e681ead6cba">> <<set $game_state.current_node = "7abb8bdd-5477-43a9-8a48-f19a03315437">> <<script>>setup.markCanvasTriggered("d22394d1-d096-49be-96a2-3e681ead6cba");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_almost_kiss.the_moment") === -1) { vn.push("scene_almost_kiss.the_moment"); }<</script>> <<nobr>> <<link "Next time, lock the door." "Canvas_scene_almost_kiss_Node_2">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>You go inside separately. Separate doors. Separate showers. The cold water doesn't help.</p> <<set $game_state.current_canvas = "d22394d1-d096-49be-96a2-3e681ead6cba">> <<set $game_state.current_node = "6e4b8165-06ce-490d-ad3a-6b50a06be176">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_almost_kiss.parting") === -1) { vn.push("scene_almost_kiss.parting"); }<</script>> <<script>>advanceTime(30);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.showEffectNotification();<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "almost_kiss_complete");<</script>> [[Go inside->Location_Backyard_&_Pool]]
<p>Third glass of wine on the patio. Stars out. Pool lights shimmer turquoise across the water.</p><p>The alcohol makes the truth easier. Or maybe it's not the alcohol at all.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> I used to stay awake at night listening for your footsteps. Hoping you'd knock on my door. Terrified you would.</div></div><p>The silence that follows is the loudest thing you've ever heard.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I thought I was the only one.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> You weren't.</div></div><p>Long pause. The pool filter hums. A lifetime passes.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> What do we do now?</div></div><img src="./videos/story/scene_confession_under_the_stars.jpg" alt="Intense eye contact over wine on a patio at night, stars visible, emotional confession" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "6e6edb75-dc97-4b5d-ad95-1882e783f81e">> <<set $game_state.current_node = "d0f699c0-0308-4934-a31e-d5f622c280f9">> <<script>>setup.markCanvasTriggered("6e6edb75-dc97-4b5d-ad95-1882e783f81e");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_confession.under_the_stars") === -1) { vn.push("scene_confession.under_the_stars"); }<</script>> <<nobr>> <<link "We still have time to figure that out." "Canvas_scene_confession_Node_2">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>The night air feels different now. Charged. Whatever walls were left have started to crumble.</p><p>The stars don't care about your problems. The pool lights keep shimmering. The world keeps turning. But something between you has changed permanently.</p> <<set $game_state.current_canvas = "6e6edb75-dc97-4b5d-ad95-1882e783f81e">> <<set $game_state.current_node = "d54fed57-7a3e-46b6-a82f-5d7a7f87f987">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_confession.after") === -1) { vn.push("scene_confession.after"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "confession_complete");<</script>> [[Go inside->Location_Backyard_&_Pool]]
<p>Movie night. The couch is big enough for three people but somehow you've both migrated to the middle. A blanket covers both of you.</p><p>On screen, a couple is doing what you're both pretending not to think about.</p><p>Under the blanket, bodies touching. His arm settles around your shoulders — starts casual, drifts to pulling you closer. His hand on your thigh. Starts innocent. Drifts higher. Neither of you acknowledges it.</p><img src="./videos/story/scene_the_couch_movie_night.jpg" alt="Two people on couch under blanket, movie night, physical tension and proximity" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> This is nice. Just... this.</div></div><p>His thumb traces small circles on your thigh through the blanket. Deliberate. Not accidental.</p> <<set $game_state.current_canvas = "538e115a-5000-4942-afc6-782fa559e36b">> <<set $game_state.current_node = "4bb7e530-fe87-43cc-b803-f88066874e36">> <<script>>setup.markCanvasTriggered("538e115a-5000-4942-afc6-782fa559e36b");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_the_couch.movie_night") === -1) { vn.push("scene_the_couch.movie_night"); }<</script>> <<nobr>> <<link "Move closer" "Canvas_scene_the_couch_Node_2">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "boldness", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>Something shifts. Or almost shifts. The movie ends. Credits roll. Neither of you has any idea what happened in it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> It's late. We should...</div></div><p>He doesn't finish the sentence. You both know what's happening. You both know you're not going to stop it.</p> <<set $game_state.current_canvas = "538e115a-5000-4942-afc6-782fa559e36b">> <<set $game_state.current_node = "5e4fc887-0b39-4962-bfaa-0a2743bab02a">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_the_couch.breaking_apart") === -1) { vn.push("scene_the_couch.breaking_apart"); }<</script>> <<script>>advanceTime(60);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.showEffectNotification();<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "the_couch_complete");setup.applyAndNotifyFlag("player", null, "flirt_unlock");setup.applyAndNotifyFlag("player", null, "ethan_interested");<</script>> [[Say goodnight->Location_Living_Room]]
<p>Thunder cracks like a gunshot and the lights die. The house goes dark. No TV, no Wi-Fi, phones at single digits.</p><p>He digs candles out of the kitchen drawer. You open wine. The living room in candlelight feels like a different world — smaller, warmer, more honest.</p><img src="./videos/story/scene_rainy_day_power_out.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>You sit on the couch — the same couch that will matter later, but tonight it's innocent. Rain hammers the windows. And without screens to hide behind, you just... talk.</p> <<set $game_state.current_canvas = "1fdab88a-42a0-4960-b377-1a6bee3fb3e5">> <<set $game_state.current_node = "f106ccd0-7453-4a77-b0c4-6cbf19b9ecc2">> <<script>>setup.markCanvasTriggered("1fdab88a-42a0-4960-b377-1a6bee3fb3e5");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_rainy_day.power_out") === -1) { vn.push("scene_rainy_day.power_out"); }<</script>> <<nobr>> <<link "Tell him the truth about why you left" "Canvas_scene_rainy_day_Node_2">><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>The candlelight makes it easier to say the thing you've never said out loud.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/player.jpg" alt="You" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>You:</strong> I didn't leave for the program, Ethan. I left because I couldn't keep living in a house with you and pretending I didn't feel what I felt. Running was easier than staying and aching.</div></div><p>He's quiet for a long time. The rain fills the silence.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I knew. Part of me always knew. I just... I told myself you were just growing up. Leaving the nest. Normal stuff.</div></div><p>Neither topic is explicitly romantic. But both reveal vulnerability that daylight would never allow.</p> <<set $game_state.current_canvas = "1fdab88a-42a0-4960-b377-1a6bee3fb3e5">> <<set $game_state.current_node = "8c710dc9-1612-4b4f-a931-cb64690f0fd9">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_rainy_day.truth") === -1) { vn.push("scene_rainy_day.truth"); }<</script>> <<nobr>> <<link "Do you wish I hadn't come back?" "Canvas_scene_rainy_day_Node_3">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>Whatever he says, it's more honest than anything either of you has managed in two years. The storm rages outside but inside the candlelight makes everything feel suspended — outside of time, outside of consequence.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> You were always... chaos. The best kind.</div></div><p>The candles burn low. Neither of you moves to replace them. In the growing dark, his hand finds yours. Just that. Just hands. It's enough.</p> <<set $game_state.current_canvas = "1fdab88a-42a0-4960-b377-1a6bee3fb3e5">> <<set $game_state.current_node = "abffc159-29e4-4ea2-b417-199436e84022">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_rainy_day.his_answer") === -1) { vn.push("scene_rainy_day.his_answer"); }<</script>> <<script>>advanceTime(90);<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "rainy_day_complete");<</script>> [[Let the night end->Location_Living_Room]]
<p>His phone rings. The screen lights up: Madison.</p><p>He answers without looking at you. Loving words that sound rehearsed. 'Yes, everything's fine. She just got here. We're just catching up.'</p><p>You watch him perform. The way his voice changes — lighter, easier, the voice of someone who hasn't been awake at 3 AM thinking about his step-sister.</p><img src="./videos/story/scene_madison_calls_the_call.jpg" alt="Overhearing a phone call, conflicted looks exchanged between two people" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>He tells Madison he loves her. While looking directly at you. Something dark and wanting flickers across his face like a shadow.</p><p>When he hangs up, neither of you speaks. She isn't an abstract concept anymore. She's a real woman with a real voice who calls him 'babe' and talks about flower arrangements.</p> <<set $game_state.current_canvas = "8da6c91e-00e5-4da6-9ad6-2b7e3b66c562">> <<set $game_state.current_node = "29048f8a-0e3d-4926-bf0c-0b39c085ff3e">> <<script>>setup.markCanvasTriggered("8da6c91e-00e5-4da6-9ad6-2b7e3b66c562");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_madison_calls.the_call") === -1) { vn.push("scene_madison_calls.the_call"); }<</script>> <<nobr>> <<link "Say nothing. Just look at him." "Canvas_scene_madison_calls_Node_2">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> She's... she's great. Really.</div></div><p>He sets his phone face-down on the coffee table. He doesn't sound convinced. Neither are you.</p> <<set $game_state.current_canvas = "8da6c91e-00e5-4da6-9ad6-2b7e3b66c562">> <<set $game_state.current_node = "08b37d0d-2f3d-4507-b362-1d47b7c927c4">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_madison_calls.aftermath") === -1) { vn.push("scene_madison_calls.aftermath"); }<</script>> <<script>>advanceTime(30);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 3.0, true, null);setup.showEffectNotification();<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "madison_calls_complete");<</script>> [[Give him space->Location_Living_Room]]
<p>You can't sleep. The house is too quiet and your thoughts are too loud. You go downstairs for water and find him sitting at the kitchen island in the dark.</p><p>Both in sleep clothes. Both pretending this is normal. The moonlight through the window paints everything silver.</p><img src="./videos/activities/cant_sleep.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>He looks at you — your thin nightgown, bare shoulders — and doesn't look away. You feel his gaze like a physical thing.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Couldn't sleep either?</div></div><p>He reaches out and tucks a strand of hair behind your ear. His fingers brush your neck. He freezes.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I should... I should go back to bed.</div></div><p>His hand trembles slightly. Neither of you moves.</p> <<set $game_state.current_canvas = "05c8b646-7055-435b-95ab-5d912df91b2f">> <<set $game_state.current_node = "39570f67-c425-4f3a-beec-c87aeafe5bd5">> <<script>>setup.markCanvasTriggered("05c8b646-7055-435b-95ab-5d912df91b2f");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_sleepless_night.three_am") === -1) { vn.push("scene_sleepless_night.three_am"); }<</script>> <<nobr>> <<link "Stay. Just for a bit." "Canvas_scene_sleepless_night_Node_2">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>The moment stretches. Then breaks. You retreat to separate rooms, separate beds, separate lies about why your hearts are racing.</p><p>Sleep doesn't come any easier.</p> <<set $game_state.current_canvas = "05c8b646-7055-435b-95ab-5d912df91b2f">> <<set $game_state.current_node = "93aa1bbf-b85c-4939-a55a-f7ad2ee9038f">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_sleepless_night.parting") === -1) { vn.push("scene_sleepless_night.parting"); }<</script>> <<script>>advanceTime(30);<</script>> <<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_ethan", "corruption", "add", 2.0, true, null);setup.showEffectNotification();<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "sleepless_night_complete");<</script>> [[Go back to bed->Location_Kitchen]]
<p>You find the boxes while looking for something to do with yourself. Photo albums. High school. Prom. Beach trips. That summer you both try not to think about.</p><p>He appears in the garage doorway. Sees what you've found. Doesn't leave.</p><p>You look through them together. Heads bent close, shoulders pressed against each other. His finger traces a photo of you in that sundress from the Fourth of July.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I remember that night.</div></div><p>His voice has dropped. Your eyes meet. Something unspoken passes between you — something that's been waiting two years to be acknowledged.</p><img src="./videos/activities/garage_boxes_base.jpg" alt="Two people looking through old photos together, intimate proximity, heads close" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "cb00fcea-c923-48c6-b58a-bd18daeaa9dd">> <<set $game_state.current_node = "b87fb252-4c24-4321-af01-835afe85628e">> <<script>>setup.markCanvasTriggered("cb00fcea-c923-48c6-b58a-bd18daeaa9dd");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_old_photos.discovery") === -1) { vn.push("scene_old_photos.discovery"); }<</script>> <<nobr>> <<link "Why did you keep these?" "Canvas_scene_old_photos_Node_2">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>He doesn't answer right away. He looks at the photo, then at you. The silence stretches like taffy.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> We should probably put these away.</div></div><p>But neither of you moves. His hand is still touching yours where it rests on the album page. Neither of you pulls away.</p> <<set $game_state.current_canvas = "cb00fcea-c923-48c6-b58a-bd18daeaa9dd">> <<set $game_state.current_node = "1f9c314c-c3d6-4985-ad4c-a323e8301682">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_old_photos.lingering") === -1) { vn.push("scene_old_photos.lingering"); }<</script>> <<script>>advanceTime(45);<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "old_photos_complete");setup.applyAndNotifyFlag("player", null, "lingering_touch_unlock");<</script>> [[Put the albums back->Location_Garage]]
<p>The kitchen smells incredible. He's made your favorite — that pasta recipe your mom used to make before the divorce, the one you mentioned exactly once, years ago.</p><p>He remembered.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> Some things you don't forget.</div></div><p>He pours you a glass of wine and makes small talk about safe things — the neighborhood, his work, the wedding venue. But you catch him watching you when he thinks you're not looking. His eyes linger a beat too long.</p><img src="./videos/story/scene_welcome_dinner_dinner.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /> <<set $game_state.current_canvas = "7b969fdb-fb87-47d8-80ff-68a706ceb0dc">> <<set $game_state.current_node = "f9a99157-7a32-4237-b100-769569772437">> <<script>>setup.markCanvasTriggered("7b969fdb-fb87-47d8-80ff-68a706ceb0dc");<</script>> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_welcome_dinner.dinner") === -1) { vn.push("scene_welcome_dinner.dinner"); }<</script>> <<nobr>> <<link "Remember when we used to have dinner parties? Just us, pretending to be fancy?" "Canvas_scene_welcome_dinner_Node_2">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_ethan", "love", "add", 3.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br> <</nobr>>
<p>He clears the dishes, waving off your offer to help. You watch him move through the kitchen — efficient, comfortable, at home. This is his life now. A life you're not part of.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/ethan.jpg" alt="Ethan" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Ethan:</strong> I'm glad you're here. Really.</div></div><p>The way he says it — you're not sure if he means here for the wedding, or here in this kitchen, or just... here.</p> <<set $game_state.current_canvas = "7b969fdb-fb87-47d8-80ff-68a706ceb0dc">> <<set $game_state.current_node = "6a7a597d-513c-49a3-846d-9eaecc350fbb">> <<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("scene_welcome_dinner.after_dinner") === -1) { vn.push("scene_welcome_dinner.after_dinner"); }<</script>> <<script>>advanceTime(90);<</script>> <<script>>setup.applyAndNotifyFlag("player", null, "welcome_dinner_complete");setup.applyAndNotifyFlag("player", null, "ethan_comfortable");<</script>> [[Head upstairs->Location_Kitchen]]
<h2>Missing Media Files</h2> <p>No missing media files found.</p> <<link "← Back">><<run Engine.play(State.variables.last_game_passage || "Navigation")>><</link>>