{"release_id":"01a1998d-9802-0000-b0f7-33a23307f4ed","chunk_id":"01a1998d-993b-78f1-8414-324289cb90c2","file":"Pm61p6Kq.mjs","ignoreList":[],"mappings":"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","names":["friendly","key","scene","named","clip","upper","s","explainReason","reason","subject","toActivity","e","at","id","text","tone","detail","parseEventLog","raw","row","rows","line","KEY","KEEP","MIN_SAMPLES","FLOOR_MS","CEIL_MS","PLAUSIBLE_MS","read","parsed","recordWait","kind","ms","store","list","typicalWait","cap","sorted","a","b","mid","typicalLabel","CLIP_WAIT_S","LENGTHS","expectedClipMs","seconds","res","continuation","length","l","spans","out","parts","p","cells","c","IS_TABLE_ROW","IS_TABLE_RULE","IS_SLUG","IS_TRANSITION","IS_CUE","parseMarkdown","src","lines","para","flushPara","i","t","head","h","items","m","who","parenthetical","spoken","par","parseVisualBible","data","d","anchorOf","v","o","k","style","section","title","block","entries","name","renderDocument","body","bible","DEFAULT_VOICE","SCENE_COUNT_MIN","SCENE_COUNT_MAX","RENDER_TIMEOUT_SEC","RENDER_MAX_ATTEMPTS","RUN_CEILING_MS","ONE_CLIP_WS","ONE_CLIP_CEILING_MS","runCeilingFor","workspace","BASE","CATALOGUE","BY_KEY","loraFor","MAX_PICKS","shown","lengths","TOKENS_BY_SECONDS","lengthWords","priceParts","priceWords","linkClass","STEPS","root","$.sibling","$.child","a_1","$.set_class","a_2","a_3","a_4","a_5","a_6","a_7","ol","step","$$anchor","li","root_1","span","$.reset","p_1","strong","$.set_text","$.get","text_1","text_2","p_2","a_8","p_3","p_4","a_9","a_10","div","STUDIO_FAQ","f","div_1","root_2","h3","p_5","text_4","text_5","$.append","UPLOAD_BUDGET_BYTES","FIRST_EDGE","FALLBACK_EDGES","JPEG_QUALITY","HEIC_BRANDS","isHeic","file","heicToJpegBlob","n","decode","blob","url","resolve","img","sizeOf","encode","maxEdge","type","quality","w","scale","width","height","ctx","renamed","ext","shrinkForUpload","source","jpeg","pic","keepPng","firstType","first","edge","isPicture","forStatus","status","readReply","said","whyFailed","UPLOAD_TIMEOUT_MS","timeoutSignal","PROBE_TYPE","mediaSourceClass","liveSupported","SURFACE_KEY","SETUP_KEY","ONBOARDED_KEY","fallback","write","value","vs","setSurface","from","trackVoiceSession","warmGpu","rememberSetup","warmedAt","keepWarm","WARM_EVERY_MS","WARM_FOR_MS","openSheet","loadHers","loadTokens","opened","toastTimer","toast","msg","maybeOnboard","arrival","shouldOnboard","onboarded","loadSessions","r","hersAt","force","media","SILENCE","registerMedia","el","unlockMedia","openPlayer","fresh","closePlayer","onboarding","$.prop","$$props","$.state","onMount","sheet","player","chooser","parts_Sheet","parts_Player","parts_Chooser","$$render","consequent","button","button_1","classes","classes_1","$.set_attribute","classes_2","signedIn","$.each","node_1","span_1","SCENARIOS","document","blocks","$.noop","div_12","root_32","$.index","h4","root_34","text_25","p_13","root_35","root_37","$.template_effect","text_26","em","root_38","text_27","text_28","consequent_23","consequent_24","alternate_1","ul","root_40","item","li_1","root_41","strong_1","root_43","text_29","em_1","root_44","text_30","text_31","consequent_26","consequent_27","alternate_2","div_13","root_46","table","thead","tr","th","root_47","text_32","tbody","tr_1","root_48","cell","td","root_49","text_33","hr","root_50","p_14","root_51","text_34","p_15","root_52","text_35","div_14","root_53","p_16","span_7","root_54","text_37","consequent_33","p_17","root_55","text_36","div_15","root_56","p_18","p_19","text_39","text_40","consequent_25","consequent_28","consequent_29","consequent_30","consequent_31","consequent_32","consequent_35","statusPill","STATUS_LABEL","railList","root_4","offline","lastError","node_2","rail","root_5","text_3","node_3","node_4","div_2","root_6","root_7","planningWs","consequent_1","root_8","renderWs","consequent_2","root_9","lastTick","consequent_3","showDetails","consequent_4","$.set","confirmReply","canPress","$.derived","splitConfirm","div_3","root_11","ask","askAbout","bad","div_4","root_12","text_8","p_6","div_5","root_13","label_1","text_10","input_1","refBusy","$.delegated","guardUpload","attachRefs","shutMenus","pickKind","consequent_5","div_6","$.first_child","fragment_3","bit","button_2","root_15","text_11","suggesting","addMissing","consequent_6","text_9","d_1","div_7","root_16","cardClips","takes","angles","cardPrice","tokensForSend","composerShape","freeClip","div_8","root_17","button_3","node_14","now","clock","text_14","shotBusy","consequent_9","alternate","span_2","span_3","text_15","consequent_10","tokens","priceLabel","pressToClipMs","selfAnsweredBySending","acceptConfirm","d_2","div_9","node_13","p_7","root_22","p_8","root_23","text_19","consequent_12","consequent_13","p_9","node_16","span_4","root_24","consequent_14","p_10","root_25","span_5","root_26","consequent_15","text_21","consequent_16","p_11","fragment_7","button_4","text_22","sending","retryConfirm","consequent_17","p_12","root_28","$0","text_23","consequent_18","text_20","consequent_19","consequent_20","filmReel","div_10","root_29","button_5","div_11","film","filmKey","span_6","root_31","seamJumps","consequent_21","button_6","node_22","video_1","$.action","$$node","looping","still","fileUrl","$.event","CLIP_DRAG","moveInFilm","dropFromFilm","openFilmViewer","button_7","text_24","filmBusy","dropClipIntoFilm","exportFilm","videoCard","caption","clipKey","figure","root_57","video_2","node_28","figcaption","root_58","consequent_36","recoverVideo","starters","div_16","root_59","div_17","examples","ex","button_8","root_60","span_8","starterAt","useExample","$$value","starterRow","div_18","span_9","root_61","starterScrolled","starterHeld","mediaTile","button_9","root_62","video_3","tileEls","$1","$2","clipClock","tileRatio","shelfUrl","mediaPopup","takeTile","run","$$arg2","$.derived_safe_equal","$.fallback","chosen","takesAt","button_10","root_64","video_4","span_10","text_43","openTake","div_19","root_65","node_30","span_11","root_66","consequent_38","span_12","text_44","consequent_37","alternate_3","WS_SUFFIX","POLL_FAST_MS","POLL_SLOW_MS","QUIET_CYCLES","POLL_CLOSING_MS","CLOSING_FROM","DONE","DEAD","PLANNING_STEPS","CONSISTENCY_INSTRUCTION","ASSEMBLY_MSG","OFFLINE_TEXT","WEDGED_TEXT","mode","MODE_KEY","WELCOME_LINES","WELCOME_TEXT","welcomeId","showWelcome","chat","pushStudio","RESUME_KEY","SCENE_CHOICES","_","ASSEMBLE_RE","$.proxy","superseded","idSeq","mkId","pushItem","partial","pushError","brief","originalPitch","launchedBrief","latestPlanId","sceneCount","activeWs","simpleRun","sheetRun","sessionSlug","runSlug","startSession","slug","guest","GUEST_SESSION_KEY","announceSession","history","runKey","POINTER_KEY","startedAt","planningPoll","renderPoll","pollingActive","staleRun","docPhase","docAccepted","latestDocItem","docBody","docFile","docUrl","docTaskId","boardId","sidebarOpen","NAV_KEY","loadHistory","adoptForgottenRun","mine","renderIsSheet","persist","startPolling","listedSlug","$.user_effect","savedTurns","rebuiltChat","logRow","prompt","request","add","reopen","setNavOpen","saved","rebuilt","asBrief","reset","resumeFrom","dropFromHistory","SHEET_TITLE","runKind","historyDays","days","label","whenLabel","day","ts","today","same","y","board","tasks","x","phase","state","boardDone","shootBoard","arts","sceneNo","done","failed","retryCounts","shootElapsed","mmss","sec","approvalId","chain","shootsAnnounced","assemblySent","finalPosted","renderLaunching","stopArmed","stopping","writing","launchingSlug","stopWanted","stopNotice","genSlug","genFor","genState","genTimer","STOPPED_BEFORE_CHARGE","ALREADY_STARTED","settleStop","ended","endClipWait","endDeadClip","stageStartedAt","stageWaitFrom","stageWaitBlurUrl","stopRun","ids","runId","input","composer","seenActivity","RETRY_ALARM","retryWarned","pollActivity","target","call","evKey","routine","planning","st","refFiles","refError","loadRefFiles","problems","picked","fd","dropRef","pendingPhoto","holdPhoto","uploadSubject","wantTarget","small","reply","sheets","watchSheets","currentCharacter","describeRefFile","description","editingPlan","editTitle","editStory","editStyle","expanded","changeOpen","changeText","changeBusy","railOpen","clipPosted","failedNoted","preAssemblyIds","finalByNameOnly","errorNoted","offlineNoted","planningLaunchAttempts","renderLaunchAttempts","timer","quiet","lastSig","sawAllDone","op","ws","fileKeyOf","kindOf","firstFileOfKind","artifactId","fileKey","bust","q","keepClip","videoAttempts","fetchDocBody","composerHint","sendingSince","sendingFor","typicalPrompt","typicalClip","OVERDUE","promptOverdue","gateOpen","showExamples","selfAnswered","FILENAME_ONLY","FIRST_PERSON","SECOND_PERSON","SHEET_FORMAT","UNDER_AGE","HAS_PLACE","HAS_CAMERA","missingBits","chosenCharacter","FALLBACK_CLAUSE","basis","clause","stop","bell","j","flush","grow","rounds","last","continuing","words","composerPlaceholder","submit","askToSignUp","shrink","sheetFromRequest","confirmFromRequest","managerChat","refinePlan","planFromIdea","callShotPrompt","pin","askedAt","RESOLUTIONS","RES_KEYS","frameFor","orientation","long","short","wantSeconds","wantOrientation","wantRes","subjectFrom","drawStartedMs","sendingWhat","wantCharacter","wantLocation","addOpen","pillGrp","pillAt","panelAt","openPanelAt","ev","which","box","PANEL","open","fmtOpen","modeOpen","openPillAt","wrap","pillGroups","g","stageContinuable","pinSeam","contOffFor","spendConfirmChain","startContinue","MAX_AT_ONCE","saveSetup","lastRequest","rest","added","marker","brk","lead","settingsNow","chosenLocation","effAngles","settingsWritten","settingsTimer","settingsDiff","untrack","live","diffs","confirmHistory","changed","auto","cont","snap","ctl","deadline","batchLabel","reader","decoder","startFromCard","card","cardShort","affords","askForTokens","cardAngles","renderBatch","renderShot","continues","clips","price","itemId","stageNewest","handedOff","already","agreed","STAGE_UI","generateOnServer","continueFromRequest","shotFromRequest","prior","resolution","longest","frame","pinned","characters","page","round","watchGeneration","unwatchGeneration","session","look","landGeneration","stopPolling","refreshTokens","waiting","shot","drawn","takeRun","watchBatches","sendPrice","resumeGeneration","info","contInfo","part","chainOf","joinScene","joining","firstWords","locations","sheetBusy","renameCharacter","next","loadSheets","seed","previewSubject","previewBusy","why","voice","followPreview","job","started","r2","resumePreviews","sh","launchSheetRender","opts","spec","pendingSheet","renderSheet","saveSubject","sheetWatch","sheetsWorking","TURN_ETA_SEC","DRAW_ETA_SEC","etaSec","since","iso","turnStatus","eta","left","sessionsDrawing","drawingHere","sessionsDone","markedSeen","awaitedSheets","sheetShown","tick","wasRendering","awaited","sheetPosted","keepSheet","artifact","carried","prev","rewriteShot","launchShot","announce","NOT_ENOUGH_TOKENS","variants","ar","payload","batchWatch","cards","runs","moved","loadVerdicts","retryShot","errorItemId","shotItemId","err","respin","setShotSeconds","toggleLora","lora","picks","kept","verdict","rate","outcome","diagnose","takesFrom","readyTakes","index","shutTake","swipeX","swipeY","swipeStart","swipeEnd","dx","dy","stepTake","keepTake","spareTakeCards","have","spare","filmOpen","films","filmPopup","mediaOpen","shelf","shelfSettled","loadMedia","whenSeen","node","eye","pool","pins","loadPool","togglePin","STARTER_MS","reduced","wide","showStarter","liveAt","STEP_MS","stagePhase","pair","loadFilms","filmSeconds","inFilm","filmPart","saveFilm","addClipToFilm","addToFilm","to","rec","doc","filmAt","filmReturn","shutFilmViewer","stepFilm","nextShot","tileEyes","sheetImageMissing","lastFrame","park","grabFrames","video","count","dur","wasAt","canvas","reject","diagnosing","charFromClipBusy","charFromClipError","countLabel","anglesApply","effAtOnce","PENDING_KEY","PENDING_TTL_MS","flow","press","openAuthSheet","recordGuestSession","resumePendingPress","afterSignIn","wasGuest","buyOpen","buyReason","buySource","need","askForSessionTokens","dropArmed","keptEdits","editKept","field","updateKept","dropSheet","order","seen","cur","it","fix","diagnosed","diagnoseError","frames","fixBusy","renderFix","openFix","setLoraStrength","setBaseStrength","resetLoraStrength","setShotOrientation","setMode","callPlan","idea","feedback","openEdit","saveEdit","launchingPlanning","callLaunch","launchPlanning","launchRender","approved","extras","loadedWf","failedWf","loadedSk","failedSk","signature","refreshClipEstimate","pollDelay","elapsed","answered","sig","processRender","processPlanning","terminal","finished","anyDead","allPosted","made","madeFile","dead","resetTaskByKey","taskKey","instructions","requestChange","artifactKey","idx","isShootTask","shoots","stage","clipArts","isFinal","mapStatus","ptasks","rtasks","sched","asm","railDone","railRunning","railSummary","elapsedLabel","snapshot","withBodies","persistTimer","forget","stageSel","stageAutoplayId","COMPOSER_MAX_LINES","max","want","isLong","bottomEl","scrollEl","isSheetWorkspace","stageClips","stageClip","stageAllClips","stageThumbs","links","known","stageShownWs","stageShown","stageShownUrl","clipSecs","stageError","after","lastSaid","errs","stageNow","stageClockFrom","viewedClips","noteClipViewed","clipId","trackStudioClipViewed","stageVideoW","stageVidW","stageVidH","cleanupAudio","stageEtaMs","renderInFlight","stageElapsedMs","stageScroll","threadSeen","stageThread","near","floorHold","spacer","thread","atFloor","hold","pad","room","onScroll","watch","stageRound","roundRequest","COMPOSER_FLOOR","composerCap","makingSubject","stageMaking","stagePhaseIsWorking","showStrip","stagePending","stageSheet","charView","stageDrawing","stageSubjectId","stageSubject","VOICE_PRESETS","voiceDraft","saveVoice","stageTakes","atBottom","TAIL_SLACK","onTranscriptScroll","scrollToBottom","behavior","seenLen","len","audible","onPlay","onHidden","was","right","pointer","resumed","handed","setSessionSurface","dropFromAddress","wantSession","ready","READY_SESSIONS","openSessionSheet","again","clean","replaceState","settleAnyway","fast","watchdog","faqJsonLd","jsonLdScript","studioFaqSchema","$.head","$.document","STUDIO_TITLE","$.window","div_20","fragment_15","header","button_11","button_12","button_13","root_67","consequent_39","aside","node_31","div_21","door","button_14","root_68","svg","node_33","consequent_40","alternate_4","span_13","sessionState","text_45","button_15","node_32","svg_1","nav","SessionList","node_34","node_36","p_20","fragment_19","text_46","node_37","current","working","drawing","div_22","root_73","button_16","span_14","span_15","root_76","consequent_41","consequent_42","consequent_43","span_16","node_38","text_47","span_17","root_77","text_48","span_18","root_78","span_19","root_79","text_50","consequent_45","consequent_46","button_17","p_21","root_80","consequent_47","alternate_5","div_23","root_81","div_24","button_18","root_82","video_5","button_19","node_41","consequent_48","node_42","div_25","root_83","consequent_49","main","div_26","root_84","$.await","node_44","__vitePreload","surface","surface_default","consequent_50","div_27","node_43","div_28","div_29","div_30","root_86","button_20","span_20","span_21","node_47","div_31","root_87","node_48","consequent_51","consequent_52","div_32","root_88","div_33","root_89","node_51","div_34","root_91","consequent_53","node_53","here","button_21","root_93","video_6","node_55","span_22","root_94","text_53","consequent_54","consequent_55","consequent_56","div_35","node_50","div_36","node_56","div_37","root_95","node_57","video_7","root_96","div_38","root_97","consequent_57","alternate_6","div_39","div_40","span_23","p_22","div_41","root_98","button_22","div_42","root_99","div_43","button_23","text_56","button_24","button_25","root_100","consequent_59","alternate_7","gone","div_44","root_101","div_45","p_23","span_24","fragment_26","text_57","span_25","root_104","text_58","consequent_61","alternate_8","node_60","p_24","root_105","text_59","div_46","root_106","div_47","div_48","root_108","p_25","text_60","consequent_63","node_62","$.attach","retryPrice","div_49","root_109","p_26","div_50","fragment_28","button_26","text_62","span_26","button_27","button_28","root_111","consequent_65","alternate_9","text_61","shownId","six","turn","isChar","refUrl","view","div_51","root_112","div_52","img_1","root_113","$.replay_events","video_8","root_114","img_2","root_115","img_3","root_116","img_4","root_117","span_27","root_118","consequent_70","consequent_71","alternate_10","div_53","div_54","input_2","$.remove_input_defaults","button_29","div_55","root_119","input_3","div_56","button_30","root_120","text_64","consequent_72","div_57","node_65","button_31","root_121","node_66","span_28","root_122","consequent_73","span_29","text_65","p_27","root_123","span_30","text_66","p_28","root_124","p_29","root_125","consequent_74","alternate_11","consequent_75","$.set_value","h2","fragment_29","br","root_128","consequent_77","node_69","div_58","fragment_31","button_32","consequent_78","StudioAbout","node_71","sws","shownPart","sv","div_59","root_131","div_60","node_72","video_9","root_132","$.bind_element_size","$.bind_property","consequent_80","div_61","span_31","node_73","span_32","root_134","button_33","root_135","consequent_82","span_33","node_75","button_34","fragment_34","button_35","span_34","fragment_35","node_76","text_69","consequent_84","alternate_13","node_77","button_36","root_140","consequent_85","alternate_14","consequent_83","node_78","div_62","root_141","div_63","node_79","fx","div_64","fragment_38","p_30","p_31","root_143","consequent_86","div_65","button_37","text_72","button_38","text_70","p_32","fragment_39","button_39","text_73","consequent_87","alternate_15","consequent_88","$.set_style","consequent_60","consequent_62","consequent_64","consequent_66","consequent_76","consequent_79","consequent_89","div_66","root_145","node_81","itemAt","p_33","root_147","consequent_91","consequent_92","div_67","root_150","p_34","text_76","h2_1","root_151","br_1","root_153","consequent_96","div_68","root_154","p_35","text_78","article","root_155","div_69","span_35","div_70","div_71","root_156","div_72","span_36","svg_2","root_157","span_37","root_158","svg_3","root_159","span_38","root_160","consequent_101","alternate_16","span_39","text_80","node_86","button_40","root_161","text_81","span_40","root_162","span_41","root_163","span_42","root_164","consequent_105","div_73","root_165","root_167","consequent_106","consequent_107","div_74","node_88","button_41","button_42","div_75","form","fragment_45","label_2","input_4","button_43","consequent_108","consequent_109","p_36","root_169","div_76","root_170","button_44","text_82","span_43","consequent_111","consequent_112","alternate_17","consequent_110","alternate_18","article_1","root_174","div_77","span_44","root_175","text_86","consequent_114","p_37","consequent_115","div_78","root_177","div_79","root_178","span_45","svg_4","root_179","svg_5","root_180","span_46","root_181","consequent_117","alternate_19","span_47","span_48","span_49","root_184","text_91","consequent_119","consequent_120","alternate_20","text_88","p_38","root_186","consequent_121","alternate_21","div_80","root_187","span_50","div_81","p_39","text_93","node_97","details","root_188","p_40","text_94","consequent_123","newest","stalled","div_82","root_190","p_41","button_45","fragment_47","text_96","span_51","consequent_126","text_95","article_2","root_192","div_83","span_52","img_5","fragment_48","node_100","p_42","root_194","consequent_128","textarea","fragment_49","$.remove_textarea_child","node_101","p_43","root_196","consequent_130","div_84","p_44","button_46","text_103","consequent_129","alternate_22","div_85","root_197","dn","dv","p_45","fragment_50","span_53","p_46","root_199","consequent_131","div_86","node_104","label_3","fragment_51","input_5","consequent_132","label_4","node_105","text_106","div_87","input_6","node_106","button_47","root_201","text_107","consequent_133","button_48","text_104","p_47","root_202","span_54","text_110","label_5","fragment_53","input_7","consequent_136","label_6","node_107","text_111","div_88","input_8","button_49","text_112","p_48","consequent_134","consequent_135","alternate_23","div_89","root_205","p_49","root_206","span_55","label_7","fragment_54","div_90","input_9","button_50","text_115","consequent_138","alternate_24","consequent_137","consequent_139","text_98","article_3","root_208","div_91","h3_1","span_56","root_209","text_116","consequent_141","span_57","text_117","details_1","label_8","textarea_1","p_50","root_210","consequent_142","div_92","node_110","div_93","node_111","div_94","root_211","span_58","span_59","root_212","input_10","fragment_55","button_51","text_121","span_60","text_122","consequent_143","alternate_25","text_119","div_95","span_61","root_214","consequent_144","div_96","root_215","span_62","root_216","text_123","span_63","text_124","div_97","fragment_56","on","button_52","root_218","text_125","span_64","root_219","consequent_146","node_116","div_98","root_221","span_65","input_11","button_53","text_128","span_66","text_129","text_127","consequent_147","consequent_145","alternate_26","div_99","fragment_58","div_100","button_54","root_223","text_130","span_67","root_224","div_101","fragment_59","node_121","button_55","root_226","text_132","div_102","val","$$item","$$array","button_56","root_227","text_133","consequent_148","alternate_27","p_51","root_228","text_134","consequent_149","p_52","root_229","text_135","consequent_150","div_103","node_124","button_57","text_136","button_58","span_68","consequent_151","article_4","root_230","input_12","fragment_60","textarea_2","textarea_3","div_104","button_59","button_60","h3_2","fragment_61","p_53","root_233","text_139","consequent_154","p_54","node_126","p_55","button_61","fragment_62","text_142","node_128","p_56","root_235","text_143","consequent_155","consequent_156","p_57","node_127","consequent_157","div_105","root_237","button_62","text_146","button_63","p_58","root_238","consequent_158","consequent_159","consequent_153","alternate_28","art","isCurrent","article_5","root_239","h3_3","div_106","fragment_63","node_132","node_133","button_64","root_241","text_148","p_59","root_243","text_149","consequent_162","alternate_29","div_107","root_244","p_60","root_245","p_61","root_246","div_108","fragment_65","button_65","button_66","form_1","fragment_66","label_9","input_13","button_67","consequent_165","consequent_163","consequent_164","alternate_30","consequent_166","text_147","p_62","root_249","text_150","div_109","root_250","p_63","text_152","consequent_169","consequent_170","div_110","div_111","node_139","span_69","fragment_70","text_153","consequent_171","span_70","root_256","consequent_172","span_71","node_141","text_155","consequent_173","node_142","p_64","root_257","consequent_174","node_143","p_65","root_258","text_156","scenic","div_112","root_259","div_113","ci","others","div_114","root_261","div_115","button_68","node_146","span_72","root_263","button_69","root_264","button_70","root_265","text_157","consequent_178","button_71","root_266","text_158","consequent_179","span_73","node_149","button_72","fragment_74","button_73","span_74","root_268","span_75","root_269","span_76","root_271","text_159","consequent_183","button_74","node_151","text_160","consequent_181","consequent_182","consequent_184","consequent_180","p_66","root_272","text_161","p_67","root_273","consequent_186","consequent_185","consequent_187","div_116","root_275","p_68","p_69","root_276","consequent_188","div_117","node_155","button_75","text_164","button_76","button_77","text_162","consequent_189","consequent_190","consequent_93","consequent_95","consequent_97","consequent_98","consequent_122","consequent_124","consequent_125","consequent_127","consequent_140","consequent_152","consequent_160","consequent_167","consequent_168","consequent_175","consequent_191","p_70","root_277","span_77","span_78","root_278","consequent_192","span_79","fragment_77","consequent_193","consequent_194","consequent_195","div_118","node_159","consequent_90","alternate_32","div_119","node_49","button_78","root_281","consequent_196","div_120","node_161","div_121","$.clsx","div_122","root_285","div_123","root_286","span_83","text_171","input_14","button_79","consequent_200","div_124","root_287","p_72","button_80","consequent_201","div_125","root_288","p_73","button_81","consequent_202","div_126","root_289","node_168","node_169","p_74","root_291","span_84","span_85","text_174","consequent_203","consequent_204","consequent_206","div_127","div_128","ModeSwitch","node_171","div_129","root_294","p_75","button_82","root_295","span_86","text_177","span_87","consequent_207","consequent_208","div_130","div_131","grp","div_132","root_296","button_83","root_297","node_175","span_88","root_298","consequent_209","text_178","div_133","node_174","button_84","button_85","node_176","img_6","fragment_84","span_89","text_179","consequent_210","alternate_33","node_177","button_86","root_301","img_7","span_90","text_180","consequent_211","node_178","span_91","root_302","node_179","button_87","root_304","text_181","button_88","root_305","node_181","span_92","root_306","consequent_213","svg_6","text_182","alternate_34","consequent_212","button_89","text_183","button_90","text_184","svg_7","div_134","node_182","clipUrl","bandOnPhone","div_135","root_308","div_136","div_137","node_184","button_91","node_185","video_10","root_310","consequent_216","button_92","node_186","img_8","root_311","span_93","root_312","consequent_217","alternate_35","button_93","p_80","root_316","consequent_220","consequent_221","consequent_223","div_138","root_318","div_139","p_82","p_83","button_94","text_190","text_191","consequent_224","div_140","root_319","span_95","root_320","button_95","img_9","span_96","button_96","text_192","button_97","root_321","consequent_225","alternate_36","span_97","root_322","img_10","span_98","button_98","text_193","consequent_226","button_99","root_323","text_194","consequent_227","button_100","node_195","text_195","consequent_228","button_101","root_324","consequent_229","div_141","root_325","$$array_1","button_102","root_326","span_99","node_198","svg_8","root_327","svg_9","root_328","consequent_230","alternate_37","span_100","span_101","span_102","svg_10","root_329","consequent_231","consequent_232","div_142","root_330","div_143","button_103","span_103","button_104","text_199","div_144","div_145","sheetState","div_146","root_331","button_105","span_104","span_105","node_201","svg_11","root_332","consequent_233","node_202","img_11","root_333","consequent_234","span_106","text_200","node_203","span_107","root_335","root_336","consequent_235","consequent_236","div_147","root_337","button_106","button_107","button_108","root_338","consequent_237","alternate_38","consequent_238","text_198","consequent_239","div_148","root_339","button_109","span_108","node_207","axis","other","div_149","root_340","span_109","span_110","text_201","span_111","off","over","button_110","root_341","text_202","p_84","root_342","p_85","root_343","text_203","consequent_241","consequent_240","consequent_242","div_150","root_344","div_151","span_112","button_111","root_345","text_204","div_152","span_113","button_112","root_346","text_205","div_153","span_114","$$array_2","button_113","root_347","span_115","node_210","div_154","root_348","span_116","$$array_3","button_114","root_349","text_207","consequent_243","consequent_244","div_155","root_350","node_212","span_117","root_351","span_118","button_115","text_208","span_119","root_352","consequent_245","consequent_246","div_156","root_353","span_120","span_121","button_116","span_122","text_209","text_210","consequent_247","div_157","node_214","button_117","fragment_90","div_158","label_10","node_216","span_123","root_355","svg_12","root_356","consequent_248","alternate_39","input_15","label_11","root_357","node_217","span_124","root_358","svg_13","root_359","consequent_250","alternate_40","input_16","consequent_249","alternate_41","textarea_4","node_215","node_218","div_159","root_360","button_118","root_361","text_211","consequent_251","button_119","node_219","span_125","root_362","svg_14","root_363","consequent_252","alternate_42","aside_1","root_364","div_160","p_86","root_365","p_87","root_366","consequent_253","consequent_254","div_161","node_221","node_222","div_162","root_367","button_120","root_368","div_163","fragment_91","button_121","text_213","button_122","alternate_43","consequent_255","consequent_256","consequent_257","div_164","root_370","div_165","root_371","consequent_258","alternate_44","div_166","root_372","button_123","node_227","button_124","fragment_92","button_125","consequent_259","div_167","node_228","$.key","video_11","root_374","div_168","span_126","div_169","root_375","button_126","root_376","video_12","consequent_260","text_214","consequent_261","div_170","root_378","button_127","node_232","button_128","fragment_94","button_129","consequent_262","div_171","node_233","video_13","root_380","div_172","span_127","span_128","root_381","text_216","consequent_263","span_129","root_382","button_130","root_383","consequent_264","alternate_45","span_130","root_385","button_131","root_386","consequent_265","alternate_46","consequent_266","div_173","root_387","consequent_267","text_215","consequent_268","consequent_269","BuyTokensModal","node_239","Sessions","div_174","root_389","button_132","video_14","button_133","consequent_270","div_175","root_390","button_134","video_15","p_88","text_217","consequent_271","div_176","root_391","button_135","p_89","div_177","button_136","root_392","video_16","$$action_arg","span_131","text_219","node_244","span_132","root_393","consequent_272","text_218","consequent_273"],"sources":["../../../../../../src/lib/studio/activity.ts","../../../../../../src/lib/studio/timings.ts","../../../../../../src/lib/studio/render-doc.ts","../../../../../../src/lib/studio/types.ts","../../../../../../src/lib/studio/loras.ts","../../../../../../src/lib/components/studio/StudioAbout.svelte","../../../../../../src/lib/studio/shrink.ts","../../../../../../src/lib/studio/reply.ts","../../../../../../src/lib/studio/session/support.ts","../../../../../../src/lib/studio/session/state.svelte.ts","../../../../../../src/lib/studio/session/Sessions.svelte","../../../../../../src/lib/studio/session/ModeSwitch.svelte","../../../../../../src/lib/studio/session/SessionList.svelte","../../../../../../src/routes/studio/+page.svelte"],"sourcesContent":["/** The harness's event log, turned into sentences.\n *\n *  The harness narrates itself well — dispatches, policy rejections, root-cause\n *  analyses — but only to whoever runs curl against get-event-log. From the\n *  studio a rejected task and a slow task look identical: both are \"running\",\n *  for as long as the harness keeps retrying. That gap has cost this project\n *  several afternoons, so this file exists to close it.\n *\n *  Pure: events in, display rows out. No fetching, no state.\n */\n\n/** One line as the harness emits it. Everything past `t` and `event` varies by\n *  event type, so the fields are optional and read defensively. */\nexport interface HarnessEvent {\n\tt?: string;\n\tevent?: string;\n\tkey?: string;\n\ttaskId?: string;\n\tattempt?: number;\n\treason?: string;\n\tsummary?: string;\n\tmessage?: string;\n}\n\nexport type ActivityTone = 'step' | 'good' | 'warn' | 'bad';\n\nexport interface ActivityRow {\n\t/** Stable across polls, so a row already shown is never shown twice. */\n\tid: string;\n\tat: number;\n\ttone: ActivityTone;\n\ttext: string;\n\t/** The harness's own words, when they add something the sentence cannot. */\n\tdetail?: string;\n}\n\n/** Task keys are machine names; nobody should have to read `write_visual_bible`\n *  in a transcript. Scene keys are numbered rather than listed, because there\n *  can be any number of them. */\nexport function friendly(key: string | undefined): string {\n\tif (!key) return 'a task';\n\tconst scene = /^shoot_scene_(\\d+)$/.exec(key);\n\tif (scene) return `Scene ${scene[1]}`;\n\tconst named: Record<string, string> = {\n\t\twrite_screenplay: 'the screenplay',\n\t\tcharacter_table: 'the cast list',\n\t\tcreate_scenes: 'the scene list',\n\t\twrite_art_direction: 'the art direction',\n\t\twrite_visual_bible: 'the visual bible',\n\t\tschedule_video_renders: 'the shooting plan',\n\t\tuser_reference_material: 'your reference files'\n\t};\n\tif (named[key]) return named[key];\n\tconst clip = /^scene_(\\d+)_clip$/.exec(key);\n\tif (clip) return `the Scene ${clip[1]} clip`;\n\treturn key.replace(/_/g, ' ');\n}\n\n/** Capitalise for the start of a sentence without touching the rest — \"the\n *  screenplay\" becomes \"The screenplay\", \"Scene 1\" stays \"Scene 1\". */\nfunction upper(s: string): string {\n\treturn s.charAt(0).toUpperCase() + s.slice(1);\n}\n\n/** A reason arrives prefixed with its class: `policy-rejected: …`,\n *  `model-error: …`, `init-error: …`. The prefix decides the sentence; the rest\n *  is the harness's own explanation and is kept verbatim, because it is usually\n *  the only thing that says what to actually change. */\nfunction explainReason(reason: string, subject: string): { text: string; tone: ActivityTone } {\n\tconst kind = /^([a-z-]+):/.exec(reason.trim())?.[1] ?? '';\n\n\tswitch (kind) {\n\t\tcase 'policy-rejected':\n\t\t\treturn {\n\t\t\t\ttone: 'warn',\n\t\t\t\ttext: `${upper(subject)} was sent back — the quality gate refused it. Trying again.`\n\t\t\t};\n\t\tcase 'model-error':\n\t\t\treturn { tone: 'bad', text: `${upper(subject)} hit a model error. Trying again.` };\n\t\tcase 'init-error':\n\t\t\treturn {\n\t\t\t\ttone: 'bad',\n\t\t\t\ttext: `${upper(subject)} could not start at all — the agent failed to initialise.`\n\t\t\t};\n\t\tdefault:\n\t\t\treturn { tone: 'warn', text: `${upper(subject)} is being retried.` };\n\t}\n}\n\n/** Turn one harness event into a row, or null for events with nothing to say to\n *  a person. */\nexport function toActivity(e: HarnessEvent): ActivityRow | null {\n\tconst at = e.t ? Date.parse(e.t) : Date.now();\n\tconst subject = friendly(e.key);\n\tconst id = `${e.t ?? ''}|${e.event ?? ''}|${e.key ?? ''}|${e.attempt ?? ''}`;\n\n\tswitch (e.event) {\n\t\tcase 'dispatch':\n\t\t\treturn { id, at, tone: 'step', text: `Started ${subject}.` };\n\n\t\tcase 'complete':\n\t\t\treturn { id, at, tone: 'good', text: `${upper(subject)} is done.` };\n\n\t\tcase 'artifact-received':\n\t\t\treturn { id, at, tone: 'good', text: `${upper(subject)} came back.` };\n\n\t\tcase 'subgraph-ingested':\n\t\t\treturn { id, at, tone: 'step', text: 'The shooting tasks are scheduled.' };\n\n\t\tcase 'redispatch': {\n\t\t\tconst { text, tone } = explainReason(e.reason ?? '', subject);\n\t\t\t// The harness's own words go under the sentence rather than into it:\n\t\t\t// they are long, they are written for an engineer, and they are the\n\t\t\t// part worth reading when the sentence is not enough.\n\t\t\tconst detail = (e.reason ?? '').replace(/^[a-z-]+:\\s*/, '').trim() || undefined;\n\t\t\treturn { id, at, tone, text, detail };\n\t\t}\n\n\t\tcase 'workflow-load-error':\n\t\t\t// A render workflow that did not load is not a warning about a task —\n\t\t\t// it is a capability the whole production no longer has, and it shows up\n\t\t\t// as \"a task\" because the event carries no key. Say what it is, and keep\n\t\t\t// the harness's own message: it names the workflow.\n\t\t\treturn {\n\t\t\t\tid,\n\t\t\t\tat,\n\t\t\t\ttone: 'bad',\n\t\t\t\ttext: 'A render workflow could not be loaded — anything that needed it cannot run.',\n\t\t\t\tdetail: (e.message ?? e.reason ?? '').trim() || undefined\n\t\t\t};\n\n\t\tcase 'rca':\n\t\t\treturn {\n\t\t\t\tid,\n\t\t\t\tat,\n\t\t\t\ttone: 'warn',\n\t\t\t\ttext: `The crew looked into why ${subject} failed.`,\n\t\t\t\tdetail: (e.summary ?? '').trim() || undefined\n\t\t\t};\n\n\t\tdefault:\n\t\t\t// Unknown event types are shown rather than dropped — a new one that\n\t\t\t// mattered would otherwise be invisible exactly like the rejections\n\t\t\t// this file was written for.\n\t\t\tif (!e.event) return null;\n\t\t\treturn { id, at, tone: 'step', text: `${e.event.replace(/[-_]/g, ' ')} — ${subject}` };\n\t}\n}\n\n/** The event log arrives as newline-delimited JSON. A malformed line is skipped\n *  rather than throwing: this is a progress display, and it must never be the\n *  thing that breaks the run it is describing. */\nexport function parseEventLog(raw: unknown): ActivityRow[] {\n\t// Three shapes, because the log arrives as all three.\n\t//\n\t// The harness returns newline-separated JSON as one string, and the proxy\n\t// parses whatever comes back. With several events that string is not itself\n\t// valid JSON, so it survives as a string and this worked. With exactly ONE\n\t// event it IS valid JSON, the proxy parses it a second time, and an object\n\t// arrives here — where a string was the only thing accepted, so the feed went\n\t// silent precisely when a run had a single event in it. That is the first\n\t// moment of every run: the operator saw no \"Started clip 1.\" at all, and the\n\t// page had already dropped its own announcement on the grounds that this feed\n\t// carried it.\n\tif (Array.isArray(raw)) return raw.flatMap((e) => parseEventLog(e));\n\tif (raw && typeof raw === 'object') {\n\t\tconst row = toActivity(raw as HarnessEvent);\n\t\treturn row ? [row] : [];\n\t}\n\tif (typeof raw !== 'string' || !raw.trim()) return [];\n\tconst rows: ActivityRow[] = [];\n\tfor (const line of raw.trim().split('\\n')) {\n\t\tif (!line.trim()) continue;\n\t\ttry {\n\t\t\tconst row = toActivity(JSON.parse(line) as HarnessEvent);\n\t\t\tif (row) rows.push(row);\n\t\t} catch {\n\t\t\t/* skip */\n\t\t}\n\t}\n\treturn rows;\n}\n","/** How long things usually take here.\n *\n *  A waiting line that counts up is honest but half a sentence: `4m 59s` means\n *  nothing on its own, because the reader cannot tell whether the answer is due\n *  at five minutes or at twenty. That is the difference between waiting and\n *  wondering whether it has hung.\n *\n *  The harness cannot supply the other half — no step count reaches the studio,\n *  and the sampler's `steps` is a setting sent *into* the workflow, not a\n *  readout coming back. So the short waits — the brief being written, the\n *  read-back — are learned from this machine's own finished runs, a median of\n *  the last few. A render is not: see expectedClipMs, which is a table.\n *\n *  Median rather than mean: one run that sat behind a slow answer is normal, and\n *  it would drag an average somewhere useless for every run after it.\n *\n *  Pure and local. No fetching, no server, nothing shared between machines.\n */\n\nconst KEY = 'auteur-studio-timings';\n\n/** The waits worth measuring. `prompt` is the model round trip that writes the\n *  shot; `confirm` is the read-back. Renders are not recorded: a browser sees\n *  too few of them, and not by length or resolution, to learn anything the\n *  table below does not already say better. */\nexport type WaitKind = 'prompt' | 'confirm';\n\n/** Enough to be a median rather than an anecdote, few enough that a change in\n *  the machine or the model shows up within a session's worth of runs. */\nconst KEEP = 24;\nconst MIN_SAMPLES = 3;\n\n/** A run interrupted by a reload, or a clock that moved, produces a duration\n *  that is not a duration. Both ends are well outside anything real. */\nconst FLOOR_MS = 1_000;\nconst CEIL_MS = 2 * 60 * 60 * 1_000;\n\n/** What each wait can plausibly take. Past these it is a tab that slept, not a\n *  wait.\n *\n *  Enforced on READ as well as write, which is the part that matters. Samples\n *  already on disk can be poisoned — the clip samples that were once kept here\n *  read 6746, 2557, 5428, 5486 seconds, a median of ninety minutes — and a rule\n *  about what is shown has to hold for what is already stored, or the fix only\n *  helps people who have never used the app. */\nconst PLAUSIBLE_MS: Partial<Record<WaitKind, number>> = {\n\tprompt: 5 * 60 * 1_000,\n\tconfirm: 30 * 1_000\n};\n\ntype Store = Partial<Record<WaitKind, number[]>>;\n\nfunction read(): Store {\n\ttry {\n\t\tconst raw = localStorage.getItem(KEY);\n\t\tif (!raw) return {};\n\t\tconst parsed = JSON.parse(raw) as unknown;\n\t\tif (!parsed || typeof parsed !== 'object') return {};\n\t\treturn parsed as Store;\n\t} catch {\n\t\treturn {};\n\t}\n}\n\n/** Remember one completed wait. Called on the success path only — a failed run\n *  tells you how long the failure took, which is not what the line promises. */\nexport function recordWait(kind: WaitKind, ms: number): void {\n\tif (!Number.isFinite(ms) || ms < FLOOR_MS || ms > (PLAUSIBLE_MS[kind] ?? CEIL_MS)) return;\n\ttry {\n\t\tconst store = read();\n\t\tconst list = [...(store[kind] ?? []), Math.round(ms)].slice(-KEEP);\n\t\tlocalStorage.setItem(KEY, JSON.stringify({ ...store, [kind]: list }));\n\t} catch {\n\t\t/* private mode, a full quota — the estimate is a courtesy, not a feature */\n\t}\n}\n\n/** The median of what has been seen, or nothing until there is enough of it.\n *  Nothing ships for these two: they are fast enough that a number beside them\n *  is noise, and they have their own wording for being slower than usual. */\nexport function typicalWait(kind: WaitKind): number | null {\n\tconst cap = PLAUSIBLE_MS[kind] ?? CEIL_MS;\n\tconst list = read()[kind]?.filter((ms) => ms <= cap);\n\tif (!list || list.length < MIN_SAMPLES) return null;\n\tconst sorted = [...list].sort((a, b) => a - b);\n\tconst mid = Math.floor(sorted.length / 2);\n\treturn sorted.length % 2 ? sorted[mid] : Math.round((sorted[mid - 1] + sorted[mid]) / 2);\n}\n\n/** Deliberately rounder than the elapsed clock beside it. The elapsed number is\n *  precise because it is a measurement; this one is an expectation, and writing\n *  it as `4m 47s` would claim a precision it does not have — and would make the\n *  two numbers look like they were meant to be compared digit by digit. */\nexport function typicalLabel(ms: number): string {\n\tconst s = Math.round(ms / 1000);\n\tif (s < 20) return `about ${Math.max(5, Math.round(s / 5) * 5)}s`;\n\tif (s < 90) return `about ${Math.round(s / 10) * 10}s`;\n\tconst m = Math.round(s / 60);\n\treturn `about ${m}m`;\n}\n\n/** How long a render takes, in seconds, launch to clip: by what is being made,\n *  its resolution and its length.\n *\n *  Written out, not computed. It replaces a straight line through two runs —\n *  162 s plus 21.3 s a second — that knew nothing of resolution, so it promised\n *  a fifteen second 864p clip eight minutes when those took twelve, and a\n *  fifteen second 576p one eight when those took six.\n *\n *  Every number is the production render log's median for its row over the two\n *  weeks before 2026-09-27, less what RAT-494 took out that day: about seventy\n *  seconds of preflight on every render, plus RIFE's share, which grows with\n *  the frames. Five seconds at 576p was measured after the change — 178 s over\n *  three renders against 258 s before. Rounded to the half minute.\n *\n *  Rows marked as placed had too few renders for a median — 288p, a short 864p\n *  clip, most 864p continuations — and are set from their neighbours, on the\n *  slow side. Replace them when the log has them.\n */\nconst CLIP_WAIT_S = {\n\tnew: {\n\t\t'288p': { 5: 120, 10: 150, 15: 180 }, // placed: three renders at 5 s, none longer\n\t\t'576p': { 5: 180, 10: 210, 15: 270 },\n\t\t'864p': { 5: 300, 10: 360, 15: 570 } // 5 s placed: one render\n\t},\n\tcontinuation: {\n\t\t'288p': { 5: 150, 10: 180, 15: 210 }, // placed: none rendered\n\t\t'576p': { 5: 210, 10: 240, 15: 300 },\n\t\t'864p': { 5: 390, 10: 450, 15: 750 } // 5 s and 10 s placed\n\t}\n} as const;\n\n/** The three lengths the composer offers. Anything else takes the next one up —\n *  a wait quoted long is a nicer surprise than one quoted short. */\nconst LENGTHS = [5, 10, 15] as const;\n\nexport function expectedClipMs(seconds: number, res = '576p', continuation = false): number {\n\tconst rows = CLIP_WAIT_S[continuation ? 'continuation' : 'new'];\n\tconst row = rows[res as keyof typeof rows] ?? rows['576p'];\n\tconst s = Number(seconds);\n\tconst length = Number.isFinite(s) && s > 0 ? (LENGTHS.find((l) => l >= s) ?? 15) : 5;\n\treturn row[length] * 1000;\n}\n","/** Turns the documents the crew writes into blocks the page can lay out.\n *\n *  These files arrive as markdown (screenplay, scene list, art direction) or as\n *  JSON (visual bible), and until now the page printed them verbatim — which\n *  meant a scene list showed up as a wall of `| Scene | Setting | ... |`, the\n *  one document a reader most wants to scan.\n *\n *  This returns DATA, not HTML: the component renders each block with ordinary\n *  Svelte markup, so nothing model-written is ever injected as markup and there\n *  is no escaping to get wrong. The parser handles only what these documents\n *  actually contain — it is not a markdown implementation and should not grow\n *  into one.\n */\n\nexport type Span = { text: string; bold?: boolean; italic?: boolean };\n\nexport type Block =\n\t| { kind: 'heading'; level: 1 | 2 | 3; text: string }\n\t| { kind: 'para'; spans: Span[] }\n\t| { kind: 'list'; items: Span[][] }\n\t| { kind: 'table'; head: string[]; rows: string[][] }\n\t| { kind: 'rule' }\n\t/** Screenplay: a scene heading (INT./EXT. …) */\n\t| { kind: 'slug'; text: string }\n\t/** Screenplay: a character cue and the lines under it */\n\t| { kind: 'cue'; who: string; parenthetical?: string; lines: string[] }\n\t/** Screenplay: FADE TO BLACK. and friends */\n\t| { kind: 'transition'; text: string }\n\t/** Visual bible: one anchor — the thing every render prompt inherits */\n\t| { kind: 'anchor'; label: string; text: string };\n\n/** `**bold**` and `*italic*`. Deliberately no links, images or code: none of\n *  these documents contain them, and every construct supported is a construct\n *  that has to be rendered safely. */\nfunction spans(text: string): Span[] {\n\tconst out: Span[] = [];\n\t// Split on the emphasis markers, keeping them, then walk the pieces.\n\tconst parts = text.split(/(\\*\\*[^*]+\\*\\*|\\*[^*]+\\*)/g);\n\tfor (const p of parts) {\n\t\tif (!p) continue;\n\t\tif (p.startsWith('**') && p.endsWith('**') && p.length > 4) {\n\t\t\tout.push({ text: p.slice(2, -2), bold: true });\n\t\t} else if (p.startsWith('*') && p.endsWith('*') && p.length > 2) {\n\t\t\tout.push({ text: p.slice(1, -1), italic: true });\n\t\t} else {\n\t\t\tout.push({ text: p });\n\t\t}\n\t}\n\treturn out.length ? out : [{ text }];\n}\n\n/** A markdown table row: leading and trailing pipes are optional in the wild,\n *  so strip them before splitting rather than trusting either. */\nfunction cells(line: string): string[] {\n\treturn line\n\t\t.replace(/^\\s*\\|/, '')\n\t\t.replace(/\\|\\s*$/, '')\n\t\t.split('|')\n\t\t.map((c) => c.trim());\n}\n\nconst IS_TABLE_ROW = /\\|/;\n/** The `| :--- | ---: |` separator under a table's header. */\nconst IS_TABLE_RULE = /^\\s*\\|?[\\s:|-]+\\|[\\s:|-]*$/;\nconst IS_SLUG = /^\\s*(INT\\.|EXT\\.|INT\\/EXT\\.|I\\/E\\.)/i;\nconst IS_TRANSITION = /^\\s*(>\\s*.+|[A-Z][A-Z\\s]+TO:)\\s*$/;\n/** A Fountain character cue: a short all-caps line with no sentence\n *  punctuation, optionally with an extension like (V.O.). */\nconst IS_CUE = /^[A-Z][A-Z0-9 .'’-]{0,30}(\\s*\\([^)]+\\))?$/;\n\nfunction parseMarkdown(src: string): Block[] {\n\tconst lines = src.replace(/\\r\\n/g, '\\n').split('\\n');\n\tconst out: Block[] = [];\n\tlet para: string[] = [];\n\n\tconst flushPara = () => {\n\t\tif (!para.length) return;\n\t\tconst text = para.join(' ').trim();\n\t\tif (text) out.push({ kind: 'para', spans: spans(text) });\n\t\tpara = [];\n\t};\n\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tconst raw = lines[i];\n\t\tconst line = raw.trimEnd();\n\t\tconst t = line.trim();\n\n\t\tif (!t) {\n\t\t\tflushPara();\n\t\t\tcontinue;\n\t\t}\n\n\t\t// ── table: a header row followed by the dashed separator ──\n\t\tif (IS_TABLE_ROW.test(t) && i + 1 < lines.length && IS_TABLE_RULE.test(lines[i + 1])) {\n\t\t\tflushPara();\n\t\t\tconst head = cells(t);\n\t\t\tconst rows: string[][] = [];\n\t\t\ti += 2;\n\t\t\twhile (i < lines.length && lines[i].includes('|') && lines[i].trim()) {\n\t\t\t\trows.push(cells(lines[i]));\n\t\t\t\ti++;\n\t\t\t}\n\t\t\ti--; // the outer loop will advance past the line that ended the table\n\t\t\tout.push({ kind: 'table', head, rows });\n\t\t\tcontinue;\n\t\t}\n\n\t\t// ── headings ──\n\t\tconst h = /^(#{1,3})\\s+(.*)$/.exec(t);\n\t\tif (h) {\n\t\t\tflushPara();\n\t\t\tout.push({ kind: 'heading', level: h[1].length as 1 | 2 | 3, text: h[2].trim() });\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (/^(-{3,}|_{3,}|\\*{3,})$/.test(t)) {\n\t\t\tflushPara();\n\t\t\tout.push({ kind: 'rule' });\n\t\t\tcontinue;\n\t\t}\n\n\t\t// ── bullets: gather the whole run ──\n\t\tif (/^[-*•]\\s+/.test(t)) {\n\t\t\tflushPara();\n\t\t\tconst items: Span[][] = [];\n\t\t\twhile (i < lines.length && /^\\s*[-*•]\\s+/.test(lines[i])) {\n\t\t\t\titems.push(spans(lines[i].replace(/^\\s*[-*•]\\s+/, '').trim()));\n\t\t\t\ti++;\n\t\t\t}\n\t\t\ti--;\n\t\t\tout.push({ kind: 'list', items });\n\t\t\tcontinue;\n\t\t}\n\n\t\t// ── screenplay shapes ──\n\t\tif (IS_SLUG.test(t) || (t.startsWith('.') && t.length > 1 && t === t.toUpperCase())) {\n\t\t\tflushPara();\n\t\t\tout.push({ kind: 'slug', text: t.replace(/^\\./, '') });\n\t\t\tcontinue;\n\t\t}\n\t\tif (IS_TRANSITION.test(t)) {\n\t\t\tflushPara();\n\t\t\tout.push({ kind: 'transition', text: t.replace(/^>\\s*/, '') });\n\t\t\tcontinue;\n\t\t}\n\t\t// A cue only counts when a line actually follows it — otherwise an\n\t\t// all-caps sentence in an action block would swallow the paragraph after it.\n\t\tif (IS_CUE.test(t) && t === t.toUpperCase() && lines[i + 1]?.trim()) {\n\t\t\tconst m = /^([^(]+?)\\s*(\\(([^)]+)\\))?$/.exec(t);\n\t\t\tconst who = (m?.[1] ?? t).trim();\n\t\t\tconst parenthetical = m?.[3]?.trim();\n\t\t\tconst spoken: string[] = [];\n\t\t\ti++;\n\t\t\twhile (i < lines.length && lines[i].trim()) {\n\t\t\t\tspoken.push(lines[i].trim());\n\t\t\t\ti++;\n\t\t\t}\n\t\t\ti--;\n\t\t\tflushPara();\n\t\t\t// Fountain puts a delivery cue on its own line under the character, so\n\t\t\t// the first spoken line is often \"(quietly)\" — an instruction, not a\n\t\t\t// line. Lift it out so the dialogue reads as dialogue.\n\t\t\tlet par = parenthetical;\n\t\t\tif (!par && spoken.length > 1 && /^\\(.+\\)$/.test(spoken[0])) {\n\t\t\t\tpar = spoken[0].slice(1, -1);\n\t\t\t\tspoken.shift();\n\t\t\t}\n\t\t\tout.push({ kind: 'cue', who, parenthetical: par, lines: spoken });\n\t\t\tcontinue;\n\t\t}\n\n\t\tpara.push(t);\n\t}\n\tflushPara();\n\treturn out;\n}\n\n/** The visual bible is the one document whose shape we know exactly, because a\n *  skill in the workflow library writes it: a style block, then one anchor per\n *  character and per location. Those anchor phrases are pasted verbatim into\n *  every render prompt, so they are what a reader needs to check — showing them\n *  as raw JSON hides the only thing worth looking at. */\nfunction parseVisualBible(src: string): Block[] | null {\n\tlet data: unknown;\n\ttry {\n\t\tdata = JSON.parse(src);\n\t} catch {\n\t\treturn null;\n\t}\n\tif (!data || typeof data !== 'object') return null;\n\n\tconst d = data as Record<string, unknown>;\n\tconst out: Block[] = [];\n\n\tconst anchorOf = (v: unknown): string => {\n\t\tif (typeof v === 'string') return v;\n\t\tif (v && typeof v === 'object') {\n\t\t\tconst o = v as Record<string, unknown>;\n\t\t\tfor (const k of ['t2i_anchor', 'anchor', 'anchor_phrase', 'description', 'prompt']) {\n\t\t\t\tif (typeof o[k] === 'string') return o[k] as string;\n\t\t\t}\n\t\t}\n\t\treturn '';\n\t};\n\n\tconst style = d.style as Record<string, unknown> | string | undefined;\n\tif (style) {\n\t\tout.push({ kind: 'heading', level: 2, text: 'Style' });\n\t\tif (typeof style === 'string') {\n\t\t\tout.push({ kind: 'anchor', label: 'style', text: style });\n\t\t} else {\n\t\t\tfor (const [k, v] of Object.entries(style)) {\n\t\t\t\tif (typeof v === 'string' && v.trim()) {\n\t\t\t\t\tout.push({ kind: 'anchor', label: k.replace(/_/g, ' '), text: v });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const [section, title] of [\n\t\t['characters', 'Characters'],\n\t\t['locations', 'Locations']\n\t] as const) {\n\t\tconst block = d[section];\n\t\tif (!block || typeof block !== 'object') continue;\n\t\tconst entries = Object.entries(block as Record<string, unknown>);\n\t\tif (!entries.length) continue;\n\t\tout.push({ kind: 'heading', level: 2, text: title });\n\t\tfor (const [name, v] of entries) {\n\t\t\tconst text = anchorOf(v);\n\t\t\tif (text) out.push({ kind: 'anchor', label: name.replace(/_/g, ' '), text });\n\t\t}\n\t}\n\n\treturn out.length ? out : null;\n}\n\n/** Parse a document for display. `name` picks the strategy: only the visual\n *  bible gets the JSON treatment, everything else is markdown-ish. Falls back\n *  to plain paragraphs whenever a parse would otherwise lose content. */\nexport function renderDocument(name: string, body: string): Block[] {\n\tconst text = (body ?? '').trim();\n\tif (!text) return [];\n\n\tif (name.toLowerCase().endsWith('.json')) {\n\t\tconst bible = parseVisualBible(text);\n\t\tif (bible) return bible;\n\t\t// Unknown JSON: show it as-is rather than guessing at its shape.\n\t\treturn [{ kind: 'para', spans: [{ text }] }];\n\t}\n\n\treturn parseMarkdown(text);\n}\n","/** What a character sounds like when nobody has said.\n *\n *  A real sentence rather than a placeholder, because a placeholder is not a\n *  value: it looks filled in and renders as nothing, so a character shipped\n *  with a grey suggestion in the box and silence in the clip. This is written\n *  into the character when it is made, and shown as the starting text for the\n *  ones made before it existed.\n *\n *  Deliberately unremarkable — it is a floor, not a characterisation. Anything\n *  more specific would be a guess about someone the description has not\n *  described, and would be wrong more often than it was right. */\nexport const DEFAULT_VOICE = 'a low, unhurried voice with a slight rasp';\n\n/** The shared contract for the /studio surface.\n *\n *  Three files agree on the shapes in here: the composer (compose.ts), the\n *  launch endpoint (api/launch/+server.ts) and the studio page itself. Nothing\n *  in this file has a runtime cost beyond a couple of constants — it exists so\n *  those three never disagree about what a production is.\n */\nimport type { Pick } from '$lib/studio/loras';\n\n/** Everything a production is described by, before any YAML exists.\n *\n *  A Brief is what the user actually authors: one sentence expanded into a\n *  story, a look, a length, and a pinned seed. `composeWorkspace` turns it into\n *  the ~850-line workspace the harness wants; nothing upstream of that ever\n *  touches YAML. */\nexport interface Brief {\n\t/** url-safe slug, unique per launch, becomes the workspace name.\n\t *  Workspaces are immutable once opened — reopening an id is a silent no-op —\n\t *  so a fresh slug per launch is the only thing that gets a fresh run.\n\t *  Must match {@link SLUG_RE}. */\n\tslug: string;\n\t/** short human title shown in the UI (becomes spec.description) */\n\ttitle: string;\n\t/** Three to five words naming the voice the model chose for this film. Shown\n\t *  on the card because it is a decision made on the user's behalf, from a\n\t *  sentence that usually did not specify one — and a decision you can see is\n\t *  a decision you can argue with. */\n\tregister?: string;\n\t/** Two or three sentences saying what the film is. Shown instead of the\n\t *  story, which runs to four hundred words nobody reads before deciding\n\t *  whether the plan is right. The story is one click away and unchanged. */\n\tsummary?: string;\n\t/** the prose the screenwriter agent adapts — this becomes spec.story.plot */\n\tstory: string;\n\t/** one sentence naming the visual medium, e.g.\n\t *  \"2D digital comic book illustration, bold ink outlines, cel-shaded\".\n\t *  Lands in the art-direction prompt, which is what every render prompt\n\t *  ultimately inherits its look from. */\n\tstyle: string;\n\t/** how many scenes to produce, 2..6 — one rendered clip per scene */\n\tsceneCount: number;\n\t/** pinned so every render in this production shares a look */\n\tseed: number;\n}\n\n/** Accepted slug grammar. Deliberately identical to the check in\n *  api/launch/+server.ts: the slug becomes half of a workspace id (`name@1.0`)\n *  and therefore lands verbatim in a URL path. */\nexport const SLUG_RE = /^[A-Za-z0-9._-]+$/;\n\n/** Scene bounds. The floor is what makes a film rather than a clip; the ceiling\n *  is wall-clock — renders run ~10 minutes per batch of four. */\nexport const SCENE_COUNT_MIN = 2;\nexport const SCENE_COUNT_MAX = 6;\n\n/** What the harness will spend on one render, and how many times it will try.\n *\n *  These are not the studio's numbers to choose. They are written into the\n *  compute profile of every workspace it composes — compose.ts reads them from\n *  here — and the harness abandons a task that outlives them. They live in this\n *  file because the studio also has to read them back: a run can only still be\n *  running for as long as the harness is still prepared to run it.\n */\nexport const RENDER_TIMEOUT_SEC = 1800;\nexport const RENDER_MAX_ATTEMPTS = 2;\n\n/** How long a run can still plausibly be alive.\n *\n *  Deliberately not a round number. It is the longest thing the harness can\n *  produce, worked out from its own budget: the longest phase is the shoot, at\n *  most SCENE_COUNT_MAX clips plus the assembly, and each of those is one task\n *  the harness gives up on after RENDER_TIMEOUT_SEC x RENDER_MAX_ATTEMPTS. They\n *  are counted end to end rather than overlapping, because the studio does not\n *  schedule them — the planner creates them at runtime and we do not get to\n *  assume they run at once. Every other phase fits well inside it: planning is\n *  LLM-only and takes minutes, and a clip, a sheet or a continuation is a single\n *  task under the same budget (a continuation's is longer — CONT_TIMEOUT_SEC in\n *  compose.ts — and still well short of this).\n *\n *  Past this point nothing the harness was asked to do is still being attempted.\n *  A run restored from older than this is a record of something that ended, not\n *  work in progress, and must not be presented as one.\n */\nexport const RUN_CEILING_MS =\n\t(SCENE_COUNT_MAX + 1) * RENDER_TIMEOUT_SEC * RENDER_MAX_ATTEMPTS * 1_000;\n\n/** A one-clip run (`-direct@` / `-cont@` workspaces) is one render: every\n *  attempt it gets, plus a few minutes to collect. The production ceiling above\n *  is seven hours, and a single clip held to it counted for a whole night. */\nexport const ONE_CLIP_WS = /-(direct|cont)@/;\nexport const ONE_CLIP_CEILING_MS = (RENDER_TIMEOUT_SEC * RENDER_MAX_ATTEMPTS + 300) * 1_000;\n\n/** How long this workspace's run can still be working. */\nexport function runCeilingFor(workspace: string): number {\n\treturn ONE_CLIP_WS.test(workspace) ? ONE_CLIP_CEILING_MS : RUN_CEILING_MS;\n}\n\n/** What POST /studio/api/launch answers with. It always returns 200:\n *  a down container and a rejected YAML are both normal states here, to be drawn\n *  as a banner rather than thrown. */\n/** What the render launch loaded into the workspace after opening it. Absent\n *  for the planning stage, which needs neither. */\nexport interface LaunchExtras {\n\tlibrary?: {\n\t\tworkflows: { name: string; ok: boolean; detail?: string }[];\n\t\tskills: { name: string; ok: boolean; detail?: string }[];\n\t};\n\trefs?: { artifactId?: string; imported: string[]; error?: string };\n}\n\nexport type LaunchResult =\n\t| ({ ok: true; workspaceId: string } & LaunchExtras)\n\t| {\n\t\t\tok: false;\n\t\t\t/** true when the harness could not be reached at all (nobody started\n\t\t\t *  the container) as opposed to reached and unhappy */\n\t\t\toffline?: boolean;\n\t\t\terror: string;\n\t  };\n\n/** One step of the production pipeline as the task rail draws it.\n *\n *  The first six keys map 1:1 onto planning — 'plan' is our own LLM expansion\n *  of the one-sentence idea (no workspace exists yet), the next five are the\n *  planning workspace's tasks in dependency order. The last three belong to the\n *  render workspace: its planner ('schedule'), the fan-out of shoot_* tasks\n *  ('shoot', drawn as one step with a counter), and the chat-triggered final\n *  assembly ('assemble'). */\nexport type StageKey =\n\t| 'plan'\n\t| 'screenplay'\n\t| 'cast'\n\t| 'scenes'\n\t| 'art'\n\t| 'bible'\n\t| 'schedule'\n\t| 'shoot'\n\t| 'assemble';\n\n/** One entry of the chat transcript — the studio surface is a single list of\n *  these, nothing else. `who: 'studio'` is our app narrating (progress lines,\n *  artifacts landing, approval prompts); the manager agent's chat replies also\n *  arrive as studio text. Exactly one of the optional payloads is set,\n *  according to `kind`. */\nexport interface ChatItem {\n\tid: string;\n\twho: 'user' | 'studio';\n\tkind:\n\t\t| 'text'\n\t\t| 'plan'\n\t\t| 'shot'\n\t\t| 'artifact'\n\t\t| 'clips'\n\t\t| 'error'\n\t\t| 'approval'\n\t\t| 'activity'\n\t\t| 'board'\n\t\t| 'shootboard'\n\t\t| 'sheet'\n\t\t| 'takes'\n\t\t| 'confirm';\n\t/** What is about to be shot, in the operator's own language, awaiting their\n\t *  yes (kind=confirm).\n\t *\n\t *  This is the contract, not a summary: it is written before the brief and the\n\t *  brief is made from it, so a fact missing here is a fact the clip will not\n\t *  have. `said` is what they typed verbatim, kept because it is the only place\n\t *  their exact words survive — the writer is told to use the operator's plain\n\t *  terms, and it can only do that if it is given them.\n\t */\n\tconfirm?: {\n\t\t/** Verbatim, this round. Empty on a round that answers a setting change. */\n\t\tsaid: string;\n\t\t/** The setting that moved, when that is what this round answers. */\n\t\tchanged?: string;\n\t\t/** The read-back, filled in as it streams. */\n\t\tline: string;\n\t\t/** Still arriving. */\n\t\tstreaming?: boolean;\n\t\t/** Accepted — the writer has it, or has had it. */\n\t\tsent?: boolean;\n\t\t/** What the checker changed on the way, in plain words. Present means the\n\t\t *  render did not start on its own and is waiting to be sent again. */\n\t\tfixed?: string[];\n\t\t/** The brief this produced.\n\t\t *\n\t\t *  Held so that pressing the button a second time renders that brief\n\t\t *  rather than writing a new one. Without it \"send it anyway\" re-ran the\n\t\t *  writer, which is a minute of waiting for a DIFFERENT brief — so the\n\t\t *  operator would have been shown one set of changes and shot another. */\n\t\tcardId?: string;\n\t\t/** Which wait the button is on, and since when.\n\t\t *\n\t\t *  \"indul…\" stood alone for two and a half minutes on a real launch — two\n\t\t *  for the brief, thirty seconds for the harness to open the workspace —\n\t\t *  and the operator asked whether it had hung. A word alone reads as\n\t\t *  frozen after ten seconds; the clock is the proof something is\n\t\t *  happening, and the phase says what. */\n\t\tphase?: 'writing' | 'starting';\n\t\tbusySince?: number;\n\t\t/** The slug of the press the server is carrying for this round, while it\n\t\t *  is on its way — so a tab that comes back to it knows not to offer the\n\t\t *  button again. See generate.server.ts. */\n\t\tgen?: string;\n\t\t/** The continuation this round was written for, fixed at creation so that\n\t\t *  toggling the seam afterwards changes the NEXT round, not the meaning of\n\t\t *  this one. Absent on an ordinary clip. */\n\t\tcontinues?: {\n\t\t\tworkspace: string;\n\t\t\tpinned: boolean;\n\t\t\tcharacterName?: string;\n\t\t\tlocationName?: string;\n\t\t};\n\t\terror?: string;\n\t};\n\n\t/** plain text content (kind=text, error, approval) */\n\ttext?: string;\n\t/** the expanded brief awaiting the user's yes (kind=plan) */\n\tplan?: Brief;\n\t/** simple mode's render prompt, awaiting the user's yes (kind=shot).\n\t *  `prompt` is the literal text the workflow will receive — editable on the\n\t *  card, because a prompt nobody can see is how the planning chain spent\n\t *  months shipping briefs that described a face instead of a scene. */\n\tshot?: {\n\t\tprompt: string;\n\t\tseconds: number;\n\t\torientation: 'portrait' | 'landscape';\n\t\twhy: string;\n\t\t/** The adapters the writer chose for this shot, shown on the card so the\n\t\t *  choice is visible before it costs a render rather than after. */\n\t\tloras?: Pick[];\n\t\t/** Strengths for the always-loaded adapters, where you moved one on this\n\t\t *  card. Absent keys use the catalogue's figure. */\n\t\tbaseLoras?: Record<string, number>;\n\t\t/** Frame size step — '480p', '576p' or '720p'. Unlike the length and the\n\t\t *  frame shape this does not change a word of the brief, so moving it\n\t\t *  never costs a rewrite. */\n\t\tresolution?: string;\n\t\t/** The same list as the writer first returned it, kept untouched while\n\t\t *  `loras` is edited. The two are compared at launch: where they differ,\n\t\t *  a person disagreed with the writer, and that is the only free quality\n\t\t *  signal this app produces. */\n\t\twroteLoras?: Pick[];\n\t\t/** Set once this card has been sent to render, so it stops offering. */\n\t\tlaunched?: boolean;\n\t\t/** Set when this shot continues an existing clip rather than starting one.\n\t\t *\n\t\t *  Carries the three ids the server reads the prior clip's bytes by, and the\n\t\t *  character and location it was shot with — a continuation requires both,\n\t\t *  and they are not a fresh choice: they are whatever the first clip used. */\n\t\tcontinues?: {\n\t\t\tworkspace: string;\n\t\t\tartifact: string;\n\t\t\tfile: string;\n\t\t\t/** Absent where the clip was shot without a kept sheet. The launch then\n\t\t\t *  cuts that plate out of the clip itself — the workflow wants a picture\n\t\t\t *  in the slot, not specifically a sheet. */\n\t\t\tcharacterId?: string;\n\t\t\tlocationId?: string;\n\t\t\tcharacterName?: string;\n\t\t\tlocationName?: string;\n\t\t\t/** Both plates come from kept sheets. False means at least one is a\n\t\t\t *  frame of the clip, which still works and holds less precisely. */\n\t\t\texact?: boolean;\n\t\t\t/** Whether the seam is pinned to the prior clip's final frame. Default\n\t\t\t *  true. Chosen before the brief is written, so the writer and the\n\t\t\t *  render always agree about whether <Picture 3> exists. */\n\t\t\tpinned?: boolean;\n\t\t};\n\t\t/** The kept character sheet this clip is shot with. Chosen before the\n\t\t *  writer runs, because it decides which brief template gets written. */\n\t\tcharacterId?: string;\n\t\tcharacterName?: string;\n\t\t/** The kept location this clip is shot in. */\n\t\tlocationId?: string;\n\t\tlocationName?: string;\n\t\t/** Structural faults the brief still has after the writer was given one\n\t\t *  chance to fix them. Never a refusal — the brief renders and this is the\n\t\t *  line that says what to look at if the clip comes back wrong. */\n\t\twarn?: string[];\n\t\t/** Faults the check caught and the retry then cleared. Shown in the past\n\t\t *  tense: nothing to act on, but it is why the brief took two passes. */\n\t\tfixed?: string[];\n\t};\n\t/** This error's retry has been pressed. On the item so it survives a reload —\n\t *  a flag beside the transcript emptied on every refresh and handed the button\n\t *  back over a render that was already running. */\n\tretried?: boolean;\n\t/** a planning document or rendered clip presented in the transcript\n\t *  (kind=artifact, clips). `taskId` is what reset-task needs for a revision;\n\t *  `body` is the fetched text of a planning document, absent for videos. */\n\tartifact?: {\n\t\t/** The harness's artifact id. Carried because a scene is assembled from the\n\t\t *  chat, and the server reads each clip's bytes by (workspace, artifact,\n\t\t *  file) — the id was previously buried inside the file url alone. */\n\t\tid?: string;\n\t\tkey: string;\n\t\ttitle: string;\n\t\ttaskId: string;\n\t\tfiles: { name: string; url: string }[];\n\t\tbody?: string;\n\t\t/** Which run produced this, so a verdict given on a card you scrolled back\n\t\t *  to lands on that run's row and not on whichever one is current. */\n\t\tworkspace?: string;\n\t};\n\t/** several takes of one beat, rendered at once, waiting to be chosen between\n\t *  (kind=takes).\n\t *\n\t *  One card for the whole batch rather than one per take, because four takes\n\t *  exist for a single reason — one of them gets used — and four independent\n\t *  cards make the only decision that matters a scroll-and-remember. The runs\n\t *  keep their own workspaces, so a chosen take rates, continues and joins a\n\t *  scene exactly like a clip rendered on its own.\n\t *\n\t *  Every run is listed from the moment the batch starts, including the ones\n\t *  still on the GPU: the strip then has its final shape from the first\n\t *  second and nothing moves as they land. */\n\ttakes?: {\n\t\t/** The batch id the server groups these under, and the key this card is\n\t\t *  found by when a poll brings news. */\n\t\tbatch: string;\n\t\truns: {\n\t\t\tindex: number;\n\t\t\tslug: string;\n\t\t\tstate: 'rendering' | 'ready' | 'failed';\n\t\t\terror?: string;\n\t\t\tclip?: { workspace: string; artifact: string; file: string };\n\t\t}[];\n\t\t/** The take you kept, once you have chosen one. Choosing also fills in\n\t\t *  `artifact` above, which is what lets the kept take render through the\n\t\t *  same card a single clip does — with the same verdict, continue and\n\t\t *  scene controls, none of them rewritten for this. */\n\t\tkept?: number;\n\t};\n\t/** a rendered character or location sheet, awaiting a name and a keep\n\t *  (kind=sheet). `id` is set once it has been stored, which is also what\n\t *  stops the card offering to store it twice. */\n\tsheet?: {\n\t\tkind: 'character' | 'location';\n\t\t/** `anchor` is the cheap single-picture preview of a character; `sheet` is\n\t\t *  the six-view turnaround. They share the model and the seed, so an\n\t\t *  anchor is the same face the sheet will produce. Absent means sheet. */\n\t\tstage?: 'anchor' | 'sheet';\n\t\t/** Carried from the preview into the full sheet, so the turnaround is of\n\t\t *  the person you approved rather than a new one. */\n\t\tseed?: number;\n\t\t/** The English description the workflow will receive — editable on the\n\t\t *  card, for the same reason the shot prompt is: a description nobody can\n\t\t *  see is one nobody can correct, and this one is rendered once and lived\n\t\t *  with for a whole production. */\n\t\tdescription: string;\n\t\t/** What you typed, kept so a rewrite asks for the same subject again\n\t\t *  rather than editing the writer's English. */\n\t\trequest?: string;\n\t\t/** How this person sounds, invented alongside their look and carried onto\n\t\t *  their sheet when the character is kept. Characters only. */\n\t\tvoice?: string;\n\t\t/** One line from the writer saying what it changed, or that it only\n\t\t *  translated. */\n\t\twhy?: string;\n\t\t/** Set once this card has been sent to render, so it stops offering. */\n\t\tlaunched?: boolean;\n\t\t/** This subject is a picture you supplied rather than one we drew.\n\t\t *\n\t\t *  It changes what the card may promise. A drawn character gets its\n\t\t *  six-view turnaround rendered behind you; an uploaded one never will,\n\t\t *  because both sheet workflows are text-to-image and have no image input\n\t\t *  to redraw a photograph from. One picture is the whole of it, and saying\n\t\t *  otherwise would leave someone waiting for views that are not coming. */\n\t\tuploaded?: boolean;\n\t\t/** Set on the card the render comes back on. Its presence is what tells\n\t\t *  the two states apart: no url is a draft awaiting your yes, a url is a\n\t\t *  finished sheet awaiting a name. */\n\t\turl?: string;\n\t\t/** The three ids the store needs to fetch the bytes itself, for anything\n\t\t *  that came back through the harness. */\n\t\tworkspace?: string;\n\t\tartifact?: string;\n\t\tfile?: string;\n\t\t/** A direct preview instead: the render skipped the harness, so the bytes\n\t\t *  are already a file on this machine and the job id is what names them. */\n\t\tjob?: string;\n\t\t/** Your name for it. Seeded from the description, editable on the card. */\n\t\tname?: string;\n\t\t/** Set by a successful keep. */\n\t\tid?: string;\n\t};\n\t/** one line of the harness's own progress log (kind=activity) */\n\tactivity?: {\n\t\tid: string;\n\t\tat: number;\n\t\ttone: 'step' | 'good' | 'warn' | 'bad';\n\t\ttext: string;\n\t\tdetail?: string;\n\t};\n\tat: number;\n}\n\n/** Where a production is in the studio flow. Exactly one is true at a time.\n *\n *  brief     — the user is typing the one-sentence idea\n *  drafting  — that sentence is being expanded into a story\n *  review    — the story is on screen, editable, Go is armed\n *  launching — the launch POST is in flight; the slug is being burned\n *  producing — the workspace is open and being polled; clips appear as they land\n *  screening — the film exists and is playable\n *  failed    — the launch was refused, or the workspace is unreachable/dead */\nexport type StudioPhase =\n\t| 'brief'\n\t| 'drafting'\n\t| 'review'\n\t| 'launching'\n\t| 'producing'\n\t| 'screening'\n\t| 'failed';\n\n/** ── harness wire shapes ────────────────────────────────────────────────────\n *  What poll-state actually sends back. Kept here rather than in each consumer\n *  so the studio page and any future panel read the same fields; the older\n *  dashboard at /ops declares its own copies and is left alone. */\n\n/** One sheet as the store keeps it. The server module owns the writing; this is\n *  the shape the page reads back, and it is declared here rather than imported\n *  from sheets.server.ts because that module touches the filesystem and must\n *  never be pulled into a client bundle. */\nexport interface StoredSheet {\n\tid: string;\n\tkind: 'character' | 'location';\n\tname: string;\n\tdescription: string;\n\tfile: string;\n\tsize: number;\n\taddedAt: string;\n\tworkspace?: string;\n\tseed?: number;\n\t/** How this person sounds, one sentence, carried into every clip they are in.\n\t *  Characters only — a room does not speak. See sheets.server.ts. */\n\tvoice?: string;\n\t/** The conversation this subject was made in, so its six views are posted\n\t *  back into it. Absent on everything made before this existed, and absent\n\t *  means any. */\n\tsessionSlug?: string;\n\t/** Made from a photograph rather than from a description.\n\t *\n\t *  sheets.server.ts has stored this from the start; the page's own type did\n\t *  not carry it, so the surface could not tell the two apart — and the two\n\t *  differ in what gets made. An uploaded character gets a turnaround render\n\t *  and its six views out of that; an uploaded location gets neither, because\n\t *  the sheet workflows are text-to-image and cannot redraw a photograph. */\n\tuploaded?: boolean;\n\t/** Whether the six views have already been shown in that conversation. What\n\t *  the sidebar reads to tell \"finished while you were elsewhere\" from\n\t *  \"finished and seen\". */\n\tdelivered?: boolean;\n\t/** The six-view turnaround, which arrives minutes after the character does —\n\t *  absent until one has been asked for. */\n\tsheet?: {\n\t\tstate: 'rendering' | 'ready' | 'failed';\n\t\t/** When the turnaround was started, so the card can say how long it has\n\t\t *  been going. */\n\t\tstartedAt?: string;\n\t\tfile?: string;\n\t\terror?: string;\n\t\t/** The turnaround clip the six views were cut from, when there was one. */\n\t\tclip?: { workspace: string; artifact: string; file: string };\n\t\tattempt?: number;\n\t};\n}\n\n/** status is `pending | running | success | permanently-failed`, with\n *  `completed` and `failed` also observed — hence the widened type. */\nexport interface Task {\n\tid: string;\n\tkey: string;\n\ttitle: string;\n\tstatus: string;\n\torigin: string | null;\n\tagent: string | null;\n\tgolem_agent_id?: string | null;\n}\n\n/** The harness sends a bare filename per file (`[\"scene1_clip.mp4\"]`). Object\n *  entries are tolerated in case a richer shape shows up later.\n *\n *  Clip filenames are NOT uniform across scenes — `scene1_clip.mp4` next to\n *  `scene3.mp4` is normal. Always resolve files from `files`, never by guessing\n *  a name from a scene number. */\nexport type ArtifactFile = string | Record<string, unknown>;\n\n/** status is `empty | approved | rejected`. */\nexport interface Artifact {\n\tid: string;\n\tkey: string;\n\tname: string;\n\tdescription: string;\n\tstatus: string;\n\tfiles: ArtifactFile[];\n}\n\nexport interface PollState {\n\tworkspace?: { name?: string; version?: string; is_open?: boolean; context_utilization?: number };\n\ttasks?: Task[];\n\tartifacts?: Artifact[];\n\tworkflows?: { entry: { name: string }; state: string; in_flight?: number; pending?: number }[];\n\tworker_statuses?: unknown[];\n\trecent_messages?: unknown[];\n}\n\n/** One line of get-event-log, which arrives as a JSON-encoded string of\n *  newline-separated objects. `event` is one of dispatch, redispatch, rca,\n *  artifact-received, complete, subgraph-ingested — the union is left open\n *  because the harness adds to it. */\nexport interface EventLine {\n\tt: string;\n\tevent: string;\n\ttaskId?: string;\n\tkey?: string;\n\tstatus?: string;\n\tfiles?: string[];\n\t[k: string]: unknown;\n}\n\n/** The envelope every /studio/api/harness call comes back in. Also always 200 —\n *  `ok` is the harness's status, not the proxy's. */\nexport interface ProxyResult<T = unknown> {\n\tok: boolean;\n\tstatus?: number;\n\t/** The harness itself did not answer. */\n\toffline?: boolean;\n\t/** The harness answered, but this one workspace did not. Its agent has\n\t *  wedged; the render behind it may still be running on the GPU. Told apart\n\t *  from `offline` because the two need opposite advice — one says restart the\n\t *  container, the other says on no account restart the container. */\n\twedged?: boolean;\n\terror?: string;\n\tdata?: T;\n}\n","/** The adapters a clip can be rendered with, and what each one is for.\n *\n *  The workflow bundle in git carries one fixed pair — the turbo adapter and\n *  the realism detailer — because a bundle is a file and a file cannot decide\n *  anything. Everything past that pair is chosen per clip, which is the whole\n *  point: a blowjob wants the blowjob adapter and the penis detailer, a\n *  missionary shot wants neither and wants two others, and a bundle committed\n *  ahead of time can only ever be right about one of them.\n *\n *  So the bundle is generated at render time from this list. `key` is what the\n *  prompt writer returns and what travels in the URL, so it stays short and\n *  free of dashes — the URL splits pairs on the last dash.\n *\n *  `strength` is the author's own recommendation, read off each model's page\n *  rather than guessed. Where an author gave a range the midpoint is used,\n *  except where they named a figure they run themselves.\n *\n *  One hard constraint on `url`, learned the expensive way: **a civitai version\n *  must be referenced by its first .safetensors and no other.** The harness\n *  rewrites the fileId and fetches the version's first file regardless of what\n *  the url asks for — twice observed, once as a 403 on a file we never wanted\n *  and once as a sha mismatch after the wrong bytes arrived. Both failures cost\n *  minutes of A100 time per retry, because model downloads run on the GPU\n *  container. Before adding an entry, check the version's file list: if the file\n *  you want is not first, you cannot have it from that version.\n */\n\nexport interface Lora {\n\t/** URL-safe, dash-free. What the writer names and what the path carries. */\n\tkey: string;\n\t/** For the card, so you can see what was chosen without decoding a key. */\n\tlabel: string;\n\t/** Filename on the Modal volume. The graph refers to it as MINIMAX\\<file>. */\n\tfile: string;\n\turl: string;\n\tsha256: string;\n\t/** The author's recommendation. Overridable per clip. */\n\tstrength: number;\n\t/** The viewpoint this adapter was trained on, where it has one.\n\t *\n\t *  Most adapters draw a thing and leave the camera alone. A few were trained\n\t *  on one framing and put it back whatever the brief says, and the brief\n\t *  loses: a missionary shot written as \"high angle looking down at the sofa,\n\t *  his shoulders in the lower centre\" came back as POV from his own eyeline,\n\t *  with him out of frame entirely and her body stretched by the\n\t *  foreshortening that viewpoint forces.\n\t *\n\t *  Named here so the writer is told before it picks, rather than finding out\n\t *  in the render. */\n\tcamera?: string;\n\t/** The range the author themselves gave, where they gave one. The writer may\n\t *  place the strength anywhere inside it and nowhere outside it.\n\t *\n\t *  Absent on most of these, and left absent on purpose. Inventing a band for\n\t *  an adapter whose author never published one just moves the guess up a\n\t *  level and dresses it as provenance — those keep the fixed number. */\n\tband?: [number, number];\n\t/** Words that aim the adapter. They do not switch it on — a loaded adapter\n\t *  is always acting — but the prompt lands closer with them present. */\n\ttrigger?: string;\n\t/** Shown to the writer so it can choose. One line, plain, no adjectives:\n\t *  the writer is picking equipment, not being sold it. */\n\tuse: string;\n\t/** Acts are mutually exclusive — a clip is one thing happening. Everything\n\t *  else stacks. The writer is told this rather than left to infer it. */\n\tkind: 'base' | 'act' | 'detail';\n}\n\n/** Always loaded, all three, in every clip. None of them has anything to do with\n *  what is happening in the shot: the turbo adapter is what makes four steps\n *  possible, the realism slider keeps skin from reading as plastic, and mystic\n *  keeps bodies resolving instead of coming apart.\n *\n *  Mystic was a pick until a doggy clip — a position with no adapter of its own —\n *  came back visibly better with it than without. Nothing about it is specific to\n *  that position, so keeping it as a pick meant it competed for a slot with the\n *  moans or the breast physics on every other clip, and won on the ones where it\n *  was least needed.\n *\n *  That is one clip's evidence, extrapolated. It also raises the ceiling to five\n *  adapters where the verified-good configuration had four, so if clips start\n *  coming back muddy this is the first line to question. */\nexport const BASE: Lora[] = [\n\t{\n\t\tkey: 'turbo',\n\t\tlabel: 'LightX2V turbo 4-step',\n\t\tfile: 'minimax_h3_fl2v_lightx2v_turbo_4step_v0.1_comfy.safetensors',\n\t\turl: 'https://huggingface.co/Kijai/MiniMax-H3_comfy/resolve/main/loras/minimax_h3_fl2v_lightx2v_turbo_4step_v0.1_comfy.safetensors',\n\t\tsha256: 'fc9b6500f0331fe925b004738baaa31bd34104741c8bf9334816f9ac3005c8c1',\n\t\tstrength: 0.7,\n\t\tuse: 'the speed distillation the step count is built around',\n\t\tkind: 'base'\n\t},\n\t{\n\t\tkey: 'realism',\n\t\tlabel: 'PlagueKind realism slider',\n\t\tfile: 'PlagueKind-tiddies-realismslider.safetensors',\n\t\turl: 'https://civitai.com/api/download/models/3229050?fileId=3111384',\n\t\tsha256: 'e5c8c275af58663a664ad2922cc10a248bff70b941043375d2c82d9cc55b7030',\n\t\t/** 0.5, which is below the author's band on purpose — measured, not guessed.\n\t\t *\n\t\t *  Four renders of one prompt at one seed, 1.6 / 1.0 / 0.8 / 0.5: the body\n\t\t *  the brief asked for (thin, flat) and the anatomy both resolve as this\n\t\t *  comes down, and the wardrobe is the last thing to survive. At 0.8 the\n\t\t *  skirt the prompt named five times was simply absent; at 0.5, same seed\n\t\t *  and same composition, it is there — the only difference between those\n\t\t *  two frames. Above 1.0 the clips were the ones that got reported as\n\t\t *  \"body parts completely mixed up\".\n\t\t *\n\t\t *  The band below is the author's, for this slider used alone on stills.\n\t\t *  We run it under Mystic, LightX2V turbo and two act adapters, which is\n\t\t *  not what it was rated against — and in that stack it overpowers the\n\t\t *  prompt rather than adding skin texture. Raise it if skin starts looking\n\t\t *  plastic; this is one number and it is easy to walk back. */\n\t\tstrength: 0.5,\n\t\t/** author: realistic band 1.0-2.0, breaks past 2.0 */\n\t\tband: [1.0, 2.0],\n\t\tuse: 'skin texture — pores, freckles, uneven tone',\n\t\tkind: 'base'\n\t},\n\t{\n\t\tkey: 'mystic',\n\t\tlabel: 'Mystic XXX v3',\n\t\tfile: 'MysticXXX_MMH3-V3.safetensors',\n\t\turl: 'https://civitai.com/api/download/models/3260276?fileId=3143593',\n\t\tsha256: '99307e313784cbea7d9ee2a56ecb8794272f1024737985b824eca8c5c619a0b6',\n\t\t/** 0.6, inside the author's band and at the other end of it, because at 0.9\n\t\t *  this adapter overrules the reference face.\n\t\t *\n\t\t *  Measured on one prompt at one seed, the first batch that actually\n\t\t *  reached the GPU with a character sheet attached: at 0.9 her hair comes\n\t\t *  out long and loose and the build is fuller than the sheet; at 0.6, same\n\t\t *  seed and same composition, the hair is up and the build is thin, which\n\t\t *  is what the sheet shows. The turnaround path already learned this and\n\t\t *  zeroes the base adapters; the clip path had not.\n\t\t *\n\t\t *  It does not cost explicitness — the mid-clip close-up at 0.6 is at least\n\t\t *  as graphic as at 0.9, which was the risk worth checking before moving\n\t\t *  it. One pair, so treat it as a direction rather than a constant. */\n\t\tstrength: 0.6,\n\t\t/** author: '0.5 - 0.9', runs 0.9 themselves */\n\t\tband: [0.5, 0.9],\n\t\t// Described here as general scene quality until someone asked whether it\n\t\t// would help a doggy shot. That description was mine and it was wrong: the\n\t\t// author's own headline is \"Unlock Real Anatomy in T2V — finally,\n\t\t// text-to-video with proper anatomy\", which is a different thing entirely\n\t\t// and is the thing that keeps failing in this workflow. It is also native\n\t\t// to t2v, unlike every position adapter the catalogue search turned up,\n\t\t// which were all i2v-only.\n\t\tuse: 'anatomy — limbs, joins and bodies resolving instead of coming apart',\n\t\tkind: 'base'\n\t}\n];\n\n/** Chosen per clip. Exactly one `act`, any number of `detail`. */\n/** Other speed distillations that can stand in for BASE's `turbo` — one at a time,\n *  never stacked. Chosen server-side by AUTEUR_TURBO_KEY (bundle.server.ts,\n *  stack()) so an A/B needs a restart and a key, not a code change.\n *\n *  Why these two: the H3 Acceleration Arena (crowd A/B, 2026-09) put the\n *  LightX2V 8-step v1.0 and Larryvrh's turbo at the top, and several users\n *  report better motion and audio from them than from the 4-step v0.1 that BASE\n *  carries. Strengths are the authors' own: 1.0 for both. Steps stay at 8 —\n *  Larry's README says 6–8 is the useful range and above 8 over-sharpens.\n *  Larry's is trained on the t2v base and documented as working on the int8\n *  convrot variants; whether it also lands on the fl2va graph is what the A/B\n *  was for.\n *\n *  MEASURED 2026-09-06, and the answer is no — do not switch to these. Same\n *  seed, same request, our stack (bj 1.2, cum 0.8, realism, mystic, 8 steps):\n *  both came back with the anatomy gone — detached genitals floating before\n *  the face, limbs multiplied or merged, a face-sized organ, the camera\n *  yanked into a tight POV — while the 4-step v0.1 held the framing and the\n *  bodies. Speed was identical (232/239/242 s). The arena ranked these on\n *  plain clips with no adapters; our adapter stack is tuned to the v0.1 and\n *  collapses under the others. Kept only so the switch has something to\n *  point at when a future stack is re-tuned. */\nexport const TURBO_ALTERNATIVES: Lora[] = [\n\t{\n\t\tkey: 'turbo8',\n\t\tlabel: 'LightX2V turbo 8-step v1.0',\n\t\tfile: 'minimax_h3_fl2v_lightx2v_turbo_8step_v1.0_resized_avg_rank_24_bf16.safetensors',\n\t\turl: 'https://huggingface.co/Kijai/MiniMax-H3_comfy/resolve/main/loras/minimax_h3_fl2v_lightx2v_turbo_8step_v1.0_resized_avg_rank_24_bf16.safetensors',\n\t\tsha256: '8e05b7b982c3aff7deb692a188c8a8d8acaeff8a12abfe1aeac822fb8ee3f0b7',\n\t\tstrength: 1.0,\n\t\tuse: 'the speed distillation the step count is built around',\n\t\tkind: 'base'\n\t},\n\t{\n\t\tkey: 'larry',\n\t\tlabel: 'Larryvrh turbo v4-600 ema',\n\t\tfile: 'minimax_h3_turbo_v4_step600_ema.safetensors',\n\t\turl: 'https://huggingface.co/larryvrh/MiniMax-H3-Turbo-Lora/resolve/main/minimax_h3_turbo_v4_step600_ema.safetensors',\n\t\tsha256: '5f3a626cd72c93a8b9318d6760c510bc5092d2ab13aaba1f932c5bab07a416d3',\n\t\tstrength: 1.0,\n\t\tuse: 'the speed distillation the step count is built around',\n\t\tkind: 'base'\n\t}\n];\n\n/** A new entry here, or a new file for an existing one, renders with the\n *  harness's pre-render checks on until its file joins ON_FLEET in preflight.ts —\n *  and it joins only once a clip that loads it has come back from the hosted\n *  fleet, because those checks are what download it there. */\nexport const CATALOGUE: Lora[] = [\n\t{\n\t\tkey: 'bj',\n\t\tlabel: 'Blowjob v2.1',\n\t\t// Hyphens, not spaces, and not cosmetic: this string is the download's\n\t\t// filename, the entry's name in the bundle AND the value the graph's lora\n\t\t// loader asks for, so it has to be the name the file actually has in the\n\t\t// mirror bucket — where it is `MM-H3-Blowjob-v2.1.safetensors`.\n\t\tfile: 'MM-H3-Blowjob-v2.1.safetensors',\n\t\turl: 'https://civitai.com/api/download/models/3235946?fileId=3118341',\n\t\tsha256: 'aef6d0c6b758352fd4cfe302d3b9121fb0c18e470bde4bdb2025229e1febee6d',\n\t\tstrength: 1.2,\n\t\t/** author: 'Strength: 1.0 - 1.5' */\n\t\tband: [1.0, 1.5],\n\t\ttrigger: 'bl0w_j0b',\n\t\tuse: 'oral sex, mouth on cock',\n\t\tkind: 'act'\n\t},\n\t{\n\t\tkey: 'deep',\n\t\tlabel: \"Daring's Deepthroat\",\n\t\tfile: 'deepthroat_v02.safetensors',\n\t\turl: 'https://civitai.com/api/download/models/3226989?fileId=3109184',\n\t\tsha256: '1fd239662f6290255b0bb3a220764fb53aab2859378f7fd3024030c1e1991cb2',\n\t\tstrength: 0.8,\n\t\tuse: 'oral sex taken deep into the throat',\n\t\tkind: 'act'\n\t},\n\t{\n\t\tkey: 'miss',\n\t\tlabel: 'POV Missionary Insertion',\n\t\tfile: 'H3_Mis_Insrt_v07.safetensors',\n\t\turl: 'https://civitai.com/api/download/models/3210503?fileId=3092209',\n\t\tsha256: '8d1ed16cdae02e25308063053f7f459b88fb4c50d7e6ea4d05ebc4950a992584',\n\t\tstrength: 0.8,\n\t\tuse: 'vaginal penetration seen from above, missionary',\n\t\tcamera:\n\t\t\t\"POV from the penetrating partner's own eyeline, looking down her body. \" +\n\t\t\t'They are behind the lens and will not be in frame.',\n\t\tkind: 'act'\n\t},\n\t{\n\t\tkey: 'aio',\n\t\tlabel: 'HMNSFW AIO V2',\n\t\tfile: 'HMNSFW_AIO_V2.safetensors',\n\t\turl: 'https://civitai.com/api/download/models/3206518?fileId=3088013',\n\t\tsha256: '608e4212f2788b6063330ff1196fc1f4b4228cfd9a413a63c198a09d7e4a61cb',\n\t\tstrength: 0.5,\n\t\t/** author: 'use it at strength 0.5 or below' */\n\t\tband: [0.3, 0.5],\n\t\ttrigger: 'hmmotion',\n\t\tuse: 'sex in general, and the only thing covering doggy or standing — no adapter exists for those, so the brief carries the geometry alone',\n\t\tkind: 'act'\n\t},\n\n\t{\n\t\tkey: 'penis',\n\t\tlabel: 'HMPenis v2.0',\n\t\tfile: 'PenisV2_minimax-h3_epoch60.safetensors',\n\t\turl: 'https://civitai.com/api/download/models/3247473?fileId=3130327',\n\t\tsha256: '017dd1adddc1be3ec0605dd2e7de97138eb2c6c6ba24be402cf47f103ac1f1b3',\n\t\tstrength: 0.8,\n\t\ttrigger: 'penis',\n\t\tuse: 'the cock itself is the subject — the frame is on it, not merely containing it',\n\t\tkind: 'detail'\n\t},\n\t{\n\t\tkey: 'pussy',\n\t\tlabel: 'HMPussy v0.5',\n\t\t// The version's FIRST .safetensors, which is not the one this entry used to\n\t\t// name. The harness rewrites the fileId in a civitai url and fetches the\n\t\t// first file regardless: asked for hmpussy_v6_epoch30 it delivered\n\t\t// vagassist_e40's bytes, and the sha check caught it after two failed\n\t\t// download jobs on an A100. Same version, same \"stills + motion\" training,\n\t\t// half the size.\n\t\tfile: 'vagassist_e40.safetensors',\n\t\turl: 'https://civitai.com/api/download/models/3215304?fileId=3097094',\n\t\tsha256: '2c2fdb66bf558de1aabda504a81d4ada5f4cebc20e8f519dc6ed3bb6d4be8c9a',\n\t\tstrength: 0.8,\n\t\ttrigger: 'hmpussy',\n\t\tuse: 'her pussy is the subject — the frame is on it, not merely containing it',\n\t\tkind: 'detail'\n\t},\n\t{\n\t\tkey: 'breast',\n\t\tlabel: 'Breast Play & Jiggle',\n\t\tfile: 'breastplayjiggle_h3_v1.safetensors',\n\t\turl: 'https://civitai.com/api/download/models/3225638?fileId=3107724',\n\t\tsha256: 'f9cbcaa596b6b281f154388e407e7b4c4ee97ba9917614ab36bc5e86edf374f5',\n\t\tstrength: 0.75,\n\t\t/** author: '0.7-0.8 seems to be the sweet spot', 1.0 gives finger artifacts */\n\t\tband: [0.7, 0.8],\n\t\tuse: 'breasts move, or are touched',\n\t\tkind: 'detail'\n\t},\n\t{\n\t\tkey: 'moan',\n\t\tlabel: 'moawxx moans + writhing',\n\t\t// Epoch 2000 rather than the 2500 that was chosen, for the same reason as\n\t\t// the pussy entry: this version ships three epochs and the harness fetches\n\t\t// the first whatever the fileId says. This one had never been downloaded,\n\t\t// so the trap was sitting there waiting for the first clip that wanted\n\t\t// moaning. The author gave no guidance beyond \"test different epochs\".\n\t\tfile: 'moawxx_000002000.safetensors',\n\t\turl: 'https://civitai.com/api/download/models/3228089?fileId=3110357',\n\t\tsha256: 'bc0841e216198174ff5937e3ba2f4c9234c163082276cd9f4e5f8889ae12e4e5',\n\t\tstrength: 0.75,\n\t\t/** author: '0.6 - 0.85 (1.0 often degrades image quality)' */\n\t\tband: [0.6, 0.85],\n\t\ttrigger: 'moawxx',\n\t\tuse: 'she moans, and her body responds to it',\n\t\tkind: 'detail'\n\t},\n\t{\n\t\tkey: 'cum',\n\t\tlabel: 'HMCumshot V2',\n\t\tfile: 'HMCumshot_V2.safetensors',\n\t\t// CoachBate's cumshot adapter was here and had to go: civitai answers 403\n\t\t// for both of its files, with a valid token that every other entry in this\n\t\t// list downloads with, and no availability or early-access flag in the API\n\t\t// to explain it. A clip that picked it did not fail fast either — the\n\t\t// download retried for seventeen minutes before the run gave up.\n\t\t//\n\t\t// This one is from the same author as aio, pussy and penis, so it is built\n\t\t// to sit alongside them rather than argue with them.\n\t\turl: 'https://civitai.com/api/download/models/3238531?fileId=3121030',\n\t\tsha256: '1a5b7948bb97f27737e62c3dd5497a3afb77517f230787f45e45c7d8fe3dc24d',\n\t\tstrength: 0.8,\n\t\ttrigger: 'cumshot',\n\t\t// A squirt clip picked this and came back with a thin dribble instead of\n\t\t// jets. The adapter is a cumshot adapter and the request was a female\n\t\t// squirt — different events, whatever else is true of the training set —\n\t\t// and the catalogue's old wording, \"the shot ends in ejaculation\", covered\n\t\t// both. A search across four terms found no squirt adapter for this model\n\t\t// at all, so that one is the brief's job and nothing here helps it.\n\t\tuse: 'a man ejaculates and that is the point. Not a female squirt — nothing in this list covers that',\n\t\tkind: 'detail'\n\t}\n];\n\nconst BY_KEY = new Map([...BASE, ...CATALOGUE].map((l) => [l.key, l]));\n\nexport function loraFor(key: string): Lora | undefined {\n\treturn BY_KEY.get(key);\n}\n\n/** One chosen adapter: which, and how hard. */\nexport interface Pick {\n\tkey: string;\n\tstrength: number;\n}\n\n/** How many can be stacked before the render turns to soup. Base pair excluded.\n *\n *  Four was the opening guess and it did not survive its first outing: a POV\n *  missionary clip rendered with miss + penis + pussy + breast came back with\n *  the anatomy at the point of penetration incoherent — everything else in the\n *  frame correct, that one region not. Two adapters were drawing the same few\n *  hundred pixels and disagreed.\n *\n *  Two is not a measured number either, and saying otherwise would be pretending\n *  we know something we do not. It is the smallest count that still does the job\n *  the feature exists for — the act, plus the one thing that most needs helping —\n *  and it is the safe end to start from when the only evidence in hand is a\n *  failure at four. If clips start looking underserved, raise it a notch and\n *  look; do not raise it on the argument that more adapters must be better,\n *  which is the argument that produced the broken one. */\nexport const MAX_PICKS = 2;\n\n/** Parse the selection out of a URL path segment.\n *\n *  `bj-1.2,penis-0.8,moan-0.75` — pairs split on the last dash, so a key can\n *  never be mistaken for part of a number. Unknown keys are dropped rather\n *  than failing the request: a render that quietly loses one adapter is\n *  recoverable, and one that 404s at the harness is a dead workspace.\n */\nexport function parsePicks(seg: string): Pick[] {\n\tconst out: Pick[] = [];\n\tfor (const raw of decodeURIComponent(seg).split(',')) {\n\t\tconst part = raw.trim();\n\t\tif (!part) continue;\n\t\tconst cut = part.lastIndexOf('-');\n\t\tif (cut < 1) continue;\n\t\tconst key = part.slice(0, cut);\n\t\tconst n = Number(part.slice(cut + 1));\n\t\tif (!BY_KEY.has(key) || !Number.isFinite(n)) continue;\n\t\t// Base adapters are never picks. They load on every clip regardless, and\n\t\t// letting one through here would let it fill the cap below and push a real\n\t\t// choice off the end — which is exactly what happened once already, and\n\t\t// silently, because the url looked correct while the graph was not.\n\t\tif (BY_KEY.get(key)!.kind === 'base') continue;\n\t\tout.push({ key, strength: Math.min(3, Math.max(0, n)) });\n\t}\n\treturn out.slice(0, MAX_PICKS);\n}\n\n/** The base adapters' strengths, read out of the same path segment.\n *\n *  Kept separate from parsePicks on purpose. A base adapter must never be able\n *  to consume one of the pick slots — that is exactly how the chosen adapters\n *  got dropped out of a bundle for eight renders — so parsePicks refuses base\n *  keys outright and this reads them instead. They carry a strength but never a\n *  slot, and there is no cap here because the base set is fixed. */\nexport function parseBaseOverrides(seg: string): Record<string, number> {\n\tconst out: Record<string, number> = {};\n\tfor (const raw of decodeURIComponent(seg).split(',')) {\n\t\tconst part = raw.trim();\n\t\tconst cut = part.lastIndexOf('-');\n\t\tif (cut < 1) continue;\n\t\tconst key = part.slice(0, cut);\n\t\tconst n = Number(part.slice(cut + 1));\n\t\tconst l = BY_KEY.get(key);\n\t\tif (!l || l.kind !== 'base' || !Number.isFinite(n)) continue;\n\t\tout[key] = Math.min(3, Math.max(0, n));\n\t}\n\treturn out;\n}\n\n/** How many reference images this clip was launched with.\n *\n *  Carried in the same path segment as everything else, as `ref-<n>`, and read\n *  separately for the same reason the base strengths are: it is not a pick and\n *  must never occupy a pick slot. Zero means the bundle is built exactly as it\n *  was before any of this existed. */\nexport function parseRefCount(seg: string): number {\n\tfor (const raw of decodeURIComponent(seg).split(',')) {\n\t\tconst m = /^ref-(\\d+)$/.exec(raw.trim());\n\t\tif (m) return Math.min(9, Math.max(0, Number(m[1])));\n\t}\n\treturn 0;\n}\n\n/** The run this bundle belongs to, as `run-<slug>`.\n *\n *  Needed because the reference images are served from the same directory the\n *  bundle is, and the generator has to know whose they are. Parsed with its own\n *  pattern rather than the pair-splitter, since a slug is full of dashes. */\nexport function parseRunSlug(seg: string): string {\n\tfor (const raw of decodeURIComponent(seg).split(',')) {\n\t\tconst m = /^run-([a-z0-9-]{4,80})$/.exec(raw.trim());\n\t\tif (m) return m[1];\n\t}\n\treturn '';\n}\n\n/** Whether this continuation is pinned to the prior clip's final frame.\n *\n *  Carried as `pin-1`, beside `ref-<n>` and for the same reason: the bundle is\n *  built per workspace, and the seam anchor is a node that must not exist when\n *  the operator asked for a free start. Absent means the bundle is built exactly\n *  as it was before the anchor existed.\n */\nexport function parsePinSeam(seg: string): boolean {\n\treturn decodeURIComponent(seg)\n\t\t.split(',')\n\t\t.some((raw) => raw.trim() === 'pin-1');\n}\n\n/** Whether this clip samples on four GPUs through Raylight (raylight.ts).\n *\n *  Carried as `fast-1`, beside `pin-1` and for the same reason: the graph is\n *  built when the harness asks for it, and the selector is the one thing that\n *  reaches the builder. Not a pick — parsePicks drops keys it does not know.\n */\nexport function parseFast(seg: string): boolean {\n\treturn decodeURIComponent(seg)\n\t\t.split(',')\n\t\t.some((raw) => raw.trim() === 'fast-1');\n}\n\nexport function formatPicks(picks: Pick[]): string {\n\treturn picks.map((p) => `${p.key}-${p.strength}`).join(',');\n}\n\n/** The catalogue as the writer reads it.\n *\n *  Generated from the list above rather than written out again in the prompt,\n *  because the two would drift and the drift would be silent: the writer would\n *  keep naming an adapter that had been removed, the render would quietly lose\n *  it, and nothing anywhere would say so.\n */\nexport function catalogueForWriter(): string {\n\tconst line = (l: Lora) => {\n\t\tconst w = l.band ? `${l.strength} (${l.band[0]}-${l.band[1]})` : `${l.strength} fixed`;\n\t\treturn (\n\t\t\t`  ${l.key.padEnd(8)}${w.padEnd(16)}${l.use}` +\n\t\t\t(l.trigger ? `  [trigger: ${l.trigger}]` : '') +\n\t\t\t(l.camera ? `\\n${' '.repeat(26)}CAMERA: ${l.camera}` : '')\n\t\t);\n\t};\n\tconst acts = CATALOGUE.filter((l) => l.kind === 'act')\n\t\t.map(line)\n\t\t.join('\\n');\n\tconst details = CATALOGUE.filter((l) => l.kind === 'detail')\n\t\t.map(line)\n\t\t.join('\\n');\n\n\treturn `ADAPTERS\n\nAlong with the prompt, choose the adapters this clip is rendered with. Two are\nloaded on every clip regardless — the speed distillation and the skin detailer —\nand are not yours to pick. From the lists below choose:\n\n  - at most one ACT: the thing that is happening in THIS shot\n  - at most one DETAIL, and only when it is central to the shot\n\nTHE ACT IS WHAT HAPPENS IN THIS CLIP, AND SOMETIMES THAT IS NONE OF THEM. An act\nadapter is weights, not a label: it is trained on one thing happening and it\nkeeps producing that thing whether or not the description asks for it. So it is\nchosen for what this five seconds contains, and a clip that contains none of\nthese acts takes none — the base adapters carry it perfectly well, and the slot\nis not a quota.\n\nThe trap is a continuation, where the scene has an act but the beat has moved\npast it. A clip written as \"he withdraws and comes on her stomach\" was rendered\nwith the general sex adapter, because the scene was sex. Nothing in it should\nhave been thrusting — the brief asks for the aftershocks to fade, the withdrawal,\nand then a cumshot — and the measured motion came back at six strokes a second\nanyway. The adapter was doing exactly what it was trained to do, in a clip that\nhad no use for it.\n\n  the act is happening for most of the clip     take the act\n  the act ends in the first beat, or is over    take no act\n  aftermath, withdrawal, a cumshot, dialogue,\n  standing up, walking away                     take no act\n\nWhere the clip is only the finish, the cumshot detail alone is the right choice\nand the act slot stays empty.\n\nThe anatomy corrector is no longer on this list; it loads on every clip now, so\nyou never need to ask for it and it no longer costs you a slot.\n  - ${MAX_PICKS} in total, never more\n\nPrefer the act alone. A detail earns its place only when the request turns on\nit — a shot that ends in a cumshot needs the cumshot adapter, a shot where the\nmoaning is the point needs the moaning one. A detail that is merely true of the\nframe is not worth an adapter slot, and adding it costs something real.\n\nAN ADAPTER MARKED \"CAMERA\" DECIDES THE VIEWPOINT, NOT YOU. Most of these draw a\nthing and leave the framing alone. The ones with a CAMERA line were trained on a\nsingle viewpoint and put it back whatever the description says — and the\ndescription loses. A missionary brief written as \"elevated high angle looking\ndown at the sofa, his shoulders and upper back in the lower centre of frame\"\ncame back as POV from his own eyeline: he was not in the shot at all, and her\nbody was stretched long by the foreshortening that viewpoint forces on it.\n\nSo the choice and the camera are one decision, not two:\n\n  - Picking one of these means writing ITS camera. Describe the shot from that\n    viewpoint, and do not place the person behind the lens in the frame — say\n    what is visible of them instead: their hands, their hips entering frame.\n  - If the shot needs both bodies visible, or the operator asked for a framing\n    of their own, choose a different act. A shot that shows both people and one\n    that is shot from inside one of them are different shots, and no wording\n    gets you both.\n\nThe two anatomy detailers are the ones to be most careful with. They draw a\nsmall region of the frame very hard, and on a penetration shot that is enough to\nwreck it: the same POV missionary brief rendered as one fused mass with the\npenis detailer loaded, and correctly — a shaft entering an opening — with the\nact adapter alone. Tested both ways, twice broken and then fixed by removing it.\n\nTake one only when that anatomy is what the frame is built around, and never\nboth: they draw the same pixels and disagree.\n\nWhere a shot shows a join — penetration, a mouth on a cock — the act adapter\nalready knows how the parts meet. Adding a detailer on top of it is the case\nthat has failed, so prefer the act alone unless the request is specifically\nabout how one of the parts looks.\n\nReturn them in the \"loras\" field as [{\"key\":\"bj\",\"strength\":1.2}, …].\n\nThe number beside each is the adapter author's own recommendation. Where a range\nfollows it in brackets, that is the range the author published, and you may\nplace the strength anywhere inside it: higher when the thing that adapter does is\nwhat the request is really about, lower when it is present but not the point.\nNever go outside the range. Where the number says \"fixed\" the author published no\nrange, so send that number unchanged — a range nobody measured is not yours to\ninvent either.\n\nChoose on what the shot actually contains, not on what sounds related. An\nadapter for something that is not in the frame does not sit idle — a loaded\nadapter is always acting, and one trained on the wrong thing pulls the image\ntoward it. Where a trigger word is listed, the shot lands closer if that word\nappears naturally somewhere in the prompt text; the word aims the adapter, it\ndoes not switch it on.\n\nACTS — choose exactly one\n${acts}\n\nDETAILS — choose any that are true of this shot\n${details}`;\n}\n\n/** How far into the prior clip the reference video starts reading.\n *\n *  Carried as `st-<seconds>`, in the same segment as `pin-1` and for the same\n *  reason: the bundle is built per workspace and this value belongs to the clip\n *  being continued, which the bundle route never sees. Absent means read from\n *  the start, which is what the bundle does on its own.\n */\nexport function parseRefStart(seg: string): number | null {\n\tfor (const raw of decodeURIComponent(seg).split(',')) {\n\t\tconst m = /^st-(\\d+(?:\\.\\d+)?)$/.exec(raw.trim());\n\t\tif (m) {\n\t\t\tconst v = Number(m[1]);\n\t\t\tif (Number.isFinite(v) && v >= 0) return v;\n\t\t}\n\t}\n\treturn null;\n}\n","<script lang=\"ts\">\n\t/** What the studio is, said underneath it (RAT-632).\n\t *\n\t *  The front page is the studio and stays the studio (D2, 2026-09-26): the\n\t *  first screen is the greeting, the starters and the wall, and nothing here\n\t *  moves any of them. This sits after the wall, at the end of the empty\n\t *  stage's own scroll, for whoever scrolls past the films — and for search\n\t *  engines, which read a page from top to bottom and until now found ninety\n\t *  words on this one and none of the searches it answers.\n\t *\n\t *  Every sentence is a fact about the studio as it ships. The questions come\n\t *  from $lib/studio-seo, where the FAQPage markup is built from the same list;\n\t *  the lengths from the price table. Change the words when the product\n\t *  changes, and move the sitemap's lastmod for `/` with them.\n\t *\n\t *  `shown` holds the block invisible until the wall has arrived. The wall is\n\t *  fetched after the page loads; drawn before it, this block would be pushed\n\t *  a screen down the moment the films land — a layout shift every visitor\n\t *  pays for. Invisible, it still takes its room and is still in the HTML the\n\t *  server sent, so the greeting no longer jumps either — it used to sit in\n\t *  the middle of an empty stage and leap to the top when the wall came in. */\n\timport { STUDIO_FAQ } from '$lib/studio-seo';\n\timport { TOKENS_BY_SECONDS } from '$lib/studio/pricing';\n\n\tlet { shown = true }: { shown?: boolean } = $props();\n\n\tconst lengths = Object.keys(TOKENS_BY_SECONDS)\n\t\t.map(Number)\n\t\t.sort((a, b) => a - b);\n\tconst lengthWords = `${lengths.slice(0, -1).join(', ')} or ${lengths[lengths.length - 1]}`;\n\t/** \"4 for 5 seconds, 6 for 10 and 9 for 15\", from the same table. */\n\tconst priceParts = lengths.map(\n\t\t(s, i) => `${TOKENS_BY_SECONDS[s]} for ${s}${i === 0 ? ' seconds' : ''}`\n\t);\n\tconst priceWords = `${priceParts.slice(0, -1).join(', ')} and ${priceParts[priceParts.length - 1]}`;\n\n\tconst linkClass =\n\t\t'text-[var(--st-text)] underline underline-offset-2 transition-opacity hover:opacity-70';\n\n\tconst STEPS = [\n\t\t{\n\t\t\ttitle: 'Describe one shot.',\n\t\t\tbody: 'One action, in plain words: who, where, what happens. Everyone on screen is an adult, and the studio holds to that.'\n\t\t},\n\t\t{\n\t\t\ttitle: 'Choose who is in it.',\n\t\t\tbody: 'A character you built, or let the writer cast one from your description.'\n\t\t},\n\t\t{\n\t\t\ttitle: 'Pick the shape.',\n\t\t\tbody: `${lengthWords} seconds, 288p, 576p or 864p, portrait or landscape — and several takes or camera angles at once, if you want to choose between them.`\n\t\t},\n\t\t{\n\t\t\ttitle: 'Keep it, or carry it on.',\n\t\t\tbody: 'A clip takes a few minutes. Keep the ones you like, continue the best one into the next beat, and join them into a film.'\n\t\t}\n\t];\n</script>\n\n<!-- No fade on the way in. A transition only runs on painted frames, and a\n\t page nobody is looking at — a background tab, a crawler's renderer — may\n\t never paint one, which would leave the words at opacity 0 for good. A\n\t visibility switch takes effect the moment the style is computed. -->\n<section\n\taria-labelledby=\"studio-about-title\"\n\tclass=\"about w-full max-w-3xl pt-16 pb-6 text-left {shown ? '' : 'invisible'}\"\n>\n\t<h2 id=\"studio-about-title\" class=\"text-[1.75rem] leading-tight font-semibold tracking-[-0.03em] text-balance\">\n\t\tAn NSFW AI video generator, with sound\n\t</h2>\n\t<p class=\"mt-3 max-w-[36rem] text-[15px] leading-relaxed text-[var(--about-body)]\">\n\t\tPornPilot turns a sentence into an adult clip. Describe the shot — who is in it, where they are,\n\t\twhat happens — and the studio writes the direction, casts the people, films it and hands the clip\n\t\tback with sound. Then carry it on into the next one, with the same people, until it is a film.\n\t</p>\n\t<p class=\"mt-3 max-w-[36rem] text-[15px] leading-relaxed text-[var(--about-body)]\">\n\t\tIt is built for film rather than single pictures. A picture generator gives you a still; this\n\t\tgives you a shot that moves and makes a sound, then the next shot of the same scene, so a few\n\t\tclips joined together become a short film.\n\t</p>\n\n\t<ul class=\"mt-8 grid gap-2.5 sm:grid-cols-3\">\n\t\t<li class=\"rounded-xl bg-[var(--st-surface)] p-4\">\n\t\t\t<h3 class=\"text-[15px] font-semibold\">Sound in every clip</h3>\n\t\t\t<p class=\"mt-1.5 text-sm leading-relaxed text-[var(--about-body)]\">\n\t\t\t\tMoans, breathing and spoken lines come back with the picture. Put a line in quotes and it is\n\t\t\t\tsaid, with the lips moving to it.\n\t\t\t</p>\n\t\t</li>\n\t\t<li class=\"rounded-xl bg-[var(--st-surface)] p-4\">\n\t\t\t<h3 class=\"text-[15px] font-semibold\">The same people, shot after shot</h3>\n\t\t\t<p class=\"mt-1.5 text-sm leading-relaxed text-[var(--about-body)]\">\n\t\t\t\tBuild a character from a description or from a photo of yourself. The studio draws them in\n\t\t\t\tfour views and keeps them the same from one clip to the next.\n\t\t\t</p>\n\t\t</li>\n\t\t<li class=\"rounded-xl bg-[var(--st-surface)] p-4\">\n\t\t\t<h3 class=\"text-[15px] font-semibold\">Clips that carry on</h3>\n\t\t\t<p class=\"mt-1.5 text-sm leading-relaxed text-[var(--about-body)]\">\n\t\t\t\tContinue a clip and the next one starts on its last frame, in the same voice, so a scene keeps\n\t\t\t\tgoing instead of starting over. Join them into one film when you are done.\n\t\t\t</p>\n\t\t</li>\n\t</ul>\n\n\t<h2 class=\"mt-14 text-[1.375rem] leading-tight font-semibold tracking-[-0.025em]\">\n\t\tWhat you can make\n\t</h2>\n\t<p class=\"mt-3 max-w-[36rem] text-[15px] leading-relaxed text-[var(--about-body)]\">\n\t\tCouples, two women, two men, one person alone: say who is there and the studio casts them —\n\t\tinvented adults, or you. It is at its best with <a href=\"/prompts#pov\" class={linkClass}>POV</a>,\n\t\t<a href=\"/prompts#breasts\" class={linkClass}>breast play</a>,\n\t\t<a href=\"/prompts#sound\" class={linkClass}>sex with moaning and dialogue</a> and\n\t\t<a href=\"/prompts#sensual\" class={linkClass}>slow, sensual scenes</a> with no act at all, and\n\t\t<a href=\"/prompts#oral\" class={linkClass}>oral</a> works too. The same box makes\n\t\t<a href=\"/prompts#gay\" class={linkClass}>gay AI porn</a> and\n\t\t<a href=\"/prompts#lesbian\" class={linkClass}>lesbian scenes</a>.\n\t</p>\n\n\t<h2 class=\"mt-14 text-[1.375rem] leading-tight font-semibold tracking-[-0.025em]\">\n\t\tHow to make an NSFW AI video\n\t</h2>\n\t<ol class=\"mt-5 grid gap-4\">\n\t\t{#each STEPS as step, i (step.title)}\n\t\t\t<li class=\"flex gap-3\">\n\t\t\t\t<span\n\t\t\t\t\tclass=\"flex size-6 shrink-0 items-center justify-center rounded-full bg-[var(--st-surface-2)] text-xs font-semibold tabular-nums\"\n\t\t\t\t\taria-hidden=\"true\">{i + 1}</span\n\t\t\t\t>\n\t\t\t\t<p class=\"text-[15px] leading-relaxed text-[var(--about-body)]\">\n\t\t\t\t\t<strong class=\"font-semibold text-[var(--st-text)]\">{step.title}</strong>\n\t\t\t\t\t{step.body}\n\t\t\t\t</p>\n\t\t\t</li>\n\t\t{/each}\n\t</ol>\n\n\t<h2 class=\"mt-14 text-[1.375rem] leading-tight font-semibold tracking-[-0.025em]\">\n\t\tCharacters, and pictures of you\n\t</h2>\n\t<p class=\"mt-3 max-w-[36rem] text-[15px] leading-relaxed text-[var(--about-body)]\">\n\t\tA character is someone the studio keeps. Describe them — age, build, hair, what they wear — or\n\t\tattach a photo of yourself, and it draws them in four views; any clip you shoot with them starts\n\t\tfrom those. A clip can carry up to nine reference pictures, for the people and the place. Every\n\t\tpicture is of you or of someone invented, never of a real person who has not agreed to it.\n\t</p>\n\n\t<h2 class=\"mt-14 text-[1.375rem] leading-tight font-semibold tracking-[-0.025em]\">\n\t\tTips for a better first clip\n\t</h2>\n\t<ul class=\"mt-4 grid max-w-[36rem] list-disc gap-2 pl-5 text-[15px] leading-relaxed text-[var(--about-body)]\">\n\t\t<li>Say everyone's age: \"a woman in her twenties\", \"a man in his thirties\".</li>\n\t\t<li>One action per clip. The next beat is the next clip, carried on from this one.</li>\n\t\t<li>Give it a place and a light — a pool at night, a hotel room in the morning.</li>\n\t\t<li>Name the camera: POV, from the side, from above, close.</li>\n\t\t<li>Write the sound in, and put spoken lines in quotes.</li>\n\t</ul>\n\t<p class=\"mt-3 max-w-[36rem] text-[15px] leading-relaxed text-[var(--about-body)]\">\n\t\tMore in the <a href=\"/prompts#how-to-write\" class={linkClass}>guide to writing NSFW AI prompts</a>.\n\t</p>\n\n\t<h2 class=\"mt-14 text-[1.375rem] leading-tight font-semibold tracking-[-0.025em]\">\n\t\tUncensored, with three rules\n\t</h2>\n\t<p class=\"mt-3 max-w-[36rem] text-[15px] leading-relaxed text-[var(--about-body)]\">\n\t\tExplicit scenes between consenting adults are what PornPilot is built for. Three things are off\n\t\tlimits, however the request is worded: anyone under 18, which the studio refuses outright; a real\n\t\tperson who has not agreed to it — upload photos of yourself only; and deepfakes or impersonation of\n\t\tanyone.\n\t</p>\n\n\t<h2 class=\"mt-14 text-[1.375rem] leading-tight font-semibold tracking-[-0.025em]\">What it costs</h2>\n\t<p class=\"mt-3 max-w-[36rem] text-[15px] leading-relaxed text-[var(--about-body)]\">\n\t\tWriting scenes and building characters is free, and you can start without an account. A clip is\n\t\tpaid in tokens when it renders — {priceWords} — and each extra take or camera angle is another clip.\n\t\tTokens come in packs from the shop, and a clip that fails to render is paid back.\n\t</p>\n\n\t<h2 class=\"mt-14 text-[1.375rem] leading-tight font-semibold tracking-[-0.025em]\">\n\t\tStart from a prompt\n\t</h2>\n\t<p class=\"mt-3 max-w-[36rem] text-[15px] leading-relaxed text-[var(--about-body)]\">\n\t\tNot sure where to begin? The <a href=\"/prompts\" class={linkClass}>NSFW AI prompts</a> page has prompts\n\t\twritten for what the studio renders well — POV, sensual, oral, lesbian, gay and solo — each a tap away\n\t\tfrom this box. Or build one with the\n\t\t<a href=\"/prompts/generator\" class={linkClass}>NSFW AI prompt generator</a>.\n\t</p>\n\n\t<h2 class=\"mt-14 text-[1.375rem] leading-tight font-semibold tracking-[-0.025em]\">Questions</h2>\n\t<div class=\"mt-5 grid gap-6\">\n\t\t{#each STUDIO_FAQ as f (f.q)}\n\t\t\t<div>\n\t\t\t\t<h3 class=\"text-[15px] font-semibold\">{f.q}</h3>\n\t\t\t\t<p class=\"mt-1 max-w-[36rem] text-[15px] leading-relaxed text-[var(--about-body)]\">{f.a}</p>\n\t\t\t</div>\n\t\t{/each}\n\t</div>\n</section>\n\n<style>\n\t/* Paragraph copy one step brighter than the studio's --st-muted (#737373,\n\t * 4.1:1 on its background): a label can live with that, a paragraph cannot.\n\t * The same step the prompt library uses. */\n\t.about {\n\t\t--about-body: #a1a1a6;\n\t}\n</style>\n","/** Pictures made small enough to cross the host's request limit.\n *\n *  A studio upload is a function request, and the host caps a function's\n *  request body at 4.5 MB. Phone photos routinely exceed that, and the attach\n *  control answered them with a 413 that surfaced as a raw `SyntaxError` — 144\n *  rage clicks from 118 people on that one control since launch, most of them on\n *  phones. A reference is also the studio's biggest conversion lever, so the\n *  picture that fails is the one that mattered most.\n *\n *  So every picture goes through the canvas first: orientation baked in from the\n *  EXIF (a sideways reference is a sideways render), metadata dropped, and the\n *  long edge capped at 2048 px. That alone brings a 12 MB phone JPEG to about a\n *  megabyte. What it does NOT do is shrink a PNG much — a PNG photo stays\n *  lossless, and the failure that was reproduced was a 7.2 MB PNG — so anything\n *  still over the budget is re-encoded as JPEG, stepping the edge down until it\n *  fits.\n *\n *  Written here rather than borrowed from the site's own EXIF helper because the\n *  studio is meant to lift out of this app with one seam (host.ts), and a\n *  picture shrinker is not host business.\n */\n\n/** Under the 4.5 MB body limit with room for the multipart framing and the\n *  description that travels beside the file. */\nexport const UPLOAD_BUDGET_BYTES = 3.5 * 1024 * 1024;\n\nconst FIRST_EDGE = 2048;\n/** Tried in order when a picture is still over budget after the first pass. */\nconst FALLBACK_EDGES = [2048, 1600, 1280, 1024];\nconst JPEG_QUALITY = 0.88;\n\n/** HEIC/HEIF brands, read from the container rather than trusted from the name:\n *  an iPhone photo renamed to .jpg keeps its HEIC payload. */\nconst HEIC_BRANDS = new Set([\n\t'heic',\n\t'heix',\n\t'heim',\n\t'heis',\n\t'hevc',\n\t'hevx',\n\t'hevm',\n\t'hevs',\n\t'mif1',\n\t'msf1'\n]);\n\nasync function isHeic(file: File): Promise<boolean> {\n\tif (/\\.(heic|heif)$/i.test(file.name) || /heic|heif/i.test(file.type)) return true;\n\ttry {\n\t\tconst b = new Uint8Array(await file.slice(0, 12).arrayBuffer());\n\t\tif (b.length < 12) return false;\n\t\tif (String.fromCharCode(b[4], b[5], b[6], b[7]) !== 'ftyp') return false;\n\t\treturn HEIC_BRANDS.has(String.fromCharCode(b[8], b[9], b[10], b[11]));\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/** HEIC to a JPEG blob. Only Safari decodes HEIC natively, so everywhere else\n *  this is the difference between a picture and nothing. Imported on demand:\n *  the decoder is a large download that almost no session needs. */\nasync function heicToJpegBlob(file: File): Promise<Blob | null> {\n\ttry {\n\t\tconst mod = (await import('heic2any')) as unknown as {\n\t\t\tdefault: (o: { blob: Blob; toType: string; quality: number }) => Promise<Blob | Blob[]>;\n\t\t};\n\t\tconst out = await mod.default({ blob: file, toType: 'image/jpeg', quality: 0.92 });\n\t\treturn Array.isArray(out) ? (out[0] ?? null) : out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\ntype Drawable = ImageBitmap | HTMLImageElement;\n\nasync function decode(blob: Blob): Promise<Drawable | null> {\n\tif (typeof createImageBitmap === 'function') {\n\t\ttry {\n\t\t\treturn await createImageBitmap(blob, { imageOrientation: 'from-image' });\n\t\t} catch {\n\t\t\t/* fall through to an <img>, which every browser can decode */\n\t\t}\n\t}\n\t// onload rather than img.decode(): decode() is deferred in a backgrounded\n\t// document, and someone who switches apps while a photo attaches is exactly\n\t// who this path is for.\n\tconst url = URL.createObjectURL(blob);\n\ttry {\n\t\treturn await new Promise<HTMLImageElement | null>((resolve) => {\n\t\t\tconst img = new Image();\n\t\t\timg.onload = () => resolve(img);\n\t\t\timg.onerror = () => resolve(null);\n\t\t\timg.src = url;\n\t\t});\n\t} finally {\n\t\tURL.revokeObjectURL(url);\n\t}\n}\n\nfunction sizeOf(d: Drawable): { w: number; h: number } {\n\treturn 'naturalWidth' in d\n\t\t? { w: d.naturalWidth, h: d.naturalHeight }\n\t\t: { w: d.width, h: d.height };\n}\n\nasync function encode(\n\td: Drawable,\n\tmaxEdge: number,\n\ttype: 'image/jpeg' | 'image/png',\n\tquality: number\n): Promise<Blob | null> {\n\tconst { w, h } = sizeOf(d);\n\tif (!w || !h) return null;\n\tconst scale = Math.min(1, maxEdge / Math.max(w, h));\n\tconst width = Math.max(1, Math.round(w * scale));\n\tconst height = Math.max(1, Math.round(h * scale));\n\t// OffscreenCanvas where it exists (Safari from 16.4), the DOM canvas where it\n\t// does not — older iPhones are exactly the people with the biggest photos.\n\tif (typeof OffscreenCanvas !== 'undefined') {\n\t\ttry {\n\t\t\tconst c = new OffscreenCanvas(width, height);\n\t\t\tconst ctx = c.getContext('2d');\n\t\t\tif (ctx) {\n\t\t\t\tctx.drawImage(d, 0, 0, width, height);\n\t\t\t\treturn await c.convertToBlob({ type, quality });\n\t\t\t}\n\t\t} catch {\n\t\t\t/* try the DOM canvas */\n\t\t}\n\t}\n\ttry {\n\t\tconst c = document.createElement('canvas');\n\t\tc.width = width;\n\t\tc.height = height;\n\t\tconst ctx = c.getContext('2d');\n\t\tif (!ctx) return null;\n\t\tctx.drawImage(d, 0, 0, width, height);\n\t\treturn await new Promise<Blob | null>((res) => c.toBlob(res, type, quality));\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction renamed(name: string, ext: 'jpg' | 'png'): string {\n\treturn `${name.replace(/\\.[^.]+$/, '') || 'photo'}.${ext}`;\n}\n\n/** A picture that fits the upload budget, or the best attempt at one.\n *\n *  Never throws: a picture that cannot be decoded comes back as it went in, and\n *  the caller's size check turns that into a sentence instead of a request that\n *  is going to fail. The returned name carries the type, because the server\n *  decides the stored content type from the extension. */\nexport async function shrinkForUpload(file: File): Promise<File> {\n\tlet source: Blob = file;\n\tif (await isHeic(file)) {\n\t\tconst jpeg = await heicToJpegBlob(file);\n\t\tif (jpeg) source = jpeg;\n\t}\n\n\tconst pic = await decode(source);\n\tif (!pic) return file;\n\ttry {\n\t\t// Keep a PNG a PNG on the first pass — transparency survives, and a small\n\t\t// PNG was never the problem. Everything else becomes a JPEG.\n\t\tconst keepPng = file.type === 'image/png' && source === file;\n\t\tconst firstType = keepPng ? 'image/png' : 'image/jpeg';\n\t\tconst first = await encode(pic, FIRST_EDGE, firstType, JPEG_QUALITY);\n\t\tif (first && first.size <= UPLOAD_BUDGET_BYTES) {\n\t\t\treturn new File([first], renamed(file.name, keepPng ? 'png' : 'jpg'), {\n\t\t\t\ttype: firstType,\n\t\t\t\tlastModified: Date.now()\n\t\t\t});\n\t\t}\n\t\tfor (const edge of FALLBACK_EDGES) {\n\t\t\tconst jpeg = await encode(pic, edge, 'image/jpeg', JPEG_QUALITY);\n\t\t\tif (jpeg && jpeg.size <= UPLOAD_BUDGET_BYTES) {\n\t\t\t\treturn new File([jpeg], renamed(file.name, 'jpg'), {\n\t\t\t\t\ttype: 'image/jpeg',\n\t\t\t\t\tlastModified: Date.now()\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn file;\n\t} finally {\n\t\tif ('close' in pic) pic.close();\n\t}\n}\n\n/** Whether a picked file is something this path can carry at all. Videos are\n *  not: the image bucket accepts no video type, and a video is also the file\n *  most likely to be over the request limit, so it is refused here in words\n *  rather than sent to fail. */\nexport function isPicture(file: File): boolean {\n\tif (file.type.startsWith('image/')) return true;\n\treturn /\\.(jpe?g|png|webp|heic|heif)$/i.test(file.name);\n}\n","/** A studio endpoint's answer, read without assuming it is JSON.\n *\n *  Every page call did `await res.json()` on whatever came back, and not\n *  everything that comes back is ours: the host answers an oversized request\n *  with a plain-text 413 before our code runs, a platform 5xx is HTML, and a\n *  dropped connection is a thrown TypeError. Each of those reached the operator\n *  as the exception's own text — `SyntaxError: Unexpected token 'R', \"Request\n *  En\"... is not valid JSON` — which says nothing they can act on. This turns\n *  every one of them into a sentence.\n */\n\nexport type Reply<T> = { ok: true; body: T } | { ok: false; error: string };\n\n/** What went wrong, said to the person who has to decide what to do next. */\nfunction forStatus(status: number): string {\n\tif (status === 413) return 'that file is too large to send — try a smaller one';\n\tif (status === 401 || status === 403) return 'your session has ended — sign in again';\n\tif (status === 429) return 'too many at once — wait a moment and try again';\n\tif (status >= 500) return 'the studio could not take that just now — try again';\n\treturn 'that did not go through — try again';\n}\n\nexport async function readReply<T>(res: Response): Promise<Reply<T>> {\n\tconst json = (res.headers.get('content-type') ?? '').includes('json');\n\tif (!json) return { ok: false, error: forStatus(res.status) };\n\tlet body: unknown;\n\ttry {\n\t\tbody = await res.json();\n\t} catch {\n\t\treturn { ok: false, error: forStatus(res.status) };\n\t}\n\tif (!res.ok) {\n\t\t// Our own error bodies carry a sentence already; a framework one carries\n\t\t// `message`. Neither is shown if it is not a plain short string.\n\t\tconst b = body as { error?: unknown; message?: unknown };\n\t\tconst said =\n\t\t\ttypeof b?.error === 'string' ? b.error : typeof b?.message === 'string' ? b.message : '';\n\t\treturn { ok: false, error: said && said.length < 200 ? said : forStatus(res.status) };\n\t}\n\treturn { ok: true, body: body as T };\n}\n\n/** A fetch that threw rather than answered. */\nexport function whyFailed(e: unknown): string {\n\tconst name = e instanceof Error ? e.name : '';\n\tif (name === 'TimeoutError' || name === 'AbortError') {\n\t\treturn 'that took too long and was stopped — try again, ideally on a steadier connection';\n\t}\n\tif (e instanceof TypeError)\n\t\treturn 'the studio could not be reached — check the connection and try again';\n\treturn 'that did not go through — try again';\n}\n\n/** How long an upload may take before it is given up on. Long enough for a\n *  few megabytes on a slow phone connection; short enough that a stalled one\n *  does not leave the control dead for the rest of the session, which is what\n *  an untimed fetch did. */\nexport const UPLOAD_TIMEOUT_MS = 60_000;\n\n/** An abort signal that fires after `ms`. AbortSignal.timeout where it exists;\n *  a controller where it does not — it arrived in Safari 16, and the phones\n *  that lack it are the ones on the slowest connections. */\nexport function timeoutSignal(ms: number): AbortSignal {\n\tif (typeof AbortSignal !== 'undefined' && 'timeout' in AbortSignal)\n\t\treturn AbortSignal.timeout(ms);\n\tconst c = new AbortController();\n\tsetTimeout(() => c.abort(), ms);\n\treturn c.signal;\n}\n","/** Whether this browser can play her live stream (live.ts) — on its own, so\n *  the studio page's first script can ask it (state.svelte.ts) without\n *  carrying the stream itself: the page is pornpilot.app's search landing,\n *  and none of a session's code belongs in what it loads first. */\n\nconst PROBE_TYPE = 'video/mp4; codecs=\"avc1.4d401f,mp4a.40.2\"';\n\nexport type MSClass = { new (): MediaSource; isTypeSupported(type: string): boolean };\n\n/** MediaSource, or Safari's ManagedMediaSource (the one iPhones have, from\n *  iOS 17.1), when it takes h264 + aac. */\nexport function mediaSourceClass(): MSClass | null {\n\tif (typeof window === 'undefined') return null;\n\tconst w = window as unknown as { ManagedMediaSource?: MSClass; MediaSource?: MSClass };\n\tif (w.ManagedMediaSource?.isTypeSupported(PROBE_TYPE)) return w.ManagedMediaSource;\n\tif (w.MediaSource?.isTypeSupported(PROBE_TYPE)) return w.MediaSource;\n\treturn null;\n}\n\n/** Whether this browser can play her live (MediaSource or ManagedMediaSource,\n *  h264 + aac, websockets). */\nexport function liveSupported(): boolean {\n\treturn !!mediaSourceClass() && typeof WebSocket !== 'undefined';\n}\n","/** The page's side of guided sessions: which door is open (clip or session),\n *  what the setup sheet is set to, which session is playing, and the lists the\n *  sidebar and the sheet show. One shared object so the studio page, the\n *  sidebar and the session components agree without threading props through\n *  a fifteen-thousand-line page. */\nimport { browser } from '$app/environment';\nimport { trackVoiceSession } from '$lib/analytics';\nimport { liveSupported } from './support';\nimport {\n\tshouldOnboard,\n\ttype HerView,\n\ttype Scenario,\n\ttype SessionSetup,\n\ttype SessionSummary\n} from './shared';\n\nexport type Surface = 'clip' | 'session';\n\nexport interface SheetOpen {\n\tscenario: Scenario;\n\ther?: string;\n\tminutes?: 10 | 20;\n\ttones?: string[];\n\trequest?: string;\n\t/** Where it was opened from, for the funnel. */\n\tfrom: 'scenario' | 'ready' | 'composer' | 'chip' | 'link';\n}\n\nconst SURFACE_KEY = 'pp-surface';\nconst SETUP_KEY = 'pp-session-setup';\nconst ONBOARDED_KEY = 'pp-onboarded';\n\nfunction read<T>(key: string, fallback: T): T {\n\tif (!browser) return fallback;\n\ttry {\n\t\tconst v = localStorage.getItem(key);\n\t\treturn v ? (JSON.parse(v) as T) : fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction write(key: string, value: unknown) {\n\ttry {\n\t\tlocalStorage.setItem(key, JSON.stringify(value));\n\t} catch {\n\t\t// private mode or full: only a convenience is lost\n\t}\n}\n\nexport const vs = $state({\n\tsurface: read<Surface>(SURFACE_KEY, 'clip'),\n\tsheet: null as SheetOpen | null,\n\t/** The session open in the player, and whether it was just made (plays at once). */\n\tplaying: null as { id: string; fresh: boolean } | null,\n\thers: [] as HerView[],\n\tsessions: [] as SessionSummary[],\n\ttokens: null as number | null,\n\t/** What the last setup was, so the composer's chips say it and the sheet opens on it. */\n\tlast: read<Partial<SessionSetup> & { her?: string; scenario?: Scenario }>(SETUP_KEY, {}),\n\tonboarding: false,\n\ttoast: '' as string\n});\n\nexport function setSurface(s: Surface, from = 'switch') {\n\tif (vs.surface === s) return;\n\tvs.surface = s;\n\twrite(SURFACE_KEY, s);\n\ttrackVoiceSession('mode', { surface: s, from });\n\tif (s === 'session') warmGpu();\n}\n\nexport function rememberSetup(v: typeof vs.last) {\n\tvs.last = v;\n\twrite(SETUP_KEY, v);\n}\n\n/** Her GPU takes twenty seconds to over a minute to start cold, so it is\n *  started before it is needed: when the session side of the studio is opened,\n *  and again every minute while a setup is being chosen — a started GPU stays\n *  up two minutes, so this keeps one ready right up to the press. Five minutes\n *  at most: a setup left open is not a reason to keep paying for a GPU. The\n *  server warms only for someone who can pay for a session, and the GPU\n *  service starts at most one new GPU every 45 s however many pages ask. */\nlet warmedAt = 0;\nlet keepWarm: ReturnType<typeof setInterval> | undefined;\nconst WARM_EVERY_MS = 55_000;\nconst WARM_FOR_MS = 5 * 60_000;\n\nexport function warmGpu() {\n\tif (!browser || vs.last.video === false || Date.now() - warmedAt < WARM_EVERY_MS - 5_000) return;\n\twarmedAt = Date.now();\n\tvoid fetch('/studio/api/session/warm', {\n\t\tmethod: 'POST',\n\t\theaders: { 'content-type': 'application/json' },\n\t\tbody: JSON.stringify({ live: liveSupported() })\n\t}).catch(() => {});\n}\n\nexport function openSheet(o: SheetOpen) {\n\tvs.sheet = o;\n\ttrackVoiceSession('setup_opened', { scenario: o.scenario, from: o.from });\n\tvoid loadHers();\n\tvoid loadTokens();\n\twarmGpu();\n\tclearInterval(keepWarm);\n\tconst opened = Date.now();\n\tkeepWarm = setInterval(() => {\n\t\tif (!vs.sheet || Date.now() - opened > WARM_FOR_MS) clearInterval(keepWarm);\n\t\telse warmGpu();\n\t}, WARM_EVERY_MS);\n}\n\nlet toastTimer: ReturnType<typeof setTimeout> | undefined;\nexport function toast(msg: string) {\n\tvs.toast = msg;\n\tclearTimeout(toastTimer);\n\ttoastTimer = setTimeout(() => (vs.toast = ''), 2600);\n}\n\n/** The first visit sees the chooser; nobody sees it twice. */\n/** The first visit sees the chooser; nobody sees it twice — and some first\n *  visits never do (shared.ts, shouldOnboard). */\nexport function maybeOnboard(arrival: { referrer: string; search: string }) {\n\tif (!browser) return;\n\tconst show = shouldOnboard({\n\t\tseen: read<boolean>(ONBOARDED_KEY, false),\n\t\tuserAgent: navigator.userAgent,\n\t\t...arrival\n\t});\n\tif (show) vs.onboarding = true;\n}\n\nexport function onboarded() {\n\tvs.onboarding = false;\n\twrite(ONBOARDED_KEY, true);\n}\n\nexport async function loadSessions() {\n\ttry {\n\t\tconst r = (await (await fetch('/studio/api/session')).json()) as {\n\t\t\tok?: boolean;\n\t\t\tsessions?: SessionSummary[];\n\t\t};\n\t\tif (r.ok && r.sessions) vs.sessions = r.sessions;\n\t} catch {\n\t\t// the sidebar keeps what it had\n\t}\n}\n\nlet hersAt = 0;\nexport async function loadHers(force = false) {\n\tif (!force && Date.now() - hersAt < 30_000 && vs.hers.length) return;\n\thersAt = Date.now();\n\ttry {\n\t\tconst r = (await (await fetch('/studio/api/session/her')).json()) as {\n\t\t\tok?: boolean;\n\t\t\thers?: HerView[];\n\t\t};\n\t\tif (r.ok && r.hers) vs.hers = r.hers;\n\t} catch {\n\t\thersAt = 0;\n\t}\n}\n\nexport async function loadTokens() {\n\ttry {\n\t\tconst r = (await (await fetch('/studio/api/tokens')).json()) as {\n\t\t\tok?: boolean;\n\t\t\ttokens?: number;\n\t\t};\n\t\tif (r.ok && typeof r.tokens === 'number') vs.tokens = r.tokens;\n\t} catch {\n\t\t// the line under the button just leaves the balance out\n\t}\n}\n\n// ─── media unlock ───────────────────────────────────────────────────────────\n//\n// iOS Safari plays sound only from a media element that has been started inside\n// a tap. A session starts a minute after its tap, and her answers arrive whenever\n// they arrive, so every element the player will ever use is started — silently —\n// inside the tap that opens it, and stays allowed after that.\n\n// eslint-disable-next-line svelte/prefer-svelte-reactivity -- elements, not state\nconst media = new Set<HTMLMediaElement>();\nconst SILENCE = '/studio/session/silence.mp3';\n\nexport function registerMedia(el: HTMLMediaElement) {\n\tmedia.add(el);\n\treturn () => media.delete(el);\n}\n\n/** Call synchronously inside the click handler, before any await. */\nexport function unlockMedia() {\n\tfor (const el of media) {\n\t\tif (el.dataset.unlocked) continue;\n\t\ttry {\n\t\t\tif (!el.src) el.src = SILENCE;\n\t\t\tel.muted = false;\n\t\t\t// Started and stopped inside the tap: that is what allows it, and\n\t\t\t// nothing is left playing. Leaving it to play and pausing later let a\n\t\t\t// stretch loaded in the meantime play on as a second voice (2026-10-31).\n\t\t\tvoid el.play()?.catch(() => {});\n\t\t\tel.pause();\n\t\t\tel.dataset.unlocked = '1';\n\t\t} catch {\n\t\t\t// an element that cannot be started now will ask for a tap later\n\t\t}\n\t}\n}\n\nexport function openPlayer(id: string, fresh: boolean) {\n\tunlockMedia();\n\tvs.playing = { id, fresh };\n}\n\nexport function closePlayer() {\n\tvs.playing = null;\n\tvoid loadSessions();\n}\n","<!-- The full-screen parts of a guided session — the setup sheet, the player and\n     the first-visit chooser — mounted once at the root of the studio page so\n     they cover everything. The Session surface itself (SessionSurface) sits in\n     the main column instead.\n\n     Loaded after the page is up, in the browser only. The studio is\n     pornpilot.app's front page and its search landing (RAT-632): its HTML and\n     its first script stay what they were, with none of a session's markup or\n     code in them. The player still exists well before anyone can press Start,\n     which is all its sound needs (state.svelte.ts, unlockMedia). -->\n<script lang=\"ts\">\n\timport { onMount, type Component } from 'svelte';\n\timport { maybeOnboard } from './state.svelte';\n\n\tlet {\n\t\tonNeedTokens,\n\t\tonboarding = true\n\t}: { onNeedTokens: (need: number) => void; onboarding?: boolean } = $props();\n\n\ttype WithTokens = Component<{ onNeedTokens: (need: number) => void }>;\n\tlet parts = $state<{ Sheet: WithTokens; Player: WithTokens; Chooser: Component } | null>(null);\n\n\tonMount(() => {\n\t\t// How the visitor arrived, read now: the page takes ?prompt= out of the\n\t\t// address as soon as it has used it, which is before the chooser loads.\n\t\tconst arrival = { referrer: document.referrer, search: location.search };\n\t\tvoid Promise.all([\n\t\t\timport('./SessionSheet.svelte'),\n\t\t\timport('./SessionPlayer.svelte'),\n\t\t\timport('./SessionOnboarding.svelte')\n\t\t]).then(([sheet, player, chooser]) => {\n\t\t\tparts = { Sheet: sheet.default, Player: player.default, Chooser: chooser.default };\n\t\t\tif (onboarding) maybeOnboard(arrival);\n\t\t});\n\t});\n</script>\n\n{#if parts}\n\t<parts.Sheet {onNeedTokens} />\n\t<parts.Player {onNeedTokens} />\n\t<parts.Chooser />\n{/if}\n","<!-- Clip | Session, above the composer. Two doors to the same studio: the\n     switch only changes what the page shows; nothing is lost going back. -->\n<script lang=\"ts\">\n\timport { setSurface, vs } from './state.svelte';\n</script>\n\n<div class=\"modes\" class:s={vs.surface === 'session'} role=\"tablist\" aria-label=\"What to make\">\n\t<span class=\"thumb\" aria-hidden=\"true\"></span>\n\t<button\n\t\ttype=\"button\"\n\t\trole=\"tab\"\n\t\tclass=\"mode\"\n\t\tclass:on={vs.surface === 'clip'}\n\t\taria-selected={vs.surface === 'clip'}\n\t\tonclick={() => setSurface('clip')}>Clip</button\n\t>\n\t<button\n\t\ttype=\"button\"\n\t\trole=\"tab\"\n\t\tclass=\"mode\"\n\t\tclass:on={vs.surface === 'session'}\n\t\taria-selected={vs.surface === 'session'}\n\t\tonclick={() => setSurface('session')}>Session <span class=\"pill-new\">New</span></button\n\t>\n</div>\n\n<style>\n\t.modes {\n\t\tposition: relative;\n\t\tdisplay: inline-grid;\n\t\tgrid-template-columns: 1fr 1fr;\n\t\tbackground: #1c1c1e;\n\t\tborder-radius: 999px;\n\t\tpadding: 3px;\n\t\tborder: 1px solid rgba(255, 255, 255, 0.08);\n\t\tflex: none;\n\t}\n\t.mode {\n\t\tposition: relative;\n\t\tz-index: 1;\n\t\tpadding: 6px 18px;\n\t\tborder-radius: 999px;\n\t\tfont-weight: 600;\n\t\tfont-size: 14px;\n\t\tcolor: #a1a1a6;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 7px;\n\t\tjustify-content: center;\n\t\ttransition: color 0.2s;\n\t\tcursor: pointer;\n\t\twhite-space: nowrap;\n\t}\n\t.mode.on {\n\t\tcolor: #000;\n\t}\n\t.pill-new {\n\t\tfont-size: 10px;\n\t\tletter-spacing: 0;\n\t\tbackground: #f5f5f7;\n\t\tcolor: #000;\n\t\tborder-radius: 6px;\n\t\tpadding: 1px 6px;\n\t\tfont-weight: 600;\n\t}\n\t.mode.on .pill-new {\n\t\tbackground: #000;\n\t\tcolor: #fff;\n\t}\n\t.thumb {\n\t\tposition: absolute;\n\t\ttop: 3px;\n\t\tbottom: 3px;\n\t\tleft: 3px;\n\t\twidth: calc(50% - 3px);\n\t\tborder-radius: 999px;\n\t\tbackground: #f5f5f7;\n\t\ttransition: transform 0.35s cubic-bezier(0.32, 0.72, 0, 1);\n\t}\n\t.modes.s .thumb {\n\t\ttransform: translateX(100%);\n\t}\n\t.mode:focus-visible {\n\t\toutline: 2px solid #0a84ff;\n\t\toutline-offset: 2px;\n\t}\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.thumb {\n\t\t\ttransition: none;\n\t\t}\n\t}\n</style>\n","<!-- Your sessions, at the top of the studio's sidebar. Sessions are kept: one\n     tap reopens where it was left, or its ending if it ended. -->\n<script lang=\"ts\">\n\timport { onMount } from 'svelte';\n\timport { SCENARIOS } from './shared';\n\timport { loadSessions, openPlayer, vs } from './state.svelte';\n\n\tlet { onOpen, signedIn = true }: { onOpen?: () => void; signedIn?: boolean } = $props();\n\n\tonMount(() => {\n\t\t// A visitor who is not signed in has no sessions; asking would only 401.\n\t\tif (signedIn) void loadSessions();\n\t});\n</script>\n\n{#if vs.sessions.length}\n\t<p\n\t\tclass=\"flex items-center gap-2 px-3 pt-4 pb-1.5 font-mono text-[0.7rem] font-medium tracking-[0.13em] text-[var(--st-faint)] uppercase\"\n\t>\n\t\tYour sessions <span class=\"pill-new\">New</span>\n\t</p>\n\t{#each vs.sessions.slice(0, 8) as s (s.id)}\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tonclick={() => {\n\t\t\t\topenPlayer(s.id, false);\n\t\t\t\tonOpen?.();\n\t\t\t}}\n\t\t\tclass=\"flex w-full cursor-pointer items-center justify-between gap-2 rounded-xl px-3 py-2 text-left text-sm transition-colors hover:bg-[var(--st-surface)] {vs\n\t\t\t\t.playing?.id === s.id\n\t\t\t\t? 'bg-[var(--st-surface)]'\n\t\t\t\t: ''}\"\n\t\t>\n\t\t\t<!-- Her session's own name: two \"Timed JOI · Vera\" rows were indistinguishable. -->\n\t\t\t<span class=\"min-w-0 truncate\" title=\"{SCENARIOS[s.scenario]?.name ?? 'Session'} · {s.her}\"\n\t\t\t\t>{s.title || `${SCENARIOS[s.scenario]?.name ?? 'Session'} · ${s.her}`}</span\n\t\t\t>\n\t\t\t<span class=\"shrink-0 text-xs text-[var(--st-faint)] tabular-nums\">{s.minutes} min</span>\n\t\t</button>\n\t{/each}\n{/if}\n\n<style>\n\t.pill-new {\n\t\tfont-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', Inter, sans-serif;\n\t\tletter-spacing: 0;\n\t\ttext-transform: none;\n\t\tfont-size: 10.5px;\n\t\tbackground: #f5f5f7;\n\t\tcolor: #000;\n\t\tborder-radius: 6px;\n\t\tpadding: 1px 6px;\n\t\tfont-weight: 600;\n\t}\n</style>\n","<script lang=\"ts\">\n\t/*  Two workspace-id patterns, hoisted to the very top of the script.\n\t *\n\t *  They used to sit among the $derived declarations, and one of the deriveds\n\t *  reads WS_SUFFIX — which is a temporal dead zone waiting to happen, because\n\t *  Svelte evaluates the reactive graph by dependency rather than by source\n\t *  order. It happened: every load threw \"WS_SUFFIX is not defined\" from inside\n\t *  the runtime, and the derived that throws is the one that names the slug a\n\t *  conversation is saved under. That is why runs kept coming back with\n\t *  \"this conversation was not saved\".\n\t *\n\t *  Plain constants at the top of the file cannot be caught out that way.\n\t *  ONE_CLIP_WS (imported from $lib/studio/types, so just as safe) has to agree\n\t *  with it about `cont`. */\n\tconst WS_SUFFIX = /-(shoot|direct|cont)$/;\n\n\t/** Studio — chat-first surface for the local auteur harness (~/auteur).\n\t *\n\t *  The whole production happens in one growing transcript, like a chat with a\n\t *  studio. The user types an idea; the plan comes back as a chat message; each\n\t *  planning document (screenplay, cast, scenes, art direction, visual bible)\n\t *  lands in the chat for approval or revision; only after the user approves\n\t *  does GPU rendering start. A right-hand rail mirrors the full pipeline —\n\t *  including steps that have not started yet — the way the harness's own TUI\n\t *  does.\n\t *\n\t *  Workspaces are immutable and unpausable, so the approval gate cannot live\n\t *  in the harness. The production is therefore split in two:\n\t *\n\t *    planning workspace  `${slug}@1.0`        — LLM-only, ~4 minutes, no GPU\n\t *    render workspace    `${slug}-shoot@1.0`  — opened only after approval,\n\t *                                               carries the approved documents\n\t *                                               inline and renders on Modal\n\t *\n\t *  Per-document revision goes through reset-task on the planning workspace.\n\t *  Downstream planning documents do NOT re-run on their own, so this page\n\t *  orchestrates a chain reset: wait for the upstream artifact to come back,\n\t *  then reset the next planning task in dependency order, and so on.\n\t *\n\t *  Same-origin routes back it:\n\t *    /studio/api/plan    — idea (or prior plan + feedback) -> Brief\n\t *    /studio/api/launch  — {stage:'planning'|'render'} -> workspace\n\t *    /studio/api/harness        — POST proxy (poll-state, chat, reset-task…)\n\t *    /studio/api/file           — raw artifact bytes, same-origin (the harness\n\t *                          itself sends no CORS headers)\n\t */\n\t// Aliased: this file already has a tick(id) of its own for the poll loop.\n\timport { onMount, tick as flush, untrack } from 'svelte';\n\timport { trackStudioClipViewed } from '$lib/analytics';\n\timport { friendly, parseEventLog, type ActivityRow } from '$lib/studio/activity';\n\timport { expectedClipMs, recordWait, typicalWait, typicalLabel } from '$lib/studio/timings';\n\timport { renderDocument, type Block } from '$lib/studio/render-doc';\n\timport {\n\t\tDEFAULT_VOICE,\n\t\tONE_CLIP_WS,\n\t\trunCeilingFor,\n\t\tSCENE_COUNT_MAX,\n\t\tSCENE_COUNT_MIN,\n\t\ttype Artifact,\n\t\ttype ArtifactFile,\n\t\ttype Brief,\n\t\ttype ChatItem,\n\t\ttype LaunchResult,\n\t\ttype PollState,\n\t\ttype ProxyResult,\n\t\ttype StoredSheet,\n\t\ttype Task\n\t} from '$lib/studio/types';\n\timport { BASE, CATALOGUE, MAX_PICKS, loraFor, type Pick } from '$lib/studio/loras';\n\timport { NOT_ENOUGH_TOKENS, priceLabel, tokensForSend } from '$lib/studio/pricing';\n\timport { STUDIO_TITLE, studioFaqSchema } from '$lib/studio-seo';\n\timport { jsonLdScript } from '$lib/seo';\n\timport StudioAbout from '$lib/components/studio/StudioAbout.svelte';\n\timport { UPLOAD_BUDGET_BYTES, isPicture, shrinkForUpload } from '$lib/studio/shrink';\n\timport {\n\t\tUPLOAD_TIMEOUT_MS,\n\t\treadReply,\n\t\ttimeoutSignal,\n\t\twhyFailed,\n\t\ttype Reply\n\t} from '$lib/studio/reply';\n\timport { page } from '$app/state';\n\timport { replaceState } from '$app/navigation';\n\timport { openAuthSheet } from '$lib/stores/authSheet';\n\timport { STUDIO_PAUSED } from '$lib/studio/pause';\n\t/** The host's shop. The one ratemyd component this surface reaches for, and\n\t *  deliberately: a person who runs out mid-shot should meet the same packages\n\t *  and the same checkout they would anywhere else in the app, not a second\n\t *  till built into the studio. Everything else about the purse crosses at\n\t *  `host.ts`; this is a window, not a wire. */\n\timport BuyTokensModal from '$lib/components/BuyTokensModal.svelte';\n\t// Guided sessions (Epic Q): the second door, beside Clip.\n\timport Sessions from '$lib/studio/session/Sessions.svelte';\n\timport ModeSwitch from '$lib/studio/session/ModeSwitch.svelte';\n\timport SessionList from '$lib/studio/session/SessionList.svelte';\n\timport {\n\t\tvs as sessionState,\n\t\tsetSurface as setSessionSurface,\n\t\topenSheet as openSessionSheet\n\t} from '$lib/studio/session/state.svelte';\n\timport { READY as READY_SESSIONS } from '$lib/studio/session/shared';\n\n\t/** Polling cadence, unchanged from the previous surface: the harness author\n\t *  asked not to hammer the status endpoints. 15s while things move, 30s once\n\t *  three consecutive polls came back identical, no polling once terminal. */\n\tconst POLL_FAST_MS = 15_000;\n\tconst POLL_SLOW_MS = 30_000;\n\tconst QUIET_CYCLES = 3;\n\t/** The cadence while the answer is expected, and the share of the typical\n\t *  wait it starts at. A render moves nothing for minutes, so `quiet` always\n\t *  reaches its cap and the loop settles on POLL_SLOW_MS — which means a clip\n\t *  that has finished can sit unnoticed for a full thirty seconds.\n\t *\n\t *  Half, not three quarters. The last dozen renders in the harness log ran\n\t *  246, 254, 285, 305, 395, 426, 426, 436, 458 and 686 seconds — a median\n\t *  around 410, with the quickest finishing at 0.60 of it. A window opening at\n\t *  0.7 would therefore miss exactly the fast runs, which are the ones where a\n\t *  thirty-second tail is the largest share of the wait.\n\t *\n\t *  Eight seconds rather than five for what it costs: five would shave another\n\t *  1.5s off the average lag and ask half again as many times. See pollDelay\n\t *  for the bound at the other end. */\n\tconst POLL_CLOSING_MS = 8_000;\n\tconst CLOSING_FROM = 0.5;\n\n\t// Status vocabularies differ per entity: tasks end on `success`, artifacts on\n\t// `approved`, and the harness also says `completed` in places. Failure is\n\t// `permanently-failed` for tasks, `rejected` for artifacts.\n\tconst DONE = ['success', 'completed', 'approved'];\n\tconst DEAD = ['permanently-failed', 'failed', 'rejected'];\n\n\t/** The five planning steps in dependency order. Each pairs the task that\n\t *  writes it with the artifact it registers, the ApprovedDocs field the\n\t *  render launch carries it under, and the name a person reads.\n\t *  The chain reset walks this list top to bottom. */\n\tconst PLANNING_STEPS = [\n\t\t{ task: 'write_screenplay', artifact: 'screenplay', doc: 'screenplay', label: 'Screenplay' },\n\t\t{ task: 'character_table', artifact: 'character_table', doc: 'characterTable', label: 'Cast' },\n\t\t{ task: 'create_scenes', artifact: 'scene_list', doc: 'sceneList', label: 'Scenes' },\n\t\t{\n\t\t\ttask: 'write_art_direction',\n\t\t\tartifact: 'art_direction',\n\t\t\tdoc: 'artDirection',\n\t\t\tlabel: 'Art direction'\n\t\t},\n\t\t{\n\t\t\ttask: 'write_visual_bible',\n\t\t\tartifact: 'visual_bible',\n\t\t\tdoc: 'visualBible',\n\t\t\tlabel: 'Visual bible'\n\t\t}\n\t] as const;\n\n\ttype PlanningStep = (typeof PLANNING_STEPS)[number];\n\n\t/** The instruction a downstream planning task gets when an upstream document\n\t *  changed. Agent-facing, so English. */\n\tconst CONSISTENCY_INSTRUCTION =\n\t\t'An upstream planning document changed — regenerate this document so it stays consistent with the updated upstream content.';\n\n\t/** The assembly instruction that worked live — verbatim, do not \"improve\" it. */\n\tconst ASSEMBLY_MSG =\n\t\t'Combine all rendered scene clips into a single final video file, in scene order. ' +\n\t\t'Use the scene-assembler skill. Resolve each clip from the artifact index rather ' +\n\t\t'than guessing by filename. Register the result as a new artifact.';\n\n\tconst OFFLINE_TEXT =\n\t\t'The harness is not responding. Start the container: cd ~/auteur && ./run.sh';\n\n\t/** The other silence, and it needs the opposite advice. The harness answers\n\t *  fine and every other workspace answers in milliseconds; this run's agent\n\t *  has stopped talking on its own. The render behind it is very likely still\n\t *  going on the GPU, so restarting the container is the one thing that would\n\t *  actually lose work. */\n\tconst WEDGED_TEXT =\n\t\t'This run has stopped reporting — but the harness is up and the render may ' +\n\t\t'still be running on the GPU. Do not restart the container; it would lose ' +\n\t\t'the render. The clip appears here if it finishes.';\n\n\t/** Which door this session is using. Simple by default: it is the one that\n\t *  produced usable clips today, and the planning chain is a twenty-minute\n\t *  round trip to find out whether it did. Remembered across visits — a mode\n\t *  is a working habit, not a per-run choice. */\n\tlet mode = $state<'simple' | 'advanced'>('simple');\n\tconst MODE_KEY = 'auteur-studio-mode';\n\n\t/** The empty page says what the product is, in two beats, and stops. It used\n\t *  to run to three sentences of mechanics — how the prompt gets written, who\n\t *  approves what, when the GPU starts — which is a subhead's job on a page\n\t *  that has no subhead, and it changed wording with the mode, so the first\n\t *  thing you read moved when you touched a toggle.\n\t *\n\t *  \"Adult film\" fixes the medium beyond argument; \"directed by you\" is the\n\t *  one credit this tool can print, and it is what the name already means.\n\t *  Two lines, one per beat, at every width. */\n\tconst WELCOME_LINES = ['Adult film.', 'Directed by you.'];\n\tconst WELCOME_TEXT = WELCOME_LINES.join(' ');\n\n\t/** The greeting is a property of the empty page, not a message in the\n\t *  conversation — so switching modes rewrites it where it stands. Pushing a\n\t *  fresh one each time stacked a paragraph per switch, and flipping twice to\n\t *  compare the two modes left four of them. */\n\tlet welcomeId = $state('');\n\n\tfunction showWelcome() {\n\t\t// Still a chat item so reset() and the transcript machinery keep working,\n\t\t// but the template gives it the page's own treatment rather than a\n\t\t// paragraph's — see the welcomeId branch in the transcript.\n\t\tif (welcomeId && chat.find((c) => c.id === welcomeId)) return;\n\t\twelcomeId = pushStudio(WELCOME_TEXT).id;\n\t}\n\n\t/** Seed pitches for the audience this plugs into: adult creators making promo\n\t *  and teaser content for their own profiles. They set the register in one\n\t *  glance — confident, sensual, character-led — which a blank input never\n\t *  does, and they steer the model away from the children's-story default it\n\t *  otherwise falls into. */\n\tconst EXAMPLES = [\n\t\t'a latino man jerking off over a naked blonde lying in front of him',\n\t\t'two guys alone in the gym showers, one jerking the other off',\n\t\t\"two women in their twenties in a pool at night, one licking the other's breasts\"\n\t];\n\n\t// One set for both modes. The simple-mode trio named the act outright, which\n\t// put hardcore copy in the chrome — the first thing on screen for anyone who\n\t// walks past the machine. These steer the register just as well and the\n\t// prompt box accepts exactly the same input.\n\tconst examples = EXAMPLES;\n\n\t/** Survives an accidental reload mid-run. Chat items are rebuilt from poll\n\t *  state on resume; only the run identity is persisted. */\n\tconst RESUME_KEY = 'auteur-studio-chat-v2';\n\n\tconst SCENE_CHOICES = Array.from(\n\t\t{ length: SCENE_COUNT_MAX - SCENE_COUNT_MIN + 1 },\n\t\t(_, i) => SCENE_COUNT_MIN + i\n\t);\n\n\t/** Tasks whose key/title says they combine clips belong to the Final cut\n\t *  rail entry, not the shoot list. */\n\tconst ASSEMBLE_RE = /assemb|combin|final|stitch|concat/i;\n\n\t// --- transcript ----------------------------------------------------------\n\n\tlet chat = $state<ChatItem[]>([]);\n\t/** Items replaced by a newer version collapse to one line. */\n\tlet superseded = $state<Record<string, boolean>>({});\n\tlet idSeq = 0;\n\tfunction mkId(): string {\n\t\tidSeq += 1;\n\t\treturn `i${Date.now().toString(36)}-${idSeq}`;\n\t}\n\tfunction pushItem(partial: Omit<ChatItem, 'id' | 'at'>): ChatItem {\n\t\tconst item: ChatItem = { ...partial, id: mkId(), at: Date.now() };\n\t\tchat.push(item);\n\t\t// The pushed element, not the object that was pushed. `chat` is $state, so\n\t\t// writing into it stores a proxy — and the local `item` is the unproxied\n\t\t// original. Returning that made every mutation through the returned\n\t\t// reference invisible: the character preview updated its own card's url\n\t\t// when the picture arrived, nothing re-rendered, and the card sat empty\n\t\t// while the file was on disk. Callers that re-find by id were unaffected,\n\t\t// which is why only one path ever showed it.\n\t\treturn chat[chat.length - 1];\n\t}\n\tfunction pushStudio(text: string): ChatItem {\n\t\treturn pushItem({ who: 'studio', kind: 'text', text });\n\t}\n\tfunction pushError(text: string): ChatItem {\n\t\treturn pushItem({ who: 'studio', kind: 'error', text });\n\t}\n\n\t// --- the production ------------------------------------------------------\n\n\t/** The current plan. Refinement replaces it wholesale; edits patch it. */\n\tlet brief = $state<Brief | null>(null);\n\t/** The user's first idea, kept as context for plan revisions. */\n\tlet originalPitch = $state('');\n\t/** The Brief the planning workspace was actually opened with — its slug may\n\t *  carry a retry suffix, and the render workspace derives from it. */\n\tlet launchedBrief = $state<Brief | null>(null);\n\tlet latestPlanId = $state('');\n\tlet sceneCount = $state(4);\n\n\tlet planningWs = $state('');\n\tlet renderWs = $state('');\n\tconst activeWs = $derived(renderWs || planningWs);\n\n\t/** Whether the live render workspace is a simple-mode one. Read off the id\n\t *  rather than carried as a flag, because a flag has to be set on every road\n\t *  in and one of them was missed: reopening a past run from the sidebar\n\t *  restored the workspace without it, the single clip task counted as a\n\t *  finished shoot, and the page asked a workspace with no assembler to\n\t *  assemble — which it answered, at length, in the transcript.\n\t *\n\t *  The id cannot be missed. composeDirectWorkspace names every direct\n\t *  workspace `<slug>-direct`, so this is true however we arrived. */\n\t/** One task, one clip — as opposed to a planned shoot with several scenes and\n\t *  a final cut.\n\t *\n\t *  Two id shapes qualify, and forgetting the second cost a delivery: a\n\t *  continuation opens `<slug>-cont@v`, not `<slug>-direct@v`. Tested in one\n\t *  place because it is asked in five, and a run that answers \"no\" here is\n\t *  offered a planning rail it has no documents for and an assembly step it has\n\t *  nothing to assemble. */\n\t/** Every suffix a render workspace id can carry. It lives beside ONE_CLIP_WS\n\t *  because the two have to agree, and they had stopped: `cont` was added there\n\t *  and not to the slug derivation below, so a continuation filed its\n\t *  conversation under `cont-xxx-cont` while the sidebar looked it up as\n\t *  `cont-xxx`. Reopening one could never find what it had just saved. */\n\tconst simpleRun = $derived(ONE_CLIP_WS.test(renderWs));\n\n\t/** Whether the live render is a sheet rather than a clip. Read off the id for\n\t *  exactly the reason simpleRun is: composeSheetWorkspace names every one of\n\t *  them `<slug>-sheet`, so no road in can forget to set it. It decides what a\n\t *  finished artifact becomes — a clip card or a sheet card — and it keeps a\n\t *  sheet run out of the shoot bookkeeping, which counts clips. */\n\tconst sheetRun = $derived(/-sheet@/.test(renderWs));\n\n\t/** The slug this run is filed under, the same one the history sidebar shows.\n\t *  Derived rather than stored: the planning workspace is `<slug>@v` and the\n\t *  render one `<slug>-shoot@v` or `<slug>-direct@v`, so the ids already carry\n\t *  it and cannot disagree with a copy. */\n\t/** The working session, once one has been started.\n\t *\n\t *  A session is what a person does in one sitting: describe a character, keep\n\t *  it, shoot a clip with it, continue the clip. The sidebar files a row per\n\t *  slug, and before this the slug came off the workspace id — so each of those\n\t *  four steps opened its own row, and the first minute of a session, before\n\t *  any render existed, had no row at all. */\n\tlet sessionSlug = $state('');\n\n\tconst runSlug = $derived(\n\t\tsessionSlug ||\n\t\t\t(planningWs\n\t\t\t\t? planningWs.split('@')[0]\n\t\t\t\t: renderWs\n\t\t\t\t\t? renderWs.split('@')[0].replace(WS_SUFFIX, '')\n\t\t\t\t\t: '')\n\t);\n\n\t/** Open a row the moment work starts, rather than when a GPU does.\n\t *\n\t *  Called from every path a session can begin on. It is idempotent: a session\n\t *  that already has a slug keeps it, which is the whole point — the row is the\n\t *  sitting, not the render. */\n\tasync function startSession(title: string): Promise<void> {\n\t\tif (sessionSlug) return;\n\t\tconst slug = `s-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;\n\t\tsessionSlug = slug;\n\t\t// A visitor has no account to file it under yet. It is kept here and\n\t\t// announced once they sign up — see recordGuestSession.\n\t\tif (guest) {\n\t\t\ttry {\n\t\t\t\tlocalStorage.setItem(GUEST_SESSION_KEY, JSON.stringify({ slug, title }));\n\t\t\t} catch {\n\t\t\t\t/* private mode: the session appears when its first render lands */\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tawait announceSession(slug, title);\n\t}\n\n\t/** File a session under the signed-in account. */\n\tasync function announceSession(slug: string, title: string): Promise<void> {\n\t\ttry {\n\t\t\tconst r = await fetch('/studio/api/history', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tslug,\n\t\t\t\t\ttitle: title.slice(0, 80) || 'New session',\n\t\t\t\t\tpitch: title.slice(0, 200)\n\t\t\t\t})\n\t\t\t});\n\t\t\tconst d = (await r.json()) as { productions?: Production[] };\n\t\t\tif (d.productions) history = d.productions;\n\t\t} catch {\n\t\t\t// The row is a convenience. A session that could not announce itself\n\t\t\t// still works; it simply appears when its first render lands.\n\t\t}\n\t}\n\n\t/** One saved conversation per run, plus a pointer at the live one.\n\t *  Before this there was a single slot, so opening an older run from the\n\t *  sidebar could only rebuild a guess of it — a synthetic brief, an advanced\n\t *  plan card, and the planning rail sitting in `waiting` over a simple run\n\t *  that never had a plan. The conversation itself is the state; keep it. */\n\tconst runKey = (slug: string) => `auteur-studio-run-${slug}`;\n\tconst POINTER_KEY = 'auteur-studio-current';\n\n\tlet startedAt = $state(0);\n\tlet now = $state(Date.now());\n\n\t/** Last good poll per workspace — kept separately so the rail can keep\n\t *  drawing the planning steps after the poll target moves to rendering. */\n\tlet planningPoll = $state<PollState | null>(null);\n\tlet renderPoll = $state<PollState | null>(null);\n\n\tlet offline = $state(false);\n\tlet lastError = $state('');\n\tlet lastTick = $state<Date | null>(null);\n\tlet pollingActive = $state(false);\n\n\t/** A restored run the harness cannot still be working on.\n\t *\n\t *  `startedAt` outlives the tab, so a run left behind by a closed laptop —\n\t *  or simply reopened from the sidebar a week later — comes back with a\n\t *  timestamp and a poller and no way of its own to tell that it is over. The\n\t *  clock then counts up from a moment that is days gone, and the composer\n\t *  says a production is in progress that nothing has touched since Tuesday.\n\t *  Of everything this surface can get wrong, claiming to be doing work it is\n\t *  not is the one that costs the reader their trust in the rest of it.\n\t *\n\t *  So an old run is restored as what it is: the record of a run, whole and\n\t *  readable, with nothing on it pretending to be live. See runCeilingFor for\n\t *  where the line sits and why it sits there.\n\t */\n\tlet staleRun = $state(false);\n\n\t// --- planning documents ----------------------------------------------------\n\n\t/** Per planning artifact key: undefined/waiting -> posted -> (regen -> posted).\n\t *  `regen` means a reset was issued and the current artifact content is stale. */\n\tlet docPhase = $state<Record<string, 'posted' | 'regen'>>({});\n\t/** [ok] is a UI state only — the harness already moved on. */\n\tlet docAccepted = $state<Record<string, boolean>>({});\n\t/** artifact key -> latest chat item showing it, so re-posts collapse the old. */\n\tlet latestDocItem = $state<Record<string, string>>({});\n\t/** artifact key -> loaded document text (what the render launch carries). */\n\tlet docBody = $state<Record<string, string>>({});\n\t/** artifact key -> the filename it came from. The renderer needs it to tell a\n\t *  JSON visual bible from a markdown document. */\n\tlet docFile = $state<Record<string, string>>({});\n\n\t/** Where the document can be downloaded, and which task wrote it — both were\n\t *  carried on the per-document card until the board replaced it. */\n\tlet docUrl = $state<Record<string, string>>({});\n\tlet docTaskId = $state<Record<string, string>>({});\n\n\t/** Posted once, the moment planning starts, so the five steps are on screen\n\t *  before any of them has finished. */\n\tlet boardId = $state('');\n\n\t/* ── past productions ──────────────────────────────────────────────────────\n\t *  A run used to exist only in the tab that started it. The server now keeps\n\t *  a bookmark per production, and this is the way back to one.\n\t */\n\ttype Production = {\n\t\tslug: string;\n\t\ttitle: string;\n\t\tsceneCount: number;\n\t\tplanningWs?: string;\n\t\trenderWs?: string;\n\t\tstartedAt: number;\n\t\tupdatedAt: number;\n\t\tpitch?: string;\n\t\t/** Simple mode keeps the writer's prompt whole on the row. Dropped from\n\t\t *  this type until now, which is why a run with no saved conversation had\n\t\t *  nothing to show even though the text was on disk. */\n\t\tprompt?: string;\n\t};\n\tlet history = $state<Production[]>([]);\n\t/** The rail is a place you go back to, not a thing you read, so it closes.\n\t *  It used to be permanent above lg with no way to shut it, and off-canvas\n\t *  below with no way to keep it — one control now does both, and the choice\n\t *  survives a reload because it is a working habit, not a per-run decision. */\n\tlet sidebarOpen = $state(false);\n\tconst NAV_KEY = 'auteur-studio-nav';\n\n\tasync function loadHistory() {\n\t\t// A visitor has none of this: it all lives under an account.\n\t\tif (guest) return;\n\t\ttry {\n\t\t\tconst r = await fetch('/studio/api/history');\n\t\t\tif (!r.ok) return;\n\t\t\thistory = ((await r.json()) as { productions: Production[] }).productions;\n\t\t\tadoptForgottenRun();\n\t\t} catch {\n\t\t\t/* the list is a convenience; never let it break the studio */\n\t\t}\n\t}\n\n\t/** A run this browser forgot and the server remembers.\n\t *\n\t *  The server's row for a production is written during the launch request;\n\t *  the browser's snapshot is written when the page next gets round to it.\n\t *  Between the two a tab can close, and it did: reopened, the page found a\n\t *  snapshot with no workspace, showed the front page, and a clip that had\n\t *  landed and been collected sat in the library with nothing asking for it.\n\t *  When the row for the current session names a workspace and the snapshot\n\t *  does not, the row is right — take it and start asking, the same way a\n\t *  restored session does. Never over a run this tab is already watching. */\n\tfunction adoptForgottenRun() {\n\t\tif (renderWs || pollingActive || !runSlug) return;\n\t\tconst mine = history.find((p) => p.slug === runSlug);\n\t\tif (!mine?.renderWs || !ONE_CLIP_WS.test(mine.renderWs)) return;\n\t\trenderIsSheet = false;\n\t\trenderWs = mine.renderWs;\n\t\tstartedAt = mine.startedAt || startedAt || Date.now();\n\t\tmode = 'simple';\n\t\tpersist();\n\t\tstartPolling();\n\t}\n\n\t/** Put a run in the sidebar the moment it becomes one.\n\t *\n\t *  The list was fetched once, on mount, so a production you had just started\n\t *  was absent from its own history until the next reload — the one place a\n\t *  user is certain to look for it. The server row is written during the\n\t *  launch request, so by the time a slug exists here it exists there too.\n\t *\n\t *  Keyed on the slug rather than on every change: a run's row is written\n\t *  once, and re-fetching the whole list on each poll would be sixty rows a\n\t *  second for a number that did not move. */\n\tlet listedSlug = '';\n\t$effect(() => {\n\t\tconst slug = runSlug;\n\t\tif (!slug || slug === listedSlug) return;\n\t\tlistedSlug = slug;\n\t\tvoid loadHistory();\n\t});\n\n\t/** How much of a conversation a saved snapshot actually holds.\n\t *\n\t *  The greeting does not count: it is pushed on load by every session,\n\t *  including one that went on to do nothing, so a snapshot containing only\n\t *  the greeting is an empty page that happens to have a file. */\n\tfunction savedTurns(slug: string): number {\n\t\ttry {\n\t\t\tconst raw = localStorage.getItem(runKey(slug));\n\t\t\tif (!raw) return 0;\n\t\t\tconst s = JSON.parse(raw) as { chat?: { id?: string }[]; welcomeId?: string };\n\t\t\tconst chat = s.chat ?? [];\n\t\t\treturn chat.filter((c) => !s.welcomeId || c.id !== s.welcomeId).length;\n\t\t} catch {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t/** A run rebuilt from the records that outlived its conversation.\n\t *\n\t *  Productions launched before conversations were saved have no transcript,\n\t *  so their sidebar row opened onto an empty page. But the two halves worth\n\t *  reading both survived on disk: the render log keeps the request verbatim —\n\t *  untruncated, unlike the history row's sixty-character title — and the\n\t *  prompt the writer produced from it, with the adapters it chose.\n\t *\n\t *  The clip cannot come back. Its cache file is named\n\t *  sha256(workspace + artifact + file) and the artifact id was written down\n\t *  nowhere, so the mp4 is still in ~/auteur/studio-library/clips with nothing\n\t *  to say which run it belongs to. A page that silently omits the video looks\n\t *  like a page that lost it, so this says so instead.\n\t *\n\t *  The shot card is marked launched: it is a record of what ran, not an offer\n\t *  to spend a GPU running it again.\n\t */\n\tfunction rebuiltChat(p: Production): ChatItem[] | null {\n\t\tconst row = p.renderWs ? logRow[p.renderWs] : undefined;\n\t\tconst prompt = row?.prompt ?? p.prompt ?? '';\n\t\tconst request = row?.request ?? p.title ?? '';\n\t\tif (!prompt && !request) return null;\n\n\t\tconst at = p.startedAt || Date.now();\n\t\tconst out: ChatItem[] = [];\n\t\tconst add = (i: Omit<ChatItem, 'id' | 'at'>) => out.push({ ...i, id: mkId(), at });\n\n\t\tadd({\n\t\t\twho: 'studio',\n\t\t\tkind: 'text',\n\t\t\ttext: row\n\t\t\t\t? 'This conversation was not saved. Rebuilt from the render log — the request and the prompt below are exact. The clip it produced cannot be located.'\n\t\t\t\t: 'This conversation was not saved, and there is no render log for it. All that is left is the request.'\n\t\t});\n\t\tif (request) add({ who: 'user', kind: 'text', text: request });\n\t\tif (prompt) {\n\t\t\tadd({\n\t\t\t\twho: 'studio',\n\t\t\t\tkind: 'shot',\n\t\t\t\tshot: {\n\t\t\t\t\tprompt,\n\t\t\t\t\tseconds: row?.seconds ?? 0,\n\t\t\t\t\torientation: (row?.width ?? 0) >= (row?.height ?? 1) ? 'landscape' : 'portrait',\n\t\t\t\t\twhy: '',\n\t\t\t\t\tloras: row?.launched ?? [],\n\t\t\t\t\tlaunched: true,\n\t\t\t\t\t...(row?.characterId ? { characterId: row.characterId } : {}),\n\t\t\t\t\t...(row?.characterName ? { characterName: row.characterName } : {}),\n\t\t\t\t\t...(row?.locationId ? { locationId: row.locationId } : {}),\n\t\t\t\t\t...(row?.locationName ? { locationName: row.locationName } : {})\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\tif (row?.outcome) {\n\t\t\tadd({ who: 'studio', kind: 'text', text: `You marked this one ${row.outcome}.` });\n\t\t}\n\t\treturn out;\n\t}\n\n\t/** Reopening writes the resume payload and reloads.\n\t *\n\t *  Deliberately not a soft in-place swap: restoring a run means rebuilding\n\t *  the transcript, the poller, the document phases and the revision chain\n\t *  from scratch, and there is already one tested path that does all of that —\n\t *  the one that runs on load. Reusing it is worth the reload. */\n\tfunction reopen(p: Production) {\n\t\tif (runSlug === p.slug) {\n\t\t\t// Already the run on screen. On a phone the rail covers the page, so\n\t\t\t// the only useful thing left to do is get out of the way.\n\t\t\tif (window.innerWidth < 1024) setNavOpen(false);\n\t\t\treturn;\n\t\t}\n\n\t\t// Read what this run should show BEFORE anything is torn down: reset()\n\t\t// clears the pointer and the resume slot, and the payload for the run\n\t\t// being opened may be sitting in exactly those places.\n\t\tlet raw: string | null = null;\n\t\tlet saved = false;\n\t\ttry {\n\t\t\tsaved = savedTurns(p.slug) > 0;\n\t\t\t// The run's own conversation, if it was saved: what you typed, the\n\t\t\t// prompt or the plan you approved, the documents, the clips.\n\t\t\t//\n\t\t\t// A key is not the same as a conversation, and the two were being\n\t\t\t// treated as one. A snapshot written before the run had said anything\n\t\t\t// holds nothing but the greeting, and taking its existence as proof of\n\t\t\t// content opened the row onto a blank page — with the rebuild below\n\t\t\t// skipped, because the key was there. So ask what is in it.\n\t\t\tif (saved) raw = localStorage.getItem(runKey(p.slug));\n\t\t} catch {\n\t\t\t/* private mode — fall through to the rebuild */\n\t\t}\n\n\t\t// The local copy is the richer one — it has the transcript — but it can be\n\t\t// older than the server's row about the same run: a tab closed seconds\n\t\t// after a launch saved a snapshot with no workspace in it, and reopening\n\t\t// from this list then trusted that snapshot over a server that knew the\n\t\t// render had started. Where the row knows and the copy does not, the row\n\t\t// wins; the transcript stays.\n\t\tif (raw && p.renderWs) {\n\t\t\ttry {\n\t\t\t\tconst s = JSON.parse(raw) as { renderWs?: string; startedAt?: number };\n\t\t\t\tif (!s.renderWs) {\n\t\t\t\t\ts.renderWs = p.renderWs;\n\t\t\t\t\ts.startedAt = s.startedAt || p.startedAt;\n\t\t\t\t\traw = JSON.stringify(s);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t/* unreadable copy — the rebuild below handles it */\n\t\t\t}\n\t\t}\n\n\t\tif (!raw) {\n\t\t\t// Older runs, saved before conversations were kept. The identity is\n\t\t\t// enough to poll the workspace, and for a run past RUN_CEILING_MS not\n\t\t\t// even that happens — so without the rebuild the page would open\n\t\t\t// empty. See rebuiltChat for what can honestly be put back.\n\t\t\tconst rebuilt = rebuiltChat(p);\n\t\t\tconst asBrief = {\n\t\t\t\tslug: p.slug,\n\t\t\t\ttitle: p.title,\n\t\t\t\tstory: p.pitch ?? '',\n\t\t\t\tstyle: '',\n\t\t\t\tsceneCount: p.sceneCount,\n\t\t\t\tseed: 0\n\t\t\t};\n\t\t\traw = JSON.stringify({\n\t\t\t\t// Reopening a row puts you back in that session, not in a new one\n\t\t\t\t// that happens to show its workspace.\n\t\t\t\tsessionSlug: p.slug,\n\t\t\t\tbrief: asBrief,\n\t\t\t\tlaunchedBrief: asBrief,\n\t\t\t\tplanningWs: p.planningWs ?? '',\n\t\t\t\trenderWs: p.renderWs ?? '',\n\t\t\t\tassemblySent: false,\n\t\t\t\tstartedAt: p.startedAt,\n\t\t\t\t// A simple run never had a brief. Handing one back draws the\n\t\t\t\t// advanced plan card over a run that has no plan, with a start\n\t\t\t\t// button that would open a second workspace.\n\t\t\t\t...(ONE_CLIP_WS.test(p.renderWs ?? '') ? { brief: null, launchedBrief: null } : {}),\n\t\t\t\t...(rebuilt ? { chat: rebuilt } : {})\n\t\t\t});\n\t\t}\n\n\t\t// Swapped in place rather than through a reload. Reopening used to write a\n\t\t// pointer and set location.href, which threw away a warm page — fonts,\n\t\t// history, staged references and all — and blanked the screen, to arrive\n\t\t// at a state this tab could simply have adopted. reset() and resumeFrom()\n\t\t// between them are the whole of what that reload was for.\n\t\treset();\n\t\tif (!resumeFrom(raw)) showWelcome();\n\t\ttry {\n\t\t\tif (saved) localStorage.setItem(POINTER_KEY, p.slug);\n\t\t} catch {\n\t\t\t/* the run is on screen either way; the pointer only matters next load */\n\t\t}\n\t\tif (window.innerWidth < 1024) setNavOpen(false);\n\t}\n\n\tasync function dropFromHistory(p: Production, e: MouseEvent) {\n\t\te.stopPropagation();\n\t\tconst r = await fetch(`/studio/api/history?slug=${encodeURIComponent(p.slug)}`, {\n\t\t\tmethod: 'DELETE'\n\t\t});\n\t\tif (r.ok) history = ((await r.json()) as { productions: Production[] }).productions;\n\t}\n\n\t/** \"today\", \"yesterday\", then the date — the grouping people actually use\n\t *  when looking for something they made recently. */\n\t/** What a history row actually is. Nothing stores this, so it is read back\n\t *  from what the launch route wrote:\n\t *\n\t *    a film  — the only stage that opens a planning workspace\n\t *    a sheet — the sheet stage passes a literal title and no plan\n\t *    a clip  — everything else, which is direct mode\n\t *\n\t *  A `kind` on the record would be sturdier than a title match, and it is a\n\t *  three-line change in api/launch. Until then this reads the same facts. */\n\tconst SHEET_TITLE = /^(Character|Location) sheet$/;\n\tfunction runKind(p: Production): 'film' | 'sheet' | 'clip' {\n\t\tif (p.planningWs) return 'film';\n\t\tif (SHEET_TITLE.test(p.title)) return 'sheet';\n\t\treturn 'clip';\n\t}\n\n\t/** Runs, by the day they were last touched, with the sheets taken out.\n\t *\n\t *  Sheets were never productions: they are in Cast & sets, they carry a\n\t *  constant title so nine of them read identically, and reopening one lands\n\t *  in the legacy branch because runSlug never matches a sheet id. Listing\n\t *  them here also spent the 60-row budget on rows nobody can use. */\n\tconst historyDays = $derived.by(() => {\n\t\tconst days: { label: string; items: Production[] }[] = [];\n\t\tfor (const p of history) {\n\t\t\tif (runKind(p) === 'sheet') continue;\n\t\t\tconst label = whenLabel(p.updatedAt);\n\t\t\tconst day = days.find((d) => d.label === label);\n\t\t\tif (day) day.items.push(p);\n\t\t\telse days.push({ label, items: [p] });\n\t\t}\n\t\treturn days;\n\t});\n\n\tfunction whenLabel(ts: number): string {\n\t\tconst d = new Date(ts);\n\t\tconst today = new Date();\n\t\tconst same = (a: Date, b: Date) => a.toDateString() === b.toDateString();\n\t\tif (same(d, today)) return 'today';\n\t\tconst y = new Date(today);\n\t\ty.setDate(y.getDate() - 1);\n\t\tif (same(d, y)) return 'yesterday';\n\t\treturn d.toLocaleDateString('en-GB', { day: 'numeric', month: 'short' });\n\t}\n\n\ttype BoardState = 'waiting' | 'writing' | 'done' | 'rewriting' | 'failed';\n\n\t/** The five planning steps with the state each is actually in.\n\t *\n\t *  Derived rather than pushed, so the board is never a snapshot of a moment\n\t *  that has passed — the row for the document being written now is the same\n\t *  row that will carry it when it is done. */\n\tconst board = $derived.by(() => {\n\t\tconst tasks = planningPoll?.tasks ?? [];\n\t\treturn PLANNING_STEPS.map((step) => {\n\t\t\tconst t = tasks.find((x) => x.key === step.task);\n\t\t\tconst phase = docPhase[step.artifact];\n\t\t\tlet state: BoardState = 'waiting';\n\t\t\tif (phase === 'regen') state = 'rewriting';\n\t\t\telse if (phase === 'posted') state = 'done';\n\t\t\telse if (t && DEAD.includes(t.status)) state = 'failed';\n\t\t\telse if (t && !DONE.includes(t.status)) state = 'writing';\n\t\t\treturn {\n\t\t\t\tkey: step.artifact,\n\t\t\t\tlabel: step.label,\n\t\t\t\tstate,\n\t\t\t\tbody: docBody[step.artifact],\n\t\t\t\tfile: docFile[step.artifact],\n\t\t\t\turl: docUrl[step.artifact]\n\t\t\t};\n\t\t});\n\t});\n\n\tconst boardDone = $derived(board.filter((b) => b.state === 'done').length);\n\n\t/** The shoot, one row per scene.\n\t *\n\t *  A render is minutes of nothing — no output until the clip exists, and the\n\t *  harness reports the task as \"running\" throughout whether it is writing a\n\t *  prompt, waiting for a GPU, or stuck. So this says what can honestly be\n\t *  said: which scene, what stage it has reached, and how long it has been\n\t *  there. The elapsed number is the important one. It is what turns \"this\n\t *  feels slow\" into a judgement someone can actually make. */\n\tconst shootBoard = $derived.by(() => {\n\t\tconst tasks = (renderPoll?.tasks ?? []).filter((t) =>\n\t\t\t/shoot[_ ]?scene/i.test(t.key ?? t.title ?? '')\n\t\t);\n\t\tconst arts = renderPoll?.artifacts ?? [];\n\t\treturn tasks\n\t\t\t.map((t) => {\n\t\t\t\tconst n = sceneNo(t.key ?? '', t.title ?? '');\n\t\t\t\tconst art = arts.find(\n\t\t\t\t\t(a) => sceneNo(a.key ?? '', a.name ?? '') === n && /clip/i.test(a.key ?? '')\n\t\t\t\t);\n\t\t\t\tconst done = art?.status === 'approved';\n\t\t\t\tconst failed = DEAD.includes(t.status);\n\t\t\t\treturn {\n\t\t\t\t\tn,\n\t\t\t\t\ttitle: (t.title ?? `Scene ${n}`).replace(/^Shoot\\s+/i, ''),\n\t\t\t\t\tstate: done ? 'done' : failed ? 'failed' : 'running',\n\t\t\t\t\tretries: retryCounts.get(t.key ?? '') ?? 0\n\t\t\t\t};\n\t\t\t})\n\t\t\t.sort((a, b) => a.n - b.n);\n\t});\n\n\t/** Set when the shoot workspace opens, so a row can say how long it has been\n\t *  going. Per-scene start times are not available — the harness dispatches\n\t *  them together — so this is the honest granularity. */\n\tconst shootElapsed = $derived(startedAt ? Math.floor((now - startedAt) / 1000) : 0);\n\n\tfunction mmss(sec: number): string {\n\t\tconst m = Math.floor(sec / 60);\n\t\treturn m < 1 ? `${sec}s` : `${m}m ${String(sec % 60).padStart(2, '0')}s`;\n\t}\n\tlet approvalId = $state('');\n\n\t/** The chain reset in flight, or null. `armed` flips once the reset task was\n\t *  seen non-terminal — only then does \"success\" mean \"re-ran\", not \"still the\n\t *  old result\". `polls` is the safety valve for a re-run faster than one poll\n\t *  interval: after enough polls with the task terminal and never armed, the\n\t *  re-run is assumed missed rather than stalling the chain forever. */\n\tlet chain = $state<{\n\t\ttaskKey: string;\n\t\tdownstream: string[];\n\t\tarmed: boolean;\n\t\tpolls: number;\n\t} | null>(null);\n\n\t// --- rendering -------------------------------------------------------------\n\n\tlet shootsAnnounced = $state(false);\n\tlet assemblySent = $state(false);\n\tlet finalPosted = $state(false);\n\tlet renderLaunching = $state(false);\n\n\t/* Stopping a run.\n\t *\n\t *  Two-step, because it is not undoable: a workspace id can be opened once,\n\t *  so a stopped production cannot be resumed — it can only be started again\n\t *  from the plan, under a fresh slug. A single misplaced click should not\n\t *  cost that.\n\t */\n\tlet stopArmed = $state(false);\n\tlet stopping = $state(false);\n\n\t/** Where a clip is on its way to the GPU, so a stop knows what it can do.\n\t *\n\t *  A clip can be stopped for real only before it is charged: while its brief is\n\t *  being written, or while its launch is on the way. Past the charge it is paid\n\t *  for and nothing recalls it — see stop.server.ts for what was measured. The\n\t *  stop used to try anyway, and on the hosted harness that threw away a clip\n\t *  the GPU went on to render and bill. */\n\tlet writing = $state(false);\n\tlet launchingSlug = '';\n\tlet stopWanted = false;\n\t/** What the last stop did, said where the simple studio can show it. The\n\t *  transcript line pushStudio writes is never drawn on the stage, so without\n\t *  this a stop that worked said nothing at all. */\n\tlet stopNotice = $state('');\n\n\t/** The press the server is carrying for the session on screen, while it is on\n\t *  its way — see generate.server.ts. `genSlug` is what a stop names; empty\n\t *  when nothing is on its way. */\n\tlet genSlug = $state('');\n\tlet genFor = '';\n\tlet genState: 'writing' | 'launching' = 'writing';\n\tlet genTimer: ReturnType<typeof setTimeout> | null = null;\n\n\tconst STOPPED_BEFORE_CHARGE = 'Stopped before it started — nothing was charged.';\n\tconst ALREADY_STARTED =\n\t\t'It had already started, and a render that has started cannot be called back. ' +\n\t\t'It is paid for, so it will still arrive here when it is done.';\n\n\t/** A stop pressed during a launch, resolved once the launch has answered.\n\t *  `ended` means the clip is not coming, so the stage stops waiting; when it\n\t *  is coming, the stage goes on waiting for it and the line says why. */\n\tfunction settleStop(line: string, ended: boolean) {\n\t\tstopWanted = false;\n\t\tstopping = false;\n\t\tstopArmed = false;\n\t\tif (ended) endClipWait();\n\t\tif (line) {\n\t\t\tpushStudio(line);\n\t\t\tstopNotice = line;\n\t\t}\n\t\tpersist();\n\t}\n\n\t/** The slot lets go of a one-clip run that died. Its error stays on the stage,\n\t *  and the retry there sends the same brief again. */\n\tfunction endDeadClip() {\n\t\tendClipWait();\n\t\trenderWs = '';\n\t\tstartedAt = 0;\n\t\tpersist();\n\t}\n\n\t/** The stage stops waiting, for a clip that is not going to arrive. */\n\tfunction endClipWait() {\n\t\tstageStartedAt = 0;\n\t\tstageWaitFrom = '';\n\t\tstageWaitBlurUrl = '';\n\t\tsending = false;\n\t}\n\n\tasync function stopRun() {\n\t\tif (stopping) return;\n\t\t// A press the server is carrying (see generateOnServer). The stop is\n\t\t// recorded against the slug the press was given; the server looks for it\n\t\t// after the brief and again immediately before the charge. While the brief\n\t\t// is still being written that is a real stop and it can be said at once.\n\t\t// Once the launch is under way the answer is the server's, and the watcher\n\t\t// says which way it went when it lands.\n\t\tif (genSlug && genFor === sessionSlug) {\n\t\t\tstopping = true;\n\t\t\tstopWanted = true;\n\t\t\tawait fetch('/studio/api/stop', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({ pendingSlug: genSlug })\n\t\t\t}).catch(() => {});\n\t\t\tif (genState === 'writing') {\n\t\t\t\tendClipWait();\n\t\t\t\tpushStudio(STOPPED_BEFORE_CHARGE);\n\t\t\t\tstopNotice = STOPPED_BEFORE_CHARGE;\n\t\t\t\tstopArmed = false;\n\t\t\t\tstopping = false;\n\t\t\t\tpersist();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// A clip still being written: nothing is charged yet, so this is a real\n\t\t// stop and it can be said at once. The writer's answer is ignored when it\n\t\t// comes back. This branch did not exist — with no workspace id and no brief\n\t\t// the function returned without doing anything, while the button stayed\n\t\t// on screen, and people pressed it again: 16 rage clicks from 12 people.\n\t\tif (!planningWs && !renderWs && writing && !launchingSlug) {\n\t\t\tstopWanted = true;\n\t\t\tendClipWait();\n\t\t\tpushStudio(STOPPED_BEFORE_CHARGE);\n\t\t\tstopNotice = STOPPED_BEFORE_CHARGE;\n\t\t\tpersist();\n\t\t\tstopArmed = false;\n\t\t\treturn;\n\t\t}\n\t\t// A clip whose launch is on its way: whether it was charged is decided on\n\t\t// the server, a moment from now. The stop is recorded where the launch\n\t\t// checks immediately before charging, and the launch's answer says which\n\t\t// way it went — see launchShot.\n\t\tif (!planningWs && !renderWs && launchingSlug) {\n\t\t\tstopping = true;\n\t\t\tstopWanted = true;\n\t\t\tawait fetch('/studio/api/stop', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({ pendingSlug: launchingSlug })\n\t\t\t}).catch(() => {});\n\t\t\treturn;\n\t\t}\n\t\t// A clip already open: it cannot be recalled, and tearing it down only\n\t\t// throws the paid result away. Say so, and keep watching for it.\n\t\tif (!planningWs && renderWs) {\n\t\t\tpushStudio(ALREADY_STARTED);\n\t\t\tstopNotice = ALREADY_STARTED;\n\t\t\tpersist();\n\t\t\tstopArmed = false;\n\t\t\treturn;\n\t\t}\n\t\tconst b = launchedBrief ?? brief;\n\t\tconst ids = [planningWs, renderWs].filter((w): w is string => !!w);\n\t\t// A clip run has no brief, and used to have no way out.\n\t\t//\n\t\t// This required a slug, which only a full production has — so on the stage\n\t\t// the only stop was the studio's own api/stop called by hand, and the page\n\t\t// went on counting over a render that had already been torn down. A run\n\t\t// with a live workspace id is stoppable whether or not anything wrote a\n\t\t// plan for it.\n\t\tif ((!b?.slug && !ids.length) || stopping) return;\n\t\tstopping = true;\n\t\ttry {\n\t\t\tconst res = await fetch('/studio/api/stop', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\t// Ids when the run has them, the slug only as the planning path's\n\t\t\t\t// fallback — and by here one of the two exists.\n\t\t\t\tbody: JSON.stringify(ids.length ? { workspaces: ids } : { slug: b!.slug })\n\t\t\t});\n\t\t\tconst d = (await res.json()) as { ok: boolean; removed?: number; torndown?: boolean };\n\n\t\t\t// Stop polling before saying anything: a tick that lands after the\n\t\t\t// message would re-report the run as alive.\n\t\t\trunId += 1;\n\t\t\tpollingActive = false;\n\t\t\t// And the stage, which counts on its own clock. Left set, the loader\n\t\t\t// carried on over a torn-down run — which is the state this whole\n\t\t\t// button exists to leave.\n\t\t\tstageStartedAt = 0;\n\t\t\tstageWaitFrom = '';\n\t\t\tstageWaitBlurUrl = '';\n\t\t\tsending = false;\n\t\t\trenderWs = '';\n\t\t\tstartedAt = 0;\n\n\t\t\tpushStudio(\n\t\t\t\td.ok\n\t\t\t\t\t? `Stopped. ${d.torndown ? 'The compute is released' : 'Compute was already idle'}` +\n\t\t\t\t\t\t\t`${d.removed ? ` and ${d.removed} queued ${d.removed === 1 ? 'task was' : 'tasks were'} removed` : ''}. ` +\n\t\t\t\t\t\t\t`This production cannot be resumed — start a new one when you are ready.`\n\t\t\t\t\t: 'Nothing was left running to stop.'\n\t\t\t);\n\t\t\tpersist();\n\t\t} catch {\n\t\t\tpushError('the stop did not go through — try it again');\n\t\t} finally {\n\t\t\tstopping = false;\n\t\t\tstopArmed = false;\n\t\t}\n\t}\n\n\t// --- composer ----------------------------------------------------------------\n\n\tlet input = $state('');\n\tlet sending = $state(false);\n\tlet composer = $state<HTMLTextAreaElement | null>(null);\n\n\t/* ── the harness's own account of itself ──────────────────────────────────\n\t *  Its event log carries the things that matter most and show up nowhere\n\t *  else: a task sent back by a quality gate, an agent that could not start,\n\t *  the crew's own analysis of a failure. Without this the studio shows\n\t *  \"running\" while the harness quietly retries the same rejection, which is\n\t *  indistinguishable from slow work and has cost this project whole\n\t *  afternoons.\n\t */\n\tlet seenActivity = $state(new Set<string>());\n\n\t/** How many times each task has been sent back, and which ones we have\n\t *  already warned about.\n\t *\n\t *  The harness retries a failed task indefinitely, including failures that\n\t *  cannot succeed on a second attempt — a CUDA kernel mismatch, a model that\n\t *  refuses the content. Each retry of a render is a GPU call you pay for.\n\t *  One afternoon of that cost real money before anyone noticed, because from\n\t *  outside a retry loop looks exactly like slow work. */\n\tconst RETRY_ALARM = 3;\n\tlet retryCounts = $state(new Map<string, number>());\n\tlet retryWarned = new Set<string>();\n\n\tasync function pollActivity(target: string) {\n\t\tconst r = await call('get-event-log', {}, target);\n\t\tif (!r.ok) return;\n\t\tfor (const row of parseEventLog(r.data)) {\n\t\t\tif (seenActivity.has(row.id)) continue;\n\t\t\tseenActivity.add(row.id);\n\n\t\t\t// The board says all of this, in one place, without three lines per\n\t\t\t// document. What survives is trouble — a rejection or a failure has\n\t\t\t// nowhere else to appear, and is the whole reason this feed exists.\n\t\t\tconst evKey = row.id.split('|')[2] ?? '';\n\t\t\tconst routine = row.tone === 'step' || row.tone === 'good';\n\t\t\tconst planning = PLANNING_STEPS.some((st) => st.task === evKey || st.artifact === evKey);\n\t\t\tif (!(routine && planning)) {\n\t\t\t\tpushItem({ who: 'studio', kind: 'activity', activity: row });\n\t\t\t}\n\n\t\t\t// A retry is identified by the task it belongs to, which is the part\n\t\t\t// of the row id before the first bar. Counting rows rather than\n\t\t\t// parsing the event again keeps this on one source of truth.\n\t\t\tif (row.tone !== 'warn' && row.tone !== 'bad') continue;\n\t\t\tconst key = row.id.split('|')[2] || row.id;\n\t\t\tif (!key) continue;\n\t\t\tconst n = (retryCounts.get(key) ?? 0) + 1;\n\t\t\tretryCounts.set(key, n);\n\t\t\tif (n >= RETRY_ALARM && !retryWarned.has(key)) {\n\t\t\t\tretryWarned.add(key);\n\t\t\t\t// No \"see the details above\" on the stage. There is nothing above: the\n\t\t\t\t// stage replaces the transcript, so the rows carrying the harness's\n\t\t\t\t// own words are not drawn anywhere the reader can reach. Sending\n\t\t\t\t// somebody to look at a thing that is not there is worse than saying\n\t\t\t\t// less — it reads as their failure to find it.\n\t\t\t\t//\n\t\t\t\t// What IS on the stage is the stop, under the loader, and that is the\n\t\t\t\t// only decision this message exists to prompt.\n\t\t\t\tpushError(\n\t\t\t\t\tSTAGE_UI\n\t\t\t\t\t\t? `This attempt has failed ${n} times and the harness keeps starting it again. ` +\n\t\t\t\t\t\t\t\t`When the cause does not change — a prompt it will not take, a workflow that ` +\n\t\t\t\t\t\t\t\t`cannot run — another attempt cannot help, and each one is paid for in GPU ` +\n\t\t\t\t\t\t\t\t`time. Stop is under the picture while a render is going.`\n\t\t\t\t\t\t: `This has failed ${n} times in a row and the harness will keep trying. ` +\n\t\t\t\t\t\t\t\t`If the cause is the same every time — a refused prompt, a broken workflow — ` +\n\t\t\t\t\t\t\t\t`retrying cannot fix it, and every render attempt costs GPU time. ` +\n\t\t\t\t\t\t\t\t`Open the details above to see what it actually said, and stop the run if it reads final.`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/* ── reference files ───────────────────────────────────────────────────────\n\t *  Faces, rooms, movements you want the render to copy. They are staged on\n\t *  the server until the plan is approved, because there is nowhere to put\n\t *  them before that — the harness keeps artifacts inside a workspace, and\n\t *  the one that needs them is the render workspace, which does not exist\n\t *  yet. Launching the render consumes them, which is why the list empties\n\t *  itself at that point rather than lingering into the next film.\n\t */\n\ttype RefFile = { id: string; name: string; description: string; size: number };\n\tlet refFiles = $state<RefFile[]>([]);\n\tlet refBusy = $state(false);\n\tlet refError = $state('');\n\tlet refDragging = $state(false);\n\n\tasync function loadRefFiles() {\n\t\tif (guest) return;\n\t\ttry {\n\t\t\tconst r = await fetch('/studio/api/refs');\n\t\t\tif (!r.ok) return;\n\t\t\trefFiles = ((await r.json()) as { files: RefFile[] }).files;\n\t\t} catch {\n\t\t\t/* the staging area is optional; never let it break the composer */\n\t\t}\n\t}\n\n\t/** Attach pictures as references, one request per picture.\n\t *\n\t *  Each picture is shrunk on the device first — see shrink.ts for why — and\n\t *  sent on its own. They all used to go in one body, so three photos that\n\t *  were each fine crossed the host's 4.5 MB limit together. Failures are\n\t *  collected per file and said in words; the raw exception text that used to\n\t *  land here (`SyntaxError: Unexpected token 'R'…`) can no longer reach the\n\t *  screen, and a stalled upload times out instead of leaving the paperclip\n\t *  dead for the rest of the session. */\n\tasync function attachRefs(list: FileList | null) {\n\t\tif (!list?.length || refBusy) return;\n\t\trefBusy = true;\n\t\trefError = '';\n\t\tconst problems: string[] = [];\n\t\ttry {\n\t\t\tfor (const picked of Array.from(list)) {\n\t\t\t\tif (!isPicture(picked)) {\n\t\t\t\t\tproblems.push(`${picked.name}: only pictures can be attached for now`);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst f = await shrinkForUpload(picked);\n\t\t\t\tif (f.size > UPLOAD_BUDGET_BYTES) {\n\t\t\t\t\tproblems.push(`${picked.name}: too large to send, even after shrinking it`);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst fd = new FormData();\n\t\t\t\tfd.append('file', f);\n\t\t\t\tfd.append('description', '');\n\t\t\t\tlet r: Reply<{ ok: boolean; error?: string; files?: RefFile[] }>;\n\t\t\t\ttry {\n\t\t\t\t\tconst res = await fetch('/studio/api/refs', {\n\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\tbody: fd,\n\t\t\t\t\t\tsignal: timeoutSignal(UPLOAD_TIMEOUT_MS)\n\t\t\t\t\t});\n\t\t\t\t\tr = await readReply(res);\n\t\t\t\t} catch (e) {\n\t\t\t\t\tr = { ok: false, error: whyFailed(e) };\n\t\t\t\t}\n\t\t\t\tif (!r.ok) {\n\t\t\t\t\tproblems.push(`${picked.name}: ${r.error}`);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (r.body.files) refFiles = r.body.files;\n\t\t\t\tif (!r.body.ok) problems.push(`${picked.name}: ${r.body.error ?? 'could not attach that'}`);\n\t\t\t}\n\t\t} finally {\n\t\t\trefBusy = false;\n\t\t\trefError = problems.join(' · ');\n\t\t}\n\t}\n\n\tasync function dropRef(id: string) {\n\t\tconst r = await fetch(`/studio/api/refs?id=${encodeURIComponent(id)}`, { method: 'DELETE' });\n\t\tconst d = (await r.json()) as { files?: RefFile[] };\n\t\tif (d.files) refFiles = d.files;\n\t}\n\n\t/** Keep a picture you already have as a character or a location.\n\t *\n\t *  Same paperclip, different meaning, decided by the mode the composer is\n\t *  already in: attaching in clip mode stages a reference for that one render,\n\t *  attaching in character mode makes the picture a character you keep. No\n\t *  second control for a second meaning — the band above the box already says\n\t *  which question is being answered.\n\t *\n\t *  One file, not the list: a character is one person. Clip mode still takes as\n\t *  many as you like.\n\t *\n\t *  There is no render here and no GPU. The picture is the character, and a\n\t *  clip that uses it stages it exactly as it stages a drawn one.\n\t */\n\t/** A picked photograph, waiting for you to press send.\n\t *\n\t *  It used to upload the instant it was chosen, which put the description on\n\t *  the wrong side of the click: attach first and then start typing, and the\n\t *  character was already made — named after the file, with nothing for the\n\t *  turnaround to work from. Holding it here means one flow either way, and the\n\t *  text in the box at send time is the text that counts. */\n\tlet pendingPhoto = $state<File | null>(null);\n\n\tfunction holdPhoto(list: FileList | null) {\n\t\tconst file = list?.[0];\n\t\tif (!file) return;\n\t\tpendingPhoto = file;\n\t\tcomposer?.focus();\n\t}\n\n\tasync function uploadSubject(file: File | null, said: string) {\n\t\tif (!file || refBusy) return;\n\t\tconst kind: 'character' | 'location' = wantTarget === 'location' ? 'location' : 'character';\n\t\trefBusy = true;\n\t\trefError = '';\n\t\ttry {\n\t\t\t// Shrunk on the device first, for the same reason attachRefs does: this\n\t\t\t// posts through the same 4.5 MB function limit.\n\t\t\tconst small = isPicture(file) ? await shrinkForUpload(file) : file;\n\t\t\tif (!isPicture(file) || small.size > UPLOAD_BUDGET_BYTES) {\n\t\t\t\tpushError(\n\t\t\t\t\tisPicture(file)\n\t\t\t\t\t\t? 'That picture is too large to send, even after shrinking it.'\n\t\t\t\t\t\t: 'Only pictures can be kept as a character or a location.'\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst fd = new FormData();\n\t\t\tfd.append('file', small);\n\t\t\tfd.append('kind', kind);\n\t\t\t// Passed in, not read from the box.\n\t\t\t//\n\t\t\t// submit() clears the composer before it dispatches, so reading `input`\n\t\t\t// here found an empty string every time — three uploads went out\n\t\t\t// described only by their filename because of it, and each one looked\n\t\t\t// like the operator had forgotten to type.\n\t\t\tfd.append('description', said.trim());\n\t\t\t// Which conversation this belongs to. The turnaround behind it runs for\n\t\t\t// minutes server-side, and without this the card announcing it was posted\n\t\t\t// into whatever chat happened to be open when the poll caught it.\n\t\t\tif (runSlug) fd.append('sessionSlug', runSlug);\n\t\t\tlet reply: Reply<{\n\t\t\t\tok?: boolean;\n\t\t\t\tsheet?: StoredSheet;\n\t\t\t\tsheets?: StoredSheet[];\n\t\t\t\terror?: string;\n\t\t\t}>;\n\t\t\ttry {\n\t\t\t\tconst res = await fetch('/studio/api/sheet', {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\tbody: fd,\n\t\t\t\t\tsignal: timeoutSignal(UPLOAD_TIMEOUT_MS)\n\t\t\t\t});\n\t\t\t\treply = await readReply(res);\n\t\t\t} catch (e) {\n\t\t\t\treply = { ok: false, error: whyFailed(e) };\n\t\t\t}\n\t\t\tif (!reply.ok) {\n\t\t\t\tpushError(reply.error);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst r = reply.body;\n\t\t\tif (!r.ok || !r.sheet) {\n\t\t\t\tpushError(r.error || 'That picture could not be kept.');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (r.sheets) sheets = r.sheets;\n\t\t\t// Start watching. The turnaround runs server-side, so nothing else in\n\t\t\t// this tab will ever notice it finish — and without this the cards it\n\t\t\t// posts on that transition simply never arrive.\n\t\t\twatchSheets();\n\t\t\tpushItem({\n\t\t\t\twho: 'studio',\n\t\t\t\tkind: 'sheet',\n\t\t\t\tsheet: {\n\t\t\t\t\tkind,\n\t\t\t\t\tstage: 'anchor',\n\t\t\t\t\tuploaded: true,\n\t\t\t\t\tid: r.sheet.id,\n\t\t\t\t\tname: r.sheet.name,\n\t\t\t\t\tdescription: r.sheet.description,\n\t\t\t\t\turl: `/studio/api/sheet/img/${r.sheet.id}`,\n\t\t\t\t\t// Saved the moment it arrived — there is nothing to approve about a\n\t\t\t\t\t// picture you chose yourself, and nothing to launch.\n\t\t\t\t\tlaunched: true\n\t\t\t\t}\n\t\t\t});\n\t\t\t// Said out loud now, unlike the first version of this. A render starts\n\t\t\t// here — minutes of GPU — and someone who does not know that reads the\n\t\t\t// pause as the app having done nothing.\n\t\t\tif (kind === 'character') {\n\t\t\t\tpushStudio(\n\t\t\t\t\t'Building the six views behind this — a short turnaround render of the photograph, ' +\n\t\t\t\t\t\t'which is what the longer videos use to keep the same person across shots.'\n\t\t\t\t);\n\t\t\t}\n\t\t\tinput = '';\n\t\t\tpendingPhoto = null;\n\t\t\t// The generator stays open, and `back to clips` is how you leave it.\n\t\t\t//\n\t\t\t// This went back to clips on its own, and that one line undid the whole\n\t\t\t// arrangement the stage now depends on: the mode is what decides whether\n\t\t\t// the stage is showing a subject or the round, so putting it back to\n\t\t\t// 'clip' the instant the picture was kept handed the stage straight back\n\t\t\t// to a read-back nobody had pressed. The loader ran, the character\n\t\t\t// landed, and the plan card returned over the top of it — a character\n\t\t\t// made and thrown away, as far as anyone watching could tell.\n\t\t\t//\n\t\t\t// The typed path never did this, which is why only the uploaded one\n\t\t\t// broke. Both leave the same way now.\n\t\t\tcurrentCharacter = null;\n\t\t\tpersist();\n\t\t} catch (e) {\n\t\t\tpushError(whyFailed(e));\n\t\t} finally {\n\t\t\trefBusy = false;\n\t\t}\n\t}\n\n\tasync function describeRefFile(id: string, description: string) {\n\t\tawait fetch('/studio/api/refs', {\n\t\t\tmethod: 'PATCH',\n\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\tbody: JSON.stringify({ id, description })\n\t\t});\n\t}\n\n\t// --- plan editing (only the latest plan, only before launch) -----------------\n\n\tlet editingPlan = $state(false);\n\tlet editTitle = $state('');\n\tlet editStory = $state('');\n\tlet editStyle = $state('');\n\n\t// --- per-item UI state ---------------------------------------------------------\n\n\tlet expanded = $state<Record<string, boolean>>({});\n\tlet changeOpen = $state<Record<string, boolean>>({});\n\tlet changeText = $state<Record<string, string>>({});\n\tlet changeBusy = $state<Record<string, boolean>>({});\n\n\t// --- rail ---------------------------------------------------------------------\n\n\tlet railOpen = $state(false); // mobile toggle\n\tlet showDetails = $state(false);\n\n\t// --- one-shot bookkeeping, deliberately non-reactive ---------------------------\n\t// Written from the poll loop (imperative code), read nowhere in the template.\n\t/* eslint-disable svelte/prefer-svelte-reactivity */\n\tconst clipPosted = new Set<string>(); // artifact ids already shown as clips\n\tconst failedNoted = new Set<string>(); // task ids already reported as failed\n\tconst preAssemblyIds = new Set<string>(); // artifacts that existed before assembly\n\t/* eslint-enable svelte/prefer-svelte-reactivity */\n\t/** After a resume, preAssemblyIds is empty — \"new artifact since assembly\"\n\t *  can no longer be told apart from a scene clip, so the final film is\n\t *  recognised by name alone until this run observes an assembly itself. */\n\tlet finalByNameOnly = false;\n\tlet errorNoted = false; // one error chat item per error episode\n\tlet offlineNoted = false;\n\tlet planningLaunchAttempts = 0;\n\tlet renderLaunchAttempts = 0;\n\n\t// Poll loop bookkeeping — plain locals, nothing here belongs on screen.\n\tlet timer: ReturnType<typeof setTimeout> | null = null;\n\t/** Epoch, bumped by every stop. A tick already awaiting its fetch when the\n\t *  loop is stopped or retargeted checks this and exits instead of scheduling\n\t *  a second loop against a harness whose author asked us not to hammer it. */\n\tlet runId = 0;\n\tlet quiet = 0;\n\tlet lastSig = '';\n\tlet sawAllDone = false;\n\n\t// --- proxy ------------------------------------------------------------------\n\n\tasync function call(op: string, body: unknown = {}, ws: string = activeWs) {\n\t\tconst res = await fetch('/studio/api/harness', {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\tbody: JSON.stringify({ workspace: ws, op, body })\n\t\t});\n\t\tif (!res.ok) throw new Error(`proxy ${res.status}`);\n\t\treturn (await res.json()) as ProxyResult;\n\t}\n\n\t// --- files -------------------------------------------------------------------\n\n\tfunction fileKeyOf(f: ArtifactFile): string {\n\t\tif (typeof f === 'string') return f;\n\t\tfor (const k of ['key', 'name', 'filename', 'file']) {\n\t\t\tconst v = f[k];\n\t\t\tif (typeof v === 'string' && v) return v;\n\t\t}\n\t\treturn '';\n\t}\n\n\tfunction kindOf(name: string): 'image' | 'video' | 'text' | 'other' {\n\t\tconst n = name.toLowerCase();\n\t\tif (/\\.(png|jpe?g|webp|gif|avif)$/.test(n)) return 'image';\n\t\tif (/\\.(mp4|webm|mov|m4v)$/.test(n)) return 'video';\n\t\tif (/\\.(md|markdown|txt|json|ya?ml)$/.test(n)) return 'text';\n\t\treturn 'other';\n\t}\n\n\t/** Clip filenames are NOT uniform (`scene1_clip.mp4` next to `scene3.mp4`) —\n\t *  ordering reads the first number found anywhere in the artifact's identity.\n\t *  No number means \"sort last\", never \"scene zero\". */\n\tfunction sceneNo(...parts: string[]): number {\n\t\tconst m = parts.join(' ').match(/\\d+/);\n\t\treturn m ? Number(m[0]) : 999;\n\t}\n\n\tfunction firstFileOfKind(a: Artifact, kind: 'text' | 'video' | 'image'): string {\n\t\tfor (const f of a.files ?? []) {\n\t\t\tconst name = fileKeyOf(f);\n\t\t\tif (name && kindOf(name) === kind) return name;\n\t\t}\n\t\treturn '';\n\t}\n\n\t/** Same-origin file route — the browser cannot read the harness directly\n\t *  (no CORS on it), so everything renderable goes through this proxy. */\n\tfunction fileUrl(ws: string, artifactId: string, fileKey: string, bust = false): string {\n\t\tconst q = new URLSearchParams({ workspace: ws, artifact: artifactId, file: fileKey });\n\t\tif (bust) q.set('t', String(Date.now()));\n\t\treturn `/studio/api/file?${q.toString()}`;\n\t}\n\n\t/** The same url, asked to land on a frame.\n\t *\n\t *  A <video> shows its first decoded frame, and iOS Safari decodes nothing\n\t *  until something plays. `preload=\"metadata\"` fetches the duration and\n\t *  stops, so a tile that never plays is a black rectangle — which is what\n\t *  every thumbnail here has always been on a phone, the clock in the corner\n\t *  reading 0:05 over the black to prove the file was fine all along.\n\t *\n\t *  Playing them is not the fix, because permission to play is exactly what\n\t *  gets withdrawn: Low Power Mode blocks muted autoplay outright, and a\n\t *  picture that vanishes when the battery is low is not a picture. A media\n\t *  fragment makes a frame the destination instead — the element seeks there\n\t *  on load and paints it, with or without permission to play, and the tiles\n\t *  that do autoplay still autoplay.\n\t *\n\t *  A tenth of a second rather than zero: on some encodes nothing is\n\t *  decodable at the very first timestamp and the seek lands on nothing. */\n\tfunction still(url: string): string {\n\t\treturn `${url}#t=0.1`;\n\t}\n\n\t/** Take a copy of a clip on the server, now, while the workspace agent is\n\t *  still alive to serve it.\n\t *\n\t *  Every clip URL goes through the harness, and the harness needs a living\n\t *  workspace agent to resolve an artifact. The agent reliably dies at the\n\t *  assembly step — so without this, finishing the shoot and failing the\n\t *  assembly leaves every rendered clip unplayable, which is the wrong way\n\t *  round: the clips are the expensive part and they were already finished.\n\t *\n\t *  Fire-and-forget on purpose. A failed copy costs a clip its safety net; a\n\t *  copy that blocked the poll loop would cost the run its progress display. */\n\tfunction keepClip(ws: string, artifactId: string, fileKey: string): void {\n\t\tconst q = new URLSearchParams({\n\t\t\tworkspace: ws,\n\t\t\tartifact: artifactId,\n\t\t\tfile: fileKey,\n\t\t\twarm: '1'\n\t\t});\n\t\tvoid fetch(`/studio/api/file?${q.toString()}`).catch(() => {});\n\t}\n\n\t/** Give a video element that failed a second chance.\n\t *\n\t *  A clip's src is set the moment the clip is posted, which is also the moment\n\t *  its local copy starts downloading — so the first request can still land on\n\t *  the harness, and the harness is exactly what is unreliable at the end of a\n\t *  run. An element that loses that race is stuck: a <video> that has errored\n\t *  never retries, so the card sits black at 0:00 and pressing play does\n\t *  nothing, even after the local copy has finished and would serve instantly.\n\t *\n\t *  The retry costs nothing when the first load worked, which is most of them.\n\t *  The changing query is only there to stop the browser reusing its own cached\n\t *  failure — the file route ignores it. */\n\tconst videoAttempts = new WeakMap<HTMLVideoElement, number>();\n\n\tfunction recoverVideo(el: HTMLVideoElement, url: string): void {\n\t\tconst n = videoAttempts.get(el) ?? 0;\n\t\tif (n >= 4) return;\n\t\tvideoAttempts.set(el, n + 1);\n\t\t// Backing off: the copy is several megabytes, so the first retry can be\n\t\t// too early. Measured against a cold cache the recovery landed on the third\n\t\t// try, which is uncomfortably close to the end — four reach ~15s, wide\n\t\t// enough for the largest clip a run has produced.\n\t\tsetTimeout(\n\t\t\t() => {\n\t\t\t\tel.src = `${url}${url.includes('?') ? '&' : '?'}retry=${n + 1}`;\n\t\t\t\tel.load();\n\t\t\t},\n\t\t\t1500 * (n + 1)\n\t\t);\n\t}\n\n\t/** Reads a planning document as text. Cache-busted: after a chain reset the\n\t *  same artifact id carries new bytes. Returns null on failure — the chat\n\t *  item then degrades to a link, never an empty box. */\n\tasync function fetchDocBody(artifactId: string, fileKey: string): Promise<string | null> {\n\t\ttry {\n\t\t\tconst res = await fetch(fileUrl(planningWs, artifactId, fileKey, true));\n\t\t\tif (!res.ok) return null;\n\t\t\tconst text = (await res.text()).trim();\n\t\t\treturn text || null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t// --- composer: one input, three meanings --------------------------------------\n\n\t/** What the input does right now. Deliberately does NOT switch to a \"sending\"\n\t *  state: the transcript already shows a live line for that, and a label that\n\t *  flickers between two strings on every submit is noise on a surface whose\n\t *  whole job is to stay calm for twenty minutes. */\n\tconst composerHint = $derived.by(() => {\n\t\t// Simple mode has no plan to refine and no crew to message: every line is\n\t\t// another shot, whether it is the first or the fifth.\n\t\tif (mode === 'simple') {\n\t\t\t// Nothing above the box for a character or a location: the banner inside\n\t\t\t// it already says what this mode is for, and saying it twice in two\n\t\t\t// different wordings is how a screen stops being read at all.\n\t\t\t// Nothing at all in the clip state: the placeholder says the same thing\n\t\t\t// in the same words, five pixels lower, and one of them was always the\n\t\t\t// one nobody read.\n\t\t\treturn '';\n\t\t}\n\t\tif (!brief) return 'New film — describe the idea in one sentence';\n\t\tif (!planningWs) return 'Refining the plan — describe what to change';\n\t\tif (!renderWs) return 'Message to the planning crew lead';\n\t\treturn 'Message to the shooting crew lead';\n\t});\n\n\t/** Examples are a cure for the blank page, so they belong only on a blank\n\t *  page. They go the moment the user commits to anything — not when the plan\n\t *  comes back, which is several seconds later and leaves them sitting under\n\t *  the user's own message looking like unread options. */\n\t/** Seconds the current request has been in flight — drives the live counter\n\t *  next to the busy line. Reset on every send so it always counts this call,\n\t *  not the session. */\n\tlet sendingSince = $state(0);\n\tlet sendingFor = $state(0);\n\t/** What this wait has cost before, from this machine's own finished runs.\n\t *  Read when the wait starts rather than derived: the source is localStorage,\n\t *  which nothing can subscribe to, and the answer cannot change while a\n\t *  single request is in flight. Null until there are enough runs to mean\n\t *  something — see typicalWait. */\n\tlet typicalPrompt = $state<number | null>(null);\n\t/** What the running render should take, from the table in timings.ts. Set\n\t *  when the render starts, so it describes that render and not whatever the\n\t *  composer has been changed to since. */\n\tlet typicalClip = $state<number | null>(null);\n\t$effect(() => {\n\t\tif (!sending) {\n\t\t\tsendingFor = 0;\n\t\t\treturn;\n\t\t}\n\t\ttypicalPrompt = typicalWait('prompt');\n\t\tsendingSince = Date.now();\n\t\tconst id = setInterval(() => {\n\t\t\tsendingFor = Math.round((Date.now() - sendingSince) / 1000);\n\t\t}, 1000);\n\t\treturn () => clearInterval(id);\n\t});\n\n\t/** Half again as long as usual. Not an error — a cold model load does this\n\t *  legitimately — but it is the moment the reader starts wondering, and\n\t *  saying it first is the difference between a slow page and a broken one. */\n\tconst OVERDUE = 1.5;\n\tconst promptOverdue = $derived(!!typicalPrompt && sendingFor * 1000 > typicalPrompt * OVERDUE);\n\tconst clipOverdue = $derived(\n\t\t!!typicalClip && startedAt > 0 && now - startedAt > typicalClip * OVERDUE\n\t);\n\n\t/** Which document cards the GPU gate opens. Not persisted: the gate is a\n\t *  single decision made once, and a reopened production is past it. */\n\tlet gateOpen = $state<Record<string, boolean>>({});\n\n\t/** The gate's review list. Visual bible first and visually lifted: it is the\n\t *  document every render prompt inherits verbatim, so a mistake there is the\n\t *  one that shows up in all four clips. */\n\tconst GATE_NOTES: Record<string, string> = {\n\t\tvisual_bible: 'how every character and place will look — inherited by every shot',\n\t\tart_direction: 'the visual language of the whole film',\n\t\tscene_list: 'what happens in each scene',\n\t\tcharacter_table: 'who appears and how they are described',\n\t\tscreenplay: 'the script the scenes came from'\n\t};\n\tconst GATE_ORDER = [\n\t\t'visual_bible',\n\t\t'art_direction',\n\t\t'scene_list',\n\t\t'character_table',\n\t\t'screenplay'\n\t];\n\n\tconst summaryDocs = $derived.by(() =>\n\t\tGATE_ORDER.map((key) => {\n\t\t\tconst step = PLANNING_STEPS.find((s) => s.artifact === key);\n\t\t\treturn {\n\t\t\t\tkey,\n\t\t\t\tartifact: key,\n\t\t\t\tlabel: step?.label ?? key,\n\t\t\t\tnote: GATE_NOTES[key] ?? '',\n\t\t\t\tfile: docFile[key] ?? '',\n\t\t\t\tbody: docBody[key] ?? ''\n\t\t\t};\n\t\t}).filter((d) => d.body)\n\t);\n\n\t/** Only while the transcript holds nothing but the greeting.\n\t *\n\t *  It used to ask whether the user had said anything, which is not the same\n\t *  question: keeping a picture, a sheet card, an error or a run's activity\n\t *  all leave the transcript full while `who === 'user'` never appears, so the\n\t *  seed cards sat under the reply. And because this same flag puts\n\t *  `justify-center` on the scroll container, a transcript taller than the box\n\t *  was then centred inside it — which puts its top above scrollTop 0, where\n\t *  nothing can reach it. One wrong predicate, two bugs. */\n\tconst showExamples = $derived(!brief && !sending && chat.every((c) => c.id === welcomeId));\n\n\t/** Somebody put themselves in the shot, and there is no picture of them.\n\t *\n\t *  Three quarters of the people who answered the survey want to be in the clip\n\t *  and the prompts show them trying: \"me and a woman I just met\", \"Your\n\t *  apartment, late night\", \"Quero medir meu pau\". What the model does with\n\t *  that is invent a person — and the measurement that matters here is that\n\t *  invented content is exactly what destroys a likeness, one made-up detail at\n\t *  a time. So the clip comes back, and it is not them, and nothing said it was\n\t *  never going to be.\n\t *\n\t *  This asks. It does not block: the button underneath still says generate and\n\t *  still works, because a person who meant \"someone like me\" is not wrong and\n\t *  should not have to argue with a dialog. It is an offer with the consequence\n\t *  attached, which is the only honest shape for it — the accounts that\n\t *  attached a reference rendered something 63% of the time against 9% for\n\t *  those that did not, so this is also the moment most likely to turn a\n\t *  visitor into somebody who finishes.\n\t *\n\t *  Pronouns in the four languages the operators actually write in. A false\n\t *  positive costs one tap; a missed one costs a render and a disappointment. */\n\tconst SELF_WORDS =\n\t\t/(?:^|[^\\p{L}])(?:i|i'm|im|me|my|myself|mine|you|you're|your|yourself|én|engem|nekem|velem|rám|rajtam|te|téged|neked|veled|rád|yo|mí|conmigo|tú|ti|contigo|eu|meu|minha|comigo|você)(?:[^\\p{L}]|$)/iu;\n\n\t/** What the studio had to make up, and could ask about instead.\n\t *\n\t *  One mechanism, six shapes of the same fault: a brief that invents more than\n\t *  it was told. The measurement underneath all of them is the same — invented\n\t *  content is what destroys a likeness, one made-up detail at a time — and the\n\t *  cheapest moment to catch it is before the GPU, not after.\n\t *\n\t *    blocked   an age that cannot be rendered at all. Not a question: this one\n\t *              refuses, in the one place on this surface that is allowed to be\n\t *              red, and asks for a different shot.\n\t *    self      \"me\", \"my\" — themselves in it, with no picture of themselves.\n\t *              The answer is a reference, so the offer is to attach one.\n\t *    who       \"you\", \"your\" — somebody addressed, and nobody kept. The answer\n\t *              is not a photo, it is a name: who is this supposed to be.\n\t *    sheet     a character sheet typed into a shot box. Eight of a hundred and\n\t *              eighteen opened with `subject_definitions:`. It is long and it\n\t *              is third person, so nothing else here catches it.\n\t *    no-words  a picture arrived and nothing was typed, so the filename is the\n\t *              whole prompt.\n\t *    too-short a subject, not a shot: under four words there is no place, no\n\t *              action and no camera, so all three get made up.\n\t *\n\t *  Only on the first round, and never on a continuation — except `blocked`,\n\t *  which is not a matter of how far into a conversation anybody is. A short\n\t *  line later is a refinement (\"faster\", \"closer\"), and answering that with a\n\t *  question about what happens would be the studio failing to follow itself. */\n\tlet selfAnswered = $state<Record<string, boolean>>({});\n\t/** Pressing generate answers it: they meant what they wrote. */\n\tfunction selfAnsweredBySending(id: string) {\n\t\tselfAnswered[id] = true;\n\t}\n\n\tconst FILENAME_ONLY = /^[\\w .()-]+\\.(?:jpe?g|png|webp|heic|gif|mp4|mov|webm)$/i;\n\tconst FIRST_PERSON =\n\t\t/(?:^|[^\\p{L}])(?:i|i'm|im|me|my|myself|mine|én|engem|nekem|velem|rám|yo|mí|conmigo|eu|meu|minha|comigo)(?:[^\\p{L}]|$)/iu;\n\tconst SECOND_PERSON =\n\t\t/(?:^|[^\\p{L}])(?:you|you're|your|yourself|te|téged|neked|veled|rád|tú|ti|contigo|você|teu|tua)(?:[^\\p{L}]|$)/iu;\n\tconst SHEET_FORMAT = /^\\s*subject_definitions\\b|<Subject \\d>|<Picture \\d>/i;\n\n\t/** An age this cannot render, in any wording.\n\t *\n\t *  Deliberately narrow and deliberately deterministic. It is the front door,\n\t *  not the lock: `minors.server.ts` still runs on the input AND the output at\n\t *  both prompt endpoints, and that is what actually refuses. What this adds is\n\t *  the answer arriving before a round trip, in words, on the screen where the\n\t *  sentence was typed — \"A small teen with Open mouth\" came through the real\n\t *  studio and the operator learned nothing from what happened next. */\n\tconst UNDER_AGE =\n\t\t/(?:^|[^\\p{L}])(?:teen|teens|teenage[rd]?|schoolgirl|schoolboy|preteen|underage|minor|child|kid|loli|jailbait|kislány|kisfiú|tini|tinédzser|serdülő|niña|niño|adolescente|menina|menino)(?:[^\\p{L}]|$)|(?:^|[^\\d])(?:1[0-7])\\s*(?:years?[- ]old|yo|éves|años|anos)/iu;\n\n\t/** What a thin line is missing, named rather than asked for in general.\n\t *\n\t *  \"Say more\" is not help. What helps is the two or three things this\n\t *  particular sentence does not have — and the checks are crude on purpose,\n\t *  because a wrong suggestion costs a glance and a missing one costs a\n\t *  render. */\n\tconst HAS_PLACE =\n\t\t/\\b(room|bed|bedroom|kitchen|bathroom|shower|office|hotel|car|pool|beach|sofa|couch|desk|floor|stairs|garden|club|gym|street|window|wall|table|szoba|ágy|konyha|fürdő|zuhany|iroda|hotel|autó|medence|kanapé)\\b/i;\n\tconst HAS_CAMERA =\n\t\t/\\b(camera|close[- ]?up|pov|wide|angle|shot from|behind|over the shoulder|handheld|slow|zoom|frame|lens|kamera|közeli|hátulról)\\b/i;\n\tfunction missingBits(said: string): ('character' | 'place' | 'action' | 'camera')[] {\n\t\tconst out: ('character' | 'place' | 'action' | 'camera')[] = [];\n\t\tif (!chosenCharacter && !refFiles.length) out.push('character');\n\t\tif (!HAS_PLACE.test(said)) out.push('place');\n\t\tout.push('action');\n\t\tif (!HAS_CAMERA.test(said)) out.push('camera');\n\t\treturn out;\n\t}\n\n\t/** Press a missing part and it does the thing, rather than pointing at where\n\t *  the thing could be done.\n\t *\n\t *  A character and a place are objects the studio already keeps, so those two\n\t *  open their pickers. The other two have nowhere to go but the sentence, so\n\t *  they write into it — a short clause the operator can edit, delete or send\n\t *  as it stands. It is a starting point, not a decision: it lands in the box\n\t *  with the cursor after it, which is the one place invention is harmless,\n\t *  because nothing has been agreed and nobody has spent anything.\n\t *\n\t *  The clause is written FOR the line it is being added to. It used to be one\n\t *  of two fixed phrases — true of any clip, which is exactly what was wrong\n\t *  with them: on a shot about one thing filling the frame, \"slow at first,\n\t *  then faster\" is filler to delete before it hurts the brief. Those two stay\n\t *  on as the fallback, because a chip that does nothing when the model is slow\n\t *  is worse than a chip that writes something generic.\n\t *\n\t *  What it adds is still shape, not content: a turn, which is what a\n\t *  five-second clip needs and what almost none of the short prompts had, or a\n\t *  camera, which sixty-five of seventy first messages never mentioned and\n\t *  which the adapter takes for itself when nobody claims it. Neither invents a\n\t *  person, a place or an act — those are the operator's, and guessing them is\n\t *  the mistake this whole mechanism exists to stop. */\n\tconst FALLBACK_CLAUSE = { action: 'slow at first, then faster', camera: 'close and handheld' };\n\tlet suggesting = $state<'action' | 'camera' | null>(null);\n\n\tasync function addMissing(bit: 'character' | 'place' | 'action' | 'camera', said = '') {\n\t\tif (bit === 'character' || bit === 'place') {\n\t\t\tshutMenus();\n\t\t\tpickKind = bit === 'character' ? 'character' : 'location';\n\t\t\treturn;\n\t\t}\n\t\tif (suggesting) return;\n\n\t\t// Everything they have written for this shot, in order: the message that\n\t\t// raised the question, then whatever is in the box now — which after one tap\n\t\t// holds the previous clause, so the second one is written knowing about the\n\t\t// first instead of repeating it.\n\t\tconst basis = [said.trim(), input.trim()].filter(Boolean).join(' ').slice(0, 600);\n\t\tlet clause = FALLBACK_CLAUSE[bit];\n\t\tif (basis) {\n\t\t\tsuggesting = bit;\n\t\t\t// Never a hang: the chip is under a finger, and a clause that arrives after\n\t\t\t// the operator has given up is worse than the generic one.\n\t\t\tconst stop = AbortController ? new AbortController() : null;\n\t\t\tconst bell = setTimeout(() => stop?.abort(), 9000);\n\t\t\ttry {\n\t\t\t\tconst r = await fetch('/studio/api/suggest', {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify({ request: basis, kind: bit }),\n\t\t\t\t\tsignal: stop?.signal\n\t\t\t\t});\n\t\t\t\tconst j = r.ok ? await r.json() : null;\n\t\t\t\tif (j?.clause) clause = j.clause as string;\n\t\t\t} catch {\n\t\t\t\t/* the fixed phrase */\n\t\t\t} finally {\n\t\t\t\tclearTimeout(bell);\n\t\t\t\tsuggesting = null;\n\t\t\t}\n\t\t}\n\n\t\t// Joined to a sentence, or standing as one. After a send the box is empty\n\t\t// and the next thing typed is a refinement, so a clause that opens with a\n\t\t// dash arrives as punctuation with nothing in front of it.\n\t\tconst now = input.trimEnd();\n\t\tif (!now.toLowerCase().includes(clause.toLowerCase())) {\n\t\t\tinput = !now ? clause : now + (bit === 'action' ? ' — ' : ', ') + clause;\n\t\t}\n\t\tcomposer?.focus();\n\t\tvoid flush().then(() => {\n\t\t\tgrow(composer);\n\t\t\tcomposer?.setSelectionRange(input.length, input.length);\n\t\t});\n\t}\n\n\ttype AskKind = 'blocked' | 'self' | 'who' | 'sheet' | 'no-words' | 'too-short';\n\tconst askAbout = $derived.by<{\n\t\tid: string;\n\t\tkind: AskKind;\n\t\tsaid: string;\n\t\tmissing: ReturnType<typeof missingBits>;\n\t} | null>(() => {\n\t\tif (mode !== 'simple' || wantTarget !== 'clip') return null;\n\t\tconst rounds = chat.filter((c) => c.kind === 'confirm' && c.confirm);\n\t\tconst last = rounds.at(-1);\n\t\tif (!last?.confirm || selfAnswered[last.id] || last.confirm.sent) return null;\n\t\tconst said = (last.confirm.said ?? '').trim();\n\t\tconst at = (kind: AskKind) => ({ id: last.id, kind, said, missing: missingBits(said) });\n\n\t\t// Refusal first, and on every round: an age does not become renderable\n\t\t// because it arrived late in a conversation.\n\t\tif (UNDER_AGE.test(said)) return at('blocked');\n\t\tif (continuing) return null;\n\n\t\tif (FILENAME_ONLY.test(said)) return at('no-words');\n\t\tif (SHEET_FORMAT.test(said)) return at('sheet');\n\n\t\t// Themselves in it, or somebody addressed and nobody kept. A picture or a\n\t\t// kept face answers the first; only a name answers the second.\n\t\t//\n\t\t// Ahead of the thin-prompt question, because \"me an you\" is both and only\n\t\t// one of them can be asked. Three words asks for detail they can add\n\t\t// later; a missing likeness decides whose body comes back, and that one\n\t\t// cannot be added later — it is spent at the same button. It also read as\n\t\t// a bug: four words with a \"me\" in them got the likeness question and\n\t\t// three words did not, on the same machine.\n\t\tconst mine = !refFiles.length && !chosenCharacter;\n\t\tif (mine && FIRST_PERSON.test(said)) return at('self');\n\t\tif (mine && SECOND_PERSON.test(said)) return at('who');\n\n\t\tconst words = said.split(/\\s+/).filter(Boolean).length;\n\t\tif (rounds.length === 1 && words > 0 && words < 4) return at('too-short');\n\t\treturn null;\n\t});\n\n\tconst composerPlaceholder = $derived.by(() => {\n\t\t// An instruction, not an example. A worked example belongs on the empty\n\t\t// page, where there is room to read three of them and pick one; in the box\n\t\t// it has to be short enough to set on one line, and it was not — the old\n\t\t// simple-mode line ran to 74 characters and clipped against a rows=\"1\"\n\t\t// field whose autosize only runs on input, so half of it was never seen.\n\t\t// It also put explicit copy in the chrome, where it greets anyone walking\n\t\t// past the machine before they have asked for anything.\n\t\t// While the studio has asked something, the box says so. This is the whole\n\t\t// signal that it can be talked to — a label under the question saying \"you\n\t\t// can reply here\" would be a sign taped to a door that already opens.\n\t\tif (askAbout)\n\t\t\treturn askAbout.kind === 'blocked'\n\t\t\t\t? 'Describe a different shot'\n\t\t\t\t: askAbout.kind === 'self' || askAbout.kind === 'who'\n\t\t\t\t\t? 'Answer, or say who they are'\n\t\t\t\t\t: 'What happens in the shot?';\n\t\tif (mode === 'simple') {\n\t\t\tif (wantTarget === 'character')\n\t\t\t\treturn currentCharacter ? 'Describe the change' : 'Describe the person, with an age';\n\t\t\tif (wantTarget === 'location') return 'Describe the place';\n\t\t\treturn 'Describe one shot';\n\t\t}\n\t\tif (!brief) return 'Describe the film in one sentence';\n\t\tif (!planningWs) return 'Describe what to change';\n\t\treturn 'Message the crew';\n\t});\n\n\tasync function submit() {\n\t\tconst text = input.trim();\n\t\t// A held photograph is a message on its own: the picture is the character,\n\t\t// and the description is optional. Without this the send button stays dead\n\t\t// until you type something, which reads as the attachment not having worked.\n\t\tif (!text && !pendingPhoto) return;\n\t\t// Already sending: say so, and change nothing.\n\t\t//\n\t\t// This returned in silence, and the row below was created BEFORE the check —\n\t\t// so a press that did nothing still left a production in the sidebar with\n\t\t// no render behind it. Two of those appeared while a stuck send held this\n\t\t// flag, and from the outside it looked as though the studio had accepted\n\t\t// two orders and lost both.\n\t\tif (sending) {\n\t\t\tpushError('one is already being made — wait for it, or reload if it has stalled');\n\t\t\treturn;\n\t\t}\n\t\t// The row appears now, not when a GPU starts. Idempotent, so the second\n\t\t// message of a session does nothing here. AFTER the guards: a row for a\n\t\t// message that was never sent is a lie the sidebar keeps.\n\t\t// Making a character or a location stores it under an account, so a visitor\n\t\t// signs up here. Before anything is consumed: the words stay in the box.\n\t\tif (guest && (pendingPhoto || wantTarget !== 'clip')) {\n\t\t\taskToSignUp('studio_character');\n\t\t\treturn;\n\t\t}\n\t\tstopNotice = '';\n\t\tvoid startSession(text || pendingPhoto?.name || 'New session');\n\t\tinput = '';\n\t\tshrink(composer);\n\t\tpushItem({ who: 'user', kind: 'text', text });\n\t\tsending = true;\n\t\t// The stage's clock does not start here.\n\t\t//\n\t\t// It used to: everything between the send and the clip was machinery the\n\t\t// operator asked not to be shown, so the loader stood in for all of it and\n\t\t// a surface that stayed empty read as a button that did nothing. That held\n\t\t// while the read-back was accepted automatically and the send really did\n\t\t// run to a render.\n\t\t//\n\t\t// It no longer does. There is a decision in the middle now, and a loader\n\t\t// over it hides the one thing the operator is being asked to look at —\n\t\t// `working` outranks every other stage phase, so the sentence never gets\n\t\t// drawn. The clock starts on the press instead, in acceptConfirm; until\n\t\t// then `sending` covers the read-back's own second or two, which is honest,\n\t\t// because during it something is genuinely happening.\n\t\ttry {\n\t\t\t// Simple mode never plans. Every message is a scene to render, and the\n\t\t\t// answer is the prompt itself — offered for reading and editing before\n\t\t\t// it costs anything.\n\t\t\tif (mode === 'simple') {\n\t\t\t\t// What is being made is the target's question, and it has to be asked\n\t\t\t\t// first. `continuing` was asked before it and answered for it: switching\n\t\t\t\t// to the character generator does not clear a pending continuation —\n\t\t\t\t// nor should it, since making a character in the middle of a chain is\n\t\t\t\t// what the switch is for — so the description went down the clip path,\n\t\t\t\t// no subject was ever drawn, and the picture never came.\n\t\t\t\tif (pendingPhoto && wantTarget !== 'clip') await uploadSubject(pendingPhoto, text);\n\t\t\t\telse if (wantTarget !== 'clip') await sheetFromRequest(text, wantTarget);\n\t\t\t\t// Read it back first, in a sentence, before spending anything on it.\n\t\t\t\t// The brief and the render follow from the button on that card —\n\t\t\t\t// see confirmFromRequest and acceptConfirm. A continuation goes the same\n\t\t\t\t// way and always did: the round remembers which clip and which seam it\n\t\t\t\t// was written for, and continueFromRequest writes it.\n\t\t\t\t//\n\t\t\t\t// The stage used to accept this for you, which made the send one press\n\t\t\t\t// instead of two. But the read-back exists because a creator took their\n\t\t\t\t// references to a general chat and iterated there: what they wanted was\n\t\t\t\t// to see whether they had been understood while changing it was still\n\t\t\t\t// free. Accepting it automatically spends a render on every misreading,\n\t\t\t\t// which is the thing it was built to stop. The stage draws it now.\n\t\t\t\telse await confirmFromRequest(text);\n\t\t\t} else if (!brief) await planFromIdea(text);\n\t\t\telse if (!planningWs) await refinePlan(text);\n\t\t\telse await managerChat(text);\n\t\t} catch (e) {\n\t\t\t// A throw here used to be invisible: the try had a finally and no catch,\n\t\t\t// so an exception after the response arrived — a body that would not\n\t\t\t// parse, most likely — cleared the spinner and put nothing in the\n\t\t\t// transcript. No card, no error, no clue. That happened, and the only\n\t\t\t// way to tell it apart from \"the writer is still thinking\" was to read\n\t\t\t// the source. Whatever breaks, say so on screen.\n\t\t\tpushError(`Something went wrong sending that: ${e instanceof Error ? e.message : e}`);\n\t\t} finally {\n\t\t\tsending = false;\n\t\t}\n\t}\n\n\t// --- simple mode: one prompt, one clip -------------------------------------------\n\n\t/** Ask the writer for a render prompt. `pin` carries a duration or a frame the\n\t *  user has already moved on the card: both change the beat structure — the\n\t *  timestamps come from the duration and the camera language from the shape of\n\t *  the frame — so the prompt is written again rather than patched. */\n\tasync function callShotPrompt(\n\t\trequest: string,\n\t\tpin?: {\n\t\t\tseconds?: number;\n\t\t\torientation?: 'portrait' | 'landscape';\n\t\t\tcharacter?: string;\n\t\t\t/** The kept character's voice, when they have one. Sent so the writer\n\t\t\t *  names it rather than inventing a new one for this clip. */\n\t\t\tvoice?: string;\n\t\t\tlocation?: string;\n\t\t\tcontinues?: {\n\t\t\t\tpriorPrompt?: string;\n\t\t\t\tpriorLoras?: Pick[];\n\t\t\t\tpinned?: boolean;\n\t\t\t\t/** The two plates are frames of the prior clip, not kept sheets. */\n\t\t\t\tplatesFromClip?: boolean;\n\t\t\t};\n\t\t}\n\t): Promise<ChatItem['shot'] | null> {\n\t\tconst askedAt = Date.now();\n\t\tlet res: Response;\n\t\ttry {\n\t\t\t// The same deadline the read-back got, and for the same reason: a call\n\t\t\t// with no clock can only be ended by closing the tab. This one genuinely\n\t\t\t// is slow — 42, 44 and 59 seconds on three ordinary runs, and 120.1 on a\n\t\t\t// bad one — and a cap that cuts a working writer short would be a worse\n\t\t\t// bug than the one it fixes.\n\t\t\t//\n\t\t\t// Longer than the server's own budget, deliberately. The endpoint spends\n\t\t\t// at most ten minutes across its calls and its retries, and it knows why\n\t\t\t// it gave up; the browser knows only that nothing came back. Whichever\n\t\t\t// clock runs out first decides what the operator is told, and the one\n\t\t\t// with the reason should win.\n\t\t\tres = await fetch('/studio/api/shotprompt', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\tsignal: AbortSignal.timeout(11 * 60_000),\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({ request, ...pin })\n\t\t\t});\n\t\t} catch (e) {\n\t\t\tpushError(\n\t\t\t\te instanceof Error && (e.name === 'TimeoutError' || e.name === 'AbortError')\n\t\t\t\t\t? 'the brief writer did not answer in five minutes — send it again'\n\t\t\t\t\t: `Could not reach the prompt writer: ${e}`\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\t\tif (!res.ok) {\n\t\t\tconst m = (await res.json().catch(() => null)) as { message?: string } | null;\n\t\t\tpushError(m?.message || `The prompt could not be written (${res.status}).`);\n\t\t\treturn null;\n\t\t}\n\t\tlet r: {\n\t\t\tok: boolean;\n\t\t\tshot?: ChatItem['shot'];\n\t\t\twarn?: string[];\n\t\t\tfixed?: string[];\n\t\t\terror?: string;\n\t\t};\n\t\ttry {\n\t\t\tr = (await res.json()) as typeof r;\n\t\t} catch {\n\t\t\tpushError('The prompt writer answered with something this page could not read.');\n\t\t\treturn null;\n\t\t}\n\t\t// Carried onto the shot so the card can say it. The server has already given\n\t\t// the writer one chance to clear these; what is left is worth reading before\n\t\t// pressing render, and worth nothing at all after.\n\t\tif (r.shot && r.warn?.length) r.shot.warn = r.warn;\n\t\tif (r.shot && r.fixed?.length) r.shot.fixed = r.fixed;\n\t\t// Snapshot the writer's own choice the moment it arrives. Everything after\n\t\t// this can be edited on the card; this copy is what the edit is measured\n\t\t// against, so it is taken before anyone can touch it.\n\t\tif (r.shot) r.shot.wroteLoras = (r.shot.loras ?? []).map((p) => ({ ...p }));\n\t\tif (!r.ok || !r.shot) {\n\t\t\tpushError(r.error || 'The prompt could not be written.');\n\t\t\treturn null;\n\t\t}\n\t\t// Only the path that produced a shot. A failure tells you how long the\n\t\t// failure took, which is not what the waiting line is promising.\n\t\trecordWait('prompt', Date.now() - askedAt);\n\t\treturn r.shot;\n\t}\n\n\t/** Frame size, as three named steps rather than a pair of numbers.\n\t *\n\t *  Two constraints, and together they leave far fewer sizes than either does\n\t *  alone. Both sides must divide by 32, which the workflow requires. And the\n\t *  ratio must be one the harness recognises — it validates the profile\n\t *  against a fixed list and rejects the workspace outright otherwise:\n\t *\n\t *    480x832 is not a supported aspect ratio (expected one of: 1:1, 16:9,\n\t *    9:16, 4:3, 3:4, 3:2, 2:3, 21:9)\n\t *\n\t *  A first attempt at this offered 480p and 720p at 832x480 and 1280x704,\n\t *  which divide by 32 and are not 16:9 — 1.733 and 1.818 against 1.778 — so\n\t *  both were refused before a GPU was touched. Between 256 and 1920 there are\n\t *  exactly three 16:9 sizes whose sides both divide by 32, and these are they.\n\t *\n\t *  Named for the short edge, which is what they actually are. The steps are\n\t *  wide apart because that is where the arithmetic put them: a quarter of the\n\t *  pixels, the middle, and two and a quarter times. 576p stays the default —\n\t *  every clip so far was made at it. */\n\tconst RESOLUTIONS = {\n\t\t'288p': { long: 512, short: 288 },\n\t\t'576p': { long: 1024, short: 576 },\n\t\t'864p': { long: 1536, short: 864 }\n\t} as const;\n\ttype ResKey = keyof typeof RESOLUTIONS;\n\tconst RES_KEYS = Object.keys(RESOLUTIONS) as ResKey[];\n\n\tfunction frameFor(res: ResKey, orientation: 'portrait' | 'landscape') {\n\t\tconst { long, short } = RESOLUTIONS[res] ?? RESOLUTIONS['576p'];\n\t\treturn orientation === 'portrait'\n\t\t\t? { width: short, height: long }\n\t\t\t: { width: long, height: short };\n\t}\n\n\t/** What the composer is set to for the next clip.\n\t *\n\t *  These used to exist only on the card, which meant the writer produced a\n\t *  brief at whatever it felt like and changing the length or the frame threw\n\t *  that brief away and asked for another — the beats are derived from the\n\t *  duration and the camera language from the shape, so a change there is a\n\t *  rewrite, not a relabel. Setting them before you send spends one call\n\t *  instead of two.\n\t *\n\t *  Kept across reloads: these are how you work, not what this clip is. */\n\tconst SETUP_KEY = 'auteur-studio-setup';\n\t/** Five seconds and 576p to start with.\n\t *  The shortest clip and the middle frame: the first send a person makes\n\t *  should be the cheapest one that still shows whether the idea works, and\n\t *  eight seconds at the old default spent half again as much to say the same\n\t *  thing. Both stay one tap from anything else. */\n\tlet wantSeconds = $state(5);\n\tlet wantOrientation = $state<'portrait' | 'landscape'>('portrait');\n\tlet wantRes = $state<ResKey>('576p');\n\t/** What the next message makes. A clip is the default and the common case;\n\t *  the other two make a reference sheet instead, and they are here rather\n\t *  than on a separate screen because they are the same act — you describe\n\t *  something and the machine renders it. */\n\tlet wantTarget = $state<'clip' | 'character' | 'location'>('clip');\n\n\t/** When this pass through the generator began.\n\t *\n\t *  \"New character\" means a new one, and the stage was answering with the last\n\t *  one: it held the surface, wearing its name and its voice, with its four\n\t *  views still turning as though they were the thing that had just been asked\n\t *  for. The work behind them is real and goes on — it lands in the rail like\n\t *  any other — but it is not what this pass is about.\n\t *\n\t *  So the stage shows what has been made SINCE the generator was opened, and\n\t *  before that it is empty and waiting, which is what the band above it says.\n\t *  Zero means no pass has begun: everything counts, which is the behaviour a\n\t *  reload and every clip-mode read of these two want. */\n\tlet subjectFrom = $state(0);\n\n\t/** When a subject's drawing started, 0 when it does not say. Unknown counts as\n\t *  old enough for any pass, which is what keeps a zero mark meaning \"all\". */\n\tfunction drawStartedMs(row: StoredSheet): number {\n\t\tconst t = Date.parse(row.sheet?.startedAt ?? '');\n\t\treturn Number.isFinite(t) ? t : 0;\n\t}\n\n\t/** The wait named by what is being made, not by the machinery making it.\n\t *  \"writing the prompt\" was true of all three simple-mode waits, which is\n\t *  what made it useless: it never told you which one you were in. The chosen\n\t *  face is deliberately not repeated here — the chip saying so is two lines\n\t *  below, and a line this small should not spend half its width on it. */\n\tconst sendingWhat = $derived(\n\t\tmode !== 'simple'\n\t\t\t? brief && planningWs\n\t\t\t\t? 'the crew is replying'\n\t\t\t\t: 'planning'\n\t\t\t: wantTarget === 'character'\n\t\t\t\t? 'writing the character'\n\t\t\t\t: wantTarget === 'location'\n\t\t\t\t\t? 'writing the location'\n\t\t\t\t\t: 'writing the shot'\n\t);\n\n\t/** The kept character the next clip is shot with, by id. Empty means the clip\n\t *  invents whoever the words describe, which is the old behaviour and stays\n\t *  the default. */\n\tlet wantCharacter = $state('');\n\t/** The kept location the next clip is shot in. Empty means the clip invents\n\t *  wherever the words describe, which stays the default. */\n\tlet wantLocation = $state('');\n\n\t/** The composer used to carry both kept rows open at all times, each capped\n\t *  at three by `slice(0, 3)` — so a fourth character was unreachable from\n\t *  here and nothing on screen said so. Both rows fold into one menu: level\n\t *  one is what you can add, level two is a grid of everything you keep.\n\t *  What you picked does not go with them; it comes back as a chip. */\n\tlet addOpen = $state(false);\n\t/** null while the grid is closed, otherwise which kind it is showing. */\n\tlet pickKind = $state<null | 'character' | 'location'>(null);\n\t/** Length, size and frame, folded for the same reason: all three have a\n\t *  saved default that works, so none of them blocks a first send. */\n\t/** Which group in the settings row above the composer is open.\n\t *\n\t *  Empty means all of them are folded to their current value, which is the\n\t *  resting state: the row then reads as a line of facts — 5s, 288p, 9:16 —\n\t *  rather than as a wall of choices. One at a time, so the row keeps to a\n\t *  single scroll and the set being chosen is the only one expanded. */\n\tlet pillGrp = $state('');\n\t/** Where the open panel grows from, in pixels along the settings row.\n\t *\n\t *  It opened from the row's left edge whatever was tapped, so pressing the\n\t *  frame bubble at the right-hand end produced a panel somewhere else — the\n\t *  eye had to find it, which is the one thing an animation is supposed to\n\t *  save you. `x` places the panel, `o` puts the growth origin under the\n\t *  bubble itself, so it unfolds out of the thing you pressed. */\n\tlet pillAt = $state({ x: 0, o: 0 });\n\n\t/** Reads the tapped bubble's place in the row and opens from there.\n\t *\n\t *  Clamped to the row: a bubble near the right edge would otherwise put a\n\t *  144px panel half off the screen. When that happens the panel slides back\n\t *  inside and only the origin stays under the bubble, which is what keeps the\n\t *  movement pointing at the right place even when the box cannot. */\n\t/** Same trick for the two panels that are not pill groups.\n\t *\n\t *  They opened from the composer's left edge — one as a sheet glued to the\n\t *  bottom of the screen, the other as a popover two hundred pixels from the\n\t *  bubble that summoned it. Both are now measured against the composer box\n\t *  they are positioned in, so they rise out of the control that was pressed\n\t *  like everything else in this row. */\n\tlet panelAt = $state({ x: 0, o: 0 });\n\n\tfunction openPanelAt(ev: MouseEvent, which: 'fmt' | 'mode') {\n\t\tconst el = ev.currentTarget as HTMLElement;\n\t\tconst box = el.closest('.composerbox') as HTMLElement | null;\n\t\tif (box) {\n\t\t\tconst b = el.getBoundingClientRect();\n\t\t\tconst w = box.getBoundingClientRect();\n\t\t\tconst PANEL = 312;\n\t\t\tconst raw = b.left - w.left;\n\t\t\tconst x = Math.max(8, Math.min(raw, w.width - PANEL - 8));\n\t\t\tpanelAt = { x, o: Math.max(12, Math.min(b.left - w.left - x + b.width / 2, PANEL - 12)) };\n\t\t}\n\t\tconst open = which === 'fmt' ? !fmtOpen : !modeOpen;\n\t\tshutMenus();\n\t\tif (which === 'fmt') fmtOpen = open;\n\t\telse modeOpen = open;\n\t}\n\n\tfunction openPillAt(ev: MouseEvent, id: string) {\n\t\tconst el = ev.currentTarget as HTMLElement;\n\t\tconst wrap = el.closest('.pillwrap') as HTMLElement | null;\n\t\tif (wrap) {\n\t\t\tconst b = el.getBoundingClientRect();\n\t\t\tconst w = wrap.getBoundingClientRect();\n\t\t\tconst PANEL = 144;\n\t\t\tconst raw = b.left - w.left;\n\t\t\tconst x = Math.max(0, Math.min(raw, w.width - PANEL));\n\t\t\tpillAt = { x, o: Math.max(8, Math.min(b.left - w.left - x + b.width / 2, PANEL - 8)) };\n\t\t}\n\t\tpillGrp = id;\n\t}\n\n\t/** The row's groups, folded or open. Built rather than written out because\n\t *  the continuation group only exists when there is something to continue,\n\t *  and because each one has to say three things — what it is set to now, what\n\t *  else it could be, and what to do about it — which is a shape, not markup.\n\t *\n\t *  Lower case on purpose. Named PILL_GROUPS it compiled clean and threw\n\t *  \"PILL_GROUPS is not defined\" in the browser: a capitalised identifier in a\n\t *  template is a component reference to Svelte, so the call resolved against\n\t *  the component namespace instead of this scope. The whole surface below it\n\t *  went dead — including the send — with nothing on screen to say why. */\n\tfunction pillGroups() {\n\t\tconst g: {\n\t\t\tid: string;\n\t\t\tup: boolean;\n\t\t\tnow: string;\n\t\t\t/** Whether this group's answers are words rather than measurements.\n\t\t\t *  The row is set in the mono face so 5s, 576p and 16:9 line up as\n\t\t\t *  figures; \"Last frame\" is a phrase, and in that face it read as a\n\t\t\t *  different kind of control sitting between two ordinary ones. */\n\t\t\twords?: boolean;\n\t\t\topts: { v: string; l: string; box: string; off: boolean }[];\n\t\t\tpick: (v: string) => void;\n\t\t}[] = [];\n\t\tif (stageContinuable) {\n\t\t\tg.push({\n\t\t\t\tid: 'next',\n\t\t\t\tup: true,\n\t\t\t\twords: true,\n\t\t\t\tnow: !continuing ? 'new' : pinSeam ? 'seam' : 'same',\n\t\t\t\topts: [\n\t\t\t\t\t{ v: 'seam', l: 'Last frame', box: '', off: false },\n\t\t\t\t\t{ v: 'same', l: 'Same person', box: '', off: false },\n\t\t\t\t\t{ v: 'new', l: 'New clip', box: '', off: false }\n\t\t\t\t],\n\t\t\t\tpick: (v: string) => {\n\t\t\t\t\tif (v === 'new') {\n\t\t\t\t\t\tcontOffFor = stageContinuable?.id ?? '';\n\t\t\t\t\t\tcontinuing = null;\n\t\t\t\t\t\tspendConfirmChain();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tcontOffFor = '';\n\t\t\t\t\tif (!continuing && stageContinuable) startContinue(stageContinuable);\n\t\t\t\t\tpinSeam = v === 'seam';\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\treturn g;\n\t}\n\n\tlet fmtOpen = $state(false);\n\t/** One clip or a full production. It used to sit in the header, where it read\n\t *  as a property of the page; it is a property of the message you are about\n\t *  to send, so it belongs beside the send button. */\n\tlet modeOpen = $state(false);\n\n\t/** How many takes one message makes.\n\t *\n\t *  One is the resting answer and always will be: the common case must not pay\n\t *  for the rare one. Above one, the same beat is rendered that many times with\n\t *  different seeds, at once — the harness runs four together, and four at once\n\t *  cost about what one costs, where four in a row cost four cold starts.\n\t *\n\t *  It lives on the mode chip rather than beside it because that chip already\n\t *  answers the same question — what will this message make — and because this\n\t *  is the first control here where one press spends four times. A commitment\n\t *  like that has to be readable without opening anything. */\n\tlet takes = $state(1);\n\t/** How many camera angles the same beat is shot from.\n\t *\n\t *  A second axis, not a second mode: every message makes `takes × angles`\n\t *  clips, and \"one clip\" is simply 1 × 1. Both end the same way — you look at\n\t *  what came back and carry one forward — and they differ only in what varies\n\t *  between them, the seed or the camera.\n\t *\n\t *  The product is capped at MAX_AT_ONCE because that is the harness's\n\t *  RENDER_PARALLELISM: every allowed combination is one warm batch of about\n\t *  210 seconds. Without the cap 3 × 3 is nine renders in three waves, and the\n\t *  thing this app promises about speed quietly stops being true. */\n\tlet angles = $state(1);\n\tconst MAX_AT_ONCE = 4;\n\t/** What the next message will make, in the fewest words that are still true.\n\t *  With one axis raised the beat is named; with both, only the total is —\n\t *  \"2 camera angles, 2 versions each\" on a chip is a recipe, and the chip's\n\t *  job is the size of the commitment. The breakdown is one tap away. */\n\n\tfunction shutMenus() {\n\t\taddOpen = false;\n\t\tpickKind = null;\n\t\tfmtOpen = false;\n\t\tmodeOpen = false;\n\t}\n\n\tfunction saveSetup() {\n\t\ttry {\n\t\t\tlocalStorage.setItem(\n\t\t\t\tSETUP_KEY,\n\t\t\t\tJSON.stringify({\n\t\t\t\t\ts: wantSeconds,\n\t\t\t\t\to: wantOrientation,\n\t\t\t\t\tr: wantRes,\n\t\t\t\t\tt: wantTarget,\n\t\t\t\t\tc: wantCharacter,\n\t\t\t\t\tl: wantLocation,\n\t\t\t\t\t// Not persisted for a while, which made it the one composer setting\n\t\t\t\t\t// that silently reset — and the one whose reset costs money in the\n\t\t\t\t\t// wrong direction is worth writing down.\n\t\t\t\t\tk: takes,\n\t\t\t\t\tkk: angles\n\t\t\t\t})\n\t\t\t);\n\t\t} catch {\n\t\t\t/* a preference that will not persist is not worth an error */\n\t\t}\n\t}\n\n\t/** What the user typed, kept so a rewrite asks for the same scene again rather\n\t *  than editing the prompt the model last produced. */\n\tlet lastRequest = $state('');\n\n\t/** The rounds already agreed in this session, oldest first.\n\t *\n\t *  Read out of the chat rather than kept in a variable, for the same reason the\n\t *  awaited sheets are: the conversation is the record and a variable is not. It\n\t *  survives a reload; a variable does not, and a refinement that has forgotten\n\t *  what it is refining silently drops everything said before it. */\n\t/** The operator's half and ours, told apart.\n\t *\n\t *  The read-back is what they said; anything after the marker is what the\n\t *  studio is filling in for them. Rendered at the same weight they read as one\n\t *  paragraph, and then a room nobody asked for looks like a room they asked\n\t *  for — which is precisely the agreement this layer exists to make honest.\n\t *\n\t *  Split on the marker the writer is told to emit rather than on sentence\n\t *  punctuation: a full stop is in every abbreviation and half the prose, and a\n\t *  wrong split here would attribute their own words to us. No marker means\n\t *  there was nothing to add, which is a normal and good answer. */\n\tfunction splitConfirm(line: string): { lead: string; said: string; added: string } {\n\t\tlet rest = line;\n\t\tlet added = '';\n\t\t// The attribution, off the end. The marker stays: it used to be stripped,\n\t\t// back when this half was the proposal itself and the word was just a\n\t\t// seam. It is an attribution now — take it off and the line opens\n\t\t// lower-case in the middle of a thought, and stops saying whose list it is.\n\t\tconst at = rest.search(/(^|[.!?…]\\s+)(Hozzátettük|We added)\\s*:/i);\n\t\tif (at !== -1) {\n\t\t\tconst marker = rest.slice(at).search(/(Hozzátettük|We added)\\s*:/i);\n\t\t\tadded = rest.slice(at + marker).trim();\n\t\t\trest = rest.slice(0, at + marker);\n\t\t}\n\t\t// The opening line, off the front, at the first blank line. Mid-stream\n\t\t// there is no blank line yet, so everything is still the lead — which is\n\t\t// what it looks like anyway, because the lead is what arrives first.\n\t\t//\n\t\t// A model that skips the blank line loses its lead rather than leaking a\n\t\t// greeting into the description, and from there into the brief. Wrong in\n\t\t// the safe direction.\n\t\tconst brk = rest.indexOf('\\n\\n');\n\t\tconst lead = brk === -1 ? rest.trim() : rest.slice(0, brk).trim();\n\t\tconst said = brk === -1 ? '' : rest.slice(brk).trim();\n\t\t// A lead that ran long is not a lead. Prose that happens to start with a\n\t\t// short line would otherwise be filed as a greeting and dropped.\n\t\tif (lead.length > 90 && !said) return { lead: '', said: lead, added };\n\t\treturn { lead, said, added };\n\t}\n\n\t/** The composer's settings as the confirmation sees them. */\n\ttype ConfirmSettings = {\n\t\tseconds: number;\n\t\twho: string;\n\t\twhere: string;\n\t\tangles: number;\n\t\tmode: string;\n\t};\n\tfunction settingsNow(): ConfirmSettings {\n\t\treturn {\n\t\t\tseconds: composerShape.seconds,\n\t\t\twho: chosenCharacter?.name ?? '',\n\t\t\twhere: chosenLocation?.name ?? '',\n\t\t\tangles: effAngles,\n\t\t\t// Entering or leaving a continuation, or toggling its seam, is a setting\n\t\t\t// like the others: it gets a round that names the change.\n\t\t\tmode: continuing ? (pinSeam ? 'last frame' : 'same scene, new take') : 'none'\n\t\t};\n\t}\n\t/** What the newest live round was written for. Null until there is one. */\n\tlet settingsWritten: ConfirmSettings | null = null;\n\tlet settingsTimer: ReturnType<typeof setTimeout> | null = null;\n\n\tfunction settingsDiff(a: ConfirmSettings, b: ConfirmSettings): string[] {\n\t\tconst out: string[] = [];\n\t\tif (a.seconds !== b.seconds) out.push(`length ${a.seconds} -> ${b.seconds} seconds`);\n\t\tif (a.who !== b.who) out.push(`character ${a.who || 'none'} -> ${b.who || 'none'}`);\n\t\tif (a.where !== b.where) out.push(`location ${a.where || 'none'} -> ${b.where || 'none'}`);\n\t\tif (a.angles !== b.angles) out.push(`camera angles ${a.angles} -> ${b.angles}`);\n\t\tif (a.mode !== b.mode) out.push(`continuation ${a.mode} -> ${b.mode}`);\n\t\treturn out;\n\t}\n\n\t/** A setting moved under a live proposal: say so, and re-shape it.\n\t *\n\t *  The description on screen was written for the length, character and room\n\t *  the composer had at the time. Turn the length from five seconds to fifteen\n\t *  and nothing used to happen until the next message — so the paragraph a\n\t *  person was reading, and the button they were about to press, described a\n\t *  five second clip over a fifteen second setting. The render itself was\n\t *  fine (the writer is pinned to the composer), but the contract on screen\n\t *  was stale, and a stale contract is the one thing this layer must not be.\n\t *\n\t *  Only while a round is live. After the button, a changed chip is for the\n\t *  NEXT shot, and re-describing a clip already on a GPU would be noise.\n\t *  Debounced, because a person clicking through 5, 8, 15 wants one answer\n\t *  about 15, not three. */\n\t$effect(() => {\n\t\tconst now = settingsNow();\n\t\tif (!settingsWritten) return;\n\t\tif (!settingsDiff(settingsWritten, now).length) return;\n\t\t// Entering or leaving a continuation is not a change to the shot being\n\t\t// described — it is a different shot. Re-describing the old one under the\n\t\t// new mode produced a \"new clip\" that was still the continuation, and a\n\t\t// continuation that inherited an unrelated clip's agreed lines. The chain\n\t\t// closes; the next typed message starts clean. Only a seam toggle inside a\n\t\t// continuation is answered with a round.\n\t\tif ((settingsWritten.mode === 'none') !== (now.mode === 'none')) {\n\t\t\tuntrack(() => spendConfirmChain());\n\t\t\treturn;\n\t\t}\n\t\tconst live = untrack(() => chat.filter((c) => c.kind === 'confirm').at(-1));\n\t\tif (!live?.confirm || live.confirm.sent || live.confirm.streaming) return;\n\t\tif (settingsTimer) clearTimeout(settingsTimer);\n\t\tsettingsTimer = setTimeout(() => {\n\t\t\tsettingsTimer = null;\n\t\t\tif (!settingsWritten) return;\n\t\t\tconst diffs = settingsDiff(settingsWritten, settingsNow());\n\t\t\tif (diffs.length) void confirmFromRequest('', diffs.join(', '));\n\t\t}, 700);\n\t});\n\n\t/** Close the current read-back chain: every unsent round is spent.\n\t *\n\t *  The rounds of a shot are its \"agreed so far\"; a round that outlives its\n\t *  shot leaks into the next one as agreement nobody made. Called on launch,\n\t *  and on entering or leaving a continuation — a continuation is a different\n\t *  order from the new clip that was being described before it, and vice\n\t *  versa, so neither may inherit the other's history. */\n\tfunction spendConfirmChain() {\n\t\tfor (const x of chat)\n\t\t\tif (x.kind === 'confirm' && x.confirm && !x.confirm.sent) x.confirm.sent = true;\n\t\tsettingsWritten = null;\n\t\t// Written down. In memory the chain was closed; on disk it was not, so a\n\t\t// reload after \"new clip\" brought the old rounds back unsent.\n\t\tpersist();\n\t}\n\n\tfunction confirmHistory(): string[] {\n\t\tconst out: string[] = [];\n\t\tfor (const c of chat) {\n\t\t\tif (c.kind === 'confirm' && c.confirm?.line && !c.confirm.sent) out.push(c.confirm.line);\n\t\t}\n\t\treturn out;\n\t}\n\n\t/** Read back what is about to be shot, streamed, before anything is written.\n\t *\n\t *  The card is pushed empty and filled in as the text arrives. pushItem hands\n\t *  back the state proxy for exactly this. */\n\t/** `auto` presses the button this card exists to offer.\n\t *\n\t *  The read-back is a cost gate: it restates the order in a sentence so a\n\t *  misread costs a glance rather than a GPU. The stage does not show it, so on\n\t *  the stage the gate is gone by design — the operator asked for one press\n\t *  from typing to clip, and this is the half of that they are paying for. The\n\t *  card is still written and still kept; only nobody is asked about it. */\n\t/** Ask for the read-back again, in place of the one that failed.\n\t *\n\t *  A failed read-back left the sentence you typed in the transcript and\n\t *  nothing to press: the only way on was to type it again, which is asking a\n\t *  person to do the app's retrying by hand. The provider hangs occasionally\n\t *  and the route now asks twice on its own, so what reaches here is the case\n\t *  where both went unanswered — rare, and still not something to solve with a\n\t *  keyboard.\n\t *\n\t *  The failed round goes and a fresh one takes its place, so the transcript\n\t *  reads as one attempt rather than a record of the studio's bad minute. */\n\tasync function retryConfirm(item: ChatItem) {\n\t\tconst c = item.confirm;\n\t\tif (!c || c.streaming || sending) return;\n\t\tconst said = c.said;\n\t\tconst changed = c.changed ?? '';\n\t\tchat = chat.filter((x) => x !== item);\n\t\tpersist();\n\t\tawait confirmFromRequest(said, changed);\n\t}\n\n\tasync function confirmFromRequest(said: string, changed = '', auto = false) {\n\t\t// What this round is written for. The settings watcher diffs against it.\n\t\tsettingsWritten = settingsNow();\n\t\tconst cont = continuing;\n\t\tconst snap = cont\n\t\t\t? {\n\t\t\t\t\tworkspace: cont.workspace,\n\t\t\t\t\tpinned: pinSeam,\n\t\t\t\t\tcharacterName: cont.characterName,\n\t\t\t\t\tlocationName: cont.locationName\n\t\t\t\t}\n\t\t\t: undefined;\n\t\tconst item = pushItem({\n\t\t\twho: 'studio',\n\t\t\tkind: 'confirm',\n\t\t\tconfirm: {\n\t\t\t\tsaid,\n\t\t\t\tline: '',\n\t\t\t\tstreaming: true,\n\t\t\t\t...(changed ? { changed } : {}),\n\t\t\t\t...(snap ? { continues: snap } : {})\n\t\t\t}\n\t\t});\n\t\tconst askedAt = Date.now();\n\t\t// A deadline on the whole read-back, stream included.\n\t\t//\n\t\t// There was none, and the read loop below waits on `reader.read()` with no\n\t\t// clock: a stream that stops arriving without closing parks that await\n\t\t// forever. Nothing throws, so nothing is caught, so nothing is said — the\n\t\t// send stays \"in flight\", the loader counts to its cap and sits there, and\n\t\t// the render is never launched. Ninety-seven per cent, no message, no clip.\n\t\t//\n\t\t// Sixty seconds is far past any real one — the read-back measures under\n\t\t// three — and the point is not to be tight, it is to end.\n\t\tconst ctl = new AbortController();\n\t\tconst deadline = setTimeout(() => ctl.abort(), 60_000);\n\t\ttry {\n\t\t\t// Asked twice when the platform answers 503. The route itself answers\n\t\t\t// 200 with {ok:false} on its own failures, so a 503 is the layer in\n\t\t\t// front of it — 3 of 6 calls in one test window — and it is the kind\n\t\t\t// of refusal a second request a moment later usually gets past.\n\t\t\tconst ask = () =>\n\t\t\t\tfetch('/studio/api/shotconfirm', {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\tsignal: ctl.signal,\n\t\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\trequest: said,\n\t\t\t\t\t\t...(changed ? { changed } : {}),\n\t\t\t\t\t\tseconds: composerShape.seconds,\n\t\t\t\t\t\tres: composerShape.res,\n\t\t\t\t\t\taspect: composerShape.portrait ? '9:16' : '16:9',\n\t\t\t\t\t\tmakes: batchLabel,\n\t\t\t\t\t\t// Continuing: the people and the place are the prior clip's, whatever\n\t\t\t\t\t\t// the chips say; and the prior brief goes along so the end state can\n\t\t\t\t\t\t// be read off it.\n\t\t\t\t\t\tcharacter: cont ? cont.characterName : chosenCharacter?.name,\n\t\t\t\t\t\tlocation: cont\n\t\t\t\t\t\t\t? cont.locationName\n\t\t\t\t\t\t\t: chosenLocation?.description || chosenLocation?.name,\n\t\t\t\t\t\t...(cont\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tcontinuing: {\n\t\t\t\t\t\t\t\t\t\tseam: pinSeam ? 'pinned' : 'free',\n\t\t\t\t\t\t\t\t\t\twho: cont.characterName,\n\t\t\t\t\t\t\t\t\t\twhere: cont.locationName,\n\t\t\t\t\t\t\t\t\t\tpriorPrompt: logRow[cont.workspace]?.prompt\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\trefs: refFiles.map((f) => f.description || f.name).filter(Boolean),\n\t\t\t\t\t\thistory: confirmHistory()\n\t\t\t\t\t})\n\t\t\t\t});\n\t\t\tlet res = await ask();\n\t\t\tif (res.status === 503) {\n\t\t\t\tawait new Promise((r) => setTimeout(r, 1200));\n\t\t\t\tres = await ask();\n\t\t\t}\n\n\t\t\t// One endpoint, two shapes: a stream when it worked, our house JSON\n\t\t\t// error when it did not. Content-type tells them apart, so neither has\n\t\t\t// to lie about its status code.\n\t\t\tif (!res.ok || !res.body || (res.headers.get('content-type') ?? '').includes('json')) {\n\t\t\t\tconst r = (await res.json().catch(() => null)) as { error?: string } | null;\n\t\t\t\titem.confirm!.error = r?.error || `that could not be read back (${res.status})`;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst reader = res.body.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tfor (;;) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\t\t\t\titem.confirm!.line += decoder.decode(value, { stream: true });\n\t\t\t}\n\t\t\tif (!item.confirm!.line.trim()) {\n\t\t\t\titem.confirm!.error = 'nothing came back — try sending that again';\n\t\t\t}\n\t\t} catch (e) {\n\t\t\t// An abort is this deadline firing, and it deserves its own words: the\n\t\t\t// generic \"could not be read back\" reads like the model refused.\n\t\t\titem.confirm!.error =\n\t\t\t\te instanceof Error && e.name === 'AbortError'\n\t\t\t\t\t? 'the read-back stopped part way — send it again'\n\t\t\t\t\t: `that could not be read back — ${e instanceof Error ? e.message : e}`;\n\t\t} finally {\n\t\t\tclearTimeout(deadline);\n\t\t\titem.confirm!.streaming = false;\n\t\t\trecordWait('confirm', Date.now() - askedAt);\n\t\t\tpersist();\n\t\t}\n\t\t// Nothing to accept if the read-back itself failed: the error is what the\n\t\t// stage should show, and shooting from a line that could not be written is\n\t\t// how a misread becomes a render.\n\t\tif (auto && !item.confirm!.error) await acceptConfirm(item.id);\n\t\t// And when it did fail, the stage has to hear about it: the read-back's\n\t\t// error lives on the card, which is exactly what the stage does not draw.\n\t\telse if (auto && item.confirm!.error) pushError(item.confirm!.error);\n\t}\n\n\t/** The operator pressed the button: write the brief, check it, and shoot.\n\t *\n\t *  What goes to the writer is BOTH texts, the raw one first. The writer is\n\t *  told to use the operator's own plain words and it can only do that if it\n\t *  has them — a read-back is a restatement, and a restatement that has been\n\t *  through a model is not a safe place to keep the only copy of what somebody\n\t *  actually asked for. The accepted line follows it as the agreed reading.\n\t *\n\t *  It renders on its own from here. That is the whole point: the brief is not\n\t *  a thing to approve twice. The one exception is a brief the checker had to\n\t *  change — then it stops and says what changed, because that is a different\n\t *  clip from the one that was agreed to.\n\t */\n\t/** Start a brief the way its own card would.\n\t *\n\t *  The composer can be set to several takes or several camera angles, and the\n\t *  card offers renderBatch for those — calling renderShot from here regardless\n\t *  would quietly deliver one clip where two were asked for and read back. */\n\tasync function startFromCard(card: ChatItem): Promise<void> {\n\t\tif (!card.shot) return;\n\t\tif (guest) {\n\t\t\taskToSignUp('studio_generate');\n\t\t\treturn;\n\t\t}\n\t\t// The other way in — \"render this\" on a card, and the retry under an\n\t\t// error — reaches the GPU without passing the confirmation, so it asks the\n\t\t// same question here.\n\t\tconst cardShort = affords(!!card.shot.continues && card.shot.continues.pinned !== false);\n\t\tif (cardShort !== null) {\n\t\t\taskForTokens(cardShort);\n\t\t\treturn;\n\t\t}\n\t\tconst cardAngles = card.shot.continues && card.shot.continues.pinned !== false ? 1 : angles;\n\t\tconst n = takes * cardAngles;\n\t\tif (n > 1) await renderBatch(card.id, takes, cardAngles);\n\t\telse await renderShot(card.id);\n\t}\n\n\t/** What this press costs, and whether the purse can take it.\n\t *\n\t *  Asked BEFORE anything starts. The server refuses on its own — that is what\n\t *  actually protects the GPU — but by the time it answers, the clock is\n\t *  running, the loader is over the stage and a brief has been written: the\n\t *  surface has spent a minute saying \"Generating\" about a render that was\n\t *  never going to happen. The only honest place for \"you cannot afford this\"\n\t *  is instead of the press, not after it. */\n\tfunction affords(continues: boolean): number | null {\n\t\tconst clips = takes * (continues ? 1 : effAngles);\n\t\tconst price = tokensForSend(composerShape.seconds, clips, freeClip);\n\t\treturn tokens < price ? price : null;\n\t}\n\n\tasync function acceptConfirm(itemId: string) {\n\t\tconst item = chat.find((c) => c.id === itemId);\n\t\tconst c = item?.confirm;\n\t\tif (!c || shotBusy[itemId]) return;\n\t\t// A visitor signs up here, at the press that starts a GPU, and the press\n\t\t// is kept: once they are in, resumePendingPress makes it for them.\n\t\tif (guest) {\n\t\t\taskToSignUp('studio_generate', itemId);\n\t\t\treturn;\n\t\t}\n\t\t// Before the clock, before the brief, before the busy state: the shop\n\t\t// opens and the stage is left exactly as it was.\n\t\tconst short = affords(!!c.continues && c.continues.pinned !== false);\n\t\tif (short !== null) {\n\t\t\taskForTokens(short);\n\t\t\treturn;\n\t\t}\n\t\tshotBusy[itemId] = true;\n\t\tc.busySince = Date.now();\n\t\tc.error = undefined;\n\t\t// This is the press the stage waits on. From here to the clip is the\n\t\t// machinery nobody asked to watch — the brief, the checker, the workspace,\n\t\t// the GPU — so the loader covers all of it, and the blur underneath is the\n\t\t// clip this one grows out of when it is a continuation.\n\t\tif (STAGE_UI) {\n\t\t\tstageStartedAt = Date.now();\n\t\t\t// stageWaitFrom stays the newest: it is how the arrival of a NEW clip is\n\t\t\t// noticed, not where this one comes from.\n\t\t\tstageWaitFrom = stageNewest?.id ?? '';\n\t\t\t// The blur is the clip being continued, which is the one being watched\n\t\t\t// and no longer necessarily the last one. Left on the newest it showed\n\t\t\t// the wrong picture under the loader the moment somebody clicked back\n\t\t\t// through the strip and carried on from there.\n\t\t\tstageWaitBlurUrl = c.continues ? (stageContinuable?.artifact?.files?.[0]?.url ?? '') : '';\n\t\t}\n\t\t// Set once the server has the press: from then on the round's busy state and\n\t\t// the loader belong to the watcher, not to this function's way out.\n\t\tlet handedOff = false;\n\t\ttry {\n\t\t\t// Second press: the brief already exists and was shown, changes and all.\n\t\t\t// Write it again and the operator is shown one set of changes and shoots\n\t\t\t// another — and pays a minute for the privilege.\n\t\t\tconst already = c.cardId ? chat.find((x) => x.id === c.cardId) : undefined;\n\t\t\tif (already?.shot && !already.shot.launched) {\n\t\t\t\tc.fixed = undefined;\n\t\t\t\tc.phase = 'starting';\n\t\t\t\tif (stopWanted) {\n\t\t\t\t\tstopWanted = false;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait startFromCard(already);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tc.fixed = undefined;\n\t\t\t// The description only. The opening line and the attribution are the\n\t\t\t// studio talking to a person — \"írd át, ha más kell\" is an offer, and\n\t\t\t// in a brief it reads as an instruction to the crew.\n\t\t\tconst agreed = splitConfirm(c.line).said.trim();\n\t\t\t// Their own words from EVERY round of this shot, not only the last.\n\t\t\t// Round one said \"nagy faszt\" and round two said \"tedd bele hogy\n\t\t\t// remeg\"; the writer is told to keep the operator's plain terms, and\n\t\t\t// it can only keep the ones it is given. A round that answered a\n\t\t\t// setting change said nothing and contributes nothing here.\n\t\t\tconst rounds = chat.filter((x) => x.kind === 'confirm' && x.confirm && !x.confirm.sent);\n\t\t\tconst raw = rounds\n\t\t\t\t.map((x) => x.confirm!.said.trim())\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join('\\n');\n\t\t\tconst request = agreed ? `${raw || c.said}\\n\\n---\\n\\n${agreed}` : raw || c.said;\n\t\t\tc.phase = 'writing';\n\t\t\t// A continuation writes through its own path, against the clip the round\n\t\t\t// was started from — not whatever the composer points at now.\n\t\t\tif (c.continues) {\n\t\t\t\tif (continuing?.workspace !== c.continues.workspace) {\n\t\t\t\t\tc.error = 'that clip is no longer being continued — press Continue on it again';\n\t\t\t\t\tif (STAGE_UI) pushError(c.error);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tpinSeam = c.continues.pinned;\n\t\t\t}\n\t\t\t// The stage hands the press to the server, which writes the brief and\n\t\t\t// launches whether or not this tab is still here — see generateOnServer.\n\t\t\t// The transcript keeps the path below: it stops on a checker's changes\n\t\t\t// and asks, and that conversation needs the tab.\n\t\t\tif (STAGE_UI && mode === 'simple' && sessionSlug) {\n\t\t\t\thandedOff = await generateOnServer(itemId, c, request);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\twriting = true;\n\t\t\tlet card: ChatItem | null | undefined;\n\t\t\ttry {\n\t\t\t\tcard = c.continues ? await continueFromRequest(request) : await shotFromRequest(request);\n\t\t\t} finally {\n\t\t\t\twriting = false;\n\t\t\t}\n\t\t\t// Stopped while the brief was being written. It was already said, and\n\t\t\t// nothing was charged; the brief is simply not used.\n\t\t\tif (stopWanted) {\n\t\t\t\tstopWanted = false;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!card?.shot) {\n\t\t\t\t// The writer came back with nothing. It says so on the card, and on\n\t\t\t\t// the stage there is no card — so say it where the loader is.\n\t\t\t\tif (STAGE_UI) pushError('the brief could not be written — try sending that again');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// The whole chain is spent, not just the round with the button. Left\n\t\t\t// unsent, the earlier rounds of this shot were being read back as\n\t\t\t// \"agreed so far\" into the NEXT shot's first round.\n\t\t\tspendConfirmChain();\n\t\t\tc.cardId = card.id;\n\t\t\t// Stop and say so. A brief the checker rewrote is not the brief that was\n\t\t\t// read back, and letting it shoot anyway would mean the sentence they\n\t\t\t// approved was not the order — which is the one thing this whole layer\n\t\t\t// exists to prevent.\n\t\t\t//\n\t\t\t// The stage does not stop here, because it has nowhere to stop TO. This\n\t\t\t// gate guards a promise the stage never made: there, the read-back is\n\t\t\t// accepted for you and never shown, so there is no approved sentence\n\t\t\t// for a rewritten brief to differ from. Stopping anyway left the loader\n\t\t\t// sitting at 97% with no card, no message and no way forward — a render\n\t\t\t// that looks like it is running and is not.\n\t\t\t//\n\t\t\t// The checker's changes are already IN the brief either way; what is\n\t\t\t// lost is being told about them, and that is the trade the stage was\n\t\t\t// asked for.\n\t\t\tif (card.shot.fixed?.length && !STAGE_UI) {\n\t\t\t\tc.fixed = card.shot.fixed;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tc.phase = 'starting';\n\t\t\tawait startFromCard(card);\n\t\t} catch (e) {\n\t\t\tc.error = `that could not be started — ${e instanceof Error ? e.message : e}`;\n\t\t\t// Onto the stage as well. The card carries this in the transcript, and\n\t\t\t// the stage does not draw cards — so without this the failure was\n\t\t\t// written somewhere nobody was looking while the loader kept counting.\n\t\t\tif (STAGE_UI) pushError(c.error);\n\t\t} finally {\n\t\t\tif (!handedOff) {\n\t\t\t\tshotBusy[itemId] = false;\n\t\t\t\tc.phase = undefined;\n\t\t\t\tc.busySince = undefined;\n\t\t\t\t// A press that never reached a GPU gives the surface back.\n\t\t\t\t//\n\t\t\t\t// The clock is started by the press and was only ever stopped by a clip\n\t\t\t\t// arriving, so every way this can fail — the writer returning nothing,\n\t\t\t\t// the checker's changes, a continuation whose clip is no longer the one\n\t\t\t\t// being continued, a throw — left the loader running over nothing. The\n\t\t\t\t// error said so and the loader disagreed with it, and dismissing the\n\t\t\t\t// error handed back a stage counting toward a render that was never\n\t\t\t\t// dispatched. Read from the card rather than tracked through the three\n\t\t\t\t// exits: what matters is whether a clip is actually on its way.\n\t\t\t\tconst started = c.cardId ? chat.find((x) => x.id === c.cardId) : undefined;\n\t\t\t\tif (STAGE_UI && !started?.shot?.launched) {\n\t\t\t\t\tstageStartedAt = 0;\n\t\t\t\t\tstageWaitFrom = '';\n\t\t\t\t\tstageWaitBlurUrl = '';\n\t\t\t\t}\n\t\t\t}\n\t\t\tpersist();\n\t\t}\n\t}\n\n\t/** What the server keeps about a press it is carrying — generate.server.ts. */\n\ttype ServerGen = {\n\t\tround: string;\n\t\tslug: string;\n\t\tkind: 'direct' | 'continue';\n\t\tn: number;\n\t\tstate: 'writing' | 'launching' | 'launched' | 'failed' | 'stopped' | 'refused';\n\t\tstartedAt: number;\n\t\tlaunchedAt?: number;\n\t\trequest?: string;\n\t\tshot?: ChatItem['shot'];\n\t\tworkspaceId?: string;\n\t\tbatch?: string;\n\t\truns?: ServerRun[];\n\t\tneed?: number;\n\t\terror?: string;\n\t};\n\n\t/** The press, handed to the server whole: the brief, the angles, the launch.\n\t *\n\t *  The tab used to carry the minute between the press and the charge, and a\n\t *  tab that went somewhere else in that minute took the clip with it — or,\n\t *  coming back, found the button live over a launch its own old code was\n\t *  still making, and paid for it twice (2026-09-21: two launches of one\n\t *  request thirty seconds apart). Nobody watches a loader for six minutes, so\n\t *  the server does the work and this tab, or the next one, only follows it.\n\t *\n\t *  Everything the writer and the launch are given is decided here, exactly as\n\t *  shotFromRequest, continueFromRequest, launchShot and renderBatch decide it;\n\t *  only who carries it has changed. */\n\tasync function generateOnServer(\n\t\titemId: string,\n\t\tc: NonNullable<ChatItem['confirm']>,\n\t\trequest: string\n\t): Promise<boolean> {\n\t\tconst cont = c.continues ? continuing : null;\n\t\tconst prior = cont ? logRow[cont.workspace] : undefined;\n\t\t// The frame follows the clip being continued, where its size is on record —\n\t\t// two pieces at different sizes cannot be joined. Otherwise the composer's.\n\t\tlet resolution: ResKey = wantRes;\n\t\tlet orientation: 'portrait' | 'landscape' = wantOrientation;\n\t\tif (cont && prior?.width && prior?.height) {\n\t\t\tconst longest = Math.max(prior.width, prior.height);\n\t\t\tresolution = RES_KEYS.find((k) => RESOLUTIONS[k].long === longest) ?? wantRes;\n\t\t\torientation = prior.width >= prior.height ? 'landscape' : 'portrait';\n\t\t}\n\t\tconst frame = frameFor(resolution, orientation);\n\t\tconst pinned = !!c.continues && c.continues.pinned !== false;\n\t\tconst body = {\n\t\t\tsession: sessionSlug,\n\t\t\tround: itemId,\n\t\t\trequest,\n\t\t\tpin: cont\n\t\t\t\t? {\n\t\t\t\t\t\tseconds: wantSeconds,\n\t\t\t\t\t\torientation: wantOrientation,\n\t\t\t\t\t\tcharacter: cont.characterName,\n\t\t\t\t\t\tvoice: characters.find((x) => x.id === cont.characterId)?.voice,\n\t\t\t\t\t\tlocation: cont.locationName,\n\t\t\t\t\t\tcontinues: {\n\t\t\t\t\t\t\tpriorPrompt: prior?.prompt,\n\t\t\t\t\t\t\tpriorLoras: prior?.launched,\n\t\t\t\t\t\t\tpinned: pinSeam,\n\t\t\t\t\t\t\tplatesFromClip: cont.exact === false\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t: {\n\t\t\t\t\t\tseconds: wantSeconds,\n\t\t\t\t\t\torientation: wantOrientation,\n\t\t\t\t\t\tcharacter: chosenCharacter?.name,\n\t\t\t\t\t\tvoice: chosenCharacter?.voice,\n\t\t\t\t\t\tlocation: chosenLocation?.description || chosenLocation?.name\n\t\t\t\t\t},\n\t\t\tlaunch: {\n\t\t\t\tkind: cont ? 'continue' : 'direct',\n\t\t\t\ttakes,\n\t\t\t\t// A pinned seam has one frame to start from, so one angle.\n\t\t\t\tangles: pinned ? 1 : angles,\n\t\t\t\twidth: frame.width,\n\t\t\t\theight: frame.height,\n\t\t\t\tresolution,\n\t\t\t\torientation,\n\t\t\t\t...(cont\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tcharacterId: cont.characterId,\n\t\t\t\t\t\t\tcharacterName: cont.characterName,\n\t\t\t\t\t\t\tlocationId: cont.locationId,\n\t\t\t\t\t\t\tlocationName: cont.locationName,\n\t\t\t\t\t\t\tcontinues: { ...cont },\n\t\t\t\t\t\t\tpriorWorkspace: cont.workspace,\n\t\t\t\t\t\t\tpriorArtifact: cont.artifact,\n\t\t\t\t\t\t\tpriorFile: cont.file,\n\t\t\t\t\t\t\tpinned: pinSeam\n\t\t\t\t\t\t}\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tcharacterId: chosenCharacter?.id,\n\t\t\t\t\t\t\tcharacterName: chosenCharacter?.name,\n\t\t\t\t\t\t\tlocationId: chosenLocation?.id,\n\t\t\t\t\t\t\tlocationName: chosenLocation?.name,\n\t\t\t\t\t\t\t// The 4-GPU path while it is measured (RAT-502): asked for with\n\t\t\t\t\t\t\t// ?fast=1 in the address, honoured only for the studio's own\n\t\t\t\t\t\t\t// operators. No control on the page until it has earned one.\n\t\t\t\t\t\t\t...(page.url.searchParams.get('fast') === '1' ? { fast: true } : {})\n\t\t\t\t\t\t})\n\t\t\t}\n\t\t};\n\t\tlet r: Reply<{ ok: boolean; gen?: ServerGen; busy?: boolean; error?: string }>;\n\t\ttry {\n\t\t\tconst res = await fetch('/studio/api/generate', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify(body)\n\t\t\t});\n\t\t\tr = await readReply(res);\n\t\t} catch (e) {\n\t\t\tr = { ok: false, error: whyFailed(e) };\n\t\t}\n\t\tconst g = r.ok ? r.body.gen : undefined;\n\t\tif (!r.ok || !g) {\n\t\t\tc.error = r.ok ? r.body.error || 'that could not be started — try again' : r.error;\n\t\t\tif (STAGE_UI) pushError(c.error);\n\t\t\treturn false;\n\t\t}\n\t\t// A press already on its way for this session — this tab's, after a detour,\n\t\t// or another tab's. It is followed, not repeated.\n\t\tconst round = chat.find((x) => x.id === g.round)?.confirm ?? c;\n\t\tround.gen = g.slug;\n\t\tround.phase = 'writing';\n\t\tround.busySince = g.startedAt;\n\t\tif (round !== c) {\n\t\t\tshotBusy[itemId] = false;\n\t\t\tc.phase = undefined;\n\t\t\tc.busySince = undefined;\n\t\t}\n\t\tstageStartedAt = g.startedAt;\n\t\tpersist();\n\t\twatchGeneration(sessionSlug, g);\n\t\treturn true;\n\t}\n\n\tfunction unwatchGeneration() {\n\t\tgenFor = '';\n\t\tgenSlug = '';\n\t\tif (genTimer) clearTimeout(genTimer);\n\t\tgenTimer = null;\n\t}\n\n\t/** Follow a press until it has launched, or has not. Only while its session\n\t *  is the one on screen: leave and the server carries on regardless, and the\n\t *  session asks again when it is opened (resumeGeneration). */\n\tfunction watchGeneration(session: string, now?: ServerGen) {\n\t\tif (guest) return;\n\t\tunwatchGeneration();\n\t\tgenFor = session;\n\t\tif (now) {\n\t\t\tgenSlug = now.slug;\n\t\t\tgenState = now.state === 'launching' ? 'launching' : 'writing';\n\t\t}\n\t\tconst look = async () => {\n\t\t\tgenTimer = null;\n\t\t\tif (genFor !== session || sessionSlug !== session) return;\n\t\t\tlet g: ServerGen | null = null;\n\t\t\ttry {\n\t\t\t\tconst res = await fetch(`/studio/api/generate?session=${encodeURIComponent(session)}`);\n\t\t\t\tif (res.ok) g = ((await res.json()) as { gen?: ServerGen | null }).gen ?? null;\n\t\t\t} catch {\n\t\t\t\t/* a look that failed is only a late one */\n\t\t\t}\n\t\t\tif (genFor !== session || sessionSlug !== session) return;\n\t\t\tif (g && (g.state === 'writing' || g.state === 'launching')) {\n\t\t\t\tgenSlug = g.slug;\n\t\t\t\tgenState = g.state;\n\t\t\t\tgenTimer = setTimeout(look, 2500);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tunwatchGeneration();\n\t\t\tif (g) landGeneration(g);\n\t\t};\n\t\tgenTimer = setTimeout(look, now ? 2500 : 0);\n\t}\n\n\t/** A press has ended, one way or the other. Idempotent: a look that finds a\n\t *  launch this tab has already drawn does nothing, because the second tab, the\n\t *  reload and the detour all end up here with the same record. */\n\tfunction landGeneration(g: ServerGen) {\n\t\tconst round = chat.find((x) => x.id === g.round);\n\t\tconst c = round?.confirm;\n\t\tif (!round || !c) {\n\t\t\t// A round this tab does not have — another tab's press in the same\n\t\t\t// session, followed here because it was already on its way. Its clip is\n\t\t\t// still this session's clip; anything else only ends the wait.\n\t\t\tif (g.state === 'launched' && g.workspaceId && renderWs !== g.workspaceId) {\n\t\t\t\trenderIsSheet = false;\n\t\t\t\trenderWs = g.workspaceId;\n\t\t\t\tstartedAt = g.launchedAt ?? Date.now();\n\t\t\t\tshootsAnnounced = true;\n\t\t\t\tstartPolling();\n\t\t\t\tpersist();\n\t\t\t} else if (g.state !== 'launched') {\n\t\t\t\tendClipWait();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tconst card = c.cardId ? chat.find((x) => x.id === c.cardId) : undefined;\n\t\tif (card?.shot?.launched) {\n\t\t\t// Launched, and then the render died — the server's word, not a guess\n\t\t\t// (endLaunchedGen). This tab's own poll normally gets there first; a tab\n\t\t\t// that missed it still stops waiting.\n\t\t\tif (g.state === 'failed' && g.workspaceId && renderWs === g.workspaceId) {\n\t\t\t\tstopPolling();\n\t\t\t\tpushError(`A shooting step stalled: ${g.error || 'the render did not finish'}`);\n\t\t\t\tendDeadClip();\n\t\t\t\tvoid refreshTokens();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tconst waiting = c.gen === g.slug;\n\t\tc.gen = undefined;\n\t\tc.phase = undefined;\n\t\tc.busySince = undefined;\n\t\tshotBusy[round.id] = false;\n\n\t\tif (g.state === 'launched' && g.shot) {\n\t\t\tconst shot = { ...g.shot, launched: true };\n\t\t\tconst drawn = pushItem({ who: 'studio', kind: 'shot', shot });\n\t\t\tc.cardId = drawn.id;\n\t\t\tspendConfirmChain();\n\t\t\tif (g.request) lastRequest = g.request;\n\t\t\tif (shot.continues && continuing?.workspace === shot.continues.workspace) continuing = null;\n\t\t\tif (g.batch) {\n\t\t\t\t// The single-render slot is not a batch's — see renderBatch.\n\t\t\t\trenderWs = '';\n\t\t\t\tstartedAt = 0;\n\t\t\t\trenderIsSheet = false;\n\t\t\t\tpushItem({\n\t\t\t\t\twho: 'studio',\n\t\t\t\t\tkind: 'takes',\n\t\t\t\t\ttakes: { batch: g.batch, runs: (g.runs ?? []).map(takeRun) }\n\t\t\t\t});\n\t\t\t\twatchBatches();\n\t\t\t} else if (g.workspaceId) {\n\t\t\t\trenderIsSheet = false;\n\t\t\t\trenderWs = g.workspaceId;\n\t\t\t\tstartedAt = g.launchedAt ?? Date.now();\n\t\t\t\tshootsAnnounced = true;\n\t\t\t\tif (shot.continues) pushStudio(`Continuing — ${shot.seconds}s more.`);\n\t\t\t\tstartPolling();\n\t\t\t}\n\t\t\tvoid refreshTokens();\n\t\t\t// The launch took the staged pictures with it; the chips go too.\n\t\t\tvoid loadRefFiles();\n\t\t\t// A stop that arrived after the charge: paid for, and on its way.\n\t\t\tif (stopWanted) settleStop(ALREADY_STARTED, false);\n\t\t\tpersist();\n\t\t\treturn;\n\t\t}\n\t\t// Not coming. An old ending this round was not waiting for is not news.\n\t\tif (!waiting) return;\n\t\tendClipWait();\n\t\tif (g.state === 'stopped') {\n\t\t\t// Said at the press when the brief was still being written.\n\t\t\tsettleStop(stopNotice === STOPPED_BEFORE_CHARGE ? '' : STOPPED_BEFORE_CHARGE, true);\n\t\t} else if (g.state === 'refused') {\n\t\t\taskForTokens(g.need || sendPrice);\n\t\t} else {\n\t\t\tpushError(g.error || 'the clip could not be started — send it again');\n\t\t}\n\t\tpersist();\n\t}\n\n\t/** Pick up a press this session made before it was last on screen: still on\n\t *  its way — show it and follow it — or ended while nobody looked. */\n\tasync function resumeGeneration(session: string) {\n\t\tif (guest) return;\n\t\tif (!STAGE_UI || !session) return;\n\t\tlet g: ServerGen | null = null;\n\t\ttry {\n\t\t\tconst res = await fetch(`/studio/api/generate?session=${encodeURIComponent(session)}`);\n\t\t\tif (res.ok) g = ((await res.json()) as { gen?: ServerGen | null }).gen ?? null;\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tif (!g || sessionSlug !== session) return;\n\t\tconst round = chat.find((x) => x.id === g.round);\n\t\tif (!round?.confirm) return;\n\t\tif (g.state === 'writing' || g.state === 'launching') {\n\t\t\tround.confirm.gen = g.slug;\n\t\t\tround.confirm.phase = 'writing';\n\t\t\tround.confirm.busySince = g.startedAt;\n\t\t\tshotBusy[round.id] = true;\n\t\t\tstageStartedAt = g.startedAt;\n\t\t\tstageWaitFrom = stageNewest?.id ?? '';\n\t\t\tstageWaitBlurUrl =\n\t\t\t\tg.kind === 'continue' ? (stageContinuable?.artifact?.files?.[0]?.url ?? '') : '';\n\t\t\twatchGeneration(session, g);\n\t\t\treturn;\n\t\t}\n\t\tlandGeneration(g);\n\t}\n\n\tasync function shotFromRequest(request: string): Promise<ChatItem | null> {\n\t\t// Pinned from the composer, so the brief arrives written for this length\n\t\t// and this frame rather than being rewritten into them afterwards.\n\t\tconst shot = await callShotPrompt(request, {\n\t\t\tseconds: wantSeconds,\n\t\t\torientation: wantOrientation,\n\t\t\tcharacter: chosenCharacter?.name,\n\t\t\t// Carried so the writer names this person's voice rather than inventing\n\t\t\t// one. Without it every clip is an independent roll and the same woman\n\t\t\t// comes back sounding like somebody else two shots later.\n\t\t\tvoice: chosenCharacter?.voice,\n\t\t\t// The description, not the label.\n\t\t\t//\n\t\t\t// The writer's only knowledge of the place is this string, and a plate\n\t\t\t// uploaded without a description falls back to its filename — so it was\n\t\t\t// being told \"the location IMG 2482\" and inventing the room from the act\n\t\t\t// instead. A scene that needs no furniture survives that; one that needs\n\t\t\t// a surface gets a mattress conjured into a dining room.\n\t\t\tlocation: chosenLocation?.description || chosenLocation?.name\n\t\t});\n\t\tif (!shot) return null;\n\t\tlastRequest = request;\n\t\tshot.resolution = wantRes;\n\t\t// Stamped onto the card rather than read at launch: the brief was written\n\t\t// for this person, so a card that renders with a different one — because\n\t\t// the picker moved while you were reading — would be a brief describing\n\t\t// somebody who is not in the shot.\n\t\tif (chosenCharacter) {\n\t\t\tshot.characterId = chosenCharacter.id;\n\t\t\tshot.characterName = chosenCharacter.name;\n\t\t}\n\t\tif (chosenLocation) {\n\t\t\tshot.locationId = chosenLocation.id;\n\t\t\tshot.locationName = chosenLocation.name;\n\t\t}\n\t\treturn pushItem({ who: 'studio', kind: 'shot', shot });\n\t}\n\n\t/** Put the composer into continuation mode for one clip.\n\t *\n\t *  Nothing is rendered here — this only changes what the next message means.\n\t *  The character and the location come from the render log rather than the\n\t *  pickers: a continuation is of a particular clip, and swapping either would\n\t *  make it a different scene wearing the same seam. */\n\tfunction startContinue(item: ChatItem) {\n\t\tconst ws = item.artifact?.workspace ?? '';\n\t\tconst info = contInfo(ws);\n\t\t// A joined scene has no artifact id of its own — it is a file assembled\n\t\t// here out of clips that each have one. So continue its LAST PART, which\n\t\t// is what continuing a scene means anyway: the scene ends where that clip\n\t\t// ends, and the workflow reads its reference video from the start, so\n\t\t// handing it a 25-second assembly would ask the model to guess what\n\t\t// follows second 25 from the first few seconds of it.\n\t\t//\n\t\t// Until now the button was offered on the scene card and answered with an\n\t\t// error, which is a wall placed exactly where somebody would carry on.\n\t\tconst part = item.artifact?.id\n\t\t\t? { artifact: item.artifact.id, file: item.artifact.files?.[0]?.name ?? '' }\n\t\t\t: (() => {\n\t\t\t\t\tconst chain = chainOf(ws);\n\t\t\t\t\tconst last = chain[chain.length - 1];\n\t\t\t\t\treturn last ? { artifact: last.artifact, file: last.file } : null;\n\t\t\t\t})();\n\t\tif (!info.ok || !info.row || !part?.artifact || !part.file) {\n\t\t\tpushError(info.why || 'this clip cannot be continued');\n\t\t\treturn;\n\t\t}\n\t\tcontinuing = {\n\t\t\tworkspace: ws,\n\t\t\tartifact: part.artifact,\n\t\t\tfile: part.file,\n\t\t\t// Either may be absent, and that is no longer a refusal: the launch\n\t\t\t// cuts the missing plate out of the clip. Passed through as undefined\n\t\t\t// rather than coerced, so the server can tell \"none was kept\" from\n\t\t\t// \"this one\".\n\t\t\tcharacterId: info.row.characterId,\n\t\t\tlocationId: info.row.locationId,\n\t\t\tcharacterName: info.row.characterName,\n\t\t\tlocationName: info.row.locationName,\n\t\t\texact: info.exact\n\t\t};\n\t\tpinSeam = true;\n\t\t// A continuation is a new order; whatever was being read back before it is\n\t\t// not its history.\n\t\tspendConfirmChain();\n\t\t// Angles cannot apply to a pinned seam, and a chip left reading \"2 camera\n\t\t// angles\" with the row inert is a control the user cannot put down. Reset it\n\t\t// here: a continuation starts as one clip per version, and raising it again\n\t\t// is one tap once the seam is free.\n\t\tangles = 1;\n\t\tsaveSetup();\n\t\twantTarget = 'clip';\n\t\tcomposer?.focus();\n\t}\n\n\t/** Write the continuation brief and offer it, exactly as a clip is offered. */\n\tasync function continueFromRequest(request: string): Promise<ChatItem | null> {\n\t\tconst c = continuing;\n\t\tif (!c) return null;\n\t\tconst prior = logRow[c.workspace];\n\t\t// The length is yours, and only the length.\n\t\t//\n\t\t// This followed the prior clip for a while, on the same argument that makes\n\t\t// the frame follow it. That argument does not reach this far: two pieces at\n\t\t// different sizes cannot be concatenated, but two pieces of different\n\t\t// LENGTHS join perfectly well. Deciding how long the next beat runs is a\n\t\t// director's choice and taking it away was a mistake.\n\t\t//\n\t\t// What actually went wrong was never the source of the number, it was that\n\t\t// there were two of them — the card's and the composer's — and only one was\n\t\t// on screen. They are kept in step now, at setShotSeconds.\n\t\tconst shot = await callShotPrompt(request, {\n\t\t\tseconds: wantSeconds,\n\t\t\torientation: wantOrientation,\n\t\t\tcharacter: c.characterName,\n\t\t\t// From the character the clip was shot with, not the composer's current\n\t\t\t// pick — a continuation is of a particular clip, and the person in it is\n\t\t\t// whoever was in it. The continuation writer is told to carry the prior\n\t\t\t// brief's voice sentence across; this pins it for the case where that\n\t\t\t// brief predates the rule and names none.\n\t\t\tvoice: characters.find((x) => x.id === c.characterId)?.voice,\n\t\t\tlocation: c.locationName,\n\t\t\tcontinues: {\n\t\t\t\tpriorPrompt: prior?.prompt,\n\t\t\t\tpriorLoras: prior?.launched,\n\t\t\t\tpinned: pinSeam,\n\t\t\t\t// So the writer knows what the two plates are. A frame of the scene\n\t\t\t\t// and a sheet on a grey backdrop want opposite retention rules, and\n\t\t\t\t// getting that backwards throws away the room.\n\t\t\t\tplatesFromClip: c.exact === false\n\t\t\t}\n\t\t});\n\t\tif (!shot) return null;\n\t\tlastRequest = request;\n\t\t// The frame follows the clip being continued, not the composer: two pieces\n\t\t// at different sizes cannot be joined, and joining is the whole point.\n\t\t//\n\t\t// This said exactly that and then read the composer anyway, so a clip whose\n\t\t// size was set on its own card was continued at whatever the composer still\n\t\t// had — and the join refused the pair it had just offered to make, after\n\t\t// both renders were paid for. The composer is the fallback now, for a prior\n\t\t// row that has no size recorded.\n\t\tif (prior?.width && prior?.height) {\n\t\t\tconst longest = Math.max(prior.width, prior.height);\n\t\t\tshot.resolution = RES_KEYS.find((k) => RESOLUTIONS[k].long === longest) ?? wantRes;\n\t\t\tshot.orientation = prior.width >= prior.height ? 'landscape' : 'portrait';\n\t\t} else {\n\t\t\tshot.resolution = wantRes;\n\t\t}\n\t\tshot.continues = { ...c, pinned: pinSeam };\n\t\tshot.characterId = c.characterId;\n\t\tshot.characterName = c.characterName;\n\t\tshot.locationId = c.locationId;\n\t\tshot.locationName = c.locationName;\n\t\tcontinuing = null;\n\t\treturn pushItem({ who: 'studio', kind: 'shot', shot });\n\t}\n\n\t/** Glue every clip in this one's chain into a single scene. */\n\tasync function joinScene(ws: string) {\n\t\tif (joining[ws]) return;\n\t\tconst parts = chainOf(ws);\n\t\tif (parts.length < 2) {\n\t\t\tpushError('there is only one clip here — nothing to join yet');\n\t\t\treturn;\n\t\t}\n\t\tjoining[ws] = true;\n\t\ttry {\n\t\t\tconst res = await fetch('/studio/api/join', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({ parts })\n\t\t\t});\n\t\t\tconst r = (await res.json()) as {\n\t\t\t\tok?: boolean;\n\t\t\t\terror?: string;\n\t\t\t\t/** What a SvelteKit error() puts the reason in. */\n\t\t\t\tmessage?: string;\n\t\t\t\turl?: string;\n\t\t\t\tparts?: number;\n\t\t\t\tseconds?: number;\n\t\t\t};\n\t\t\tif (!r.ok || !r.url) {\n\t\t\t\tpushError(r.error || r.message || 'the scene could not be assembled');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpushItem({\n\t\t\t\twho: 'studio',\n\t\t\t\tkind: 'clips',\n\t\t\t\ttext: `The whole scene — ${r.parts} clips, ${r.seconds}s.`,\n\t\t\t\tartifact: {\n\t\t\t\t\tkey: 'scene',\n\t\t\t\t\ttitle: 'The whole scene',\n\t\t\t\t\ttaskId: '',\n\t\t\t\t\tfiles: [{ name: 'scene.mp4', url: r.url }],\n\t\t\t\t\tworkspace: ws\n\t\t\t\t}\n\t\t\t});\n\t\t\tpersist();\n\t\t} catch (e) {\n\t\t\tpushError(`the scene could not be assembled: ${e}`);\n\t\t} finally {\n\t\t\tjoining[ws] = false;\n\t\t}\n\t}\n\n\t/** The same first-name rule the store uses, applied here so the card shows the\n\t *  name it is about to be saved under rather than an empty box. */\n\tfunction firstWords(description: string, kind: 'character' | 'location'): string {\n\t\tconst words = description\n\t\t\t.replace(/[\\n\\r]+/g, ' ')\n\t\t\t// A character description is required to open with the workflow's own\n\t\t\t// framing — the sheet writer is told to begin exactly \"A photography of\n\t\t\t// full body of\" — so the first six words are that formula every single\n\t\t\t// time, and every character was offered the same name. Drop it and the\n\t\t\t// six words that follow are the person.\n\t\t\t.replace(/^\\s*a\\s+photograph(?:y|)\\s+of\\s+(?:a\\s+)?full\\s+body\\s+of\\s+/i, '')\n\t\t\t.split(/\\s+/)\n\t\t\t.filter(Boolean)\n\t\t\t.slice(0, 6)\n\t\t\t.join(' ')\n\t\t\t.slice(0, 60)\n\t\t\t.trim();\n\t\treturn words || (kind === 'character' ? 'Character' : 'Location');\n\t}\n\n\t// --- sheets: a character or a location, kept and reused ------------------------\n\n\t/** Every sheet this machine has made, newest first. Loaded once on mount and\n\t *  refreshed by every write, so a picker never has to ask. */\n\tlet sheets = $state<StoredSheet[]>([]);\n\tconst characters = $derived(sheets.filter((s) => s.kind === 'character'));\n\tconst locations = $derived(sheets.filter((s) => s.kind === 'location'));\n\t/** The chosen character, or undefined once it has been deleted from under us.\n\t *  Derived rather than stored so a removed sheet cannot be launched with. */\n\tconst chosenCharacter = $derived(characters.find((c) => c.id === wantCharacter));\n\tconst chosenLocation = $derived(locations.find((l) => l.id === wantLocation));\n\tlet sheetBusy = $state<Record<string, boolean>>({});\n\n\t// The voice — its presets, its draft and its save — moved down beside\n\t// `stageSubject`, which it now reads. Declared here it referred forward to a\n\t// binding four thousand lines below it, which works only because a $derived\n\t// body is lazy; a reader should not have to know that to follow it.\n\n\t/** Give the character a name.\n\t *\n\t *  Saved on blur, like the voice, and for the same reason: one field, one\n\t *  line, and a button to confirm a name is a button nobody needs. */\n\tasync function renameCharacter(id: string, next: string) {\n\t\tconst row = sheets.find((x) => x.id === id);\n\t\tconst name = next.trim().slice(0, 80);\n\t\tif (!row || !name || name === row.name) return;\n\t\ttry {\n\t\t\tconst res = await fetch('/studio/api/sheet', {\n\t\t\t\tmethod: 'PATCH',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({ id, name })\n\t\t\t});\n\t\t\tconst r = (await res.json()) as { ok?: boolean; sheets?: StoredSheet[]; error?: string };\n\t\t\tif (r.ok && r.sheets) sheets = r.sheets;\n\t\t\telse pushError(r.error || 'the name could not be saved');\n\t\t} catch (e) {\n\t\t\tpushError(`the name could not be saved — ${e}`);\n\t\t}\n\t}\n\n\tasync function loadSheets() {\n\t\tif (guest) return;\n\t\ttry {\n\t\t\tconst res = await fetch('/studio/api/sheet');\n\t\t\tif (!res.ok) return;\n\t\t\tconst r = (await res.json()) as { ok?: boolean; sheets?: StoredSheet[] };\n\t\t\tif (r.sheets) sheets = r.sheets;\n\t\t} catch {\n\t\t\t/* the list is a convenience; failing to load it must not break the page */\n\t\t}\n\t}\n\n\t/** Render a sheet from a plain-English description.\n\t *\n\t *  No writer stands between the two. Both sheet workflows take a description\n\t *  rather than a structured prompt — their own port notes say so in as many\n\t *  words — so a writer here would only have prose to paraphrase, and every\n\t *  paraphrase is a chance to lose the detail you actually cared about.\n\t */\n\t/** The character currently being worked on: the description that produced the\n\t *  last preview and the seed it was rendered with.\n\t *\n\t *  Both travel forward. The description so the next message can refine it\n\t *  rather than start over, and the seed so refining changes the person because\n\t *  of the words rather than because the noise moved — and so the full sheet,\n\t *  when you ask for it, is a turnaround of the face you approved. */\n\tlet currentCharacter = $state<{ description: string; seed: number } | null>(null);\n\n\tasync function sheetFromRequest(request: string, kind: 'character' | 'location') {\n\t\ttry {\n\t\t\t// The writer is back in front of a character, and it is worth being clear\n\t\t\t// about what it buys, because it was taken out and put back in one\n\t\t\t// afternoon. It no longer exists to translate — the audience is English\n\t\t\t// — it exists because a four-word description leaves the model to invent\n\t\t\t// the rest at random, and a random face is not something anyone can\n\t\t\t// refine. It fills identity gaps only, in a few words, and says which\n\t\t\t// ones it filled.\n\t\t\t//\n\t\t\t// It costs about ten seconds, measured. That is more than the five it\n\t\t\t// cost when it only translated, and the difference is output length\n\t\t\t// rather than the model: grok-4.5 and grok-fast came in at the same\n\t\t\t// number, so this sits on grok-fast.\n\t\t\tconst res = await fetch('/studio/api/sheetprompt', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\trequest,\n\t\t\t\t\tkind,\n\t\t\t\t\t// A refinement merges into the description already on screen\n\t\t\t\t\t// rather than starting over from the few words you just typed.\n\t\t\t\t\t...(currentCharacter ? { previous: currentCharacter.description } : {})\n\t\t\t\t})\n\t\t\t});\n\t\t\tconst r = (await res.json()) as {\n\t\t\t\tok?: boolean;\n\t\t\t\tsheet?: {\n\t\t\t\t\tkind: 'character' | 'location';\n\t\t\t\t\tdescription: string;\n\t\t\t\t\tvoice?: string;\n\t\t\t\t\twhy?: string;\n\t\t\t\t};\n\t\t\t\terror?: string;\n\t\t\t};\n\t\t\tif (!r.ok || !r.sheet) {\n\t\t\t\tpushError(r.error || 'The description could not be prepared.');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Neither stops to be approved any more. A preview costs a third of a\n\t\t\t// sheet and is the same picture the sheet would build on, so it is a\n\t\t\t// better thing to react to than a paragraph — you look at it and say\n\t\t\t// what to change.\n\t\t\tconst seed = currentCharacter?.seed ?? Math.floor(Math.random() * 1_000_000_000);\n\t\t\tcurrentCharacter = { description: r.sheet.description, seed };\n\t\t\tawait previewSubject(kind, r.sheet.description, seed, r.sheet.why, r.sheet.voice);\n\t\t} catch (e) {\n\t\t\tpushError(String(e));\n\t\t}\n\t}\n\n\t/** A character preview, rendered without the harness.\n\t *\n\t *  The full sheet still goes through launchSheetRender and a workspace; only\n\t *  this one skips it, because only this one has nothing for the harness to\n\t *  decide. Measured, that is 112 seconds against 150.\n\t *\n\t *  The job is detached server-side and polled here, rather than the request\n\t *  being held open for two minutes: a reload during a render would otherwise\n\t *  abandon GPU time that is already being paid for.\n\t */\n\tlet previewBusy = $state(false);\n\n\tasync function previewSubject(\n\t\tkind: 'character' | 'location',\n\t\tdescription: string,\n\t\tseed: number,\n\t\twhy?: string,\n\t\t/** Written by the same call that wrote the description, and carried here so\n\t\t *  it is on the card before the picture is — keeping the character then\n\t\t *  files the voice with the face rather than leaving the field empty for\n\t\t *  somebody to fill in by hand. */\n\t\tvoice?: string\n\t) {\n\t\tif (previewBusy) return;\n\t\tpreviewBusy = true;\n\t\t// The card first, so the stage has the subject the moment the button is\n\t\t// pressed rather than half a minute later.\n\t\tconst card = pushItem({\n\t\t\twho: 'studio',\n\t\t\tkind: 'sheet',\n\t\t\tsheet: { kind, stage: 'anchor', description, why, seed, voice, launched: true }\n\t\t});\n\t\ttry {\n\t\t\t// Drawn, not rendered.\n\t\t\t//\n\t\t\t// This was a GPU render of one picture, then a second GPU render of a\n\t\t\t// six-second orbit that six frames were cut out of: between them the\n\t\t\t// better part of ten minutes, of which twelve seconds was drawing and the\n\t\t\t// rest was containers starting and forty-five gigabytes of weights being\n\t\t\t// read. A sheet is a reference — the clip workflow takes it as a plain\n\t\t\t// picture and an uploaded photograph has always gone down the same road —\n\t\t\t// so nothing required it to come from the model that makes the clip.\n\t\t\t//\n\t\t\t// One call, four views, about thirty seconds. The subject is kept by the\n\t\t\t// same route that draws it, so there is no second step and nothing to\n\t\t\t// approve: it exists when it appears.\n\t\t\tconst res = await fetch('/studio/api/subject', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tkind,\n\t\t\t\t\tdescription,\n\t\t\t\t\tseed,\n\t\t\t\t\t...(voice ? { voice } : {}),\n\t\t\t\t\t...(runSlug ? { sessionSlug: runSlug } : {})\n\t\t\t\t})\n\t\t\t});\n\t\t\tconst r = (await res.json()) as {\n\t\t\t\tok?: boolean;\n\t\t\t\tsheet?: StoredSheet;\n\t\t\t\tsheets?: StoredSheet[];\n\t\t\t\terror?: string;\n\t\t\t};\n\t\t\tif (!r.ok || !r.sheet) {\n\t\t\t\tcard.kind = 'error';\n\t\t\t\tcard.text = r.error || 'The subject could not be drawn.';\n\t\t\t\tpersist();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (card.sheet) {\n\t\t\t\tcard.sheet.id = r.sheet.id;\n\t\t\t\tcard.sheet.url = `/studio/api/sheet/img/${r.sheet.id}`;\n\t\t\t\tcard.sheet.name = r.sheet.name;\n\t\t\t}\n\t\t\tif (r.sheets) sheets = r.sheets;\n\t\t\t// Cast, because that is what you asked for by describing them.\n\t\t\tif (kind === 'character') wantCharacter = r.sheet.id;\n\t\t\telse wantLocation = r.sheet.id;\n\t\t\tsaveSetup();\n\t\t\tpersist();\n\t\t} catch (e) {\n\t\t\tcard.kind = 'error';\n\t\t\tcard.text = String(e);\n\t\t} finally {\n\t\t\tpreviewBusy = false;\n\t\t}\n\t}\n\n\t/** Watch a character preview to its end. Separate from starting one, because\n\t *  a reload has to be able to do the second half without the first. */\n\tasync function followPreview(card: ChatItem, job: string) {\n\t\t{\n\t\t\tconst started = Date.now();\n\t\t\tfor (;;) {\n\t\t\t\tif (Date.now() - started > 10 * 60 * 1000) {\n\t\t\t\t\tcard.kind = 'error';\n\t\t\t\t\tcard.text = 'The preview did not finish within ten minutes.';\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait new Promise((r2) => setTimeout(r2, 2500));\n\t\t\t\tlet st: { ok?: boolean; phase?: string; url?: string; error?: string; elapsedSec?: number };\n\t\t\t\ttry {\n\t\t\t\t\tconst p = await fetch(`/studio/api/anchor?job=${encodeURIComponent(job)}`);\n\t\t\t\t\tst = (await p.json()) as typeof st;\n\t\t\t\t} catch {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (st.phase === 'failed') {\n\t\t\t\t\tcard.kind = 'error';\n\t\t\t\t\tcard.text = st.error || 'The preview failed.';\n\t\t\t\t\tpersist();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// A reply with no phase at all is the \"no such preview\" envelope: the\n\t\t\t\t// job is gone, not slow. Treated as still running it would spin until\n\t\t\t\t// the ten minutes were up.\n\t\t\t\tif (!st.phase && st.ok === false) {\n\t\t\t\t\tcard.kind = 'error';\n\t\t\t\t\tcard.text = st.error || 'That preview is gone — render it again.';\n\t\t\t\t\tpersist();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (st.phase !== 'done' || !st.url) continue;\n\t\t\t\tif (card.sheet) {\n\t\t\t\t\tcard.sheet.url = st.url;\n\t\t\t\t\tcard.sheet.job = job;\n\t\t\t\t\tcard.sheet.name = firstWords(card.sheet.description ?? '', card.sheet.kind);\n\t\t\t\t}\n\t\t\t\tpersist();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Re-attach to any preview that was still rendering when this tab last\n\t *  closed. The render did not stop when the page did. */\n\tfunction resumePreviews() {\n\t\tif (guest) return;\n\t\tfor (const c of chat) {\n\t\t\tconst sh = c.sheet;\n\t\t\tif (c.kind !== 'sheet' || !sh) continue;\n\t\t\tif (sh.stage !== 'anchor' || sh.url || !sh.job) continue;\n\t\t\tvoid followPreview(c, sh.job);\n\t\t}\n\t}\n\n\t/** The one road to the GPU for anything sheet-shaped. Returns true when the\n\t *  render actually started. */\n\tasync function launchSheetRender(opts: {\n\t\tkind: 'character' | 'location';\n\t\tdescription: string;\n\t\tstage: 'anchor' | 'sheet';\n\t\tseed: number;\n\t\twhy?: string;\n\t\tvoice?: string;\n\t}): Promise<boolean> {\n\t\t// Guarded the same way a clip launch is, and no more strictly. There is one\n\t\t// render slot and starting a second render retargets it — that is already\n\t\t// true of every clip you launch, so a sheet must not be the one thing that\n\t\t// refuses because a finished run is still on screen.\n\t\tif (renderLaunching) return false;\n\t\trenderLaunching = true;\n\t\ttry {\n\t\t\tconst spec = {\n\t\t\t\tslug: `sheet-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`,\n\t\t\t\tkind: opts.kind,\n\t\t\t\tstage: opts.stage,\n\t\t\t\tdescription: opts.description,\n\t\t\t\tseed: opts.seed\n\t\t\t};\n\t\t\tconst res = await fetch('/studio/api/launch', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({ stage: 'sheet', sheet: spec })\n\t\t\t});\n\t\t\tconst r = (await res.json()) as { ok?: boolean; workspaceId?: string; error?: string };\n\t\t\tif (!r.ok || !r.workspaceId) {\n\t\t\t\tpushError(r.error || 'The sheet render did not start.');\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tpendingSheet = { ...opts };\n\t\t\t// On for the whole of a sheet run — renderInFlight reads it to keep the\n\t\t\t// clip's loader off a render that puts no clip on the stage. It was set and\n\t\t\t// unset on consecutive lines here, which is a no-op, and the loader came\n\t\t\t// back for every sheet.\n\t\t\trenderIsSheet = true;\n\t\t\trenderWs = r.workspaceId;\n\t\t\tstartedAt = Date.now();\n\t\t\tshootsAnnounced = true;\n\t\t\tpushStudio(\n\t\t\t\topts.stage === 'anchor'\n\t\t\t\t\t? 'Rendering one picture of them — about a minute.'\n\t\t\t\t\t: opts.kind === 'character'\n\t\t\t\t\t\t? 'Rendering the full character sheet — six views of the same person.'\n\t\t\t\t\t\t: 'Rendering a location sheet — six views of the same place.'\n\t\t\t);\n\t\t\tpersist();\n\t\t\tstartPolling();\n\t\t\treturn true;\n\t\t} catch (e) {\n\t\t\tpushError(String(e));\n\t\t\treturn false;\n\t\t} finally {\n\t\t\trenderLaunching = false;\n\t\t}\n\t}\n\n\t/** The location card's button: an approved description goes to the GPU. */\n\tasync function renderSheet(itemId: string) {\n\t\tconst item = chat.find((c) => c.id === itemId);\n\t\tif (!item?.sheet || item.sheet.launched || sheetBusy[itemId]) return;\n\t\tconst { kind, description } = item.sheet;\n\t\tif (!description.trim()) return;\n\t\tsheetBusy[itemId] = true;\n\t\ttry {\n\t\t\tconst ok = await launchSheetRender({\n\t\t\t\tkind,\n\t\t\t\tdescription,\n\t\t\t\tstage: 'sheet',\n\t\t\t\tseed: item.sheet.seed ?? Math.floor(Math.random() * 1_000_000_000)\n\t\t\t});\n\t\t\tif (ok) item.sheet.launched = true;\n\t\t} finally {\n\t\t\tsheetBusy[itemId] = false;\n\t\t}\n\t}\n\n\t/** Keep the person, then let the turnaround catch up.\n\t *\n\t *  The save is the whole transaction as far as you are concerned: the\n\t *  character exists, has a face, and is pickable for a clip the moment this\n\t *  returns. The six-view sheet is started behind it and lands minutes later —\n\t *  which is why nothing here waits for it, and why the rail shows which\n\t *  characters are still being drawn. */\n\tasync function saveSubject(itemId: string) {\n\t\tif (guest) {\n\t\t\taskToSignUp('studio_character');\n\t\t\treturn;\n\t\t}\n\t\tconst item = chat.find((c) => c.id === itemId);\n\t\tif (!item?.sheet || item.sheet.id || sheetBusy[itemId]) return;\n\t\tconst { job, description, name, seed, kind, voice } = item.sheet;\n\t\tif (!job) return;\n\t\tsheetBusy[itemId] = true;\n\t\ttry {\n\t\t\tconst res = await fetch('/studio/api/sheet', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\t// The voice as it stands on the card, not as the writer first wrote\n\t\t\t\t// it: the field above the name is editable, and a save that ignored\n\t\t\t\t// an edit would keep a voice the operator had just changed.\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tkind,\n\t\t\t\t\tname,\n\t\t\t\t\tdescription,\n\t\t\t\t\tjob,\n\t\t\t\t\tseed,\n\t\t\t\t\t...(voice ? { voice } : {}),\n\t\t\t\t\t...(runSlug ? { sessionSlug: runSlug } : {})\n\t\t\t\t})\n\t\t\t});\n\t\t\tconst r = (await res.json()) as {\n\t\t\t\tok?: boolean;\n\t\t\t\tsheet?: StoredSheet;\n\t\t\t\tsheets?: StoredSheet[];\n\t\t\t\terror?: string;\n\t\t\t};\n\t\t\tif (!r.ok || !r.sheet) {\n\t\t\t\tpushError(r.error || 'The character could not be saved.');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\titem.sheet.id = r.sheet.id;\n\t\t\tif (r.sheets) sheets = r.sheets;\n\t\t\t// This subject is finished; the next message describes a new one.\n\t\t\tcurrentCharacter = null;\n\t\t\tpersist();\n\n\t\t\t// Fire and forget. It is server-side and outlives this tab, so a failure\n\t\t\t// here costs the turnaround, never the character.\n\t\t\tvoid fetch('/studio/api/sheetfull', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({ id: r.sheet.id })\n\t\t\t})\n\t\t\t\t.then(() => watchSheets())\n\t\t\t\t.catch(() => {});\n\t\t} catch (e) {\n\t\t\tpushError(String(e));\n\t\t} finally {\n\t\t\tsheetBusy[itemId] = false;\n\t\t}\n\t}\n\n\t/** Poll the library while any turnaround is still being drawn, and stop when\n\t *  none is. A spinner that never resolves is worse than no spinner. */\n\tlet sheetWatch: ReturnType<typeof setTimeout> | null = null;\n\n\t/** A sheet being drawn somewhere. It is work in this session even though this\n\t *  tab is polling nothing: the turnaround runs server-side, so the render\n\t *  poller — which is what the sidebar's dot used to key off — sits idle\n\t *  throughout and the row looked asleep while a GPU was busy. */\n\tconst sheetsWorking = $derived(sheets.some((x) => x.sheet?.state === 'rendering'));\n\n\t/** How long the six views take, and how long this one has been going.\n\t *\n\t *  Measured, not guessed: a sheet's workspace id carries the moment it was\n\t *  launched and its file carries the moment it landed, so the wait is the\n\t *  difference — start to picture on screen, which is what a person sits\n\t *  through rather than the GPU's share of it.\n\t *\n\t *  Character turnarounds only: 269, 250, 240 seconds. The first version of\n\t *  this number averaged in the location sheets on disk (273, 277, 350) and\n\t *  came out 20 seconds long. Those are a different job on a different\n\t *  endpoint — `sheetwf`, drawing a still — and this line is only ever shown\n\t *  beside a character.\n\t *\n\t *  Stated as an estimate because it is one — a cold endpoint adds most of a\n\t *  minute — and replaced by plain words once it is past, rather than counting\n\t *  down into the negative and calling that information.\n\t *\n\t *  There are two waits now. That 250 was measured when the views were six\n\t *  frames cut out of a GPU orbit, and almost none of it was drawing: it was a\n\t *  container starting and forty-five gigabytes of weights being read. Four\n\t *  views drawn by Grok take about thirty seconds, and the line went on\n\t *  promising four minutes for them — which does not read as a fast job, it\n\t *  reads as a job that stopped.\n\t *\n\t *  The full sheet still goes to the harness and still takes minutes, so both\n\t *  numbers are alive and something has to choose. A render records which\n\t *  attempt it is at the moment it starts and a drawing records none; that\n\t *  absence is the difference. */\n\tconst TURN_ETA_SEC = 250;\n\tconst DRAW_ETA_SEC = 30;\n\n\tfunction etaSec(sh?: StoredSheet): number {\n\t\treturn sh?.sheet?.attempt === undefined ? DRAW_ETA_SEC : TURN_ETA_SEC;\n\t}\n\n\tfunction since(iso?: string): number {\n\t\tif (!iso) return 0;\n\t\tconst t = Date.parse(iso);\n\t\treturn Number.isFinite(t) ? Math.max(0, Math.round((now - t) / 1000)) : 0;\n\t}\n\n\tfunction clock(sec: number): string {\n\t\tconst m = Math.floor(sec / 60);\n\t\treturn `${m}:${String(sec % 60).padStart(2, '0')}`;\n\t}\n\n\t/** What the card says while a turnaround is drawing. The counter is the proof\n\t *  that something is happening; the estimate is what stops it feeling open\n\t *  ended. Past the estimate it stops guessing rather than guessing wrong. */\n\tfunction turnStatus(sh?: StoredSheet): string {\n\t\tconst el = since(sh?.sheet?.startedAt);\n\t\tconst eta = etaSec(sh);\n\t\tconst left = eta - el;\n\t\t// Minutes for the render, seconds for the drawing. Rounding half a minute\n\t\t// up to \"about 1 min left\" is how the short wait started sounding like the\n\t\t// long one in the first place.\n\t\tif (left > 15)\n\t\t\treturn left >= 60\n\t\t\t\t? `${clock(el)} · about ${Math.ceil(left / 60)} min left`\n\t\t\t\t: `${clock(el)} · about ${Math.ceil(left / 10) * 10}s left`;\n\t\t// The grace before it admits to being late is the wait's own size: two\n\t\t// minutes past a four-minute render is patience, two minutes past a\n\t\t// thirty-second drawing is the app not noticing.\n\t\tif (el < eta + (eta < 60 ? 60 : 120)) return `${clock(el)} · nearly there`;\n\t\treturn `${clock(el)} · taking longer than usual`;\n\t}\n\n\t/** Sessions with six views still being drawn, and sessions where they have\n\t *  landed and nobody has looked yet. Both keyed by the session the subject\n\t *  was made in, which is what stops a mark appearing on a row that has\n\t *  nothing to do with it. */\n\tconst sessionsDrawing = $derived(\n\t\tnew Set(\n\t\t\tsheets\n\t\t\t\t.filter((x) => x.sheet?.state === 'rendering' && x.sessionSlug)\n\t\t\t\t.map((x) => x.sessionSlug!)\n\t\t)\n\t);\n\t/** The characters this session is drawing right now, for the strip above the\n\t *  box. Scoped to this session for the same reason the six views are: a\n\t *  turnaround started in another conversation is that conversation's news,\n\t *  and the sidebar is where it belongs. */\n\tconst drawingHere = $derived(\n\t\tsheets.filter((x) => x.sheet?.state === 'rendering' && x.sessionSlug === runSlug)\n\t);\n\n\tconst sessionsDone = $derived(\n\t\tnew Set(\n\t\t\tsheets\n\t\t\t\t.filter((x) => x.sheet?.state === 'ready' && x.sheet.file && !x.delivered && x.sessionSlug)\n\t\t\t\t.map((x) => x.sessionSlug!)\n\t\t)\n\t);\n\n\t/** Ids this tab has already reported, so the effect below settles after one\n\t *  pass instead of chasing its own write. */\n\tconst markedSeen = new Set<string>();\n\n\t/** Take the mark off this session's finished sheets.\n\t *\n\t *  Tied to the session being open, not to a card being posted. The first\n\t *  version rode along with the card: mark it seen at the moment the six views\n\t *  are pushed into the conversation. But a card is posted once, and the mark\n\t *  outlives it — come back to a session whose card was posted last week and\n\t *  the poller skips it as already shown, so the green never cleared and the\n\t *  sidebar claimed news that had been read for days.\n\t *\n\t *  Opening the session is the whole condition, which is also what it means. */\n\t$effect(() => {\n\t\tconst slug = runSlug;\n\t\tif (!slug) return;\n\t\tfor (const x of sheets) {\n\t\t\tif (x.sheet?.state !== 'ready' || !x.sheet.file) continue;\n\t\t\tif (x.sessionSlug !== slug || x.delivered || markedSeen.has(x.id)) continue;\n\t\t\tmarkedSeen.add(x.id);\n\t\t\tx.delivered = true;\n\t\t\tvoid fetch('/studio/api/sheet', {\n\t\t\t\tmethod: 'PATCH',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({ id: x.id, delivered: true })\n\t\t\t}).catch(() => {});\n\t\t}\n\t});\n\n\t/** The characters this conversation is still owed a sheet for.\n\t *\n\t *  Read out of the chat rather than held in a variable, because the wait\n\t *  outlives the tab. An upload posts its picture and the turnaround then runs\n\t *  for minutes on a server that does not care whether anyone is still looking:\n\t *  close the tab, reload, and the expectation has to survive that. The card\n\t *  that announced the render is the record of it. */\n\tfunction awaitedSheets(): Set<string> {\n\t\tconst out = new Set<string>();\n\t\tfor (const c of chat) {\n\t\t\tconst sh = c.sheet;\n\t\t\tif (c.kind === 'sheet' && sh?.uploaded && sh.stage === 'anchor' && sh.id) out.add(sh.id);\n\t\t}\n\t\treturn out;\n\t}\n\n\t/** Whether the six views are already in this conversation. The chat is the\n\t *  record and a set in memory is not — the set dies on reload, and then the\n\t *  same sheet is posted a second time. */\n\tfunction sheetShown(id: string): boolean {\n\t\treturn chat.some((c) => c.kind === 'sheet' && c.sheet?.stage === 'sheet' && c.sheet.id === id);\n\t}\n\n\tfunction watchSheets() {\n\t\tif (guest) return;\n\t\tif (sheetWatch) clearTimeout(sheetWatch);\n\t\tconst tick = async () => {\n\t\t\t// Which ones were still being drawn before this poll.\n\t\t\tconst wasRendering = new Set(\n\t\t\t\tsheets.filter((x) => x.sheet?.state === 'rendering').map((x) => x.id)\n\t\t\t);\n\t\t\tawait loadSheets();\n\t\t\t// Two ways to be due a card. Watching one finish is the obvious one, and\n\t\t\t// it was the only one — which made the card depend on this tab happening\n\t\t\t// to be looking at the right second. It was not, twice: once because the\n\t\t\t// upload path never started this poller, and once because a reload landed\n\t\t\t// mid-render. The moment passed and the sheet was never mentioned again.\n\t\t\t//\n\t\t\t// So the question asked here is not \"did it just finish\" but \"am I still\n\t\t\t// waiting for it\" — which the chat can answer at any time, including\n\t\t\t// minutes later in a tab that was closed when it happened.\n\t\t\tconst awaited = awaitedSheets();\n\t\t\tfor (const x of sheets) {\n\t\t\t\tif (x.sheet?.state !== 'ready' || !x.sheet.file) continue;\n\t\t\t\t// Its own conversation, and no other.\n\t\t\t\t//\n\t\t\t\t// `wasRendering` is a purely temporal test — it fires wherever the tab\n\t\t\t\t// happens to be when the poll catches the finish. Start a character,\n\t\t\t\t// switch sessions, and the six views landed in the middle of unrelated\n\t\t\t\t// work. A sheet made before this was recorded has no session, and no\n\t\t\t\t// session means any: those keep the old behaviour rather than becoming\n\t\t\t\t// undeliverable.\n\t\t\t\tif (x.sessionSlug && x.sessionSlug !== runSlug) continue;\n\t\t\t\tif (!wasRendering.has(x.id) && !awaited.has(x.id)) continue;\n\t\t\t\tif (sheetPosted.has(x.id) || sheetShown(x.id)) continue;\n\t\t\t\tsheetPosted.add(x.id);\n\t\t\t\t// The turnaround first, then what was cut out of it. That is the order\n\t\t\t\t// they happened in and the order they explain each other in: the video\n\t\t\t\t// is why the six views look the way they do.\n\t\t\t\tconst c = x.sheet.clip;\n\t\t\t\tif (c?.workspace && c.artifact && c.file) {\n\t\t\t\t\tpushItem({\n\t\t\t\t\t\twho: 'studio',\n\t\t\t\t\t\tkind: 'clips',\n\t\t\t\t\t\tartifact: {\n\t\t\t\t\t\t\tid: c.artifact,\n\t\t\t\t\t\t\tkey: 'turnaround',\n\t\t\t\t\t\t\ttitle: `${x.name} — the turn`,\n\t\t\t\t\t\t\ttaskId: '',\n\t\t\t\t\t\t\tfiles: [{ name: c.file, url: fileUrl(c.workspace, c.artifact, c.file) }],\n\t\t\t\t\t\t\tworkspace: c.workspace\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\t// The six views, in the conversation that asked for them. The upload\n\t\t\t\t// itself stays quiet; this is the other end of it — you were told a\n\t\t\t\t// render had started, so you are told when it finished.\n\t\t\t\tpushItem({\n\t\t\t\t\twho: 'studio',\n\t\t\t\t\tkind: 'sheet',\n\t\t\t\t\tsheet: {\n\t\t\t\t\t\tkind: x.kind,\n\t\t\t\t\t\tstage: 'sheet',\n\t\t\t\t\t\tdescription: x.description ?? '',\n\t\t\t\t\t\tname: x.name,\n\t\t\t\t\t\tid: x.id,\n\t\t\t\t\t\turl: `/studio/api/sheet/full/${x.id}`,\n\t\t\t\t\t\tlaunched: true\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tpersist();\n\t\t\t}\n\t\t\tif (sheets.some((s) => s.sheet?.state === 'rendering')) {\n\t\t\t\tsheetWatch = setTimeout(tick, 8000);\n\t\t\t} else {\n\t\t\t\tsheetWatch = null;\n\t\t\t}\n\t\t};\n\t\tsheetWatch = setTimeout(tick, 4000);\n\t}\n\n\t/** Sheets already shown as a card, so a later poll does not post them twice. */\n\tconst sheetPosted = new Set<string>();\n\n\t/** What the running sheet render was asked for. The finished artifact carries\n\t *  no memory of the description that produced it, and that description is the\n\t *  most useful thing to keep beside a sheet — it is what you would edit to\n\t *  make a variant. */\n\tlet pendingSheet: {\n\t\tkind: 'character' | 'location';\n\t\tdescription: string;\n\t\tstage: 'anchor' | 'sheet';\n\t\tseed: number;\n\t\twhy?: string;\n\t\tvoice?: string;\n\t} | null = null;\n\n\t/** Keep a rendered sheet. The bytes are fetched server-side, from the harness,\n\t *  while this workspace is still answering. */\n\tasync function keepSheet(itemId: string) {\n\t\tconst item = chat.find((c) => c.id === itemId);\n\t\tif (!item?.sheet || item.sheet.id || sheetBusy[itemId]) return;\n\t\tconst { workspace, artifact, file, job } = item.sheet;\n\t\t// A draft card has no render behind it yet, so it has nothing to keep.\n\t\tif (!job && (!workspace || !artifact || !file)) return;\n\t\tsheetBusy[itemId] = true;\n\t\ttry {\n\t\t\tconst res = await fetch('/studio/api/sheet', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tkind: item.sheet.kind,\n\t\t\t\t\tname: item.sheet.name,\n\t\t\t\t\tdescription: item.sheet.description,\n\t\t\t\t\t...(item.sheet.voice ? { voice: item.sheet.voice } : {}),\n\t\t\t\t\t...(job ? { job } : { workspace, artifact, file })\n\t\t\t\t})\n\t\t\t});\n\t\t\tconst r = (await res.json()) as {\n\t\t\t\tok?: boolean;\n\t\t\t\tsheet?: StoredSheet;\n\t\t\t\tsheets?: StoredSheet[];\n\t\t\t\terror?: string;\n\t\t\t};\n\t\t\tif (!r.ok || !r.sheet) {\n\t\t\t\tpushError(r.error || 'The sheet could not be kept.');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\titem.sheet.id = r.sheet.id;\n\t\t\t// Point the card at our own copy now that there is one — the harness\n\t\t\t// stops serving an artifact the moment its workspace agent dies.\n\t\t\titem.sheet.url = `/studio/api/sheet/img/${r.sheet.id}`;\n\t\t\tif (r.sheets) sheets = r.sheets;\n\t\t\tpersist();\n\t\t} catch (e) {\n\t\t\tpushError(String(e));\n\t\t} finally {\n\t\t\tsheetBusy[itemId] = false;\n\t\t}\n\t}\n\n\t/** Rewrite the card in place. The old one collapses rather than disappearing:\n\t *  a prompt that was nearly right is worth being able to look back at. */\n\t/** What a rewrite must not touch.\n\t *\n\t *  The writer authored the brief and nothing else on the card. Who is in the\n\t *  shot, where it is, what clip it follows, the frame size and any adapter\n\t *  strengths you moved were all your decisions — and they are the fields that\n\t *  decide what actually gets sent to the GPU. Dropping them turned \"make this\n\t *  eight seconds instead of six\" into a render of a different person in a\n\t *  different room that could then neither be continued nor joined, because\n\t *  nothing recorded what it followed.\n\t *\n\t *  Carried explicitly rather than by spreading the old shot: the brief fields\n\t *  must lose to the new one, and a blanket spread in the wrong order is how\n\t *  that silently reverses. */\n\tfunction carried(prev: NonNullable<ChatItem['shot']>) {\n\t\treturn {\n\t\t\t...(prev.continues ? { continues: prev.continues } : {}),\n\t\t\t...(prev.characterId ? { characterId: prev.characterId } : {}),\n\t\t\t...(prev.characterName ? { characterName: prev.characterName } : {}),\n\t\t\t...(prev.locationId ? { locationId: prev.locationId } : {}),\n\t\t\t...(prev.locationName ? { locationName: prev.locationName } : {}),\n\t\t\tresolution: prev.resolution ?? wantRes,\n\t\t\tbaseLoras: prev.baseLoras\n\t\t};\n\t}\n\n\tasync function rewriteShot(itemId: string) {\n\t\tconst item = chat.find((c) => c.id === itemId);\n\t\tif (!item?.shot || shotBusy[itemId]) return;\n\t\tshotBusy[itemId] = true;\n\t\ttry {\n\t\t\tconst shot = await callShotPrompt(lastRequest || item.shot.prompt, {\n\t\t\t\tseconds: item.shot.seconds,\n\t\t\t\torientation: item.shot.orientation\n\t\t\t});\n\t\t\tif (!shot) return;\n\t\t\tsuperseded[itemId] = true;\n\t\t\tpushItem({ who: 'studio', kind: 'shot', shot: { ...shot, ...carried(item.shot) } });\n\t\t} finally {\n\t\t\tshotBusy[itemId] = false;\n\t\t}\n\t}\n\n\t/** Send the card's prompt — the edited text, whatever is in the box now — to\n\t *  the renderer. The clip comes back through the same poll, cache and player\n\t *  the planning chain uses; only the road to the GPU is shorter. */\n\t/** Send one shot to the renderer, wherever it came from.\n\t *\n\t *  Both the writer's card and the fix a diagnosis produced go through here.\n\t *  They were about to be two copies of the same forty lines, and the copy the\n\t *  fix used would have been the one that quietly stopped matching. */\n\t/** Keep the person in the clip you are continuing as a character.\n\t *\n\t *  Offered only when the clip has none. Without it the launch cuts a plate out\n\t *  of the prior clip every generation, so each continuation is measured against\n\t *  the last render rather than against a face anybody approved — and the drift\n\t *  compounds down the chain.\n\t *\n\t *  The character is usable the moment this returns: a render reads the picture,\n\t *  not the six views, and those arrive later on the card by themselves.\n\t */\n\t/** Choosing to carry on with these people is the answer \"Keep this person\"\n\t *  was asking for, so it stops being a question.\n\t *\n\t *  It sat beside the choice as a button of its own, which made the ordinary\n\t *  path — continue, then keep — two clicks and, on a phone, a third line in a\n\t *  row that already wrapped. Either continuation answers it. A new clip does\n\t *  not: that is the one case where this person is not being carried anywhere.\n\t *\n\t *  Guarded on exactly what the button was guarded on, so this fires where the\n\t *  offer would have been made and nowhere else — the call reads five frames\n\t *  and costs a vision request, and a second one would only overwrite the\n\t *  sheet the first wrote. */\n\t/** NOT WIRED UP, and this comment is why.\n\t *\n\t *  Choosing a continuation is the answer \"Keep this person\" asks for, so\n\t *  firing it from that choice looked free. It is not: makeCharacterFromClip\n\t *  calls /studio/api/charfromclip, which writes a sheet AND kicks off a\n\t *  turnaround — a GPU render, fire-and-forget, paid for. Tapping \"Same\n\t *  person\" started one, silently, and three of them were running before\n\t *  anybody noticed.\n\t *\n\t *  An automatic action may cost a request. It may not cost a render. Kept\n\t *  here so the next attempt starts from the constraint rather than from the\n\t *  idea: the marking has to happen without the turnaround, or not at all. */\n\tfunction keepPersonForContinuation() {\n\t\tif (!continuing || continuing.characterId || charFromClipBusy) return;\n\t\tvoid makeCharacterFromClip();\n\t}\n\n\tasync function makeCharacterFromClip() {\n\t\tconst c = continuing;\n\t\tif (!c || charFromClipBusy) return;\n\t\tcharFromClipBusy = true;\n\t\tcharFromClipError = '';\n\t\ttry {\n\t\t\tconst res = await fetch('/studio/api/charfromclip', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tworkspace: c.workspace,\n\t\t\t\t\tartifact: c.artifact,\n\t\t\t\t\tfile: c.file,\n\t\t\t\t\t...(sessionSlug ? { sessionSlug } : {})\n\t\t\t\t})\n\t\t\t});\n\t\t\tconst r = (await res.json()) as {\n\t\t\t\tok?: boolean;\n\t\t\t\terror?: string;\n\t\t\t\tsheet?: StoredSheet;\n\t\t\t\tsheets?: StoredSheet[];\n\t\t\t};\n\t\t\tif (!r.ok || !r.sheet) {\n\t\t\t\t// Said out loud, not just stored.\n\t\t\t\t//\n\t\t\t\t// `charFromClipError` was only ever drawn beside the button this used\n\t\t\t\t// to have, and that paragraph was behind `!STAGE_UI` — so on the only\n\t\t\t\t// surface anyone uses it has never been visible. When this fails the\n\t\t\t\t// continuation loses its anchor and the likeness drifts down the\n\t\t\t\t// chain, which is exactly the thing worth being told about.\n\t\t\t\tcharFromClipError = r.error || 'the character could not be made';\n\t\t\t\tpushError(`${charFromClipError} — this continuation has no kept face to hold on to`);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (r.sheets) sheets = r.sheets;\n\t\t\t// Onto the continuation itself, which is what launchShot reads. Replaced\n\t\t\t// rather than mutated so the panel re-renders with the name on it.\n\t\t\tcontinuing = { ...c, characterId: r.sheet.id, characterName: r.sheet.name };\n\t\t\t// And onto the composer, so the chip stops saying \"anyone\".\n\t\t\t//\n\t\t\t// It is the same person by every measure that matters — the clip it was\n\t\t\t// cut from is the one being continued — and leaving the chip on \"anyone\"\n\t\t\t// said the opposite in the one place that answers \"who is in this\". The\n\t\t\t// next shot, continuation or not, is now cast.\n\t\t\twantCharacter = r.sheet.id;\n\t\t\tsaveSetup();\n\t\t} catch (e) {\n\t\t\tcharFromClipError = `the character could not be made: ${e}`;\n\t\t\tpushError(charFromClipError);\n\t\t} finally {\n\t\t\tcharFromClipBusy = false;\n\t\t}\n\t}\n\n\tasync function launchShot(\n\t\tshot: NonNullable<ChatItem['shot']>,\n\t\tannounce = true\n\t): Promise<boolean> {\n\t\t// A continuation is a different workspace with different inputs, so it takes\n\t\t// its own stage rather than a flag on this one: the only thing the two share\n\t\t// is that a prompt goes to a GPU.\n\t\tif (shot.continues) {\n\t\t\tconst c = shot.continues;\n\t\t\tconst spec = {\n\t\t\t\tslug: `cont-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`,\n\t\t\t\ttitle: (lastRequest || 'Continuation').slice(0, 60),\n\t\t\t\tprompt: shot.prompt,\n\t\t\t\tseconds: shot.seconds,\n\t\t\t\t...frameFor((shot.resolution as ResKey) ?? wantRes, shot.orientation),\n\t\t\t\tseed: Math.floor(Math.random() * 1_000_000_000),\n\t\t\t\tloras: shot.loras ?? [],\n\t\t\t\tbaseLoras: shot.baseLoras ?? {},\n\t\t\t\trequest: lastRequest,\n\t\t\t\tpriorWorkspace: c.workspace,\n\t\t\t\tpriorArtifact: c.artifact,\n\t\t\t\tpriorFile: c.file,\n\t\t\t\tcharacterId: c.characterId,\n\t\t\t\tlocationId: c.locationId,\n\t\t\t\tpinned: c.pinned !== false,\n\t\t\t\t...(sessionSlug ? { sessionSlug } : {})\n\t\t\t};\n\t\t\t// Stopped between the brief and the launch: nothing charged, and\n\t\t\t// already said.\n\t\t\tif (stopWanted) {\n\t\t\t\tstopWanted = false;\n\t\t\t\tstopping = false;\n\t\t\t\tstopArmed = false;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tlaunchingSlug = spec.slug;\n\t\t\ttry {\n\t\t\t\tconst res = await fetch('/studio/api/launch', {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify({ stage: 'continue', continuation: spec })\n\t\t\t\t});\n\t\t\t\tconst r = (await res.json()) as {\n\t\t\t\t\tok?: boolean;\n\t\t\t\t\terror?: string;\n\t\t\t\t\tworkspaceId?: string;\n\t\t\t\t\t/** What the render would have cost, when the purse could not cover it. */\n\t\t\t\t\tneed?: number;\n\t\t\t\t\t/** The launch found a stop recorded against its slug and did not charge. */\n\t\t\t\t\tstopped?: boolean;\n\t\t\t\t};\n\t\t\t\tlaunchingSlug = '';\n\t\t\t\tif (r.stopped) {\n\t\t\t\t\tsettleStop(STOPPED_BEFORE_CHARGE, true);\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif (!r.ok || !r.workspaceId) {\n\t\t\t\t\tif (stopWanted) settleStop('', true);\n\t\t\t\t\t// The server is the authority on the purse, and it can refuse a press\n\t\t\t\t\t// the check above let through — a second tab, a stale balance. Same\n\t\t\t\t\t// answer either way: the shop, not an error.\n\t\t\t\t\tif (r.error === NOT_ENOUGH_TOKENS) {\n\t\t\t\t\t\taskForTokens(Number(r.need) || sendPrice);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tpushError(r.error || 'The continuation could not start.');\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t// Stopped while this was being charged and opened: it went through\n\t\t\t\t// before the stop could land, so it is paid and on its way.\n\t\t\t\tif (stopWanted) settleStop(ALREADY_STARTED, false);\n\t\t\t\trenderIsSheet = false;\n\t\t\t\trenderWs = r.workspaceId;\n\t\t\t\tstartedAt = Date.now();\n\t\t\t\tshootsAnnounced = true;\n\t\t\t\tif (announce) pushStudio(`Continuing — ${shot.seconds}s more.`);\n\t\t\t\tpersist();\n\t\t\t\tstartPolling();\n\t\t\t\treturn true;\n\t\t\t} catch {\n\t\t\t\tlaunchingSlug = '';\n\t\t\t\tif (stopWanted) settleStop('', true);\n\t\t\t\tpushError('The continuation could not start — try again.');\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tconst spec = {\n\t\t\tslug: `direct-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`,\n\t\t\ttitle: lastRequest.slice(0, 60) || 'Direct render',\n\t\t\tprompts: [shot.prompt],\n\t\t\tseconds: shot.seconds,\n\t\t\t...frameFor((shot.resolution as ResKey) ?? wantRes, shot.orientation),\n\t\t\tseed: Math.floor(Math.random() * 1_000_000_000),\n\t\t\tloras: shot.loras ?? [],\n\t\t\tbaseLoras: shot.baseLoras ?? {},\n\t\t\twroteLoras: shot.wroteLoras ?? shot.loras ?? [],\n\t\t\trequest: lastRequest,\n\t\t\t...(sessionSlug ? { sessionSlug } : {}),\n\t\t\t...(shot.characterId ? { characterId: shot.characterId } : {}),\n\t\t\t...(shot.locationId ? { locationId: shot.locationId } : {})\n\t\t};\n\t\t// Stopped between the brief and the launch: nothing charged, and\n\t\t// already said.\n\t\tif (stopWanted) {\n\t\t\tstopWanted = false;\n\t\t\tstopping = false;\n\t\t\tstopArmed = false;\n\t\t\treturn false;\n\t\t}\n\t\tlaunchingSlug = spec.slug;\n\t\ttry {\n\t\t\tconst res = await fetch('/studio/api/launch', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({ stage: 'direct', direct: spec })\n\t\t\t});\n\t\t\tconst r = (await res.json()) as {\n\t\t\t\tok?: boolean;\n\t\t\t\terror?: string;\n\t\t\t\tworkspaceId?: string;\n\t\t\t\tneed?: number;\n\t\t\t\tstopped?: boolean;\n\t\t\t};\n\t\t\tlaunchingSlug = '';\n\t\t\tif (r.stopped) {\n\t\t\t\tsettleStop(STOPPED_BEFORE_CHARGE, true);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!r.ok || !r.workspaceId) {\n\t\t\t\tif (stopWanted) settleStop('', true);\n\t\t\t\t// An empty purse is not a failure to explain, it is a shop to open.\n\t\t\t\tif (r.error === NOT_ENOUGH_TOKENS) {\n\t\t\t\t\taskForTokens(Number(r.need) || sendPrice);\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tpushError(r.error || 'The render could not start.');\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t// Stopped while this was being charged and opened: it went through before\n\t\t\t// the stop could land, so it is paid and on its way.\n\t\t\tif (stopWanted) settleStop(ALREADY_STARTED, false);\n\t\t\trenderIsSheet = false;\n\t\t\trenderWs = r.workspaceId;\n\t\t\tstartedAt = Date.now();\n\t\t\t// Same reason as the simple-clip launch: the snapshot must know the\n\t\t\t// workspace before anything else can happen to this tab.\n\t\t\tpersist();\n\t\t\t// The render poll narrates a shoot it announces first; there is no\n\t\t\t// shoot here, only this clip, so the announcement is already spent.\n\t\t\tshootsAnnounced = true;\n\t\t\t// Nothing is announced here any more. The card above already carries\n\t\t\t// the length and the frame size, the event feed says \"Started clip 1\"\n\t\t\t// a moment later, and the live line below counts. Three statements of\n\t\t\t// the same fact, and the only one of the three that could not tell you\n\t\t\t// it was still going was this one.\n\t\t\t// Charged server-side, spent here too. One clip, whatever the composer\n\t\t\t// is set to now — the batch path counts its own.\n\t\t\ttokens = Math.max(0, tokens - tokensForSend(composerShape.seconds, 1, freeClip));\n\t\t\tfreeClip = false;\n\t\t\tvoid loadRefFiles();\n\t\t\tpersist();\n\t\t\tstartPolling();\n\t\t\treturn true;\n\t\t} catch {\n\t\t\tlaunchingSlug = '';\n\t\t\tif (stopWanted) settleStop('', true);\n\t\t\tpushError('The render could not start — try again.');\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/** Several takes of one card, rendered at once and followed by the server.\n\t *\n\t *  A continuation cannot be one of these: take two would need take one's clip\n\t *  as its reference, so the chain is sequential by physics rather than by\n\t *  interface. Only a fresh clip can be forked.\n\t *\n\t *  The card is marked launched exactly as a single render marks it, because it\n\t *  has been: the beat is spent, and offering \"render this\" underneath four\n\t *  takes of it already running is how somebody pays for a fifth by accident. */\n\tasync function renderBatch(itemId: string, takes: number, angles: number) {\n\t\tconst item = chat.find((c) => c.id === itemId);\n\t\tif (!item?.shot || item.shot.launched || shotBusy[itemId]) return;\n\t\t// Stopped while the brief was being written: nothing charged, already said.\n\t\tif (stopWanted) {\n\t\t\tstopWanted = false;\n\t\t\treturn;\n\t\t}\n\t\tshotBusy[itemId] = true;\n\t\t// The single-render slot is not this run's, and leaving the last run in it\n\t\t// is what made a finished batch look like a running one.\n\t\t//\n\t\t// `renderInFlight` asks whether the newest clip on the stage is the one the\n\t\t// slot names. A batch never writes the slot — it has several workspaces and\n\t\t// the slot holds one — so it kept the PREVIOUS clip's, which the newest\n\t\t// clip was equal to until a take was kept. The moment one was, the newest\n\t\t// stopped matching, the test said \"still rendering\", and the loader came\n\t\t// back over the clip that had just been chosen, showing a percentage\n\t\t// counted from a render that had ended minutes earlier.\n\t\t//\n\t\t// The batch reports itself: its card carries a state per take and the poll\n\t\t// keeps it current, on this tab or the next one. So the slot is emptied\n\t\t// rather than borrowed.\n\t\trenderWs = '';\n\t\tstartedAt = 0;\n\t\trenderIsSheet = false;\n\t\ttry {\n\t\t\tconst shot = item.shot;\n\n\t\t\t// Angles are written before anything is launched, from the prompt on the\n\t\t\t// card rather than from the request behind it. By now that prompt has\n\t\t\t// been read and possibly edited; sending the request back to the writer\n\t\t\t// would produce different scenes, and sending it the prompt produces\n\t\t\t// different views of one.\n\t\t\tlet variants: string[] = [];\n\t\t\tif (angles > 1) {\n\t\t\t\tconst a = await fetch('/studio/api/angles', {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify({ prompt: shot.prompt, count: angles })\n\t\t\t\t});\n\t\t\t\tconst ar = (await a.json()) as { ok?: boolean; angles?: string[]; error?: string };\n\t\t\t\tif (!ar.ok || !ar.angles?.length) {\n\t\t\t\t\tpushError(ar.error || 'the camera angles could not be written');\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tvariants = ar.angles;\n\t\t\t}\n\t\t\t// A continuation batch is the same two axes against a different stage.\n\t\t\t// Every take continues the SAME prior clip — not the take before it —\n\t\t\t// which is what makes versions coherent here at all: they are\n\t\t\t// alternative next stretches of one film, and you keep the one you like.\n\t\t\tconst c = shot.continues;\n\t\t\tconst payload = c\n\t\t\t\t? {\n\t\t\t\t\t\ttakes,\n\t\t\t\t\t\t...(variants.length ? { variants } : {}),\n\t\t\t\t\t\tcontinuation: {\n\t\t\t\t\t\t\ttitle: (lastRequest || 'Continuation').slice(0, 60),\n\t\t\t\t\t\t\tprompt: shot.prompt,\n\t\t\t\t\t\t\tseconds: shot.seconds,\n\t\t\t\t\t\t\t...frameFor((shot.resolution as ResKey) ?? wantRes, shot.orientation),\n\t\t\t\t\t\t\tloras: shot.loras ?? [],\n\t\t\t\t\t\t\tbaseLoras: shot.baseLoras ?? {},\n\t\t\t\t\t\t\trequest: lastRequest,\n\t\t\t\t\t\t\tpriorWorkspace: c.workspace,\n\t\t\t\t\t\t\tpriorArtifact: c.artifact,\n\t\t\t\t\t\t\tpriorFile: c.file,\n\t\t\t\t\t\t\tcharacterId: c.characterId,\n\t\t\t\t\t\t\tlocationId: c.locationId,\n\t\t\t\t\t\t\tpinned: c.pinned !== false,\n\t\t\t\t\t\t\t...(sessionSlug ? { sessionSlug } : {})\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t: {\n\t\t\t\t\t\ttakes,\n\t\t\t\t\t\t...(variants.length ? { variants } : {}),\n\t\t\t\t\t\tdirect: {\n\t\t\t\t\t\t\ttitle: (lastRequest || 'Take').slice(0, 60),\n\t\t\t\t\t\t\tprompts: [shot.prompt],\n\t\t\t\t\t\t\tseconds: shot.seconds,\n\t\t\t\t\t\t\t...frameFor((shot.resolution as ResKey) ?? wantRes, shot.orientation),\n\t\t\t\t\t\t\tloras: shot.loras ?? [],\n\t\t\t\t\t\t\tbaseLoras: shot.baseLoras ?? {},\n\t\t\t\t\t\t\twroteLoras: shot.wroteLoras ?? shot.loras ?? [],\n\t\t\t\t\t\t\trequest: lastRequest,\n\t\t\t\t\t\t\t...(sessionSlug ? { sessionSlug } : {}),\n\t\t\t\t\t\t\t...(shot.characterId ? { characterId: shot.characterId } : {}),\n\t\t\t\t\t\t\t...(shot.locationId ? { locationId: shot.locationId } : {})\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\tconst res = await fetch('/studio/api/batch', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify(payload)\n\t\t\t});\n\t\t\tconst r = (await res.json()) as {\n\t\t\t\tok?: boolean;\n\t\t\t\terror?: string;\n\t\t\t\tbatch?: string;\n\t\t\t\truns?: ServerRun[];\n\t\t\t\t/** What the whole batch would have cost, when it could not be afforded. */\n\t\t\t\tneed?: number;\n\t\t\t};\n\t\t\tif (!r.ok || !r.batch) {\n\t\t\t\tif (r.error === NOT_ENOUGH_TOKENS) {\n\t\t\t\t\taskForTokens(Number(r.need) || sendPrice);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tpushError(r.error || 'The takes could not start.');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tshot.launched = true;\n\t\t\t// The card goes up with every take already on it, the ones still on the\n\t\t\t// GPU included. The strip is then its final shape from the first second\n\t\t\t// and nothing moves under the cursor as they land — which matters here\n\t\t\t// more than usual, because the thing you do on this card is aim at a\n\t\t\t// small picture.\n\t\t\tpushItem({\n\t\t\t\twho: 'studio',\n\t\t\t\tkind: 'takes',\n\t\t\t\ttakes: { batch: r.batch, runs: (r.runs ?? []).map(takeRun) }\n\t\t\t});\n\t\t\tpersist();\n\t\t\t// Charged per run, where each one opens, so only the ones that actually\n\t\t\t// started took anything. A run that failed to open was refunded there\n\t\t\t// and must not be counted here.\n\t\t\tconst started = (r.runs ?? []).filter((x) => x.state !== 'failed').length;\n\t\t\ttokens = Math.max(\n\t\t\t\t0,\n\t\t\t\ttokens - tokensForSend(composerShape.seconds, started, freeClip && started > 0)\n\t\t\t);\n\t\t\tif (started > 0) freeClip = false;\n\t\t\tvoid loadRefFiles();\n\t\t\twatchBatches();\n\t\t} catch (e) {\n\t\t\tpushError(`The takes could not start: ${e}`);\n\t\t} finally {\n\t\t\tshotBusy[itemId] = false;\n\t\t}\n\t}\n\n\t/** What the batch record looks like on the wire. Wider than the card needs,\n\t *  so it is narrowed on the way in rather than stored as it arrives: every\n\t *  poll writes the transcript to localStorage, and a batch of four carries\n\t *  four seeds, four timestamps and four workspace ids that nothing reads. */\n\ttype ServerRun = {\n\t\tbatch?: string;\n\t\tindex: number;\n\t\tslug: string;\n\t\tstate: string;\n\t\terror?: string;\n\t\tclip?: { workspace: string; artifact: string; file: string };\n\t};\n\n\tfunction takeRun(r: ServerRun): NonNullable<ChatItem['takes']>['runs'][number] {\n\t\treturn {\n\t\t\tindex: r.index,\n\t\t\tslug: r.slug,\n\t\t\tstate: r.state === 'ready' ? 'ready' : r.state === 'failed' ? 'failed' : 'rendering',\n\t\t\t...(r.error ? { error: r.error } : {}),\n\t\t\t...(r.clip ? { clip: r.clip } : {})\n\t\t};\n\t}\n\n\t/** Bring the takes cards in this conversation up to date, and stop.\n\t *\n\t *  It only ever updates cards that are already here. The version before it\n\t *  created one card per landed take, keyed on \"is this take already on\n\t *  screen\" — which is a question a fresh transcript answers no to for every\n\t *  take of every batch ever run, so opening the studio in a new conversation\n\t *  would have posted the whole history into it. A batch is announced by the\n\t *  press that started it and by nothing else; a poll may only bring news\n\t *  about one that is already on the page. */\n\tlet batchWatch: ReturnType<typeof setTimeout> | null = null;\n\n\tfunction watchBatches() {\n\t\tif (guest) return;\n\t\tif (batchWatch) clearTimeout(batchWatch);\n\t\tconst waiting = () =>\n\t\t\tchat.filter((c) => c.kind === 'takes' && c.takes?.runs.some((r) => r.state === 'rendering'));\n\t\tif (!waiting().length) {\n\t\t\tbatchWatch = null;\n\t\t\treturn;\n\t\t}\n\t\tconst tick = async () => {\n\t\t\tconst cards = waiting();\n\t\t\tif (!cards.length) {\n\t\t\t\tbatchWatch = null;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet runs: ServerRun[] = [];\n\t\t\ttry {\n\t\t\t\tconst r = (await (await fetch('/studio/api/batch')).json()) as { runs?: ServerRun[] };\n\t\t\t\truns = r.runs ?? [];\n\t\t\t} catch {\n\t\t\t\tbatchWatch = setTimeout(tick, 12000);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet moved = false;\n\t\t\tfor (const card of cards) {\n\t\t\t\tconst t = card.takes;\n\t\t\t\tif (!t) continue;\n\t\t\t\tfor (const run of runs) {\n\t\t\t\t\tif (run.batch !== t.batch) continue;\n\t\t\t\t\tconst mine = t.runs.find((x) => x.slug === run.slug);\n\t\t\t\t\tif (!mine || mine.state !== 'rendering') continue;\n\t\t\t\t\tconst next = takeRun(run);\n\t\t\t\t\tif (next.state === 'rendering') continue;\n\t\t\t\t\tObject.assign(mine, next);\n\t\t\t\t\tmoved = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (moved) {\n\t\t\t\tpersist();\n\t\t\t\t// A take that has landed has a row in the render log, and the card\n\t\t\t\t// under it reads that row for the adapters, the size and the seed.\n\t\t\t\tvoid loadVerdicts();\n\t\t\t}\n\t\t\tbatchWatch = waiting().length ? setTimeout(tick, 12000) : null;\n\t\t};\n\t\tbatchWatch = setTimeout(tick, 5000);\n\t}\n\n\t/** Returns whether the brief actually reached a GPU. The retry button reads it:\n\t *  a launch that never started should hand the button back, and one that did\n\t *  must not. */\n\tasync function renderShot(itemId: string): Promise<boolean> {\n\t\tconst item = chat.find((c) => c.id === itemId);\n\t\tif (!item?.shot || item.shot.launched || shotBusy[itemId]) return false;\n\t\tshotBusy[itemId] = true;\n\t\ttry {\n\t\t\tif (await launchShot(item.shot)) {\n\t\t\t\titem.shot.launched = true;\n\t\t\t\t// Written down, not just remembered.\n\t\t\t\t//\n\t\t\t\t// launchShot persists on its way out — before this line runs — so the\n\t\t\t\t// snapshot on disk said the card had never been launched. Leave the\n\t\t\t\t// conversation and come back and the restore believed it: the card\n\t\t\t\t// came up with its controls live and \"render this\" ready, over a clip\n\t\t\t\t// that was already on a GPU. Pressing it would have paid for the same\n\t\t\t\t// five seconds twice.\n\t\t\t\tpersist();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t} finally {\n\t\t\tshotBusy[itemId] = false;\n\t\t}\n\t}\n\n\t/** Send a brief to the GPU again, after a render died on the way.\n\t *\n\t *  The same prompt, a new seed and a new workspace — nothing about the brief\n\t *  is at fault when the harness reports its own infrastructure, so rewriting\n\t *  it would throw away a good one and cost another writer call. `launched` is\n\t *  cleared first because renderShot refuses a card that has already been sent,\n\t *  which is the guard that stops a double-spend and has to be stood down\n\t *  deliberately rather than worked around. */\n\tasync function retryShot(errorItemId: string, shotItemId: string) {\n\t\tconst err = chat.find((c) => c.id === errorItemId);\n\t\tconst item = chat.find((c) => c.id === shotItemId);\n\t\tif (!err || !item?.shot || shotBusy[shotItemId] || err.retried) return;\n\t\t// On the card, not in a map beside it.\n\t\t//\n\t\t// A map is component state and a reload empties it, so the button came back\n\t\t// live over a render that was already going — which is what a person\n\t\t// reloading to pick up a fix saw, and it is the same double-spend the guard\n\t\t// was added to stop. The transcript is saved; the press belongs in it.\n\t\terr.retried = true;\n\t\tpersist();\n\t\t// Stood down so renderShot will take the card, and put back if it does not.\n\t\t// Leaving it false after a launch that never started loses the retry\n\t\t// altogether: the error card finds its brief by looking for a launched one,\n\t\t// so the button would vanish at exactly the moment it is wanted. Measured,\n\t\t// after breaking it that way.\n\t\titem.shot.launched = false;\n\t\tif (!(await renderShot(shotItemId))) {\n\t\t\titem.shot.launched = true;\n\t\t\terr.retried = false;\n\t\t\tpersist();\n\t\t}\n\t}\n\n\t/** Moving the duration or the frame rewrites the prompt rather than relabelling\n\t *  it. Both are structural: timestamps are derived from the duration, and the\n\t *  camera language from the shape of the frame. A card that said 6 seconds over\n\t *  beats written for 10 would be a card that lies. */\n\tasync function respin(\n\t\titemId: string,\n\t\tpin: { seconds: number; orientation: 'portrait' | 'landscape' }\n\t) {\n\t\tconst item = chat.find((c) => c.id === itemId);\n\t\tif (!item?.shot || item.shot.launched || shotBusy[itemId]) return;\n\t\tshotBusy[itemId] = true;\n\t\ttry {\n\t\t\tconst shot = await callShotPrompt(lastRequest || item.shot.prompt, pin);\n\t\t\tif (!shot) return;\n\t\t\t// Everything except the brief survives the rewrite. Only the words are\n\t\t\t// being written again; the settings around them were your decisions and\n\t\t\t// losing them silently is how a rewrite turns into a step backwards.\n\t\t\tsuperseded[itemId] = true;\n\t\t\tpushItem({\n\t\t\t\twho: 'studio',\n\t\t\t\tkind: 'shot',\n\t\t\t\tshot: { ...shot, ...pin, ...carried(item.shot) }\n\t\t\t});\n\t\t} finally {\n\t\t\tshotBusy[itemId] = false;\n\t\t}\n\t}\n\n\tfunction setShotSeconds(itemId: string, seconds: number) {\n\t\tconst item = chat.find((c) => c.id === itemId);\n\t\tif (!item?.shot || item.shot.seconds === seconds) return;\n\t\t// Through to the composer as well.\n\t\t//\n\t\t// The card and the chip were two stores of one setting, and only the chip\n\t\t// is on screen when you press \"continue this\". Change it on a card, and\n\t\t// the chip went on showing the old number and quietly sending it — the way\n\t\t// a clip made at five seconds came to be continued at ten. Whichever of\n\t\t// the two you touch, both now say the same thing.\n\t\twantSeconds = seconds;\n\t\tsaveSetup();\n\t\tvoid respin(itemId, { seconds, orientation: item.shot.orientation });\n\t}\n\n\t/** Add or remove one adapter on a card that has not been sent yet.\n\t *\n\t *  The writer chooses, and this is where you disagree with it — before the\n\t *  render rather than after, which is the whole reason the choice is on the\n\t *  card at all. Acts replace each other rather than stacking, the same rule\n\t *  the writer is given, because a clip is one thing happening. */\n\tfunction toggleLora(itemId: string, key: string) {\n\t\tconst item = chat.find((c) => c.id === itemId);\n\t\tconst lora = loraFor(key);\n\t\tif (!item?.shot || item.shot.launched || !lora) return;\n\t\tconst picks = item.shot.loras ?? [];\n\t\tif (picks.some((p) => p.key === key)) {\n\t\t\titem.shot.loras = picks.filter((p) => p.key !== key);\n\t\t\treturn;\n\t\t}\n\t\tconst kept = lora.kind === 'act' ? picks.filter((p) => loraFor(p.key)?.kind !== 'act') : picks;\n\t\tif (kept.length >= MAX_PICKS) return;\n\t\titem.shot.loras = [...kept, { key, strength: lora.strength }];\n\t}\n\n\t/** Your verdict on a finished clip, and what was wrong with it.\n\t *\n\t *  Keyed by workspace rather than held on the chat item, so scrolling back to\n\t *  an older clip shows the verdict you already gave it and cannot collect a\n\t *  second one. Nothing here reads the log back — the file is the record, this\n\t *  is only what the page needs to stop offering a button you already pressed.\n\t */\n\tlet verdict = $state<Record<string, 'kept' | 'rejected'>>({});\n\n\tasync function rate(workspace: string, outcome: 'kept' | 'rejected') {\n\t\tif (!workspace) return;\n\t\tverdict[workspace] = outcome;\n\t\t// Fire and forget, like the run's own closing call. A verdict that fails to\n\t\t// save is a lost row; a verdict that blocks the page is a lost afternoon.\n\t\tvoid fetch('/studio/api/renders', {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\tbody: JSON.stringify({ workspace, outcome })\n\t\t}).catch(() => {});\n\t\t// A clip that missed is looked at straight away. Asking you to type what\n\t\t// was wrong first was asking for the answer before doing the work — the\n\t\t// model is about to look at the frames and can see it for itself, and the\n\t\t// question only stood between you and the fix.\n\t\tif (outcome === 'rejected') void diagnose(workspace);\n\t}\n\n\t/** The takes card you are looking at properly, and which take of it.\n\t *\n\t *  One overlay for the whole page, because only one can be open and because\n\t *  of the single number this feature turns on: the clip is 1024 across in\n\t *  here, which is its own width, and the transcript column can never give it\n\t *  more than 720. Four near-identical five-second takes are not separable at\n\t *  thumbnail size and barely separable at column width, so the room to look\n\t *  at them properly is the feature — the strip is only the way in. */\n\tlet takesAt = $state<{ id: string; index: number } | null>(null);\n\t/** The tile it was opened from, to hand focus back to on the way out. */\n\tlet takesFrom: HTMLElement | null = null;\n\n\t/** The takes there is anything to look at. One still on the GPU and one that\n\t *  failed have no frames, so the arrows step over them rather than landing on\n\t *  a black rectangle. */\n\tfunction readyTakes(id: string) {\n\t\tconst item = chat.find((c) => c.id === id);\n\t\treturn (item?.takes?.runs ?? []).filter((r) => r.state === 'ready' && r.clip);\n\t}\n\n\tfunction openTake(id: string, index: number, from?: HTMLElement) {\n\t\tif (!readyTakes(id).some((r) => r.index === index)) return;\n\t\ttakesFrom = from ?? null;\n\t\ttakesAt = { id, index };\n\t}\n\n\tfunction shutTake() {\n\t\ttakesAt = null;\n\t\ttakesFrom?.focus();\n\t\ttakesFrom = null;\n\t}\n\n\t/** Left and right on the picture itself. The arrows are hidden on a phone —\n\t *  a 44px target floating over a 316px-wide clip covers the thing it is there\n\t *  to help you look at — so this is how you move between takes there.\n\t *  Vertical drags are left alone, or the transcript could not be scrolled\n\t *  from over the viewer. */\n\tlet swipeX = 0;\n\tlet swipeY = 0;\n\n\tfunction swipeStart(e: TouchEvent) {\n\t\tswipeX = e.changedTouches[0].clientX;\n\t\tswipeY = e.changedTouches[0].clientY;\n\t}\n\n\tfunction swipeEnd(e: TouchEvent) {\n\t\tconst dx = e.changedTouches[0].clientX - swipeX;\n\t\tconst dy = e.changedTouches[0].clientY - swipeY;\n\t\tif (Math.abs(dx) > 45 && Math.abs(dx) > Math.abs(dy) * 1.5) stepTake(dx < 0 ? 1 : -1);\n\t}\n\n\tfunction stepTake(d: number) {\n\t\tconst at = takesAt;\n\t\tif (!at) return;\n\t\tconst list = readyTakes(at.id);\n\t\tif (list.length < 2) return;\n\t\tconst i = list.findIndex((r) => r.index === at.index);\n\t\ttakesAt = { id: at.id, index: list[(i + d + list.length) % list.length].index };\n\t}\n\n\t/** Keep one take: the card becomes the clip you chose.\n\t *\n\t *  It writes the same `artifact` a single render produces, and that is the\n\t *  whole trick — the kept take then draws through the clip card unchanged,\n\t *  with the verdict buttons, the continue and the scene join it already has.\n\t *  None of that was rewritten for batches.\n\t *\n\t *  Choosing does not rate anything. Best of four and good are different\n\t *  claims, and the card goes on asking \"how was it?\" underneath, because a\n\t *  batch where you kept the least bad of four is exactly the case the quality\n\t *  signal most needs to hear about. The takes you passed over are not marked\n\t *  bad either, and not deleted: they stay behind \"the other three\". */\n\tfunction keepTake(id: string, index: number) {\n\t\tconst item = chat.find((c) => c.id === id);\n\t\tconst run = item?.takes?.runs.find((r) => r.index === index);\n\t\tif (!item?.takes || !run?.clip) return;\n\t\tconst c = run.clip;\n\n\t\t// The ones you did not choose are still clips, and they were still paid\n\t\t// for. Choosing is about which take this card becomes — it is not a\n\t\t// verdict on the others, and it must not be the moment they stop\n\t\t// existing. Each ready sibling gets its own card here, so it lives in the\n\t\t// strip like any other clip: playable, addable to the film, continuable\n\t\t// from, and still there tomorrow.\n\t\t//\n\t\t// Before this card in the transcript, and carrying its timestamp, because\n\t\t// that is when they landed — all of them at once. The one you kept stays\n\t\t// last, so it is the newest and the stage stays on it.\n\t\t//\n\t\t// Keyed by workspace against the cards that already exist, so keeping a\n\t\t// second time does not post a duplicate, and so the take you are moving\n\t\t// AWAY from gets a card of its own on the way out. This card is excluded\n\t\t// from that check: it is about to name a different take.\n\t\titem.takes.kept = index;\n\t\t// No caption. \"Take 3, kept.\" said what the card already showed: the strip\n\t\t// collapses to the one you pressed, so the only picture on screen IS the\n\t\t// one you kept, and a line naming it is a label on a thing with no\n\t\t// alternatives left to distinguish it from.\n\t\titem.artifact = {\n\t\t\tid: c.artifact,\n\t\t\tkey: run.slug,\n\t\t\ttitle: `Take ${index}`,\n\t\t\ttaskId: '',\n\t\t\tfiles: [{ name: c.file, url: fileUrl(c.workspace, c.artifact, c.file) }],\n\t\t\tworkspace: c.workspace\n\t\t};\n\t\tspareTakeCards();\n\t\tpersist();\n\t\tshutTake();\n\t}\n\n\t/** Give every take nobody chose a card of its own.\n\t *\n\t *  Choosing is about which take a batch card becomes. It is not a verdict on\n\t *  the others and it must not be the moment they stop existing: they\n\t *  rendered, they were paid for, and one of them is often the one you want a\n\t *  week later. With a card each they are ordinary clips — playable from the\n\t *  strip, addable to the film, continuable from, and in the snapshot.\n\t *\n\t *  Run on restore as well as on keeping, so a batch chosen from before any of\n\t *  this heals the next time the studio opens. Keyed by workspace, so it never\n\t *  posts the same clip twice however often it runs.\n\t *\n\t *  Only for batches somebody has already chosen from. While a batch is still\n\t *  a batch its takes belong to it, and the stage shows them there. */\n\tfunction spareTakeCards() {\n\t\tif (guest) return;\n\t\tconst have = new Set(\n\t\t\tchat.filter((c) => c.artifact?.workspace).map((c) => c.artifact!.workspace)\n\t\t);\n\t\t// Newest first, so the splices below do not move a card this loop has yet\n\t\t// to reach.\n\t\tfor (let i = chat.length - 1; i >= 0; i--) {\n\t\t\tconst item = chat[i];\n\t\t\tif (!item.takes || !item.artifact) continue;\n\t\t\tconst spare = item.takes.runs.filter(\n\t\t\t\t(r) => r.state === 'ready' && r.clip && !have.has(r.clip.workspace)\n\t\t\t);\n\t\t\tif (!spare.length) continue;\n\t\t\tfor (const r of spare) have.add(r.clip!.workspace);\n\t\t\t// Before the card, and carrying its timestamp: they all landed at once,\n\t\t\t// and the one that was kept stays last so the stage stays on it.\n\t\t\tchat.splice(\n\t\t\t\ti,\n\t\t\t\t0,\n\t\t\t\t...spare.map((r) => ({\n\t\t\t\t\tid: mkId(),\n\t\t\t\t\tat: item.at,\n\t\t\t\t\twho: 'studio' as const,\n\t\t\t\t\tkind: 'clips' as const,\n\t\t\t\t\tartifact: {\n\t\t\t\t\t\tid: r.clip!.artifact,\n\t\t\t\t\t\tkey: r.slug,\n\t\t\t\t\t\ttitle: `Take ${r.index}`,\n\t\t\t\t\t\ttaskId: '',\n\t\t\t\t\t\tfiles: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: r.clip!.file,\n\t\t\t\t\t\t\t\turl: fileUrl(r.clip!.workspace, r.clip!.artifact, r.clip!.file)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\tworkspace: r.clip!.workspace\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t\t);\n\t\t}\n\t}\n\n\t// --- the film -----------------------------------------------------------------\n\n\t/** The clips you have decided to keep, in the order they will be cut.\n\t *\n\t *  Server-side, not in the transcript: a transcript is stored per run, so a\n\t *  film living there would vanish the moment you opened another production —\n\t *  which is exactly the thing it must survive. It holds the same\n\t *  (workspace, artifact, file) triple `api/join` already takes, so a clip\n\t *  from any run, of any age, can be added without copying anything. That is\n\t *  also why it works backwards: every clip card in every old conversation\n\t *  already carries those three ids. */\n\tinterface FilmClip {\n\t\tworkspace: string;\n\t\tartifact: string;\n\t\tfile: string;\n\t\ttitle?: string;\n\t\tcontinues?: string;\n\t\tat: string;\n\t}\n\tlet film = $state<FilmClip[]>([]);\n\t/** Open by choice, remembered for the session. The first clip opens it once —\n\t *  so the shelf is discovered rather than explained — and after that it obeys\n\t *  you. */\n\tlet filmOpen = $state(false);\n\tlet filmEverOpened = false;\n\tlet filmBusy = $state(false);\n\n\tconst filmKey = (c: { workspace: string; artifact: string; file: string }) =>\n\t\t`${c.workspace} ${c.artifact} ${c.file}`;\n\t/** Every film ever assembled, newest first, from the log beside the renders.\n\t *\n\t *  The shelf at the foot of the sidebar and the all-media grid both read this.\n\t *  Loaded once and refreshed by Export, which is the only thing that adds to\n\t *  it. */\n\ttype FilmRow = {\n\t\tworkspace: string;\n\t\tartifact: string;\n\t\tfile: string;\n\t\tparts: number;\n\t\tseconds: number;\n\t\tat: number;\n\t};\n\tlet films = $state<FilmRow[]>([]);\n\t/** The finished film, shown the moment it is made. Closing it does not lose\n\t *  it — that is what the shelf is for. */\n\tlet filmPopup = $state<FilmRow | null>(null);\n\t/** The all-media grid, opened from the shelf's last tile. */\n\tlet mediaOpen = $state(false);\n\n\t/** Everything this studio has made, for the page you land on.\n\t *\n\t *  The film log answers \"what did I finish\" and is the right list for the\n\t *  shelf. It is the wrong list for a front page, because on a studio that has\n\t *  assembled nothing it is empty, and an empty front page says the tool has\n\t *  never worked. The clip cache is never empty on a studio that has run: it\n\t *  is the work itself, a hundred and eighty-eight of them here, and the only\n\t *  record that survives a transcript being thrown away.\n\t *\n\t *  Films first regardless, because a film is the thing that was finished and\n\t *  a shot is a part. Duration is what separates them — over twenty seconds is\n\t *  longer than this studio can render in one go, so it was assembled. */\n\t/** The shelf, and only films are on it.\n\t *\n\t *  A shot is a part; a wall of parts is a scratch folder. What belongs on a\n\t *  front page is what was finished, and the server decides what counts —\n\t *  the film log for anything assembled since it existed, and, before it, a\n\t *  file longer than this studio can render in one go, which can only have\n\t *  been joined. Deduplicated there too: a logged film is usually also sitting\n\t *  in the cache, and two tiles for one video is worse than a short shelf.\n\t *\n\t *  So there is nothing to filter or merge here. Whatever comes back is the\n\t *  shelf, newest first. */\n\tinterface ShelfItem {\n\t\tid: string;\n\t\tseconds: number;\n\t\tparts?: number;\n\t}\n\tlet shelf = $state<ShelfItem[]>([]);\n\t/** Whether the first answer about the shelf is in — films or none. What sits\n\t *  below the wall (StudioAbout) waits for it, so the wall arriving does not\n\t *  push it down a screen in front of the reader. */\n\tlet shelfSettled = $state(false);\n\tlet mediaPopup = $state<ShelfItem | null>(null);\n\tfunction shelfUrl(m: ShelfItem): string {\n\t\treturn `/studio/api/media?id=${m.id}`;\n\t}\n\tasync function loadMedia() {\n\t\ttry {\n\t\t\tconst r = (await (await fetch('/studio/api/media')).json()) as { items?: ShelfItem[] };\n\t\t\tif (r.items) shelf = r.items;\n\t\t} catch {\n\t\t\t/* the front page is a shelf; a failed list leaves the greeting alone */\n\t\t} finally {\n\t\t\tshelfSettled = true;\n\t\t}\n\t}\n\n\t/** Load a video only once it is on screen.\n\t *\n\t *  The picker draws a hundred and eighty-eight of them. A browser will not\n\t *  keep that many media elements alive — Chrome stops somewhere under a\n\t *  hundred — so past the limit every tile stays black, and a picker you\n\t *  cannot see is not a picker. The src is held back until the tile scrolls\n\t *  into view and the element is then a normal one.\n\t *\n\t *  Kept once it has loaded. Tearing the src out again on the way past would\n\t *  make scrolling back up reload everything, and the elements that survive\n\t *  are the ones somebody is choosing between. */\n\tfunction whenSeen(node: HTMLVideoElement, url: string) {\n\t\tconst eye = new IntersectionObserver(\n\t\t\t(entries) => {\n\t\t\t\tfor (const e of entries) {\n\t\t\t\t\tif (!e.isIntersecting) continue;\n\t\t\t\t\tif (!node.src) node.src = url;\n\t\t\t\t\teye.unobserve(node);\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ rootMargin: '400px' }\n\t\t);\n\t\teye.observe(node);\n\t\treturn { destroy: () => eye.disconnect() };\n\t}\n\n\t/** Everything in the cache, for choosing from. Loaded only when the picker is\n\t *  opened: it is a hundred and eighty-eight rows and the front page needs\n\t *  none of them. */\n\tlet pool = $state<ShelfItem[]>([]);\n\tlet pins = $state<string[]>([]);\n\tasync function loadPool() {\n\t\ttry {\n\t\t\tconst r = (await (await fetch('/studio/api/media?all=1')).json()) as {\n\t\t\t\titems?: ShelfItem[];\n\t\t\t\tpins?: string[];\n\t\t\t};\n\t\t\tif (r.items) pool = r.items;\n\t\t\tif (r.pins) pins = r.pins;\n\t\t} catch {\n\t\t\t/* nothing to choose from is the same as not opening the picker */\n\t\t}\n\t}\n\tasync function togglePin(id: string) {\n\t\tconst pinned = !pins.includes(id);\n\t\t// Moved before the answer comes back. A pin is a click on a thumbnail and\n\t\t// the wall behind it is already drawn; waiting a round trip to redraw one\n\t\t// ring makes a instant decision feel like a form submission.\n\t\tpins = pinned ? [...pins, id] : pins.filter((x) => x !== id);\n\t\ttry {\n\t\t\tconst r = (await (\n\t\t\t\tawait fetch('/studio/api/media', {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify({ id, pinned })\n\t\t\t\t})\n\t\t\t).json()) as { pins?: string[] };\n\t\t\tif (r.pins) pins = r.pins;\n\t\t} catch {\n\t\t\t/* the optimistic move stands; the next open reads the truth */\n\t\t}\n\t\tvoid loadMedia();\n\t}\n\n\t/** The starters are a row you page through, not a stack you read.\n\t *\n\t *  Three cards took two hundred and eighty-four points — a third of a phone —\n\t *  and pushed every film below the fold on the one screen that exists to show\n\t *  them. One at a time costs a fifth of that and still teaches, which is what\n\t *  these are for: not conversion (of the accounts that only ever tapped one,\n\t *  one in forty-two rendered anything) but showing what a usable sentence\n\t *  looks like — who is in it, where, and what happens. That only works if the\n\t *  whole sentence is legible, which is why they are not shortened to labels.\n\t *\n\t *  Paged rather than swapped on a timer alone. A line that changes by itself\n\t *  is a line you might not notice changed; a row with the next card showing at\n\t *  its edge and dots underneath says \"there are three\" before anything moves.\n\t *  It still advances on its own, because nobody swipes a thing they have not\n\t *  been told is swipeable — but the first frame already says it is.\n\t *\n\t *  A phone only. From `sm` up the three fit side by side as they always did,\n\t *  and the same markup lays out as a grid: no peek, no dots, no advancing.\n\t *  The carousel answers a shortage of height, and a desktop has none. */\n\tlet starterAt = $state(0);\n\tlet starterRow = $state<HTMLElement | null>(null);\n\t/** Stop advancing the moment somebody takes hold of it. Nothing is more\n\t *  irritating than a carousel that moves while you are reading it. */\n\tlet starterHeld = $state(false);\n\tconst STARTER_MS = 5200;\n\n\t$effect(() => {\n\t\tconst reduced =\n\t\t\ttypeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;\n\t\t// Only where it is a carousel. From `sm` up the three sit side by side and\n\t\t// there is nothing to page — a row that scrolled itself while all of it was\n\t\t// already on screen would be motion for its own sake.\n\t\tconst wide = typeof matchMedia === 'function' && matchMedia('(min-width: 640px)').matches;\n\t\tif (reduced || wide || starterHeld || !showExamples || examples.length < 2) return;\n\t\tconst t = setInterval(() => showStarter((starterAt + 1) % examples.length), STARTER_MS);\n\t\treturn () => clearInterval(t);\n\t});\n\n\t/** Scroll the row to one card. The row is the source of truth for which card\n\t *  is showing — a swipe moves it without asking — so this only pushes it, and\n\t *  `onscroll` reads it back. */\n\tfunction showStarter(i: number) {\n\t\tstarterAt = i;\n\t\tconst row = starterRow;\n\t\tconst card = row?.children[i] as HTMLElement | undefined;\n\t\tif (row && card) row.scrollTo({ left: card.offsetLeft - row.offsetLeft, behavior: 'smooth' });\n\t}\n\n\tfunction starterScrolled() {\n\t\tconst row = starterRow;\n\t\tif (!row) return;\n\t\tconst w = (row.children[0] as HTMLElement | undefined)?.clientWidth ?? 1;\n\t\tstarterAt = Math.max(0, Math.min(examples.length - 1, Math.round(row.scrollLeft / (w + 8))));\n\t}\n\n\t/** Two tiles at a time, moving along the wall.\n\t *\n\t *  A still wall is a folder; eleven walls playing at once is a browser with\n\t *  eleven video decoders and a fan. So a couple of them are alive at any\n\t *  moment and the pair walks, which is how a shelf of moving pictures is\n\t *  usually done — enough motion that the page is clearly not a screenshot,\n\t *  little enough that the eye is not being shouted at from six directions.\n\t *\n\t *  The two are taken from opposite ends of the list rather than side by side.\n\t *  Neighbours playing together read as one thing twitching in a corner;\n\t *  spread apart, the wall looks alive all over.\n\t *\n\t *  Off for anybody who has asked for less motion, where a page that animates\n\t *  by itself is exactly what was asked against. */\n\tlet tileEls = $state<Record<string, HTMLVideoElement>>({});\n\tlet liveAt = $state(0);\n\tconst STEP_MS = 4200;\n\t$effect(() => {\n\t\tconst still =\n\t\t\ttypeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;\n\t\tif (still || stagePhase !== 'empty' || shelf.length < 2) return;\n\t\tconst t = setInterval(() => (liveAt = (liveAt + 1) % shelf.length), STEP_MS);\n\t\treturn () => clearInterval(t);\n\t});\n\n\t/** Play the two that are up, park everything else on its first frame.\n\t *\n\t *  Parked rather than merely paused: a video left where it stopped shows a\n\t *  different frame every time it comes round, so the wall never settles into\n\t *  a picture. Back to the tenth of a second the tile was drawn on. */\n\t$effect(() => {\n\t\tconst n = shelf.length;\n\t\tif (!n) return;\n\t\tconst pair = new Set([shelf[liveAt % n]?.id, shelf[(liveAt + Math.floor(n / 2)) % n]?.id]);\n\t\tfor (const item of shelf) {\n\t\t\tconst el = tileEls[item.id];\n\t\t\tif (!el) continue;\n\t\t\tif (pair.has(item.id)) {\n\t\t\t\tvoid el.play().catch(() => {});\n\t\t\t} else {\n\t\t\t\tel.pause();\n\t\t\t\ttry {\n\t\t\t\t\tif (el.currentTime > 0.2) el.currentTime = 0.1;\n\t\t\t\t} catch {\n\t\t\t\t\t/* seeking before metadata throws; the next pass gets it */\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\n\t/** How tall one sits in its column.\n\t *\n\t *  A longer film is more work and more to look at, so it takes more wall — and\n\t *  a column of identical rectangles is a contact sheet. Set as a ratio rather\n\t *  than a row span because the tiles flow in columns now: each column fills\n\t *  independently, which is what staggers them against each other instead of\n\t *  ruling them into a grid. */\n\tfunction tileRatio(seconds: number): string {\n\t\tif (seconds >= 30) return 'aspect-[3/4]';\n\t\tif (seconds >= 15) return 'aspect-square';\n\t\treturn 'aspect-[4/3]';\n\t}\n\tasync function loadFilms() {\n\t\tif (guest) return;\n\t\ttry {\n\t\t\tconst r = (await (await fetch('/studio/api/films')).json()) as { films?: FilmRow[] };\n\t\t\tif (r.films) films = r.films;\n\t\t} catch {\n\t\t\t/* the shelf is a convenience; a failed list is not worth a message */\n\t\t}\n\t}\n\n\tconst filmSeconds = $derived(film.reduce((n, c) => n + (logRow[c.workspace]?.seconds || 5), 0));\n\n\tfunction inFilm(a: NonNullable<ChatItem['artifact']> | undefined): boolean {\n\t\tconst c = filmPart(a);\n\t\treturn !!c && film.some((x) => filmKey(x) === filmKey(c));\n\t}\n\n\t/** The three ids, or null when the card cannot name them — a joined scene has\n\t *  no artifact id of its own, and neither has anything that arrived before the\n\t *  studio recorded one. Those cannot be cut into a film, and the button is not\n\t *  offered rather than offered and failing. */\n\tfunction filmPart(a: NonNullable<ChatItem['artifact']> | undefined) {\n\t\tconst f = a?.files?.[0]?.name;\n\t\tif (!a?.workspace || !a?.id || !f) return null;\n\t\treturn { workspace: a.workspace, artifact: a.id, file: f };\n\t}\n\n\tasync function saveFilm() {\n\t\ttry {\n\t\t\tconst res = await fetch('/studio/api/film', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({ clips: film })\n\t\t\t});\n\t\t\tconst r = (await res.json()) as { ok?: boolean; clips?: FilmClip[]; error?: string };\n\t\t\tif (r.ok && r.clips) film = r.clips;\n\t\t\telse if (r.error) pushError(r.error);\n\t\t} catch (e) {\n\t\t\tpushError(`the film could not be saved: ${e}`);\n\t\t}\n\t}\n\n\t/** Add a clip the film by its three ids. The card path and the viewer path\n\t *  both end here — the film has never cared where a clip came from, only\n\t *  that it can be found. */\n\tfunction addClipToFilm(c: { workspace: string; artifact: string; file: string }, title: string) {\n\t\tif (film.some((x) => filmKey(x) === filmKey(c))) return;\n\t\tfilm.push({\n\t\t\t...c,\n\t\t\ttitle,\n\t\t\tcontinues: logRow[c.workspace]?.continuesWorkspace || undefined,\n\t\t\tat: new Date().toISOString()\n\t\t});\n\t\t// Always, not just the first time. Adding a clip is the one moment you want\n\t\t// to see what the film now looks like — and the reel is where the order and\n\t\t// the seams are, which is exactly what a new clip changes.\n\t\tfilmOpen = true;\n\t\tfilmEverOpened = true;\n\t\tvoid saveFilm();\n\t}\n\n\tfunction addToFilm(item: ChatItem) {\n\t\tconst part = filmPart(item.artifact);\n\t\tif (!part) return;\n\t\taddClipToFilm(part, item.artifact?.title || item.text || '');\n\t}\n\n\t/** A clip dragged out of the strip and into the film.\n\t *\n\t *  Its own MIME type, not the plain text the reel already uses for\n\t *  reordering: that payload is an index into the film, and a stray index\n\t *  arriving from outside would move a clip nobody touched. Two kinds of drag\n\t *  land on the same row, so they have to be told apart by what they carry. */\n\tconst CLIP_DRAG = 'application/x-auteur-clip';\n\tfunction dropClipIntoFilm(e: DragEvent) {\n\t\tconst raw = e.dataTransfer?.getData(CLIP_DRAG);\n\t\tif (!raw) return false;\n\t\te.preventDefault();\n\t\ttry {\n\t\t\tconst c = JSON.parse(raw) as { workspace?: string; artifact?: string; file?: string };\n\t\t\tif (!c.workspace || !c.artifact || !c.file) return true;\n\t\t\taddClipToFilm({ workspace: c.workspace, artifact: c.artifact, file: c.file }, '');\n\t\t\tfilmOpen = true;\n\t\t} catch {\n\t\t\t/* something else was dropped — the film is unchanged, which is the\n\t\t\t   right answer to a payload we cannot read */\n\t\t}\n\t\treturn true;\n\t}\n\n\tfunction dropFromFilm(i: number) {\n\t\tfilm.splice(i, 1);\n\t\tif (!film.length) filmOpen = false;\n\t\tvoid saveFilm();\n\t}\n\n\tfunction moveInFilm(from: number, to: number) {\n\t\tif (from === to || from < 0 || to < 0 || from >= film.length || to >= film.length) return;\n\t\tconst [m] = film.splice(from, 1);\n\t\tfilm.splice(to, 0, m);\n\t\tvoid saveFilm();\n\t}\n\n\t/** Two neighbours match only when one continues the other — the workflow\n\t *  starts the second from the first's final frame. Anything else will jump,\n\t *  and the reel marks the seam rather than letting playback be the first\n\t *  place you find out. */\n\tfunction seamJumps(i: number): boolean {\n\t\tif (i <= 0 || i >= film.length) return false;\n\t\treturn film[i].continues !== film[i - 1].workspace;\n\t}\n\n\t/** Assemble what is in the reel. Same endpoint the scene join has always\n\t *  used — it never cared whether the parts were a chain. */\n\tasync function exportFilm() {\n\t\tif (film.length < 2 || filmBusy) return;\n\t\tfilmBusy = true;\n\t\ttry {\n\t\t\tconst res = await fetch('/studio/api/join', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tparts: film.map((c) => ({ workspace: c.workspace, artifact: c.artifact, file: c.file }))\n\t\t\t\t})\n\t\t\t});\n\t\t\tconst r = (await res.json()) as {\n\t\t\t\tok?: boolean;\n\t\t\t\terror?: string;\n\t\t\t\t/** What a SvelteKit error() puts the reason in. */\n\t\t\t\tmessage?: string;\n\t\t\t\turl?: string;\n\t\t\t\tparts?: number;\n\t\t\t\tseconds?: number;\n\t\t\t};\n\t\t\tif (!r.ok || !r.url) {\n\t\t\t\t// `message` too: a SvelteKit error() answers with that field, not\n\t\t\t\t// `error`, so every server-side refusal arrived as the generic line and\n\t\t\t\t// the actual reason — \"ffmpeg is not installed\" — never reached the\n\t\t\t\t// screen. The one thing that would have told you what to do.\n\t\t\t\tpushError(r.error || r.message || 'the film could not be assembled');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Written down before anything else. A film nobody recorded is a file\n\t\t\t// nobody can find again, which is the whole reason this log exists.\n\t\t\ttry {\n\t\t\t\tconst rec = (await (\n\t\t\t\t\tawait fetch('/studio/api/films', {\n\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\t\tworkspace: film[film.length - 1]?.workspace,\n\t\t\t\t\t\t\tartifact: film[film.length - 1]?.artifact,\n\t\t\t\t\t\t\tfile: 'scene.mp4',\n\t\t\t\t\t\t\tparts: r.parts,\n\t\t\t\t\t\t\tseconds: r.seconds,\n\t\t\t\t\t\t\tshots: film.map((c) => c.workspace)\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t).json()) as { films?: FilmRow[] };\n\t\t\t\tif (rec.films) films = rec.films;\n\t\t\t\tfilmPopup = films[0] ?? null;\n\t\t\t} catch {\n\t\t\t\t/* the file exists either way */\n\t\t\t}\n\t\t\t// Straight to disk as well as onto the stage. Export is the word for\n\t\t\t// getting the thing OUT — watching it here is what the stage was\n\t\t\t// already for, and a button called Export that only plays something\n\t\t\t// leaves you hunting for a save.\n\t\t\t//\n\t\t\t// Same origin, so `download` is honoured and names the file; a\n\t\t\t// cross-origin url would ignore it and navigate instead.\n\t\t\ttry {\n\t\t\t\t// `globalThis.document`, because this component has a `document` of its\n\t\t\t\t// own — a snippet by that name — and the bare identifier resolves to\n\t\t\t\t// it here rather than to the page.\n\t\t\t\tconst doc = globalThis.document;\n\t\t\t\tconst a = doc.createElement('a');\n\t\t\t\ta.href = r.url;\n\t\t\t\ta.download = `auteur-film-${r.parts}-clips-${Math.round(r.seconds ?? 0)}s.mp4`;\n\t\t\t\tdoc.body.appendChild(a);\n\t\t\t\ta.click();\n\t\t\t\ta.remove();\n\t\t\t} catch {\n\t\t\t\t/* the clip is on the stage either way — a blocked download is not a\n\t\t\t\t   reason to lose the assembly */\n\t\t\t}\n\t\t\tpushItem({\n\t\t\t\twho: 'studio',\n\t\t\t\tkind: 'clips',\n\t\t\t\ttext: `The film — ${r.parts} clips, ${r.seconds}s.`,\n\t\t\t\tartifact: {\n\t\t\t\t\tkey: 'film',\n\t\t\t\t\ttitle: 'The film',\n\t\t\t\t\ttaskId: '',\n\t\t\t\t\tfiles: [{ name: 'film.mp4', url: r.url }],\n\t\t\t\t\t// The last clip's workspace, so continuing the film continues where\n\t\t\t\t\t// it ends — the same rule joinScene already follows.\n\t\t\t\t\tworkspace: film[film.length - 1]?.workspace\n\t\t\t\t}\n\t\t\t});\n\t\t\tpersist();\n\t\t} catch (e) {\n\t\t\tpushError(`the film could not be assembled: ${e}`);\n\t\t} finally {\n\t\t\tfilmBusy = false;\n\t\t}\n\t}\n\n\t/** Which shot of the film is on screen, or null. Separate from `takesAt`\n\t *  rather than folded into it: the takes viewer exists to choose between\n\t *  drafts and carries the controls for it, while this one is a cut being\n\t *  watched. Sharing the state would mean every control asking which of the\n\t *  two it is in. */\n\tlet filmAt = $state<number | null>(null);\n\tlet filmReturn: HTMLElement | null = null;\n\n\tfunction openFilmViewer(i: number, from?: HTMLElement) {\n\t\tif (!film.length) return;\n\t\tfilmReturn = from ?? null;\n\t\tfilmAt = Math.min(Math.max(0, i), film.length - 1);\n\t}\n\tfunction shutFilmViewer() {\n\t\tfilmAt = null;\n\t\tfilmReturn?.focus();\n\t\tfilmReturn = null;\n\t}\n\tfunction stepFilm(d: number) {\n\t\tif (filmAt === null || film.length < 2) return;\n\t\tfilmAt = (filmAt + d + film.length) % film.length;\n\t}\n\t/** The clips are separate files, so playing the film means chaining them:\n\t *  when one ends the next begins. Nothing is written to disk until Export —\n\t *  you can watch the cut before paying to assemble it. */\n\tfunction nextShot() {\n\t\tif (filmAt === null) return;\n\t\tif (filmAt < film.length - 1) filmAt += 1;\n\t}\n\n\t/** Tiles play, silently, and only while they are on screen.\n\t *\n\t *  A take cannot be judged from a poster frame — the takes share the shot,\n\t *  the prompt and the framing, and what separates them is motion. So they\n\t *  move. But a transcript with a few batches in it would then be a dozen\n\t *  videos decoding at once, on a laptop that is also running the render, so\n\t *  the ones scrolled away from stop. */\n\tlet tileEyes: IntersectionObserver | null = null;\n\n\t/** Park a video on its final frame and leave it there.\n\t *\n\t *  The chip that offers to continue from the last frame shows the last frame.\n\t *  A poster is the first one — which on a five second clip is a different\n\t *  moment, a different pose, sometimes a different framing, so the picture\n\t *  would have been illustrating the wrong thing while claiming to be exact.\n\t *  A hair before the end rather than the end itself: seeking to `duration`\n\t *  lands past the last decodable frame in Safari and paints nothing. */\n\t/** A portrait that has not been drawn yet is not a broken picture.\n\t *\n\t *  The sheet's own image url answers 404 until the turnaround lands, which is\n\t *  the ten minutes right after you upload the photograph — so the card that\n\t *  appeared the instant you uploaded showed the browser's torn-page icon for\n\t *  the whole of it, which reads as a file that failed rather than a picture\n\t *  being made. Hiding the element lets whatever the markup puts behind it —\n\t *  the placeholder disc, the surface fill — stand in until there is something\n\t *  to show. */\n\tfunction sheetImageMissing(e: Event) {\n\t\tconst img = e.currentTarget as HTMLImageElement | null;\n\t\tif (img) img.hidden = true;\n\t}\n\n\tfunction lastFrame(el: HTMLVideoElement) {\n\t\tconst park = () => {\n\t\t\tif (!Number.isFinite(el.duration) || el.duration <= 0) return;\n\t\t\ttry {\n\t\t\t\tel.currentTime = Math.max(0, el.duration - 0.06);\n\t\t\t} catch {\n\t\t\t\t/* a seek before the media is ready throws; loadedmetadata retries it */\n\t\t\t}\n\t\t};\n\t\tel.addEventListener('loadedmetadata', park);\n\t\tif (el.readyState >= 1) park();\n\t\treturn {\n\t\t\tdestroy() {\n\t\t\t\tel.removeEventListener('loadedmetadata', park);\n\t\t\t}\n\t\t};\n\t}\n\n\tfunction looping(el: HTMLVideoElement) {\n\t\ttileEyes ??= new IntersectionObserver(\n\t\t\t(entries) =>\n\t\t\t\tentries.forEach((e) => {\n\t\t\t\t\tconst v = e.target as HTMLVideoElement;\n\t\t\t\t\tif (e.isIntersecting) void v.play().catch(() => {});\n\t\t\t\t\telse v.pause();\n\t\t\t\t}),\n\t\t\t{ rootMargin: '120px' }\n\t\t);\n\t\ttileEyes.observe(el);\n\t\treturn { destroy: () => tileEyes?.unobserve(el) };\n\t}\n\n\t/** Three stills off the clip that is already on screen: near the start, the\n\t *  middle where the key beat sits, and near the end.\n\t *\n\t *  Drawn from the <video> element rather than cut server-side. The host has no\n\t *  ffmpeg, the harness's copy sits behind a docker exec this app should not be\n\t *  making, and the browser has the decoded frames already. Scaled down on the\n\t *  way out — the model reads a malformed hand at 768 across as well as at\n\t *  1024, and three full-size stills is a megabyte of base64 for nothing. */\n\tasync function grabFrames(video: HTMLVideoElement, count = 3): Promise<string[]> {\n\t\tconst dur = video.duration;\n\t\tif (!Number.isFinite(dur) || dur <= 0) return [];\n\t\tconst wasAt = video.currentTime;\n\t\tconst scale = Math.min(1, 768 / (video.videoWidth || 768));\n\t\t// globalThis, not the bare name. This component declares a snippet called\n\t\t// `document`, and svelte compiles snippets to module-scoped consts — which\n\t\t// shadows the global for the whole file. The failure it caused was not\n\t\t// obvious from the outside: the button did nothing at all, because\n\t\t// `document.querySelector is not a function` came back as an unhandled\n\t\t// rejection with no visible effect on the page.\n\t\tconst canvas = globalThis.document.createElement('canvas');\n\t\tcanvas.width = Math.round((video.videoWidth || 768) * scale);\n\t\tcanvas.height = Math.round((video.videoHeight || 432) * scale);\n\t\tconst ctx = canvas.getContext('2d');\n\t\tif (!ctx) return [];\n\n\t\tconst out: string[] = [];\n\t\tfor (let i = 0; i < count; i++) {\n\t\t\tconst at = dur * (0.15 + (0.7 * i) / Math.max(1, count - 1));\n\t\t\ttry {\n\t\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\t\tconst done = () => {\n\t\t\t\t\t\tvideo.removeEventListener('seeked', done);\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t};\n\t\t\t\t\tvideo.addEventListener('seeked', done);\n\t\t\t\t\t// A clip that will not seek must not hang the button forever.\n\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\tvideo.removeEventListener('seeked', done);\n\t\t\t\t\t\treject(new Error('seek timed out'));\n\t\t\t\t\t}, 4000);\n\t\t\t\t\tvideo.currentTime = at;\n\t\t\t\t});\n\t\t\t\tctx.drawImage(video, 0, 0, canvas.width, canvas.height);\n\t\t\t\tout.push(canvas.toDataURL('image/jpeg', 0.82));\n\t\t\t} catch {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tvideo.currentTime = wasAt;\n\t\treturn out;\n\t}\n\n\tlet diagnosing = $state<Record<string, boolean>>({});\n\n\t/** Put back the verdicts already given, from the log that has them.\n\t *\n\t *  They were held only in this tab, so a reload lost them and offered the same\n\t *  clip for rating a second time. The log is the record; the page was keeping\n\t *  a private copy and losing it. Failures are ignored on purpose — a studio\n\t *  that will not load because the verdict history did not is a bad trade for a\n\t *  row of buttons. */\n\tinterface LogRow {\n\t\tworkspace: string;\n\t\tlaunched?: { key: string; strength: number }[];\n\t\tsteps?: number;\n\t\twidth?: number;\n\t\theight?: number;\n\t\tseconds?: number;\n\t\tfps?: number;\n\t\tseed?: number;\n\t\twallSeconds?: number;\n\t\toutcome?: string;\n\t\t/** What you typed, verbatim. The history row's title is the same text cut\n\t\t *  to sixty characters; this one is not cut. */\n\t\trequest?: string;\n\t\t/** What the clip was shot with, and what it continues. Both arrived with the\n\t\t *  render log rather than the chat, so an old clip has neither. */\n\t\tprompt?: string;\n\t\tcharacterId?: string;\n\t\tcharacterName?: string;\n\t\tlocationId?: string;\n\t\tlocationName?: string;\n\t\tcontinuesWorkspace?: string;\n\t\t/** Where this run's clip is, written down when it landed. Absent on rows\n\t\t *  from before the log carried it — every reader falls back. */\n\t\tclipArtifact?: string;\n\t\tclipFile?: string;\n\t}\n\tlet logRow = $state<Record<string, LogRow>>({});\n\n\t/** The clip the composer is currently continuing, or null.\n\t *\n\t *  A separate axis from `wantTarget`: that one chooses what a NEW message\n\t *  makes, and this one says the next message extends something that already\n\t *  exists. Setting it puts the composer into continuation mode; sending or\n\t *  cancelling clears it. */\n\tlet continuing = $state<NonNullable<ChatItem['shot']>['continues'] | null>(null);\n\t/** Set while a character is being cut out of the clip being continued. The\n\t *  composer is held for it: sending first would launch the continuation with\n\t *  the plate this is replacing, which is the whole thing it is here to fix. */\n\tlet charFromClipBusy = $state(false);\n\tlet charFromClipError = $state('');\n\n\t/** Whether the seam is pinned to the prior clip's final frame.\n\t *\n\t *  Up here with `continuing` rather than down by the banner that sets it,\n\t *  because the composer's own chip reads it: a pinned seam rules camera angles\n\t *  out, and a derived declared above the state it reads is the dead zone this\n\t *  file documents at composerShape. */\n\tlet pinSeam = $state(true);\n\n\t/** What the composer is actually about to send, which is not always what the\n\t *  composer is set to.\n\t *\n\t *  While you are continuing a clip, its length, size and frame all come from\n\t *  the clip being continued — they have to, or the pieces cannot be joined.\n\t *  The chip went on showing the saved defaults anyway, so the screen read\n\t *  \"5s\" directly beside \"Continuing — 10s more.\" and the honest question\n\t *  followed immediately: why is it making ten. It was right to make ten. The\n\t *  chip was wrong to say five.\n\t *\n\t *  Declared here rather than up with the other settings because it reads\n\t *  `continuing`, and a derived that reads a state declared below it is the\n\t *  dead zone this file has already been caught by once. */\n\tconst composerShape = $derived.by(() => {\n\t\tconst prior = continuing ? logRow[continuing.workspace] : null;\n\t\tif (!prior?.width || !prior?.height) {\n\t\t\treturn {\n\t\t\t\tseconds: wantSeconds,\n\t\t\t\tres: wantRes,\n\t\t\t\tportrait: wantOrientation === 'portrait',\n\t\t\t\tfixed: false\n\t\t\t};\n\t\t}\n\t\tconst longest = Math.max(prior.width, prior.height);\n\t\t// Length stays yours even here. Only the two that decide whether the pieces\n\t\t// can be joined are taken over.\n\t\treturn {\n\t\t\tseconds: wantSeconds,\n\t\t\tres: RES_KEYS.find((k) => RESOLUTIONS[k].long === longest) ?? wantRes,\n\t\t\tportrait: prior.height > prior.width,\n\t\t\tfixed: true\n\t\t};\n\t});\n\n\t/** What a given pair of counts makes, in the fewest words that are still true. */\n\tfunction countLabel(t: number, a: number): string {\n\t\tconst n = t * a;\n\t\tif (n === 1) return 'one clip';\n\t\tif (a === 1) return `${t} versions`;\n\t\tif (t === 1) return `${a} camera angles`;\n\t\treturn `${n} clips`;\n\t}\n\n\t/** Camera angles cannot apply to a continuation whose seam is pinned.\n\t *\n\t *  Versions can, and do: every take continues the SAME prior clip, so they are\n\t *  alternative next stretches and you keep one. An angle is different in kind —\n\t *  the pinned seam nails the first instant to the frame the last clip ended on,\n\t *  and a second camera cannot start from that frame. On a free start it is\n\t *  coherent again, which is why this is a condition rather than a ban. */\n\tconst anglesApply = $derived(!(continuing && pinSeam));\n\tconst effAngles = $derived(anglesApply ? angles : 1);\n\tconst effAtOnce = $derived(takes * effAngles);\n\n\t/** What this send costs, and what is left to pay it with.\n\t *\n\t *  The balance is seeded from the layout's — the same number the nav bar\n\t *  shows — and then kept by hand, because nothing here navigates: a render\n\t *  charged server-side would leave the figure on this page stale until the\n\t *  next page load. The server is still the authority; this is only what the\n\t *  button is allowed to say before the press.\n\t */\n\tlet tokens = $state(0);\n\t$effect(() => {\n\t\ttokens = (page.data.tokens as number) ?? 0;\n\t});\n\t/** Whether the next clip is the account's free first one. Only the server\n\t *  knows, so it arrives with the balance; until it does the button quotes the\n\t *  full price, which is the safe way to be wrong. */\n\tlet freeClip = $state(!page.data.userId && page.data.freeFirstClip === true);\n\n\t/** Not signed in yet.\n\t *\n\t *  A visitor arriving cold used to land on a sign-up page before typing a\n\t *  word. Now they write first — the pitch, the read-back, the brief all run\n\t *  without an account (withStudioWriter) — and sign up at the press that\n\t *  starts a GPU. Anything that would store something under an account (a\n\t *  picture, a character, the history, the films) asks for the account at that\n\t *  point instead, and none of it is loaded for them. When the free first clip\n\t *  is on (StudioSettings.freeFirstClip) the button already says so; when it is\n\t *  off they see the price an account would pay. */\n\tconst guest = $derived(!page.data.userId);\n\t/** The press a visitor made before signing up, made for them after. */\n\tconst PENDING_KEY = 'auteur-studio-pending-press';\n\t/** A visitor's session, filed under their account once they have one. */\n\tconst GUEST_SESSION_KEY = 'auteur-studio-guest-session';\n\t/** A press kept past sign-up is made for them only within this long. */\n\tconst PENDING_TTL_MS = 60 * 60 * 1000;\n\n\t/** Raise the sign-up sheet, keeping the press when there is one to keep. */\n\tfunction askToSignUp(flow: string, press?: string) {\n\t\tif (press) {\n\t\t\ttry {\n\t\t\t\tlocalStorage.setItem(PENDING_KEY, JSON.stringify({ itemId: press, at: Date.now() }));\n\t\t\t} catch {\n\t\t\t\t/* private mode: they sign up and press again */\n\t\t\t}\n\t\t\t// The conversation has to be on disk too: signing in with Google\n\t\t\t// leaves the page and comes back to a fresh one.\n\t\t\tpersist();\n\t\t}\n\t\topenAuthSheet('/studio', flow);\n\t}\n\n\t/** The picker is not opened for a visitor: the upload would have nowhere to go. */\n\tfunction guardUpload(e: MouseEvent) {\n\t\tif (!guest) return;\n\t\te.preventDefault();\n\t\taskToSignUp('studio_upload');\n\t}\n\n\t/** File the session a visitor started, now that there is an account for it. */\n\tasync function recordGuestSession() {\n\t\tlet s: { slug?: string; title?: string } | null;\n\t\ttry {\n\t\t\ts = JSON.parse(localStorage.getItem(GUEST_SESSION_KEY) ?? 'null');\n\t\t\tlocalStorage.removeItem(GUEST_SESSION_KEY);\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tif (s?.slug) await announceSession(s.slug, s.title ?? '');\n\t}\n\n\t/** Make the press a visitor made before signing up.\n\t *\n\t *  Runs whenever a signed-in page comes up and when a visitor signs in on this\n\t *  one: Google comes back to a fresh page, the emailed code signs in where they\n\t *  are. Either way the conversation is back by then and the balance is asked\n\t *  for first, so the button's gate sees the free clip and not an empty purse. */\n\tasync function resumePendingPress() {\n\t\tlet p: { itemId?: string; at?: number } | null;\n\t\ttry {\n\t\t\tp = JSON.parse(localStorage.getItem(PENDING_KEY) ?? 'null');\n\t\t\tlocalStorage.removeItem(PENDING_KEY);\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tawait recordGuestSession();\n\t\tif (!p?.itemId || !p.at || Date.now() - p.at > PENDING_TTL_MS) return;\n\t\tawait refreshTokens();\n\t\tconst item = chat.find((x) => x.id === p.itemId);\n\t\tif (item?.confirm && !item.confirm.sent) await acceptConfirm(p.itemId);\n\t}\n\n\t/** What a signed-in page loads at the start, for somebody who just signed in. */\n\tfunction afterSignIn() {\n\t\tvoid loadFilms();\n\t\tvoid loadMedia();\n\t\tvoid loadRefFiles();\n\t\tvoid loadHistory();\n\t\tvoid loadVerdicts();\n\t\tvoid loadSheets().then(() => watchSheets());\n\t\twatchBatches();\n\t\tvoid resumePendingPress();\n\t}\n\tlet wasGuest = untrack(() => guest);\n\t$effect(() => {\n\t\tconst g = guest;\n\t\tif (wasGuest && !g) afterSignIn();\n\t\twasGuest = g;\n\t});\n\n\t/** Ask the server what the balance really is.\n\t *\n\t *  The seed above is the nav bar's number: a row cached for fifteen seconds,\n\t *  which is fine for showing and wrong for deciding. This page decides —\n\t *  it refuses presses — and it never navigates, so without this a balance\n\t *  that changed elsewhere would stay wrong for as long as the tab is open.\n\t *  Most of all after a purchase: coming back from the shop to a studio that\n\t *  still says you cannot afford it is the one failure this whole gate exists\n\t *  to prevent. */\n\tasync function refreshTokens() {\n\t\tif (guest) return;\n\t\ttry {\n\t\t\tconst r = (await (await fetch('/studio/api/tokens')).json()) as {\n\t\t\t\tok?: boolean;\n\t\t\t\ttokens?: number;\n\t\t\t\tfreeClip?: boolean;\n\t\t\t};\n\t\t\tif (r.ok && typeof r.tokens === 'number') tokens = r.tokens;\n\t\t\tif (r.ok && typeof r.freeClip === 'boolean') freeClip = r.freeClip;\n\t\t} catch {\n\t\t\t// The cached seed stands. A balance we could not read is not a reason to\n\t\t\t// block a press — the server refuses on its own, and that is the check\n\t\t\t// that actually protects anything.\n\t\t}\n\t}\n\n\t/** The price of pressing send, versions and angles included. */\n\tconst sendPrice = $derived(tokensForSend(composerShape.seconds, effAtOnce, freeClip));\n\n\t/** The shop, when the purse cannot cover the press. */\n\tlet buyOpen = $state(false);\n\tlet buyReason = $state<string | null>(null);\n\tlet buySource = $state<'studio_render' | 'voice_session'>('studio_render');\n\n\t/** Open it, saying what the money is for.\n\t *\n\t *  Called from the launch paths rather than from the button: the button's\n\t *  arithmetic can be out of date by a render, and the only number that\n\t *  decides anything is the one the server refused on. */\n\tfunction askForTokens(need: number) {\n\t\tconst short = Math.max(0, need - tokens);\n\t\tbuyReason = short\n\t\t\t? `you are ${short} ${short === 1 ? 'token' : 'tokens'} short of this render`\n\t\t\t: 'this render needs more tokens than you have';\n\t\tbuySource = 'studio_render';\n\t\tbuyOpen = true;\n\t}\n\t/** The same shop for a guided session, said as a session and counted as one. */\n\tfunction askForSessionTokens(need: number) {\n\t\tif (guest) {\n\t\t\taskToSignUp('studio_session');\n\t\t\treturn;\n\t\t}\n\t\tconst short = Math.max(0, need - tokens);\n\t\tbuyReason = short\n\t\t\t? `you are ${short} ${short === 1 ? 'token' : 'tokens'} short of this session`\n\t\t\t: 'this session needs more tokens than you have';\n\t\tbuySource = 'voice_session';\n\t\tbuyOpen = true;\n\t}\n\t/** What the next message will make. It reports the run, not the setting: the\n\t *  chip used to read \"2 camera angles\" with a pinned continuation in flight\n\t *  while one clip came back, which is the same lie composerShape was written\n\t *  to stop the length and the size telling. */\n\tconst batchLabel = $derived(countLabel(takes, effAngles));\n\tlet joining = $state<Record<string, boolean>>({});\n\t/** The character or location the delete button is armed on.\n\t *\n\t *  Two clicks, because one is how a face nobody meant to touch disappears —\n\t *  and a sheet is minutes of GPU time, not a row in a list. */\n\tlet dropArmed = $state('');\n\n\t/** What is being typed into a kept subject's card, before it is applied.\n\t *\n\t *  Held here rather than bound to the card's own copy of the subject, because\n\t *  that copy is a snapshot taken when the card was written and the store has\n\t *  moved since — the voice is also editable from the composer, and a rename\n\t *  applied there would be silently undone by a card still showing the old name.\n\t *  Empty means \"show what is stored\", which is the state a card should be in\n\t *  for all but the few seconds someone is editing it. */\n\tlet keptEdits = $state<Record<string, { name?: string; voice?: string }>>({});\n\n\tfunction editKept(id: string, field: 'name' | 'voice', v: string) {\n\t\tkeptEdits[id] = { ...keptEdits[id], [field]: v };\n\t}\n\n\t/** Name and voice of a subject that is already kept, edited from its own card.\n\t *\n\t *  Separate from saveSubject, which creates one. This is the case the card had\n\t *  no answer for: an uploaded character is kept the moment the picture lands, so\n\t *  there is nothing to save — but the voice arrives blank and the name arrives\n\t *  as whatever was typed in the box, and both are worth changing once the six\n\t *  views are on screen and you can see who they are.\n\t *\n\t *  An explicit button rather than saving on blur, because the voice is a\n\t *  sentence: leaving the field to re-read the sheet should not commit a half\n\t *  written one, and there is no way back from a save nobody asked for.\n\t */\n\tasync function updateKept(itemId: string, id: string, name: string, voice: string) {\n\t\tif (!id || sheetBusy[itemId]) return;\n\t\tsheetBusy[itemId] = true;\n\t\ttry {\n\t\t\tconst res = await fetch('/studio/api/sheet', {\n\t\t\t\tmethod: 'PATCH',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({ id, name: name.trim(), voice: voice.trim().slice(0, 240) })\n\t\t\t});\n\t\t\tconst r = (await res.json()) as { ok?: boolean; sheets?: StoredSheet[]; error?: string };\n\t\t\tif (r.ok && r.sheets) {\n\t\t\t\tsheets = r.sheets;\n\t\t\t\t// Back to showing what is stored, which is now what was typed.\n\t\t\t\tdelete keptEdits[itemId];\n\t\t\t} else pushError(r.error || 'that could not be updated');\n\t\t} catch (e) {\n\t\t\tpushError(`that could not be updated — ${e}`);\n\t\t} finally {\n\t\t\tsheetBusy[itemId] = false;\n\t\t}\n\t}\n\n\tasync function dropSheet(id: string) {\n\t\ttry {\n\t\t\tconst res = await fetch(`/studio/api/sheet?id=${encodeURIComponent(id)}`, {\n\t\t\t\tmethod: 'DELETE'\n\t\t\t});\n\t\t\tconst r = (await res.json()) as { ok?: boolean; sheets?: StoredSheet[] };\n\t\t\tif (r.sheets) sheets = r.sheets;\n\t\t\t// Whatever was pointing at it stops pointing at it.\n\t\t\tif (wantCharacter === id) wantCharacter = '';\n\t\t\tif (wantLocation === id) wantLocation = '';\n\t\t\tsaveSetup();\n\t\t} catch (e) {\n\t\t\tpushError(`could not remove that: ${e}`);\n\t\t} finally {\n\t\t\tdropArmed = '';\n\t\t}\n\t}\n\n\t/** Whether a clip can be continued, how exactly, and why not when it cannot.\n\t *\n\t *  It used to demand a kept character AND a kept location, because the\n\t *  workflow declares a picture in each of its two plate slots as required. But\n\t *  the workflow requires a *picture*, not a *sheet* — and the person and the\n\t *  room are both already in the clip. So a missing plate is cut out of the\n\t *  clip at launch, and the only clip that cannot be continued now is one whose\n\t *  bytes are gone.\n\t *\n\t *  `exact` is the difference that remains and it is worth stating rather than\n\t *  hiding: with both sheets the continuation is anchored to a face the\n\t *  operator approved and a room they chose, and can hold across many clips.\n\t *  From frames it is anchored to one moment of one clip, so whatever drifted\n\t *  there is inherited. */\n\tfunction contInfo(ws: string): { ok: boolean; why: string; exact: boolean; row?: LogRow } {\n\t\tconst row = logRow[ws];\n\t\tif (!row) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\texact: false,\n\t\t\t\twhy: 'this clip was made before the studio started recording what it was shot with — it cannot be extended'\n\t\t\t};\n\t\t}\n\t\tconst c = !!row.characterId && sheets.some((x) => x.id === row.characterId);\n\t\tconst l = !!row.locationId && sheets.some((x) => x.id === row.locationId);\n\t\treturn { ok: true, why: '', exact: c && l, row };\n\t}\n\n\t/** The chain this clip belongs to, oldest first.\n\t *\n\t *  Walked backwards along continuesWorkspace and then matched against the\n\t *  transcript, because the render log knows the order and the transcript knows\n\t *  the artifact ids. A gap in either returns nothing: a scene assembled from\n\t *  a chain with a hole in it would skip, and skipping quietly is worse than\n\t *  offering nothing. */\n\tfunction chainOf(ws: string): { workspace: string; artifact: string; file: string }[] {\n\t\tconst order: string[] = [];\n\t\tconst seen = new Set<string>();\n\t\tlet cur: string | undefined = ws;\n\t\twhile (cur && !seen.has(cur)) {\n\t\t\tseen.add(cur);\n\t\t\torder.unshift(cur);\n\t\t\tcur = logRow[cur]?.continuesWorkspace;\n\t\t}\n\t\tconst out: { workspace: string; artifact: string; file: string }[] = [];\n\t\tfor (const w of order) {\n\t\t\tconst it = chat.find(\n\t\t\t\t(c) =>\n\t\t\t\t\tc.kind === 'clips' &&\n\t\t\t\t\tc.artifact?.workspace === w &&\n\t\t\t\t\tc.artifact?.id &&\n\t\t\t\t\tc.artifact.files?.length\n\t\t\t);\n\t\t\tif (it?.artifact?.id) {\n\t\t\t\tout.push({ workspace: w, artifact: it.artifact.id, file: it.artifact.files[0].name });\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Not in this transcript, which is the normal case for a session\n\t\t\t// reopened from the library: the cards were never rebuilt, only the\n\t\t\t// log rows. The row now carries the address, so the chain survives a\n\t\t\t// reload. A row from before it did returns nothing, as before.\n\t\t\tconst row = logRow[w];\n\t\t\tif (!row?.clipArtifact || !row.clipFile) return [];\n\t\t\tout.push({ workspace: w, artifact: row.clipArtifact, file: row.clipFile });\n\t\t}\n\t\treturn out;\n\t}\n\n\tasync function loadVerdicts() {\n\t\tif (guest) return;\n\t\ttry {\n\t\t\tconst res = await fetch('/studio/api/renders?limit=200');\n\t\t\tconst { rows } = (await res.json()) as { rows: LogRow[] };\n\t\t\tfor (const r of rows ?? []) {\n\t\t\t\tlogRow[r.workspace] = r;\n\t\t\t\tif (r.outcome === 'kept' || r.outcome === 'rejected') {\n\t\t\t\t\tverdict[r.workspace] = r.outcome as 'kept' | 'rejected';\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// see above\n\t\t}\n\t}\n\n\t/** The fix a diagnosis produced, held until you decide what to do with it.\n\t *\n\t *  It used to be pushed straight into the transcript as another card. That\n\t *  works, but it puts a second launch button on screen for the same clip and\n\t *  buries the diagnosis three hundred pixels below the thing it is about. It\n\t *  reads better attached to the clip it explains. */\n\tlet fix = $state<Record<string, NonNullable<ChatItem['shot']>>>({});\n\t/** Set only once a replacement card actually exists. The card used to announce\n\t *  \"the next attempt is below\" from the verdict alone, which is a sentence\n\t *  that reads as a fact and was not one — a diagnosis interrupted mid-flight\n\t *  left the claim on screen with nothing under it, and no way to tell. */\n\tlet diagnosed = $state<Record<string, boolean>>({});\n\n\t/** Show the clip to a model that can see, and put the next attempt on a card.\n\t *\n\t *  A diagnosis you have to act on by hand is a diagnosis most people read and\n\t *  close, so what comes back is a whole shot — prompt and adapters both —\n\t *  ready to send. Nothing is spent until you send it. */\n\t/** Why a diagnosis did not arrive, per clip.\n\t *\n\t *  These went through pushError, and an error item takes the whole stage — so\n\t *  a failed look at one clip put the clip itself out of sight and replaced it\n\t *  with the failure, and nothing sent afterwards could be seen. A diagnosis is\n\t *  about one clip; its failure is said beside that clip and offers to look\n\t *  again. */\n\tlet diagnoseError = $state<Record<string, string>>({});\n\n\tasync function diagnose(workspace: string) {\n\t\tif (!workspace || diagnosing[workspace]) return;\n\t\tdelete diagnoseError[workspace];\n\t\t// globalThis for the same reason as in grabFrames: `document` is a snippet\n\t\t// in this file and shadows the global.\n\t\t//\n\t\t// Both players carry data-clip now. Only the transcript card did, and the\n\t\t// stage — where \"Not good\" is pressed in the simple studio — did not, so\n\t\t// every rejection given on the stage found nothing to look at. 67 verdicts\n\t\t// from 49 people since launch went down that road.\n\t\tconst video = globalThis.document.querySelector<HTMLVideoElement>(\n\t\t\t`video[data-clip=\"${CSS.escape(workspace)}\"]`\n\t\t);\n\t\tif (!video) {\n\t\t\tdiagnoseError[workspace] = 'the clip is not on screen, so there was nothing to look at.';\n\t\t\treturn;\n\t\t}\n\t\tdiagnosing[workspace] = true;\n\t\ttry {\n\t\t\tconst frames = await grabFrames(video);\n\t\t\tif (!frames.length) {\n\t\t\t\tdiagnoseError[workspace] = 'could not read any frames out of that clip.';\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst res = await fetch('/studio/api/diagnose', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({ workspace, frames })\n\t\t\t});\n\t\t\t// A platform error page is not JSON; reading it as JSON put the parser's\n\t\t\t// exception on screen.\n\t\t\tconst r = (res.headers.get('content-type') ?? '').includes('json')\n\t\t\t\t? ((await res.json().catch(() => ({}))) as {\n\t\t\t\t\t\tok?: boolean;\n\t\t\t\t\t\tshot?: ChatItem['shot'];\n\t\t\t\t\t\terror?: string;\n\t\t\t\t\t})\n\t\t\t\t: {};\n\t\t\tif (!r.ok || !r.shot) {\n\t\t\t\tdiagnoseError[workspace] =\n\t\t\t\t\tr.error || 'the diagnosis did not come back with anything usable.';\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tr.shot.wroteLoras = (r.shot.loras ?? []).map((p) => ({ ...p }));\n\t\t\tfix[workspace] = r.shot;\n\t\t\t// The diagnosis is what the note field was for. Written by whoever\n\t\t\t// actually looked rather than typed from memory, and it turns a row\n\t\t\t// that says a clip failed into one that says how.\n\t\t\tif (r.shot.why) {\n\t\t\t\tvoid fetch('/studio/api/renders', {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify({ workspace, note: r.shot.why })\n\t\t\t\t}).catch(() => {});\n\t\t\t}\n\t\t\tdiagnosed[workspace] = true;\n\t\t\tpersist();\n\t\t} catch {\n\t\t\tdiagnoseError[workspace] = 'the diagnosis did not go through — look again.';\n\t\t} finally {\n\t\t\tdiagnosing[workspace] = false;\n\t\t}\n\t}\n\n\tlet fixBusy = $state<Record<string, boolean>>({});\n\n\tasync function renderFix(workspace: string) {\n\t\tconst shot = fix[workspace];\n\t\tif (!shot || fixBusy[workspace]) return;\n\t\tfixBusy[workspace] = true;\n\t\ttry {\n\t\t\tawait launchShot(shot);\n\t\t} finally {\n\t\t\tfixBusy[workspace] = false;\n\t\t}\n\t}\n\n\t/** For when you want to read the whole brief, or change it, before spending\n\t *  three minutes on it. The fix arrives as a summary; this is the long form. */\n\tfunction openFix(workspace: string) {\n\t\tconst shot = fix[workspace];\n\t\tif (!shot) return;\n\t\tpushItem({ who: 'studio', kind: 'shot', shot });\n\t\tdelete fix[workspace];\n\t\tpersist();\n\t}\n\n\t/** Move one adapter's strength on a card that has not been sent yet.\n\t *\n\t *  The writer is held inside the range its author published, because it is\n\t *  choosing a number from a description and has no way to check the result.\n\t *  You are not held to it. You have the clip in front of you, and the author's\n\t *  range came from their material rather than yours — so the band is shown as\n\t *  a reference and the slider goes past it. */\n\tfunction setLoraStrength(itemId: string, key: string, value: number) {\n\t\tconst item = chat.find((c) => c.id === itemId);\n\t\tif (!item?.shot || item.shot.launched) return;\n\t\tconst n = Math.round(Math.min(2, Math.max(0, value)) * 20) / 20;\n\t\titem.shot.loras = (item.shot.loras ?? []).map((p) =>\n\t\t\tp.key === key ? { ...p, strength: n } : p\n\t\t);\n\t}\n\n\t/** Move an always-loaded adapter for this clip only.\n\t *\n\t *  These cannot be switched off — they are what every clip is built on — but\n\t *  the realism slider and the anatomy corrector are both worth a nudge now and\n\t *  then, and until this existed nudging one meant editing the catalogue and\n\t *  committing it. */\n\tfunction setBaseStrength(itemId: string, key: string, value: number) {\n\t\tconst item = chat.find((c) => c.id === itemId);\n\t\tconst l = loraFor(key);\n\t\tif (!item?.shot || item.shot.launched || !l) return;\n\t\tconst n = Math.round(Math.min(2, Math.max(0, value)) * 20) / 20;\n\t\titem.shot.baseLoras = { ...(item.shot.baseLoras ?? {}), [key]: n };\n\t}\n\n\t/** Back to what the adapter's author recommends. */\n\tfunction resetLoraStrength(itemId: string, key: string) {\n\t\tconst l = loraFor(key);\n\t\tif (!l) return;\n\t\tif (l.kind === 'base') setBaseStrength(itemId, key, l.strength);\n\t\telse setLoraStrength(itemId, key, l.strength);\n\t}\n\n\tfunction setShotOrientation(itemId: string, orientation: 'portrait' | 'landscape') {\n\t\tconst item = chat.find((c) => c.id === itemId);\n\t\tif (!item?.shot || item.shot.orientation === orientation) return;\n\t\tvoid respin(itemId, { seconds: item.shot.seconds, orientation });\n\t}\n\n\tlet shotBusy = $state<Record<string, boolean>>({});\n\n\t/** Switching modes changes what the composer does with the next message and\n\t *  nothing else. The transcript stays: a card you already rendered is still\n\t *  worth looking at from the other side of the switch, and a run in flight\n\t *  keeps polling. */\n\tfunction setMode(next: 'simple' | 'advanced') {\n\t\tif (mode === next) return;\n\t\tmode = next;\n\t\ttry {\n\t\t\tlocalStorage.setItem(MODE_KEY, next);\n\t\t} catch {\n\t\t\t/* private mode — the switch still works for this session */\n\t\t}\n\t\tif (!chat.some((c) => c.who === 'user')) showWelcome();\n\t}\n\n\t// --- planning the plan ----------------------------------------------------------\n\n\tasync function callPlan(body: unknown): Promise<Brief | null> {\n\t\tconst res = await fetch('/studio/api/plan', {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\tbody: JSON.stringify(body)\n\t\t});\n\t\tif (!res.ok) {\n\t\t\t// A 4xx here is a SvelteKit error(), whose body is { message }.\n\t\t\tconst m = (await res.json().catch(() => null)) as { message?: string } | null;\n\t\t\tpushError(m?.message || `Planning could not start (${res.status}).`);\n\t\t\treturn null;\n\t\t}\n\t\tconst r = (await res.json()) as { ok: boolean; brief?: Brief; error?: string };\n\t\tif (!r.ok || !r.brief) {\n\t\t\tpushError(r.error || 'The plan could not be created.');\n\t\t\treturn null;\n\t\t}\n\t\treturn r.brief;\n\t}\n\n\tasync function planFromIdea(idea: string) {\n\t\tconst b = await callPlan({ prompt: idea, sceneCount });\n\t\tif (!b) return;\n\t\toriginalPitch = idea;\n\t\tbrief = b;\n\t\tsceneCount = b.sceneCount;\n\t\tconst item = pushItem({ who: 'studio', kind: 'plan', plan: b });\n\t\tlatestPlanId = item.id;\n\t}\n\n\t/** A message typed while a plan awaits approval refines that plan: the prior\n\t *  Brief and the feedback both travel to /plan, and the revision lands as a\n\t *  new chat item while the old one collapses. */\n\tasync function refinePlan(feedback: string) {\n\t\tif (!brief) return;\n\t\tconst b = await callPlan({ prompt: originalPitch, sceneCount, prior: brief, feedback });\n\t\tif (!b) return;\n\t\tif (latestPlanId) superseded[latestPlanId] = true;\n\t\teditingPlan = false;\n\t\tbrief = b;\n\t\tsceneCount = b.sceneCount;\n\t\tconst item = pushItem({ who: 'studio', kind: 'plan', plan: b });\n\t\tlatestPlanId = item.id;\n\t}\n\n\tfunction openEdit() {\n\t\tif (!brief) return;\n\t\teditTitle = brief.title;\n\t\teditStory = brief.story;\n\t\teditStyle = brief.style;\n\t\teditingPlan = true;\n\t}\n\n\tfunction saveEdit() {\n\t\tif (!brief) return;\n\t\tbrief = {\n\t\t\t...brief,\n\t\t\ttitle: editTitle.trim() || brief.title,\n\t\t\tstory: editStory.trim() || brief.story,\n\t\t\tstyle: editStyle.trim() || brief.style\n\t\t};\n\t\tconst item = chat.find((c) => c.id === latestPlanId);\n\t\tif (item) item.plan = brief;\n\t\teditingPlan = false;\n\t}\n\n\t// --- talking to a workspace manager --------------------------------------------\n\n\tasync function managerChat(msg: string) {\n\t\ttry {\n\t\t\tconst r = await call('chat', { msg }, activeWs);\n\t\t\tconst d = r.data;\n\t\t\tif (r.offline || r.wedged) {\n\t\t\t\tpushError(r.wedged ? WEDGED_TEXT : OFFLINE_TEXT);\n\t\t\t} else if (!r.ok) {\n\t\t\t\tconst e = d as { code?: string; error?: string } | string | undefined;\n\t\t\t\tconst detail =\n\t\t\t\t\ttypeof e === 'string' ? e : `${e?.code ?? r.status} — ${(e?.error ?? '').slice(0, 300)}`;\n\t\t\t\tpushError(`Error from the harness: ${detail}`);\n\t\t\t} else {\n\t\t\t\tpushStudio(typeof d === 'string' ? d : JSON.stringify(d));\n\t\t\t\t// A manager message can schedule new work — make sure the poller is\n\t\t\t\t// awake and fast again, whatever state the backoff was in.\n\t\t\t\tstartPolling();\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tpushError(String(e));\n\t\t}\n\t}\n\n\t// --- launching --------------------------------------------------------------------\n\n\tlet launchingPlanning = $state(false);\n\n\tasync function callLaunch(body: unknown): Promise<LaunchResult> {\n\t\tconst res = await fetch('/studio/api/launch', {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\tbody: JSON.stringify(body)\n\t\t});\n\t\tif (!res.ok) {\n\t\t\tconst m = (await res.json().catch(() => null)) as { message?: string } | null;\n\t\t\treturn { ok: false, error: m?.message || `launch ${res.status}` };\n\t\t}\n\t\treturn (await res.json()) as LaunchResult;\n\t}\n\n\t/** [start] on the plan card. Opens the LLM-only planning workspace — cheap,\n\t *  no GPU. A workspace id can only be opened once (reopening is a silent\n\t *  no-op), so a retry after a failure carries a suffixed slug. */\n\tasync function launchPlanning() {\n\t\tif (!brief || launchingPlanning || planningWs) return;\n\t\tlaunchingPlanning = true;\n\t\tplanningLaunchAttempts += 1;\n\t\tconst b: Brief = {\n\t\t\t...brief,\n\t\t\tslug: planningLaunchAttempts === 1 ? brief.slug : `${brief.slug}-r${planningLaunchAttempts}`,\n\t\t\tsceneCount\n\t\t};\n\t\ttry {\n\t\t\tconst r = await callLaunch({ stage: 'planning', brief: b });\n\t\t\tif (!r.ok) {\n\t\t\t\tpushError(r.offline ? OFFLINE_TEXT : r.error || 'The workspace did not open.');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlaunchedBrief = b;\n\t\t\tplanningWs = r.workspaceId;\n\t\t\t// Before anything has been written. The point of the board is to show\n\t\t\t// the shape of the work while it is still empty.\n\t\t\tif (!boardId) boardId = pushItem({ who: 'studio', kind: 'board' }).id;\n\t\t\tstartedAt = Date.now();\n\t\t\tnow = Date.now();\n\t\t\teditingPlan = false;\n\t\t\tpushStudio(\n\t\t\t\t'Planning has started. About four minutes, still no GPU — documents appear here as they are written.'\n\t\t\t);\n\t\t\tpersist();\n\t\t\tstartPolling();\n\t\t} catch (e) {\n\t\t\tpushError(String(e));\n\t\t} finally {\n\t\t\tlaunchingPlanning = false;\n\t\t}\n\t}\n\n\t/** [start shooting] on the approval card. Collects the five approved\n\t *  document texts and opens the render workspace, whose planner prompt\n\t *  carries them inline. */\n\tasync function launchRender() {\n\t\tif (!launchedBrief || renderLaunching || renderWs) return;\n\t\trenderLaunching = true;\n\t\ttry {\n\t\t\t// Re-fetch anything that failed to load earlier — the render launch is\n\t\t\t// the one moment every document text must actually be in hand. Keys are\n\t\t\t// the ApprovedDocs field names compose.ts asserts on, not artifact keys.\n\t\t\tconst approved: Record<string, string> = {};\n\t\t\tfor (const step of PLANNING_STEPS) {\n\t\t\t\tlet body = docBody[step.artifact];\n\t\t\t\tif (!body) {\n\t\t\t\t\tconst a = (planningPoll?.artifacts ?? []).find((x) => x.key === step.artifact);\n\t\t\t\t\tconst name = a ? firstFileOfKind(a, 'text') : '';\n\t\t\t\t\tif (a && name) body = (await fetchDocBody(a.id, name)) ?? '';\n\t\t\t\t}\n\t\t\t\tif (!body) {\n\t\t\t\t\tpushError(`The ${step.label} document could not be read — shooting cannot start.`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tapproved[step.doc] = body;\n\t\t\t}\n\t\t\trenderLaunchAttempts += 1;\n\t\t\tconst b: Brief = {\n\t\t\t\t...launchedBrief,\n\t\t\t\tslug:\n\t\t\t\t\trenderLaunchAttempts === 1\n\t\t\t\t\t\t? launchedBrief.slug\n\t\t\t\t\t\t: `${launchedBrief.slug}-s${renderLaunchAttempts}`\n\t\t\t};\n\t\t\tconst r = await callLaunch({ stage: 'render', brief: b, approved });\n\t\t\tif (!r.ok) {\n\t\t\t\tpushError(r.offline ? OFFLINE_TEXT : r.error || 'The shooting workspace did not open.');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\trenderIsSheet = false;\n\t\t\trenderWs = r.workspaceId;\n\t\t\tstartedAt = Date.now();\n\t\t\tnow = Date.now();\n\t\t\t// Written down now, not at the next convenient moment. A tab closed in\n\t\t\t// the seconds after this line — a phone locked, a window shut — left a\n\t\t\t// snapshot with no workspace in it, and the run was forgotten by the one\n\t\t\t// place that could resume it.\n\t\t\tpersist();\n\n\t\t\t// Say what the crew was actually given. A workflow that failed to load\n\t\t\t// does not stop the shoot — it quietly removes an option the agents\n\t\t\t// would otherwise have had, and that is invisible unless said here.\n\t\t\tconst extras: string[] = [];\n\t\t\tconst loadedWf = r.library?.workflows.filter((w) => w.ok).map((w) => w.name) ?? [];\n\t\t\tconst failedWf = r.library?.workflows.filter((w) => !w.ok) ?? [];\n\t\t\tconst loadedSk = r.library?.skills.filter((k) => k.ok).map((k) => k.name) ?? [];\n\t\t\tconst failedSk = r.library?.skills.filter((k) => !k.ok) ?? [];\n\t\t\tif (loadedWf.length) extras.push(`Extra workflows loaded: ${loadedWf.join(', ')}.`);\n\t\t\tif (loadedSk.length) extras.push(`Extra skills loaded: ${loadedSk.join(', ')}.`);\n\t\t\tfor (const f of [...failedWf, ...failedSk]) {\n\t\t\t\textras.push(\n\t\t\t\t\t`${f.name} did not load — ${f.detail ?? 'no reason given'}. The shoot goes on without it.`\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (r.refs?.imported.length) {\n\t\t\t\textras.push(\n\t\t\t\t\t`Reference material attached: ${r.refs.imported.join(', ')}. The crew cannot see these — they go to the render as reference input, guided by the descriptions you wrote.`\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (r.refs?.error) {\n\t\t\t\textras.push(`Your reference files could not be attached — ${r.refs.error}`);\n\t\t\t}\n\n\t\t\tpushItem({ who: 'studio', kind: 'shootboard' });\n\t\t\tif (extras.length) pushStudio(extras.join('\\n\\n'));\n\t\t\tpersist();\n\t\t\tstartPolling();\n\t\t} catch (e) {\n\t\t\tpushError(String(e));\n\t\t} finally {\n\t\t\trenderLaunching = false;\n\t\t}\n\t}\n\n\t// --- polling ----------------------------------------------------------------------\n\n\t/** Everything that would make the screen change. Identical signatures back to\n\t *  back mean nothing moved, which is what the backoff keys off. */\n\tfunction signature(p: PollState): string {\n\t\tconst t = (p.tasks ?? []).map((x) => `${x.id}:${x.status}`).join(',');\n\t\tconst a = (p.artifacts ?? [])\n\t\t\t.map((x) => `${x.id}:${x.status}:${(x.files ?? []).length}`)\n\t\t\t.join(',');\n\t\treturn `${t}|${a}`;\n\t}\n\n\t/** Called wherever a render begins: the expected wait for the render now on\n\t *  the stage. Its length and resolution are the composer's, as launched; a\n\t *  continuation is read off the workspace id, which says so whichever road the\n\t *  run came in by — `continuing` is already cleared once a continuation lands. */\n\tfunction refreshClipEstimate() {\n\t\ttypicalClip = expectedClipMs(\n\t\t\tcomposerShape.seconds,\n\t\t\tcomposerShape.res,\n\t\t\t/-cont@/.test(renderWs) || !!continuing\n\t\t);\n\t}\n\n\t/** How long until the next poll.\n\t *\n\t *  A render changes nothing for minutes at a time, so `quiet` reaches\n\t *  QUIET_CYCLES early and every long run finishes on the slow cadence. The\n\t *  cost of that is not the render, it is nobody asking: measured on\n\t *  direct-mtr00ax5-5lx19, a 321s clip, the row closed 10.8s after the file\n\t *  had already landed, and on the slow cadence that gap is up to 30s.\n\t *\n\t *  So the cadence tightens where the answer is expected instead of\n\t *  everywhere. From half the expected wait until the run is overdue, a\n\t *  clip polls every eight seconds; outside that window nothing changes. On a\n\t *  five-minute render that is roughly thirty extra asks against one workspace\n\t *  — more than before and said plainly rather than hidden, but not the\n\t *  hammering the harness author asked us to avoid: the first half of every\n\t *  run keeps the old cadence, and a production is untouched entirely.\n\t *\n\t *  Bounded at the top on purpose. Past OVERDUE the page already says \"longer\n\t *  than usual\", and a run that turns out to take twenty minutes must not be\n\t *  asked every five seconds for all of them. Bounded at the bottom by the\n\t *  estimate existing at all: until a render has started there is none, and\n\t *  this returns the old cadence. */\n\tfunction pollDelay(): number {\n\t\tif (simpleRun && startedAt > 0 && typicalClip) {\n\t\t\tconst elapsed = Date.now() - startedAt;\n\t\t\tif (elapsed >= typicalClip * CLOSING_FROM) {\n\t\t\t\t// Past overdue the tight window closes — but not all the way back to\n\t\t\t\t// the slow cadence. The page is already saying \"longer than usual\" by\n\t\t\t\t// then, which means somebody is watching it, and a simulation over the\n\t\t\t\t// last dozen real durations showed the one run that went far past its\n\t\t\t\t// estimate losing eighteen seconds to a thirty-second tick it happened\n\t\t\t\t// to land badly on. Fifteen halves that without asking every eight\n\t\t\t\t// seconds for a quarter of an hour.\n\t\t\t\treturn elapsed <= typicalClip * OVERDUE ? POLL_CLOSING_MS : POLL_FAST_MS;\n\t\t\t}\n\t\t}\n\t\treturn quiet >= QUIET_CYCLES ? POLL_SLOW_MS : POLL_FAST_MS;\n\t}\n\n\tfunction stopPolling() {\n\t\tif (timer) clearTimeout(timer);\n\t\ttimer = null;\n\t\trunId += 1;\n\t\tpollingActive = false;\n\t}\n\n\tfunction startPolling() {\n\t\tstopPolling();\n\t\t// Nothing to watch is not the same as watching nothing happen. `tick` bails\n\t\t// out when there is no workspace, but it bailed out *after* the flag was\n\t\t// already true — so the live status line sat under the composer with its\n\t\t// dot pulsing and its clock climbing, announcing a render that had never\n\t\t// been launched. A session restored before its first render did exactly\n\t\t// this, and read as hung. Refuse the run instead of narrating it.\n\t\tif (!activeWs) return;\n\t\t// The one choke point where a run becomes live again: a resumed shoot, a\n\t\t// new clip, a continuation. Whatever it was, it is not a leftover now.\n\t\tstaleRun = false;\n\t\trefreshClipEstimate();\n\t\tquiet = 0;\n\t\tlastSig = '';\n\t\tsawAllDone = false;\n\t\tpollingActive = true;\n\t\ttick(runId);\n\t}\n\n\tasync function tick(id: number) {\n\t\t// The target is captured up front: a launch mid-tick retargets the loop\n\t\t// through startPolling (which bumps runId), so a stale tick simply exits.\n\t\tconst target = activeWs;\n\t\tif (id !== runId) return;\n\t\t// The workspace can also go away under a live loop — a reset, a session\n\t\t// swap. Same rule as above: stop claiming to poll.\n\t\tif (!target) {\n\t\t\tpollingActive = false;\n\t\t\treturn;\n\t\t}\n\n\t\t// The board carries the documents AND the button that starts the shoot, so\n\t\t// a production without one cannot be approved at all — there is nothing to\n\t\t// press. It was posted in exactly one place, at launch, behind a guard that\n\t\t// a stale id could hold shut; that happened, and the run became\n\t\t// unapprovable while the rail cheerfully showed it progressing.\n\t\t//\n\t\t// So it is posted from here as well. Whatever went wrong upstream — a\n\t\t// resumed tab, a guard left set, a launch path that did not run — the next\n\t\t// poll puts it back.\n\t\tif (planningWs && !boardId) {\n\t\t\tboardId = pushItem({ who: 'studio', kind: 'board' }).id;\n\t\t}\n\t\t// Only a poll that actually answered may be used to decide anything.\n\t\t// A failed poll is never progress.\n\t\tlet answered = false;\n\t\tlet fresh: PollState | null = null;\n\t\ttry {\n\t\t\tconst r = await call('poll-state', {}, target);\n\t\t\t// Fetched alongside, never instead: a failing event log must not stop\n\t\t\t// the state poll that drives everything else on screen.\n\t\t\tvoid pollActivity(target).catch(() => {});\n\t\t\tif (r.offline || r.wedged) {\n\t\t\t\toffline = true;\n\t\t\t\tquiet += 1;\n\t\t\t\tif (!offlineNoted) {\n\t\t\t\t\tofflineNoted = true;\n\t\t\t\t\tpushError(r.wedged ? WEDGED_TEXT : OFFLINE_TEXT);\n\t\t\t\t}\n\t\t\t} else if (!r.ok) {\n\t\t\t\t// A crashed workspace agent answers 500 INTERNAL_AGENT_EXECUTION_FAILED\n\t\t\t\t// on every endpoint, forever. The last good state stays on screen and\n\t\t\t\t// the harness's own words go above it — never an empty page.\n\t\t\t\toffline = false;\n\t\t\t\tconst d = r.data as { code?: string; error?: string } | string | undefined;\n\t\t\t\tlastError =\n\t\t\t\t\ttypeof d === 'string' ? d : `${d?.code ?? r.status} — ${(d?.error ?? '').slice(0, 300)}`;\n\t\t\t\tquiet += 1;\n\t\t\t\tif (!errorNoted) {\n\t\t\t\t\terrorNoted = true;\n\t\t\t\t\tpushError(`Error from the harness: ${lastError}`);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\toffline = false;\n\t\t\t\tofflineNoted = false;\n\t\t\t\terrorNoted = false;\n\t\t\t\tlastError = '';\n\t\t\t\tanswered = true;\n\t\t\t\tfresh = r.data as PollState;\n\t\t\t\tlastTick = new Date();\n\t\t\t\tconst sig = signature(fresh);\n\t\t\t\tif (sig === lastSig) quiet += 1;\n\t\t\t\telse {\n\t\t\t\t\tquiet = 0;\n\t\t\t\t\tlastSig = sig;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tlastError = String(e);\n\t\t\tquiet += 1;\n\t\t}\n\n\t\tif (answered && fresh) {\n\t\t\tif (target === renderWs && renderWs) {\n\t\t\t\trenderPoll = fresh;\n\t\t\t\tawait processRender(fresh);\n\t\t\t} else if (target === planningWs) {\n\t\t\t\tplanningPoll = fresh;\n\t\t\t\tawait processPlanning(fresh);\n\t\t\t}\n\t\t}\n\n\t\t// End of the loop, decided only on confirmed data. A production waits for\n\t\t// two consecutive polls to agree; a simple run with its clip already in\n\t\t// hand does not. The reasoning for both is at the settle decision below.\n\t\t//\n\t\t// processRender ran above, in this same tick, so an artifact carried by\n\t\t// this poll is already a card in `chat` by the time that decision is made.\n\t\tif (answered && fresh && target === activeWs) {\n\t\t\tconst ts = fresh.tasks ?? [];\n\t\t\tconst terminal =\n\t\t\t\tts.length > 0 && ts.every((t) => DONE.includes(t.status) || DEAD.includes(t.status));\n\t\t\tlet finished = false;\n\t\t\tif (renderWs) {\n\t\t\t\tconst anyDead = ts.some((t) => DEAD.includes(t.status));\n\t\t\t\t// Rendering ends when the final film was posted, or when a shoot\n\t\t\t\t// failed permanently and no assembly will be requested.\n\t\t\t\t//\n\t\t\t\t// Simple mode has neither. There is one clip and no assembly step, so\n\t\t\t\t// `finalPosted` — which needs `assemblySent` before a clip can count\n\t\t\t\t// as the film — stays false for the entire run, and a successful run\n\t\t\t\t// never concluded. Only failures did, through the anyDead branch,\n\t\t\t\t// which is why every finished row in the render log was missing its\n\t\t\t\t// elapsed time while every failed one had it.\n\t\t\t\tfinished = simpleRun ? terminal : terminal && (finalPosted || (anyDead && !assemblySent));\n\t\t\t} else {\n\t\t\t\tconst allPosted = PLANNING_STEPS.every((s) => docPhase[s.artifact] === 'posted');\n\t\t\t\tconst anyDead = ts.some((t) => DEAD.includes(t.status));\n\t\t\t\t// Planning ends (poll-wise) when every document is on screen and no\n\t\t\t\t// chain reset is in flight — the workspace just sits there while the\n\t\t\t\t// user reads. Anything that schedules new work restarts the loop.\n\t\t\t\tfinished = terminal && !chain && (allPosted || anyDead);\n\t\t\t}\n\t\t\tif (finished) {\n\t\t\t\t// Where the clip landed. Looked up before the decision below rather\n\t\t\t\t// than inside it, because in simple mode this is also what makes the\n\t\t\t\t// second confirming poll unnecessary.\n\t\t\t\tconst made = chat.find(\n\t\t\t\t\t(c) =>\n\t\t\t\t\t\tc.artifact?.workspace === renderWs &&\n\t\t\t\t\t\tc.artifact?.id &&\n\t\t\t\t\t\tc.artifact.files?.some((f) => /\\.mp4$/i.test(f.name))\n\t\t\t\t);\n\t\t\t\tconst madeFile = made?.artifact?.files?.find((f) => /\\.mp4$/i.test(f.name));\n\t\t\t\t// Two consecutive polls have to agree before a run is called finished,\n\t\t\t\t// because there are windows — the planner just finished, assembly just\n\t\t\t\t// requested — where \"all terminal\" is true for one poll while new\n\t\t\t\t// tasks are still being ingested.\n\t\t\t\t//\n\t\t\t\t// Every one of those windows belongs to a production. A simple run has\n\t\t\t\t// no planner and no assembly step, so the second poll confirms nothing\n\t\t\t\t// and costs a whole interval: 10.8s on the run measured above, and up\n\t\t\t\t// to 30s once the loop has settled on the slow cadence.\n\t\t\t\t//\n\t\t\t\t// Dropped only when the clip is already on screen, though. A task can\n\t\t\t\t// read terminal before its artifact has been posted, and closing then\n\t\t\t\t// would write the row without clipArtifact/clipFile — the one record a\n\t\t\t\t// later session has of where the clip went, and the thing that took a\n\t\t\t\t// backfill to repair last time it was missing.\n\t\t\t\tif (sawAllDone || (simpleRun && !!made?.artifact?.id && !!madeFile)) {\n\t\t\t\t\t// Close the render log's row for this run. Fire and forget: the\n\t\t\t\t\t// row is evidence, and a clip that rendered must not look failed\n\t\t\t\t\t// because the bookkeeping call did.\n\t\t\t\t\tconst dead = ts.some((t) => DEAD.includes(t.status));\n\t\t\t\t\tif (renderWs) {\n\t\t\t\t\t\t// `made` and `madeFile` are resolved above, before the settle\n\t\t\t\t\t\t// decision. Written down while the clip is in front of us: a\n\t\t\t\t\t\t// session rebuilt from this log later has no other way to find\n\t\t\t\t\t\t// it, because the harness resolves an artifact only while the\n\t\t\t\t\t\t// workspace agent is alive, and that is the thing that dies.\n\t\t\t\t\t\tvoid fetch('/studio/api/renders', {\n\t\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\t\t\tworkspace: renderWs,\n\t\t\t\t\t\t\t\tfinished: true,\n\t\t\t\t\t\t\t\t...(made?.artifact?.id && madeFile\n\t\t\t\t\t\t\t\t\t? { clipArtifact: made.artifact.id, clipFile: madeFile.name }\n\t\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t\t...(dead ? { outcome: 'failed' } : {})\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.then(() => {\n\t\t\t\t\t\t\t\tvoid loadVerdicts();\n\t\t\t\t\t\t\t\t// The close is where a dead clip is paid back.\n\t\t\t\t\t\t\t\tif (dead) void refreshTokens();\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.catch(() => {});\n\t\t\t\t\t}\n\t\t\t\t\tstopPolling();\n\t\t\t\t\t// A clip that died is not coming. The slot still named it, and the\n\t\t\t\t\t// stage's clock reads the slot: dismiss the error, or type the next\n\t\t\t\t\t// thing, and the loader came back over a render that had ended — and\n\t\t\t\t\t// a read-back is never drawn while it counts, so the next press went\n\t\t\t\t\t// nowhere. Measured 2026-10-27: a hundred minutes of it.\n\t\t\t\t\tif (simpleRun && dead && !madeFile) endDeadClip();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsawAllDone = true;\n\t\t\t} else {\n\t\t\t\tsawAllDone = false;\n\t\t\t}\n\t\t}\n\n\t\tif (id !== runId) return;\n\t\t// Past every attempt the run could have had, nothing is still working on\n\t\t// it: the connection dropped or the agent never came back, and the clock\n\t\t// below the composer counted for days. Close it as failed — the server\n\t\t// checks the same ceiling before paying the tokens back.\n\t\tif (target === renderWs && renderWs && startedAt && Date.now() - startedAt > runCeilingFor(renderWs)) {\n\t\t\tconst ws = renderWs;\n\t\t\tstopPolling();\n\t\t\tstaleRun = true;\n\t\t\tvoid fetch('/studio/api/renders', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({ workspace: ws, finished: true, outcome: 'failed' })\n\t\t\t})\n\t\t\t\t.then((r) => (r.ok ? r.json() : null))\n\t\t\t\t.then((d: { refunded?: number } | null) => {\n\t\t\t\t\tpushError(\n\t\t\t\t\t\td?.refunded\n\t\t\t\t\t\t\t? `This render never finished. Your ${d.refunded} tokens are back.`\n\t\t\t\t\t\t\t: 'This render never finished.'\n\t\t\t\t\t);\n\t\t\t\t\tvoid loadVerdicts();\n\t\t\t\t})\n\t\t\t\t.catch(() => pushError('This render never finished.'));\n\t\t\treturn;\n\t\t}\n\t\ttimer = setTimeout(() => tick(id), pollDelay());\n\t}\n\n\t// --- planning workspace: post documents, drive the chain reset ----------------------\n\n\tasync function processPlanning(p: PollState) {\n\t\tconst tasks = p.tasks ?? [];\n\t\tconst arts = p.artifacts ?? [];\n\n\t\t// Advance the chain reset, if one is in flight. The next downstream task is\n\t\t// reset only AFTER the upstream re-ran to completion AND its artifact is\n\t\t// approved again — the artifact content is what the downstream regeneration\n\t\t// reads, so a merely-terminal task is not enough.\n\t\tif (chain) {\n\t\t\tconst t = tasks.find((x) => x.key === chain!.taskKey);\n\t\t\tif (t) {\n\t\t\t\tchain.polls += 1;\n\t\t\t\tif (t.status === 'pending' || t.status === 'running') chain.armed = true;\n\t\t\t\tconst step = PLANNING_STEPS.find((s) => s.task === chain!.taskKey);\n\t\t\t\tconst a = step ? arts.find((x) => x.key === step.artifact) : undefined;\n\t\t\t\tconst done = DONE.includes(t.status) && (!step || a?.status === 'approved');\n\t\t\t\t// A re-run that never comes back approved (rejected artifact, wedged\n\t\t\t\t// task) must not hold the chain — and the poll loop — open forever.\n\t\t\t\t// ~40 polls is 10-20 minutes; a planning task re-run is ~1 minute.\n\t\t\t\tif (!done && chain.polls >= 40) {\n\t\t\t\t\tpushError(\n\t\t\t\t\t\t'The regeneration did not finish in time — the documents stay on their previous version.'\n\t\t\t\t\t);\n\t\t\t\t\tfor (const key of [chain.taskKey, ...chain.downstream]) {\n\t\t\t\t\t\tconst s = PLANNING_STEPS.find((x) => x.task === key);\n\t\t\t\t\t\tif (s && docPhase[s.artifact] === 'regen') docPhase[s.artifact] = 'posted';\n\t\t\t\t\t}\n\t\t\t\t\tchain = null;\n\t\t\t\t}\n\t\t\t\t// Armed-and-done is the normal path; the polls>=8 fallback covers a\n\t\t\t\t// re-run faster than one poll interval, so the chain cannot stall on\n\t\t\t\t// a missed observation.\n\t\t\t\telse if (done && (chain.armed || chain.polls >= 8)) {\n\t\t\t\t\tif (step) delete docPhase[step.artifact]; // back to waiting -> re-posts below\n\t\t\t\t\tconst next = chain.downstream[0];\n\t\t\t\t\tif (next) {\n\t\t\t\t\t\tconst ok = await resetTaskByKey(next, CONSISTENCY_INSTRUCTION);\n\t\t\t\t\t\tif (ok) {\n\t\t\t\t\t\t\tchain = {\n\t\t\t\t\t\t\t\ttaskKey: next,\n\t\t\t\t\t\t\t\tdownstream: chain.downstream.slice(1),\n\t\t\t\t\t\t\t\tarmed: false,\n\t\t\t\t\t\t\t\tpolls: 0\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tpushError('Could not start regenerating the next document.');\n\t\t\t\t\t\t\t// The unreset tasks still hold their old (valid) content —\n\t\t\t\t\t\t\t// hand the cards back so the user is not stuck on a card\n\t\t\t\t\t\t\t// that says \"regenerating\" forever.\n\t\t\t\t\t\t\tfor (const key of [chain.taskKey, ...chain.downstream]) {\n\t\t\t\t\t\t\t\tconst s = PLANNING_STEPS.find((x) => x.task === key);\n\t\t\t\t\t\t\t\tif (s && docPhase[s.artifact] === 'regen') docPhase[s.artifact] = 'posted';\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tchain = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tchain = null;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Report a permanently failed planning step, once.\n\t\tfor (const t of tasks) {\n\t\t\tif (DEAD.includes(t.status) && !failedNoted.has(t.id)) {\n\t\t\t\tfailedNoted.add(t.id);\n\t\t\t\tconst step = PLANNING_STEPS.find((s) => s.task === t.key);\n\t\t\t\tpushError(\n\t\t\t\t\t`A planning step stalled: ${step?.label ?? t.key}. Ask for a change to run it again.`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Post each approved document as a chat item, exactly once per version.\n\t\t// A key in `regen` is skipped: its artifact still carries the pre-reset\n\t\t// content until the chain confirms the re-run.\n\t\tfor (const step of PLANNING_STEPS) {\n\t\t\tconst phase = docPhase[step.artifact];\n\t\t\tif (phase === 'posted' || phase === 'regen') continue;\n\t\t\tconst a = arts.find((x) => x.key === step.artifact);\n\t\t\tconst t = tasks.find((x) => x.key === step.task);\n\t\t\tif (!a || a.status !== 'approved' || !t || !DONE.includes(t.status)) continue;\n\t\t\tconst name = firstFileOfKind(a, 'text');\n\t\t\tif (!name) continue;\n\t\t\tconst body = await fetchDocBody(a.id, name);\n\t\t\tdocPhase[step.artifact] = 'posted';\n\t\t\tdocAccepted[step.artifact] = false;\n\t\t\tif (body) {\n\t\t\t\tdocBody[step.artifact] = body;\n\t\t\t\tdocFile[step.artifact] = name;\n\t\t\t}\n\t\t\t// No card per document any more. They land in the board that was posted\n\t\t\t// when the run started, which is the only way to see the five as a set\n\t\t\t// with the one still being written marked as such — a card that appears\n\t\t\t// on completion can only ever show what is already finished.\n\t\t\tdocUrl[step.artifact] = fileUrl(planningWs, a.id, name);\n\t\t\tdocTaskId[step.artifact] = t.id;\n\t\t}\n\n\t\t// The approval gate: all five documents on screen, no revision in flight.\n\t\tconst allPosted = PLANNING_STEPS.every((s) => docPhase[s.artifact] === 'posted');\n\t\tif (allPosted && !chain && !renderWs && !approvalId) {\n\t\t\tconst item = pushItem({\n\t\t\t\twho: 'studio',\n\t\t\t\tkind: 'approval',\n\t\t\t\ttext: 'The plan is ready. Read anything you want to check above — once shooting starts, none of it can be changed.\\n\\nIf you want a particular face, room or movement in the film, attach it with the clip on the message box before you start. That is the last moment it can be handed to the crew.\\n\\nShooting uses GPU time and costs money.'\n\t\t\t});\n\t\t\tapprovalId = item.id;\n\t\t}\n\t}\n\n\tasync function resetTaskByKey(taskKey: string, instructions: string): Promise<boolean> {\n\t\tconst t = (planningPoll?.tasks ?? []).find((x) => x.key === taskKey);\n\t\tif (!t) return false;\n\t\ttry {\n\t\t\tconst r = await call('reset-task', { req: { taskId: t.id, instructions } }, planningWs);\n\t\t\treturn r.ok;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/** [request a change] submitted on a document card. Resets that one planning\n\t *  task with the user's instructions, then marks it and everything downstream\n\t *  for regeneration — the chain reset regenerates them in dependency order. */\n\tasync function requestChange(itemId: string, artifactKey: string) {\n\t\tconst text = (changeText[itemId] ?? '').trim();\n\t\tif (!text || changeBusy[itemId]) return;\n\t\tconst idx = PLANNING_STEPS.findIndex((s) => s.artifact === artifactKey);\n\t\tif (idx === -1) return;\n\t\tconst step = PLANNING_STEPS[idx];\n\t\tchangeBusy[itemId] = true;\n\t\ttry {\n\t\t\tconst ok = await resetTaskByKey(step.task, text);\n\t\t\tif (!ok) {\n\t\t\t\tpushError('Could not send the change request.');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfor (const s of PLANNING_STEPS.slice(idx)) {\n\t\t\t\tdocPhase[s.artifact] = 'regen';\n\t\t\t\tdocAccepted[s.artifact] = false;\n\t\t\t}\n\t\t\tchain = {\n\t\t\t\ttaskKey: step.task,\n\t\t\t\tdownstream: PLANNING_STEPS.slice(idx + 1).map((s) => s.task),\n\t\t\t\tarmed: false,\n\t\t\t\tpolls: 0\n\t\t\t};\n\t\t\t// A pending approval card no longer describes the truth — collapse it;\n\t\t\t// a fresh one posts when the regenerated set is complete.\n\t\t\tif (approvalId) {\n\t\t\t\tsuperseded[approvalId] = true;\n\t\t\t\tapprovalId = '';\n\t\t\t}\n\t\t\tchangeOpen[itemId] = false;\n\t\t\tchangeText[itemId] = '';\n\t\t\tpushStudio(\n\t\t\t\t`Rewriting the ${step.label.toLowerCase()}. Everything built on it is refreshed too, in order.`\n\t\t\t);\n\t\t\tstartPolling();\n\t\t} finally {\n\t\t\tchangeBusy[itemId] = false;\n\t\t}\n\t}\n\n\t// --- render workspace: clips, then the assembly ---------------------------------------\n\n\tfunction isShootTask(t: Task): boolean {\n\t\treturn t.key !== 'schedule_video_renders' && !ASSEMBLE_RE.test(`${t.key} ${t.title}`);\n\t}\n\n\tasync function processRender(p: PollState) {\n\t\tconst tasks = p.tasks ?? [];\n\t\tconst arts = p.artifacts ?? [];\n\t\tconst shoots = tasks.filter(isShootTask);\n\n\t\tif (!shootsAnnounced && shoots.length > 0) {\n\t\t\tshootsAnnounced = true;\n\t\t\tpushStudio(`Shoot scheduled — ${shoots.length} scenes to render.`);\n\t\t}\n\n\t\tfor (const t of tasks) {\n\t\t\tif (DEAD.includes(t.status) && !failedNoted.has(t.id)) {\n\t\t\t\tfailedNoted.add(t.id);\n\t\t\t\t// The card itself offers the retry — see the error branch in the\n\t\t\t\t// markup, which looks back for the brief rather than being told about\n\t\t\t\t// it here. A render dies on the harness's side more often than on\n\t\t\t\t// ours: \"render-infra-fatal: invalid-output: timed out\" at 340s against\n\t\t\t\t// a 2400s budget, on a chain whose previous clip took 657s and was\n\t\t\t\t// fine. The answer to that is the same button again, and it used to be\n\t\t\t\t// three scrolls up on a card that says \"launched\".\n\t\t\t\tpushError(`A shooting step stalled: ${t.title || t.key}.`);\n\t\t\t}\n\t\t}\n\n\t\t// A sheet run produces one image and nothing else, so it is handled first\n\t\t// and returns — the clip bookkeeping below counts scenes and assembles\n\t\t// films, neither of which a sheet has any business in.\n\t\tif (sheetRun) {\n\t\t\tfor (const a of arts) {\n\t\t\t\tif (a.status !== 'approved') continue;\n\t\t\t\tconst name = firstFileOfKind(a, 'image');\n\t\t\t\tif (!name || clipPosted.has(a.id)) continue;\n\t\t\t\tclipPosted.add(a.id);\n\t\t\t\tconst kind = pendingSheet?.kind ?? 'character';\n\t\t\t\tconst description = pendingSheet?.description ?? '';\n\t\t\t\tconst stage = pendingSheet?.stage ?? 'sheet';\n\t\t\t\t// A preview launched from the composer already has its card — posted at\n\t\t\t\t// the press so the stage had the subject while the harness drew it, and\n\t\t\t\t// stamped with this workspace. Fill that one rather than standing a\n\t\t\t\t// second beside it; a sheet run started from a card has none, and gets\n\t\t\t\t// one here as before.\n\t\t\t\tconst waiting = chat.find(\n\t\t\t\t\t(c) => c.kind === 'sheet' && c.sheet?.workspace === renderWs && !c.sheet.url\n\t\t\t\t);\n\t\t\t\tif (waiting?.sheet) {\n\t\t\t\t\twaiting.sheet.url = fileUrl(renderWs, a.id, name);\n\t\t\t\t\twaiting.sheet.artifact = a.id;\n\t\t\t\t\twaiting.sheet.file = name;\n\t\t\t\t\twaiting.sheet.name ??= firstWords(waiting.sheet.description ?? description, kind);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tpushItem({\n\t\t\t\t\twho: 'studio',\n\t\t\t\t\tkind: 'sheet',\n\t\t\t\t\tsheet: {\n\t\t\t\t\t\tkind,\n\t\t\t\t\t\tstage,\n\t\t\t\t\t\tdescription,\n\t\t\t\t\t\twhy: pendingSheet?.why,\n\t\t\t\t\t\tseed: pendingSheet?.seed,\n\t\t\t\t\t\tvoice: pendingSheet?.voice,\n\t\t\t\t\t\turl: fileUrl(renderWs, a.id, name),\n\t\t\t\t\t\tworkspace: renderWs,\n\t\t\t\t\t\tartifact: a.id,\n\t\t\t\t\t\tfile: name,\n\t\t\t\t\t\tname: firstWords(description, kind),\n\t\t\t\t\t\t// A preview is already spent — it exists to be looked at, not\n\t\t\t\t\t\t// launched again — so it arrives latched.\n\t\t\t\t\t\tlaunched: true\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Every approved artifact with a video file becomes a clip in the chat.\n\t\t// After the assembly request, a new artifact (or one named like a final\n\t\t// cut) is the film itself and closes the transcript.\n\t\tconst clipArts = arts\n\t\t\t.filter((a) => a.status === 'approved' && firstFileOfKind(a, 'video'))\n\t\t\t.sort((x, y) => sceneNo(x.key, x.name) - sceneNo(y.key, y.name));\n\t\tfor (const a of clipArts) {\n\t\t\tif (clipPosted.has(a.id)) continue;\n\t\t\tclipPosted.add(a.id);\n\t\t\tconst name = firstFileOfKind(a, 'video');\n\t\t\t// Before anything else is done with it — this is the one moment the\n\t\t\t// clip is known to exist and the agent is known to be answering.\n\t\t\tkeepClip(renderWs, a.id, name);\n\t\t\tconst isFinal =\n\t\t\t\tassemblySent &&\n\t\t\t\t(ASSEMBLE_RE.test(`${a.key} ${a.name} ${name}`) ||\n\t\t\t\t\t(!finalByNameOnly && !preAssemblyIds.has(a.id)));\n\t\t\tpushItem({\n\t\t\t\twho: 'studio',\n\t\t\t\tkind: 'clips',\n\t\t\t\ttext: isFinal ? 'The film is ready.' : a.name || name,\n\t\t\t\tartifact: {\n\t\t\t\t\tid: a.id,\n\t\t\t\t\tkey: a.key,\n\t\t\t\t\ttitle: isFinal ? 'A film' : a.name || name,\n\t\t\t\t\ttaskId: '',\n\t\t\t\t\tfiles: [{ name, url: fileUrl(renderWs, a.id, name) }],\n\t\t\t\t\tworkspace: renderWs\n\t\t\t\t}\n\t\t\t});\n\t\t\tif (isFinal) {\n\t\t\t\tfinalPosted = true;\n\t\t\t\tpersist();\n\t\t\t}\n\t\t}\n\n\t\t// The assembly cannot be a declared task (requires.tasks cannot reference\n\t\t// dynamically created shoots), so this page triggers it: when every shoot\n\t\t// is a success, the proven instruction goes to the manager.\n\t\tif (\n\t\t\t!assemblySent &&\n\t\t\t!simpleRun &&\n\t\t\tshoots.length > 0 &&\n\t\t\tshoots.every((t) => DONE.includes(t.status)) &&\n\t\t\ttasks.every((t) => !DEAD.includes(t.status))\n\t\t) {\n\t\t\tassemblySent = true;\n\t\t\tpersist();\n\t\t\tfor (const a of arts) preAssemblyIds.add(a.id);\n\t\t\tpushStudio('All scenes are done. Assembling the final cut.');\n\t\t\ttry {\n\t\t\t\tconst r = await call('chat', { msg: ASSEMBLY_MSG }, renderWs);\n\t\t\t\tif (r.ok && typeof r.data === 'string' && r.data.trim()) pushStudio(r.data);\n\t\t\t\telse if (!r.ok)\n\t\t\t\t\tpushError(\n\t\t\t\t\t\t'The assembly request did not go through — you can send it again from the chat.'\n\t\t\t\t\t);\n\t\t\t} catch (e) {\n\t\t\t\tpushError(`The assembly request did not go through: ${e}`);\n\t\t\t}\n\t\t\t// New work was just scheduled — wake the backoff and forget any\n\t\t\t// \"everything is terminal\" observation.\n\t\t\tstartPolling();\n\t\t}\n\t}\n\n\t// --- rail --------------------------------------------------------------------------\n\n\ttype RailStatus = 'pending' | 'running' | 'done' | 'failed' | 'regen';\n\ttype RailEntry = { id: string; label: string; status: RailStatus };\n\n\tfunction mapStatus(s: string): RailStatus {\n\t\tif (DONE.includes(s)) return 'done';\n\t\tif (DEAD.includes(s)) return 'failed';\n\t\tif (s === 'running') return 'running';\n\t\treturn 'pending';\n\t}\n\n\tconst STATUS_LABEL: Record<RailStatus, string> = {\n\t\tpending: 'waiting',\n\t\trunning: 'running',\n\t\tdone: 'done',\n\t\tfailed: 'stalled',\n\t\tregen: 'rerunning'\n\t};\n\n\t/** The full pipeline, future steps included — ghosts (pending) until a poll\n\t *  brings the real task, shoot entries from the render poll once the planner\n\t *  created them, N static ghosts from the brief before that. */\n\tconst rail = $derived.by<RailEntry[]>(() => {\n\t\t// A simple run is one task per clip. Showing it the planning chain listed\n\t\t// every document it will never write as `waiting`, above the clip that was\n\t\t// actually running — a rail describing the other mode.\n\t\tif (simpleRun) {\n\t\t\treturn (renderPoll?.tasks ?? [])\n\t\t\t\t.slice()\n\t\t\t\t.sort((x, y) => sceneNo(x.key) - sceneNo(y.key))\n\t\t\t\t.map((t) => ({ id: t.id, label: t.key, status: mapStatus(t.status) }));\n\t\t}\n\n\t\tconst b = brief;\n\t\tif (!b) return [];\n\t\t// The rail speaks the workspace's own vocabulary: these are the exact task\n\t\t// keys the harness, its event log and its terminal UI use. One name per\n\t\t// step, so a screenshot of this rail and a line in an error message are\n\t\t// talking about the same thing without translation.\n\t\tconst out: RailEntry[] = [{ id: 'plan', label: 'plan', status: 'done' }];\n\n\t\tconst ptasks = planningPoll?.tasks ?? [];\n\t\tfor (const s of PLANNING_STEPS) {\n\t\t\tconst t = ptasks.find((x) => x.key === s.task);\n\t\t\tlet status: RailStatus = t ? mapStatus(t.status) : 'pending';\n\t\t\tif (docPhase[s.artifact] === 'regen' || chain?.taskKey === s.task) status = 'regen';\n\t\t\tout.push({ id: s.task, label: s.task, status });\n\t\t}\n\n\t\tconst rtasks = renderPoll?.tasks ?? [];\n\t\tconst sched = rtasks.find((t) => t.key === 'schedule_video_renders');\n\t\tout.push({\n\t\t\tid: 'schedule',\n\t\t\tlabel: 'schedule_video_renders',\n\t\t\tstatus: sched ? mapStatus(sched.status) : 'pending'\n\t\t});\n\n\t\tconst shoots = rtasks.filter(isShootTask).sort((x, y) => sceneNo(x.key) - sceneNo(y.key));\n\t\tif (shoots.length > 0) {\n\t\t\tshoots.forEach((t, i) => out.push({ id: t.id, label: t.key, status: mapStatus(t.status) }));\n\t\t} else {\n\t\t\tfor (let i = 0; i < b.sceneCount; i++) {\n\t\t\t\tout.push({ id: `shoot-ghost-${i}`, label: `shoot_scene_${i + 1}`, status: 'pending' });\n\t\t\t}\n\t\t}\n\n\t\tconst asm = rtasks.find((t) => ASSEMBLE_RE.test(`${t.key} ${t.title}`));\n\t\tout.push({\n\t\t\tid: 'assemble',\n\t\t\t// The only step whose key we cannot know in advance: the manager agent\n\t\t\t// names this task itself when it creates it at runtime (we have seen\n\t\t\t// both assemble_final_film and combine_clips_final_video). Show the\n\t\t\t// real key once it exists, a placeholder while it is still a ghost.\n\t\t\tlabel: asm?.key ?? 'assemble_final_film',\n\t\t\t// `assemblySent` survives a reload, so on a stale run this pill drew\n\t\t\t// `rendering` forever — the live line's claim, made a second time by\n\t\t\t// the rail beside it.\n\t\t\tstatus: finalPosted\n\t\t\t\t? 'done'\n\t\t\t\t: asm\n\t\t\t\t\t? mapStatus(asm.status)\n\t\t\t\t\t: assemblySent && !staleRun\n\t\t\t\t\t\t? 'running'\n\t\t\t\t\t\t: 'pending'\n\t\t});\n\t\treturn out;\n\t});\n\n\tconst railDone = $derived(rail.filter((e) => e.status === 'done').length);\n\tconst railRunning = $derived(rail.find((e) => e.status === 'running' || e.status === 'regen'));\n\tconst railSummary = $derived.by(() => {\n\t\tconst head = `${railDone}/${rail.length} steps done`;\n\t\treturn railRunning ? `${head} · ${railRunning.label.toLowerCase()}` : head;\n\t});\n\n\t/** Precise rather than rounded: during a twenty-minute run the difference\n\t *  between \"4 minutes\" and \"4m 31s\" is the difference between a page that\n\t *  looks frozen and one that is visibly counting. */\n\tfunction elapsedLabel(ms: number): string {\n\t\tconst s = Math.max(0, Math.floor(ms / 1000));\n\t\tconst m = Math.floor(s / 60);\n\t\treturn m === 0 ? `${s}s` : `${m}m ${String(s % 60).padStart(2, '0')}s`;\n\t}\n\n\t// --- resume / reset -----------------------------------------------------------------\n\n\t/** Everything a reload has to bring back.\n\t *\n\t *  This used to save the run's identity only, on the theory that the\n\t *  transcript would rebuild itself from poll state. It does — right up until\n\t *  the workspace agent dies, which is when a reload is most likely and the\n\t *  poll returns nothing. A finished production would then reload into an\n\t *  empty page: the clips still on disk, the conversation simply gone.\n\t *\n\t *  So the transcript is saved as itself. The clip cards keep working because\n\t *  their urls resolve against the local copy, not the harness.\n\t */\n\tfunction snapshot(withBodies: boolean) {\n\t\treturn {\n\t\t\tsessionSlug,\n\t\t\t// What you are in the middle of continuing.\n\t\t\t//\n\t\t\t// It lived only in memory, so a reload between pressing \"continue this\"\n\t\t\t// and sending the next beat dropped the intent silently — and the text\n\t\t\t// you then typed would have been shot as an unrelated new clip rather\n\t\t\t// than as the next part of the scene. The banner disappears with it, so\n\t\t\t// there is a version of this that is merely confusing and a version that\n\t\t\t// costs a render.\n\t\t\tcontinuing,\n\t\t\tbrief,\n\t\t\tlaunchedBrief,\n\t\t\tplanningWs,\n\t\t\trenderWs,\n\t\t\tassemblySent,\n\t\t\tstartedAt,\n\t\t\t// the conversation\n\t\t\tchat: withBodies\n\t\t\t\t? chat\n\t\t\t\t: chat.map((i) =>\n\t\t\t\t\t\ti.artifact?.body ? { ...i, artifact: { ...i.artifact, body: undefined } } : i\n\t\t\t\t\t),\n\t\t\tsuperseded,\n\t\t\tlatestPlanId,\n\t\t\tboardId,\n\t\t\tapprovalId,\n\t\t\tgateOpen,\n\t\t\t// document cards and the revision chain behind them\n\t\t\tdocPhase,\n\t\t\tdocAccepted,\n\t\t\tlatestDocItem,\n\t\t\tdocBody: withBodies ? docBody : {},\n\t\t\tdocFile,\n\t\t\tdocUrl,\n\t\t\tdocTaskId,\n\t\t\t// the guards that stop a live poll re-posting what is already shown\n\t\t\tclipPosted: [...clipPosted],\n\t\t\tfailedNoted: [...failedNoted],\n\t\t\tpreAssemblyIds: [...preAssemblyIds],\n\t\t\tseenActivity: [...seenActivity],\n\t\t\tshootsAnnounced,\n\t\t\tfinalPosted,\n\t\t\tfinalByNameOnly,\n\t\t\t// Without this the greeting returns as the transcript's first paragraph\n\t\t\t// on every reopen: the item is in `chat`, but the id that tells the\n\t\t\t// template it is the greeting was minted fresh on load and no longer\n\t\t\t// matches it, so it falls through to the plain-text branch.\n\t\t\twelcomeId,\n\t\t\t// The text a re-render is a re-render OF. Without it a clip launched\n\t\t\t// after reopening files itself under the title 'Direct render' and\n\t\t\t// writes an empty request into the render log — the one record that is\n\t\t\t// supposed to say what was asked for.\n\t\t\tlastRequest\n\t\t};\n\t}\n\n\tfunction persist() {\n\t\tconst slug = runSlug;\n\t\tif (!slug) return;\n\t\t// Document bodies are most of the payload and the least of the loss: the\n\t\t// transcript still reads, the cards fall back to a link. So a run too big\n\t\t// for the quota drops them rather than saving nothing at all.\n\t\tfor (const withBodies of [true, false]) {\n\t\t\ttry {\n\t\t\t\tlocalStorage.setItem(runKey(slug), JSON.stringify(snapshot(withBodies)));\n\t\t\t\tlocalStorage.setItem(POINTER_KEY, slug);\n\t\t\t\treturn;\n\t\t\t} catch {\n\t\t\t\t/* quota, or private mode — try smaller, then give up */\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Save whenever the conversation grows.\n\t *\n\t *  The explicit calls elsewhere mark milestones — a launch, an approval, the\n\t *  final cut — and between them the transcript fills with documents, clips and\n\t *  progress lines that a reload would otherwise lose. Debounced because\n\t *  activity arrives in bursts on each poll, and one write per burst is\n\t *  plenty. */\n\tlet persistTimer: ReturnType<typeof setTimeout> | null = null;\n\t$effect(() => {\n\t\tchat.length;\n\t\t// The precondition is a slug to file it under, and nothing more. It used\n\t\t// to also demand a brief — which only an advanced production has — so in\n\t\t// simple mode the conversation was written once, by the explicit call at\n\t\t// launch, and then never again: the clip that came back, the activity and\n\t\t// every later message were all dropped. That is most of the list.\n\t\tif (!runSlug) return;\n\t\tif (persistTimer) clearTimeout(persistTimer);\n\t\tpersistTimer = setTimeout(persist, 800);\n\t});\n\n\tfunction forget() {\n\t\tif (persistTimer) clearTimeout(persistTimer);\n\t\ttry {\n\t\t\t// Only the pointer. Each run's conversation stays where it is — that is\n\t\t\t// what the sidebar is for, and starting a new production is not a\n\t\t\t// reason to lose the last one.\n\t\t\tlocalStorage.removeItem(POINTER_KEY);\n\t\t\tlocalStorage.removeItem(RESUME_KEY);\n\t\t\tsessionStorage.removeItem(RESUME_KEY);\n\t\t} catch {\n\t\t\t/* ignore */\n\t\t}\n\t}\n\n\tfunction reset() {\n\t\tstopPolling();\n\t\t// The server goes on with it; this tab only stops watching. The session\n\t\t// asks again when it is opened.\n\t\tunwatchGeneration();\n\t\tstaleRun = false;\n\t\tforget();\n\t\tchat = [];\n\t\tsuperseded = {};\n\t\tbrief = null;\n\t\toriginalPitch = '';\n\t\tlaunchedBrief = null;\n\t\tlatestPlanId = '';\n\t\tsessionSlug = '';\n\t\tplanningWs = '';\n\t\trenderWs = '';\n\t\tplanningPoll = null;\n\t\trenderPoll = null;\n\t\toffline = false;\n\t\tlastError = '';\n\t\tdocPhase = {};\n\t\tdocAccepted = {};\n\t\tlatestDocItem = {};\n\t\tdocBody = {};\n\t\tdocFile = {};\n\t\tdocUrl = {};\n\t\tdocTaskId = {};\n\t\t// The board is posted once per production, guarded by this id. Left set, the\n\t\t// guard held on the second run and the board never appeared — the tasks were\n\t\t// running, the rail showed them, and the transcript showed nothing.\n\t\tboardId = '';\n\t\t// Per-run too: the activity feed dedupes against this, the retry alarm\n\t\t// counts against these, and the stop button arms into these.\n\t\tseenActivity = new Set<string>();\n\t\tretryCounts = new Map<string, number>();\n\t\tretryWarned = new Set<string>();\n\t\tstopArmed = false;\n\t\tstopping = false;\n\t\tgateOpen = {};\n\t\tapprovalId = '';\n\t\tchain = null;\n\t\tshootsAnnounced = false;\n\t\tassemblySent = false;\n\t\tshotBusy = {};\n\t\tlastRequest = '';\n\t\tfinalPosted = false;\n\t\teditingPlan = false;\n\t\texpanded = {};\n\t\tchangeOpen = {};\n\t\tchangeText = {};\n\t\tchangeBusy = {};\n\t\trailOpen = false;\n\t\tshowDetails = false;\n\t\tclipPosted.clear();\n\t\tfailedNoted.clear();\n\t\tpreAssemblyIds.clear();\n\t\tfinalByNameOnly = false;\n\t\terrorNoted = false;\n\t\tofflineNoted = false;\n\t\tplanningLaunchAttempts = 0;\n\t\trenderLaunchAttempts = 0;\n\t\twelcomeId = '';\n\t\tshowWelcome();\n\t\t// Every \"busy\" flag, because a new production is not busy.\n\t\t//\n\t\t// `sending` was left set, and it is the one that guards the send: a hung\n\t\t// round leaves it true, and from then on \"New clip\" hands you a page that\n\t\t// looks completely fresh — empty stage, welcome, examples — with a dead\n\t\t// send button behind it. Nothing on screen says why, because everything on\n\t\t// screen was reset except the one flag that mattered. The others are here\n\t\t// for the same reason rather than because they were seen to stick.\n\t\tsending = false;\n\t\tfilmBusy = false;\n\t\tlaunchingPlanning = false;\n\t\tjoining = {};\n\n\t\t// The stage, which had no state to clear when this was written.\n\t\t//\n\t\t// A new production opened with the continuation bar still on it, offering\n\t\t// to continue a clip from the session that had just been thrown away —\n\t\t// there was nothing to continue, and the one control that says what the\n\t\t// next message does was lying about it.\n\t\tcontinuing = null;\n\t\tcontOffFor = '';\n\t\tpinSeam = true;\n\t\tstageSel = '';\n\t\tstageStartedAt = 0;\n\t\tstageWaitFrom = '';\n\t\tstageWaitBlurUrl = '';\n\t\tstageAutoplayId = '';\n\t}\n\n\t// --- small UI helpers ------------------------------------------------------------------\n\n\t/** How tall the box may get before it starts scrolling instead.\n\t *\n\t *  Five lines. Long orders are normal here — a shot description runs to a\n\t *  paragraph — and a box that stays one line makes you write blind. Growing\n\t *  without a limit is the other failure: paste a brief and the composer eats\n\t *  the clip it was written for. Measured in lines rather than pixels so it\n\t *  holds at any text size. */\n\tconst COMPOSER_MAX_LINES = 5;\n\tfunction grow(el: HTMLTextAreaElement | null) {\n\t\tif (!el) return;\n\t\tel.style.height = 'auto';\n\t\tconst line = parseFloat(getComputedStyle(el).lineHeight);\n\t\t// A line-height of `normal` parses to NaN; 24px is this composer's own.\n\t\tconst max = (Number.isFinite(line) ? line : 24) * COMPOSER_MAX_LINES;\n\t\tconst want = el.scrollHeight;\n\t\tel.style.height = `${Math.min(want, max)}px`;\n\t\t// Only when it is actually capped, so a short message has no scrollbar.\n\t\tel.style.overflowY = want > max ? 'auto' : 'hidden';\n\t}\n\n\t/** Back to one row.\n\t *\n\t *  Not grow() with an empty value: clearing `input` is a state assignment\n\t *  that has not reached the DOM yet when this runs, so measuring scrollHeight\n\t *  here measures the message that was just sent and the box stays tall.\n\t *  Dropping the inline height hands the size back to the rows attribute,\n\t *  which needs no measurement to be right. */\n\tfunction shrink(el: HTMLTextAreaElement | null) {\n\t\tif (!el) return;\n\t\tel.style.height = '';\n\t\tel.style.overflowY = '';\n\t}\n\n\tasync function useExample(text: string) {\n\t\tinput = text;\n\t\tcomposer?.focus();\n\t\t// After the flush, not before it: grow() measures scrollHeight, and until\n\t\t// Svelte has written the new value into the element that is the height of\n\t\t// the old one. Measuring early left a three-line seed clipped to one.\n\t\tawait flush();\n\t\tgrow(composer);\n\t}\n\n\t/** Collapse threshold for document prose — roughly twelve lines of text. */\n\tfunction isLong(text: string): boolean {\n\t\treturn text.length > 700 || text.split('\\n').length > 12;\n\t}\n\n\t// Follow the conversation, but only when the reader is already near the\n\t// bottom — nobody's scroll position gets yanked while they re-read a scene.\n\tlet bottomEl = $state<HTMLElement | null>(null);\n\t/** The transcript element — the page's only scroll container. */\n\tlet scrollEl = $state<HTMLElement | null>(null);\n\n\t// ── the stage ────────────────────────────────────────────────────────────\n\t//\n\t// One fixed surface where the transcript used to scroll. The clip being made\n\t// is the only thing on screen, and the machinery that makes it — the read-back,\n\t// the brief, the launch — runs behind it without a card.\n\t//\n\t// Built beside the transcript rather than in place of it: the engine is the\n\t// same functions either way, and a flag that can be turned off is the\n\t// difference between trying a new surface and betting the working one on it.\n\tconst STAGE_UI = true;\n\n\t/** The workspaces that make a sheet rather than a clip.\n\t *\n\t *  Both prefixes are minted in exactly one place each — `turn-` in the\n\t *  turnaround route, `-sheet@` in sheetWorkspaceId — so matching on them is a\n\t *  fact about this app rather than a guess about a string. */\n\tfunction isSheetWorkspace(ws: string): boolean {\n\t\treturn ws.startsWith('turn-') || ws.includes('-sheet@');\n\t}\n\n\t/** Every clip in this session, oldest first, read off the transcript the\n\t *  engine already keeps. The stage renders this rather than the cards.\n\t *\n\t *  A sheet's render is not one of them, and it used to be. The six views come\n\t *  back as an mp4 — a slow orbit of the person — so it satisfied the only test\n\t *  here and landed on the stage as the newest clip, wearing \"Add to film\",\n\t *  \"Good\" and \"Not good\". None of those means anything about a turnaround:\n\t *  it is not a shot, it cannot be cut into a film, and rating it rates\n\t *  nothing. It also pushed the actual last clip out of the way in the middle\n\t *  of making a character, which is when you are least able to explain what\n\t *  you are looking at. The views belong to the sheet, and the sheet says so\n\t *  in its own line. */\n\tlet stageClips = $derived(\n\t\tchat.filter(\n\t\t\t(c) =>\n\t\t\t\tc.artifact?.files?.some((f) => /\\.mp4$/i.test(f.name)) &&\n\t\t\t\t!isSheetWorkspace(c.artifact?.workspace ?? '')\n\t\t)\n\t);\n\t/** The newest clip, which is what the stage follows on its own. */\n\tlet stageNewest = $derived(stageClips[stageClips.length - 1] ?? null);\n\t/** Which clip the operator clicked back to, by workspace. Cleared whenever a\n\t *  new one lands: a strip that keeps showing an old take while a fresh one\n\t *  arrives is hiding the thing the wait was for. */\n\tlet stageSel = $state('');\n\t$effect(() => {\n\t\tif (stageNewest) stageSel = '';\n\t});\n\t/** The transcript card for what is on the stage, when there is one. The action\n\t *  row needs it — rating and adding to the film both work on a card — and a\n\t *  clip recovered from the log alone has none, so the row hides itself. */\n\tlet stageClip = $derived(\n\t\t(stageSel && stageClips.find((c) => c.artifact?.workspace === stageSel)) || stageNewest\n\t);\n\t/** What is down the left edge: the last four of THE CHAIN, newest first, the\n\t *  one on the stage among them. Including it is what makes the strip a place\n\t *  rather than a leftovers pile — you can see where you are.\n\t *\n\t *  The chain rather than the transcript, because a session reopened from the\n\t *  library has one and not the other: the log knows what continues what, and\n\t *  now also where each clip is. Falls back to the transcript for a session that\n\t *  has clips the log cannot place — an older run, or one still in flight. */\n\t/** Every clip this conversation holds, takes included, newest last.\n\t *\n\t *  A batch's takes live on one card and only the kept one is ever written to\n\t *  that card's `artifact` — so from the strip's point of view the others did\n\t *  not exist. Reading them here rather than only writing cards for them at\n\t *  the moment of keeping is what makes it true backwards as well: a batch\n\t *  chosen from yesterday, before any of this, still shows its alternatives. */\n\tlet stageAllClips = $derived(\n\t\t(() => {\n\t\t\tconst out = stageClips.map((c) => ({\n\t\t\t\tkey: c.id,\n\t\t\t\turl: c.artifact?.files?.[0]?.url ?? '',\n\t\t\t\tworkspace: c.artifact?.workspace ?? '',\n\t\t\t\tartifact: c.artifact?.id ?? '',\n\t\t\t\tfile: c.artifact?.files?.[0]?.name ?? ''\n\t\t\t}));\n\t\t\tconst seen = new Set(out.map((e) => e.workspace));\n\t\t\tfor (const c of chat) {\n\t\t\t\tfor (const r of c.takes?.runs ?? []) {\n\t\t\t\t\tif (r.state !== 'ready' || !r.clip || seen.has(r.clip.workspace)) continue;\n\t\t\t\t\tseen.add(r.clip.workspace);\n\t\t\t\t\tout.push({\n\t\t\t\t\t\tkey: `${c.id}-${r.slug}`,\n\t\t\t\t\t\turl: fileUrl(r.clip.workspace, r.clip.artifact, r.clip.file),\n\t\t\t\t\t\tworkspace: r.clip.workspace,\n\t\t\t\t\t\tartifact: r.clip.artifact,\n\t\t\t\t\t\tfile: r.clip.file\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn out;\n\t\t})()\n\t);\n\n\tlet stageThumbs = $derived(\n\t\t(() => {\n\t\t\tconst ws = stageNewest?.artifact?.workspace ?? '';\n\t\t\tconst chain = ws ? chainOf(ws) : [];\n\t\t\tif (chain.length > 1) {\n\t\t\t\tconst links = chain\n\t\t\t\t\t.slice(-4)\n\t\t\t\t\t.reverse()\n\t\t\t\t\t.map((c) => ({\n\t\t\t\t\t\tkey: c.workspace,\n\t\t\t\t\t\turl: fileUrl(c.workspace, c.artifact, c.file),\n\t\t\t\t\t\tworkspace: c.workspace,\n\t\t\t\t\t\tartifact: c.artifact,\n\t\t\t\t\t\tfile: c.file\n\t\t\t\t\t}));\n\t\t\t\t// A clip in this conversation that the chain does not know about.\n\t\t\t\t//\n\t\t\t\t// The chain follows what continues what, and the other takes of a\n\t\t\t\t// batch continue nothing — they are alternatives to the one that\n\t\t\t\t// does. So the moment a chain existed they fell out of the strip\n\t\t\t\t// entirely, which is the same disappearance the batch card had:\n\t\t\t\t// rendered, paid for, and nowhere to be seen.\n\t\t\t\t//\n\t\t\t\t// Beside the newest rather than after the whole chain, because that\n\t\t\t\t// is what they are alternatives TO, and it is the only position\n\t\t\t\t// where a cap cannot push them off the end.\n\t\t\t\tconst known = new Set(links.map((e) => e.workspace));\n\t\t\t\tconst spare = stageAllClips\n\t\t\t\t\t.filter((e) => e.workspace && !known.has(e.workspace))\n\t\t\t\t\t.slice(-3)\n\t\t\t\t\t.reverse();\n\t\t\t\treturn [links[0], ...spare, ...links.slice(1)].filter(Boolean).slice(0, 5);\n\t\t\t}\n\t\t\t// The transcript id, not the workspace. An assembled film carries the last\n\t\t\t// clip's workspace, so keying by workspace put two entries under one key\n\t\t\t// and Svelte tore the whole page down with `each_key_duplicate`. The keys\n\t\t\t// in this list are unique by construction.\n\t\t\treturn stageAllClips.slice(-4).reverse();\n\t\t})()\n\t);\n\t/** Which strip entry is on the stage, by workspace. */\n\tlet stageShownWs = $derived(stageSel || (stageNewest?.artifact?.workspace ?? ''));\n\t/** The picture to play: the selected entry's, or the newest clip's. */\n\t/** The strip entry on the stage, with the three ids the film needs. Every clip\n\t *  in the chain has them now — the log carries the address — so the actions\n\t *  work on whichever one is being looked at, not only on the newest. */\n\tlet stageShown = $derived(stageThumbs.find((t) => t.workspace === stageShownWs) ?? null);\n\t/** The picture to play: the clicked entry's, or the newest thing's OWN file.\n\t *\n\t *  Its own file, not the strip entry that shares its workspace. An assembled\n\t *  film carries the last clip's workspace — that is how continuing a film\n\t *  continues where it ends — so looking the url up by workspace found the\n\t *  clip and played that instead. Export ran, ffmpeg wrote 27 MB, and the\n\t *  stage went on showing the same six seconds: \"nothing happened\", from the\n\t *  only place anybody was looking. */\n\tlet stageShownUrl = $derived(\n\t\tstageSel\n\t\t\t? (stageThumbs.find((t) => t.workspace === stageSel)?.url ?? '')\n\t\t\t: (stageNewest?.artifact?.files?.[0]?.url ?? '')\n\t);\n\n\t/** Each clip's length, read off the <video> element once its metadata lands.\n\t *  Nothing upstream carries it: the artifact is a filename and a url. */\n\tlet clipSecs = $state<Record<string, number>>({});\n\tfunction clipClock(sec?: number): string {\n\t\tif (!sec || !Number.isFinite(sec)) return '';\n\t\tconst s = Math.round(sec);\n\t\treturn `${Math.floor(s / 60)}:${String(s % 60).padStart(2, '0')}`;\n\t}\n\t/** An error that arrived after the last clip — anything older has been\n\t *  answered by a clip landing and is not what the operator is looking at. */\n\tlet stageError = $derived(\n\t\t(() => {\n\t\t\tconst after = stageNewest ? chat.indexOf(stageNewest) : -1;\n\t\t\t// Nor once the operator has said something since. An error is news about\n\t\t\t// the last attempt, and it held the stage against everything after it:\n\t\t\t// a message sent then went into the conversation and was never drawn,\n\t\t\t// nor was its read-back, so people sent it again — four times in one\n\t\t\t// test, all four arriving at once later. The error was persisted, so a\n\t\t\t// reload brought the same wall back. The next message is the operator\n\t\t\t// moving on, and the stage moves with them.\n\t\t\tlet lastSaid = -1;\n\t\t\tfor (let i = chat.length - 1; i >= 0; i--) {\n\t\t\t\tif (chat[i].who === 'user') {\n\t\t\t\t\tlastSaid = i;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst errs = chat.filter((c, i) => i > after && i > lastSaid && c.kind === 'error');\n\t\t\tconst last = errs[errs.length - 1] ?? null;\n\t\t\t// Not while looking at an older clip: the error belongs to the end of the\n\t\t\t// chain, and stepping back through the strip is not the moment to be told\n\t\t\t// about it again.\n\t\t\treturn stageSel ? null : last;\n\t\t})()\n\t);\n\n\t/** When the current wait began. Set on send, cleared when a clip lands.\n\t *\n\t *  Its own clock rather than a reading of the render machinery: the stage only\n\t *  needs to know that something is being made and for how long, and every other\n\t *  answer to that question is spread across the launch, the poll and the chain. */\n\tlet stageStartedAt = $state(0);\n\tlet stageNow = $state(Date.now());\n\t$effect(() => {\n\t\tif (!stageClockFrom) return;\n\t\tconst t = setInterval(() => (stageNow = Date.now()), 500);\n\t\treturn () => clearInterval(t);\n\t});\n\t/** The clip that arrived while this page was watching for it.\n\t *\n\t *  Only that one plays by itself. Autoplay on every load meant opening the app\n\t *  started making noise in a tab nobody was looking at — and the clips have\n\t *  sound, which is the whole point of them. A finished render is different: the\n\t *  operator has been waiting for it and pressing play is a step they did not\n\t *  ask for. */\n\tlet stageAutoplayId = $state('');\n\t/** RAT-515: `studio_clip_viewed`, once per clip per tab — the first time its\n\t *  stage video actually plays. A plain record, not state: nothing on screen\n\t *  reads it. */\n\tconst viewedClips: Record<string, true> = {};\n\tfunction noteClipViewed(clipId: string) {\n\t\tif (!clipId || viewedClips[clipId]) return;\n\t\tviewedClips[clipId] = true;\n\t\ttrackStudioClipViewed({ autoplayed: clipId === stageAutoplayId });\n\t}\n\t/** Which clip was newest when the current wait began.\n\t *\n\t *  Without it the wait ended the instant it started: the effect below cleared\n\t *  the clock as soon as it saw a clip on the stage, and there is always a clip\n\t *  on the stage — the one being continued. So the phase never reached\n\t *  'working', the loader never appeared, and the previous take sat there\n\t *  looking finished for the whole six minutes. */\n\tlet stageWaitFrom = $state('');\n\t/** The clip this wait continues, blurred behind the loader.\n\t *\n\t *  Only for a continuation: the picture behind it is then genuinely where the\n\t *  new one starts, so it says something true about what is coming. For a fresh\n\t *  clip there is nothing honest to show — the last take is a different scene —\n\t *  and a picture that has nothing to do with the render would read as progress\n\t *  on it. That case gets the dot field instead. */\n\tlet stageWaitBlurUrl = $state('');\n\t/** The clip's rendered width, measured rather than assumed. The composer under\n\t *  it is set to match: the two read as one object when their edges line up, and\n\t *  the width depends on the clip's aspect ratio and the height it was given, so\n\t *  no fixed number could follow it. */\n\tlet stageVideoW = $state(0);\n\t/** The shown clip's own shape, straight off the file.\n\t *\n\t *  `max-height: 100%` only means anything when the box above it has a height\n\t *  to take the percentage of, and the picture's box is sized to the picture —\n\t *  auto height, so the limit silently evaluated to no limit. A 16:9 clip never\n\t *  noticed: it runs out of width first and stops well short. A 9:16 one is\n\t *  taller than the room it is given, so it drew its full height regardless and\n\t *  the composer, painted after it, ended up lying across the bottom of the\n\t *  picture — fifty-two points of clip behind the chip row and the field.\n\t *\n\t *  So the box gets a real height, but only when the clip is the shape that\n\t *  needs it. Given one unconditionally, a landscape clip on a phone leaves its\n\t *  box standing full-height behind a picture a third that tall, and the marks\n\t *  that ride on the picture — kept, good, not good — float in the black above\n\t *  it. Measured: 322x572 in a 572 slot with it, 335x188 in a 335x188 box\n\t *  without. */\n\tlet stageVidW = $state(0);\n\tlet stageVidH = $state(0);\n\t/** Teardown for the audio rules installed on mount. */\n\tlet cleanupAudio: (() => void) | null = null;\n\t/** The brief behind what is on the stage.\n\t *\n\t *  The stage deliberately shows no prompt — that was the point of it. But the\n\t *  brief is still what decides how a clip comes back, and when one misses,\n\t *  editing the words and sending again is the shortest fix there is. The\n\t *  button beside Continue puts it back in the composer, where the send arrow\n\t *  already lives. */\n\t// A NEW clip landing is what ends the wait — not the presence of an old one.\n\t$effect(() => {\n\t\tif (!stageStartedAt || !stageNewest) return;\n\t\tif (stageNewest.id === stageWaitFrom) return;\n\t\tstageAutoplayId = stageNewest.id;\n\t\tstageStartedAt = 0;\n\t});\n\n\t/** From the press to the clip: the writing, as this machine has measured it,\n\t *  then a render of this length and resolution from the table in timings.ts.\n\t *\n\t *  The card beside the button and the loader after the press both quote this,\n\t *  so the two never disagree. They used to: the card showed a median of this\n\t *  browser's past renders, blind to length, while the loader worked the length\n\t *  through a formula — two numbers for one wait, both wrong. */\n\tfunction pressToClipMs(continuation: boolean): number {\n\t\treturn (\n\t\t\t(typicalWait('prompt') ?? 0) +\n\t\t\texpectedClipMs(composerShape.seconds, composerShape.res, continuation)\n\t\t);\n\t}\n\tlet stageEtaMs = $derived(pressToClipMs(!!continuing));\n\t/** A render this page did not start, or started before a reload.\n\t *\n\t *  `stageStartedAt` is memory and dies with the tab; `startedAt` is written\n\t *  into the session and comes back with it. Reload during a six-minute render\n\t *  and the stage went blank — welcome screen, examples, no loader — while the\n\t *  GPU was still working and the old one-line counter below carried on\n\t *  ticking. The clip is in flight until its own workspace is the newest thing\n\t *  on the stage. */\n\t/** Whether the run holding the render slot is a sheet rather than a clip.\n\t *\n\t *  `$state`, and set at every launch rather than only at the sheet's: a flag\n\t *  that is only ever turned on stops being a fact about the current run the\n\t *  first time a clip follows a sheet. */\n\tlet renderIsSheet = $state(false);\n\n\tlet renderInFlight = $derived(\n\t\t!!renderWs &&\n\t\t\t// A sheet is not a clip, and the stage is the clip's.\n\t\t\t//\n\t\t\t// Sheets take the same render slot as a shot — one slot, deliberately —\n\t\t\t// so launching one sets renderWs and startedAt exactly as a clip does.\n\t\t\t// But this clock only stops when the newest thing on the stage IS that\n\t\t\t// workspace, and a sheet never puts a clip there: it draws six views into\n\t\t\t// the library. So the loader started, found nothing to wait for, climbed\n\t\t\t// to its 97% cap and sat there for the whole ten minutes a turnaround\n\t\t\t// takes — announcing a video that was never coming, over a picture that\n\t\t\t// had already arrived. The sheet has its own line under the composer and\n\t\t\t// says its own name in it.\n\t\t\t!renderIsSheet &&\n\t\t\tstartedAt > 0 &&\n\t\t\t// The slot has to still be about something unfinished. This test reads\n\t\t\t// \"the newest clip is not the one we are waiting for\", which is only\n\t\t\t// evidence of a running render while the slot is current — and a slot\n\t\t\t// goes stale on its own, because not every kind of run writes it. Then\n\t\t\t// any clip arriving from somewhere else makes the test true again and\n\t\t\t// the loader returns over finished work, counting from a start time\n\t\t\t// belonging to a render that ended long ago.\n\t\t\t//\n\t\t\t// A workspace that has already put a clip on the stage is done. Saying\n\t\t\t// so here means a stale slot heals the moment its own clip is on\n\t\t\t// screen, rather than waiting for a stop nobody knows to press.\n\t\t\t!stageClips.some((c) => c.artifact?.workspace === renderWs) &&\n\t\t\tstageNewest?.artifact?.workspace !== renderWs\n\t);\n\t/** Whichever clock is running. */\n\tlet stageClockFrom = $derived(stageStartedAt || (renderInFlight ? startedAt : 0));\n\tlet stageElapsedMs = $derived(stageClockFrom ? stageNow - stageClockFrom : 0);\n\t/** Capped just short of full: a bar that sits at 100% while nothing happens is\n\t *  a worse lie than one that sits at 97%. */\n\tlet stagePercent = $derived(\n\t\tMath.min(97, Math.floor((stageElapsedMs / Math.max(1, stageEtaMs)) * 100))\n\t);\n\n\t/** The clip the composer would continue if asked: the newest one, when the\n\t *  chain can actually take a continuation from it. */\n\t/** The clip a continuation would carry on from: the one being WATCHED, not the\n\t *  one that happened to land last.\n\t *\n\t *  The chain is a strip you can click back through, and clicking back is how\n\t *  you decide a take went wrong three clips ago. Pinned to the newest, the\n\t *  continuation ignored that: you could sit on the second clip, press \"Last\n\t *  frame\", and get a fifth one starting from the fourth — the surface showed\n\t *  one thing and the render used another, with nothing saying so.\n\t *\n\t *  stageClip already resolves the same way for everything else on this\n\t *  surface: the selected entry, or the newest when nothing is selected. The\n\t *  effect below re-targets whenever this changes, so switching clips in the\n\t *  strip moves the continuation with it, and contOffFor is keyed per clip so\n\t *  turning it off for one does not turn it off for the rest. */\n\tlet stageContinuable = $derived(\n\t\t(() => {\n\t\t\tconst ws = stageClip?.artifact?.workspace ?? '';\n\t\t\t// The card has to BE the clip on screen. `stageClip` falls back to the\n\t\t\t// newest when the strip's selection has no card of its own, and a\n\t\t\t// fallback is fine for reading a title — it is not fine for a button\n\t\t\t// that spends a GPU on \"continue from this\", which would quietly have\n\t\t\t// continued something else.\n\t\t\treturn ws && ws === stageShownWs && contInfo(ws).ok ? stageClip : null;\n\t\t})()\n\t);\n\t/** The clip the operator switched continuation OFF for. Without it the effect\n\t *  below would switch it straight back on, which is a tick box that cannot be\n\t *  unticked. */\n\tlet contOffFor = $state('');\n\t// Ticked by default: a clip that just landed is nearly always the thing the\n\t// next message continues, and making that the standing answer is the whole\n\t// point of moving the button down here.\n\t$effect(() => {\n\t\tif (!STAGE_UI || !stageContinuable) return;\n\t\tif (contOffFor === stageContinuable.id) return;\n\t\tif (continuing?.workspace === stageContinuable.artifact?.workspace) return;\n\t\tstartContinue(stageContinuable);\n\t});\n\n\t/** The round the operator is in the middle of: streaming, waiting to be\n\t *  agreed with, or stopped and waiting to be sent again.\n\t *\n\t *  All three at once, on purpose. They were split before — the stage drew\n\t *  only a round that had already finished arriving, and took the loader for\n\t *  everything up to it — which put \"Generating\" over a read-back nobody had\n\t *  agreed to yet, then produced the finished sentence in one go. The\n\t *  read-back exists to be watched while it is still free to change; a\n\t *  surface that hides the writing and shows only the verdict is the\n\t *  automatic accept again, wearing a spinner.\n\t *\n\t *  Never over a render. Once the clock is running the round has been agreed\n\t *  and the clip is what the stage is for, and a round left behind by a shot\n\t *  that went on to render is history rather than a prompt for action. */\n\t/** Every round of the exchange about the shot being agreed, oldest first.\n\t *\n\t *  The stage used to show one. That is right for a picture — there is one\n\t *  clip and the chain is in the strip beside it — but this part of the stage\n\t *  is not a picture, it is a conversation, and a conversation that replaces\n\t *  itself on every reply is not one. You wrote \"a woman on a sofa\", it\n\t *  answered, you wrote \"make it slower and closer\", and the first exchange\n\t *  was simply gone: no way to see what you had asked for, or what it had\n\t *  proposed, or what your correction was correcting.\n\t *\n\t *  It runs back to the last launched round and stops there. Everything before\n\t *  that became a clip and lives on the strip; everything after it is still\n\t *  being agreed, which is what this column is for. */\n\t/** The column follows the conversation down.\n\t *\n\t *  A chat that grows above the fold and leaves you looking at the middle of\n\t *  it is a chat you have to chase. Only on a new round, and only if you were\n\t *  already near the bottom — scrolling up to read what was proposed two\n\t *  rounds ago and being yanked back down is worse than not following at\n\t *  all. */\n\tlet stageScroll = $state<HTMLElement | null>(null);\n\tlet threadSeen = $state(0);\n\t$effect(() => {\n\t\tconst n = stageThread.length;\n\t\tif (!stageScroll || n === threadSeen) return;\n\t\tconst el = stageScroll;\n\t\tconst near = el.scrollHeight - el.scrollTop - el.clientHeight < 220;\n\t\tthreadSeen = n;\n\t\tif (near || n <= 1) {\n\t\t\tvoid flush().then(() => el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' }));\n\t\t}\n\t});\n\n\t/** The conversation sits on the floor of its column by scrolling, not by\n\t *  layout.\n\t *\n\t *  It sat there on `mt-auto`, and that is a layout shift every time a reply\n\t *  lands: the margin above the thread gives up the reply's height, and every\n\t *  line already on screen moves up by it. On a phone the first read-back is\n\t *  about 270 pixels of a 450-pixel column and arrives two seconds after the\n\t *  send — well past the half-second a browser forgives after a tap — and in\n\t *  a scripted three-message session it was the whole score: 0.21, where the\n\t *  mobile p75 was 0.398.\n\t *\n\t *  So the thread is laid out from the top, under a spacer that fills the\n\t *  column down to the first thing said, and the column is kept at its floor\n\t *  while you are at its floor. A reply then lengthens the scroll instead of\n\t *  moving what is on screen, and a scroll is not a shift. The picture is the\n\t *  one it was: a short exchange still sits just above the composer.\n\t *\n\t *  The spacer stops at the first line rather than filling the column, so\n\t *  scrolling all the way up shows the conversation as it began and not a\n\t *  screen of nothing. Measured before it was chosen: a transform holding the\n\t *  old position (FLIP) is still counted as a shift in Chrome; this is not. */\n\tfunction floorHold(box: HTMLElement) {\n\t\tconst spacer = box.firstElementChild as HTMLElement | null;\n\t\tconst thread = box.lastElementChild as HTMLElement | null;\n\t\tif (!spacer || !thread || spacer === thread) return;\n\t\tlet atFloor = true;\n\t\tconst hold = () => {\n\t\t\tconst first = thread.firstElementChild as HTMLElement | null;\n\t\t\tconst pad = parseFloat(globalThis.getComputedStyle(box).paddingTop) || 0;\n\t\t\tconst room = box.clientHeight - pad - (first?.offsetHeight ?? 0);\n\t\t\tspacer.style.height = `${Math.max(0, room)}px`;\n\t\t\tif (atFloor) box.scrollTop = box.scrollHeight;\n\t\t};\n\t\t// Read on every scroll, including the ones `hold` makes: reading back what\n\t\t// was proposed two rounds up is not at the floor, and a reply landing then\n\t\t// must not pull you down to it.\n\t\tconst onScroll = () => {\n\t\t\tatFloor = box.scrollHeight - box.scrollTop - box.clientHeight < 24;\n\t\t};\n\t\tconst watch = new ResizeObserver(hold);\n\t\twatch.observe(box);\n\t\twatch.observe(thread);\n\t\tbox.addEventListener('scroll', onScroll, { passive: true });\n\t\thold();\n\t\treturn () => {\n\t\t\twatch.disconnect();\n\t\t\tbox.removeEventListener('scroll', onScroll);\n\t\t};\n\t}\n\n\tlet stageThread = $derived.by(() => {\n\t\tif (!stageRound) return [] as typeof chat;\n\t\tconst out: typeof chat = [];\n\t\tfor (const item of chat) {\n\t\t\tconst c = item.confirm;\n\t\t\tif (!c) continue;\n\t\t\tconst card = c.cardId ? chat.find((x) => x.id === c.cardId) : null;\n\t\t\t// A round that went on to render closes the thread behind it.\n\t\t\tif (card?.shot?.launched) {\n\t\t\t\tout.length = 0;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout.push(item);\n\t\t\tif (item.id === stageRound.id) break;\n\t\t}\n\t\treturn out;\n\t});\n\n\tlet stageRound = $derived(\n\t\t(() => {\n\t\t\tif (stageClockFrom) return null;\n\t\t\tfor (let i = chat.length - 1; i >= 0; i--) {\n\t\t\t\tconst c = chat[i].confirm;\n\t\t\t\tif (!c) continue;\n\t\t\t\tconst card = c.cardId ? chat.find((x) => x.id === c.cardId) : null;\n\t\t\t\tif (card?.shot?.launched) return null;\n\t\t\t\tif (c.error || c.fixed?.length || c.streaming || (!c.sent && c.line.trim())) {\n\t\t\t\t\treturn chat[i];\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn null;\n\t\t})()\n\t);\n\n\t/** The message a round is an answer to.\n\t *\n\t *  Usually its own `said`. A round raised by a moved setting has none — it\n\t *  answers the composer rather than a sentence — and in the transcript that\n\t *  is fine, because the message it is still about is a few lines up. The\n\t *  stage shows one round and nothing above it, so it carries the request\n\t *  forward rather than drawing a reply to an empty screen. */\n\tfunction roundRequest(item: ChatItem): string {\n\t\tconst at = chat.indexOf(item);\n\t\tfor (let i = at; i >= 0; i--) {\n\t\t\tconst said = chat[i].confirm?.said?.trim();\n\t\t\tif (said) return said;\n\t\t}\n\t\treturn '';\n\t}\n\n\t/** Whether the strip is on screen at all.\n\t *\n\t *  Named because two places need the same answer and they had drifted: the\n\t *  column rendered whenever a clip was being made, but the margin that keeps\n\t *  it clear of the picture was only applied when there was more than one\n\t *  thumbnail. One clip plus one pending tile satisfied the first and not the\n\t *  second, so the strip sat on top of the video. */\n\t/** `sending` still counts, for the sends that have no round to show: a photo\n\t *  going up, a character or a location sheet being written. A clip's own send\n\t *  raises its round in the same tick, so the loader never gets the read-back. */\n\t/** The measure everything under the stage keeps: the clip's own width.\n\t *\n\t *  The composer was capped to it so their edges line up, but the rows above it\n\t *  — the reference chips, the hint line, the film reel — were outside that and\n\t *  ran the full column. Adding a clip to the film therefore opened a bar that\n\t *  reached from edge to edge over a composer half its width, which reads as\n\t *  two unrelated surfaces rather than one stack.\n\t *\n\t *  Three consumers, one definition. The last time the same measure was written\n\t *  out twice, the two copies drifted the moment the layout beside them moved.\n\t *\n\t *  There is no gutter any more. Both boxes used to carry six rems of padding\n\t *  on the left to clear the strip; the strip is in the right-hand margin now\n\t *  and takes nothing, so the padding was width surrendered to a column that\n\t *  had moved out from under it. */\n\t/** …and a floor under it, which is the measure the empty page already uses.\n\t *\n\t *  Capped to the picture alone the composer inherited the picture's problem:\n\t *  the picture is 16:9 and height-bound, so on a wide, short window it is\n\t *  narrow, and everything pinned to it went narrow with it — a composer and a\n\t *  film reel squeezed into the middle of a screen with room to spare, under\n\t *  the three suggestion cards that had been sitting at 48rem the whole time.\n\t *\n\t *  So: at least what the suggestions take, and the picture's width whenever\n\t *  that is more. The two line up on any window tall enough for the picture to\n\t *  reach 48rem, which is the case the alignment was for; below that the\n\t *  composer stops shrinking rather than following the picture down. */\n\tconst COMPOSER_FLOOR = '48rem';\n\tlet composerCap = $derived(\n\t\tSTAGE_UI\n\t\t\t? // min() around the floor, because the floor is a desktop measurement.\n\t\t\t\t// 48rem is 768px and a phone is 375: without this the composer asks to be\n\t\t\t\t// twice the window, and the \"stops shrinking rather than following the\n\t\t\t\t// picture down\" rule — written for a window wider than any clip — turns\n\t\t\t\t// into an element wider than the screen.\n\t\t\t\t`max-width:min(100%, max(${COMPOSER_FLOOR}, ${Math.max(stageVideoW, 0)}px))`\n\t\t\t: undefined\n\t);\n\n\t/** A subject being made right now — a photograph going up, or a description\n\t *  going to the sheet writer.\n\t *\n\t *  It is a send like any other, so the stage took the clip's loader for it:\n\t *  the full 16:9 block with \"Generating\" on it, for a second, before the\n\t *  character surface replaced it. A picture of a video being made, over work\n\t *  that makes no video, and the swap between the two is the jump. The\n\t *  character surface owns this from the first press; the wait belongs in the\n\t *  frame the picture will land in, at the size the picture will be. */\n\tlet makingSubject = $derived(sending && wantTarget !== 'clip');\n\n\t/** The subject being drawn right now, which nothing else on this surface can\n\t *  see.\n\t *\n\t *  `stageSheet` only counts a card that already has an id, a job, a url or a\n\t *  workspace, because those are what it needs to draw something. A subject on\n\t *  the Grok path has none of them until it is finished: the request is held\n\t *  open and the record is written on success. So for the whole thirty seconds\n\t *  the newest card is invisible, and the stage went on showing the PREVIOUS\n\t *  subject — the character you had just made, sitting there looking finished\n\t *  while a location was being drawn behind it.\n\t *\n\t *  It only looked right the first time because there was no previous subject\n\t *  to show. The second one in the same conversation is where it broke. */\n\tlet stageMaking = $derived(\n\t\tmakingSubject\n\t\t\t? (chat\n\t\t\t\t\t.filter(\n\t\t\t\t\t\t(c) =>\n\t\t\t\t\t\t\tc.kind === 'sheet' &&\n\t\t\t\t\t\t\tc.sheet &&\n\t\t\t\t\t\t\t!c.sheet.id &&\n\t\t\t\t\t\t\t!c.sheet.url &&\n\t\t\t\t\t\t\t!c.sheet.job &&\n\t\t\t\t\t\t\t!c.sheet.workspace\n\t\t\t\t\t)\n\t\t\t\t\t.at(-1) ?? null)\n\t\t\t: null\n\t);\n\n\tlet stagePhaseIsWorking = $derived(\n\t\t!stageError && !stageRound && !makingSubject && (stageClockFrom || sending)\n\t);\n\tlet showStrip = $derived(stageThumbs.length > 1 || !!stagePhaseIsWorking);\n\n\t/** How many clips of the batch on the stage have not landed yet.\n\t *\n\t *  A message can make several at once — versions times camera angles — and\n\t *  each is its own render with its own state. The strip drew ONE mark for the\n\t *  lot, so two takes and one take looked identical the whole time both were\n\t *  being paid for, and the only place the difference was visible was the\n\t *  chip that had asked for it.\n\t *\n\t *  Counted off the runs themselves, not off the composer's setting. The\n\t *  versions dial is not frozen while a batch renders, so a hand on it would\n\t *  have changed the count of things already in flight — marks appearing for\n\t *  work nobody started. The runs are the work.\n\t *\n\t *  Zero for an ordinary single clip, which has no batch at all; the strip\n\t *  floors it at one, which is exactly what it drew before. */\n\tlet stagePending = $derived(\n\t\tchat\n\t\t\t.filter((c) => c.kind === 'takes' && c.takes)\n\t\t\t.at(-1)\n\t\t\t?.takes?.runs.filter((r) => r.state === 'rendering').length ?? 0\n\t);\n\n\t/** The subject this session made — or is making — when there is no clip.\n\t *\n\t *  It asked for `sheet.id`, and an id only exists once the subject has been\n\t *  KEPT. That is a deadlock: keeping is a button on the card, and the card is\n\t *  what an id was required to show. A preview rendered for thirty seconds, a\n\t *  loader appeared while the request was in flight and vanished the moment it\n\t *  returned, and the finished picture landed on disk with no surface able to\n\t *  offer it. Four of those are sitting in previews/ unclaimed.\n\t *\n\t *  A job or a url is the same evidence an id is: something was started, and\n\t *  it belongs on the stage. The id then arrives on the card already there.\n\t *\n\t *  (Originally: the character this session made, when it made one and no\n\t *  clip.)\n\t *\n\t *  A sheet is a chat item, and in simple mode the stage stands in for the\n\t *  transcript — so a finished character had nowhere to appear at all. You\n\t *  watched a line count down, the line went away, and the surface was the\n\t *  welcome screen again. The work was not lost: the sheet is in the library\n\t *  and the composer is already pointed at it. Nothing said so.\n\t *\n\t *  Only when there is no clip. A clip is what the stage is for, and a\n\t *  character made in the middle of a chain is a step in it, not the subject. */\n\tlet stageSheet = $derived(\n\t\t// …and only while the stage is about clips. In the character generator the\n\t\t// subject IS the work, so a clip made earlier in the conversation does not\n\t\t// get to stand in front of it.\n\t\tstageClips.length && wantTarget === 'clip'\n\t\t\t? null\n\t\t\t: (chat\n\t\t\t\t\t// `workspace` alone is a preview the harness is still drawing: the card\n\t\t\t\t\t// is on the transcript from the launch so the stage has something to\n\t\t\t\t\t// stand on while the picture is two minutes out.\n\t\t\t\t\t.filter(\n\t\t\t\t\t\t(c) =>\n\t\t\t\t\t\t\tc.kind === 'sheet' &&\n\t\t\t\t\t\t\t(c.sheet?.id || c.sheet?.job || c.sheet?.url || c.sheet?.workspace) &&\n\t\t\t\t\t\t\tc.at >= subjectFrom\n\t\t\t\t\t)\n\t\t\t\t\t.at(-1) ?? null)\n\t);\n\n\t/** The character this session is drawing right now, when there is no clip to\n\t *  keep the surface busy with.\n\t *\n\t *  Without it the stage went blank between the upload and the sheet landing —\n\t *  and then, worse, the finished picture appeared as if the work were over\n\t *  while the six views were still ten minutes out. A loader for a second,\n\t *  gone, and a photograph in its place is a surface contradicting itself.\n\t *\n\t *  Only with no clip on the stage. With one, the clip stays and the sheet\n\t *  reports on its own line — the alternative is the loader that sat at 97%\n\t *  over an arrived picture for a quarter of an hour. */\n\t/** Which of the character's three things the stage is showing.\n\t *\n\t *  Empty means follow the work: the reference until the turnaround lands, the\n\t *  turnaround until the six views are cut, the six views after that. A press\n\t *  pins it, and a different character clears the pin — the choice was about\n\t *  that person, not a standing preference. */\n\tlet charView = $state<'' | 'ref' | 'turn' | 'four'>('');\n\n\tlet stageDrawing = $derived(\n\t\tstageClips.length && wantTarget === 'clip'\n\t\t\t? null\n\t\t\t: (drawingHere.find((x) => drawStartedMs(x) >= subjectFrom) ?? null)\n\t);\n\n\t/** Whoever the character stage is showing — the same id the template works\n\t *  out inline, hoisted so things outside the template can address them.\n\t *\n\t *  The voice needed it. It used to hang off `chosenCharacter`, the person the\n\t *  NEXT clip is cast with, which is not necessarily the person on screen: you\n\t *  can be looking at somebody you have just drawn while the composer is still\n\t *  pointed at somebody else, and a field under one name that saves onto\n\t *  another is worse than no field. */\n\tlet stageSubjectId = $derived(\n\t\tstageDrawing ? (stageDrawing.id ?? '') : (stageSheet?.sheet?.id ?? '')\n\t);\n\n\tlet stageSubject = $derived(sheets.find((x) => x.id === stageSubjectId) ?? null);\n\n\t/** Three voices worth having without writing one.\n\t *\n\t *  Physical description only — pitch, weight, accent, pace. Not a mood and\n\t *  not a character trait: the model renders what a microphone would pick up,\n\t *  and \"confident\" is not a sound. */\n\tconst VOICE_PRESETS = [\n\t\t{\n\t\t\tlabel: 'low and husky',\n\t\t\ttext: 'a low, warm, slightly husky adult female voice, neutral American accent, unhurried'\n\t\t},\n\t\t{\n\t\t\tlabel: 'bright and young',\n\t\t\ttext: 'a bright, light adult female voice, neutral American accent, quick and forward'\n\t\t},\n\t\t{\n\t\t\tlabel: 'soft and breathy',\n\t\t\ttext: 'a soft, breathy adult female voice, neutral American accent, close and unhurried'\n\t\t}\n\t];\n\n\t/** The voice being edited.\n\t *\n\t *  A writable derived, not a $state seeded once: a local copy of a derived\n\t *  value stays frozen when the source changes, so switching characters would\n\t *  leave the previous one's sentence in the box and save it onto the wrong\n\t *  person at the next blur. This tracks whoever is chosen and still takes\n\t *  typing. */\n\t// The stored voice, or the one a character made today would be given. Every\n\t// character has one from the moment it exists now; only the ones made before\n\t// that fall back, and they fall back to real text rather than to grey\n\t// suggestion text that reads as filled in and renders as silence.\n\tlet voiceDraft = $derived(stageSubject?.voice ?? DEFAULT_VOICE);\n\n\t/** Written on blur, not behind a Save button. There is one field and it is\n\t *  one sentence; a button to confirm a sentence is a button nobody needs. */\n\tasync function saveVoice() {\n\t\tconst c = stageSubject;\n\t\tif (!c) return;\n\t\tconst next = voiceDraft.trim().slice(0, 240);\n\t\tif (next === (c.voice ?? '')) return;\n\t\ttry {\n\t\t\tconst res = await fetch('/studio/api/sheet', {\n\t\t\t\tmethod: 'PATCH',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({ id: c.id, voice: next })\n\t\t\t});\n\t\t\tconst r = (await res.json()) as { ok?: boolean; sheets?: StoredSheet[]; error?: string };\n\t\t\tif (r.ok && r.sheets) sheets = r.sheets;\n\t\t\telse pushError(r.error || 'the voice could not be saved');\n\t\t} catch (e) {\n\t\t\tpushError(`the voice could not be saved — ${e}`);\n\t\t}\n\t}\n\n\t/** A batch with something to show, and nobody has chosen from it yet.\n\t *\n\t *  This is the surface that was missing. A message set to several versions\n\t *  makes a `takes` card rather than a clip card, and a takes card only draws\n\t *  in the transcript — which the stage replaces. So every clip of a batch\n\t *  rendered, was paid for, was written to the log, and then had nowhere to\n\t *  appear: the loader climbed to its 97% cap and stayed there for good, and\n\t *  reloading the page brought the same stuck loader back, because the card\n\t *  it was waiting on could not be drawn on this surface either.\n\t *\n\t *  As soon as ONE take has landed, and not before. With none of them back the\n\t *  loader is telling the truth; with one back there is a clip to look at, and\n\t *  a clip that exists must be on screen. The rest arrive into the same grid.\n\t *\n\t *  Nothing here depends on this tab having watched the render: the card is in\n\t *  the snapshot, the poll that fills it in runs on mount, and the log knows\n\t *  where every clip lives. Come back tomorrow and it is still here. */\n\tlet stageTakes = $derived(\n\t\tchat\n\t\t\t.filter(\n\t\t\t\t(c) =>\n\t\t\t\t\tc.kind === 'takes' &&\n\t\t\t\t\tc.takes &&\n\t\t\t\t\t!c.artifact &&\n\t\t\t\t\tc.takes.runs.some((r) => r.state === 'ready' && r.clip)\n\t\t\t)\n\t\t\t.at(-1) ?? null\n\t);\n\n\tlet stagePhase = $derived(\n\t\tstageError\n\t\t\t? 'error'\n\t\t\t: // The generator you are in decides what the stage is about, and it did\n\t\t\t\t// not. `stageRound` outranked every subject: send one message, leave the\n\t\t\t\t// read-back unpressed — which is the whole point of a read-back — and the\n\t\t\t\t// stage was pinned to that plan card. Switching to the character\n\t\t\t\t// generator then drew the subject in full, request, picture and card, and\n\t\t\t\t// the stage went on showing the plan. The only sign of it was the loader\n\t\t\t\t// that lives on `sending`, which appeared and then vanished: a character\n\t\t\t\t// started and thrown away, as far as anyone could see.\n\t\t\t\t//\n\t\t\t\t// `back to clips` puts the round back, which is what it is for.\n\t\t\t\twantTarget !== 'clip' && (makingSubject || stageDrawing || stageSheet)\n\t\t\t\t? 'character'\n\t\t\t\t: stageRound\n\t\t\t\t\t? 'round'\n\t\t\t\t\t: makingSubject\n\t\t\t\t\t\t? 'character'\n\t\t\t\t\t\t: // Before 'working' on purpose. A batch keeps the clock running until a\n\t\t\t\t\t\t\t// take is kept, so the loader would go on covering clips that had\n\t\t\t\t\t\t\t// already arrived — which is exactly how they became unreachable.\n\t\t\t\t\t\t\tstageTakes\n\t\t\t\t\t\t\t? 'takes'\n\t\t\t\t\t\t\t: // `stagePending` too: the batch's own runs say it is working, and\n\t\t\t\t\t\t\t\t// they say so after a reload as well — `stageStartedAt` dies with\n\t\t\t\t\t\t\t\t// the tab, and the slot above is deliberately empty for a batch.\n\t\t\t\t\t\t\t\tstageClockFrom || sending || stagePending\n\t\t\t\t\t\t\t\t? 'working'\n\t\t\t\t\t\t\t\t: stageClip\n\t\t\t\t\t\t\t\t\t? 'ready'\n\t\t\t\t\t\t\t\t\t: stageDrawing || stageSheet\n\t\t\t\t\t\t\t\t\t\t? 'character'\n\t\t\t\t\t\t\t\t\t\t: 'empty'\n\t);\n\n\t/** Whether the reader is parked at the latest message. Drives both the\n\t *  follow-the-tail behaviour and the \"latest ↓\" button. */\n\tlet atBottom = $state(true);\n\n\t/** Within this many pixels of the end still counts as \"at the bottom\" —\n\t *  a reader who has scrolled up by a line or two has not left the tail. */\n\tconst TAIL_SLACK = 120;\n\n\tfunction onTranscriptScroll() {\n\t\tconst el = scrollEl;\n\t\tif (!el) return;\n\t\tatBottom = el.scrollHeight - el.scrollTop - el.clientHeight <= TAIL_SLACK;\n\t}\n\n\tfunction scrollToBottom(behavior: ScrollBehavior = 'auto') {\n\t\tconst el = scrollEl;\n\t\tif (!el) return;\n\t\tel.scrollTo({ top: el.scrollHeight, behavior });\n\t\tatBottom = true;\n\t}\n\n\tlet seenLen = 0;\n\t$effect(() => {\n\t\tconst len = chat.length;\n\t\tif (len <= seenLen) {\n\t\t\tseenLen = len;\n\t\t\treturn;\n\t\t}\n\t\tseenLen = len;\n\t\t// Follow the tail only for a reader who is already there. Yanking someone\n\t\t// out of a document they are mid-way through reading is the one thing a\n\t\t// long-running transcript must never do — this run posts messages for\n\t\t// twenty minutes.\n\t\tif (!atBottom) return;\n\t\t// Two frames: the first lets Svelte flush the new node, the second lets\n\t\t// layout settle so scrollHeight is the post-insert value.\n\t\trequestAnimationFrame(() => requestAnimationFrame(() => scrollToBottom('smooth')));\n\t});\n\n\tfunction setNavOpen(open: boolean) {\n\t\tsidebarOpen = open;\n\t\ttry {\n\t\t\tlocalStorage.setItem(NAV_KEY, open ? '1' : '0');\n\t\t} catch {\n\t\t\t/* a preference that will not persist is not worth an error */\n\t\t}\n\t}\n\n\t/** Put a saved run on screen: the transcript, the workspaces, the poller.\n\t *\n\t *  Lifted out of onMount so reopening a production can call it directly. It\n\t *  used to live inline there, which is why reopening had to write a pointer\n\t *  and reload the whole document to reach it — a white flash and a cold boot\n\t *  of the app to move between two conversations already in the same tab.\n\t *\n\t *  Everything it touches is component state, so it may be called more than\n\t *  once in a session provided reset() has run in between.\n\t */\n\tfunction resumeFrom(raw: string | null): boolean {\n\t\tif (!raw) return false;\n\t\tlet s: Partial<ReturnType<typeof snapshot>>;\n\t\ttry {\n\t\t\ts = JSON.parse(raw) as Partial<ReturnType<typeof snapshot>>;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t\t// A workspace to poll is the whole requirement. Requiring a brief on top\n\t\t// of it meant a simple run could not be resumed at all — there is no plan\n\t\t// in one, only a prompt — and a reload mid-render left the clip finishing\n\t\t// on the harness with nothing watching for it.\n\t\t// ...or a session that has started and not yet rendered anything. Before\n\t\t// sessions existed there was nothing to restore in that state; now there\n\t\t// is a conversation, and dropping it on reload was the same loss as\n\t\t// dropping a run.\n\t\tif (!(s.planningWs || s.renderWs || s.sessionSlug)) return false;\n\t\tsessionSlug = s.sessionSlug ?? '';\n\t\t// Back into the same state the banner reads, so a reload lands you where\n\t\t// you were rather than one step to the side of it.\n\t\tcontinuing = s.continuing ?? null;\n\t\tbrief = s.brief ?? null;\n\t\tlaunchedBrief = s.launchedBrief ?? s.brief ?? null;\n\t\tsceneCount = s.brief?.sceneCount ?? sceneCount;\n\t\tplanningWs = s.planningWs ?? '';\n\t\trenderWs = s.renderWs ?? '';\n\t\t// The mode follows the run you opened. Landing in a simple run with\n\t\t// the advanced composer under it is the same mismatch as the rail:\n\t\t// the page describing one mode while showing the other.\n\t\tif (ONE_CLIP_WS.test(renderWs)) mode = 'simple';\n\t\telse if (s.planningWs) mode = 'advanced';\n\t\tassemblySent = s.assemblySent ?? false;\n\t\tstartedAt = s.startedAt || Date.now();\n\t\t// Asked here rather than anywhere later, because everything below\n\t\t// reads as live: the poller, the clock, the rail's pills.\n\t\tstaleRun = Date.now() - startedAt > runCeilingFor(renderWs);\n\t\tif (assemblySent) {\n\t\t\tshootsAnnounced = true;\n\t\t\tfinalByNameOnly = true;\n\t\t}\n\n\t\t// A saved conversation is restored as itself. Anything older —\n\t\t// written before transcripts were saved — falls back to the plan\n\t\t// card, which is what it used to do.\n\t\tif (s.chat?.length) {\n\t\t\tchat = s.chat;\n\t\t\tsuperseded = s.superseded ?? {};\n\t\t\tlatestPlanId = s.latestPlanId ?? '';\n\t\t\tboardId = s.boardId ?? '';\n\t\t\tapprovalId = s.approvalId ?? '';\n\t\t\tgateOpen = s.gateOpen ?? {};\n\t\t\tdocPhase = s.docPhase ?? {};\n\t\t\tdocAccepted = s.docAccepted ?? {};\n\t\t\tlatestDocItem = s.latestDocItem ?? {};\n\t\t\tdocBody = s.docBody ?? {};\n\t\t\tdocFile = s.docFile ?? {};\n\t\t\tdocUrl = s.docUrl ?? {};\n\t\t\tdocTaskId = s.docTaskId ?? {};\n\t\t\tfor (const id of s.clipPosted ?? []) clipPosted.add(id);\n\t\t\tfor (const id of s.failedNoted ?? []) failedNoted.add(id);\n\t\t\tfor (const id of s.preAssemblyIds ?? []) preAssemblyIds.add(id);\n\t\t\tseenActivity = new Set(s.seenActivity ?? []);\n\t\t\tshootsAnnounced = s.shootsAnnounced ?? shootsAnnounced;\n\t\t\tfinalPosted = s.finalPosted ?? false;\n\t\t\tfinalByNameOnly = s.finalByNameOnly ?? finalByNameOnly;\n\t\t\twelcomeId = s.welcomeId ?? '';\n\t\t\tlastRequest = s.lastRequest ?? '';\n\t\t} else if (brief) {\n\t\t\tconst item = pushItem({ who: 'studio', kind: 'plan', plan: brief });\n\t\t\tlatestPlanId = item.id;\n\t\t}\n\t\t// A run past the ceiling is not polled. The harness has already\n\t\t// given up on every task it could have been running, so the loop\n\t\t// would only ask a dead workspace the same question every thirty\n\t\t// seconds while the page counted the hours since the tab closed.\n\t\t//\n\t\t// The cost, stated plainly: work that finished after this tab was\n\t\t// closed is not collected. Nothing that was still running can be —\n\t\t// it was abandoned hours before the ceiling — but a clip that\n\t\t// landed and was never posted stays uncollected until the run is\n\t\t// started again.\n\t\tif (!staleRun) startPolling();\n\t\t// A press this session made while it was not on screen — the server\n\t\t// carried it on. See generateOnServer.\n\t\tvoid resumeGeneration(sessionSlug);\n\t\treturn true;\n\t}\n\n\tonMount(() => {\n\t\t// The shelf's contents, which outlive every session. A visitor has no\n\t\t// films of their own, but the front page still opens on the default wall.\n\t\tif (!guest) void loadFilms();\n\t\tvoid loadMedia();\n\n\t\t// One voice at a time, and none of it behind your back.\n\t\t//\n\t\t// Several clips are on screen at once — the stage, the strip, the reel, the\n\t\t// popup — and any of them starting means the last one should stop. Muted\n\t\t// ones are decoration and are left alone; it is the audible ones that\n\t\t// collide. Without this a clip carried on talking from a closed panel or a\n\t\t// production you had already left, and the only way to find it was to hunt\n\t\t// through the page for the element still playing.\n\t\t//\n\t\t// `play` does not bubble, so the listener captures.\n\t\tconst audible = () =>\n\t\t\t[...globalThis.document.querySelectorAll('video')].filter((v) => !v.muted);\n\t\tconst onPlay = (e: Event) => {\n\t\t\t// A muted clip starting silences nothing. The rule reads \"one thing\n\t\t\t// talking at a time\", and decoration is not talking — but the guard used\n\t\t\t// to be only on the victims, never on the trigger, so any muted video\n\t\t\t// beginning would stop whatever was audible. That cost nothing until the\n\t\t\t// front page grew a wall of muted tiles that start themselves every four\n\t\t\t// seconds: opening a film and watching it play for two and a half seconds\n\t\t\t// before stopping dead was this line, every time.\n\t\t\tif ((e.target as HTMLVideoElement).muted) return;\n\t\t\tfor (const v of audible()) if (v !== e.target) v.pause();\n\t\t};\n\t\tconst onHidden = () => {\n\t\t\tif (globalThis.document.hidden) for (const v of audible()) v.pause();\n\t\t};\n\t\tglobalThis.document.addEventListener('play', onPlay, true);\n\t\tglobalThis.document.addEventListener('visibilitychange', onHidden);\n\t\tcleanupAudio = () => {\n\t\t\tglobalThis.document.removeEventListener('play', onPlay, true);\n\t\t\tglobalThis.document.removeEventListener('visibilitychange', onHidden);\n\t\t};\n\t\ttry {\n\t\t\tconst saved = localStorage.getItem(NAV_KEY);\n\t\t\t// Open on a desktop by default, shut on a phone, where the transcript\n\t\t\t// is the page and a rail would take a third of it.\n\t\t\tsidebarOpen = saved === null ? window.innerWidth >= 1024 : saved === '1';\n\t\t} catch {\n\t\t\tsidebarOpen = window.innerWidth >= 1024;\n\t\t}\n\n\t\t// Staged references survive a reload — they live on the server, not in\n\t\t// this tab — so the composer has to ask for them rather than assume none.\n\t\tif (!guest) {\n\t\t\tvoid loadRefFiles();\n\t\t\tvoid loadHistory();\n\t\t\tvoid loadVerdicts();\n\t\t}\n\t\t// Sheets live on the server too, and outlast every run — the picker has to\n\t\t// ask rather than assume this tab has seen them before.\n\t\t// Unconditionally, not only when something is still rendering. A turnaround\n\t\t// that finished while this tab was closed is the case that kept losing its\n\t\t// cards, and by definition nothing is rendering by the time you come back.\n\t\t// One poll, four seconds in; it stops itself if there is nothing to wait for.\n\t\tif (!guest) void loadSheets().then(() => watchSheets());\n\t\ttry {\n\t\t\tconst raw = localStorage.getItem(SETUP_KEY);\n\t\t\tif (raw) {\n\t\t\t\tconst v = JSON.parse(raw) as {\n\t\t\t\t\ts?: number;\n\t\t\t\t\to?: string;\n\t\t\t\t\tr?: string;\n\t\t\t\t\tt?: string;\n\t\t\t\t\tc?: string;\n\t\t\t\t\tl?: string;\n\t\t\t\t\tk?: number;\n\t\t\t\t\tkk?: number;\n\t\t\t\t};\n\t\t\t\t// Snapped to what the row can show. The durations were 5/6/8/10/12/15\n\t\t\t\t// and are now 5/10/15, so a stored 6, 8 or 12 would restore a value with\n\t\t\t\t// no pill to light up — the fold would read 5s while the send made 8. A\n\t\t\t\t// setting the surface cannot show is a setting nobody can correct.\n\t\t\t\tif (typeof v.s === 'number' && v.s >= 4 && v.s <= 15) {\n\t\t\t\t\tconst was = v.s;\n\t\t\t\t\twantSeconds = [5, 10, 15].reduce((a, b) =>\n\t\t\t\t\t\tMath.abs(b - was) < Math.abs(a - was) ? b : a\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (v.o === 'portrait' || v.o === 'landscape') wantOrientation = v.o;\n\t\t\t\tif (v.r && v.r in RESOLUTIONS) wantRes = v.r as ResKey;\n\t\t\t\tif (v.t === 'clip' || v.t === 'character' || v.t === 'location') wantTarget = v.t;\n\t\t\t\tif (typeof v.c === 'string') wantCharacter = v.c;\n\t\t\t\tif (typeof v.l === 'string') wantLocation = v.l;\n\t\t\t\tif (typeof v.k === 'number' && v.k >= 1 && v.k <= 4) takes = Math.round(v.k);\n\t\t\t\tif (typeof v.kk === 'number' && v.kk >= 1 && v.kk <= 4) angles = Math.round(v.kk);\n\t\t\t\tif (takes * angles > 4) angles = 1;\n\t\t\t}\n\t\t} catch {\n\t\t\t/* a preference that will not load is not worth an error */\n\t\t}\n\n\t\t// A run left behind by a reload picks up where it was: the run identity is\n\t\t// restored and the poller re-attaches. Document and clip items rebuild\n\t\t// themselves from poll state; the conversation itself is not replayed.\n\t\t// Before anything is written to the transcript: the greeting, the composer\n\t\t// and the rail all read it.\n\t\ttry {\n\t\t\tconst saved = localStorage.getItem(MODE_KEY);\n\t\t\tif (saved === 'simple' || saved === 'advanced') mode = saved;\n\t\t} catch {\n\t\t\t/* default stands */\n\t\t}\n\n\t\t// Continuations saved their conversation under `cont-xxx-cont`, because the\n\t\t// slug derivation did not strip the `-cont` suffix that the workspace ids\n\t\t// had grown — see WS_SUFFIX. The sidebar files those runs under `cont-xxx`\n\t\t// and so could never find them. The snapshots are still there and still\n\t\t// good, so bring them to the name they should have had.\n\t\t//\n\t\t// Copied rather than moved, and never over an existing one: this runs on a\n\t\t// user's only copy of conversations that cannot be regenerated.\n\t\ttry {\n\t\t\tfor (const k of Object.keys(localStorage)) {\n\t\t\t\tconst m = /^auteur-studio-run-(.+)-cont$/.exec(k);\n\t\t\t\tif (!m) continue;\n\t\t\t\tconst right = runKey(m[1]);\n\t\t\t\tif (localStorage.getItem(right)) continue;\n\t\t\t\tconst body = localStorage.getItem(k);\n\t\t\t\tif (body) localStorage.setItem(right, body);\n\t\t\t}\n\t\t} catch {\n\t\t\t/* a full quota or private mode — the orphans stay orphans, nothing is lost */\n\t\t}\n\n\t\tconst pointer = localStorage.getItem(POINTER_KEY);\n\t\tconst resumed = resumeFrom(\n\t\t\t(pointer && localStorage.getItem(runKey(pointer))) ??\n\t\t\t\tlocalStorage.getItem(RESUME_KEY) ??\n\t\t\t\tsessionStorage.getItem(RESUME_KEY)\n\t\t);\n\t\tif (!resumed) {\n\t\t\tshowWelcome();\n\t\t}\n\n\t\t// RAT-632: a prompt handed over in the address — the prompt library's \"Open\n\t\t// in studio\" (/prompts → /?prompt=…). It lands in the box the way a\n\t\t// starter does and is never sent: the person reads it and presses. A new\n\t\t// clip, not the next message of whatever production this tab restored —\n\t\t// the same as pressing \"New clip\" first. Then it leaves the address, so a\n\t\t// reload does not paste it over whatever has been typed since.\n\t\tconst handed = page.url.searchParams.get('prompt')?.trim().slice(0, 2000);\n\t\tif (handed) {\n\t\t\t// A prompt is for a clip: open that door, whichever was open last.\n\t\t\tsetSessionSurface('clip', 'prompt');\n\t\t\tif (resumed) reset();\n\t\t\tif (wantTarget !== 'clip') {\n\t\t\t\twantTarget = 'clip';\n\t\t\t\tcurrentCharacter = null;\n\t\t\t\tsaveSetup();\n\t\t\t}\n\t\t\tvoid useExample(handed);\n\t\t\tdropFromAddress('prompt');\n\t\t}\n\t\t// A session asked for from the rest of the site — ratemyd's front page, its\n\t\t// roleplay page: `?session=1` opens the session side, `?session=<id>` one of\n\t\t// the ready-to-play sessions, set up and waiting for the press. Then it\n\t\t// leaves the address, like a handed prompt.\n\t\tconst wantSession = page.url.searchParams.get('session');\n\t\tif (wantSession) {\n\t\t\tsetSessionSurface('session', 'link');\n\t\t\tconst ready = READY_SESSIONS.find((r) => r.id === wantSession);\n\t\t\tif (ready)\n\t\t\t\topenSessionSheet({\n\t\t\t\t\tscenario: ready.scenario,\n\t\t\t\t\ther: ready.her,\n\t\t\t\t\tminutes: ready.minutes,\n\t\t\t\t\ttones: ready.tones,\n\t\t\t\t\tfrom: 'link'\n\t\t\t\t});\n\t\t\tdropFromAddress('session');\n\t\t}\n\t\tfunction dropFromAddress(name: string, again = true) {\n\t\t\t// On a first load the router is not up yet and puts the address it\n\t\t\t// started with back once it is — `?session=` stayed in the address on\n\t\t\t// pornpilot.app (2026-11-01), and a reload opened the sheet again. So\n\t\t\t// once more when it is up.\n\t\t\tif (again) setTimeout(() => dropFromAddress(name, false), 1500);\n\t\t\t// The address as it stands, not as the router last saw it: a parameter\n\t\t\t// dropped a moment ago through the fallback below is not back again.\n\t\t\tconst clean = new URL(window.location.href);\n\t\t\tif (!clean.searchParams.has(name)) return;\n\t\t\tclean.searchParams.delete(name);\n\t\t\ttry {\n\t\t\t\t// This page's own address with one parameter taken out: nothing to resolve.\n\t\t\t\t// eslint-disable-next-line svelte/no-navigation-without-resolve\n\t\t\t\treplaceState(clean, page.state);\n\t\t\t} catch {\n\t\t\t\t// Router not ready, which is every first load: the prompt still has to\n\t\t\t\t// leave the address. `window.` on purpose — this page has a `history`\n\t\t\t\t// of its own (the production list), and the bare name is that.\n\t\t\t\ttry {\n\t\t\t\t\twindow.history.replaceState(window.history.state, '', clean);\n\t\t\t\t} catch {\n\t\t\t\t\t/* the address keeps it; a reload refills the box, nothing worse */\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// The wall's answer usually decides when what is under it appears. This is\n\t\t// for an answer that never comes: the words below are not held hostage.\n\t\tconst settleAnyway = setTimeout(() => (shelfSettled = true), 6000);\n\n\t\t// After the restore, not before it. A character preview runs server-side\n\t\t// for about two minutes, and this re-enters the poll for any card still\n\t\t// waiting on one — but the cards only exist once resumeFrom has put the\n\t\t// conversation back, so called any earlier it reads an empty list and\n\t\t// quietly does nothing, which is the failure that looks like a fix.\n\t\t// None of what follows is a visitor's: it all lives under an account.\n\t\tif (!guest) {\n\t\t\tresumePreviews();\n\t\t\t// And any takes still rendering. They run server-side, so this tab simply\n\t\t\t// has to ask — the same reason the sheet watcher starts unconditionally.\n\t\t\twatchBatches();\n\t\t\t// What the purse really holds. The seed came from a cached row; this page\n\t\t\t// refuses presses on the strength of that number, so it asks once for\n\t\t\t// itself before anybody presses anything.\n\t\t\tvoid refreshTokens();\n\t\t\t// A press made before signing up, when this page is where Google\n\t\t\t// brought them back to.\n\t\t\tvoid resumePendingPress();\n\t\t}\n\t\t// Any take of an already-chosen batch that has no card yet. Batches chosen\n\t\t// from before this existed are the reason it runs here and not only at the\n\t\t// moment of choosing.\n\t\tif (!guest) spareTakeCards();\n\t\t// The film is not part of this conversation, so it is not in the snapshot\n\t\t// the restore just replayed. It is asked for once, here, and is the same\n\t\t// film in every production and every browser.\n\t\tif (!guest)\n\t\t\tvoid (async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst r = (await (await fetch('/studio/api/film')).json()) as { clips?: FilmClip[] };\n\t\t\t\t\tif (Array.isArray(r.clips)) film = r.clips;\n\t\t\t\t} catch {\n\t\t\t\t\t/* a film that will not load is not a reason to fail the studio */\n\t\t\t\t}\n\t\t\t})();\n\n\t\t// Elapsed time is shown in whole minutes, so a 15s clock is plenty.\n\t\tconst clock = setInterval(() => (now = Date.now()), 15_000);\n\t\t// Except while something is being drawn, where a counter is on screen and a\n\t\t// number that moves once a quarter of a minute reads as a frozen page.\n\t\tconst fast = setInterval(() => {\n\t\t\tif (sheetsWorking || Object.values(shotBusy).some(Boolean)) now = Date.now();\n\t\t}, 1_000);\n\t\t// A run outlives its poll, and nothing used to notice.\n\t\t//\n\t\t// The loop reschedules itself at the end of each tick, so any path that\n\t\t// returns early — a stale run id, a workspace that went missing for one\n\t\t// tick, a throw — simply stops it, and the loop is the only thing that\n\t\t// ever collects a finished render. A character sheet finished on the\n\t\t// harness, paid for and sitting on a presigned url, while this page showed\n\t\t// it building for a quarter of an hour and never asked again. The job was\n\t\t// still there: it answered on the first ask afterwards.\n\t\t//\n\t\t// So: if there is a workspace to watch and nothing watching it, start\n\t\t// again. Twenty seconds is far below the cost of noticing by hand and far\n\t\t// above the poll's own cadence, so a healthy loop never trips it.\n\t\tconst watchdog = setInterval(() => {\n\t\t\tif (activeWs && !pollingActive && !staleRun) startPolling();\n\t\t}, 20_000);\n\t\treturn () => {\n\t\t\tclearInterval(clock);\n\t\t\tclearInterval(fast);\n\t\t\tclearInterval(watchdog);\n\t\t\tclearTimeout(settleAnyway);\n\t\t\tstopPolling();\n\t\t\tcleanupAudio?.();\n\t\t};\n\t});\n\n\t// RAT-632: the front page's questions as FAQPage markup, built from the same\n\t// list StudioAbout shows under the wall.\n\tconst faqJsonLd = jsonLdScript(studioFaqSchema());\n</script>\n\n<!-- The meta comes from the root layout (lib/studio-seo.ts): the studio is\n     PornPilot's front page, canonical at `/`. The layout sets this same title;\n     the page sets it too, so arriving client-side from a page with a title of its\n     own (sign-in) does not leave that one on the tab. -->\n<svelte:head>\n\t<title>{STUDIO_TITLE}</title>\n\t{@html faqJsonLd}\n</svelte:head>\n\n{#snippet statusPill(status: RailStatus)}\n\t<span\n\t\tclass=\"shrink-0 rounded-md px-2 py-0.5 text-[10px] tracking-wide\n\t\t\t{status === 'running' ? 'bg-[var(--st-surface-2)] font-semibold text-[var(--st-text)]' : ''}\n\t\t\t{status === 'done' ? 'bg-[var(--st-surface-2)] text-[var(--st-muted)]' : ''}\n\t\t\t{status === 'failed' ? 'bg-[#5c2f24] text-[#f2d7cd]' : ''}\n\t\t\t{status === 'regen' ? 'bg-[var(--st-surface-2)] text-[var(--st-text)]' : ''}\n\t\t\t{status === 'pending' ? 'text-[var(--st-faint)]' : ''}\"\n\t>\n\t\t{STATUS_LABEL[status]}\n\t</span>\n{/snippet}\n\n{#snippet railList()}\n\t{#if offline || lastError}\n\t\t<div class=\"mb-3 rounded-xl bg-[var(--st-surface-2)] px-3 py-2\">\n\t\t\t<p class=\"text-xs leading-relaxed text-[var(--st-muted)]\">\n\t\t\t\t{offline\n\t\t\t\t\t? 'The harness is not responding — showing the last known state.'\n\t\t\t\t\t: 'Error from the harness — showing the last known state.'}\n\t\t\t</p>\n\t\t</div>\n\t{/if}\n\t<ol class=\"space-y-1.5\">\n\t\t{#each rail as e (e.id)}\n\t\t\t<li class=\"flex items-center justify-between gap-3\">\n\t\t\t\t<span\n\t\t\t\t\tclass=\"min-w-0 truncate text-[13px] {e.status === 'pending'\n\t\t\t\t\t\t? 'text-[var(--st-faint)]'\n\t\t\t\t\t\t: 'text-[var(--st-text)]'}\"\n\t\t\t\t>\n\t\t\t\t\t{e.label}\n\t\t\t\t</span>\n\t\t\t\t{@render statusPill(e.status)}\n\t\t\t</li>\n\t\t{/each}\n\t</ol>\n\t<div class=\"mt-4\">\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tclass=\"cursor-pointer text-xs text-[var(--st-faint)] underline-offset-4 hover:text-[var(--st-muted)] hover:underline\"\n\t\t\tonclick={() => (showDetails = !showDetails)}\n\t\t>\n\t\t\t{showDetails ? 'hide details' : 'details'}\n\t\t</button>\n\t\t{#if showDetails}\n\t\t\t<div class=\"mt-2 space-y-1 font-mono text-[10px] leading-relaxed text-[var(--st-faint)]\">\n\t\t\t\t{#if planningWs}<p class=\"break-all\">plan: {planningWs}</p>{/if}\n\t\t\t\t{#if renderWs}<p class=\"break-all\">shoot: {renderWs}</p>{/if}\n\t\t\t\t{#if lastTick}<p>last update: {lastTick.toLocaleTimeString('en-GB')}</p>{/if}\n\t\t\t</div>\n\t\t{/if}\n\t</div>\n{/snippet}\n\n{#snippet confirmReply(item: ChatItem, canPress: boolean)}\n\t{#if item.confirm}\n\t\t{@const c = item.confirm}\n\t\t{@const parts = splitConfirm(c.line)}\n\t\t<!-- What is about to be shot, in the operator's own language.\n\t\t     Deliberately plain: no card chrome, no heading, no label saying what it\n\t\t     is. It reads as the studio answering, because that is what it is — and a\n\t\t     box around it would make it look like a form to fill in rather than a\n\t\t     sentence to agree with.\n\n\t\t     One drawing, two surfaces. The stage grew its own version of this — the\n\t\t     same three parts, centred in a 16:9 box, at rest — and the two designs\n\t\t     drifted the moment there were two of them. `canPress` is the only thing\n\t\t     the callers disagree about: the transcript shows the button on the newest\n\t\t     unlaunched round and nowhere else, because an older round is a step in\n\t\t     the conversation rather than an order you can still place; the stage\n\t\t     shows one round and it is always that one. -->\n\t\t<div class=\"enter\">\n\t\t\t<!-- What is missing, said in as few words as it takes.\n\t\t\t\t No box, no icon, no colour — except the refusal, which is the one\n\t\t\t\t thing on this surface allowed to be red. Two of the six also take\n\t\t\t\t the card away: a rule cannot be pressed past, and a picture with\n\t\t\t\t no words is not a shot to agree to. The rest ask and leave the\n\t\t\t\t button alone, and pressing it answers on the way past. -->\n\t\t\t{#if askAbout?.id === item.id && !c.streaming && c.line.trim()}\n\t\t\t\t{@const ask = askAbout.kind}\n\t\t\t\t{@const bad = ask === 'blocked'}\n\t\t\t\t<div class=\"enter {bad ? 'rounded-xl bg-[var(--st-warn,#e06c6c)]/[0.09] p-3.5' : ''}\">\n\t\t\t\t\t<p\n\t\t\t\t\t\tclass=\"text-sm leading-relaxed {bad\n\t\t\t\t\t\t\t? 'font-semibold text-[var(--st-warn,#e06c6c)]'\n\t\t\t\t\t\t\t: 'text-[var(--st-text)]'}\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{ask === 'blocked'\n\t\t\t\t\t\t\t? \"Can't make this.\"\n\t\t\t\t\t\t\t: ask === 'self'\n\t\t\t\t\t\t\t\t? \"You're in this one.\"\n\t\t\t\t\t\t\t\t: ask === 'who'\n\t\t\t\t\t\t\t\t\t? 'Who is this?'\n\t\t\t\t\t\t\t\t\t: ask === 'sheet'\n\t\t\t\t\t\t\t\t\t\t? \"That's a character.\"\n\t\t\t\t\t\t\t\t\t\t: ask === 'no-words'\n\t\t\t\t\t\t\t\t\t\t\t? 'Add a prompt.'\n\t\t\t\t\t\t\t\t\t\t\t: \"That's a subject.\"}\n\t\t\t\t\t</p>\n\t\t\t\t\t<p class=\"mt-1 max-w-[26rem] text-xs leading-relaxed text-[var(--st-muted)]\">\n\t\t\t\t\t\t{ask === 'blocked'\n\t\t\t\t\t\t\t? 'It describes someone under age. Write a different shot — everyone in it has to be an adult.'\n\t\t\t\t\t\t\t: ask === 'self'\n\t\t\t\t\t\t\t\t? 'Attach a photo or pick a character — without one the prompt just says \"a man\" or \"a woman\", and it is not you.'\n\t\t\t\t\t\t\t\t: ask === 'who'\n\t\t\t\t\t\t\t\t\t? 'Attach a picture or pick a character — without one the prompt just says \"a man\" or \"a woman\".'\n\t\t\t\t\t\t\t\t\t: ask === 'sheet'\n\t\t\t\t\t\t\t\t\t\t? 'That describes a person. Say what they do, and where.'\n\t\t\t\t\t\t\t\t\t\t: ask === 'no-words'\n\t\t\t\t\t\t\t\t\t\t\t? 'A picture is not a shot. Say what happens in it.'\n\t\t\t\t\t\t\t\t\t\t\t: 'Say what happens, and where.'}\n\t\t\t\t\t</p>\n\n\t\t\t\t\t<!-- Two ways to answer it, because there are two. A photograph is the\n\t\t\t\t\t\t likeness; a kept character is a likeness that was already made and\n\t\t\t\t\t\t already paid for, and leaving it out sent people back to the upload\n\t\t\t\t\t\t dialogue for a face they had rendered an hour ago. Neither is\n\t\t\t\t\t\t required — the third answer is to press the button and let it be a\n\t\t\t\t\t\t stranger, which is why nothing here is styled as a blocker. -->\n\t\t\t\t\t{#if ask === 'self' || ask === 'who'}\n\t\t\t\t\t\t<div class=\"mt-2.5 flex flex-wrap items-center gap-1.5\">\n\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\taria-busy={refBusy}\n\t\t\t\t\t\t\t\tclass=\"pillglass inline-flex min-h-8 {refBusy\n\t\t\t\t\t\t\t\t\t? 'cursor-wait opacity-60'\n\t\t\t\t\t\t\t\t\t: 'cursor-pointer'} items-center gap-2 rounded-full px-3.5 text-xs font-medium text-[var(--st-text)] transition-colors\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<svg viewBox=\"0 0 20 20\" class=\"size-3.5\" fill=\"none\" aria-hidden=\"true\">\n\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\td=\"M13 7l-5.5 5.5a2.1 2.1 0 003 3L16 10a3.5 3.5 0 00-5-5l-5.5 5.5a5 5 0 007 7L18 12\"\n\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.6\"\n\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t{refBusy\n\t\t\t\t\t\t\t\t\t? 'Attaching…'\n\t\t\t\t\t\t\t\t\t: ask === 'self'\n\t\t\t\t\t\t\t\t\t\t? 'Add a photo of you'\n\t\t\t\t\t\t\t\t\t\t: 'Add a picture of them'}\n\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\ttype=\"file\"\n\t\t\t\t\t\t\t\t\tmultiple\n\t\t\t\t\t\t\t\t\taccept=\"image/*\"\n\t\t\t\t\t\t\t\t\tclass=\"hidden\"\n\t\t\t\t\t\t\t\t\tdisabled={refBusy}\n\t\t\t\t\t\t\t\t\tonclick={guardUpload}\n\t\t\t\t\t\t\t\t\tonchange={(e) => {\n\t\t\t\t\t\t\t\t\t\tconst el = e.currentTarget as HTMLInputElement;\n\t\t\t\t\t\t\t\t\t\tattachRefs(el.files);\n\t\t\t\t\t\t\t\t\t\tel.value = '';\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclass=\"pillglass inline-flex min-h-8 cursor-pointer items-center rounded-full px-3.5 text-xs font-medium text-[var(--st-muted)] transition-colors hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\tshutMenus();\n\t\t\t\t\t\t\t\t\tpickKind = 'character';\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tPick a character\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t{/if}\n\n\t\t\t\t\t<!-- The missing parts, as things to press rather than things to read.\n\t\t\t\t\t\t A chip that says \"where it happens\" is a label; one that opens the\n\t\t\t\t\t\t location picker is an answer. `action` has nowhere to go but the\n\t\t\t\t\t\t box, so it puts the cursor there. -->\n\t\t\t\t\t{#if (ask === 'too-short' || ask === 'sheet') && askAbout.missing.length}\n\t\t\t\t\t\t<p class=\"mt-3 text-xs text-[var(--st-faint)]\">Add anything?</p>\n\t\t\t\t\t\t<div class=\"mt-1.5 flex flex-wrap gap-1.5\">\n\t\t\t\t\t\t\t{#each askAbout.missing as bit (bit)}\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tclass=\"pillglass cursor-pointer rounded-full px-2.5 py-1 text-[11px] text-[var(--st-muted)] transition-colors hover:text-[var(--st-text)] disabled:cursor-default disabled:opacity-50\"\n\t\t\t\t\t\t\t\t\tdisabled={!!suggesting}\n\t\t\t\t\t\t\t\t\taria-busy={suggesting === bit}\n\t\t\t\t\t\t\t\t\tonclick={() => addMissing(bit, askAbout.said)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{suggesting === bit\n\t\t\t\t\t\t\t\t\t\t? 'writing…'\n\t\t\t\t\t\t\t\t\t\t: bit === 'character'\n\t\t\t\t\t\t\t\t\t\t\t? 'a character'\n\t\t\t\t\t\t\t\t\t\t\t: bit === 'place'\n\t\t\t\t\t\t\t\t\t\t\t\t? 'a place'\n\t\t\t\t\t\t\t\t\t\t\t\t: bit === 'action'\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'what happens'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: 'the camera'}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t{/if}\n\t\t\t\t</div>\n\t\t\t{/if}\n\t\t\t<!-- The shot is an object, and the talk around it is talk.\n\t\t\t\t Everything on this card used to be prose in one column — the\n\t\t\t\t question, the guess, the footnote and the button all the same\n\t\t\t\t weight, so there was nothing to agree TO, only a paragraph to\n\t\t\t\t read. What is being made now has an edge around it, and the two\n\t\t\t\t numbers you commit and the button that commits them are inside\n\t\t\t\t that edge rather than floating under it. You approve a thing.\n\n\t\t\t\t The conversation above it stays unframed on purpose: a bubble or a\n\t\t\t\t second card around the question would make two objects out of one\n\t\t\t\t object and one remark. -->\n\t\t\t<!-- No card under a refusal, and none under a picture with no words.\n\t\t\t\t One cannot be pressed past; the other has nothing to agree to yet, and\n\t\t\t\t showing an invented shot there is what started all of this. -->\n\t\t\t{#if askAbout?.id !== item.id || (askAbout.kind !== 'blocked' && askAbout.kind !== 'no-words')}\n\t\t\t\t<div class=\"shotglass mt-3 overflow-hidden rounded-2xl ring-1 ring-[var(--st-line)]\">\n\t\t\t\t\t<!-- No button under a refusal. Everything else here is an offer with\n\t\t\t\t\t\t the press still available; this one is not, and leaving it there\n\t\t\t\t\t\t would say the rule is a suggestion. -->\n\t\t\t\t\t{#if canPress && !c.streaming && c.line.trim() && askAbout?.kind !== 'blocked'}\n\t\t\t\t\t\t<!-- The count is this CARD's, not the composer's. A continuation makes one\n\t\t\t\t\t\t     clip whatever the angle setting says — the pinned seam has only one\n\t\t\t\t\t\t     frame to start from — and a price that read the dial instead of the\n\t\t\t\t\t\t     card would quote three for a render that makes one. -->\n\t\t\t\t\t\t{@const cardClips = takes * (c.continues && c.continues.pinned !== false ? 1 : angles)}\n\t\t\t\t\t\t{@const cardPrice = tokensForSend(composerShape.seconds, cardClips, freeClip)}\n\t\t\t\t\t\t<!-- At the head of the card, not on its floor. A card that reads down\n\t\t\t\t\t\t     to its action is fine on a page you scroll; this one is the last\n\t\t\t\t\t\t     thing in a column that is already scrolled to the bottom, so the\n\t\t\t\t\t\t     button sat under the fold of a box whose own text pushed it there\n\t\t\t\t\t\t     — the one control on the screen, and you had to go looking. Up\n\t\t\t\t\t\t     here it is the first thing in the frame and the description reads\n\t\t\t\t\t\t     under it. The cost stays beside it: the two numbers a person wants\n\t\t\t\t\t\t     before they commit, in the place where committing happens. -->\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclass=\"relative flex flex-wrap items-center justify-between gap-3 border-b border-[var(--st-line)] px-4 py-3\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tdisabled={shotBusy[item.id]}\n\t\t\t\t\t\t\t\tclass=\"btn btn-primary\"\n\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\tselfAnsweredBySending(item.id);\n\t\t\t\t\t\t\t\t\tacceptConfirm(item.id);\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{#if shotBusy[item.id]}\n\t\t\t\t\t\t\t\t\t{@const el = Math.max(0, Math.round((now - (c.busySince ?? now)) / 1000))}\n\t\t\t\t\t\t\t\t\t{c.phase === 'writing'\n\t\t\t\t\t\t\t\t\t\t? 'writing the brief'\n\t\t\t\t\t\t\t\t\t\t: c.phase === 'starting'\n\t\t\t\t\t\t\t\t\t\t\t? 'opening the workspace'\n\t\t\t\t\t\t\t\t\t\t\t: 'starting'} ·\n\t\t\t\t\t\t\t\t\t{clock(el)}\n\t\t\t\t\t\t\t\t{:else if c.fixed?.length}\n\t\t\t\t\t\t\t\t\tsend it anyway\n\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t{c.continues ? 'Continue the clip' : 'Generate the video'}\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<span class=\"text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t{composerShape.seconds}s ·\n\t\t\t\t\t\t\t\t<span class={tokens < cardPrice ? 'text-[var(--st-text)]' : ''}\n\t\t\t\t\t\t\t\t\t>{priceLabel(cardPrice, freeClip)}</span\n\t\t\t\t\t\t\t\t>{#if cardClips > 1}&nbsp;· {cardClips} clips{/if}&nbsp;·\n\t\t\t\t\t\t\t\t{typicalLabel(pressToClipMs(!!c.continues))}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t{/if}\n\t\t\t\t\t<div class=\"p-4\">\n\t\t\t\t\t\t{#if askAbout?.id === item.id}\n\t\t\t\t\t\t\t<p class=\"mb-1.5 text-xs text-[var(--st-faint)]\">One way it could go</p>\n\t\t\t\t\t\t{:else if parts.lead}\n\t\t\t\t\t\t\t<p class=\"mb-1.5 text-xs text-[var(--st-faint)]\">{parts.lead}</p>\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t<p class=\"doc text-sm leading-relaxed text-[var(--st-text)]\">\n\t\t\t\t\t\t\t{parts.said}{#if c.streaming && !parts.added}<span class=\"caret\" aria-hidden=\"true\"\n\t\t\t\t\t\t\t\t></span>{/if}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t{#if parts.added}\n\t\t\t\t\t\t\t<!-- Ours, and it has to look it. Same size, quieter colour: it is not a\n\t\t\t\t\t\t\t     footnote — it is half of what starts if the button is pressed — but\n\t\t\t\t\t\t\t     it is an offer, and an offer that looks like a statement is not one. -->\n\t\t\t\t\t\t\t<p class=\"doc mt-1.5 text-sm leading-relaxed text-[var(--st-muted)]\">\n\t\t\t\t\t\t\t\t{parts.added}{#if c.streaming}<span class=\"caret\" aria-hidden=\"true\"></span>{/if}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t{#if c.error}\n\t\t\t\t\t\t\t<p class=\"mt-2 text-xs leading-relaxed text-[var(--st-faint)]\">{c.error}</p>\n\t\t\t\t\t\t\t<!-- The one thing to do about it, where it happened. Without it the\n\t\t\t\t\t\t\t     only way past a read-back that failed was to type the sentence\n\t\t\t\t\t\t\t     again, which is the app's work, not the operator's. -->\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tdisabled={sending}\n\t\t\t\t\t\t\t\tonclick={() => retryConfirm(item)}\n\t\t\t\t\t\t\t\tclass=\"mt-2.5 cursor-pointer rounded-full bg-[var(--st-surface-2)] px-3.5 py-1.5 text-xs font-semibold transition-colors hover:bg-[var(--st-line)] disabled:cursor-default disabled:opacity-50\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\ttry again\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t<!-- The checker had to change the brief, so this is no longer the clip that\n\t\t\t\t\t\t     was agreed to. It says what moved and waits: sending it anyway is a\n\t\t\t\t\t\t     decision, and it is not ours. -->\n\t\t\t\t\t\t{#if c.fixed?.length}\n\t\t\t\t\t\t\t<p class=\"mt-3 text-xs leading-relaxed text-[var(--st-muted)]\">\n\t\t\t\t\t\t\t\tWe adjusted this while writing it: {c.fixed.join(' · ')}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t{/if}\n\t\t</div>\n\t{/if}\n{/snippet}\n\n{#snippet filmReel()}\n\t<!-- The reel. Whole clips only — that is the line between a strip and\n an editor, and the one that keeps this from becoming a tool you\n have to learn. -->\n\t<div class=\"enter mb-2 flex items-center gap-2.5 px-2\">\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\taria-label=\"play the film\"\n\t\t\tonclick={() => openFilmViewer(0)}\n\t\t\tclass=\"flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-full bg-[var(--st-surface-2)] text-[0.7rem] text-[var(--st-text)] transition-colors hover:bg-[var(--st-line-control)]\"\n\t\t>\n\t\t\t▶\n\t\t</button>\n\t\t<!-- svelte-ignore a11y_no_static_element_interactions -->\n\t\t<div\n\t\t\tclass=\"reel flex min-w-0 flex-1 items-center overflow-x-auto py-0.5\"\n\t\t\tondragover={(e) => {\n\t\t\t\tif (e.dataTransfer?.types.includes(CLIP_DRAG)) e.preventDefault();\n\t\t\t}}\n\t\t\tondrop={(e) => dropClipIntoFilm(e)}\n\t\t>\n\t\t\t{#each film as c, i (filmKey(c))}\n\t\t\t\t{#if i}\n\t\t\t\t\t<span\n\t\t\t\t\t\tclass=\"relative w-1.5 shrink-0 self-stretch\"\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\tclass:seam-jump={seamJumps(i)}\n\t\t\t\t\t></span>\n\t\t\t\t{/if}\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\taria-label=\"shot {i + 1}\"\n\t\t\t\t\tdraggable=\"true\"\n\t\t\t\t\tondragstart={(e) => e.dataTransfer?.setData('text/plain', String(i))}\n\t\t\t\t\tondragover={(e) => e.preventDefault()}\n\t\t\t\t\tondrop={(e) => {\n\t\t\t\t\t\tif (e.dataTransfer?.types.includes(CLIP_DRAG)) return;\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tmoveInFilm(Number(e.dataTransfer?.getData('text/plain')), i);\n\t\t\t\t\t}}\n\t\t\t\t\tonclick={(e) => {\n\t\t\t\t\t\tconst r = e.currentTarget.getBoundingClientRect();\n\t\t\t\t\t\tif (e.clientX > r.right - 22 && e.clientY < r.top + 22) dropFromFilm(i);\n\t\t\t\t\t\telse openFilmViewer(i);\n\t\t\t\t\t}}\n\t\t\t\t\tclass=\"group relative aspect-video w-[5.4rem] shrink-0 cursor-grab overflow-hidden rounded-lg bg-[var(--st-surface)] active:cursor-grabbing\"\n\t\t\t\t>\n\t\t\t\t\t<!-- svelte-ignore a11y_media_has_caption -->\n\t\t\t\t\t<video\n\t\t\t\t\t\tsrc={still(fileUrl(c.workspace, c.artifact, c.file))}\n\t\t\t\t\t\tmuted\n\t\t\t\t\t\tloop\n\t\t\t\t\t\tplaysinline\n\t\t\t\t\t\tpreload=\"auto\"\n\t\t\t\t\t\tuse:looping\n\t\t\t\t\t\tclass=\"h-full w-full bg-black object-cover\"\n\t\t\t\t\t></video>\n\t\t\t\t\t<span\n\t\t\t\t\t\tclass=\"pointer-events-none absolute top-0.5 right-0.5 flex size-[1.1rem] items-center justify-center rounded-full bg-black/60 text-[0.65rem] text-white opacity-0 backdrop-blur transition-opacity group-hover:opacity-100\"\n\t\t\t\t\t\t>✕</span\n\t\t\t\t\t>\n\t\t\t\t</button>\n\t\t\t{/each}\n\t\t</div>\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tdisabled={film.length < 2 || filmBusy}\n\t\t\tonclick={exportFilm}\n\t\t\tclass=\"shrink-0 cursor-pointer rounded-full bg-[var(--st-text)] px-3.5 py-1.5 text-xs font-medium text-black transition-colors hover:bg-white disabled:cursor-default disabled:opacity-40 disabled:hover:bg-[var(--st-text)]\"\n\t\t>\n\t\t\t{filmBusy ? 'assembling…' : 'Export'}\n\t\t</button>\n\t</div>\n{/snippet}\n\n{#snippet document(blocks: Block[])}\n\t<div class=\"space-y-3.5 text-[0.95rem] leading-[1.7] text-[var(--st-text)]\">\n\t\t{#each blocks as b, i (i)}\n\t\t\t{#if b.kind === 'heading'}\n\t\t\t\t<h4\n\t\t\t\t\tclass=\"font-display font-semibold {b.level === 1 ? 'text-base' : 'text-sm'} {i > 0\n\t\t\t\t\t\t? 'pt-2'\n\t\t\t\t\t\t: ''}\"\n\t\t\t\t>\n\t\t\t\t\t{b.text}\n\t\t\t\t</h4>\n\t\t\t{:else if b.kind === 'para'}\n\t\t\t\t<p>\n\t\t\t\t\t{#each b.spans as s, j (j)}{#if s.bold}<strong class=\"font-semibold\">{s.text}</strong\n\t\t\t\t\t\t\t>{:else if s.italic}<em>{s.text}</em>{:else}{s.text}{/if}{/each}\n\t\t\t\t</p>\n\t\t\t{:else if b.kind === 'list'}\n\t\t\t\t<ul class=\"space-y-1.5 pl-4\">\n\t\t\t\t\t{#each b.items as item, j (j)}\n\t\t\t\t\t\t<li class=\"list-disc\">\n\t\t\t\t\t\t\t{#each item as s, k (k)}{#if s.bold}<strong class=\"font-semibold\">{s.text}</strong\n\t\t\t\t\t\t\t\t\t>{:else if s.italic}<em>{s.text}</em>{:else}{s.text}{/if}{/each}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t{/each}\n\t\t\t\t</ul>\n\t\t\t{:else if b.kind === 'table'}\n\t\t\t\t<!-- Wide tables scroll inside their own card rather than pushing the\n\t\t\t\t\t whole column sideways. -->\n\t\t\t\t<div class=\"scroller -mx-1 overflow-x-auto px-1\">\n\t\t\t\t\t<table class=\"w-full min-w-[34rem] border-collapse text-sm\">\n\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t{#each b.head as h (h)}\n\t\t\t\t\t\t\t\t\t<th\n\t\t\t\t\t\t\t\t\t\tclass=\"border-b border-[var(--st-line)] px-2.5 py-2 text-left text-xs font-semibold tracking-wide text-[var(--st-muted)] uppercase\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{h}\n\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t{#each b.rows as row, r (r)}\n\t\t\t\t\t\t\t\t<tr class=\"align-top\">\n\t\t\t\t\t\t\t\t\t{#each row as cell, c (c)}\n\t\t\t\t\t\t\t\t\t\t<td\n\t\t\t\t\t\t\t\t\t\t\tclass=\"border-b border-[var(--st-line)] px-2.5 py-2.5 {c === 0\n\t\t\t\t\t\t\t\t\t\t\t\t? 'font-semibold whitespace-nowrap'\n\t\t\t\t\t\t\t\t\t\t\t\t: ''}\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{cell}\n\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t</tbody>\n\t\t\t\t\t</table>\n\t\t\t\t</div>\n\t\t\t{:else if b.kind === 'rule'}\n\t\t\t\t<hr class=\"border-[var(--st-line)]\" />\n\t\t\t{:else if b.kind === 'slug'}\n\t\t\t\t<p\n\t\t\t\t\tclass=\"pt-3 font-mono text-xs font-semibold tracking-widest text-[var(--st-muted)] uppercase\"\n\t\t\t\t>\n\t\t\t\t\t{b.text}\n\t\t\t\t</p>\n\t\t\t{:else if b.kind === 'transition'}\n\t\t\t\t<p class=\"text-right font-mono text-xs tracking-widest text-[var(--st-faint)] uppercase\">\n\t\t\t\t\t{b.text}\n\t\t\t\t</p>\n\t\t\t{:else if b.kind === 'cue'}\n\t\t\t\t<!-- Dialogue indented the way a script page does it: the eye finds who\n\t\t\t\t\t is speaking without reading the line. -->\n\t\t\t\t<div class=\"pl-6 sm:pl-12\">\n\t\t\t\t\t<p class=\"font-mono text-xs font-semibold tracking-wider\">\n\t\t\t\t\t\t{b.who}{#if b.parenthetical}<span class=\"font-normal text-[var(--st-muted)]\">\n\t\t\t\t\t\t\t\t({b.parenthetical})</span\n\t\t\t\t\t\t\t>{/if}\n\t\t\t\t\t</p>\n\t\t\t\t\t{#each b.lines as l, j (j)}\n\t\t\t\t\t\t<p class=\"text-[0.95rem]\">{l}</p>\n\t\t\t\t\t{/each}\n\t\t\t\t</div>\n\t\t\t{:else if b.kind === 'anchor'}\n\t\t\t\t<!-- An anchor is pasted verbatim into every render prompt, so it is\n\t\t\t\t\t shown as the quotable unit it is, not as prose. -->\n\t\t\t\t<div class=\"rounded-xl bg-[var(--st-surface-2)] px-3.5 py-3\">\n\t\t\t\t\t<p class=\"text-[10px] font-semibold tracking-[0.18em] text-[var(--st-faint)] uppercase\">\n\t\t\t\t\t\t{b.label}\n\t\t\t\t\t</p>\n\t\t\t\t\t<p class=\"mt-1.5 text-[0.9rem] leading-relaxed\">{b.text}</p>\n\t\t\t\t</div>\n\t\t\t{/if}\n\t\t{/each}\n\t</div>\n{/snippet}\n\n{#snippet videoCard(name: string, url: string, caption: string, clipKey = '')}\n\t<!-- No surface and no rounding of its own. The caller wraps this together with\n\t     the action band in one rounded card: the picture and what you can do with\n\t     it are one object, and drawing them as two blocks with the page's black\n\t     between them said they were not. -->\n\t<figure class=\"contents\">\n\t\t<!-- The app-wide CSS in layout.css hides every native media control on\n\t\t     <video> unless the element opts in with .video-with-controls. -->\n\t\t<!-- svelte-ignore a11y_media_has_caption -->\n\t\t<video\n\t\t\tsrc={url}\n\t\t\tcontrols\n\t\t\tplaysinline\n\t\t\tpreload=\"metadata\"\n\t\t\tdata-clip={clipKey || null}\n\t\t\tcrossorigin=\"anonymous\"\n\t\t\tonerror={(e) => recoverVideo(e.currentTarget as HTMLVideoElement, url)}\n\t\t\tclass=\"video-with-controls block aspect-video w-full bg-black\"\n\t\t></video>\n\t\t{#if caption}\n\t\t\t<figcaption class=\"px-4 pt-3 text-sm text-[var(--st-muted)]\">{caption}</figcaption>\n\t\t{/if}\n\t</figure>\n{/snippet}\n\n<!-- One take, at whatever size the grid it sits in gives it. The same tile\n\t serves the strip in the transcript and the filmstrip inside the viewer, so\n\t the two can never drift apart — pressing one in the strip opens the viewer\n\t on exactly the picture that was pressed.\n\n\t A run still on the GPU keeps its place and its number rather than being left\n\t out: the strip is then its final shape from the first second, and nothing\n\t moves under the cursor as the takes land. -->\n<!-- One thing this studio made. Films get the wider box and the shot count\n\t the badge; a shot gets its length and nothing else, because on a grid of\n\t thirty-six the only question is which one it was. -->\n{#snippet starters()}\n\t<!-- The next card peeks past the right edge, so the row reads as a row before\n\t\t anything has moved. Snap, so a swipe lands on a card rather than between\n\t\t two of them. -->\n\t<div class=\"w-full max-w-[34rem] pt-2 sm:max-w-3xl\">\n\t\t<div\n\t\t\tbind:this={starterRow}\n\t\t\tonscroll={starterScrolled}\n\t\t\tonpointerdown={() => (starterHeld = true)}\n\t\t\tclass=\"starterrow flex snap-x snap-mandatory gap-2 overflow-x-auto pb-1 sm:grid sm:grid-cols-3 sm:gap-2.5 sm:overflow-visible sm:pb-0\"\n\t\t>\n\t\t\t{#each examples as ex, i (ex)}\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tonclick={() => useExample(ex)}\n\t\t\t\t\taria-label=\"use this example\"\n\t\t\t\t\taria-current={i === starterAt}\n\t\t\t\t\tclass=\"relative flex min-h-[5.5rem] w-[86%] shrink-0 cursor-pointer snap-start items-start gap-2.5 rounded-xl p-4 text-left text-sm leading-snug text-[var(--st-muted)] ring-1 ring-[var(--st-line)] transition-colors hover:bg-[var(--st-surface)] hover:text-[var(--st-text)] hover:ring-transparent sm:w-full sm:shrink\"\n\t\t\t\t>\n\t\t\t\t\t<span class=\"min-w-0 pr-4\">{ex}</span>\n\t\t\t\t\t<!-- Says what the tap does: it fills the box, it does not send. -->\n\t\t\t\t\t<svg\n\t\t\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\t\t\tclass=\"absolute top-3 right-3 size-3 opacity-30\"\n\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<path\n\t\t\t\t\t\t\td=\"M4 12L12 4M6 4h6v6\"\n\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\tstroke-width=\"1.6\"\n\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</svg>\n\t\t\t\t</button>\n\t\t\t{/each}\n\t\t</div>\n\t\t<!-- How many there are, and which one this is. The dots do the telling;\n\t\t\t they are not a control, so they are not a target. -->\n\t\t<div class=\"mt-2 flex justify-center gap-1.5 sm:hidden\" aria-hidden=\"true\">\n\t\t\t{#each examples as ex, i (ex)}\n\t\t\t\t<span\n\t\t\t\t\tclass=\"h-1.5 rounded-full transition-all duration-300 {i === starterAt\n\t\t\t\t\t\t? 'w-4 bg-[var(--st-muted)]'\n\t\t\t\t\t\t: 'w-1.5 bg-[var(--st-line)]'}\"\n\t\t\t\t></span>\n\t\t\t{/each}\n\t\t</div>\n\t</div>\n{/snippet}\n\n{#snippet mediaTile(m: ShelfItem, i: number)}\n\t<button\n\t\ttype=\"button\"\n\t\taria-label=\"film, {clipClock(m.seconds)}\"\n\t\tonclick={() => (mediaPopup = m)}\n\t\tclass=\"group relative mb-2 block w-full cursor-pointer break-inside-avoid overflow-hidden rounded-xl bg-[var(--st-surface)] {tileRatio(\n\t\t\tm.seconds\n\t\t)}\"\n\t>\n\t\t<!-- No length, no shot count, no caption. A wall of stills is looked at,\n\t\t\t not read, and a badge on every tile turns a shelf back into a file\n\t\t\t listing — which is what this stopped being. The length is still in the\n\t\t\t label for anybody navigating by voice or keyboard, where it is the only\n\t\t\t way to tell one tile from the next. -->\n\t\t<!-- svelte-ignore a11y_media_has_caption -->\n\t\t<video\n\t\t\tbind:this={tileEls[m.id]}\n\t\t\tsrc={still(shelfUrl(m))}\n\t\t\tmuted\n\t\t\tloop\n\t\t\tplaysinline\n\t\t\tpreload=\"metadata\"\n\t\t\tclass=\"h-full w-full object-cover transition-transform duration-200 group-hover:scale-[1.04]\"\n\t\t></video>\n\t</button>\n{/snippet}\n\n{#snippet takeTile(\n\titem: ChatItem,\n\trun: NonNullable<ChatItem['takes']>['runs'][number],\n\tfilm = false\n)}\n\t{@const chosen = film && takesAt?.index === run.index}\n\t{#if run.state === 'ready' && run.clip}\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\taria-label=\"take {run.index}\"\n\t\t\taria-current={film ? chosen : undefined}\n\t\t\tonclick={(e) => openTake(item.id, run.index, e.currentTarget)}\n\t\t\tclass=\"relative block aspect-video w-full cursor-pointer overflow-hidden rounded-[10px] bg-[var(--st-surface)] transition-[transform,opacity] duration-200 hover:scale-[1.014] {film\n\t\t\t\t? chosen\n\t\t\t\t\t? 'opacity-100 shadow-[inset_0_0_0_2px_var(--st-text)]'\n\t\t\t\t\t: 'opacity-50 hover:opacity-80'\n\t\t\t\t: ''}\"\n\t\t>\n\t\t\t<!-- svelte-ignore a11y_media_has_caption -->\n\t\t\t<video\n\t\t\t\tsrc={still(fileUrl(run.clip.workspace, run.clip.artifact, run.clip.file))}\n\t\t\t\tmuted\n\t\t\t\tloop\n\t\t\t\tplaysinline\n\t\t\t\tpreload=\"auto\"\n\t\t\t\tuse:looping\n\t\t\t\tclass=\"h-full w-full bg-black object-cover\"\n\t\t\t></video>\n\t\t\t<span\n\t\t\t\tclass=\"pointer-events-none absolute bottom-1.5 left-1.5 rounded-[5px] px-1.5 py-px text-[11px] font-medium tabular-nums backdrop-blur-md {chosen\n\t\t\t\t\t? 'bg-[var(--st-text)] text-black'\n\t\t\t\t\t: 'bg-black/50 text-white'}\"\n\t\t\t>\n\t\t\t\t{run.index}\n\t\t\t</span>\n\t\t</button>\n\t{:else}\n\t\t<div\n\t\t\tclass=\"st-slot relative flex aspect-video w-full items-center justify-center overflow-hidden rounded-[10px] bg-[var(--st-surface)] {run.state ===\n\t\t\t'rendering'\n\t\t\t\t? 'st-waiting'\n\t\t\t\t: ''}\"\n\t\t>\n\t\t\t{#if run.state === 'failed'}\n\t\t\t\t<!-- The word, not the reason: the tile is 175px across and the reason\n\t\t\t\t\t is a sentence. It goes on the card's own line underneath. -->\n\t\t\t\t<span class=\"px-2 text-center text-[11px] text-[var(--st-faint)]\">interrupted</span>\n\t\t\t{/if}\n\t\t\t<span\n\t\t\t\tclass=\"pointer-events-none absolute bottom-1.5 left-1.5 rounded-[5px] bg-black/50 px-1.5 py-px text-[11px] font-medium text-white tabular-nums backdrop-blur-md\"\n\t\t\t>\n\t\t\t\t{run.index}\n\t\t\t</span>\n\t\t</div>\n\t{/if}\n{/snippet}\n\n<!-- Escape closes the composer's menus. A click outside them is handled by the\n\t backdrop the menus render behind themselves, not from here: Svelte delegates\n\t element handlers to the root, so a window-level listener and a\n\t stopPropagation in a delegated handler do not reliably compose. -->\n<!-- The takes viewer takes the keyboard first while it is open: it covers the\n\t page, so Escape belongs to it and not to menus nobody can see. -->\n<svelte:window\n\tonkeydown={(e) => {\n\t\tif (filmAt !== null) {\n\t\t\tif (e.key === 'Escape') shutFilmViewer();\n\t\t\telse if (e.key === 'ArrowLeft') stepFilm(-1);\n\t\t\telse if (e.key === 'ArrowRight') stepFilm(1);\n\t\t\treturn;\n\t\t}\n\t\tif (takesAt) {\n\t\t\tif (e.key === 'Escape') shutTake();\n\t\t\telse if (e.key === 'ArrowLeft') stepTake(-1);\n\t\t\telse if (e.key === 'ArrowRight') stepTake(1);\n\t\t\treturn;\n\t\t}\n\t\tif (e.key === 'Escape') shutMenus();\n\t}}\n/>\n\n<!-- Chat-app shell: the page itself never scrolls. The window is split into a\n\t fixed header, a scrolling transcript and a pinned composer, so the input and\n\t the task rail stay put while only the conversation moves — the layout every\n\t chat client converges on. 100dvh (not vh) keeps it correct on mobile Safari,\n\t where the URL bar changes the viewport height mid-scroll. -->\n<div class=\"studio flex h-[100dvh] overflow-hidden\">\n\t<!-- The name and the control that opens the list, in the page's own corner.\n\t\t There were two of these — one in the rail, one in the main column, each\n\t\t shown when the other was not — on the theory that they sat at the same\n\t\t point and so nothing appeared to move. They did sit at the same point,\n\t\t until the layout beside them changed: the main column now starts after the\n\t\t rail's reserved space, so the closed copy was drawn a rail's width in and\n\t\t the wordmark jumped sideways every time the list was opened or shut.\n\n\t\t Two things that must stay in one place cannot be two things. This is one\n\t\t header, fixed to the viewport, above both the rail and the page: it is\n\t\t drawn once, it cannot be moved by anything either of them does, and the\n\t\t panel slides out from under it. -->\n\t<header class=\"fixed top-0 left-0 z-50 flex h-12 items-center gap-2.5 px-3\">\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\taria-label={sidebarOpen ? 'hide past productions' : 'show past productions'}\n\t\t\taria-expanded={sidebarOpen}\n\t\t\tonclick={() => setNavOpen(!sidebarOpen)}\n\t\t\tclass=\"flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-lg text-[var(--st-muted)] transition-colors hover:bg-[var(--st-surface)] hover:text-[var(--st-text)]\"\n\t\t>\n\t\t\t<!-- Three rules, the last one short. It reads as a list that can be\n\t\t\t\t\t pulled open rather than as a menu, and the ragged end keeps it from\n\t\t\t\t\t sitting like a block of three identical bars. -->\n\t\t\t<svg viewBox=\"0 0 16 16\" class=\"size-[18px]\" fill=\"none\" aria-hidden=\"true\">\n\t\t\t\t<path\n\t\t\t\t\td=\"M2.5 4h11M2.5 8h11M2.5 12h7\"\n\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\tstroke-width=\"1.5\"\n\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t/>\n\t\t\t</svg>\n\t\t</button>\n\t\t<!-- The product's name, at a size a name is set at. It was ten pixels of\n\t\t\t letterspaced caps in the faintest colour on the page — the least legible\n\t\t\t text in the app was the thing it is called. -->\n\t\t<!-- The name is the way home. Every product with a shelf behind it works\n\t\t\t this way, and there was no way back to the front page at all: once a\n\t\t\t session had a message in it the only route to what you had made was the\n\t\t\t sidebar, and on a phone the sidebar is closed. -->\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\ttitle=\"everything you have made\"\n\t\t\tonclick={() => {\n\t\t\t\treset();\n\t\t\t\tvoid loadMedia();\n\t\t\t\tif (window.innerWidth < 1024) setNavOpen(false);\n\t\t\t}}\n\t\t\tclass=\"cursor-pointer font-display text-[1.0625rem] font-semibold tracking-[-0.02em] transition-opacity hover:opacity-70\"\n\t\t>\n\t\t\tPornPilot\n\t\t</button>\n\t</header>\n\n\t<!-- ── past productions ────────────────────────────────────────────────────\n\t     Off-canvas below lg, because the transcript is the page on a phone and a\n\t     permanent rail would take a third of it. Above lg it is simply there:\n\t     going back to a run should not cost a click to reveal the way back. -->\n\t{#if sidebarOpen}\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\taria-label=\"close the production list\"\n\t\t\tclass=\"fixed inset-0 z-30 cursor-default bg-black/50 lg:hidden\"\n\t\t\tonclick={() => setNavOpen(false)}\n\t\t></button>\n\t{/if}\n\n\t<aside\n\t\tclass=\"fixed inset-y-0 left-0 z-40 flex w-64 shrink-0 flex-col border-r border-[var(--st-line)] bg-[var(--st-bg)] transition-transform lg:static lg:z-auto {sidebarOpen\n\t\t\t? 'translate-x-0 lg:translate-x-0'\n\t\t\t: '-translate-x-full lg:hidden'}\"\n\t>\n\t\t<!-- The room the fixed header stands in. The rail's list begins below it. -->\n\t\t<div class=\"h-12 shrink-0\" aria-hidden=\"true\"></div>\n\n\t\t<!-- The two doors, named apart.\n\t\t\t They were one radio pair inside the composer's format menu, which put a\n\t\t\t choice about WHAT KIND OF WORK this is next to a choice about how many\n\t\t\t takes of it to shoot. They are not the same size of decision: one clip\n\t\t\t is a shot you carry forward by hand, a full production writes a\n\t\t\t screenplay and shoots a scene list from it, and the second one keeps the\n\t\t\t chat surface because a plan is a conversation.\n\t\t\t The names are the ones the menu already used. -->\n\t\t<div class=\"px-3 pt-2 pb-2\">\n\t\t\t<!-- One door. The second one — a full production: a screenplay first, then a\n\t\t\t\t scene list shot from it — is a different product with a different\n\t\t\t\t surface, and offering it beside \"New clip\" asked a person to choose\n\t\t\t\t between the two before they had made anything at all. The mode is\n\t\t\t\t intact and a production already made still opens in it; it is simply\n\t\t\t\t not the first question the studio asks. -->\n\t\t\t{#each [{ id: 'simple', label: 'New clip', hint: 'shots, chained by hand' }] as door (door.id)}\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t// `New` in both names, so both start one. The row above them used to\n\t\t\t\t\t\t// be the only way to begin again, and with it gone these have to do\n\t\t\t\t\t\t// what they say — clicking \"New clip\" while already making clips\n\t\t\t\t\t\t// otherwise did nothing at all, which is the worst answer a button\n\t\t\t\t\t\t// with that name can give.\n\t\t\t\t\t\treset();\n\t\t\t\t\t\tsetMode(door.id as 'simple' | 'advanced');\n\t\t\t\t\t\tsetSessionSurface('clip', 'sidebar');\n\t\t\t\t\t\tif (window.innerWidth < 1024) setNavOpen(false);\n\t\t\t\t\t}}\n\t\t\t\t\taria-current={mode === door.id && sessionState.surface !== 'session' ? 'page' : undefined}\n\t\t\t\t\tclass=\"flex min-h-10 w-full cursor-pointer items-center gap-3 rounded-xl px-3 text-left text-sm transition-colors {mode ===\n\t\t\t\t\t\tdoor.id && sessionState.surface !== 'session'\n\t\t\t\t\t\t? 'bg-[var(--st-surface)] font-semibold text-[var(--st-text)]'\n\t\t\t\t\t\t: 'text-[var(--st-muted)] hover:bg-[var(--st-surface)]'}\"\n\t\t\t\t\ttitle={door.hint}\n\t\t\t\t>\n\t\t\t\t\t<svg\n\t\t\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\t\t\tclass=\"size-4 shrink-0 {mode === door.id && sessionState.surface !== 'session'\n\t\t\t\t\t\t\t? ''\n\t\t\t\t\t\t\t: 'text-[var(--st-faint)]'}\"\n\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{#if door.id === 'simple'}\n\t\t\t\t\t\t\t<!-- A frame with a play mark in it. The mark was a bare three-point\n\t\t\t\t\t\t\t\t triangle with hard corners sitting inside a rounded frame — the\n\t\t\t\t\t\t\t\t one shape on the icon that had not been told what the others\n\t\t\t\t\t\t\t\t were doing. Stroked in its own fill with a round join, it keeps\n\t\t\t\t\t\t\t\t its weight and gets the corners the rest of the set has. -->\n\t\t\t\t\t\t\t<rect\n\t\t\t\t\t\t\t\tx=\"2.5\"\n\t\t\t\t\t\t\t\ty=\"4\"\n\t\t\t\t\t\t\t\twidth=\"11\"\n\t\t\t\t\t\t\t\theight=\"8\"\n\t\t\t\t\t\t\t\trx=\"2\"\n\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\tstroke-width=\"1.5\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\td=\"M7.1 6.6 9.9 8l-2.8 1.4z\"\n\t\t\t\t\t\t\t\tfill=\"currentColor\"\n\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\tstroke-width=\"1.1\"\n\t\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t<rect\n\t\t\t\t\t\t\t\tx=\"2.5\"\n\t\t\t\t\t\t\t\ty=\"3\"\n\t\t\t\t\t\t\t\twidth=\"11\"\n\t\t\t\t\t\t\t\theight=\"10\"\n\t\t\t\t\t\t\t\trx=\"2\"\n\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\tstroke-width=\"1.5\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\td=\"M5.2 6h5.6M5.2 8.4h5.6M5.2 10.6h3.2\"\n\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\tstroke-width=\"1.5\"\n\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t</svg>\n\t\t\t\t\t<span class=\"min-w-0 truncate\">{door.label}</span>\n\t\t\t\t</button>\n\t\t\t{/each}\n\t\t\t<!-- The other door: a session she leads with her voice. It opens the\n\t\t\t\t session side, where a scenario is picked — the same place as Session on\n\t\t\t\t the switch above the composer, from wherever the sidebar is. -->\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tonclick={() => {\n\t\t\t\t\tsetSessionSurface('session', 'sidebar');\n\t\t\t\t\tif (window.innerWidth < 1024) setNavOpen(false);\n\t\t\t\t}}\n\t\t\t\taria-current={sessionState.surface === 'session' ? 'page' : undefined}\n\t\t\t\tclass=\"flex min-h-10 w-full cursor-pointer items-center gap-3 rounded-xl px-3 text-left text-sm transition-colors {sessionState.surface ===\n\t\t\t\t'session'\n\t\t\t\t\t? 'bg-[var(--st-surface)] font-semibold text-[var(--st-text)]'\n\t\t\t\t\t: 'text-[var(--st-muted)] hover:bg-[var(--st-surface)]'}\"\n\t\t\t\ttitle=\"she leads, you listen\"\n\t\t\t>\n\t\t\t\t<!-- Her voice, as a level: three bars, the middle one speaking. -->\n\t\t\t\t<svg\n\t\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\t\tclass=\"size-4 shrink-0 {sessionState.surface === 'session'\n\t\t\t\t\t\t? ''\n\t\t\t\t\t\t: 'text-[var(--st-faint)]'}\"\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t>\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M4.5 6v4M8 3.5v9M11.5 6v4\"\n\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\tstroke-width=\"1.5\"\n\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t/>\n\t\t\t\t</svg>\n\t\t\t\t<span class=\"min-w-0 truncate\">New session</span>\n\t\t\t</button>\n\t\t</div>\n\n\t\t<nav class=\"min-h-0 flex-1 overflow-y-auto px-3 pb-2\">\n\t\t\t<SessionList signedIn={!guest} onOpen={() => window.innerWidth < 1024 && setNavOpen(false)} />\n\t\t\t{#if historyDays.length}\n\t\t\t\t<!-- The day is a heading, not a word repeated on every row. Twelve runs\n\t\t\t\t\t in one afternoon printed \"today\" twelve times and said nothing. -->\n\t\t\t\t{#each historyDays as day (day.label)}\n\t\t\t\t\t<p\n\t\t\t\t\t\tclass=\"px-3 pt-4 pb-1.5 font-mono text-[0.7rem] font-medium tracking-[0.13em] text-[var(--st-faint)] uppercase\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{day.label}\n\t\t\t\t\t</p>\n\t\t\t\t\t{#each day.items as p (p.slug)}\n\t\t\t\t\t\t<!-- `brief` is null for every one-clip run, so this was dead-false on\n\t\t\t\t\t\t\t all of them and reopening one looked like nothing had happened.\n\t\t\t\t\t\t\t runSlug is the same value the row is filed under. -->\n\t\t\t\t\t\t{@const current = (runSlug || brief?.slug) === p.slug}\n\t\t\t\t\t\t{@const kind = runKind(p)}\n\t\t\t\t\t\t<!-- Only ever the run this tab is watching. Whether some other run is\n\t\t\t\t\t\t\t working is not knowable from here without polling sixty\n\t\t\t\t\t\t\t workspaces, and a dot that is sometimes right is worse than no\n\t\t\t\t\t\t\t dot at all.\n\n\t\t\t\t\t\t\t A sheet counts as work even though nothing here polls for it: the\n\t\t\t\t\t\t\t turnaround behind an upload runs server-side, so the render poller\n\t\t\t\t\t\t\t is idle for its whole six minutes and the row sat dark while a GPU\n\t\t\t\t\t\t\t was busy. -->\n\t\t\t\t\t\t{@const working = current && !staleRun && pollingActive}\n\t\t\t\t\t\t<!-- A character being drawn is knowable for every row, not just this\n\t\t\t\t\t\t\t one: the sheet carries the session it was started in, so the\n\t\t\t\t\t\t\t sidebar can say which conversation the GPU is busy for even while\n\t\t\t\t\t\t\t you are reading a different one. Green rather than the accent,\n\t\t\t\t\t\t\t because it is the only mark here that survives leaving the room —\n\t\t\t\t\t\t\t and it ends on a state worth walking back for. -->\n\t\t\t\t\t\t{@const drawing = sessionsDrawing.has(p.slug)}\n\t\t\t\t\t\t{@const finished = sessionsDone.has(p.slug)}\n\t\t\t\t\t\t<div class=\"group relative\">\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonclick={() => reopen(p)}\n\t\t\t\t\t\t\t\tclass=\"w-full cursor-pointer rounded-xl py-2 pr-9 pl-3 text-left transition-colors {current\n\t\t\t\t\t\t\t\t\t? 'bg-[var(--st-surface)]'\n\t\t\t\t\t\t\t\t\t: 'hover:bg-[var(--st-surface)]'}\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span class=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t{#if drawing}\n\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\tclass=\"beacon size-1.5 shrink-0 rounded-full bg-[var(--st-green)]\"\n\t\t\t\t\t\t\t\t\t\t\ttitle=\"a character is being drawn here\"\n\t\t\t\t\t\t\t\t\t\t></span>\n\t\t\t\t\t\t\t\t\t\t<span class=\"sr-only\">a character is being drawn here</span>\n\t\t\t\t\t\t\t\t\t{:else if finished}\n\t\t\t\t\t\t\t\t\t\t<!-- Steady, not pulsing. The pulse means \"wait\"; this means\n\t\t\t\t\t\t\t\t\t\t\t \"it is here\" — the same colour arriving at rest, which is\n\t\t\t\t\t\t\t\t\t\t\t the whole of what changed. -->\n\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\tclass=\"size-1.5 shrink-0 rounded-full bg-[var(--st-green)]\"\n\t\t\t\t\t\t\t\t\t\t\ttitle=\"a character is ready here\"\n\t\t\t\t\t\t\t\t\t\t></span>\n\t\t\t\t\t\t\t\t\t\t<span class=\"sr-only\">a character is ready here</span>\n\t\t\t\t\t\t\t\t\t{:else if working}\n\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\tclass=\"beacon size-1.5 shrink-0 rounded-full bg-[var(--st-accent)]\"\n\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t></span>\n\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t<!-- The full title on hover, because the row truncates it. The\n\t\t\t\t\t\t\t\t\t\t native tooltip rather than a built one: it appears only while\n\t\t\t\t\t\t\t\t\t\t the pointer is on the row, it cannot cover the row below it,\n\t\t\t\t\t\t\t\t\t\t and it costs nothing. -->\n\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\ttitle={p.title}\n\t\t\t\t\t\t\t\t\t\tclass=\"min-w-0 flex-1 truncate text-sm text-[var(--st-text)]\">{p.title}</span\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<!-- Only what is true and only where it adds something. A clip's\n\t\t\t\t\t\t\t\t\t title is the prompt, so a second line under it would be filler;\n\t\t\t\t\t\t\t\t\t a film has a scene count, which is the one number that says how\n\t\t\t\t\t\t\t\t\t big the thing is. -->\n\t\t\t\t\t\t\t\t<!-- No clock here.\n\t\t\t\t\t\t\t\t\t It used to show the elapsed time, on the argument that a moving\n\t\t\t\t\t\t\t\t\t number proves the page is alive where a static label does not.\n\t\t\t\t\t\t\t\t\t That was written when a row was one render. A row is a session\n\t\t\t\t\t\t\t\t\t now, and the number became \"how long you have been sitting here\"\n\t\t\t\t\t\t\t\t\t — which nobody asked to be told, and which reads as pressure\n\t\t\t\t\t\t\t\t\t rather than progress. The pulsing dot says the same thing\n\t\t\t\t\t\t\t\t\t without the running total, and the clip card still carries the\n\t\t\t\t\t\t\t\t\t render's own timing where it means something. -->\n\t\t\t\t\t\t\t\t{#if drawing}\n\t\t\t\t\t\t\t\t\t{@const sh = sheets.find(\n\t\t\t\t\t\t\t\t\t\t(x) => x.sessionSlug === p.slug && x.sheet?.state === 'rendering'\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t<!-- Not the name: the row above it is the name, and a session is\n\t\t\t\t\t\t\t\t\t\t usually one subject. What the second line is for is the part the\n\t\t\t\t\t\t\t\t\t\t title cannot say — what is being made and how long is left. -->\n\t\t\t\t\t\t\t\t\t<span class=\"mt-0.5 block text-xs text-[var(--st-muted)]\">\n\t\t\t\t\t\t\t\t\t\tDrawing the six views · {turnStatus(sh)}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t{:else if working && railRunning}\n\t\t\t\t\t\t\t\t\t<span class=\"mt-0.5 block text-xs text-[var(--st-muted)]\">\n\t\t\t\t\t\t\t\t\t\t{friendly(railRunning.label)}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t{:else if kind === 'film'}\n\t\t\t\t\t\t\t\t\t<span class=\"mt-0.5 block text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t{p.sceneCount} scene{p.sceneCount === 1 ? '' : 's'}{p.renderWs\n\t\t\t\t\t\t\t\t\t\t\t? ' · shot'\n\t\t\t\t\t\t\t\t\t\t\t: ' · planning'}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\taria-label=\"remove {p.title} from the list\"\n\t\t\t\t\t\t\t\tonclick={(e) => dropFromHistory(p, e)}\n\t\t\t\t\t\t\t\tclass=\"absolute top-1/2 right-1 flex size-7 -translate-y-1/2 cursor-pointer items-center justify-center rounded-lg text-[var(--st-faint)] opacity-0 transition-opacity group-focus-within:opacity-100 group-hover:opacity-100 hover:bg-[var(--st-surface-2)] hover:text-[var(--st-text)] focus-visible:opacity-100\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t×\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t{/each}\n\t\t\t\t{/each}\n\t\t\t{:else}\n\t\t\t\t<p class=\"px-3 pt-4 text-xs leading-relaxed text-[var(--st-faint)]\">\n\t\t\t\t\tFilms you start show up here, so you can come back to one after closing the tab.\n\t\t\t\t</p>\n\t\t\t{/if}\n\t\t</nav>\n\n\t\t<!-- Cast & sets used to sit here, which put a management panel — rename,\n\t\t     retry, delete — inside a list whose job is navigation. It moved to\n\t\t     /studio/admin/cast, beside the workflows and the skills, which is\n\t\t     where the local library already lives. Picking a face for the next\n\t\t     clip never happened here anyway; that is the composer's own picker.\n\n\t\t     Tuning stays at the bottom because it is a settings surface rather\n\t\t     than a destination — the same place every app of this shape puts one. -->\n\t\t<!-- The shelf: what has actually been finished.\n\t\t\t Everything above this line is work in progress — a production, a chain,\n\t\t\t a take. A film is the thing that leaves, so it lives at the foot of the\n\t\t\t rail where it is always reachable and never in the way, and the last\n\t\t\t tile opens all of them. -->\n\t\t{#if films.length}\n\t\t\t<div class=\"border-t border-[var(--st-line)] px-3 pt-2.5 pb-1\">\n\t\t\t\t<div class=\"flex items-center gap-1.5\">\n\t\t\t\t\t{#each films.slice(0, 5) as f (f.workspace + f.artifact + f.file)}\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\ttitle=\"{f.parts} shots · {Math.round(f.seconds)}s\"\n\t\t\t\t\t\t\tonclick={() => (filmPopup = f)}\n\t\t\t\t\t\t\tclass=\"size-9 shrink-0 cursor-pointer overflow-hidden rounded-lg bg-[var(--st-surface)] ring-1 ring-transparent transition hover:ring-[var(--st-line-control)]\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<!-- svelte-ignore a11y_media_has_caption -->\n\t\t\t\t\t\t\t<video\n\t\t\t\t\t\t\t\tsrc={still(fileUrl(f.workspace, f.artifact, f.file))}\n\t\t\t\t\t\t\t\tmuted\n\t\t\t\t\t\t\t\tplaysinline\n\t\t\t\t\t\t\t\tpreload=\"metadata\"\n\t\t\t\t\t\t\t\tclass=\"h-full w-full object-cover\"\n\t\t\t\t\t\t\t></video>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t{/each}\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\taria-label=\"all media\"\n\t\t\t\t\t\ttitle=\"all media\"\n\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\tmediaOpen = true;\n\t\t\t\t\t\t\tvoid loadPool();\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclass=\"flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-lg bg-[var(--st-surface)] text-[var(--st-muted)] transition-colors hover:bg-[var(--st-surface-2)] hover:text-[var(--st-text)]\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<svg viewBox=\"0 0 16 16\" class=\"size-4\" fill=\"none\" aria-hidden=\"true\">\n\t\t\t\t\t\t\t<rect\n\t\t\t\t\t\t\t\tx=\"2.5\"\n\t\t\t\t\t\t\t\ty=\"2.5\"\n\t\t\t\t\t\t\t\twidth=\"4.6\"\n\t\t\t\t\t\t\t\theight=\"4.6\"\n\t\t\t\t\t\t\t\trx=\"1.2\"\n\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\tstroke-width=\"1.5\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<rect\n\t\t\t\t\t\t\t\tx=\"8.9\"\n\t\t\t\t\t\t\t\ty=\"2.5\"\n\t\t\t\t\t\t\t\twidth=\"4.6\"\n\t\t\t\t\t\t\t\theight=\"4.6\"\n\t\t\t\t\t\t\t\trx=\"1.2\"\n\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\tstroke-width=\"1.5\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<rect\n\t\t\t\t\t\t\t\tx=\"2.5\"\n\t\t\t\t\t\t\t\ty=\"8.9\"\n\t\t\t\t\t\t\t\twidth=\"4.6\"\n\t\t\t\t\t\t\t\theight=\"4.6\"\n\t\t\t\t\t\t\t\trx=\"1.2\"\n\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\tstroke-width=\"1.5\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<rect\n\t\t\t\t\t\t\t\tx=\"8.9\"\n\t\t\t\t\t\t\t\ty=\"8.9\"\n\t\t\t\t\t\t\t\twidth=\"4.6\"\n\t\t\t\t\t\t\t\theight=\"4.6\"\n\t\t\t\t\t\t\t\trx=\"1.2\"\n\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\tstroke-width=\"1.5\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</svg>\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t{/if}\n\n\t\t<!-- The rail ends with the operator's own work.\n\t\t     It carried a link to /studio/admin under it — the prompt and model\n\t\t     registry, which is a workbench for whoever tunes the writers rather than\n\t\t     somewhere a person making a clip has reason to go. In the hosted app it\n\t\t     was worse than clutter: that route is not part of what ships there, so\n\t\t     the one control in the rail that was not the operator's own work was\n\t\t     also the one that led nowhere. The panel is reached by its own address\n\t\t     wherever it is deployed. -->\n\t</aside>\n\n\t<!-- The rail's own place, held while it is away. See the counterweight after\n\t\t </main> for why both sides are reserved from xl up. -->\n\t{#if !sidebarOpen}\n\t\t<div class=\"hidden w-64 shrink-0 xl:block\" aria-hidden=\"true\"></div>\n\t{/if}\n\n\t<main class=\"relative flex min-w-0 flex-1 flex-col overflow-hidden\">\n\t\t<!-- The same room on this side, so the transcript starts below the header\n\t\t\t rather than under it. -->\n\t\t<div class=\"h-12 shrink-0\" aria-hidden=\"true\"></div>\n\t\t<!-- Session mode lays its own surface over this column, under the header;\n\t\t\t the clip stage stays where it is beneath it, untouched. -->\n\t\t{#if sessionState.surface === 'session'}\n\t\t\t<div class=\"absolute inset-x-0 top-12 bottom-0 z-[6]\">\n\t\t\t\t<!-- Loaded when the Session door is first opened, not with the page. -->\n\t\t\t\t{#await import('$lib/studio/session/SessionSurface.svelte') then surface}\n\t\t\t\t\t<surface.default />\n\t\t\t\t{/await}\n\t\t\t</div>\n\t\t{/if}\n\n\t\t<!-- 66rem only when the task rail is beside it and needs the room. On its\n\t\t\t own a reading column that wide is not a measure, it is a stretch: the\n\t\t\t composer became a thousand pixels of single line and the eye had to\n\t\t\t travel the width of the screen to find the send button. -->\n\t\t<div\n\t\t\tclass=\"mx-auto flex min-h-0 w-full flex-1 flex-col px-5 pt-1 pb-[env(safe-area-inset-bottom,0px)] {STAGE_UI\n\t\t\t\t? 'max-w-[104rem]'\n\t\t\t\t: brief\n\t\t\t\t\t? 'max-w-[66rem]'\n\t\t\t\t\t: 'max-w-[48rem]'}\"\n\t\t>\n\t\t\t<!-- Two columns only when there is a second column to put something in.\n\t\t\t The task rail below is behind an if, but the grid reserved its 16rem\n\t\t\t and the 40px gap unconditionally — so with no production running the\n\t\t\t page held 296px of nothing on the right and pushed the reading column\n\t\t\t 148px left of centre. That was the centring that would not come right,\n\t\t\t and it was never the sidebar. -->\n\t\t\t<div\n\t\t\t\tclass=\"flex min-h-0 flex-1 flex-col {brief\n\t\t\t\t\t? 'lg:grid lg:grid-cols-[minmax(0,1fr)_16rem] lg:gap-10'\n\t\t\t\t\t: ''}\"\n\t\t\t>\n\t\t\t\t<!-- ── chat column ─────────────────────────────────────────────── -->\n\t\t\t\t<!-- min-h-0 is load-bearing: without it a flex child refuses to shrink\n\t\t\t\t below its content and the inner overflow-y-auto never engages. -->\n\t\t\t\t<div class=\"flex min-h-0 min-w-0 flex-1 flex-col\">\n\t\t\t\t\t<!-- Mobile: the rail collapses into a slim strip above the chat. -->\n\t\t\t\t\t{#if brief}\n\t\t\t\t\t\t<div class=\"mb-5 lg:hidden\">\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclass=\"flex w-full cursor-pointer items-center justify-between gap-3 rounded-2xl bg-[var(--st-surface)] px-4 py-3 text-left\"\n\t\t\t\t\t\t\t\tonclick={() => (railOpen = !railOpen)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span class=\"min-w-0 truncate text-xs text-[var(--st-muted)]\">{railSummary}</span>\n\t\t\t\t\t\t\t\t<span class=\"shrink-0 text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t{railOpen ? 'hide' : 'progress'}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t{#if railOpen}\n\t\t\t\t\t\t\t\t<div class=\"enter mt-2 rounded-2xl bg-[var(--st-surface)] p-4\">\n\t\t\t\t\t\t\t\t\t{@render railList()}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t{/if}\n\n\t\t\t\t\t<!-- ── the transcript — the only scrolling region on the page ── -->\n\t\t\t\t\t<!-- Empty state centres its own content: a welcome line pinned to the\n\t\t\t\t\t top of a tall blank column reads as a page that failed to load.\n\t\t\t\t\t Once the transcript has real messages it goes back to flowing from\n\t\t\t\t\t the top, which is what a conversation wants. -->\n\t\t\t\t\t<!-- The stage is the CLIP surface, and only that.\n\t\t\t\t\t It replaced the transcript in every mode at first, which quietly\n\t\t\t\t\t swallowed the full-production flow: the plan, the screenplay, the\n\t\t\t\t\t scene board and the crew’s replies are all cards, and a full\n\t\t\t\t\t production is a conversation with them. What was left was a loader\n\t\t\t\t\t counting to 97% over a planning run that renders no clip at all,\n\t\t\t\t\t with the work sitting in cards nobody could see. -->\n\t\t\t\t\t{#if STAGE_UI && mode === 'simple'}\n\t\t\t\t\t\t<!-- ── the stage ──────────────────────────────────────────────────\n\t\t\t\t\t A clip is made in one place and watched in the same place. The\n\t\t\t\t\t transcript below is the same engine with its cards drawn; this is\n\t\t\t\t\t what the operator asked to see instead — no read-back, no brief, no\n\t\t\t\t\t announcement of a launch, and nothing to scroll. -->\n\t\t\t\t\t\t<!-- The strip floats rather than sits in the row.\n\t\t\t\t\t In the row it took its width off the stage, so the clip centred in\n\t\t\t\t\t what was left while the composer below centred in the whole column —\n\t\t\t\t\t same width, forty-nine pixels apart, which reads as a mistake because\n\t\t\t\t\t it is one. Floated, the clip has the column to itself and the two line\n\t\t\t\t\t up; the strip lives in the margin the picture leaves beside it. -->\n\t\t\t\t\t\t<!-- A column on a phone, a row from lg up. The strip is absolute on the\n\t\t\t\t\t\t\t wide screen — it lives in the margin the 16:9 picture leaves beside\n\t\t\t\t\t\t\t it — and there is no such margin on a phone, so below lg it comes out\n\t\t\t\t\t\t\t of the corner and lies down under the picture instead. -->\n\t\t\t\t\t\t<div class=\"relative flex min-h-0 flex-1 flex-col lg:flex-row\">\n\t\t\t\t\t\t\t<!-- the chain, newest first. Four, because a strip that grows past\n\t\t\t\t\t\t the stage's own height starts scrolling, which is the thing this\n\t\t\t\t\t\t surface exists not to do. -->\n\t\t\t\t\t\t\t<!-- The chain lives in the right-hand margin, and takes nothing from\n\t\t\t\t\t\t\t\t the picture. It used to sit on the left behind six rems of padding,\n\t\t\t\t\t\t\t\t which is width off the one thing this surface exists to show — and\n\t\t\t\t\t\t\t\t a clip is 16:9 inside a column that is wider than that, so the\n\t\t\t\t\t\t\t\t margin it leaves on either side was already there and already\n\t\t\t\t\t\t\t\t empty. Right rather than left because the eye starts at the\n\t\t\t\t\t\t\t\t picture; the chain is where you go after it, not before. -->\n\t\t\t\t\t\t\t{#if showStrip}\n\t\t\t\t\t\t\t\t<!-- Lying down, the chain reads the way a filmstrip reads and gives the\n\t\t\t\t\t\t\t\t\t picture back the width it was taking. Standing in the right margin it\n\t\t\t\t\t\t\t\t\t cost a phone 72 of its 390 points — of the one surface this view\n\t\t\t\t\t\t\t\t\t exists to show — and a column of four thumbnails on a narrow screen\n\t\t\t\t\t\t\t\t\t is the worst shape a filmstrip can have. Newest first either way, so\n\t\t\t\t\t\t\t\t\t the two screens agree about which end is the front. -->\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tclass=\"railstrip z-10 order-last flex w-full shrink-0 gap-2 overflow-x-auto pt-3 lg:absolute lg:top-0 lg:right-0 lg:w-[5.4rem] lg:flex-col lg:overflow-visible lg:pt-0\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<!-- The one being made takes its place in the strip the moment it is\n\t\t\t\t\t\t\t\t asked for, at the top where it will land. The chain is what this\n\t\t\t\t\t\t\t\t column shows and the next link is already real — it is being paid\n\t\t\t\t\t\t\t\t for — so leaving the row out until it arrives makes the strip\n\t\t\t\t\t\t\t\t disagree with the stage beside it. -->\n\t\t\t\t\t\t\t\t\t<!-- `stagePending` as well as the phase: once one take of a batch has\n\t\t\t\t\t\t\t\t\t\t landed the stage moves off 'working' and onto the batch itself, but\n\t\t\t\t\t\t\t\t\t\t the ones still out are still out, and their marks belong here until\n\t\t\t\t\t\t\t\t\t\t they arrive. -->\n\t\t\t\t\t\t\t\t\t{#if stagePhase === 'working' || stagePending}\n\t\t\t\t\t\t\t\t\t\t<!-- The ring stays on the first alone. It means \"this is the one the\n\t\t\t\t\t\t\t\t\t\t\t stage is about to show\", and four of them would mean nothing —\n\t\t\t\t\t\t\t\t\t\t\t the others are coming, not arriving. With a single clip this is\n\t\t\t\t\t\t\t\t\t\t\t the same one mark it has always drawn. -->\n\t\t\t\t\t\t\t\t\t\t{#each { length: Math.max(1, stagePending) } as _, i (i)}\n\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex aspect-video w-[4.5rem] shrink-0 items-center justify-center rounded-lg bg-[var(--st-surface)] lg:w-full {i ===\n\t\t\t\t\t\t\t\t\t\t\t\t0\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'ring-2 ring-[var(--st-text)]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: ''}\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"spin size-4 rounded-full border-2 border-[var(--st-surface-2)] border-t-[var(--st-accent)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t></span>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t{#each stageThumbs as t (t.key)}\n\t\t\t\t\t\t\t\t\t\t{#if t.url}\n\t\t\t\t\t\t\t\t\t\t\t<!-- Nothing in the strip is lit while a new one is being made:\n\t\t\t\t\t\t\t\t\t\t the pending row is where the attention belongs. -->\n\t\t\t\t\t\t\t\t\t\t\t{@const here =\n\t\t\t\t\t\t\t\t\t\t\t\tstagePhase !== 'working' &&\n\t\t\t\t\t\t\t\t\t\t\t\tt.workspace === stageShownWs &&\n\t\t\t\t\t\t\t\t\t\t\t\t(!!stageSel || stageNewest?.artifact?.key !== 'film')}\n\t\t\t\t\t\t\t\t\t\t\t<!-- The one on the stage is lit and the rest are stepped back, so\n\t\t\t\t\t\t\t\t\t\t where you are in the chain is legible without reading\n\t\t\t\t\t\t\t\t\t\t anything. -->\n\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\tdraggable={!!t.artifact && !!t.file}\n\t\t\t\t\t\t\t\t\t\t\t\tondragstart={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\te.dataTransfer?.setData(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tCLIP_DRAG,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tworkspace: t.workspace,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tartifact: t.artifact,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfile: t.file\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (e.dataTransfer) e.dataTransfer.effectAllowed = 'copy';\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t// Picking a clip is leaving the error behind. Without this the\n\t\t\t\t\t\t\t\t\t\t\t\t\t// stage stayed on it: an export that failed once took over the\n\t\t\t\t\t\t\t\t\t\t\t\t\t// surface, survived a reload with the transcript, and every\n\t\t\t\t\t\t\t\t\t\t\t\t\t// thumbnail answered with the same dead message.\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (stageError) chat = chat.filter((c) => c !== stageError);\n\t\t\t\t\t\t\t\t\t\t\t\t\tstageSel =\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tt.workspace === (stageNewest?.artifact?.workspace ?? '')\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? ''\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: t.workspace;\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"relative aspect-video w-[4.5rem] shrink-0 cursor-pointer overflow-hidden rounded-lg bg-black transition-opacity lg:w-full {here\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'opacity-100 ring-2 ring-[var(--st-text)]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: 'opacity-45 hover:opacity-75'}\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<!-- svelte-ignore a11y_media_has_caption -->\n\t\t\t\t\t\t\t\t\t\t\t\t<video\n\t\t\t\t\t\t\t\t\t\t\t\t\tsrc={still(t.url)}\n\t\t\t\t\t\t\t\t\t\t\t\t\tmuted\n\t\t\t\t\t\t\t\t\t\t\t\t\tplaysinline\n\t\t\t\t\t\t\t\t\t\t\t\t\tpreload=\"metadata\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tonloadedmetadata={(e) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t(clipSecs = { ...clipSecs, [t.key]: e.currentTarget.duration })}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"h-full w-full object-cover\"\n\t\t\t\t\t\t\t\t\t\t\t\t></video>\n\t\t\t\t\t\t\t\t\t\t\t\t{#if clipSecs[t.key]}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"pointer-events-none absolute right-1 bottom-1 rounded bg-black/65 px-1 font-mono text-[0.6rem] leading-4 text-white\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{clipClock(clipSecs[t.key])}</span\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t\t<!-- Centred while it fits, scrolled from the top when it does not.\n\t\t\t\t\t\t\t\t `justify-center` alone does both jobs badly: a flex column whose\n\t\t\t\t\t\t\t\t content is taller than the box overflows in BOTH directions, and\n\t\t\t\t\t\t\t\t the part above the start edge cannot be scrolled to at all — the\n\t\t\t\t\t\t\t\t front page's own headline and its films sat up there,\n\t\t\t\t\t\t\t\t unreachable, the moment the shelf gave the column something to\n\t\t\t\t\t\t\t\t be tall about. `auto` margins centre the same way and give way\n\t\t\t\t\t\t\t\t when there is nothing left to give. -->\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclass=\"stagescroll flex min-h-0 flex-1 flex-col items-center gap-2 overflow-y-auto [&>*]:shrink-0 {stagePhase ===\n\t\t\t\t\t\t\t\t\t'empty' && shelf.length\n\t\t\t\t\t\t\t\t\t? '-mb-32 pb-24'\n\t\t\t\t\t\t\t\t\t: ''}\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<!-- Centred, except while it is a conversation. An empty stage and a\n\t\t\t\t\t\t\t\t\t render in progress are single objects and belong in the middle of\n\t\t\t\t\t\t\t\t\t the frame; a thread is a column that has to reach the bottom edge,\n\t\t\t\t\t\t\t\t\t and it cannot if its own wrapper has already shrunk to fit it. -->\n\t\t\t\t\t\t\t\t<!-- A clip on the stage takes the height it is given, too — not only a\n\t\t\t\t\t\t\t\t\t conversation. `my-auto` sized this wrapper to its content, so the\n\t\t\t\t\t\t\t\t\t video's max-h-full measured against nothing and a 9:16 clip was\n\t\t\t\t\t\t\t\t\t drawn at its native 1024px: on a 1470x663 laptop window it ran from\n\t\t\t\t\t\t\t\t\t -298 to 726, face cut off, scrolled inside the stage. Bounded here,\n\t\t\t\t\t\t\t\t\t every max-h-full below resolves to the space above the composer. -->\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tclass=\"flex w-full flex-col items-center gap-2 {stagePhase === 'round' ||\n\t\t\t\t\t\t\t\t\tstagePhase === 'ready' ||\n\t\t\t\t\t\t\t\t\tstagePhase === 'working'\n\t\t\t\t\t\t\t\t\t\t? 'h-full min-h-0'\n\t\t\t\t\t\t\t\t\t\t: 'my-auto'}\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{#if stagePhase === 'working'}\n\t\t\t\t\t\t\t\t\t\t<!-- The percentage is honest about what it is: elapsed against the\n\t\t\t\t\t\t\t\t median of this machine's own finished runs. Half of all runs are\n\t\t\t\t\t\t\t\t past a median, so it caps short of full and says so in words\n\t\t\t\t\t\t\t\t rather than sitting at 100 while nothing happens. -->\n\t\t\t\t\t\t\t\t\t\t<!-- The size the clip will be, so nothing jumps when it arrives:\n\t\t\t\t\t\t\t\t height-bound and 16:9, the same way the video is measured. -->\n\t\t\t\t\t\t\t\t\t\t<!-- The size the clip will be, so nothing jumps when it arrives. -->\n\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\tclass=\"relative flex max-h-full max-w-full items-center justify-center overflow-hidden rounded-2xl bg-[var(--st-surface)] {composerShape.portrait\n\t\t\t\t\t\t\t\t\t\t\t\t? 'aspect-[9/16] h-full w-auto'\n\t\t\t\t\t\t\t\t\t\t\t\t: 'aspect-video w-full lg:h-full lg:w-auto'}\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{#if stageWaitBlurUrl}\n\t\t\t\t\t\t\t\t\t\t\t\t<!-- svelte-ignore a11y_media_has_caption -->\n\t\t\t\t\t\t\t\t\t\t\t\t<video\n\t\t\t\t\t\t\t\t\t\t\t\t\tsrc={stageWaitBlurUrl}\n\t\t\t\t\t\t\t\t\t\t\t\t\tmuted\n\t\t\t\t\t\t\t\t\t\t\t\t\tautoplay\n\t\t\t\t\t\t\t\t\t\t\t\t\tloop\n\t\t\t\t\t\t\t\t\t\t\t\t\tplaysinline\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"absolute inset-0 h-full w-full scale-110 object-cover blur-2xl brightness-[0.55]\"\n\t\t\t\t\t\t\t\t\t\t\t\t></video>\n\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"stage-dots absolute inset-0\"></div>\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t<!-- One slow pass of light across the surface, and nothing else.\n\t\t\t\t\t\t\t\t\t A still rectangle with a number on it cannot say whether it is\n\t\t\t\t\t\t\t\t\t working or hung, and the number is the wrong instrument for\n\t\t\t\t\t\t\t\t\t that: it is an estimate against a median, so it keeps moving\n\t\t\t\t\t\t\t\t\t whether or not anything is happening. The sweep is the part\n\t\t\t\t\t\t\t\t\t that means \"running\" — long, low-contrast, monochrome, closer\n\t\t\t\t\t\t\t\t\t to breathing than to a spinner. It passes about every four\n\t\t\t\t\t\t\t\t\t seconds, which is slow enough not to be watched and often\n\t\t\t\t\t\t\t\t\t enough to be believed. -->\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"stage-sweep pointer-events-none absolute inset-0\"></div>\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"relative flex flex-col items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex items-center gap-3 rounded-full bg-black/45 px-4 py-2 backdrop-blur\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"beacon size-1.5 shrink-0 rounded-full bg-[var(--st-accent)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t></span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"font-display text-sm font-semibold text-white\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>Generating · {clock(Math.round(stageElapsedMs / 1000))}</span\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t<!-- The clock, not a percentage.\n\t\t\t\t\t\t\t\t\t\t\t\t A percentage has to be computed from something, and the only\n\t\t\t\t\t\t\t\t\t\t\t\t thing here to compute it from is elapsed time against a median\n\t\t\t\t\t\t\t\t\t\t\t\t — so it was the clock all along, dressed up as progress and\n\t\t\t\t\t\t\t\t\t\t\t\t capped at 97 so it would not lie outright. It still lied: it\n\t\t\t\t\t\t\t\t\t\t\t\t moved smoothly while nothing happened, and a five-second clip\n\t\t\t\t\t\t\t\t\t\t\t\t that takes four minutes read as almost finished for most of\n\t\t\t\t\t\t\t\t\t\t\t\t them.\n\t\t\t\t\t\t\t\t\t\t\t\t The elapsed time is the true thing, and the estimate beside it\n\t\t\t\t\t\t\t\t\t\t\t\t is what makes it mean anything — see pressToClipMs. -->\n\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"text-xs text-white/55\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{stageEtaMs\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? `usually about ${Math.max(1, Math.round(stageEtaMs / 60000))} minute${Math.round(stageEtaMs / 60000) === 1 ? '' : 's'}`\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'a few minutes'}\n\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t<!-- The way out, where the waiting is.\n\t\t\t\t\t\t\t\t\t\t\t     A render is billed per second on somebody's GPU, and until\n\t\t\t\t\t\t\t\t\t\t\t     now a clip run had no stop at all — the studio's own endpoint\n\t\t\t\t\t\t\t\t\t\t\t     had to be called by hand, and the page went on counting over\n\t\t\t\t\t\t\t\t\t\t\t     a run that had already been torn down. Quiet, and two steps,\n\t\t\t\t\t\t\t\t\t\t\t     because it cannot be undone: a workspace id opens once. -->\n\t\t\t\t\t\t\t\t\t\t\t\t{#if stopArmed && renderWs && !planningWs && !genSlug}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- Already open, so there is nothing a stop could do: the GPU\n\t\t\t\t\t\t\t\t\t\t\t\t\t cannot be told to stop, and tearing down only throws away a clip\n\t\t\t\t\t\t\t\t\t\t\t\t\t that is paid for. Said before anything is pressed, and there is\n\t\t\t\t\t\t\t\t\t\t\t\t\t no stop button here to press. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"flex max-w-xs flex-col items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"text-xs leading-snug text-white/75\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tIt has already started, and a started render cannot be called back. It\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tis paid for and will arrive here.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => (stopArmed = false)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-full bg-white/90 px-3 py-1 text-xs font-semibold text-black\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>ok</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t{:else if stopArmed}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"flex max-w-xs flex-col items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- The policy, before the press. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"text-xs leading-snug text-white/75\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tIf it has not started yet, nothing is charged. If it just has, it\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcannot be called back and will still arrive.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"flex items-center gap-1.5\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={stopping}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={stopRun}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-full bg-white/90 px-3 py-1 text-xs font-semibold text-black disabled:opacity-60\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{stopping ? 'stopping…' : 'yes, stop it'}</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => (stopArmed = false)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-full px-3 py-1 text-xs text-white/70 hover:text-white\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>keep going</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => (stopArmed = true)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-full px-3 py-1 text-xs text-white/45 transition-colors hover:text-white/80\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>stop</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t{:else if stagePhase === 'takes' && stageTakes?.takes}\n\t\t\t\t\t\t\t\t\t\t{@const t = stageTakes.takes}\n\t\t\t\t\t\t\t\t\t\t{@const done = t.runs.filter((r) => r.state === 'ready' && r.clip)}\n\t\t\t\t\t\t\t\t\t\t{@const live = t.runs.filter((r) => r.state === 'rendering').length}\n\t\t\t\t\t\t\t\t\t\t{@const gone = t.runs.filter((r) => r.state === 'failed')}\n\t\t\t\t\t\t\t\t\t\t<!-- The batch, on the stage, playing. The same tiles the transcript\n\t\t\t\t\t\t\t\t\t\t draws — one snippet, so a take looks and behaves the same wherever\n\t\t\t\t\t\t\t\t\t\t it is met — and pressing one opens the viewer that was already\n\t\t\t\t\t\t\t\t\t\t built for choosing between them. Nothing new to learn.\n\t\t\t\t\t\t\t\t\t\t The ones still out are not drawn as holes here: the strip beside\n\t\t\t\t\t\t\t\t\t\t the picture is already marking them, one mark each. -->\n\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\tclass=\"flex min-h-0 w-full flex-1 flex-col items-center justify-center gap-3\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"st-takes w-full max-w-[62rem] {showStrip ? 'lg:pr-[6.5rem]' : ''}\"\n\t\t\t\t\t\t\t\t\t\t\t\tdata-n={done.length}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{#each done as run (run.slug)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{@render takeTile(stageTakes, run)}\n\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t<p class=\"flex items-center gap-2.5 text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t{#if live}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"beacon size-1.5 shrink-0 rounded-full bg-[var(--st-green)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t></span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"tabular-nums\">{done.length} of {t.runs.length} landed</span>\n\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{done.length}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{done.length === 1 ? 'clip' : 'clips'} of this shot. Press one to keep it.</span\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t{#each gone as g (g.slug)}\n\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\tClip {g.index} — {g.error || 'no reason given'}\n\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t{:else if stagePhase === 'round' && stageRound?.confirm}\n\t\t\t\t\t\t\t\t\t\t<!-- The round, drawn as what it is: an exchange.\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t     The stage used to compose its own version of this — the same sentences,\n\t\t\t\t\t\t\t\t\t     centred inside a 16:9 box the size of the clip that did not exist yet,\n\t\t\t\t\t\t\t\t\t     with the button under them. It read as an alert rather than an answer.\n\t\t\t\t\t\t\t\t\t     What the operator asked for is the transcript's own drawing: their\n\t\t\t\t\t\t\t\t\t     line, then the studio's, flowing from the top of a reading column.\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t     Every round of it now, not only the newest. A reply that erases what\n\t\t\t\t\t\t\t\t\t     it is replying to is not a conversation, and refining a shot is\n\t\t\t\t\t\t\t\t\t     nothing but replies. The button stays on the last one alone: an older\n\t\t\t\t\t\t\t\t\t     round is a step in the conversation, not an order you can still\n\t\t\t\t\t\t\t\t\t     place. -->\n\t\t\t\t\t\t\t\t\t\t<!-- The strip's gutter, kept clear. It floats in the margin a 16:9\n\t\t\t\t\t\t\t\t\t\t\t picture leaves beside it; a phase made of text fills the width\n\t\t\t\t\t\t\t\t\t\t\t instead, and the thumbnails then sit on top of the sentences. -->\n\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\tbind:this={stageScroll}\n\t\t\t\t\t\t\t\t\t\t\t{@attach floorHold}\n\t\t\t\t\t\t\t\t\t\t\tclass=\"scroller mx-auto flex min-h-0 w-full max-w-[48rem] flex-1 flex-col overflow-y-auto px-1 pt-1 {showStrip\n\t\t\t\t\t\t\t\t\t\t\t\t? 'lg:pr-[6.5rem]'\n\t\t\t\t\t\t\t\t\t\t\t\t: ''}\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<!-- Sat at the bottom, the way a chat sits.\n\t\t\t\t\t\t\t\t\t\t\t\t A short exchange pinned to the top of a tall column reads as a\n\t\t\t\t\t\t\t\t\t\t\t\t page that failed to fill; pinned to the bottom it reads as a\n\t\t\t\t\t\t\t\t\t\t\t\t conversation that has only just started, which is what it is.\n\t\t\t\t\t\t\t\t\t\t\t\t Held there by scrolling, not by an auto margin — the margin moved\n\t\t\t\t\t\t\t\t\t\t\t\t every line on screen each time a reply landed. See floorHold. -->\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"shrink-0\" aria-hidden=\"true\"></div>\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"flex flex-col gap-5\">\n\t\t\t\t\t\t\t\t\t\t\t\t{#each stageThread as round (round.id)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{@const said = round.confirm?.said?.trim() || roundRequest(round)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{#if said}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"flex justify-end\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"enter doc max-w-[85%] rounded-2xl rounded-br-md bg-[var(--st-surface-2)] px-4 py-2.5 text-[0.95rem] leading-relaxed\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{said}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{@render confirmReply(round, round.id === stageRound.id)}\n\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t{:else if stagePhase === 'error'}\n\t\t\t\t\t\t\t\t\t\t{@const err = stageError}\n\t\t\t\t\t\t\t\t\t\t<!-- A render that died offers the same brief again, the way the\n\t\t\t\t\t\t\t\t\t\t     transcript's error card does. Its tokens are already back, so this\n\t\t\t\t\t\t\t\t\t\t     is a fresh, paid press — priced where it is pressed. Anything else\n\t\t\t\t\t\t\t\t\t\t     only clears the way for the next message. -->\n\t\t\t\t\t\t\t\t\t\t{@const src =\n\t\t\t\t\t\t\t\t\t\t\terr && /^A shooting step stalled/.test(err.text ?? '')\n\t\t\t\t\t\t\t\t\t\t\t\t? chat\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t.slice(0, chat.indexOf(err))\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t.reverse()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t.find(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// retryShot stands the card down while it launches, and a\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// launch takes ~40 s: without the busy card this fell to\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// \"try it again\" for all of it, and the press looked lost.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(c) => c.kind === 'shot' && (c.shot?.launched || shotBusy[c.id])\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t: undefined}\n\t\t\t\t\t\t\t\t\t\t{@const retryPrice = tokensForSend(\n\t\t\t\t\t\t\t\t\t\t\tNumber(src?.shot?.seconds) || composerShape.seconds,\n\t\t\t\t\t\t\t\t\t\t\t1,\n\t\t\t\t\t\t\t\t\t\t\tfreeClip\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\tclass=\"flex aspect-video w-full max-w-3xl flex-col items-center justify-center gap-3 rounded-2xl bg-[var(--st-surface)] px-8 text-center\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<p class=\"text-sm leading-relaxed text-[var(--st-muted)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t{err?.text}\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t{#if err && src}\n\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"flex flex-wrap items-center justify-center gap-3\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={shotBusy[src.id] || err.retried}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-primary\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={async () => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (tokens < retryPrice) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taskForTokens(retryPrice);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tawait retryShot(err.id, src.id);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// Started: the stage's clock takes over from here. Not\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// started: the error stays, with its button live again.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (err.retried) chat = chat.filter((c) => c !== err);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{shotBusy[src.id] ? 'starting…' : 'Render it again'}</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{priceLabel(retryPrice, freeClip)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => (chat = chat.filter((c) => c !== err))}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer text-xs text-[var(--st-faint)] underline-offset-2 hover:underline\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>dismiss</button\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (err) chat = chat.filter((c) => c !== err);\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-full bg-[var(--st-surface-2)] px-4 py-1.5 text-xs font-semibold\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>try it again</button\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t{:else if stagePhase === 'character'}\n\t\t\t\t\t\t\t\t\t\t{@const drawing = stageDrawing}\n\t\t\t\t\t\t\t\t\t\t<!-- A subject being drawn wins over one already made: the stage is\n\t\t\t\t\t\t\t\t\t\t about what is happening, and what is happening has no picture yet. -->\n\t\t\t\t\t\t\t\t\t\t{@const sh = drawing || stageMaking ? null : (stageSheet?.sheet ?? null)}\n\t\t\t\t\t\t\t\t\t\t{@const shownId = sh?.id ?? drawing?.id ?? ''}\n\t\t\t\t\t\t\t\t\t\t{@const row = sheets.find((x) => x.id === shownId)}\n\t\t\t\t\t\t\t\t\t\t{@const six = row?.sheet?.state === 'ready' && row.sheet.file ? shownId : ''}\n\t\t\t\t\t\t\t\t\t\t{@const turn = row?.sheet?.clip}\n\t\t\t\t\t\t\t\t\t\t{@const isUpload = !!row?.uploaded}\n\t\t\t\t\t\t\t\t\t\t{@const isChar = row?.kind !== 'location'}\n\t\t\t\t\t\t\t\t\t\t<!-- The subject's own picture, always by id. `sh.url` used to come first\n\t\t\t\t\t\t\t\t\t\t and that is the grid: once the four views land, the poll posts a fresh\n\t\t\t\t\t\t\t\t\t\t card whose url is the tiled sheet, so Reference and Four views were two\n\t\t\t\t\t\t\t\t\t\t chips pointing at one image. The card's own url only means anything\n\t\t\t\t\t\t\t\t\t\t before the subject is kept, when there is no id to ask by. -->\n\t\t\t\t\t\t\t\t\t\t{@const refUrl = shownId ? `/studio/api/sheet/img/${shownId}` : (sh?.url ?? '')}\n\t\t\t\t\t\t\t\t\t\t{@const view = charView || (six ? 'four' : turn ? 'turn' : 'ref')}\n\t\t\t\t\t\t\t\t\t\t<!-- The character, and the three things the work produces, in one frame.\n\t\t\t\t\t\t\t\t\t\t The reference is on screen from the moment you upload it. The turnaround\n\t\t\t\t\t\t\t\t\t\t lands and plays — it is a video, so it is shown as one. Then the six\n\t\t\t\t\t\t\t\t\t\t views are cut from it and take the surface, because that is what was\n\t\t\t\t\t\t\t\t\t\t being made and what every later clip is measured against.\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t All three existed already and two were unreachable: the turnaround only\n\t\t\t\t\t\t\t\t\t\t by landing on the clip stage where it did not belong, the six views\n\t\t\t\t\t\t\t\t\t\t behind a hover-only mark inside a menu. So the honest answer to \"why can\n\t\t\t\t\t\t\t\t\t\t I not see them\" was that there was nowhere to see them from. They switch\n\t\t\t\t\t\t\t\t\t\t here, in the frame they belong to, rather than opening a tab away from\n\t\t\t\t\t\t\t\t\t\t the work. -->\n\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\tclass=\"flex min-h-0 w-full flex-1 flex-col items-center justify-center gap-3\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"flex min-h-0 w-full flex-1 items-center justify-center\">\n\t\t\t\t\t\t\t\t\t\t\t\t<!-- Named branches, one per chip, rather than a chain that falls\n\t\t\t\t\t\t\t\t\t\t\t\t through to whatever is left. The reference used to be the\n\t\t\t\t\t\t\t\t\t\t\t\t fall-through case, so pressing Reference asked for a branch that\n\t\t\t\t\t\t\t\t\t\t\t\t was only reachable by every other one failing — and with a grid\n\t\t\t\t\t\t\t\t\t\t\t\t present the first condition kept winning. -->\n\t\t\t\t\t\t\t\t\t\t\t\t{#if view === 'four' && six}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsrc=\"/studio/api/sheet/full/{six}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonerror={sheetImageMissing}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"max-h-full max-w-full rounded-2xl object-contain\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t{:else if view === 'turn' && turn}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- svelte-ignore a11y_media_has_caption -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t<video\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsrc={still(fileUrl(turn.workspace, turn.artifact, turn.file))}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tautoplay\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tloop\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tmuted\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tplaysinline\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcontrols\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"max-h-full max-w-full rounded-2xl bg-black object-contain\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t></video>\n\t\t\t\t\t\t\t\t\t\t\t\t{:else if view === 'ref' && refUrl}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsrc={refUrl}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonerror={sheetImageMissing}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"max-h-full max-w-full rounded-2xl object-contain\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t{:else if sh?.url}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- The preview, before it has been kept.\n\t\t\t\t\t\t\t\t\t\t\t\t\t Until it is kept the subject has no id, so the sheet endpoint below\n\t\t\t\t\t\t\t\t\t\t\t\t\t has nothing to serve and the branch fell through to the spinner —\n\t\t\t\t\t\t\t\t\t\t\t\t\t which spun over a picture that had already rendered, was already on\n\t\t\t\t\t\t\t\t\t\t\t\t\t disk and was already being served at this very url. The card has\n\t\t\t\t\t\t\t\t\t\t\t\t\t carried it since the poll finished; nothing looked. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsrc={sh.url}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"max-h-full max-w-full rounded-2xl object-contain\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t{:else if shownId}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsrc=\"/studio/api/sheet/img/{shownId}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonerror={sheetImageMissing}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"max-h-full max-w-full rounded-2xl object-contain\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- Nothing to show yet, so the wait is here rather than on a rectangle\n\t\t\t\t\t\t\t\t\t\t\t\t\t the size of a clip. The picture lands in this frame, and the mark is\n\t\t\t\t\t\t\t\t\t\t\t\t\t the size of what is coming rather than of something else entirely. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"spin size-6 rounded-full border-2 border-[var(--st-surface-2)] border-t-[var(--st-muted)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t></span>\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"shrink-0 text-center\">\n\t\t\t\t\t\t\t\t\t\t\t\t<!-- Their name, and you can type it. It arrives as the first words of the\n\t\t\t\t\t\t\t\t\t\t\t\t description, truncated — a label, not a name — and this is the one\n\t\t\t\t\t\t\t\t\t\t\t\t surface where the thing being named is unmistakably a person. A field\n\t\t\t\t\t\t\t\t\t\t\t\t shaped like the heading it replaces, so nothing moves when you click\n\t\t\t\t\t\t\t\t\t\t\t\t into it. -->\n\t\t\t\t\t\t\t\t\t\t\t\t<!-- A field shaped like the heading it replaces gives no sign that it is\n\t\t\t\t\t\t\t\t\t\t\t\t one, so the mark that means \"you can change this\" sits beside it —\n\t\t\t\t\t\t\t\t\t\t\t\t faint, and solid on hover, the way the rest of this surface treats a\n\t\t\t\t\t\t\t\t\t\t\t\t control you have not reached for yet. It only focuses the field: the\n\t\t\t\t\t\t\t\t\t\t\t\t field is the control, and a second one that opened a dialog to do the\n\t\t\t\t\t\t\t\t\t\t\t\t same thing would be a step nobody asked for. -->\n\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"group mx-auto flex max-w-[24rem] items-center gap-1\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tvalue={row?.name ?? sh?.name ?? drawing?.name ?? ''}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tplaceholder=\"Name them\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={!shownId}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonblur={(e) => renameCharacter(shownId, e.currentTarget.value)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonkeydown={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (e.key === 'Enter') e.currentTarget.blur();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (e.key === 'Escape') {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\te.currentTarget.value = row?.name ?? '';\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\te.currentTarget.blur();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"min-w-0 flex-1 rounded-lg border-0 bg-transparent px-2 py-0.5 text-center font-display text-base font-semibold text-[var(--st-text)] outline-none placeholder:text-[var(--st-faint)] hover:bg-[var(--st-surface)] focus:bg-[var(--st-surface)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"rename them\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={!shownId}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst box = e.currentTarget\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.previousElementSibling as HTMLInputElement | null;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tbox?.focus();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tbox?.select();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md text-[var(--st-faint)] opacity-70 transition-opacity group-hover:opacity-100 hover:text-[var(--st-text)] disabled:cursor-default disabled:opacity-30\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-3.5\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M11.1 2.9a1.4 1.4 0 0 1 2 2L6.4 11.6l-2.7.7.7-2.7z\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t<!-- ── how they sound ───────────────────────────────────────\n\t\t\t\t\t\t\t\t\t\t\t\t Directly under the name, because it is the same kind of fact and\n\t\t\t\t\t\t\t\t\t\t\t\t the same kind of edit: two sentences that say who this is, saved\n\t\t\t\t\t\t\t\t\t\t\t\t the same way, on the one surface where that person is on screen.\n\t\t\t\t\t\t\t\t\t\t\t\t It was behind the casting picker, two taps away from the character\n\t\t\t\t\t\t\t\t\t\t\t\t it described and beside a list about a different question.\n\n\t\t\t\t\t\t\t\t\t\t\t\t A description, not a recording — the model reads the words, and\n\t\t\t\t\t\t\t\t\t\t\t\t the same sentence pulls the same voice back, which is what makes a\n\t\t\t\t\t\t\t\t\t\t\t\t two-clip scene sound like one woman rather than two. Only for a\n\t\t\t\t\t\t\t\t\t\t\t\t character: a room does not speak. -->\n\t\t\t\t\t\t\t\t\t\t\t\t{#if isChar && shownId}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"mx-auto mt-3 flex w-full max-w-[24rem] flex-col gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tbind:value={voiceDraft}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonblur={saveVoice}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonkeydown={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (e.key === 'Enter') {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(e.currentTarget as HTMLInputElement).blur();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tmaxlength=\"240\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tplaceholder=\"How they sound\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"min-h-9 w-full rounded-lg bg-[var(--st-surface-2)] px-3 text-center text-sm text-[var(--st-text)] outline-none placeholder:text-[var(--st-faint)] focus-visible:ring-2 focus-visible:ring-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"flex flex-wrap justify-center gap-1.5\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#each VOICE_PRESETS as v (v.label)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tvoiceDraft = v.text;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tvoid saveVoice();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"min-h-7 cursor-pointer rounded-full bg-[var(--st-surface-2)] px-2.5 text-[0.7rem] text-[var(--st-muted)] transition-colors hover:bg-[var(--st-line)] hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{v.label}</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t<!-- Only the stages that are actually coming, from the moment there is\n\t\t\t\t\t\t\t\t\t\t\t\t a reference. The ones not made yet stand there disabled with a\n\t\t\t\t\t\t\t\t\t\t\t\t turning mark, so the wait is attached to the thing being waited for\n\t\t\t\t\t\t\t\t\t\t\t\t rather than announced in a sentence somewhere else. They arrive in\n\t\t\t\t\t\t\t\t\t\t\t\t order, and the surface follows the newest until you press one.\n\n\t\t\t\t\t\t\t\t\t\t\t\t All three were shown for everything, and for a place that is a\n\t\t\t\t\t\t\t\t\t\t\t\t promise the code does not keep. A turnaround is a six-second render\n\t\t\t\t\t\t\t\t\t\t\t\t of the subject rotating, and api/sheet starts one only for an\n\t\t\t\t\t\t\t\t\t\t\t\t uploaded CHARACTER — \"a room does not have a front and a back the\n\t\t\t\t\t\t\t\t\t\t\t\t way a person does\", in its own words. A drawn subject has no\n\t\t\t\t\t\t\t\t\t\t\t\t turnaround either: its six views come from the sheet workflow\n\t\t\t\t\t\t\t\t\t\t\t\t directly. And an uploaded location gets neither, because the sheet\n\t\t\t\t\t\t\t\t\t\t\t\t workflows are text-to-image with no image input, so there is\n\t\t\t\t\t\t\t\t\t\t\t\t nothing that can redraw a photograph.\n\t\t\t\t\t\t\t\t\t\t\t\t A chip that spins forever is worse than one that was never\n\t\t\t\t\t\t\t\t\t\t\t\t offered. -->\n\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"mt-2 flex flex-wrap items-center justify-center gap-1.5\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{#each [{ id: 'ref', label: 'Reference', ok: !!shownId || !!sh?.url, on: true }, { id: 'turn', label: 'Turnaround', ok: !!turn, on: !!turn }, { id: 'four', label: 'Four views', ok: !!six, on: true }].filter((x) => x.on) as t (t.id)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={!t.ok}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-pressed={view === t.id}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => (charView = t.id as 'ref' | 'turn' | 'four')}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-sm {view === t.id && t.ok\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'btn-primary'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'btn-secondary'}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if !t.ok}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"spin size-3 shrink-0 rounded-full border-2 border-current/25 border-t-current\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t></span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span>{t.label}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t{#if sh && !sh.url && !sh.id}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- A preview the harness is still drawing. One picture, not six —\n\t\t\t\t\t\t\t\t\t\t\t\t\t the six-view line below would promise a turnaround this render\n\t\t\t\t\t\t\t\t\t\t\t\t\t does not make, and a wait labelled with the wrong work reads as\n\t\t\t\t\t\t\t\t\t\t\t\t\t a wait that is not moving. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"mt-2 flex items-center justify-center gap-2 text-xs text-[var(--st-faint)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"beacon size-1.5 shrink-0 rounded-full bg-[var(--st-green)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t></span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRendering one picture of {sh.kind === 'location'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'the place'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'them'} · about two minutes\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t{:else if !six}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-2 text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{turn ? 'Cutting the views' : 'Drawing the views'}{drawing?.sheet\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t?.startedAt\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? ` · ${clock(since(drawing.sheet.startedAt))}`\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: ''}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-2 text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tKept. Describe a shot and they are in it.\n\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t{:else if stagePhase === 'empty'}\n\t\t\t\t\t\t\t\t\t\t<!-- A new production opens on what it opened on before: the greeting\n\t\t\t\t\t\t\t\t and three things to try. The stage has nothing to show yet and a\n\t\t\t\t\t\t\t\t blank rectangle where a clip will be is not an invitation. -->\n\t\t\t\t\t\t\t\t\t\t<!-- RAT-632: the page's one H1 is the product's name for itself in\n\t\t\t\t\t\t\t\t the words people search for, set small above the greeting the way a\n\t\t\t\t\t\t\t\t product line sits above its headline. The greeting stays what it was.\n\t\t\t\t\t\t\t\t No `enter` on it: that animation starts from opacity 0, and a page\n\t\t\t\t\t\t\t\t that is rendered but never painted — a crawler's — must not keep\n\t\t\t\t\t\t\t\t its heading there. -->\n\t\t\t\t\t\t\t\t\t\t<h1\n\t\t\t\t\t\t\t\t\t\t\tclass=\"text-center text-sm font-semibold tracking-[-0.005em] text-[var(--st-faint)] sm:text-[15px]\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\tAI porn &amp; NSFW AI video generator\n\t\t\t\t\t\t\t\t\t\t</h1>\n\t\t\t\t\t\t\t\t\t\t<h2\n\t\t\t\t\t\t\t\t\t\t\tclass=\"enter mx-auto max-w-[26rem] text-center font-display text-[clamp(2.25rem,6vw,3.25rem)] leading-[1.06] font-semibold tracking-[-0.042em] text-balance\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{#each WELCOME_LINES as line, i (line)}\n\t\t\t\t\t\t\t\t\t\t\t\t{line}{#if i === 0}<br />{/if}\n\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t</h2>\n\t\t\t\t\t\t\t\t\t\t{@render starters()}\n\n\t\t\t\t\t\t\t\t\t\t<!-- What has already been made, under the invitation to make more.\n\t\t\t\t\t\t\t\t\t\t A front page that only invites is a brochure; the work is the\n\t\t\t\t\t\t\t\t\t\t argument, and it is sitting on the disk either way.\n\t\t\t\t\t\t\t\t\t\t Columns rather than a grid, because a grid rules everything\n\t\t\t\t\t\t\t\t\t\t into rows and what this wants is the offset — each column\n\t\t\t\t\t\t\t\t\t\t fills on its own, so a tall film in one pushes its neighbours\n\t\t\t\t\t\t\t\t\t\t out of step and the wall stops looking like a table. -->\n\t\t\t\t\t\t\t\t\t\t{#if shelf.length}\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"w-full max-w-3xl columns-2 gap-2 pt-8 pb-2 lg:columns-3\">\n\t\t\t\t\t\t\t\t\t\t\t\t{#each shelf as m, i (m.id)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{@render mediaTile(m, i)}\n\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t<!-- Quiet: this is the studio's own housekeeping, not part of making a\n\t\t\t\t\t\t\t\t\t\t\t\t film. Here rather than only in the sidebar because this is the wall\n\t\t\t\t\t\t\t\t\t\t\t\t it edits. -->\n\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\tmediaOpen = true;\n\t\t\t\t\t\t\t\t\t\t\t\t\tvoid loadPool();\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer pb-1 text-xs text-[var(--st-faint)] transition-colors hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t>choose what shows here</button\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t<!-- RAT-632: what the studio is and how it works, after the wall —\n\t\t\t\t\t\t\t\t\t\t below everything the first screen shows, for whoever scrolls on. -->\n\t\t\t\t\t\t\t\t\t\t<StudioAbout shown={shelfSettled} />\n\t\t\t\t\t\t\t\t\t{:else if stagePhase === 'ready' && stageClip}\n\t\t\t\t\t\t\t\t\t\t{@const f = stageShownUrl ? { url: stageShownUrl } : null}\n\t\t\t\t\t\t\t\t\t\t{@const sws = stageShownWs}\n\t\t\t\t\t\t\t\t\t\t{@const shownPart =\n\t\t\t\t\t\t\t\t\t\t\tstageShown?.artifact && stageShown?.file\n\t\t\t\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tworkspace: stageShown.workspace,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tartifact: stageShown.artifact,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tfile: stageShown.file\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t: null}\n\t\t\t\t\t\t\t\t\t\t{@const sv = verdict[sws]}\n\t\t\t\t\t\t\t\t\t\t<!-- The clip takes the room it is given: this is what the operator\n\t\t\t\t\t\t\t\t came to look at, and a 48rem cap in the middle of a wide screen\n\t\t\t\t\t\t\t\t left a third of it black. Height-bound rather than width-bound, so\n\t\t\t\t\t\t\t\t a portrait clip and a 16:9 one both fill what there is.\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t The actions ride on top of the picture rather than under it. Below\n\t\t\t\t\t\t\t\t the frame they pushed the clip up and competed with the composer\n\t\t\t\t\t\t\t\t for the same band of the screen. -->\n\t\t\t\t\t\t\t\t\t\t<div class=\"flex min-h-0 w-full flex-1 items-center justify-center\">\n\t\t\t\t\t\t\t\t\t\t\t<!-- Sized to the clip, not to the column: the row of actions anchors\n\t\t\t\t\t\t\t\t\t to this box, and anchored to the column it hung off the picture's\n\t\t\t\t\t\t\t\t\t edges into the black beside it. -->\n\t\t\t\t\t\t\t\t\t\t\t<!-- Mobile fits the whole clip rather than filling the box.\n\t\t\t\t\t\t\t\t\t\t\t On a phone the picture is the surface, and a landscape clip in a\n\t\t\t\t\t\t\t\t\t\t\t portrait window has to be seen whole rather than cropped or\n\t\t\t\t\t\t\t\t\t\t\t overflowing the screen — which is what h-full did, pushing a\n\t\t\t\t\t\t\t\t\t\t\t 16:9 clip past the right edge. With both dimensions capped and\n\t\t\t\t\t\t\t\t\t\t\t neither set, the element keeps its own ratio and scales down to\n\t\t\t\t\t\t\t\t\t\t\t whichever runs out first: a portrait clip fills the height, a\n\t\t\t\t\t\t\t\t\t\t\t landscape one sits centred and smaller. Desktop keeps h-full,\n\t\t\t\t\t\t\t\t\t\t\t where the window is wider than any clip and the picture should\n\t\t\t\t\t\t\t\t\t\t\t take all the height there is. -->\n\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"relative max-h-[min(100%,calc(100dvh-19rem))] w-fit max-w-full lg:h-full\"\n\t\t\t\t\t\t\t\t\t\t\t\tstyle={stageVidH > stageVidW ? 'height:100%' : undefined}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{#if f}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- svelte-ignore a11y_media_has_caption -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t<video\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsrc={still(f.url)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata-clip={sws || null}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcrossorigin=\"anonymous\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcontrols\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonerror={(e) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trecoverVideo(e.currentTarget as HTMLVideoElement, f.url)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tautoplay={stageClip.id === stageAutoplayId}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonplaying={() => noteClipViewed(stageClip.id)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tloop\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tplaysinline\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tbind:clientWidth={stageVideoW}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tbind:videoWidth={stageVidW}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tbind:videoHeight={stageVidH}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"video-with-controls max-h-[min(100%,calc(100dvh-19rem))] w-auto max-w-full rounded-2xl bg-black lg:h-full\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t></video>\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t<!-- The row breaks rather than the words. A clip bounded to a short\n\t\t\t\t\t\t\t\t\t\t\t\t     window can be 170 pixels wide, and the three labels squeezed into\n\t\t\t\t\t\t\t\t\t\t\t\t     one line broke inside themselves — \"Add / to / film\" down the\n\t\t\t\t\t\t\t\t\t\t\t\t     left edge and \"Not good\" hanging off the right. Kept whole, the\n\t\t\t\t\t\t\t\t\t\t\t\t     verdicts drop under \"Add to film\" when there is no room beside it. -->\n\t\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"pointer-events-none absolute inset-x-0 top-0 flex flex-wrap items-start justify-between gap-2 p-3\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"pointer-events-auto flex items-center gap-2 whitespace-nowrap\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if shownPart}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if film.some((x) => filmKey(x) === filmKey(shownPart))}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex items-center gap-1.5 rounded-full bg-black/55 px-3 py-1 text-xs text-white backdrop-blur\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span aria-hidden=\"true\">✓</span><span>In the film</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() =>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taddClipToFilm(shownPart, stageClip?.artifact?.title ?? '')}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-full bg-black/40 px-2.5 py-1 text-[0.6875rem] font-semibold text-white/85 backdrop-blur transition-colors hover:bg-black/75 hover:text-white lg:bg-black/55 lg:px-3 lg:text-xs lg:text-white\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>Add to film</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"pointer-events-auto ml-auto flex flex-wrap items-center justify-end gap-1 whitespace-nowrap\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if !sv}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => rate(sws, 'kept')}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-full bg-black/40 px-2.5 py-1 text-[0.6875rem] text-white/85 backdrop-blur transition-colors hover:bg-black/75 hover:text-white lg:bg-black/55 lg:px-3 lg:text-xs lg:text-white\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>Good</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => rate(sws, 'rejected')}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-full bg-black/40 px-2.5 py-1 text-[0.6875rem] text-white/85 backdrop-blur transition-colors hover:bg-black/75 hover:text-white lg:bg-black/55 lg:px-3 lg:text-xs lg:text-white\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>Not good</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex items-center gap-2 rounded-full bg-black/55 px-3 py-1 text-xs text-white backdrop-blur\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if sv === 'rejected' && diagnosing[sws]}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"beacon size-1.5 shrink-0 rounded-full bg-white\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t></span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWorking out the fix\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{sv === 'kept' ? 'Noted as good' : 'Noted'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- The verdict outlives the diagnosis. The verdict comes back from\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t     the log on a reload; the diagnosis was only ever held by the tab,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t     so a clip marked Not good and reopened said \"Noted\" and offered\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t     no way to the fix it had been given. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if sv === 'rejected' && !diagnosing[sws] && !fix[sws] && !diagnoseError[sws]}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => diagnose(sws)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-full bg-black/40 px-2.5 py-1 text-[0.6875rem] text-white/85 backdrop-blur transition-colors hover:bg-black/75 hover:text-white lg:bg-black/55 lg:px-3 lg:text-xs lg:text-white\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>What went wrong?</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t{#if sv === 'rejected' && (fix[sws] || diagnoseError[sws])}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- The answer to \"Not good\", on the picture it is about.\n\t\t\t\t\t\t\t\t\t\t\t\t\t The diagnosis lived only on the transcript's clip card, and the simple\n\t\t\t\t\t\t\t\t\t\t\t\t\t studio never draws the transcript — so even a diagnosis that arrived had\n\t\t\t\t\t\t\t\t\t\t\t\t\t nowhere to be seen from the stage. It rides on the clip the way the\n\t\t\t\t\t\t\t\t\t\t\t\t\t verdict buttons do, and scrolls inside itself rather than pushing the\n\t\t\t\t\t\t\t\t\t\t\t\t\t picture off the screen.\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t The box it sits in runs from under the verdict row to the bottom edge.\n\t\t\t\t\t\t\t\t\t\t\t\t\t A percentage cap needs a height to be a percentage of, and a box pinned\n\t\t\t\t\t\t\t\t\t\t\t\t\t only to the bottom has none: on a short window the panel grew over the\n\t\t\t\t\t\t\t\t\t\t\t\t\t whole clip, and its first lines went above the stage, where nothing can\n\t\t\t\t\t\t\t\t\t\t\t\t\t scroll back to them. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"pointer-events-none absolute inset-x-0 top-[4.5rem] bottom-0 flex flex-col justify-end p-3\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"pointer-events-auto flex max-h-[min(100%,22rem)] flex-col rounded-2xl bg-black/75 p-3.5 text-white backdrop-blur lg:p-4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if fix[sws]}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{@const fx = fix[sws]}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- The words scroll and the buttons do not. On a clip a phone\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t     gives a third of its height to, the one thing to press sat\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t     under the fold of a box the size of a thumbnail. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"min-h-0 overflow-y-auto\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"text-[13px] leading-relaxed\">{fx.why}</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if fx.loras?.length}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-1.5 text-xs text-white/70\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnext attempt with {fx.loras\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.map(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(p) => `${loraFor(p.key)?.label ?? p.key} ${p.strength}`\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.join(' · ')}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"mt-3 flex shrink-0 flex-wrap items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={fixBusy[sws]}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => renderFix(sws)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-full bg-white px-4 py-1.5 text-xs font-semibold text-black transition-opacity hover:opacity-90 disabled:cursor-default disabled:opacity-60\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{fixBusy[sws] ? 'starting…' : 'Render the fix'}</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => diagnose(sws)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-full px-3 py-1.5 text-xs text-white/75 transition-colors hover:text-white\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>Look again</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"min-h-0 overflow-y-auto text-[13px] leading-relaxed\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{diagnoseError[sws]}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => diagnose(sws)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"mt-2.5 shrink-0 cursor-pointer self-start rounded-full bg-white px-4 py-1.5 text-xs font-semibold text-black transition-opacity hover:opacity-90\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>Look again</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t{:else}\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tbind:this={scrollEl}\n\t\t\t\t\t\t\tonscroll={onTranscriptScroll}\n\t\t\t\t\t\t\tclass=\"scroller min-h-0 flex-1 space-y-5 overflow-y-auto px-1 pb-2 {showExamples\n\t\t\t\t\t\t\t\t? 'flex flex-col justify-center'\n\t\t\t\t\t\t\t\t: ''}\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{#each chat as item, itemAt (item.id)}\n\t\t\t\t\t\t\t\t{#if superseded[item.id]}\n\t\t\t\t\t\t\t\t\t<p class=\"text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\tearlier version\n\t\t\t\t\t\t\t\t\t\t{#if item.kind === 'plan' && item.plan}\n\t\t\t\t\t\t\t\t\t\t\t· {item.plan.title}\n\t\t\t\t\t\t\t\t\t\t{:else if item.kind === 'artifact' && item.artifact}\n\t\t\t\t\t\t\t\t\t\t\t· {item.artifact.title}\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t{:else if item.who === 'user'}\n\t\t\t\t\t\t\t\t\t<div class=\"flex justify-end\">\n\t\t\t\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\t\t\t\tclass=\"enter doc max-w-[85%] rounded-2xl rounded-br-md bg-[var(--st-surface-2)] px-4 py-2.5 text-[0.95rem] leading-relaxed\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{item.text}\n\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{:else if item.kind === 'text' && item.id === welcomeId && !showExamples}\n\t\t\t\t\t\t\t\t\t<!-- Nothing. The greeting is the empty page's, and once you have said\n\t\t\t\t\t\t\t\t something the page is not empty — leaving it there turns a headline\n\t\t\t\t\t\t\t\t into the first line of the transcript, which it never was. It stays\n\t\t\t\t\t\t\t\t in `chat` so reset() and the restore path are untouched. -->\n\t\t\t\t\t\t\t\t{:else if item.kind === 'text' && item.id === welcomeId && showExamples}\n\t\t\t\t\t\t\t\t\t<!-- The greeting is the page, not a message in it. Short enough to\n\t\t\t\t\t\t\t\t set at display size, so it gets one. -->\n\t\t\t\t\t\t\t\t\t<h2\n\t\t\t\t\t\t\t\t\t\tclass=\"enter mx-auto max-w-[26rem] text-center font-display text-[clamp(2.25rem,6vw,3.25rem)] leading-[1.06] font-semibold tracking-[-0.042em] text-balance\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{#each WELCOME_LINES as line, i (line)}\n\t\t\t\t\t\t\t\t\t\t\t{line}{#if i === 0}<br />{/if}\n\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t</h2>\n\t\t\t\t\t\t\t\t{:else if item.kind === 'text'}\n\t\t\t\t\t\t\t\t\t<div class=\"enter\">\n\t\t\t\t\t\t\t\t\t\t<p class=\"doc text-[0.95rem] leading-[1.75] text-[var(--st-text)]\">\n\t\t\t\t\t\t\t\t\t\t\t{item.text}\n\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{:else if item.kind === 'board'}\n\t\t\t\t\t\t\t\t\t<article class=\"enter rounded-2xl bg-[var(--st-surface)] p-5 sm:p-6\">\n\t\t\t\t\t\t\t\t\t\t<div class=\"mb-4 flex items-baseline justify-between gap-3\">\n\t\t\t\t\t\t\t\t\t\t\t<h3 class=\"font-display text-base font-semibold\">The plan</h3>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"font-mono text-[11px] text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t{boardDone} of {board.length}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t\t\t<div class=\"divide-y divide-[var(--st-surface-2)]\">\n\t\t\t\t\t\t\t\t\t\t\t{#each board as row (row.key)}\n\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"py-3 first:pt-0 last:pb-0\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"flex items-center gap-3\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- State reads without colour too: a spinner spins, a\n\t\t\t\t\t\t\t\t\t\t\t\t     check is a check. Colour alone would fail anyone who\n\t\t\t\t\t\t\t\t\t\t\t\t     cannot separate the green from the grey. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"flex size-4 shrink-0 items-center justify-center\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if row.state === 'done'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-4 text-[#5b8f6e]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M3.5 8.5l3 3 6-7\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"2\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else if row.state === 'writing' || row.state === 'rewriting'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"spin size-3.5 rounded-full border-2 border-[var(--st-surface-2)] border-t-[var(--st-accent)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t></span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else if row.state === 'failed'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-4 text-[#c4614b]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M4 4l8 8M12 4l-8 8\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"2\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"size-1.5 rounded-full bg-[var(--st-faint)]\"></span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex-1 font-display text-sm font-semibold {row.state ===\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t'waiting'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'text-[var(--st-faint)]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-text)]'}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{row.label}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if row.state === 'done'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer text-xs text-[var(--st-muted)] underline-offset-4 transition-colors hover:text-[var(--st-text)] hover:underline\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => (expanded[row.key] = !expanded[row.key])}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{expanded[row.key] ? 'close' : 'read'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else if row.state === 'rewriting'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-xs text-[var(--st-faint)]\">rewriting</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else if row.state === 'writing'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-xs text-[var(--st-faint)]\">writing…</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else if row.state === 'failed'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-xs text-[var(--st-muted)]\">stalled</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t{#if row.state === 'done' && expanded[row.key]}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"mt-3 border-l border-[var(--st-surface-2)] pl-4\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if row.body}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{@render document(renderDocument(row.file ?? '', row.body))}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else if row.url}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- eslint-disable-next-line svelte/no-navigation-without-resolve -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\thref={row.url}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"text-xs text-[var(--st-muted)] underline\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdownload\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdownload the file\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"mt-4 flex flex-wrap items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => (changeOpen[row.key] = !changeOpen[row.key])}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-secondary btn-sm\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trequest a change\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- The way out, next to the way in. A document runs to several\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t     screens, and without this the only way to close one was to\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t     scroll back up to the row that opened it. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => (expanded[row.key] = false)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-full px-3.5 py-2 text-xs text-[var(--st-faint)] transition-colors hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclose\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if changeOpen[row.key]}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<form\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"mt-3 flex gap-2\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonsubmit={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trequestChange(row.key, row.key);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<label class=\"sr-only\" for=\"change-{row.key}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>What should change</label\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tid=\"change-{row.key}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tbind:value={changeText[row.key]}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tplaceholder=\"what should change in this document\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"min-w-0 flex-1 rounded-xl border border-[var(--st-line)] bg-[var(--st-bg)] px-3.5 py-2.5 text-sm outline-none placeholder:text-[var(--st-faint)] focus:border-[var(--st-muted)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={changeBusy[row.key] ||\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t!(changeText[row.key] ?? '').trim()}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-primary\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsend\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</form>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-2 text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tThis step is rewritten, and everything built on it is refreshed\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tafter it.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t\t\t<!-- The button belongs to the thing it acts on. It is present from\n\t\t\t\t\t\t\t\t     the start, disabled, so the shape of the run is visible before\n\t\t\t\t\t\t\t\t     any of it has happened — and so nobody hunts for it once the\n\t\t\t\t\t\t\t\t     last document lands. -->\n\t\t\t\t\t\t\t\t\t\t{#if renderWs}\n\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-5 text-xs text-[var(--st-faint)]\">shooting has started</p>\n\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"mt-5 flex flex-wrap items-center gap-3\">\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={boardDone < board.length || renderLaunching || !!chain}\n\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={launchRender}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-primary\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{renderLaunching ? 'starting…' : 'start shooting'}\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{#if chain}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ta document is being rewritten\n\t\t\t\t\t\t\t\t\t\t\t\t\t{:else if boardDone < board.length}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tready when all five are written\n\t\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tuses GPU time and costs money\n\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t</article>\n\t\t\t\t\t\t\t\t{:else if item.kind === 'shootboard'}\n\t\t\t\t\t\t\t\t\t<article class=\"enter rounded-2xl bg-[var(--st-surface)] p-5 sm:p-6\">\n\t\t\t\t\t\t\t\t\t\t<div class=\"mb-1 flex items-baseline justify-between gap-3\">\n\t\t\t\t\t\t\t\t\t\t\t<h3 class=\"font-display text-base font-semibold\">Shooting</h3>\n\t\t\t\t\t\t\t\t\t\t\t{#if !staleRun}\n\t\t\t\t\t\t\t\t\t\t\t\t<!-- Dropped rather than frozen on a stale run: we know the\n\t\t\t\t\t\t\t\t\t\t\t shoot is over, but not when it ended, and a stopped clock\n\t\t\t\t\t\t\t\t\t\t\t showing a number we made up is worse than no clock. -->\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"font-mono text-[11px] text-[var(--st-faint)] tabular-nums\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{mmss(shootElapsed)}\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<p class=\"mb-4 text-xs leading-relaxed text-[var(--st-muted)]\">\n\t\t\t\t\t\t\t\t\t\t\tEach scene is written into a prompt, then rendered on a GPU. A clip usually\n\t\t\t\t\t\t\t\t\t\t\ttakes several minutes and there is no output until it is finished{#if !staleRun}\n\t\t\t\t\t\t\t\t\t\t\t\t— the timer is the only thing that moves{/if}.\n\t\t\t\t\t\t\t\t\t\t</p>\n\n\t\t\t\t\t\t\t\t\t\t{#if shootBoard.length}\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"divide-y divide-[var(--st-surface-2)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t{#each shootBoard as row (row.n)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"flex items-center gap-3 py-3 first:pt-0 last:pb-0\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"flex size-4 shrink-0 items-center justify-center\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if row.state === 'done'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-4 text-[#5b8f6e]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M3.5 8.5l3 3 6-7\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"2\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else if row.state === 'failed'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-4 text-[#c4614b]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M4 4l8 8M12 4l-8 8\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"2\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"spin size-3.5 rounded-full border-2 border-[var(--st-surface-2)] border-t-[var(--st-accent)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t></span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"min-w-0 flex-1 truncate text-sm\">{row.title}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"shrink-0 text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if row.state === 'done'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tready\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else if row.state === 'failed'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstalled\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else if row.retries >= 3}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-[var(--st-muted)]\">retried {row.retries}×</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trendering\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t<p class=\"text-sm text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\tWorking out how many scenes to shoot…\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t</article>\n\t\t\t\t\t\t\t\t{:else if item.kind === 'activity' && item.activity}\n\t\t\t\t\t\t\t\t\t<!-- Quiet by design. These are constant during a run, and a\n\t\t\t\t\t\t\t     progress line that shouts competes with the documents the\n\t\t\t\t\t\t\t     user is actually here to read. Trouble is the exception:\n\t\t\t\t\t\t\t     a rejection or a failure gets colour, because that is the\n\t\t\t\t\t\t\t     one case where not noticing is expensive. -->\n\t\t\t\t\t\t\t\t\t{@const a = item.activity}\n\t\t\t\t\t\t\t\t\t<div class=\"enter flex items-start gap-2.5 py-0.5\">\n\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\tclass=\"mt-[0.45rem] size-1.5 shrink-0 rounded-full {a.tone === 'bad'\n\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-[#c4614b]'\n\t\t\t\t\t\t\t\t\t\t\t\t: a.tone === 'warn'\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-[#b98a3e]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: a.tone === 'good'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-[#5b8f6e]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'bg-[var(--st-faint)]'}\"\n\t\t\t\t\t\t\t\t\t\t></span>\n\t\t\t\t\t\t\t\t\t\t<div class=\"min-w-0\">\n\t\t\t\t\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"text-[0.82rem] leading-relaxed {a.tone === 'bad' || a.tone === 'warn'\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'text-[var(--st-muted)]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-faint)]'}\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{a.text}\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t{#if a.detail}\n\t\t\t\t\t\t\t\t\t\t\t\t<details class=\"mt-0.5\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<summary\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer text-[0.72rem] text-[var(--st-faint)] hover:text-[var(--st-muted)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\twhat it said\n\t\t\t\t\t\t\t\t\t\t\t\t\t</summary>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"doc mt-1 border-l border-[var(--st-surface-2)] pl-3 font-mono text-[0.72rem] leading-relaxed text-[var(--st-muted)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{a.detail}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t</details>\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{:else if item.kind === 'confirm' && item.confirm}\n\t\t\t\t\t\t\t\t\t<!-- The button only appears on the newest one. An older round is a step in\n\t\t\t\t\t\t\t\t\t     the conversation, not an order you can still place, and two live buttons\n\t\t\t\t\t\t\t\t\t     is two ways to shoot the wrong version. -->\n\t\t\t\t\t\t\t\t\t{@const newest = chat.filter((c) => c.kind === 'confirm').at(-1)?.id === item.id}\n\t\t\t\t\t\t\t\t\t<!-- Gone once its clip is on a GPU. It came back reading \"start the render\"\n\t\t\t\t\t\t\t\t\t     over a render that was already running, and pressing it again wrote a\n\t\t\t\t\t\t\t\t\t     second brief and paid for the same eight seconds twice. A started clip\n\t\t\t\t\t\t\t\t\t     lives on the card below. -->\n\t\t\t\t\t\t\t\t\t{@const started = item.confirm.cardId\n\t\t\t\t\t\t\t\t\t\t? !!chat.find((x) => x.id === item.confirm?.cardId)?.shot?.launched\n\t\t\t\t\t\t\t\t\t\t: false}\n\t\t\t\t\t\t\t\t\t{@render confirmReply(item, newest && !started)}\n\t\t\t\t\t\t\t\t{:else if item.kind === 'error'}\n\t\t\t\t\t\t\t\t\t<!-- The card that launched the render this error is about, found by\n\t\t\t\t\t\t\t\t looking back rather than read off the item.\n\t\t\t\t\t\t\t     Stored at push time it would only ever appear on errors raised\n\t\t\t\t\t\t\t     after this shipped, and the one on screen when it was asked for was\n\t\t\t\t\t\t\t     already saved. A rule about what is displayed has to hold for what\n\t\t\t\t\t\t\t     is on disk — the same lesson the caption taught an hour earlier. -->\n\t\t\t\t\t\t\t\t\t{@const stalled = /^A shooting step stalled/.test(item.text ?? '')}\n\t\t\t\t\t\t\t\t\t{@const src = stalled\n\t\t\t\t\t\t\t\t\t\t? [...chat.slice(0, itemAt)]\n\t\t\t\t\t\t\t\t\t\t\t\t.reverse()\n\t\t\t\t\t\t\t\t\t\t\t\t.find((c) => c.kind === 'shot' && c.shot?.launched)\n\t\t\t\t\t\t\t\t\t\t: undefined}\n\t\t\t\t\t\t\t\t\t<div class=\"enter rounded-2xl bg-[var(--st-surface)] p-4\">\n\t\t\t\t\t\t\t\t\t\t<p class=\"text-xs font-semibold text-[#f2d7cd]\">\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"mr-2 rounded-md bg-[#5c2f24] px-2 py-0.5\">error</span>\n\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t<p class=\"doc mt-2 text-sm leading-relaxed text-[var(--st-muted)]\">\n\t\t\t\t\t\t\t\t\t\t\t{item.text}\n\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t{#if src}\n\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\tdisabled={shotBusy[src.id] || item.retried}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-secondary btn-sm mt-3\"\n\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => retryShot(item.id, src.id)}\n\t\t\t\t\t\t\t\t\t\t\t\t>{shotBusy[src.id]\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'starting…'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: item.retried\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'started again'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'try it again'}</button\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<!-- A retry is a render, and a render is paid for. One clip: the\n\t\t\t\t\t\t\t\t\t\t retry re-runs the shot that stalled, not the batch it may have\n\t\t\t\t\t\t\t\t\t\t belonged to. -->\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"mt-3 ml-2.5 text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t{priceLabel(tokensForSend(composerShape.seconds, 1, freeClip), freeClip)}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{:else if item.kind === 'sheet' && item.sheet}\n\t\t\t\t\t\t\t\t\t<article class=\"enter rounded-2xl bg-[var(--st-surface)] p-5 sm:p-6\">\n\t\t\t\t\t\t\t\t\t\t<div class=\"mb-3 flex items-baseline justify-between gap-4\">\n\t\t\t\t\t\t\t\t\t\t\t<h3 class=\"font-display text-base font-semibold\">\n\t\t\t\t\t\t\t\t\t\t\t\t{item.sheet.stage === 'anchor'\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'The character'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: item.sheet.kind === 'character'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Character sheet'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'Location sheet'}\n\t\t\t\t\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t{item.sheet.uploaded\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'your own picture — kept as it is'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: item.sheet.stage === 'anchor'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'one picture — say what to change, or save it'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: item.sheet.kind === 'character'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'front · face · profiles · rear · expression'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'six views of the same place'}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t\t\t{#if item.sheet.url}\n\t\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\t\tsrc={item.sheet.url}\n\t\t\t\t\t\t\t\t\t\t\t\talt={item.sheet.name ?? ''}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"w-full rounded-xl bg-[var(--st-bg)]\"\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t{#if item.sheet.description}\n\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"doc mt-3 text-sm leading-relaxed text-[var(--st-muted)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{item.sheet.description}\n\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t<!-- The description before it costs anything, editable, for the\n\t\t\t\t\t\t\t\t\t\t same reason the shot prompt is: a sheet is rendered once and\n\t\t\t\t\t\t\t\t\t\t every clip afterwards is shot against it. -->\n\t\t\t\t\t\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\t\t\t\t\t\tbind:value={item.sheet.description}\n\t\t\t\t\t\t\t\t\t\t\t\treadonly={item.sheet.launched}\n\t\t\t\t\t\t\t\t\t\t\t\trows=\"3\"\n\t\t\t\t\t\t\t\t\t\t\t\tspellcheck=\"false\"\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"block w-full resize-y rounded-xl bg-[var(--st-bg)] p-3 font-mono text-[0.8rem] leading-relaxed outline-none read-only:text-[var(--st-muted)] focus:ring-0\"\n\t\t\t\t\t\t\t\t\t\t\t></textarea>\n\t\t\t\t\t\t\t\t\t\t\t{#if item.sheet.why}\n\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-2 text-xs text-[var(--st-faint)]\">{item.sheet.why}</p>\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"mt-4 flex items-center justify-between gap-3 border-t border-[var(--st-line)] pt-4\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{item.sheet.launched\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Rendering — it appears here when it is done.'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'Six views, about as long as a clip takes.'}\n\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={item.sheet.launched ||\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsheetBusy[item.id] ||\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t!item.sheet.description.trim()}\n\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => renderSheet(item.id)}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-primary\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{item.sheet.launched ? 'Rendering…' : 'Render it'}\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t\t\t\t\t{#if item.sheet.url && item.sheet.stage === 'anchor'}\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"mt-4 border-t border-[var(--st-line)] pt-4\">\n\t\t\t\t\t\t\t\t\t\t\t\t{#if item.sheet.id && item.sheet.uploaded}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{@const kept = sheets.find((x) => x.id === item.sheet?.id)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{@const dn =\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tkeptEdits[item.id]?.name ?? kept?.name ?? item.sheet.name ?? ''}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- The stored voice, or the one a character made today would have\n\t\t\t\t\t\t\t\t\t\t\t been given. Characters made before that was written have none,\n\t\t\t\t\t\t\t\t\t\t\t and an empty box with grey suggestion text meant the suggestion\n\t\t\t\t\t\t\t\t\t\t\t was never what got rendered — a placeholder looks filled in and\n\t\t\t\t\t\t\t\t\t\t\t is worth nothing. Now it is real text you can edit, and Update\n\t\t\t\t\t\t\t\t\t\t\t is what makes it theirs. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t{@const dv = keptEdits[item.id]?.voice ?? kept?.voice ?? DEFAULT_VOICE}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{@const changed =\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdn.trim() !== (kept?.name ?? '') ||\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdv.trim() !== (kept?.voice ?? DEFAULT_VOICE)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- The character is usable the moment the picture lands — the\n\t\t\t\t\t\t\t\t\t turnaround is an improvement to it, not a condition of it. So\n\t\t\t\t\t\t\t\t\t the first line is the useful state and the wait is a second,\n\t\t\t\t\t\t\t\t\t quieter one, rather than the card going silent for three\n\t\t\t\t\t\t\t\t\t minutes with a sentence in the past tense above it. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"text-sm text-[var(--st-muted)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tReady to use — pick\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{kept?.name ?? item.sheet.name}</span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tfrom\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-[var(--st-text)]\">+</span> in the box below.\n\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- No progress line here. It is in the strip above the box now,\n\t\t\t\t\t\t\t\t\t\t\t where it stays on screen; a card scrolls away in a minute and\n\t\t\t\t\t\t\t\t\t\t\t took the only sign of a running GPU with it. What stays is the\n\t\t\t\t\t\t\t\t\t\t\t outcome, which the strip cannot report because by then it is\n\t\t\t\t\t\t\t\t\t\t\t gone. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t{#if kept?.sheet?.state === 'failed'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-2 text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tThe six views could not be drawn. {item.sheet.kind === 'character'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'They are'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'It is'} still usable without them.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- What is worth changing once you can see who they are: what\n\t\t\t\t\t\t\t\t\t they are called, and how they sound. The voice was reachable\n\t\t\t\t\t\t\t\t\t only from the composer, which is not where this decision\n\t\t\t\t\t\t\t\t\t happens. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"mt-4 border-t border-[var(--st-line)] pt-4\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if item.sheet.kind === 'character'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"block text-xs text-[var(--st-faint)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfor=\"kept-voice-{item.id}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tHow they sound — carried into every clip they are in\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tid=\"kept-voice-{item.id}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tvalue={dv}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\toninput={(e) => editKept(item.id, 'voice', e.currentTarget.value)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tspellcheck=\"false\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tmaxlength=\"240\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"mt-2 mb-4 w-full rounded-lg bg-[var(--st-bg)] px-3 py-2 text-sm outline-none focus:ring-0\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"block text-xs text-[var(--st-faint)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfor=\"kept-name-{item.id}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{item.sheet.kind === 'character' ? 'Name them' : 'Name it'} — this is what\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tthe picker will show\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"mt-2 flex flex-wrap items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tid=\"kept-name-{item.id}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tvalue={dn}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\toninput={(e) => editKept(item.id, 'name', e.currentTarget.value)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tspellcheck=\"false\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"min-w-0 flex-1 rounded-lg bg-[var(--st-bg)] px-3 py-2 text-sm outline-none focus:ring-0\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- Only when there is something to apply. A button that is\n\t\t\t\t\t\t\t\t\t\t\t always lit invites a press that does nothing, and then the\n\t\t\t\t\t\t\t\t\t\t\t one that matters looks the same as the one that did not. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if changed}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={sheetBusy[item.id] || !dn.trim()}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => updateKept(item.id, item.sheet?.id ?? '', dn, dv)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-primary\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{sheetBusy[item.id] ? 'Updating…' : 'Update'}</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => item.sheet?.id && dropSheet(item.sheet.id)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-secondary\">Remove</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t{:else if item.sheet.id}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{@const kept = sheets.find((x) => x.id === item.sheet?.id)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- Only claim they are drawing while they are. This said it\n\t\t\t\t\t\t\t\t\t\t\t unconditionally, so a card whose six views had finished — or\n\t\t\t\t\t\t\t\t\t\t\t whose render had failed — went on promising them for ever. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"text-sm text-[var(--st-muted)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSaved as <span class=\"font-semibold text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{item.sheet.name}</span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>. Pick {item.sheet.kind === 'character' ? 'them' : 'it'} from\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-[var(--st-text)]\">+</span> in the box below{kept\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t?.sheet?.state === 'rendering'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? ' — the six views are still drawing.'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: kept?.sheet?.file\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? ' — the six views are ready on it.'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: '.'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- The voice, where the decision to keep them is made.\n\t\t\t\t\t\t\t\t\t\t\t It was written by the same call that wrote the description and\n\t\t\t\t\t\t\t\t\t\t\t it travels onto the sheet on save, but it was doing that\n\t\t\t\t\t\t\t\t\t\t\t invisibly: the first question asked of this card was \"where\n\t\t\t\t\t\t\t\t\t\t\t would I see it?\", which is the answer to whether it belongs\n\t\t\t\t\t\t\t\t\t\t\t here. Editable, so what is on screen is what gets kept. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t{#if item.sheet.kind === 'character' && item.sheet.voice !== undefined}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"block text-xs text-[var(--st-faint)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfor=\"char-voice-{item.id}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tHow they sound — carried into every clip they are in\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tid=\"char-voice-{item.id}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tbind:value={item.sheet.voice}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tspellcheck=\"false\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tmaxlength=\"240\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"mt-2 mb-4 w-full rounded-lg bg-[var(--st-bg)] px-3 py-2 text-sm outline-none focus:ring-0\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"block text-xs text-[var(--st-faint)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tfor=\"char-name-{item.id}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{item.sheet.kind === 'character' ? 'Name them' : 'Name it'} — this is what\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tthe picker will show\n\t\t\t\t\t\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"mt-2 flex flex-wrap items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tid=\"char-name-{item.id}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tbind:value={item.sheet.name}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tspellcheck=\"false\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"min-w-0 flex-1 rounded-lg bg-[var(--st-bg)] px-3 py-2 text-sm outline-none focus:ring-0\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={sheetBusy[item.id] || !item.sheet.name?.trim()}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => saveSubject(item.id)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-primary\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{sheetBusy[item.id]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Saving…'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: item.sheet.kind === 'character'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Save character'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'Save location'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-2 text-xs leading-relaxed text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tNot right? Say what to change in the chat — {item.sheet.kind ===\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'character'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'the same person is'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'the same place is'} kept and only what you name moves.\n\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t{:else if item.sheet.url}\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"mt-4 border-t border-[var(--st-line)] pt-4\">\n\t\t\t\t\t\t\t\t\t\t\t\t{#if item.sheet.id}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"text-sm text-[var(--st-muted)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tKept as <span class=\"font-semibold text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{item.sheet.name}</span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>. Every clip can use it from here on.\n\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"block text-xs text-[var(--st-faint)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tfor=\"sheet-name-{item.id}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tName it — this is what the picker will show\n\t\t\t\t\t\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"mt-2 flex flex-wrap items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tid=\"sheet-name-{item.id}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tbind:value={item.sheet.name}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tspellcheck=\"false\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"min-w-0 flex-1 rounded-lg bg-[var(--st-bg)] px-3 py-2 text-sm outline-none focus:ring-0\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={sheetBusy[item.id] || !item.sheet.name?.trim()}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => keepSheet(item.id)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-primary\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{sheetBusy[item.id] ? 'Keeping…' : 'Keep it'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-2 text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tKeep it now — the harness stops serving this image once the run's\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tworkspace shuts down.\n\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t</article>\n\t\t\t\t\t\t\t\t{:else if item.kind === 'shot' && item.shot}\n\t\t\t\t\t\t\t\t\t{@const n = item.shot.prompt.trim()\n\t\t\t\t\t\t\t\t\t\t? item.shot.prompt.trim().split(/\\s+/).length\n\t\t\t\t\t\t\t\t\t\t: 0}\n\t\t\t\t\t\t\t\t\t{@const picked = item.shot.loras ?? []}\n\t\t\t\t\t\t\t\t\t<article class=\"enter rounded-2xl bg-[var(--st-surface)] p-5 sm:p-6\">\n\t\t\t\t\t\t\t\t\t\t<div class=\"mb-3 flex items-baseline justify-between gap-4\">\n\t\t\t\t\t\t\t\t\t\t\t<h3 class=\"font-display text-base font-semibold\">\n\t\t\t\t\t\t\t\t\t\t\t\tThe prompt\n\t\t\t\t\t\t\t\t\t\t\t\t{#if item.shot.characterName || item.shot.locationName}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"ml-2 align-middle text-xs font-normal text-[var(--st-faint)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{item.shot.characterName ? `with ${item.shot.characterName}` : ''}{item\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.shot.characterName && item.shot.locationName\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? ' '\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: ''}{item.shot.locationName ? `in ${item.shot.locationName}` : ''}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"text-xs tabular-nums {n > 700\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'font-semibold text-[#e0a03a]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-faint)]'}\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{n} / 700 words\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t\t\t<!-- The literal text the workflow will receive. Editable, because the\n\t\t\t\t\t\t\t\t\t planning chain's render prompts were invisible and that is how it\n\t\t\t\t\t\t\t\t\t shipped briefs describing a face instead of a scene.\n\n\t\t\t\t\t\t\t\t\t Closed by default now. Seven hundred words is not something a\n\t\t\t\t\t\t\t\t\t person reads on the way past — it was ten rows of monospace\n\t\t\t\t\t\t\t\t\t standing between the operator and the button, and it got clicked\n\t\t\t\t\t\t\t\t\t past rather than read, which is worse than not showing it: it\n\t\t\t\t\t\t\t\t\t looks like it was checked. What they approved is the sentence\n\t\t\t\t\t\t\t\t\t above; this is the machine's version of it, one tap away for the\n\t\t\t\t\t\t\t\t\t one time in ten that something came back wrong. -->\n\t\t\t\t\t\t\t\t\t\t<details class=\"group\">\n\t\t\t\t\t\t\t\t\t\t\t<summary\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex cursor-pointer list-none items-center gap-2 py-1 text-xs text-[var(--st-faint)] transition-colors hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"text-[0.6rem] transition-transform group-open:rotate-90\"\n\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\">›</span\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<span>The words the crew gets</span>\n\t\t\t\t\t\t\t\t\t\t\t</summary>\n\t\t\t\t\t\t\t\t\t\t\t<label class=\"sr-only\" for=\"shot-{item.id}\">Render prompt</label>\n\t\t\t\t\t\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\t\t\t\t\t\tid=\"shot-{item.id}\"\n\t\t\t\t\t\t\t\t\t\t\t\tbind:value={item.shot.prompt}\n\t\t\t\t\t\t\t\t\t\t\t\trows=\"10\"\n\t\t\t\t\t\t\t\t\t\t\t\tspellcheck=\"false\"\n\t\t\t\t\t\t\t\t\t\t\t\treadonly={item.shot.launched}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"mt-2 block w-full resize-y rounded-xl bg-[var(--st-bg)] p-3 font-mono text-[13px] leading-relaxed text-[var(--st-text)] outline-none read-only:text-[var(--st-muted)]\"\n\t\t\t\t\t\t\t\t\t\t\t></textarea>\n\t\t\t\t\t\t\t\t\t\t</details>\n\n\t\t\t\t\t\t\t\t\t\t{#if item.shot.why}\n\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-2.5 text-xs text-[var(--st-faint)]\">{item.shot.why}</p>\n\t\t\t\t\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t\t\t\t\t<!-- What this clip renders with. The writer picks; you overrule it\n\t\t\t\t\t\t\t\t\t here, before the GPU rather than after. Two more adapters load on\n\t\t\t\t\t\t\t\t\t every clip regardless and are not listed — they are not choices.\n\t\t\t\t\t\t\t\t\t The cap is two: four at once produced a clip whose anatomy fell\n\t\t\t\t\t\t\t\t\t apart exactly where two adapters overlapped. -->\n\t\t\t\t\t\t\t\t\t\t<div class=\"mt-4 border-t border-[var(--st-line)] pt-3.5\">\n\t\t\t\t\t\t\t\t\t\t\t<!-- The always-loaded set, shown rather than hidden. Moving one of\n\t\t\t\t\t\t\t\t\t\t these into the base made it vanish off the card, which is how\n\t\t\t\t\t\t\t\t\t\t you end up asking for an adapter that is already running. They\n\t\t\t\t\t\t\t\t\t\t cannot be switched off — every clip is built on them — but the\n\t\t\t\t\t\t\t\t\t\t realism slider and the anatomy corrector are both worth a nudge,\n\t\t\t\t\t\t\t\t\t\t and doing that used to mean editing the catalogue. -->\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"mb-3\">\n\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"mb-2 text-xs text-[var(--st-faint)]\">always on</div>\n\t\t\t\t\t\t\t\t\t\t\t\t{#each BASE as l (l.key)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{@const at = item.shot.baseLoras?.[l.key] ?? l.strength}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"mt-1.5 flex items-center gap-3\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"w-40 shrink-0 truncate text-xs text-[var(--st-muted)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{l.label}</span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if item.shot.launched}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"flex-1 text-xs text-[var(--st-faint)] tabular-nums\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{at}</span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"range\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tmin=\"0\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tmax=\"2\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstep=\"0.05\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tvalue={at}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"{l.label} strength\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\toninput={(e) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetBaseStrength(item.id, l.key, Number(e.currentTarget.value))}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"h-1 min-w-0 flex-1 cursor-pointer accent-[var(--st-accent)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"back to {l.strength}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => resetLoraStrength(item.id, l.key)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"w-9 shrink-0 cursor-pointer text-right text-xs tabular-nums {at ===\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tl.strength\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'text-[var(--st-muted)]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-text)]'}\">{at.toFixed(2)}</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"w-24 shrink-0 text-right text-xs text-[var(--st-faint)] tabular-nums\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{l.band ? `${l.band[0]}–${l.band[1]}` : `author ${l.strength}`}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"mb-2 flex items-baseline gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-xs text-[var(--st-faint)]\">on top of that</span>\n\t\t\t\t\t\t\t\t\t\t\t\t{#if picked.length === 0}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-xs text-[var(--st-faint)]\">— none chosen</span>\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t{#if item.shot.launched}\n\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"flex flex-wrap gap-1.5\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{#each picked as p (p.key)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"rounded-md bg-[var(--st-bg)] px-2 py-0.5 text-xs text-[var(--st-muted)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{loraFor(p.key)?.label ?? p.key}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"tabular-nums opacity-60\">{p.strength}</span></span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"flex flex-wrap gap-1.5\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{#each CATALOGUE as l (l.key)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{@const on = picked.some((p) => p.key === l.key)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"{l.use}{l.trigger ? ` · trigger: ${l.trigger}` : ''}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-md px-2 py-0.5 text-xs transition-colors {on\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-[var(--st-surface-2)] font-semibold text-[var(--st-text)]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-muted)] hover:text-[var(--st-text)]'}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => toggleLora(item.id, l.key)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{l.label}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if on}<span class=\"tabular-nums opacity-70\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{picked.find((p) => p.key === l.key)?.strength}</span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t\t\t\t\t<!-- One slider per chosen adapter. The number starts on the\n\t\t\t\t\t\t\t\t\t\t\t author's own recommendation, which is the only figure here\n\t\t\t\t\t\t\t\t\t\t\t that came from someone rendering with it. The band beside it\n\t\t\t\t\t\t\t\t\t\t\t is what they published; the slider goes past it on purpose,\n\t\t\t\t\t\t\t\t\t\t\t because you can see the clip and they could not. -->\n\t\t\t\t\t\t\t\t\t\t\t\t{#each picked as p (p.key)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{@const l = loraFor(p.key)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{#if l}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"mt-2.5 flex items-center gap-3\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"w-40 shrink-0 truncate text-xs text-[var(--st-muted)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{l.label}</span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"range\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tmin=\"0\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tmax=\"2\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstep=\"0.05\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tvalue={p.strength}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"{l.label} strength\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\toninput={(e) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetLoraStrength(item.id, p.key, Number(e.currentTarget.value))}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"h-1 min-w-0 flex-1 cursor-pointer accent-[var(--st-accent)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"back to the author's recommendation, {l.strength}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => resetLoraStrength(item.id, p.key)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"w-9 shrink-0 cursor-pointer text-right text-xs tabular-nums {p.strength ===\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tl.strength\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'text-[var(--st-muted)]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-text)]'}\">{p.strength.toFixed(2)}</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"w-24 shrink-0 text-right text-xs text-[var(--st-faint)] tabular-nums\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{l.band ? `${l.band[0]}–${l.band[1]}` : `author ${l.strength}`}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t\t\t{#if !item.shot.launched}\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"mt-4 flex flex-wrap items-center gap-x-5 gap-y-3\">\n\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"flex items-center gap-1.5\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"mr-1 text-xs text-[var(--st-faint)]\">seconds</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{#each [5, 10, 15] as sec (sec)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-md px-2 py-0.5 text-xs tabular-nums transition-colors {item\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.shot.seconds === sec\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-[var(--st-surface-2)] font-semibold text-[var(--st-text)]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-muted)] hover:text-[var(--st-text)]'}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => setShotSeconds(item.id, sec)}>{sec}</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t<!-- Not offered on a continuation. The frame there is not a choice:\n\t\t\t\t\t\t\t\t\t\t\t it is whatever the clip being continued was shot at, and anything\n\t\t\t\t\t\t\t\t\t\t\t else produces two pieces that cannot be concatenated — which is\n\t\t\t\t\t\t\t\t\t\t\t discovered only after both have been rendered. A control whose\n\t\t\t\t\t\t\t\t\t\t\t every other setting breaks the thing it feeds is not a control. -->\n\t\t\t\t\t\t\t\t\t\t\t\t{#if item.shot.continues}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-xs text-[var(--st-faint)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{frameFor(item.shot.resolution ?? '576p', item.shot.orientation)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.width}x{frameFor(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\titem.shot.resolution ?? '576p',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\titem.shot.orientation\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t).height} · follows the clip before it</span\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"flex items-center gap-1.5\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- Unlike seconds and frame shape, this changes no words in\n\t\t\t\t\t\t\t\t\t\t\t\t\t the brief, so it is set in place and costs no rewrite. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"mr-1 text-xs text-[var(--st-faint)]\">size</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#each RES_KEYS as r (r)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{@const f = frameFor(r, item.shot.orientation)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"{f.width}x{f.height}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-md px-2 py-0.5 text-xs tabular-nums transition-colors {(item\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.shot.resolution ?? '576p') === r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-[var(--st-surface-2)] font-semibold text-[var(--st-text)]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-muted)] hover:text-[var(--st-text)]'}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (item.shot) item.shot.resolution = r;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}>{r}</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"flex items-center gap-1.5\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"mr-1 text-xs text-[var(--st-faint)]\">frame</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#each [['portrait', 'portrait'], ['landscape', 'landscape']] as [val, label] (val)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-md px-2 py-0.5 text-xs transition-colors {item\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.shot.orientation === val\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-[var(--st-surface-2)] font-semibold text-[var(--st-text)]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-muted)] hover:text-[var(--st-text)]'}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() =>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetShotOrientation(item.id, val as 'portrait' | 'landscape')}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{label}</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t\t\t\t<!-- The button states what it will do. How many clips is chosen\n\t\t\t\t\t\t\t\t\t\t once, in the composer, and confirmed here at the moment of\n\t\t\t\t\t\t\t\t\t\t spend — a second control asking the same question is how a card\n\t\t\t\t\t\t\t\t\t\t grows a settings panel. A continuation is always one: a second\n\t\t\t\t\t\t\t\t\t\t version would need the first one's clip as its reference, and a\n\t\t\t\t\t\t\t\t\t\t second camera angle on a shot that continues another would\n\t\t\t\t\t\t\t\t\t\t break the join it exists to make. -->\n\t\t\t\t\t\t\t\t\t\t\t<!-- The counts this card will actually spend. Versions apply to a\n\t\t\t\t\t\t\t\t\t\t continuation like any other shot — each take continues the same\n\t\t\t\t\t\t\t\t\t\t prior clip, so they are alternatives you choose between. Angles\n\t\t\t\t\t\t\t\t\t\t do not, when the seam is pinned: the first instant is nailed to\n\t\t\t\t\t\t\t\t\t\t the frame the last clip ended on and a second camera cannot\n\t\t\t\t\t\t\t\t\t\t start there. On a free start they apply again. -->\n\t\t\t\t\t\t\t\t\t\t\t{@const cardAngles =\n\t\t\t\t\t\t\t\t\t\t\t\titem.shot.continues && item.shot.continues.pinned !== false ? 1 : angles}\n\t\t\t\t\t\t\t\t\t\t\t{@const n = takes * cardAngles}\n\t\t\t\t\t\t\t\t\t\t\t<!-- Anything the check could not get the writer to fix, said once,\n\t\t\t\t\t\t\t\t\t\t directly above the button that spends the money. Not a warning\n\t\t\t\t\t\t\t\t\t\t dialog and not a block: the brief renders, and this is what to\n\t\t\t\t\t\t\t\t\t\t look at if the clip comes back wrong. The way out is already\n\t\t\t\t\t\t\t\t\t\t here — \"write it again\" is the next control along. -->\n\t\t\t\t\t\t\t\t\t\t\t{#if item.shot.warn?.length}\n\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-4 text-xs leading-relaxed text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{item.shot.warn.join(' · ')}\n\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t<!-- A fault the check caught and the writer then fixed. Said in the\n\t\t\t\t\t\t\t\t\t\t past tense because there is nothing to do about it — it is here\n\t\t\t\t\t\t\t\t\t\t because the brief took twice as long to arrive and the wait\n\t\t\t\t\t\t\t\t\t\t otherwise looks like the writer being slow. -->\n\t\t\t\t\t\t\t\t\t\t\t{#if item.shot.fixed?.length}\n\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-4 text-xs leading-relaxed text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\tújraírva — {item.shot.fixed.join(' · ')}\n\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"mt-5 flex flex-wrap items-center gap-2.5\">\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={shotBusy[item.id]}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-primary\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() =>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tn > 1 ? renderBatch(item.id, takes, cardAngles) : renderShot(item.id)}\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{shotBusy[item.id]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'starting…'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: n > 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? `render ${countLabel(takes, cardAngles)}`\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'render this'}\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={shotBusy[item.id]}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-quiet\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => rewriteShot(item.id)}>write it again</button\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<!-- The same price the confirmation quotes, on the other button\n\t\t\t\t\t\t\t\t\t\t\t that spends it. `n` is this card's count, so a batch says what\n\t\t\t\t\t\t\t\t\t\t\t a batch costs. -->\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{priceLabel(tokensForSend(composerShape.seconds, n, freeClip), freeClip)}\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t</article>\n\t\t\t\t\t\t\t\t{:else if item.kind === 'plan' && item.plan}\n\t\t\t\t\t\t\t\t\t<article class=\"enter rounded-2xl bg-[var(--st-surface)] p-5 sm:p-6\">\n\t\t\t\t\t\t\t\t\t\t{#if editingPlan && item.id === latestPlanId}\n\t\t\t\t\t\t\t\t\t\t\t<label class=\"sr-only\" for=\"edit-title\">Title</label>\n\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\tid=\"edit-title\"\n\t\t\t\t\t\t\t\t\t\t\t\tbind:value={editTitle}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"w-full rounded-xl border border-[var(--st-line)] bg-[var(--st-bg)] px-3.5 py-2.5 font-display text-lg font-semibold outline-none focus:border-[var(--st-muted)]\"\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t<label class=\"sr-only\" for=\"edit-story\">Story</label>\n\t\t\t\t\t\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\t\t\t\t\t\tid=\"edit-story\"\n\t\t\t\t\t\t\t\t\t\t\t\tbind:value={editStory}\n\t\t\t\t\t\t\t\t\t\t\t\trows=\"12\"\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"mt-3 block w-full resize-y rounded-xl border border-[var(--st-line)] bg-[var(--st-bg)] p-4 text-[0.95rem] leading-[1.75] outline-none focus:border-[var(--st-muted)]\"\n\t\t\t\t\t\t\t\t\t\t\t></textarea>\n\t\t\t\t\t\t\t\t\t\t\t<label class=\"sr-only\" for=\"edit-style\">Look</label>\n\t\t\t\t\t\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\t\t\t\t\t\tid=\"edit-style\"\n\t\t\t\t\t\t\t\t\t\t\t\tbind:value={editStyle}\n\t\t\t\t\t\t\t\t\t\t\t\trows=\"2\"\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"mt-3 block w-full resize-y rounded-xl border border-[var(--st-line)] bg-[var(--st-bg)] p-4 text-sm leading-relaxed outline-none focus:border-[var(--st-muted)]\"\n\t\t\t\t\t\t\t\t\t\t\t></textarea>\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"mt-4 flex items-center gap-3\">\n\t\t\t\t\t\t\t\t\t\t\t\t<button type=\"button\" onclick={saveEdit} class=\"btn btn-primary\">\n\t\t\t\t\t\t\t\t\t\t\t\t\tsave\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => (editingPlan = false)}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer px-2 py-2.5 text-xs text-[var(--st-muted)] hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\tcancel\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t<h3 class=\"font-display text-lg leading-snug font-semibold tracking-tight\">\n\t\t\t\t\t\t\t\t\t\t\t\t{item.plan.title}\n\t\t\t\t\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t\t\t\t\t\t<!-- The voice the model picked. Your sentence rarely specifies one,\n\t\t\t\t\t\t\t\t\t     so this is a decision taken on your behalf — and seeing it named\n\t\t\t\t\t\t\t\t\t     is what lets you disagree with it in one line, rather than\n\t\t\t\t\t\t\t\t\t     reverse-engineering it from four hundred words of prose. -->\n\t\t\t\t\t\t\t\t\t\t\t{#if item.plan.register}\n\t\t\t\t\t\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"mt-1.5 font-mono text-[11px] tracking-wide text-[var(--st-faint)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{item.plan.register}\n\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t<!-- The summary, not the story. Four hundred words of prose is\n\t\t\t\t\t\t\t\t\t     what the crew needs and not what a person reads before deciding\n\t\t\t\t\t\t\t\t\t     whether this is the film they asked for — and asking them to\n\t\t\t\t\t\t\t\t\t     read it to find out buries the decision under the material.\n\t\t\t\t\t\t\t\t\t     The story is one click away and entirely unchanged. -->\n\t\t\t\t\t\t\t\t\t\t\t<p class=\"doc mt-3 text-[0.95rem] leading-[1.75] text-[var(--st-text)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t{item.plan.summary || item.plan.story}\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-3 text-sm leading-relaxed text-[var(--st-muted)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t{item.plan.style}\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t{#if item.plan.summary}\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"mt-3 cursor-pointer text-xs text-[var(--st-muted)] underline-offset-4 hover:text-[var(--st-text)] hover:underline\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => (expanded[item.id] = !expanded[item.id])}\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{expanded[item.id] ? 'hide the full story' : 'read the full story'}\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t{#if expanded[item.id]}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"doc enter mt-3 rounded-xl bg-[var(--st-bg)] px-4 py-3.5 text-[0.95rem] leading-[1.75] text-[var(--st-muted)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{item.plan.story}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-3 text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t{item.plan.sceneCount} scenes\n\t\t\t\t\t\t\t\t\t\t\t\t{#if item.id === latestPlanId && !planningWs}\n\t\t\t\t\t\t\t\t\t\t\t\t\t· refine it by typing in the chat\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t{#if item.id === latestPlanId && !planningWs}\n\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"mt-5 flex flex-wrap items-center gap-3\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={launchingPlanning}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={launchPlanning}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-primary\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{launchingPlanning ? 'starting…' : 'start'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={launchingPlanning}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={openEdit}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer px-2 py-2.5 text-sm text-[var(--st-muted)] transition-colors hover:text-[var(--st-text)] disabled:cursor-default disabled:opacity-50\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tedit\n\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t{:else if item.id === latestPlanId}\n\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-4 text-xs text-[var(--st-faint)]\">started</p>\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t</article>\n\t\t\t\t\t\t\t\t{:else if item.kind === 'artifact' && item.artifact}\n\t\t\t\t\t\t\t\t\t{@const art = item.artifact}\n\t\t\t\t\t\t\t\t\t{@const isCurrent = latestDocItem[art.key] === item.id}\n\t\t\t\t\t\t\t\t\t{@const phase = docPhase[art.key]}\n\t\t\t\t\t\t\t\t\t<article class=\"enter rounded-2xl bg-[var(--st-surface)] p-5 sm:p-6\">\n\t\t\t\t\t\t\t\t\t\t<h3 class=\"font-display text-base font-semibold\">{art.title}</h3>\n\t\t\t\t\t\t\t\t\t\t{#if art.body}\n\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"relative mt-3\"\n\t\t\t\t\t\t\t\t\t\t\t\tclass:clamp={isLong(art.body) && !expanded[item.id]}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{@render document(renderDocument(art.files[0]?.name ?? '', art.body))}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t{#if isLong(art.body)}\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"mt-3 cursor-pointer text-xs text-[var(--st-muted)] underline-offset-4 hover:text-[var(--st-text)] hover:underline\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => (expanded[item.id] = !expanded[item.id])}\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{expanded[item.id] ? 'collapse' : 'more'}\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t<!-- The text could not be read — the file itself is offered\n\t\t\t\t\t\t\t\t\t     instead of an empty card. Same-origin route, but not a\n\t\t\t\t\t\t\t\t\t     SvelteKit page, hence the lint exception. -->\n\t\t\t\t\t\t\t\t\t\t\t{#each art.files as f (f.name)}\n\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-2 text-sm text-[var(--st-muted)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- eslint-disable-next-line svelte/no-navigation-without-resolve -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\t\t\t\t\thref={f.url}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\trel=\"noreferrer\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"underline underline-offset-4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{f.name}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t\t\t\t\t{#if isCurrent && !renderWs}\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"mt-4 border-t border-[var(--st-line)] pt-4\">\n\t\t\t\t\t\t\t\t\t\t\t\t{#if phase === 'regen'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"text-xs text-[var(--st-muted)]\">regenerating</p>\n\t\t\t\t\t\t\t\t\t\t\t\t{:else if docAccepted[art.key]}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"text-xs text-[var(--st-faint)]\">elfogadva</p>\n\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"flex flex-wrap items-center gap-3\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => (docAccepted[art.key] = true)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-secondary btn-sm\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tok\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => (changeOpen[item.id] = !changeOpen[item.id])}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer px-1 py-2 text-xs text-[var(--st-muted)] transition-colors hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trequest a change\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{#if changeOpen[item.id]}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<form\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"mt-3 flex gap-2\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonsubmit={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trequestChange(item.id, art.key);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<label class=\"sr-only\" for=\"change-{item.id}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>What should change</label\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tid=\"change-{item.id}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tbind:value={changeText[item.id]}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tplaceholder=\"what should change in this document\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"min-w-0 flex-1 rounded-xl border border-[var(--st-line)] bg-[var(--st-bg)] px-3.5 py-2.5 text-sm outline-none placeholder:text-[var(--st-faint)] focus:border-[var(--st-muted)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={changeBusy[item.id] ||\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t!(changeText[item.id] ?? '').trim()}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-primary\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsend\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</form>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-2 text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tThis step is rewritten, and everything built on it is refreshed after\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tit.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t</article>\n\t\t\t\t\t\t\t\t{:else if item.kind === 'approval'}\n\t\t\t\t\t\t\t\t\t<!-- A second way to start the shoot. The board has the real one, and\n\t\t\t\t\t\t\t     this is deliberately duplicate: for one evening the button lived\n\t\t\t\t\t\t\t     only there, the board failed to post, and an otherwise finished\n\t\t\t\t\t\t\t     plan could not be approved by any means at all. A control that\n\t\t\t\t\t\t\t     gates the entire run should not have exactly one home. -->\n\t\t\t\t\t\t\t\t\t<!-- Text only. The board above already lists the five documents and\n\t\t\t\t\t\t\t     opens each one; repeating that here put the same list twice on\n\t\t\t\t\t\t\t     one screen, and the button that matters ended up below the\n\t\t\t\t\t\t\t     duplicate rather than beside the thing it acts on. Both now live\n\t\t\t\t\t\t\t     on the board. -->\n\t\t\t\t\t\t\t\t\t<p class=\"enter doc text-[0.95rem] leading-[1.75] text-[var(--st-text)]\">\n\t\t\t\t\t\t\t\t\t\t{item.text}\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t{:else if item.kind === 'takes' && item.takes && !item.artifact}\n\t\t\t\t\t\t\t\t\t<!-- A batch nobody has chosen from yet: the strip, and nothing else.\n\t\t\t\t\t\t\t\t No verdict row and no continue here — those belong to the take you\n\t\t\t\t\t\t\t\t keep, and offering them on four clips at once would ask for four\n\t\t\t\t\t\t\t\t answers to a question that has one. -->\n\t\t\t\t\t\t\t\t\t{@const t = item.takes}\n\t\t\t\t\t\t\t\t\t{@const live = t.runs.filter((r) => r.state === 'rendering').length}\n\t\t\t\t\t\t\t\t\t{@const done = t.runs.filter((r) => r.state === 'ready')}\n\t\t\t\t\t\t\t\t\t{@const gone = t.runs.filter((r) => r.state === 'failed')}\n\t\t\t\t\t\t\t\t\t{@const row = done[0]?.clip ? logRow[done[0].clip.workspace] : undefined}\n\t\t\t\t\t\t\t\t\t<div class=\"enter\">\n\t\t\t\t\t\t\t\t\t\t<p class=\"text-[0.95rem] leading-[1.75] text-[var(--st-text)]\">\n\t\t\t\t\t\t\t\t\t\t\t{#if live}\n\t\t\t\t\t\t\t\t\t\t\t\t{t.runs.length} clips of this shot, rendering together.\n\t\t\t\t\t\t\t\t\t\t\t{:else if done.length}\n\t\t\t\t\t\t\t\t\t\t\t\t{done.length}\n\t\t\t\t\t\t\t\t\t\t\t\t{done.length === 1 ? 'clip' : 'clips'} of this shot.\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-[var(--st-faint)]\">Press one to look properly.</span>\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t</p>\n\n\t\t\t\t\t\t\t\t\t\t<!-- The grid follows the count rather than always being four: two\n\t\t\t\t\t\t\t\t\t takes in a four-column grid leave half the row empty and the card\n\t\t\t\t\t\t\t\t\t reads as broken. Two across on a phone whatever the count — at\n\t\t\t\t\t\t\t\t\t 375px four tiles are 77 wide, which is too small to press, and\n\t\t\t\t\t\t\t\t\t two are 161. -->\n\t\t\t\t\t\t\t\t\t\t<div class=\"st-takes mt-3\" data-n={t.runs.length}>\n\t\t\t\t\t\t\t\t\t\t\t{#each t.runs as run (run.slug)}\n\t\t\t\t\t\t\t\t\t\t\t\t{@render takeTile(item, run)}\n\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\tclass=\"mt-3 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-[var(--st-faint)]\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{#if live}\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"beacon size-1.5 shrink-0 rounded-full bg-[var(--st-accent)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t></span>\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"tabular-nums\">{done.length} of {t.runs.length} landed</span>\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t<!-- One row describes all of them: the takes differ by seed and by\n\t\t\t\t\t\t\t\t\t\t nothing else, so the size, the length and the adapters are the\n\t\t\t\t\t\t\t\t\t\t same sentence four times over. -->\n\t\t\t\t\t\t\t\t\t\t\t{#if row}\n\t\t\t\t\t\t\t\t\t\t\t\t{#if row.launched?.length}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-[var(--st-muted)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{row.launched\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.map((p) => `${loraFor(p.key)?.label ?? p.key} ${p.strength}`)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.join(' · ')}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"tabular-nums\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>{row.steps} steps · {row.width}×{row.height} · {row.fps}fps · {row.seconds}s</span\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t\t\t{#if !live && !done.length}\n\t\t\t\t\t\t\t\t\t\t\t<p class=\"text-[0.95rem] leading-[1.75] text-[var(--st-text)]\">\n\t\t\t\t\t\t\t\t\t\t\t\tNone of them finished.\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t{#each gone as g (g.slug)}\n\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-1 text-xs leading-relaxed text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\tClip {g.index} — {g.error || 'no reason given'}\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{:else if (item.kind === 'clips' || item.kind === 'takes') && item.artifact}\n\t\t\t\t\t\t\t\t\t{@const ws = item.artifact.workspace ?? ''}\n\t\t\t\t\t\t\t\t\t{@const v = verdict[ws]}\n\t\t\t\t\t\t\t\t\t<!-- A caption only where it says more than the title does. An assembled\n\t\t\t\t\t\t\t     thing has a length and a part count worth reading — \"The film — 3\n\t\t\t\t\t\t\t     clips, 23s.\" — and a single clip has a name that repeats what the\n\t\t\t\t\t\t\t     card already is. Those two used to look the same.\n\n\t\t\t\t\t\t\t     A takes card never gets one. Not writing the caption at keep time\n\t\t\t\t\t\t\t     was not enough: the text lives in the saved transcript, so every\n\t\t\t\t\t\t\t     card kept before that change went on showing \"Take 1, kept.\" after\n\t\t\t\t\t\t\t     it. The rule has to hold for what is already on disk, not only for\n\t\t\t\t\t\t\t     what is written next.\n\n\t\t\t\t\t\t\t     Up here with the other consts because {@const} must be a block's\n\t\t\t\t\t\t\t     immediate child — this file's own rule, and I had just put it inside\n\t\t\t\t\t\t\t     a div. -->\n\t\t\t\t\t\t\t\t\t<!-- A turnaround is a reference picture that happens to be a video:\n\t\t\t\t\t\t\t     there is nothing to continue from it, nothing to put in a film,\n\t\t\t\t\t\t\t     and no verdict to give it — it either resembles the character or\n\t\t\t\t\t\t\t     it is redrawn from the card. Offering the scene band under it put\n\t\t\t\t\t\t\t     four controls on screen that all lead somewhere wrong. -->\n\t\t\t\t\t\t\t\t\t{@const scenic = item.artifact.key !== 'turnaround'}\n\t\t\t\t\t\t\t\t\t<!-- No caption on a turnaround either. It sat directly under the\n\t\t\t\t\t\t\t     character's own card and said, in a full sentence, what the picture\n\t\t\t\t\t\t\t     above it had already said — and with a long descriptive name in the\n\t\t\t\t\t\t\t     middle of it, \"The turn ultra slim body, flat breast was built\n\t\t\t\t\t\t\t     from.\" reads as broken English rather than a label.\n\n\t\t\t\t\t\t\t     Written into the transcript, so cards kept before this change carry\n\t\t\t\t\t\t\t     it too; the rule has to hold for what is on disk, not only for what\n\t\t\t\t\t\t\t     is written next. -->\n\t\t\t\t\t\t\t\t\t{@const said =\n\t\t\t\t\t\t\t\t\t\tscenic &&\n\t\t\t\t\t\t\t\t\t\titem.kind !== 'takes' &&\n\t\t\t\t\t\t\t\t\t\titem.text &&\n\t\t\t\t\t\t\t\t\t\titem.text !== item.artifact.title\n\t\t\t\t\t\t\t\t\t\t\t? item.text\n\t\t\t\t\t\t\t\t\t\t\t: ''}\n\t\t\t\t\t\t\t\t\t<div class=\"enter\">\n\t\t\t\t\t\t\t\t\t\t<div class=\"mt-3 overflow-hidden rounded-2xl bg-[var(--st-surface)]\">\n\t\t\t\t\t\t\t\t\t\t\t{#each item.artifact.files as f (f.name)}\n\t\t\t\t\t\t\t\t\t\t\t\t{@render videoCard(f.name, f.url, said, ws)}\n\t\t\t\t\t\t\t\t\t\t\t{/each}\n\n\t\t\t\t\t\t\t\t\t\t\t<!-- Everything you can do with a finished clip, in one band.\n\t\t\t\t\t\t\t\t     It used to be five: a caption, a line of render settings, \"how\n\t\t\t\t\t\t\t\t     was it?\", the buttons, and a paragraph. Five stacked rows are a\n\t\t\t\t\t\t\t\t     list, not a hierarchy, and the eye had nowhere to land.\n\n\t\t\t\t\t\t\t\t     One row now. What carries the work forward is filled and first;\n\t\t\t\t\t\t\t\t     the verdict sits at the far end, quiet, because it is asked\n\t\t\t\t\t\t\t\t     rather than inferred and should not stand between you and the\n\t\t\t\t\t\t\t\t     next clip. It is still a real action — \"not good\" is what starts\n\t\t\t\t\t\t\t\t     the diagnosis, not a survey answer.\n\n\t\t\t\t\t\t\t\t     The render settings go behind a disclosure. Nine times in ten\n\t\t\t\t\t\t\t\t     nobody wants them; the tenth time a clip came back wrong and the\n\t\t\t\t\t\t\t\t     seed and the adapters are exactly what is needed, so they are one\n\t\t\t\t\t\t\t\t     tap away rather than gone. -->\n\t\t\t\t\t\t\t\t\t\t\t{#if ws && scenic}\n\t\t\t\t\t\t\t\t\t\t\t\t{@const ci = contInfo(ws)}\n\t\t\t\t\t\t\t\t\t\t\t\t{@const chain = chainOf(ws)}\n\t\t\t\t\t\t\t\t\t\t\t\t{@const others =\n\t\t\t\t\t\t\t\t\t\t\t\t\titem.kind === 'takes' && item.takes\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? readyTakes(item.id).filter((r) => r.index !== item.takes?.kept)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: []}\n\t\t\t\t\t\t\t\t\t\t\t\t{@const v = verdict[ws]}\n\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"flex flex-col gap-2 px-4 pt-3 pb-4\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"flex flex-wrap items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={!ci.ok}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => startContinue(item)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-primary btn-sm\">Continue</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if filmPart(item.artifact)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if inFilm(item.artifact)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex items-center gap-1.5 px-1 text-xs text-[var(--st-muted)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span aria-hidden=\"true\">✓</span><span>In the film</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => addToFilm(item)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-secondary btn-sm\">Add to film</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if chain.length > 1}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={joining[ws]}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => joinScene(ws)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-secondary btn-sm\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{joining[ws]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Joining…'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: `The whole scene · ${chain.length}`}</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if others.length}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={(e) => openTake(item.id, others[0].index, e.currentTarget)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-quiet btn-sm\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{others.length === 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Other take'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: `${others.length} other takes`}</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- Pushed to the far end. Asked, not inferred, and not on the\n\t\t\t\t\t\t\t\t\t\t\t     path to the next clip. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"ml-auto flex items-center gap-0.5\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if !v}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => rate(ws, 'kept')}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-quiet btn-sm\">Good</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => rate(ws, 'rejected')}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-quiet btn-sm\">Not good</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else if v === 'kept'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"px-2 text-xs text-[var(--st-faint)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>Noted as good</span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else if diagnosing[ws]}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex items-center gap-2 px-2 text-xs text-[var(--st-faint)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"beacon size-1.5 shrink-0 rounded-full bg-[var(--st-accent)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t></span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span>Working out the fix</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else if !fix[ws]}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if diagnoseError[ws]}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"px-2 text-xs text-[var(--st-muted)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{diagnoseError[ws]}</span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => diagnose(ws)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-secondary btn-sm\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{diagnoseError[ws] ? 'Look again' : 'Work out why'}</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t{#if !ci.ok}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"text-xs leading-relaxed text-[var(--st-faint)]\">{ci.why}</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{:else if !ci.exact}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tContinues from a frame of this clip.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t\t\t{#if ws && scenic}\n\t\t\t\t\t\t\t\t\t\t\t{#if fix[ws]}\n\t\t\t\t\t\t\t\t\t\t\t\t{@const f = fix[ws]}\n\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"mt-2.5 rounded-2xl bg-[var(--st-surface)] p-4\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"text-[13px] leading-relaxed text-[var(--st-text)]\">{f.why}</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{#if f.loras?.length}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-2 text-xs text-[var(--st-muted)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnext attempt with {f.loras\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.map((p) => `${loraFor(p.key)?.label ?? p.key} ${p.strength}`)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.join(' · ')}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"mt-3.5 flex flex-wrap items-center gap-2.5\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={fixBusy[ws]}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-primary\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => renderFix(ws)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{fixBusy[ws] ? 'starting…' : 'render the fix'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-full px-3 py-2 text-xs text-[var(--st-faint)] transition-colors hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => openFix(ws)}>read the brief first</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-full px-3 py-2 text-xs text-[var(--st-faint)] transition-colors hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => diagnose(ws)}>look again</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t{/each}\n\n\t\t\t\t\t\t\t{#if sending}\n\t\t\t\t\t\t\t\t<!-- A word alone reads as frozen once it has been on screen for\n\t\t\t\t\t\t\t ten seconds. The counter is the proof that something is still\n\t\t\t\t\t\t\t happening, and it makes a stall visible as a stall. -->\n\t\t\t\t\t\t\t\t<p class=\"flex items-center gap-2.5 text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t<span class=\"beacon size-1.5 shrink-0 rounded-full bg-[var(--st-accent)]\"></span>\n\t\t\t\t\t\t\t\t\t<span>{sendingWhat}</span>\n\t\t\t\t\t\t\t\t\t{#if sendingFor > 1}\n\t\t\t\t\t\t\t\t\t\t<span class=\"tabular-nums\">{sendingFor}s</span>\n\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t{#if typicalPrompt && sendingFor > 2}\n\t\t\t\t\t\t\t\t\t\t<span aria-hidden=\"true\">·</span>\n\t\t\t\t\t\t\t\t\t\t<span>{promptOverdue ? 'longer than usual' : typicalLabel(typicalPrompt)}</span>\n\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t\t{#if showExamples}\n\t\t\t\t\t\t\t\t<!-- Three of them, one row, equal width — a set the eye reads as a\n\t\t\t\t\t\t\t set. Stacked full-width pills made three sentences of different\n\t\t\t\t\t\t\t lengths look like three unrelated things, and the longest one\n\t\t\t\t\t\t\t decided the shape of the block. -->\n\t\t\t\t\t\t\t\t{@render starters()}\n\t\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t\t<div bind:this={bottomEl}></div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t{/if}\n\n\t\t\t\t\t<!-- ── the composer — pinned, outside the scrolling region ── -->\n\t\t\t\t\t<div class=\"composerbox relative shrink-0 pt-3 pb-4\">\n\t\t\t\t\t\t{#if !atBottom}\n\t\t\t\t\t\t\t<!-- Scrolled up and reading? New messages must not yank the view.\n\t\t\t\t\t\t\t This is the way back down, the way every chat client offers it. -->\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclass=\"enter absolute -top-9 left-1/2 z-10 -translate-x-1/2 cursor-pointer rounded-full bg-[var(--st-surface-2)] px-3.5 py-1.5 text-xs text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\tonclick={() => scrollToBottom('smooth')}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tlatest ↓\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t<!-- Not on the stage, which says this itself and says it right. Two\n\t\t\t\t\t\t clocks for one render is one too many, and this one cannot tell that\n\t\t\t\t\t\t the clip already arrived — it counts while the poll is alive, so a\n\t\t\t\t\t\t session left open reads \"116m · longer than usual\" over a finished\n\t\t\t\t\t\t take. -->\n\t\t\t\t\t\t{#if pollingActive && startedAt && !STAGE_UI}\n\t\t\t\t\t\t\t<!-- Live status: the dot says something is happening, the clock says\n\t\t\t\t\t\t\t how long, and the label says what — the three things a reader\n\t\t\t\t\t\t\t waiting twenty minutes actually wants. Opacity-only pulse: the\n\t\t\t\t\t\t\t house rules forbid glows. -->\n\t\t\t\t\t\t\t<p class=\"mb-2 flex items-center gap-2.5 text-xs text-[var(--st-muted)]\">\n\t\t\t\t\t\t\t\t<span class=\"beacon size-1.5 shrink-0 rounded-full bg-[var(--st-accent)]\"></span>\n\t\t\t\t\t\t\t\t<span class=\"tabular-nums\">{elapsedLabel(now - startedAt)}</span>\n\t\t\t\t\t\t\t\t{#if simpleRun && typicalClip}\n\t\t\t\t\t\t\t\t\t<!-- The other half of the sentence. `4m 59s` alone cannot tell\n\t\t\t\t\t\t\t\t\t you whether the answer is due at five minutes or at twenty,\n\t\t\t\t\t\t\t\t\t and that is the whole difference between waiting and\n\t\t\t\t\t\t\t\t\t wondering whether the page has hung. -->\n\t\t\t\t\t\t\t\t\t<span class=\"text-[var(--st-faint)]\">·</span>\n\t\t\t\t\t\t\t\t\t<span class=\"text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t{clipOverdue ? 'longer than usual' : typicalLabel(typicalClip)}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t{#if railRunning}\n\t\t\t\t\t\t\t\t\t<span class=\"text-[var(--st-faint)]\">·</span>\n\t\t\t\t\t\t\t\t\t<span class=\"min-w-0 truncate\">{friendly(railRunning.label)}</span>\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t<!-- A character being drawn belongs in the same slot as everything else\n\t\t\t\t\t     that is happening, which is here: pinned above the box, the way a\n\t\t\t\t\t     chat client reports its own work. It was inside the character's\n\t\t\t\t\t     card, where it scrolls out of sight in a minute and then there is\n\t\t\t\t\t     nothing on screen saying a GPU is busy.\n\n\t\t\t\t\t     Not an {:else} of the render line above. They are different work and\n\t\t\t\t\t     both can be running — a clip shooting while a character draws — and\n\t\t\t\t\t     hiding one behind the other would make the quieter one a mystery.\n\n\t\t\t\t\t     What used to be here was \"not running · started yesterday\", which is\n\t\t\t\t\t     a line saying nothing is happening. Silence already says that, and\n\t\t\t\t\t     it said it about a `startedAt` days old, next to a box you were\n\t\t\t\t\t     about to type in. -->\n\t\t\t\t\t\t<!-- Everything under the stage shares the composer's measure: the reference\n\t\t\t\t\t\t\t     chips, the hint line and the film reel line up with the box they belong\n\t\t\t\t\t\t\t     to instead of running the width of the column over it. -->\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t<div class={STAGE_UI ? 'mx-auto w-full' : ''} style={composerCap}>\n\t\t\t\t\t\t\t\t<!-- The staged pictures with a line to describe each. The simple studio\n\t\t\t\t\t\t\t\t\t draws them as chips inside the box instead — see below — because\n\t\t\t\t\t\t\t\t\t the line read as an error: people clicked it, then took the picture\n\t\t\t\t\t\t\t\t\t off again. What reads the descriptions is the production planner,\n\t\t\t\t\t\t\t\t\t and that is only here. -->\n\t\t\t\t\t\t\t\t{#if refFiles.length && !(STAGE_UI && mode === 'simple')}\n\t\t\t\t\t\t\t\t\t<div class=\"mb-2 space-y-1.5\">\n\t\t\t\t\t\t\t\t\t\t{#each refFiles as f (f.id)}\n\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex items-center gap-2 rounded-xl bg-[var(--st-surface)] px-3 py-2\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"max-w-[9rem] shrink-0 truncate font-mono text-[11px] text-[var(--st-muted)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{f.name}\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\t\tvalue={f.description}\n\t\t\t\t\t\t\t\t\t\t\t\t\tplaceholder=\"what is this — the crew cannot see the file, only this line\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tonchange={(e) => describeRefFile(f.id, e.currentTarget.value)}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"min-w-0 flex-1 border-0 bg-transparent text-xs outline-none placeholder:text-[var(--st-faint)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"remove {f.name}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"shrink-0 cursor-pointer px-1 text-xs text-[var(--st-faint)] hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => dropRef(f.id)}\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t×\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t{#if stopNotice}\n\t\t\t\t\t\t\t\t\t<!-- What a stop did, and what happened to the tokens — said on the\n\t\t\t\t\t\t\t\t\t surface the operator is looking at, and kept until they close it. -->\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\trole=\"status\"\n\t\t\t\t\t\t\t\t\t\tclass=\"mb-2 flex items-start gap-2 rounded-xl bg-[var(--st-surface-2)] px-3 py-2 text-sm leading-snug text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<p class=\"min-w-0 flex-1\">{stopNotice}</p>\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\taria-label=\"dismiss\"\n\t\t\t\t\t\t\t\t\t\t\tclass=\"shrink-0 cursor-pointer px-1 text-[var(--st-muted)] hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\tonclick={() => (stopNotice = '')}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t×\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t{#if refError}\n\t\t\t\t\t\t\t\t\t<!-- Said where the attaching happened, at reading size. This was a\n\t\t\t\t\t\t\t\t\t muted 12px line the eye skipped, and for a while it carried a raw\n\t\t\t\t\t\t\t\t\t exception; a failed attach is the thing the operator most needs to\n\t\t\t\t\t\t\t\t\t notice, so it reads as text and goes away when dismissed. -->\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\trole=\"alert\"\n\t\t\t\t\t\t\t\t\t\tclass=\"mb-2 flex items-start gap-2 rounded-xl bg-[var(--st-surface-2)] px-3 py-2 text-sm leading-snug text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<p class=\"min-w-0 flex-1\">{refError}</p>\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\taria-label=\"dismiss\"\n\t\t\t\t\t\t\t\t\t\t\tclass=\"shrink-0 cursor-pointer px-1 text-[var(--st-muted)] hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\tonclick={() => (refError = '')}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t×\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t<!-- The hint line was a full-width row with an empty right half, so the\n\t\t\t\t\t\t film costs no height at all. It belongs in this band and not among\n\t\t\t\t\t\t the composer's setting chips: there it read as a parameter, which\n\t\t\t\t\t\t is not what it is, and nobody looked for it. -->\n\t\t\t\t\t\t\t\t<!-- Only when it has something in it. The reserved 1.6rem stopped the\n\t\t\t\t\t\t composer jumping when a hint appeared and went away, which was worth\n\t\t\t\t\t\t it when this row was usually full. It is not: with no hint and no\n\t\t\t\t\t\t film it is an empty band holding the status line a centimetre clear\n\t\t\t\t\t\t of the box it is reporting on. -->\n\t\t\t\t\t\t\t\t<!-- Standing on the stage for the same reason the reel's band is: this row\n\t\t\t\t\t\t\t\t     appears the moment there is a film to count, and appearing is height, and\n\t\t\t\t\t\t\t\t     height is the picture's width. Off the stage it keeps its old behaviour,\n\t\t\t\t\t\t\t\t     where an empty row is only an empty row. -->\n\n\t\t\t\t\t\t\t\t<!-- The reel's room is standing, whether or not there is a reel in it.\n\t\t\t\t\t\t\t\t     The picture on the stage is height-bound — it is 16:9 filling whatever\n\t\t\t\t\t\t\t\t     height is left — and the composer is capped to the picture's width. So a\n\t\t\t\t\t\t\t\t     row appearing above the composer took height off the stage, which took\n\t\t\t\t\t\t\t\t     width off the picture, which took width off the composer: adding one clip\n\t\t\t\t\t\t\t\t     to the film visibly shrank both surfaces and rewrapped the text between\n\t\t\t\t\t\t\t\t     them. Nothing about adding a clip should resize the thing you are looking\n\t\t\t\t\t\t\t\t     at.\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t     So the band is always there and the reel opens into it. It costs the\n\t\t\t\t\t\t\t\t     picture a fixed slice of height for the whole session rather than a\n\t\t\t\t\t\t\t\t     variable one at the moment you act, which is the trade this surface keeps\n\t\t\t\t\t\t\t\t     making: the rail's space is held open for the same reason. Only on the\n\t\t\t\t\t\t\t\t     stage — with the transcript there is no height-bound picture to protect\n\t\t\t\t\t\t\t\t     and an empty band is just a gap. -->\n\t\t\t\t\t\t\t\t{#if STAGE_UI && mode === 'simple'}\n\t\t\t\t\t\t\t\t\t<!-- The reel sits at the BOTTOM of its standing room, not the top.\n\t\t\t\t\t\t\t\t\t Held open the band is taller than the row inside it, and a row pinned\n\t\t\t\t\t\t\t\t\t to the top left the slack between itself and the count chip under it —\n\t\t\t\t\t\t\t\t\t a gap that reads as a mistake rather than as breathing room, because\n\t\t\t\t\t\t\t\t\t everything else in this stack is spaced by one step. Bottom-aligned,\n\t\t\t\t\t\t\t\t\t the slack goes above it, where the picture is, and the reel keeps its\n\t\t\t\t\t\t\t\t\t own step to the chip. -->\n\t\t\t\t\t\t\t\t\t<!-- The standing room is a desktop trade. Holding 4.4rem open so the\n\t\t\t\t\t\t\t\t\t\t picture does not jump when the reel arrives costs nothing on a wide\n\t\t\t\t\t\t\t\t\t\t window and seventy pixels of a phone — where it reads as an empty\n\t\t\t\t\t\t\t\t\t\t black band above the composer, and where those pixels are the ones\n\t\t\t\t\t\t\t\t\t\t the clip itself is short of. Below lg the band takes only the height\n\t\t\t\t\t\t\t\t\t\t of what is actually in it. -->\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tclass=\"flex shrink-0 flex-col justify-end lg:min-h-[4.4rem]\"\n\t\t\t\t\t\t\t\t\t\taria-hidden={!(film.length && filmOpen)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<!-- Only when the stage is not already saying it. The character surface\n\t\t\t\t\t\t\t\t\t\t\t carries the same countdown under the name whenever it is what the\n\t\t\t\t\t\t\t\t\t\t\t stage is showing, and the same sentence twice on one screen reads as\n\t\t\t\t\t\t\t\t\t\t\t two things happening. Here is where it belongs when a clip has the\n\t\t\t\t\t\t\t\t\t\t\t stage and the drawing is the quieter of the two. -->\n\t\t\t\t\t\t\t\t\t\t{#if stagePhase !== 'character'}\n\t\t\t\t\t\t\t\t\t\t\t{#each drawingHere as sh (sh.id)}\n\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"mb-2 flex items-center gap-2.5 text-xs text-[var(--st-muted)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"beacon size-1.5 shrink-0 rounded-full bg-[var(--st-green)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t></span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"min-w-0 truncate\">Building the six views for {sh.name}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-[var(--st-faint)]\">·</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"shrink-0 tabular-nums\">{turnStatus(sh)}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t{#if film.length && filmOpen}\n\t\t\t\t\t\t\t\t\t\t\t{@render filmReel()}\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{:else if film.length && filmOpen}\n\t\t\t\t\t\t\t\t\t{@render filmReel()}\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t<!-- `relative` is load-bearing: the add and format menus open upward\n\t\t\t\t\t\t from inside the composer and anchor to this box, not to the page. -->\n\t\t\t\t\t\t\t\t<!-- The stage above is a picture and takes the whole column; this is\n\t\t\t\t\t\t a line of text and keeps the measure it had. The note above is still\n\t\t\t\t\t\t true — a composer a thousand pixels wide makes the eye travel the\n\t\t\t\t\t\t screen to find the send button — so the width went to the clip and\n\t\t\t\t\t\t not to the box under it. -->\n\t\t\t\t\t\t\t\t<!-- The same gutter the stage keeps for the strip. Both boxes centre\n\t\t\t\t\t\t in the same reduced width, which is the only way their edges line\n\t\t\t\t\t\t up — pad one and not the other and they sit half a strip apart. -->\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<!-- The same measure the composer keeps, so their edges line up. Outside it\n\t\t\t\t\t\t\t these two ran the width of the column over a box half as wide. -->\n\t\t\t\t\t\t<!-- `mt-auto` so the field lands on the floor of the column rather than\n\t\t\t\t\t\t\t wherever the stage above it happens to stop. The stage takes the slack\n\t\t\t\t\t\t\t when it can, and then this changes nothing; when it cannot — a short\n\t\t\t\t\t\t\t picture, a phone, a stage with nothing in it yet — the slack collected\n\t\t\t\t\t\t\t UNDER the composer and left it floating in the middle of the screen.\n\t\t\t\t\t\t\t The margin belongs above the field, on the side the picture is on. -->\n\t\t\t\t\t\t<div class={STAGE_UI ? 'mx-auto mt-auto w-full' : 'mt-auto'} style={composerCap}>\n\t\t\t\t\t\t\t<div class=\"mb-2 flex justify-center\"><ModeSwitch /></div>\n\t\t\t\t\t\t\t{#if composerHint || film.length || (STAGE_UI && mode === 'simple')}\n\t\t\t\t\t\t\t\t<div class=\"mb-1.5 flex min-h-[1.6rem] items-center gap-3\">\n\t\t\t\t\t\t\t\t\t<p class=\"min-w-0 text-xs text-[var(--st-faint)]\">{composerHint}</p>\n\t\t\t\t\t\t\t\t\t<span class=\"flex-1\"></span>\n\t\t\t\t\t\t\t\t\t{#if film.length}\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\taria-expanded={filmOpen}\n\t\t\t\t\t\t\t\t\t\t\tondragover={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\tif (e.dataTransfer?.types.includes(CLIP_DRAG)) e.preventDefault();\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\tondrop={(e) => dropClipIntoFilm(e)}\n\t\t\t\t\t\t\t\t\t\t\tonclick={() => (filmOpen = !filmOpen)}\n\t\t\t\t\t\t\t\t\t\t\tclass=\"pillglass flex shrink-0 cursor-pointer items-center gap-1.5 rounded-full px-2.5 py-1 text-xs text-[var(--st-text)] tabular-nums transition-colors hover:bg-[var(--st-surface-2)] {filmOpen\n\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-[var(--st-surface-2)]'\n\t\t\t\t\t\t\t\t\t\t\t\t: ''}\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"reelmark\" aria-hidden=\"true\"></span>\n\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t>{film.length}\n\t\t\t\t\t\t\t\t\t\t\t\t{film.length === 1 ? 'clip' : 'clips'} · {filmSeconds}s</span\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"text-[0.6rem] text-[var(--st-faint)] {filmOpen ? 'rotate-180' : ''}\"\n\t\t\t\t\t\t\t\t\t\t\t\t>⌄</span\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t<!-- The settings, in the open, above the sentence.\n\t\t\t\t\t\t\t\t The sheet behind the slider holds the same answers and holds them\n\t\t\t\t\t\t\t\t better when there are many — but it costs a tap to see what is set,\n\t\t\t\t\t\t\t\t and on a phone the three that change most often are worth having in\n\t\t\t\t\t\t\t\t sight. A row rather than a wrap: everything stays on one line and the\n\t\t\t\t\t\t\t\t line scrolls, so a fourth group appearing never pushes the field down.\n\t\t\t\t\t\t\t\t Phones only; the desktop chips already say this on one line. -->\n\t\t\t\t\t\t\t<!-- The row scrolls, so nothing inside it can escape it: overflow-x makes\n\t\t\t\t\t\t\t\t the vertical overflow non-visible too, and a panel opening upward from a\n\t\t\t\t\t\t\t\t bubble would be sliced off at the top. The wrapper is where it opens\n\t\t\t\t\t\t\t\t from instead — outside the scroller, above the row, over the picture. -->\n\t\t\t\t\t\t\t<div class=\"pillwrap relative mb-1.5 lg:hidden\">\n\t\t\t\t\t\t\t\t{#each pillGroups().filter((x) => x.up && pillGrp === x.id) as grp (grp.id)}\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tstyle=\"left:{pillAt.x}px; transform-origin:{pillAt.o}px bottom\"\n\t\t\t\t\t\t\t\t\t\tclass=\"pillrise absolute bottom-full z-40 mb-1.5 flex min-w-36 flex-col gap-0.5 rounded-2xl bg-[var(--st-surface)] p-1 shadow-[0_16px_44px_rgba(0,0,0,.6)] ring-1 ring-[var(--st-line)]\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{#each grp.opts as o, i (o.v)}\n\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\tdisabled={o.off}\n\t\t\t\t\t\t\t\t\t\t\t\taria-pressed={grp.now === o.v}\n\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\tgrp.pick(o.v);\n\t\t\t\t\t\t\t\t\t\t\t\t\tpillGrp = '';\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\tstyle=\"animation-delay:{i * 45}ms\"\n\t\t\t\t\t\t\t\t\t\t\t\ttitle={o.off ? `over ${MAX_AT_ONCE} clips at once` : ''}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"pillopen flex min-h-9 items-center gap-2 rounded-xl px-3 text-xs transition-colors {o.off\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'cursor-default text-[var(--st-faint)] opacity-35'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: 'cursor-pointer'} {!o.off && grp.now === o.v\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-[var(--st-surface-2)] font-semibold text-[var(--st-text)]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: !o.off\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'text-[var(--st-faint)] hover:text-[var(--st-text)]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: ''}\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{#if o.box}<span class=\"block rounded-[2px] border border-current {o.box}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t></span>{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t{o.l}\n\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t<!-- Wide between groups, tight inside them. At the same 1.5 the two gaps\n\t\t\t\t\t\t\t\t\t said the same thing, so 5s 10s 15s 288p 576p 864p read as one run of\n\t\t\t\t\t\t\t\t\t six options rather than as two questions with three answers each. The\n\t\t\t\t\t\t\t\t\t grouping is the meaning here; the space has to carry it. -->\n\t\t\t\t\t\t\t\t<div class=\"railstrip flex items-center gap-3 overflow-x-auto pb-0.5\">\n\t\t\t\t\t\t\t\t\t<!-- The way in, at the head of this row on a phone for the same reason\n\t\t\t\t\t\t\t\t\t\t it heads the chip row on a desktop: everything behind it arrives as\n\t\t\t\t\t\t\t\t\t\t a bubble in THIS row. Beside the sentence it was next to the one\n\t\t\t\t\t\t\t\t\t\t thing it has nothing to do with, and it took a corner of the field\n\t\t\t\t\t\t\t\t\t\t to say so. Shaped like the bubbles it adds — same surface, same\n\t\t\t\t\t\t\t\t\t\t height, same corner. -->\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\taria-label=\"add a character, a location or a reference image\"\n\t\t\t\t\t\t\t\t\t\taria-expanded={addOpen}\n\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\tconst open = !addOpen && pickKind === null;\n\t\t\t\t\t\t\t\t\t\t\tshutMenus();\n\t\t\t\t\t\t\t\t\t\t\taddOpen = open;\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\tclass=\"flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-full bg-[var(--st-surface)] text-[var(--st-muted)] transition-colors hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<svg viewBox=\"0 0 16 16\" class=\"size-4\" fill=\"none\" aria-hidden=\"true\">\n\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\td=\"M8 3v10M3 8h10\"\n\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.5\"\n\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t<!-- One group open, the rest folded to what they are set to.\n\t\t\t\t\t\t\t\t\t\t Six settings side by side is a wall of options nobody asked to read,\n\t\t\t\t\t\t\t\t\t\t and it pushed the ones that matter off the end of the line. Folded,\n\t\t\t\t\t\t\t\t\t\t each bubble is a fact — 5s, 576p, 9:16 — and opening one is how you\n\t\t\t\t\t\t\t\t\t\t change it. Only one opens at a time, so the row never grows past a\n\t\t\t\t\t\t\t\t\t\t scroll and the answer you are choosing is the only set on screen. -->\n\t\t\t\t\t\t\t\t\t<!-- The bubble stays whether or not anyone is cast, and this is the\n\t\t\t\t\t\t\t\t\t\t second time that rule has had to be learned here: gated on\n\t\t\t\t\t\t\t\t\t\t chosenCharacter it disappeared exactly when it was needed, because\n\t\t\t\t\t\t\t\t\t\t with nobody chosen there was nothing to tap to choose somebody. The\n\t\t\t\t\t\t\t\t\t\t empty state is an answer — \"anyone\" — and it is the default one.\n\t\t\t\t\t\t\t\t\t\t The desktop chip already says this; see the note beside it. -->\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\tshutMenus();\n\t\t\t\t\t\t\t\t\t\t\tpickKind = 'character';\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\tclass=\"pillglass flex min-h-8 max-w-[6.75rem] shrink-0 cursor-pointer items-center gap-1.5 rounded-full py-1 pr-3 pl-1 text-xs whitespace-nowrap {chosenCharacter\n\t\t\t\t\t\t\t\t\t\t\t? 'text-[var(--st-text)]'\n\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-faint)]'}\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{#if chosenCharacter}\n\t\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\t\tsrc=\"/studio/api/sheet/img/{chosenCharacter.id}\"\n\t\t\t\t\t\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\t\t\t\t\t\tonerror={sheetImageMissing}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-6 shrink-0 rounded-full object-cover\"\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"min-w-0 truncate\">{chosenCharacter.name}</span>\n\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t<!-- The same filled disc the cast chip wears when it is empty: a\n\t\t\t\t\t\t\t\t\t\t\t\t placeholder is the shape it stands in for, and a hairline ring\n\t\t\t\t\t\t\t\t\t\t\t\t at this size reads as a picture that failed to load. -->\n\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex size-6 shrink-0 items-center justify-center rounded-full bg-current/10\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-[11px] opacity-55\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tfill=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<circle cx=\"8\" cy=\"5.9\" r=\"2.6\" />\n\t\t\t\t\t\t\t\t\t\t\t\t\t<path d=\"M3.5 13.4c0-2.5 2-4 4.5-4s4.5 1.5 4.5 4z\" />\n\t\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t<span>anyone</span>\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 10 10\"\n\t\t\t\t\t\t\t\t\t\t\tclass=\"size-2.5 shrink-0 opacity-70\"\n\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\td=\"M2 4l3 3 3-3\"\n\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.4\"\n\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t<!-- Where it happens, on the narrow screen too.\n\t\t\t\t\t\t\t\t\t\t Desktop has said this since the chip row existed; the rail did not,\n\t\t\t\t\t\t\t\t\t\t so a location you had chosen was invisible on the device most of\n\t\t\t\t\t\t\t\t\t\t this is watched on, and the only way to check it was to widen the\n\t\t\t\t\t\t\t\t\t\t window. Shown only when one is set: \"anywhere\" is the default and\n\t\t\t\t\t\t\t\t\t\t needs no bubble, and the rail is the one row here that cannot\n\t\t\t\t\t\t\t\t\t\t afford a chip that says nothing. -->\n\t\t\t\t\t\t\t\t\t{#if chosenLocation}\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\tshutMenus();\n\t\t\t\t\t\t\t\t\t\t\t\tpickKind = 'location';\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\tclass=\"flex min-h-8 max-w-[6.75rem] shrink-0 cursor-pointer items-center gap-1.5 rounded-full bg-[var(--st-surface)] py-1 pr-3 pl-1 text-xs whitespace-nowrap text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<!-- Square, where the character's is round. A place and a person are\n\t\t\t\t\t\t\t\t\t\t\t\t different kinds of thing and the shape says which before the name\n\t\t\t\t\t\t\t\t\t\t\t\t is read. -->\n\t\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\t\tsrc=\"/studio/api/sheet/img/{chosenLocation.id}\"\n\t\t\t\t\t\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\t\t\t\t\t\tonerror={sheetImageMissing}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-6 shrink-0 rounded-lg object-cover\"\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"min-w-0 truncate\">{chosenLocation.name}</span>\n\t\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 10 10\"\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-2.5 shrink-0 opacity-70\"\n\t\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M2 4l3 3 3-3\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t<!-- Second, right after who is in it. What follows this clip is the\n\t\t\t\t\t\t\t\t\t\t question a continuation asks first — before how many and\n\t\t\t\t\t\t\t\t\t\t before how long — and it was last in the row, past two\n\t\t\t\t\t\t\t\t\t\t settings that do not change between takes. -->\n\t\t\t\t\t\t\t\t\t{#each pillGroups() as grp (grp.id)}\n\t\t\t\t\t\t\t\t\t\t<!-- Length and size stay open; the frame and the continuation fold.\n\t\t\t\t\t\t\t\t\t\t\t Two short numbers each read faster as a set than as a fact you have\n\t\t\t\t\t\t\t\t\t\t\t to tap to question — you can see at a glance that 5s is one of\n\t\t\t\t\t\t\t\t\t\t\t three, not a value out of nowhere. The other two carry a glyph and\n\t\t\t\t\t\t\t\t\t\t\t whole sentences, and unfolding those in line pushes the row off the\n\t\t\t\t\t\t\t\t\t\t\t screen, so they open upward instead. -->\n\t\t\t\t\t\t\t\t\t\t{@const open = pillGrp === grp.id}\n\t\t\t\t\t\t\t\t\t\t{@const cur = grp.opts.find((o) => o.v === grp.now) ?? grp.opts[0]}\n\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\tclass=\"pillglass flex shrink-0 items-center gap-0.5 rounded-full p-0.5 whitespace-nowrap\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{#if !grp.up}\n\t\t\t\t\t\t\t\t\t\t\t\t{#each grp.opts as o, i (o.v)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={composerShape.fixed}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-pressed={grp.now === o.v}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => grp.pick(o.v)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstyle=\"animation-delay:{i * 40}ms\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"pillopen flex min-h-7 items-center gap-1.5 rounded-full px-3 text-xs transition-colors {grp.words\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? ''\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'font-mono tabular-nums'} {composerShape.fixed\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'cursor-default opacity-40'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'cursor-pointer'} {grp.now === o.v\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-[var(--st-text)] font-semibold text-[var(--st-bg)]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-faint)]'}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{o.l}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\taria-expanded={open}\n\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={(e) => (open ? (pillGrp = '') : openPillAt(e, grp.id))}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex min-h-7 cursor-pointer items-center gap-1.5 rounded-full px-3 text-xs transition-colors {grp.words\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? ''\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'font-mono tabular-nums'} {open\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-[var(--st-text)] font-semibold text-[var(--st-bg)]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-muted)]'}\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{#if cur.box}<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"block rounded-[2px] border border-current {cur.box}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t></span>{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{cur.l}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- Every control that opens says so the same way. The mode chip\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t carried this chevron and the folded groups did not, so two things\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t with identical behaviour looked like a control and a readout. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 10 10\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-2.5 shrink-0 opacity-70 transition-transform {open\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'rotate-180'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: ''}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M2 4l3 3 3-3\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t<!-- What the send makes, as one bubble opening the panel desktop\n\t\t\t\t\t\t\t\t\t\t already uses: one clip, versions, camera angles, full production.\n\t\t\t\t\t\t\t\t\t\t It was two groups of my own for a moment — takes and angles side by\n\t\t\t\t\t\t\t\t\t\t side — which said the same thing in a worse language and left the\n\t\t\t\t\t\t\t\t\t\t product of the two, the number that actually costs money, nowhere on\n\t\t\t\t\t\t\t\t\t\t screen. One control, one panel, one place the rule lives. -->\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\taria-expanded={modeOpen}\n\t\t\t\t\t\t\t\t\t\tonclick={(e) => openPanelAt(e, 'mode')}\n\t\t\t\t\t\t\t\t\t\tclass=\"pillglass flex min-h-8 shrink-0 cursor-pointer items-center gap-1.5 rounded-full px-3 text-xs whitespace-nowrap transition-colors {modeOpen\n\t\t\t\t\t\t\t\t\t\t\t? 'text-[var(--st-text)]'\n\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-muted)]'}\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{mode !== 'simple' ? 'full production' : batchLabel}\n\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 10 10\"\n\t\t\t\t\t\t\t\t\t\t\tclass=\"size-2.5 shrink-0 opacity-70\"\n\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\td=\"M2 4l3 3 3-3\"\n\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.4\"\n\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t<!-- Length, size and frame in one bubble, the way the desktop chip\n\t\t\t\t\t\t\t\t\t\t already says them. Three groups side by side was six pills and 604\n\t\t\t\t\t\t\t\t\t\t pixels of a 335-pixel row: everything on screen, nothing readable.\n\t\t\t\t\t\t\t\t\t\t One fact — 5s · 576p · 9:16 — and the panel behind it is the panel\n\t\t\t\t\t\t\t\t\t\t desktop opens, so the rule lives in one place. -->\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\taria-expanded={fmtOpen}\n\t\t\t\t\t\t\t\t\t\tonclick={(e) => openPanelAt(e, 'fmt')}\n\t\t\t\t\t\t\t\t\t\tclass=\"pillglass flex min-h-8 shrink-0 cursor-pointer items-center gap-1.5 rounded-full px-3 font-mono text-xs whitespace-nowrap transition-colors {fmtOpen\n\t\t\t\t\t\t\t\t\t\t\t? 'text-[var(--st-text)]'\n\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-muted)]'}\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{composerShape.seconds}s · {composerShape.res} · {composerShape.portrait\n\t\t\t\t\t\t\t\t\t\t\t? '9:16'\n\t\t\t\t\t\t\t\t\t\t\t: '16:9'}\n\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 10 10\"\n\t\t\t\t\t\t\t\t\t\t\tclass=\"size-2.5 shrink-0 opacity-70 transition-transform {fmtOpen\n\t\t\t\t\t\t\t\t\t\t\t\t? 'rotate-180'\n\t\t\t\t\t\t\t\t\t\t\t\t: ''}\"\n\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\td=\"M2 4l3 3 3-3\"\n\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.4\"\n\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclass=\"composerglass relative rounded-3xl p-3 {STAGE_UI ? 'mx-auto w-full' : ''}\"\n\t\t\t\t\t\t\t\tstyle={composerCap}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<!-- Where the press is made, so it is read before Generate rather than\n\t\t\t\t\t\t\t\t\t after it is refused. The server refuses with the same sentence. -->\n\t\t\t\t\t\t\t\t{#if STUDIO_PAUSED}\n\t\t\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\t\t\trole=\"status\"\n\t\t\t\t\t\t\t\t\t\tclass=\"mb-2.5 rounded-2xl bg-[var(--st-surface-2)] px-3.5 py-2.5 text-[13px] leading-snug font-medium text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{STUDIO_PAUSED}\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t<!-- Making a character or a location is a state you are IN, not a tab\n\t\t\t\t\t\t\t sitting beside the clip settings. It was a tab, and that put two\n\t\t\t\t\t\t\t different questions on one row — what this message makes, and who is\n\t\t\t\t\t\t\t in the clip — in identical chips. You enter this from the picker\n\t\t\t\t\t\t\t below, and this band is how you know you are here and how you leave. -->\n\t\t\t\t\t\t\t\t{#if mode === 'simple' && wantTarget === 'clip' && (continuing || (STAGE_UI && stageContinuable))}\n\t\t\t\t\t\t\t\t\t{@const clipUrl = stageContinuable?.artifact?.files?.[0]?.url ?? ''}\n\t\t\t\t\t\t\t\t\t<!-- Everything this band holds on a phone, in one place — because on a\n\t\t\t\t\t\t\t\t\t\t phone, on the stage, it holds nothing. Go through it: the three\n\t\t\t\t\t\t\t\t\t\t continuation chips are `lg:flex`, the question having moved into the\n\t\t\t\t\t\t\t\t\t\t format panel below where it is the only place it is asked on a narrow\n\t\t\t\t\t\t\t\t\t\t screen; the paragraph beside them is `!STAGE_UI`; so is the line under\n\t\t\t\t\t\t\t\t\t\t them. What is left is one error. The band drew as its own padding —\n\t\t\t\t\t\t\t\t\t\t a black rounded bar above the field, sitting where a control should\n\t\t\t\t\t\t\t\t\t\t be and doing nothing.\n\t\t\t\t\t\t\t\t\t\t \n\t\t\t\t\t\t\t\t\t\t This asked the wrong question once already. \"A kept character\" was\n\t\t\t\t\t\t\t\t\t\t read off the chips, and those are the desktop ones: keeping a face\n\t\t\t\t\t\t\t\t\t\t emptied the band on a phone, and continuing without one still drew\n\t\t\t\t\t\t\t\t\t\t the bar. The condition is not what the band is about, it is whether\n\t\t\t\t\t\t\t\t\t\t anything inside it will draw. -->\n\t\t\t\t\t\t\t\t\t{@const bandOnPhone = !STAGE_UI || !!charFromClipError}\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tclass=\"mb-2 {bandOnPhone\n\t\t\t\t\t\t\t\t\t\t\t? 'flex'\n\t\t\t\t\t\t\t\t\t\t\t: 'hidden lg:flex'} items-center justify-between gap-3 rounded-2xl bg-[var(--st-bg)] px-3.5 py-2.5\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<div class=\"min-w-0\">\n\t\t\t\t\t\t\t\t\t\t\t{#if !STAGE_UI && continuing}\n\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"font-display text-sm font-semibold\">Continuing that clip</p>\n\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-0.5 text-xs leading-relaxed text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\tSay what happens next — with {continuing.characterName ??\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'the same person'} in\n\t\t\t\t\t\t\t\t\t\t\t\t\t{continuing.locationName ?? 'the same place'}. The new piece renders on\n\t\t\t\t\t\t\t\t\t\t\t\t\tits own and joins onto the end.\n\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t<!-- Two options, not one switch.\n\t\t\t\t\t\t\t\t\t     This was a single button showing its own state, and it was read\n\t\t\t\t\t\t\t\t\t     as a choice: clicking \"starts on the last frame\" to ask for that\n\t\t\t\t\t\t\t\t\t     turned it off. A pair where the chosen one is filled is how the\n\t\t\t\t\t\t\t\t\t     length and the resolution already work on this card, and it\n\t\t\t\t\t\t\t\t\t     cannot be misread the same way.\n\n\t\t\t\t\t\t\t\t\t     The whole prior clip goes to the model either way — the person,\n\t\t\t\t\t\t\t\t\t     the room, the light and the motion all come from it. This only\n\t\t\t\t\t\t\t\t\t     decides whether the FIRST INSTANT is nailed to the frame the\n\t\t\t\t\t\t\t\t\t     last clip ended on. -->\n\t\t\t\t\t\t\t\t\t\t\t<!-- One question, three answers, and the two that carry something show it.\n\t\t\t\t\t\t\t\t\t\t     It was a toggle, two modifiers of the toggle and an escape hatch — four\n\t\t\t\t\t\t\t\t\t\t     controls for one decision, with the off state written twice (turning\n\t\t\t\t\t\t\t\t\t\t     Continue off and pressing \"new clip\" run the same three lines). And the\n\t\t\t\t\t\t\t\t\t\t     distinction that actually matters was carried by words alone: whether the\n\t\t\t\t\t\t\t\t\t\t     next clip starts on this exact frame, or merely with these people in this\n\t\t\t\t\t\t\t\t\t\t     room. Both of those are pictures. Naming them was doing the work an\n\t\t\t\t\t\t\t\t\t\t     image does better and faster.\n\n\t\t\t\t\t\t\t\t\t\t     So: the frame chip wears the frame it would start on, the person chip\n\t\t\t\t\t\t\t\t\t\t     wears the person it would keep, and the third wears nothing — which is\n\t\t\t\t\t\t\t\t\t\t     not an omission, it is the answer. An empty rectangle would read as a\n\t\t\t\t\t\t\t\t\t\t     picture that failed to load. -->\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"mt-2 hidden flex-wrap items-center gap-1.5 lg:flex\">\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\taria-pressed={!!continuing && pinSeam}\n\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcontOffFor = '';\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (!continuing && stageContinuable) startContinue(stageContinuable);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tpinSeam = true;\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex min-h-9 cursor-pointer items-center gap-2 rounded-full py-1 pr-3.5 pl-1 text-xs transition-colors {continuing &&\n\t\t\t\t\t\t\t\t\t\t\t\t\tpinSeam\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-[var(--st-text)] font-semibold text-[var(--st-bg)]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-faint)] hover:text-[var(--st-text)]'}\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{#if clipUrl}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- svelte-ignore a11y_media_has_caption -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<video\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsrc={clipUrl}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tmuted\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tplaysinline\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpreload=\"metadata\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tuse:lastFrame\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"h-7 w-[3.1rem] shrink-0 rounded-full bg-black object-cover\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t></video>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"lg:hidden\">Last frame</span><span class=\"hidden lg:inline\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>From the last frame</span\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\taria-pressed={!!continuing && !pinSeam}\n\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcontOffFor = '';\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (!continuing && stageContinuable) startContinue(stageContinuable);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tpinSeam = false;\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex min-h-9 cursor-pointer items-center gap-2 rounded-full py-1 pr-3.5 pl-1 text-xs transition-colors {continuing &&\n\t\t\t\t\t\t\t\t\t\t\t\t\t!pinSeam\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-[var(--st-text)] font-semibold text-[var(--st-bg)]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-faint)] hover:text-[var(--st-text)]'}\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{#if chosenCharacter}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsrc=\"/studio/api/sheet/img/{chosenCharacter.id}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonerror={sheetImageMissing}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-7 shrink-0 rounded-full object-cover\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- Nobody kept yet, so the disc is empty — the same placeholder the\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t cast chip wears, which is also what \"Keep this person\" fills.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Both parts are drawn from the chip's own foreground rather than a\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t fixed token: on the selected chip that foreground is dark, and a dark\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t figure on a dark disc on a white pill was a black blob with something\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t buried in it. Ten per cent for the disc and fifty-five for the figure\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t holds on either ground, which is how a placeholder avatar is drawn on\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t a light surface anyway. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex size-7 shrink-0 items-center justify-center rounded-full bg-current/10\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-[13px] opacity-55\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfill=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<circle cx=\"8\" cy=\"5.9\" r=\"2.6\" />\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<path d=\"M3.5 13.4c0-2.5 2-4 4.5-4s4.5 1.5 4.5 4z\" />\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"lg:hidden\">Same person</span><span class=\"hidden lg:inline\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>Same person &amp; place</span\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\taria-pressed={!continuing}\n\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcontOffFor = stageContinuable?.id ?? '';\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcontinuing = null;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tspendConfirmChain();\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex min-h-9 cursor-pointer items-center rounded-full px-3.5 text-xs transition-colors {continuing\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'text-[var(--st-faint)] hover:text-[var(--st-text)]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'bg-[var(--st-text)] font-semibold text-[var(--st-bg)]'}\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\tNew clip\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t\t\t\t\t\t<!-- \"Keep this person\" stood here and is gone. The row is one question —\n\t\t\t\t\t\t\t\t\t\t\t\t\t what the next clip continues from — and this was a second, quieter\n\t\t\t\t\t\t\t\t\t\t\t\t\t one wedged into the same line, about the clip behind you rather than\n\t\t\t\t\t\t\t\t\t\t\t\t\t the one ahead. It also fired on its own when a continuation was\n\t\t\t\t\t\t\t\t\t\t\t\t\t chosen, so the button was mostly a label for something that had\n\t\t\t\t\t\t\t\t\t\t\t\t\t already happened. The work it did still happens; only the control\n\t\t\t\t\t\t\t\t\t\t\t\t\t has left the row. -->\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t{#if !STAGE_UI}\n\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-1 text-xs leading-relaxed text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{pinSeam\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Seamless — the first instant is the frame the clip ended on.'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'Same people, same place, a fresh take — the action can begin anywhere. The join may step.'}\n\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t<!-- Recommended, not required.\n\t\t\t\t\t\t\t\t\t     With no kept character the launch cuts a plate out of the prior\n\t\t\t\t\t\t\t\t\t     clip on every generation, so each continuation is measured\n\t\t\t\t\t\t\t\t\t     against the last render rather than against a face anybody\n\t\t\t\t\t\t\t\t\t     approved, and the drift compounds down the chain. One frame kept\n\t\t\t\t\t\t\t\t\t     as a character stops that. The send button stays live: this is\n\t\t\t\t\t\t\t\t\t     advice on the way past, not a gate. -->\n\t\t\t\t\t\t\t\t\t\t\t<!-- The offer itself now sits in the row above, beside the choice it\n\t\t\t\t\t\t\t\t\t\t\t     belongs to. What stays here is what that row has no place for: the\n\t\t\t\t\t\t\t\t\t\t\t     reason, on the surfaces that show reasons, and whatever went wrong. -->\n\t\t\t\t\t\t\t\t\t\t\t{#if continuing && !continuing.characterId}\n\t\t\t\t\t\t\t\t\t\t\t\t{#if !STAGE_UI}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-2 text-xs leading-relaxed text-[var(--st-muted)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tNo character is kept for this clip, so each continuation copies the one\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tbefore it and the likeness drifts. Keeping one now holds it.\n\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t{#if charFromClipError}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-1.5 text-xs leading-relaxed text-[var(--st-warn,#e06c6c)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{charFromClipError}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t{:else if continuing && !STAGE_UI}\n\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-2 text-xs leading-relaxed text-[var(--st-muted)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\tKept as <span class=\"font-semibold\">{continuing.characterName}</span> — every\n\t\t\t\t\t\t\t\t\t\t\t\t\tcontinuation from here is measured against that picture.\n\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t{#if mode === 'simple' && wantTarget !== 'clip'}\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tclass=\"mb-2 flex items-center justify-between gap-3 rounded-2xl bg-[var(--st-bg)] px-3.5 py-2.5\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<div class=\"min-w-0\">\n\t\t\t\t\t\t\t\t\t\t\t<p class=\"font-display text-sm font-semibold\">\n\t\t\t\t\t\t\t\t\t\t\t\t{wantTarget === 'character' ? 'New character' : 'New location'}\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-0.5 text-xs leading-relaxed text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t{wantTarget === 'character'\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Describe them — age, build, hair, what they are wearing. A picture comes back in about a minute. Or attach a photograph, and describe only what it cannot show.'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: 'Describe the place — six views of it to shoot against. Or attach a photograph and keep that instead.'}\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-secondary btn-sm shrink-0\"\n\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\twantTarget = 'clip';\n\t\t\t\t\t\t\t\t\t\t\t\tcurrentCharacter = null;\n\t\t\t\t\t\t\t\t\t\t\t\tsaveSetup();\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\tback to clips\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t{#if mode === 'simple' && wantTarget === 'clip'}\n\t\t\t\t\t\t\t\t\t<!-- What this clip will be made with, and only that. The rows this\n\t\t\t\t\t\t\t\t replaces showed every kept sheet whether or not you had chosen\n\t\t\t\t\t\t\t\t it; a chip shows what you chose and nothing else, so the answer\n\t\t\t\t\t\t\t\t to \"who and where\" is still one glance.\n\n\t\t\t\t\t\t\t\t The way in used to head this row as well as the one below it, so a\n\t\t\t\t\t\t\t\t desktop showed the same plus twice, one above the other, both\n\t\t\t\t\t\t\t\t opening the same menu. The row below keeps it — it is where the\n\t\t\t\t\t\t\t\t thumb and the cursor already are, and it is present in every mode\n\t\t\t\t\t\t\t\t this row is. Without the glyph the chips start at the wall, level\n\t\t\t\t\t\t\t\t with the button beneath them. -->\n\t\t\t\t\t\t\t\t\t<div class=\"mb-1 hidden flex-wrap items-center gap-1.5 lg:flex\">\n\t\t\t\t\t\t\t\t\t\t{#if chosenCharacter}\n\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex items-center gap-2 rounded-full bg-[var(--st-bg)] py-1 pr-1 pl-1 text-xs\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<!-- The name opens the picker, the × clears it. Two things you\n\t\t\t\t\t\t\t\t\t\t\t might want from a chip that names a choice — change it, or\n\t\t\t\t\t\t\t\t\t\t\t stop making it — and only the second had a control. -->\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => (pickKind = 'character')}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex cursor-pointer items-center gap-2\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsrc=\"/studio/api/sheet/img/{chosenCharacter.id}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonerror={sheetImageMissing}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-5 shrink-0 rounded-full object-cover\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- 4rem, because a sheet made from a photograph is named after the\n\t\t\t\t\t\t\t\t\t\t\t file, and \"Screenshot 2026 08 25 at 23.24.11\" made a 244px chip\n\t\t\t\t\t\t\t\t\t\t\t next to an 85px one. The chip's own furniture — avatar, ×,\n\t\t\t\t\t\t\t\t\t\t\t padding — is 68px, so matching `one clip` exactly would leave\n\t\t\t\t\t\t\t\t\t\t\t the name 17px. This is the smallest cap that still fits every\n\t\t\t\t\t\t\t\t\t\t\t name anyone actually types: the longest measured, \"Neon alley\",\n\t\t\t\t\t\t\t\t\t\t\t needs 57px. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"max-w-[4rem] truncate\">{chosenCharacter.name}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"shoot with anyone instead\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\twantCharacter = '';\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsaveSetup();\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-full text-[var(--st-faint)] transition-colors hover:bg-[var(--st-surface-2)] hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>×</button\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t<!-- The chip stays whether or not anyone is cast.\n\t\t\t\t\t\t\t\t\t\t With no character the row simply vanished, so the one place\n\t\t\t\t\t\t\t\t\t\t that answers \"who is in this\" was missing exactly when the\n\t\t\t\t\t\t\t\t\t\t answer was \"nobody in particular\" — which is a real answer and\n\t\t\t\t\t\t\t\t\t\t the default one. Saying it out loud also puts the picker one\n\t\t\t\t\t\t\t\t\t\t click from where the question is asked, instead of two menus\n\t\t\t\t\t\t\t\t\t\t away behind the +. -->\n\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => (pickKind = 'character')}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex min-h-8 cursor-pointer items-center gap-2 rounded-full bg-[var(--st-bg)] py-1 pr-3 pl-1 text-xs text-[var(--st-faint)] transition-colors hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<!-- The same disc the cast chip wears, with nobody in it.\n\t\t\t\t\t\t\t\t\t\t\t It was a hairline ring around an outlined figure while the\n\t\t\t\t\t\t\t\t\t\t\t chip beside it — the same slot, filled — was a solid\n\t\t\t\t\t\t\t\t\t\t\t photograph, so one control drew its two states in two\n\t\t\t\t\t\t\t\t\t\t\t different languages and the empty one read as a dropped\n\t\t\t\t\t\t\t\t\t\t\t image rather than as a choice not yet made. A placeholder is\n\t\t\t\t\t\t\t\t\t\t\t the shape it stands in for: a filled disc, and a solid\n\t\t\t\t\t\t\t\t\t\t\t figure inside it, because hairlines at eleven pixels\n\t\t\t\t\t\t\t\t\t\t\t shimmer and an outline at that size is not a drawing, it is\n\t\t\t\t\t\t\t\t\t\t\t a suggestion of one. -->\n\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex size-5 shrink-0 items-center justify-center rounded-full bg-[var(--st-surface-2)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<svg viewBox=\"0 0 16 16\" class=\"size-[11px]\" aria-hidden=\"true\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<circle cx=\"8\" cy=\"5.9\" r=\"2.6\" fill=\"currentColor\" />\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M3.5 13.4c0-2.5 2-4 4.5-4s4.5 1.5 4.5 4z\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfill=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t<span>anyone</span>\n\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t{#if chosenLocation}\n\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex items-center gap-2 rounded-full bg-[var(--st-bg)] py-1 pr-1 pl-1 text-xs\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\t\t\tsrc=\"/studio/api/sheet/img/{chosenLocation.id}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tonerror={sheetImageMissing}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-5 shrink-0 rounded-md object-cover\"\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"max-w-[4rem] truncate\">{chosenLocation.name}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"shoot anywhere instead\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\twantLocation = '';\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsaveSetup();\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-full text-[var(--st-faint)] transition-colors hover:bg-[var(--st-surface-2)] hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>×</button\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t\t\t\t\t{#if !planningWs}\n\t\t\t\t\t\t\t\t\t\t\t<!-- Beside the format chip rather than in the field row: both\n\t\t\t\t\t\t\t\t\t\t answer \"what will this message make\", both open a panel, and\n\t\t\t\t\t\t\t\t\t\t in the row it was squeezing the sentence into a box one line\n\t\t\t\t\t\t\t\t\t\t tall — at 390px the placeholder was clipped by it. -->\n\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\taria-expanded={modeOpen}\n\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst open = !modeOpen;\n\t\t\t\t\t\t\t\t\t\t\t\t\tshutMenus();\n\t\t\t\t\t\t\t\t\t\t\t\t\tmodeOpen = open;\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex min-h-8 cursor-pointer items-center gap-2 rounded-full bg-[var(--st-bg)] px-3 text-xs whitespace-nowrap text-[var(--st-muted)] transition-colors hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{mode !== 'simple' ? 'full production' : batchLabel}\n\t\t\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 10 10\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-2.5 shrink-0\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M2 4l3 3 3-3\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t\t\t\t\t<!-- Length, size and frame in one chip. All three keep a saved\n\t\t\t\t\t\t\t\t\t default, so none of them is a question you have to answer\n\t\t\t\t\t\t\t\t\t before the first send. -->\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\taria-expanded={fmtOpen}\n\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\tconst open = !fmtOpen;\n\t\t\t\t\t\t\t\t\t\t\t\tshutMenus();\n\t\t\t\t\t\t\t\t\t\t\t\tfmtOpen = open;\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\tclass=\"hidden min-h-8 cursor-pointer items-center gap-2 rounded-full bg-[var(--st-bg)] px-3 font-mono text-xs text-[var(--st-muted)] transition-colors hover:text-[var(--st-text)] lg:flex\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{composerShape.seconds}s · {composerShape.res} · {composerShape.portrait\n\t\t\t\t\t\t\t\t\t\t\t\t? '9:16'\n\t\t\t\t\t\t\t\t\t\t\t\t: '16:9'}\n\t\t\t\t\t\t\t\t\t\t\t<!-- The same chevron the mode control carries. Two chips that open\n\t\t\t\t\t\t\t\t\t\t the same kind of panel were reading as two different kinds of\n\t\t\t\t\t\t\t\t\t\t thing: one looked like a control, the other like a readout.\n\t\t\t\t\t\t\t\t\t\t Dropped while continuing, where it really is a readout. -->\n\t\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 10 10\"\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-2.5 shrink-0\"\n\t\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M2 4l3 3 3-3\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t\t\t<!-- The dismiss target for all three menus. A backdrop rather than a\n\t\t\t\t\t\t\t window listener, for the reason given at the <svelte:window> above,\n\t\t\t\t\t\t\t and it is the same shape the off-canvas sidebar already uses. -->\n\t\t\t\t\t\t\t\t{#if addOpen || pickKind || fmtOpen || modeOpen}\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\taria-label=\"close the menu\"\n\t\t\t\t\t\t\t\t\t\tclass=\"fixed inset-0 z-20 cursor-default\"\n\t\t\t\t\t\t\t\t\t\tonclick={shutMenus}\n\t\t\t\t\t\t\t\t\t></button>\n\t\t\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t\t\t<!-- ── the add menu, level one ─────────────────────────────── -->\n\t\t\t\t\t\t\t\t{#if addOpen}\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\trole=\"menu\"\n\t\t\t\t\t\t\t\t\t\tclass=\"enter absolute bottom-full left-2 z-30 mb-2 w-[20rem] max-w-[calc(100vw-3rem)] rounded-2xl bg-[var(--st-surface)] p-2 shadow-[0_16px_44px_rgba(0,0,0,.6)] ring-1 ring-[var(--st-line)]\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{#each [['character', characters.length, 'New character'], ['location', locations.length, 'New location']] as [kind, kept, label] (kind)}\n\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t// With nothing kept there is nothing to choose between, so\n\t\t\t\t\t\t\t\t\t\t\t\t\t// the row does the only useful thing and starts making one.\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (kept === 0) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\twantTarget = kind as 'character' | 'location';\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcurrentCharacter = null;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsubjectFrom = Date.now();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsaveSetup();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tshutMenus();\n\t\t\t\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taddOpen = false;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tpickKind = kind as 'character' | 'location';\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex min-h-[3.125rem] w-full cursor-pointer items-center gap-3 rounded-xl px-3 text-left text-sm transition-colors hover:bg-[var(--st-surface-2)]\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex size-7 shrink-0 items-center justify-center rounded-full text-[var(--st-muted)] ring-1 ring-[var(--st-line)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{#if kind === 'character'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-[15px]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<circle\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcx=\"8\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcy=\"5.6\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tr=\"2.7\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M3 13.2c.7-2.3 2.6-3.4 5-3.4s4.3 1.1 5 3.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-[15px]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<rect\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tx=\"2.2\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ty=\"3.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\twidth=\"11.6\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\theight=\"9.2\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trx=\"1.6\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M2.4 10.2l3-2.6 2.6 2.2 2.4-1.8 3.2 2.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"min-w-0\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"block\">{label}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"mt-0.5 block text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{kept ? `${kept} kept — or make another` : 'nothing kept yet'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t{#if kept}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 10 10\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"ml-auto size-2.5 shrink-0 text-[var(--st-faint)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M3.5 2l3 3-3 3\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t\t\t<!-- ── the add menu, level two ─────────────────────────────── -->\n\t\t\t\t\t\t\t\t{#if pickKind}\n\t\t\t\t\t\t\t\t\t{@const kept = pickKind === 'character' ? characters : locations}\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\trole=\"menu\"\n\t\t\t\t\t\t\t\t\t\tclass=\"enter absolute bottom-full left-2 z-30 mb-2 flex w-[23rem] max-w-[calc(100vw-3rem)] flex-col overflow-hidden rounded-2xl bg-[var(--st-surface)] shadow-[0_16px_44px_rgba(0,0,0,.6)] ring-1 ring-[var(--st-line)]\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\tclass=\"flex items-center gap-2 border-b border-[var(--st-line)] py-2 pr-2.5 pl-1.5\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"back\"\n\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\tpickKind = null;\n\t\t\t\t\t\t\t\t\t\t\t\t\taddOpen = true;\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-full text-[var(--st-muted)] transition-colors hover:bg-[var(--st-surface-2)] hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<svg viewBox=\"0 0 16 16\" class=\"size-3.5\" fill=\"none\" aria-hidden=\"true\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M9.5 3l-4 5 4 5\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.5\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-sm font-medium\">\n\t\t\t\t\t\t\t\t\t\t\t\t{pickKind === 'character' ? 'Who is in it' : 'Where it happens'}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\twantTarget = pickKind as 'character' | 'location';\n\t\t\t\t\t\t\t\t\t\t\t\t\tcurrentCharacter = null;\n\t\t\t\t\t\t\t\t\t\t\t\t\tsubjectFrom = Date.now();\n\t\t\t\t\t\t\t\t\t\t\t\t\tsaveSetup();\n\t\t\t\t\t\t\t\t\t\t\t\t\tshutMenus();\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"ml-auto flex min-h-8 shrink-0 cursor-pointer items-center gap-1.5 rounded-full bg-[var(--st-surface-2)] px-3 text-xs font-medium transition-colors hover:bg-[var(--st-line)]\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-sm leading-none\">+</span>\n\t\t\t\t\t\t\t\t\t\t\t\t{pickKind === 'character' ? 'New character' : 'New location'}\n\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t\t\t<!-- A grid, not a list. The rows this replaces stopped at three\n\t\t\t\t\t\t\t\t\t with no way past them; five to a row means twenty faces read\n\t\t\t\t\t\t\t\t\t in four. -->\n\t\t\t\t\t\t\t\t\t\t<div class=\"scroller max-h-[20rem] overflow-y-auto px-3 pt-3.5 pb-3\">\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"grid grid-cols-5 gap-x-2 gap-y-3\">\n\t\t\t\t\t\t\t\t\t\t\t\t{#each [{ id: '', name: pickKind === 'character' ? 'anyone' : 'anywhere' }, ...kept] as s (s.id)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{@const on =\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tpickKind === 'character'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? wantCharacter === s.id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: wantLocation === s.id}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{@const row = sheets.find((x) => x.id === s.id)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{@const sheetState = row?.sheet?.state}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"group relative flex min-w-0 flex-col\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trole=\"menuitemradio\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-checked={on}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle={s.id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? pickKind === 'character'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? `shoot this clip with ${s.name}`\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: `shoot this clip in ${s.name}`\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: pickKind === 'character'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'nobody in particular — the clip invents whoever the words describe'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'nowhere in particular — the clip invents wherever the words describe'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (pickKind === 'character') wantCharacter = s.id;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\telse wantLocation = s.id;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsaveSetup();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tshutMenus();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex min-w-0 cursor-pointer flex-col items-center gap-1.5 rounded-xl pb-1\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- The picture, over the shape it will be.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t A sheet's image url answers 404 until its six views land, which is the\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t minutes right after you make the character — and hiding the broken\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t element on its own left a hole where a face goes, with the name under\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t nothing. The placeholder sits behind every tile rather than instead of\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t some of them, so the tile is the same shape throughout and the picture\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t simply arrives into it. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"relative block aspect-square w-full\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"absolute inset-0 flex items-center justify-center bg-[var(--st-surface-2)] {pickKind ===\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t'character'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'rounded-full'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'rounded-lg'} {on ? 'ring-2 ring-[var(--st-text)]' : ''}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if s.id}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-1/3 text-[var(--st-faint)] opacity-55\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfill=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<circle cx=\"8\" cy=\"5.9\" r=\"2.6\" />\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<path d=\"M3.5 13.4c0-2.5 2-4 4.5-4s4.5 1.5 4.5 4z\" />\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if s.id}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsrc=\"/studio/api/sheet/img/{s.id}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonerror={sheetImageMissing}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"absolute inset-0 h-full w-full object-cover transition-opacity hover:opacity-80 {pickKind ===\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t'character'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'rounded-full'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'rounded-lg'} {on ? 'ring-2 ring-[var(--st-text)]' : ''}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"max-w-full truncate text-[0.7rem] leading-tight {on\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'font-medium text-[var(--st-text)]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-faint)]'}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{s.name}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if s.id}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- The six views: a quiet dot while they render, and the\n\t\t\t\t\t\t\t\t\t\t\t\t     sheet itself to open once they are there. Not announced\n\t\t\t\t\t\t\t\t\t\t\t\t     anywhere else — this is where you would look for it. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if sheetState === 'rendering'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"the six views are rendering\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"beacon pointer-events-none absolute top-1 right-1 size-2 rounded-full bg-[var(--st-accent)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t></span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else if row?.sheet?.file}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\thref=\"/studio/api/sheet/full/{s.id}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trel=\"noreferrer\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"open the six views\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={(e) => e.stopPropagation()}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"absolute top-1 right-1 flex size-5 items-center justify-center rounded-full bg-[var(--st-bg)]/80 text-[0.6rem] text-[var(--st-muted)] opacity-0 transition-opacity group-hover:opacity-100 hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>⤢</a\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<!-- Two clicks. A face is minutes of GPU time, not a row. -->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if dropArmed === s.id}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"absolute inset-x-0 bottom-5 flex justify-center gap-1\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\te.stopPropagation();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdropSheet(s.id);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-full bg-[var(--st-bg)] px-2 py-0.5 text-[0.6rem] text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>remove</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\te.stopPropagation();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdropArmed = '';\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-full bg-[var(--st-bg)] px-2 py-0.5 text-[0.6rem] text-[var(--st-faint)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>keep</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"remove {s.name}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\te.stopPropagation();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdropArmed = s.id;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"absolute top-1 left-1 flex size-5 cursor-pointer items-center justify-center rounded-full bg-[var(--st-bg)]/80 text-[0.7rem] leading-none text-[var(--st-faint)] opacity-0 transition-opacity group-hover:opacity-100 hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>×</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t\t\t<!-- The voice used to be asked for here, under the picker, and it was\n\t\t\t\t\t\t\t\t\t the wrong counter: this list is for casting the next clip, and a\n\t\t\t\t\t\t\t\t\t sentence describing how somebody sounds is not a casting decision.\n\t\t\t\t\t\t\t\t\t It now sits under the name on the character stage, where the person\n\t\t\t\t\t\t\t\t\t it belongs to is on screen and being named. -->\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t\t\t<!-- ── one clip or a full production ───────────────────────── -->\n\t\t\t\t\t\t\t\t{#if modeOpen}\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\trole=\"menu\"\n\t\t\t\t\t\t\t\t\t\tstyle=\"left:{panelAt.x}px; transform-origin:{panelAt.o}px bottom\"\n\t\t\t\t\t\t\t\t\t\tclass=\"pillrise absolute bottom-full z-40 mb-2 w-[19.5rem] max-w-[calc(100vw-2rem)] rounded-2xl bg-[var(--st-surface)] p-2 shadow-[0_16px_44px_rgba(0,0,0,.6)] ring-1 ring-[var(--st-line)] lg:left-2! lg:z-30\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<!-- One list, one question — what this message makes. Two axes on\n\t\t\t\t\t\t\t\t\t it rather than two modes: versions vary the draw, angles vary\n\t\t\t\t\t\t\t\t\t the camera, and every message makes the product of the two.\n\t\t\t\t\t\t\t\t\t \"one clip\" is 1 x 1 rather than a special case.\n\n\t\t\t\t\t\t\t\t\t No subtitles on the two rows. The titles carry it, a popover\n\t\t\t\t\t\t\t\t\t menu is not a settings list, and beside a count they wrapped to\n\t\t\t\t\t\t\t\t\t two lines each — which is the clutter this menu has already\n\t\t\t\t\t\t\t\t\t been cleaned of twice. -->\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\trole=\"menuitemradio\"\n\t\t\t\t\t\t\t\t\t\t\taria-checked={mode === 'simple' && effAtOnce === 1}\n\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\tsetMode('simple');\n\t\t\t\t\t\t\t\t\t\t\t\ttakes = 1;\n\t\t\t\t\t\t\t\t\t\t\t\tangles = 1;\n\t\t\t\t\t\t\t\t\t\t\t\tsaveSetup();\n\t\t\t\t\t\t\t\t\t\t\t\tshutMenus();\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\tclass=\"flex min-h-[2.75rem] w-full cursor-pointer items-center gap-2.5 rounded-xl px-3 text-left text-sm transition-colors hover:bg-[var(--st-surface-2)]\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"w-3.5 shrink-0 text-xs {mode === 'simple' && effAtOnce === 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t? ''\n\t\t\t\t\t\t\t\t\t\t\t\t\t: 'invisible'}\">&#10003;</span\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"min-w-0\">one clip</span>\n\t\t\t\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t\t\t\t{#each [{ id: 'versions', label: 'versions' }, { id: 'angles', label: 'camera angles' }] as axis (axis.id)}\n\t\t\t\t\t\t\t\t\t\t\t{@const mine = axis.id === 'versions' ? takes : angles}\n\t\t\t\t\t\t\t\t\t\t\t{@const other = axis.id === 'versions' ? angles : takes}\n\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex min-h-[2.75rem] w-full items-center gap-2.5 rounded-xl px-3 text-sm\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"w-3.5 shrink-0 text-xs {mode === 'simple' && mine > 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? ''\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'invisible'}\">&#10003;</span\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"min-w-0 flex-1 whitespace-nowrap {axis.id === 'angles' &&\n\t\t\t\t\t\t\t\t\t\t\t\t\t!anglesApply\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'text-[var(--st-faint)]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: ''}\">{axis.label}</span\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"flex shrink-0 gap-0.5\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{#each [1, 2, 3, 4] as n (n)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{@const off = axis.id === 'angles' && !anglesApply}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{@const over = n * other > MAX_AT_ONCE || off}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trole=\"menuitemradio\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-checked={mode === 'simple' && !off && mine === n}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-disabled={over}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (over) return;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetMode('simple');\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (axis.id === 'versions') takes = n;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\telse angles = n;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsaveSetup();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex size-[1.55rem] items-center justify-center rounded-lg text-xs font-medium tabular-nums transition-colors {mode ===\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t'simple' && mine === n\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-[var(--st-text)] text-black'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: over\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'cursor-default text-[var(--st-faint)] opacity-25'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'cursor-pointer text-[var(--st-faint)] hover:bg-white/10 hover:text-[var(--st-text)]'}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>{n}</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t{/each}\n\n\t\t\t\t\t\t\t\t\t\t{#if !anglesApply}\n\t\t\t\t\t\t\t\t\t\t\t<!-- Said once, under the row it applies to, rather than grey\n\t\t\t\t\t\t\t\t\t\t controls with the reason left to be guessed at. And it names\n\t\t\t\t\t\t\t\t\t\t the way out: the same continuation on a free start can have\n\t\t\t\t\t\t\t\t\t\t angles, because nothing is nailed to the last frame then. -->\n\t\t\t\t\t\t\t\t\t\t\t<p class=\"mt-0.5 mb-1 pl-[2.25rem] text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\t\tAngles need a free start.\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t{:else if mode === 'simple' && takes > 1 && effAngles > 1}\n\t\t\t\t\t\t\t\t\t\t\t<!-- Only when they actually multiply. Saying \"3 clips\" under a\n\t\t\t\t\t\t\t\t\t\t row that already reads \"3\" is noise. -->\n\t\t\t\t\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"mt-0.5 mb-1 pl-[2.25rem] text-xs text-[var(--st-faint)] tabular-nums\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{effAtOnce} clips — {effAngles} angles, {takes} versions of each\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t<div class=\"my-1 h-px bg-[var(--st-line)]\"></div>\n\t\t\t\t\t\t\t\t\t\t<!-- Shown, and not yet offered.\n\t\t\t\t\t\t\t\t\t A screenplay, a cast and a multi-scene shoot is the other half\n\t\t\t\t\t\t\t\t\t of this product and it is not finished; taking the row out\n\t\t\t\t\t\t\t\t\t entirely would have been honest about today and silent about\n\t\t\t\t\t\t\t\t\t where this goes, and a person deciding whether to keep using\n\t\t\t\t\t\t\t\t\t the app is deciding about both. So it stays where it will be,\n\t\t\t\t\t\t\t\t\t reading as what it is: one step down in contrast, no check\n\t\t\t\t\t\t\t\t\t column to fill, and the word for when rather than a lock icon —\n\t\t\t\t\t\t\t\t\t a lock says you may not, and the truth is not yet.\n\n\t\t\t\t\t\t\t\t\t aria-disabled rather than a disabled button, because the row is\n\t\t\t\t\t\t\t\t\t not a control that happens to be off: it is a line of text\n\t\t\t\t\t\t\t\t\t announcing something. Screen readers get told it is unavailable\n\t\t\t\t\t\t\t\t\t and the tab order does not stop on it. -->\n\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\t\t\t\t\t\taria-disabled=\"true\"\n\t\t\t\t\t\t\t\t\t\t\tclass=\"flex min-h-[2.75rem] w-full cursor-default items-center gap-2.5 rounded-xl px-3 text-left text-sm\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"invisible w-3.5 shrink-0 text-xs\">&#10003;</span>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"min-w-0\">\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"block text-[var(--st-muted)]\">full production</span>\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"mt-0.5 block text-xs text-[var(--st-faint)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>screenplay and cast first, then a multi-scene shoot</span\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"ml-auto shrink-0 self-start pt-0.5 text-xs text-[var(--st-faint)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t>coming soon</span\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t\t\t<!-- ── length, size, frame ─────────────────────────────────── -->\n\t\t\t\t\t\t\t\t{#if fmtOpen}\n\t\t\t\t\t\t\t\t\t<!-- One row per question, label left, choices right — the shape an\n\t\t\t\t\t\t\t\t inspector has. Stacked as three headed groups, six durations did\n\t\t\t\t\t\t\t\t not fit the panel's width and 15s fell onto a line of its own, so\n\t\t\t\t\t\t\t\t the block stopped reading as one set. A row per question makes\n\t\t\t\t\t\t\t\t that wrap impossible by construction. -->\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\trole=\"menu\"\n\t\t\t\t\t\t\t\t\t\tstyle=\"left:{panelAt.x}px; transform-origin:{panelAt.o}px bottom\"\n\t\t\t\t\t\t\t\t\t\tclass=\"pillrise absolute bottom-full z-40 mb-2 w-[19.5rem] max-w-[calc(100vw-2rem)] overflow-hidden rounded-2xl bg-[var(--st-surface)] shadow-[0_16px_44px_rgba(0,0,0,.6)] ring-1 ring-[var(--st-line)] lg:left-2! lg:z-30\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<div class=\"flex items-center gap-3 px-4 py-2.5 lg:px-3\">\n\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex items-center gap-2.5 text-sm whitespace-nowrap text-[var(--st-muted)]\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-[15px] shrink-0 opacity-80\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<circle cx=\"8\" cy=\"8\" r=\"5.6\" stroke=\"currentColor\" stroke-width=\"1.4\" />\n\t\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M8 4.8V8l2.2 1.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t\t\tLength\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"ml-auto flex gap-0.5 rounded-full p-0.5 lg:bg-[var(--st-bg)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t{#each [5, 10, 15] as sec (sec)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-pressed={wantSeconds === sec}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"{sec} seconds\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\twantSeconds = sec;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsaveSetup();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex min-h-7 min-w-7 cursor-pointer items-center justify-center rounded-full px-1.5 font-mono text-xs tabular-nums transition-colors {wantSeconds ===\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsec\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'font-semibold text-[var(--st-text)] lg:bg-[var(--st-surface-2)] lg:font-medium'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-faint)] hover:text-[var(--st-text)]'}\">{sec}</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\tclass=\"flex items-center gap-3 px-4 py-2.5 shadow-[inset_0_1px_0_var(--st-line)] lg:px-3\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex items-center gap-2.5 text-sm whitespace-nowrap text-[var(--st-muted)]\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-[15px] shrink-0 opacity-80\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<rect\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tx=\"2\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ty=\"4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\twidth=\"12\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\theight=\"8\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\trx=\"1.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M5 7.5h6\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t\t\tSize\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"ml-auto flex gap-0.5 rounded-full p-0.5 lg:bg-[var(--st-bg)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t{#each RES_KEYS as r (r)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{@const f = frameFor(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tr,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcomposerShape.portrait ? 'portrait' : 'landscape'\n\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={composerShape.fixed}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-pressed={composerShape.res === r}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"{f.width}x{f.height} — bigger frames cost render time\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\twantRes = r;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsaveSetup();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex min-h-7 items-center justify-center rounded-full px-2.5 font-mono text-xs tabular-nums transition-colors {composerShape.fixed\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'cursor-default opacity-40'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'cursor-pointer'} {composerShape.res === r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'font-semibold text-[var(--st-text)] lg:bg-[var(--st-surface-2)] lg:font-medium'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-faint)] hover:text-[var(--st-text)]'}\">{r}</button\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\tclass=\"flex items-center gap-3 px-4 py-2.5 shadow-[inset_0_1px_0_var(--st-line)] lg:px-3\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex items-center gap-2.5 text-sm whitespace-nowrap text-[var(--st-muted)]\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-[15px] shrink-0 opacity-80\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<rect\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tx=\"2.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ty=\"3\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\twidth=\"11.2\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\theight=\"10\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\trx=\"1.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t\t\tFrame\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t<!-- The one place a glyph beats the label: the thing being chosen\n\t\t\t\t\t\t\t\t\t\t IS a shape, and two rectangles say it faster than 9:16 does. -->\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"ml-auto flex gap-0.5 rounded-full p-0.5 lg:bg-[var(--st-bg)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t{#each [['portrait', '9:16', 'h-3 w-2'], ['landscape', '16:9', 'h-2 w-3.5']] as [val, label, box] (val)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={composerShape.fixed}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-pressed={(composerShape.portrait ? 'portrait' : 'landscape') ===\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tval}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle={label}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\twantOrientation = val as 'portrait' | 'landscape';\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsaveSetup();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex min-h-7 items-center gap-1.5 rounded-full px-2.5 font-mono text-xs tabular-nums transition-colors {composerShape.fixed\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'cursor-default opacity-40'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'cursor-pointer'} {(composerShape.portrait\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'portrait'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'landscape') === val\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'font-semibold text-[var(--st-text)] lg:bg-[var(--st-surface-2)] lg:font-medium'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-faint)] hover:text-[var(--st-text)]'}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"block rounded-[2px] border border-current {box}\"></span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{label}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<!-- The fourth question, and on a phone the only place it is asked.\n\t\t\t\t\t\t\t\t\t It used to be a row of three chips above the field, which wrapped\n\t\t\t\t\t\t\t\t\t to two lines and put a decision in front of somebody who had not\n\t\t\t\t\t\t\t\t\t asked for one yet. Here it sits with the other three settings,\n\t\t\t\t\t\t\t\t\t in the same shape, and the field above stays a field.\n\t\t\t\t\t\t\t\t\t Only when there is something to continue — with no clip behind it\n\t\t\t\t\t\t\t\t\t the question has no answer, and an empty row is worse than none.\n\t\t\t\t\t\t\t\t\t Desktop keeps the chips, where the pictures they wear earn their\n\t\t\t\t\t\t\t\t\t width. -->\n\t\t\t\t\t\t\t\t\t\t{#if stageContinuable}\n\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex items-center gap-3 px-4 py-2.5 shadow-[inset_0_1px_0_var(--st-line)] lg:hidden\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex items-center gap-2.5 text-sm whitespace-nowrap text-[var(--st-muted)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-[15px] shrink-0 opacity-80\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M3 8h10M9.5 4.5L13 8l-3.5 3.5\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t\t\t\tNext\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"ml-auto flex gap-0.5 rounded-full p-0.5 lg:bg-[var(--st-bg)]\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{#each [['seam', 'Last frame'], ['same', 'Same person'], ['new', 'New clip']] as [val, label] (val)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{@const on =\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tval === 'new'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? !continuing\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: !!continuing && (val === 'seam') === pinSeam}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-pressed={on}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (val === 'new') {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcontOffFor = stageContinuable?.id ?? '';\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcontinuing = null;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tspendConfirmChain();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcontOffFor = '';\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (!continuing && stageContinuable)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstartContinue(stageContinuable);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpinSeam = val === 'seam';\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex min-h-7 cursor-pointer items-center rounded-full px-2.5 text-xs transition-colors {on\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'font-semibold text-[var(--st-text)] lg:bg-[var(--st-surface-2)] lg:font-medium'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-faint)] hover:text-[var(--st-text)]'}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{label}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t{#if STAGE_UI && mode === 'simple' && wantTarget === 'clip' && refFiles.length}\n\t\t\t\t\t\t\t\t\t<!-- Attached in the chat, the way a photo is held for a character: a\n\t\t\t\t\t\t\t\t\t chip with its name and a way to take it off, and nothing to fill\n\t\t\t\t\t\t\t\t\t in. It goes to the render as a reference picture, and the writer is\n\t\t\t\t\t\t\t\t\t told it is there. A continuation does not take one, so it waits for\n\t\t\t\t\t\t\t\t\t the next new clip and says so. -->\n\t\t\t\t\t\t\t\t\t<div class=\"mb-1.5 flex flex-wrap items-center gap-1.5\">\n\t\t\t\t\t\t\t\t\t\t{#each refFiles as f (f.id)}\n\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex max-w-full min-w-0 items-center gap-1.5 rounded-full bg-[var(--st-surface-2)] px-2.5 py-1 text-xs text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"truncate\">{f.name}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"remove {f.name}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => dropRef(f.id)}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer text-[var(--st-faint)] hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>×</button\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t{#if continuing}\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-xs text-[var(--st-faint)]\">used by the next new clip</span>\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t{#if pendingPhoto}\n\t\t\t\t\t\t\t\t\t<!-- Held, not sent. A description is written after the picture is\n\t\t\t\t\t\t\t\t chosen at least as often as before it, so the upload waits here\n\t\t\t\t\t\t\t\t and takes whatever is in the box when send is pressed. -->\n\t\t\t\t\t\t\t\t\t<div class=\"mb-1.5 flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\tclass=\"flex min-w-0 items-center gap-1.5 rounded-full bg-[var(--st-surface-2)] px-2.5 py-1 text-xs text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"truncate\">{pendingPhoto.name}</span>\n\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"drop the photo\"\n\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => (pendingPhoto = null)}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer text-[var(--st-faint)] hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t\t>×</button\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t<span class=\"text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t\t\t\tdescribe {wantTarget === 'location' ? 'the place' : 'them'} if you like, then send\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t<!-- One row: the way in, the sentence, the mode, the send. Everything\n\t\t\t\t\t\t\t that used to sit under this in three rows of chips either became a\n\t\t\t\t\t\t\t chip above (because you chose it) or moved into the menu on the left\n\t\t\t\t\t\t\t (because you had not). -->\n\t\t\t\t\t\t\t\t<!-- Two rows on a phone, one on a desktop.\n\t\t\t\t\t\t\t\t The sentence is the thing; sharing a line with three controls left it\n\t\t\t\t\t\t\t\t a box one line tall with the placeholder clipped at the ends. Wrapped,\n\t\t\t\t\t\t\t\t the field takes the width and starts at the wall, and the controls sit\n\t\t\t\t\t\t\t\t under it where they read as what you reach for after writing rather\n\t\t\t\t\t\t\t\t than as furniture around the writing. -->\n\t\t\t\t\t\t\t\t<div class=\"flex flex-wrap items-end gap-1.5 lg:flex-nowrap\">\n\t\t\t\t\t\t\t\t\t{#if mode === 'simple' && wantTarget === 'clip'}\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\taria-label=\"add a character or a location\"\n\t\t\t\t\t\t\t\t\t\t\taria-expanded={addOpen}\n\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\tconst open = !addOpen && pickKind === null;\n\t\t\t\t\t\t\t\t\t\t\t\tshutMenus();\n\t\t\t\t\t\t\t\t\t\t\t\taddOpen = open;\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\tclass=\"flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-full text-[var(--st-muted)] transition-colors hover:bg-[var(--st-bg)] hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-[1.05rem]\"\n\t\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M8 3v10M3 8h10\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.5\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t<!-- Out of the menu and next to the plus.\n\t\t\t\t\t\t\t\t\t\t\t It was the third row of a list you had to open, which is two\n\t\t\t\t\t\t\t\t\t\t\t taps and a read for the one thing here that is not a decision:\n\t\t\t\t\t\t\t\t\t\t\t you either have a picture or you do not. The people who attach\n\t\t\t\t\t\t\t\t\t\t\t one are the ones who finish — sixty-three per cent of the\n\t\t\t\t\t\t\t\t\t\t\t accounts with a reference rendered something, against nine of\n\t\t\t\t\t\t\t\t\t\t\t those without — so it is the last control that should be\n\t\t\t\t\t\t\t\t\t\t\t filed away behind a menu.\n\t\t\t\t\t\t\t\t\t\t\t Above the menus' dismiss backdrop (z-20, under the menus' z-30):\n\t\t\t\t\t\t\t\t\t\t\t that backdrop is `fixed`, but inside .composerglass, whose\n\t\t\t\t\t\t\t\t\t\t\t backdrop-filter makes the composer its containing block, so it\n\t\t\t\t\t\t\t\t\t\t\t covered the composer and nothing else. With a menu open, the tap meant for the\n\t\t\t\t\t\t\t\t\t\t\t paperclip only closed the menu, and it took a second one — the\n\t\t\t\t\t\t\t\t\t\t\t studio's rage clicks. Now the tap reaches the picker and closes\n\t\t\t\t\t\t\t\t\t\t\t the menus on the way. -->\n\t\t\t\t\t\t\t\t\t\t<div class=\"group relative z-[21] shrink-0\">\n\t\t\t\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\t\t\t\taria-busy={refBusy}\n\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"reference image\"\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"flex size-9 {refBusy\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'cursor-wait opacity-60'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: 'cursor-pointer'} items-center justify-center rounded-full text-[var(--st-muted)] transition-colors hover:bg-[var(--st-bg)] hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{#if refBusy}<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-4 animate-spin rounded-full border-2 border-current border-t-transparent\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t></span>{:else}<svg\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 20 20\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-[1.05rem]\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M13 7l-5.5 5.5a2.1 2.1 0 003 3L16 10a3.5 3.5 0 00-5-5l-5.5 5.5a5 5 0 007 7L18 12\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.6\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</svg>{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"file\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tmultiple\n\t\t\t\t\t\t\t\t\t\t\t\t\taccept=\"image/*\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"hidden\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={refBusy}\n\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tshutMenus();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tguardUpload(e);\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\tonchange={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst el = e.currentTarget as HTMLInputElement;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tattachRefs(el.files);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tel.value = '';\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tshutMenus();\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t\t\t\t<!-- Its own label rather than `title`: the browser's takes a\n\t\t\t\t\t\t\t\t\t\t\t\t second to appear and arrives in the operating system's\n\t\t\t\t\t\t\t\t\t\t\t\t styling, which on a surface this dark reads as a fault. -->\n\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"pointer-events-none absolute bottom-full left-1/2 mb-1.5 -translate-x-1/2 rounded-md bg-[var(--st-surface-2)] px-2 py-1 text-[11px] whitespace-nowrap text-[var(--st-text)] opacity-0 shadow-[0_6px_20px_rgba(0,0,0,.5)] transition-opacity duration-150 group-hover:opacity-100\"\n\t\t\t\t\t\t\t\t\t\t\t\t>reference image</span\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t<!-- In a creation state there is nothing to pick between, so the\n\t\t\t\t\t\t\t\t\t paperclip is the whole menu and stands on its own. Above the\n\t\t\t\t\t\t\t\t\t menus' backdrop for the same reason as the one above. -->\n\t\t\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\t\t\taria-busy={refBusy}\n\t\t\t\t\t\t\t\t\t\t\ttitle={wantTarget === 'character'\n\t\t\t\t\t\t\t\t\t\t\t\t? 'Use a picture you already have as this character'\n\t\t\t\t\t\t\t\t\t\t\t\t: wantTarget === 'location'\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Use a picture you already have as this location'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: 'Attach a face, a room, a movement for the render to copy'}\n\t\t\t\t\t\t\t\t\t\t\tclass=\"relative z-[21] flex size-9 shrink-0 {refBusy\n\t\t\t\t\t\t\t\t\t\t\t\t? 'cursor-wait opacity-60'\n\t\t\t\t\t\t\t\t\t\t\t\t: 'cursor-pointer'} items-center justify-center rounded-full text-[var(--st-muted)] transition-colors hover:bg-[var(--st-bg)] hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{#if refBusy}<span\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-4 animate-spin rounded-full border-2 border-current border-t-transparent\"\n\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t></span>{:else}<svg\n\t\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 20 20\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-4\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M13 7l-5.5 5.5a2.1 2.1 0 003 3L16 10a3.5 3.5 0 00-5-5l-5.5 5.5a5 5 0 007 7L18 12\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.6\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t</svg>{/if}\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"sr-only\">use a picture you already have</span>\n\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"file\"\n\t\t\t\t\t\t\t\t\t\t\t\taccept=\"image/*\"\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"hidden\"\n\t\t\t\t\t\t\t\t\t\t\t\tdisabled={refBusy}\n\t\t\t\t\t\t\t\t\t\t\t\tonclick={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\tshutMenus();\n\t\t\t\t\t\t\t\t\t\t\t\t\tguardUpload(e);\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\tonchange={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst el = e.currentTarget as HTMLInputElement;\n\t\t\t\t\t\t\t\t\t\t\t\t\tholdPhoto(el.files);\n\t\t\t\t\t\t\t\t\t\t\t\t\tel.value = '';\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t\t\t\t<label class=\"sr-only\" for=\"composer\">Message</label>\n\t\t\t\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\t\t\t\tid=\"composer\"\n\t\t\t\t\t\t\t\t\t\tbind:this={composer}\n\t\t\t\t\t\t\t\t\t\tbind:value={input}\n\t\t\t\t\t\t\t\t\t\trows=\"1\"\n\t\t\t\t\t\t\t\t\t\tspellcheck=\"false\"\n\t\t\t\t\t\t\t\t\t\tplaceholder={composerPlaceholder}\n\t\t\t\t\t\t\t\t\t\toninput={(e) => grow(e.currentTarget)}\n\t\t\t\t\t\t\t\t\t\tonkeydown={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t// Enter sends, Shift+Enter breaks the line.\n\t\t\t\t\t\t\t\t\t\t\tif (e.key === 'Enter' && !e.shiftKey) {\n\t\t\t\t\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t\t\t\t\tsubmit();\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\tclass=\"order-first block max-h-56 min-h-9 w-full flex-none basis-full resize-none border-0 bg-transparent px-1 py-2 text-[1.05rem] leading-relaxed outline-none placeholder:text-[var(--st-faint)] focus:ring-0 lg:order-none lg:flex-1 lg:basis-auto lg:px-2\"\n\t\t\t\t\t\t\t\t\t></textarea>\n\n\t\t\t\t\t\t\t\t\t{#if mode === 'advanced' && !planningWs}\n\t\t\t\t\t\t\t\t\t\t<!-- Scene count is the planning chain's knob: it decides how many\n\t\t\t\t\t\t\t\t\t documents get written and how many clips get scheduled. It has\n\t\t\t\t\t\t\t\t\t no simple-mode counterpart, so it sits here rather than in the\n\t\t\t\t\t\t\t\t\t menu, which is about what a clip is made with. -->\n\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\tclass=\"flex shrink-0 items-center gap-0.5 rounded-full bg-[var(--st-bg)] p-0.5\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{#each SCENE_CHOICES as n (n)}\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\taria-pressed={sceneCount === n}\n\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"{n} scenes\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"size-7 cursor-pointer rounded-full text-xs tabular-nums transition-colors {sceneCount ===\n\t\t\t\t\t\t\t\t\t\t\t\t\tn\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-[var(--st-surface-2)] font-semibold text-[var(--st-text)]'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'text-[var(--st-faint)] hover:text-[var(--st-text)]'}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsceneCount = n;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (brief) brief.sceneCount = n;\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}>{n}</button\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\taria-label=\"send\"\n\t\t\t\t\t\t\t\t\t\tdisabled={sending || charFromClipBusy || (!input.trim() && !pendingPhoto)}\n\t\t\t\t\t\t\t\t\t\tonclick={submit}\n\t\t\t\t\t\t\t\t\t\tclass=\"ml-auto flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-full bg-[var(--st-accent)] text-[var(--st-on-accent)] transition-colors hover:bg-[var(--st-accent-strong)] disabled:cursor-default disabled:bg-[var(--st-surface-2)] disabled:text-[var(--st-faint)] lg:ml-0\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{#if sending}\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-xs\">…</span>\n\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t<svg viewBox=\"0 0 20 20\" class=\"h-4 w-4\" fill=\"none\" aria-hidden=\"true\">\n\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M10 16V4M10 4l-5 5M10 4l5 5\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"2\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t<!-- ── desktop task rail ────────────────────────────────────────── -->\n\t\t\t\t{#if brief}\n\t\t\t\t\t<aside class=\"hidden min-h-0 lg:block\">\n\t\t\t\t\t\t<div class=\"scroller max-h-full overflow-y-auto rounded-2xl bg-[var(--st-surface)] p-5\">\n\t\t\t\t\t\t\t<p class=\"text-[10px] font-bold tracking-[0.25em] text-[var(--st-faint)] uppercase\">\n\t\t\t\t\t\t\t\tthe production\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t{#if pollingActive && startedAt}\n\t\t\t\t\t\t\t\t<p class=\"mt-1 text-xs text-[var(--st-faint)]\">{elapsedLabel(now - startedAt)}</p>\n\t\t\t\t\t\t\t{:else if staleRun}\n\t\t\t\t\t\t\t\t<p class=\"mt-1 text-xs text-[var(--st-faint)]\">not running</p>\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t<div class=\"mt-4\">\n\t\t\t\t\t\t\t\t{@render railList()}\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t<!-- Beneath the list, not above it: reaching for this means having\n\t\t\t\t\t\t     read the list and decided the run is not worth continuing. -->\n\t\t\t\t\t\t\t{#if pollingActive}\n\t\t\t\t\t\t\t\t<div class=\"mt-5 border-t border-[var(--st-line)] pt-4\">\n\t\t\t\t\t\t\t\t\t{#if !stopArmed}\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer text-xs text-[var(--st-faint)] underline-offset-4 transition-colors hover:text-[var(--st-text)] hover:underline\"\n\t\t\t\t\t\t\t\t\t\t\tonclick={() => (stopArmed = true)}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\tstop this production\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t<p class=\"text-xs leading-relaxed text-[var(--st-muted)]\">\n\t\t\t\t\t\t\t\t\t\t\tThis releases the GPU and clears the queue. It cannot be resumed — a new run\n\t\t\t\t\t\t\t\t\t\t\tstarts from the plan again.\n\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t<div class=\"mt-2.5 flex items-center gap-3\">\n\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\tdisabled={stopping}\n\t\t\t\t\t\t\t\t\t\t\t\tonclick={stopRun}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"btn btn-secondary btn-sm\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{stopping ? 'stopping…' : 'stop it'}\n\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"cursor-pointer text-xs text-[var(--st-faint)] hover:text-[var(--st-text)]\"\n\t\t\t\t\t\t\t\t\t\t\t\tonclick={() => (stopArmed = false)}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\tkeep going\n\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</aside>\n\t\t\t\t{/if}\n\t\t\t</div>\n\t\t</div>\n\t</main>\n\n\t<!-- The counterweight on the right, and — from xl up — a standing reservation\n\t\t on both sides, so the rail costs the page nothing to open.\n\n\t\t In the flow the rail takes 16rem off the left, and this gives the same back\n\t\t on the right, which keeps the reading column's centre on the screen's\n\t\t centre. Centred is not the same as unchanged: it still cost 32rem of width,\n\t\t so opening the list resized the composer and rewrapped every line above it,\n\t\t and a control that changes size because you opened something else has been\n\t\t moved by something that has nothing to do with it.\n\n\t\t Floating the rail over the gutter instead fixed the resizing and broke\n\t\t worse: out of the flow it covered the composer's left edge, and main\n\t\t stretched across the whole window. So the space is simply always there.\n\t\t Where there is room for it — xl and up — the same 32rem is reserved whether\n\t\t the rail is open or shut, one side standing in for the rail while it is\n\t\t away. Nothing moves, nothing is covered, and the page keeps the proportions\n\t\t it has with the list open, which is the state it is read in.\n\n\t\t Below xl there is no room to hold 32rem open for a panel that is not there,\n\t\t so that band keeps the counterweight alone.\n\n\t\t Spacers rather than a padding rule on main: the rule has to be conditional,\n\t\t and a conditional Tailwind variant is not reliably found by the scanner\n\t\t while a scoped attribute selector is dropped by Svelte's pruner. Both\n\t\t failed silently. These are static classes inside an if, which cannot.\n\t\t Below lg the rail is an overlay and owes nothing. -->\n\t{#if sidebarOpen}\n\t\t<div class=\"hidden w-64 shrink-0 lg:block\" aria-hidden=\"true\"></div>\n\t{:else}\n\t\t<div class=\"hidden w-64 shrink-0 xl:block\" aria-hidden=\"true\"></div>\n\t{/if}\n\n\t<!-- ── the film viewer ────────────────────────────────────────────────────────\n\t\t Watch the cut before paying to assemble it. One clip at a time, chained on\n\t\t `ended`, with the reel along the bottom saying where you are. No keep and\n\t\t no add: nothing here is a draft you are choosing between. -->\n\t{#if filmAt !== null && film[filmAt]}\n\t\t{@const shot = film[filmAt]}\n\t\t<div\n\t\t\tclass=\"fixed inset-0 z-50 flex flex-col items-center justify-center bg-black/92 px-6 py-12 backdrop-blur-[28px]\"\n\t\t\trole=\"dialog\"\n\t\t\taria-modal=\"true\"\n\t\t\taria-label=\"the film\"\n\t\t>\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\taria-label=\"close\"\n\t\t\t\tonclick={shutFilmViewer}\n\t\t\t\tclass=\"absolute top-5 right-5 flex size-11 cursor-pointer items-center justify-center rounded-full bg-white/10 text-sm text-[var(--st-text)] backdrop-blur-md transition-colors hover:bg-white/20\"\n\t\t\t>\n\t\t\t\t✕\n\t\t\t</button>\n\t\t\t{#if film.length > 1}\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\taria-label=\"previous shot\"\n\t\t\t\t\tonclick={() => stepFilm(-1)}\n\t\t\t\t\tclass=\"absolute top-1/2 left-6 hidden size-11 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full bg-white/10 text-xl text-[var(--st-text)] backdrop-blur-md transition-colors hover:bg-white/20 sm:flex\"\n\t\t\t\t>\n\t\t\t\t\t‹\n\t\t\t\t</button>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\taria-label=\"next shot\"\n\t\t\t\t\tonclick={() => stepFilm(1)}\n\t\t\t\t\tclass=\"absolute top-1/2 right-6 hidden size-11 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full bg-white/10 text-xl text-[var(--st-text)] backdrop-blur-md transition-colors hover:bg-white/20 sm:flex\"\n\t\t\t\t>\n\t\t\t\t\t›\n\t\t\t\t</button>\n\t\t\t{/if}\n\n\t\t\t<div\n\t\t\t\tclass=\"lift stage overflow-hidden rounded-2xl bg-black shadow-[0_24px_70px_rgba(0,0,0,.6)]\"\n\t\t\t>\n\t\t\t\t{#key filmKey(shot)}\n\t\t\t\t\t<!-- svelte-ignore a11y_media_has_caption -->\n\t\t\t\t\t<video\n\t\t\t\t\t\tsrc={still(fileUrl(shot.workspace, shot.artifact, shot.file))}\n\t\t\t\t\t\tcontrols\n\t\t\t\t\t\tautoplay\n\t\t\t\t\t\tplaysinline\n\t\t\t\t\t\tpreload=\"auto\"\n\t\t\t\t\t\tonended={nextShot}\n\t\t\t\t\t\tclass=\"video-with-controls block aspect-video w-full bg-black\"\n\t\t\t\t\t></video>\n\t\t\t\t{/key}\n\t\t\t</div>\n\n\t\t\t<div class=\"lift stage mt-4 flex flex-wrap items-center gap-2\">\n\t\t\t\t<span class=\"text-[13px] font-medium text-[var(--st-text)]\">The film</span>\n\t\t\t\t<span class=\"text-xs text-[var(--st-faint)] tabular-nums\">\n\t\t\t\t\tshot {filmAt + 1} of {film.length} · {filmSeconds}s\n\t\t\t\t</span>\n\t\t\t</div>\n\n\t\t\t{#if film.length > 1}\n\t\t\t\t<div class=\"lift stage mt-4 flex justify-center gap-1.5 overflow-x-auto\">\n\t\t\t\t\t{#each film as c, i (filmKey(c))}\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\taria-label=\"shot {i + 1}\"\n\t\t\t\t\t\t\taria-current={i === filmAt}\n\t\t\t\t\t\t\tonclick={() => (filmAt = i)}\n\t\t\t\t\t\t\tclass=\"aspect-video w-[min(16rem,22vw)] min-w-[6rem] shrink-0 cursor-pointer overflow-hidden rounded-lg bg-[var(--st-surface)] transition-opacity {i ===\n\t\t\t\t\t\t\tfilmAt\n\t\t\t\t\t\t\t\t? 'opacity-100 shadow-[inset_0_0_0_2px_var(--st-text)]'\n\t\t\t\t\t\t\t\t: 'opacity-50 hover:opacity-80'}\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<!-- svelte-ignore a11y_media_has_caption -->\n\t\t\t\t\t\t\t<video\n\t\t\t\t\t\t\t\tsrc={still(fileUrl(c.workspace, c.artifact, c.file))}\n\t\t\t\t\t\t\t\tmuted\n\t\t\t\t\t\t\t\tloop\n\t\t\t\t\t\t\t\tplaysinline\n\t\t\t\t\t\t\t\tpreload=\"auto\"\n\t\t\t\t\t\t\t\tuse:looping\n\t\t\t\t\t\t\t\tclass=\"h-full w-full bg-black object-cover\"\n\t\t\t\t\t\t\t></video>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t{/each}\n\t\t\t\t</div>\n\t\t\t{/if}\n\t\t</div>\n\t{/if}\n\n\t<!-- ── the takes viewer ───────────────────────────────────────────────────────\n\t\t The room for choosing. The clip is 1024 across in here, which is the width\n\t\t it was rendered at, so this is the one place it is never resampled — the\n\t\t transcript column tops out at 720. On a short window the picture gives way\n\t\t first and the bar and the filmstrip stay put, because a viewer you cannot\n\t\t reach the controls of is not a viewer. -->\n\t{#if takesAt}\n\t\t{@const item = chat.find((c) => c.id === takesAt?.id)}\n\t\t{@const runs = readyTakes(takesAt.id)}\n\t\t{@const run = runs.find((r) => r.index === takesAt?.index)}\n\t\t{#if item && run?.clip}\n\t\t\t{@const row = logRow[run.clip.workspace]}\n\t\t\t<div\n\t\t\t\tclass=\"fixed inset-0 z-50 flex flex-col items-center justify-center bg-black/92 px-6 py-12 backdrop-blur-3xl\"\n\t\t\t\trole=\"dialog\"\n\t\t\t\taria-modal=\"true\"\n\t\t\t\taria-label=\"the takes of this beat\"\n\t\t\t>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\taria-label=\"close\"\n\t\t\t\t\tonclick={shutTake}\n\t\t\t\t\tclass=\"absolute top-5 right-5 flex size-11 cursor-pointer items-center justify-center rounded-full bg-white/10 text-sm text-[var(--st-text)] backdrop-blur-md transition-colors hover:bg-white/20\"\n\t\t\t\t>\n\t\t\t\t\t✕\n\t\t\t\t</button>\n\t\t\t\t{#if runs.length > 1}\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\taria-label=\"previous take\"\n\t\t\t\t\t\tonclick={() => stepTake(-1)}\n\t\t\t\t\t\tclass=\"absolute top-1/2 left-6 hidden size-11 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full bg-white/10 text-xl text-[var(--st-text)] backdrop-blur-md transition-colors hover:bg-white/20 sm:flex\"\n\t\t\t\t\t>\n\t\t\t\t\t\t‹\n\t\t\t\t\t</button>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\taria-label=\"next take\"\n\t\t\t\t\t\tonclick={() => stepTake(1)}\n\t\t\t\t\t\tclass=\"absolute top-1/2 right-6 hidden size-11 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full bg-white/10 text-xl text-[var(--st-text)] backdrop-blur-md transition-colors hover:bg-white/20 sm:flex\"\n\t\t\t\t\t>\n\t\t\t\t\t\t›\n\t\t\t\t\t</button>\n\t\t\t\t{/if}\n\n\t\t\t\t<div\n\t\t\t\t\tclass=\"lift stage overflow-hidden rounded-2xl bg-black shadow-[0_24px_70px_rgba(0,0,0,.6)]\"\n\t\t\t\t\tontouchstart={swipeStart}\n\t\t\t\t\tontouchend={swipeEnd}\n\t\t\t\t>\n\t\t\t\t\t<!-- Keyed so that stepping to another take replaces the element rather\n\t\t\t\t\t\t than swapping its src: a <video> handed a new src keeps the old\n\t\t\t\t\t\t frame on screen until the new one decodes, which reads as the arrow\n\t\t\t\t\t\t having done nothing. -->\n\t\t\t\t\t{#key run.slug}\n\t\t\t\t\t\t<!-- svelte-ignore a11y_media_has_caption -->\n\t\t\t\t\t\t<video\n\t\t\t\t\t\t\tsrc={still(fileUrl(run.clip.workspace, run.clip.artifact, run.clip.file))}\n\t\t\t\t\t\t\tcontrols\n\t\t\t\t\t\t\tautoplay\n\t\t\t\t\t\t\tmuted\n\t\t\t\t\t\t\tloop\n\t\t\t\t\t\t\tplaysinline\n\t\t\t\t\t\t\tpreload=\"auto\"\n\t\t\t\t\t\t\tclass=\"video-with-controls block aspect-video w-full bg-black\"\n\t\t\t\t\t\t></video>\n\t\t\t\t\t{/key}\n\t\t\t\t</div>\n\n\t\t\t\t<div class=\"lift stage mt-4 flex flex-wrap items-center gap-2\">\n\t\t\t\t\t<span class=\"text-[13px] font-medium text-[var(--st-text)] tabular-nums\"\n\t\t\t\t\t\t>Take {run.index}</span\n\t\t\t\t\t>\n\t\t\t\t\t{#if row}\n\t\t\t\t\t\t<span class=\"text-xs text-[var(--st-faint)] tabular-nums\">seed {row.seed}</span>\n\t\t\t\t\t{/if}\n\t\t\t\t\t<span class=\"flex-1\"></span>\n\t\t\t\t\t<!-- On the take you already kept there is nothing to press: a button\n\t\t\t\t\t\t that repeats a decision you have made is a control that does\n\t\t\t\t\t\t nothing, dressed as one that does something. -->\n\t\t\t\t\t{#if item.takes?.kept === run.index}\n\t\t\t\t\t\t<span class=\"text-xs text-[var(--st-faint)]\">kept</span>\n\t\t\t\t\t{:else}\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tonclick={() => keepTake(item.id, run.index)}\n\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-full bg-[var(--st-text)] px-3.5 py-1.5 text-xs font-medium text-black transition-colors hover:bg-white\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tuse this take\n\t\t\t\t\t\t</button>\n\t\t\t\t\t{/if}\n\t\t\t\t\t{#if run.clip}\n\t\t\t\t\t\t{@const already = film.some((x) => filmKey(x) === filmKey(run.clip!))}\n\t\t\t\t\t\t{#if already}\n\t\t\t\t\t\t\t<span class=\"flex items-center gap-1.5 text-xs text-[var(--st-faint)]\">\n\t\t\t\t\t\t\t\t<span aria-hidden=\"true\">✓</span><span>in the film</span>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonclick={() => addClipToFilm(run.clip!, `Take ${run.index}`)}\n\t\t\t\t\t\t\t\tclass=\"cursor-pointer rounded-full bg-white/10 px-3.5 py-1.5 text-xs text-[var(--st-text)] transition-colors hover:bg-white/20\"\n\t\t\t\t\t\t\t\t>add to film</button\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t{/if}\n\t\t\t\t</div>\n\n\t\t\t\t{#if runs.length > 1}\n\t\t\t\t\t<div class=\"st-takes st-film lift stage mt-4\" data-n={runs.length}>\n\t\t\t\t\t\t{#each runs as r (r.slug)}\n\t\t\t\t\t\t\t{@render takeTile(item, r, true)}\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t</div>\n\t\t\t\t{/if}\n\t\t\t</div>\n\t\t{/if}\n\t{/if}\n</div>\n\n<!-- The shop, when a render costs more than the purse holds.\n\t The host's own, unchanged: the same packages, the same checkout, the same\n\t place the money would be spent from anywhere else in the app. It returns to\n\t this page, so the render that ran out is one press away rather than a\n\t navigation and a scroll away. -->\n<BuyTokensModal\n\topen={buyOpen}\n\tonclose={() => {\n\t\tbuyOpen = false;\n\t\t// They may have bought something. Ask, rather than go on refusing on the\n\t\t// strength of the number that sent them there.\n\t\tvoid refreshTokens();\n\t}}\n\treason={buyReason}\n\tsuccessPath=\"/studio\"\n\tsource={buySource}\n/>\n\n<Sessions onNeedTokens={askForSessionTokens} />\n\n<!-- The film, the moment it exists.\n\t Export used to end with a file on disk and a card somewhere below the fold,\n\t which is a strange way to hand somebody the thing they have been assembling\n\t all afternoon. Closing this loses nothing: it is on the shelf. -->\n<!-- One item from the front page, at the size it was made for. Nothing here\n\t edits or continues it: this is the shelf, and the shelf is for looking.\n\t The wall stays visible behind it. At ninety-four per cent black this was a\n\t page rather than a layer — you pressed a tile and the studio appeared to be\n\t gone, which is the one thing a preview must not do. Two thirds and a light\n\t blur reads as something opened ON the page: the shelf is still there,\n\t recognisably where you left it, and closing puts you back on the tile you\n\t pressed rather than somewhere you have to find again. -->\n{#if mediaPopup}\n\t<div\n\t\tclass=\"fixed inset-0 z-[70] flex items-center justify-center bg-black/65 p-4 backdrop-blur-md sm:p-8\"\n\t\trole=\"dialog\"\n\t\taria-modal=\"true\"\n\t\taria-label=\"clip\"\n\t>\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\taria-label=\"close\"\n\t\t\tonclick={() => (mediaPopup = null)}\n\t\t\tclass=\"fixed inset-0 cursor-default\"\n\t\t></button>\n\t\t<!-- svelte-ignore a11y_media_has_caption -->\n\t\t<video\n\t\t\tsrc={shelfUrl(mediaPopup)}\n\t\t\tcontrols\n\t\t\tautoplay\n\t\t\tloop\n\t\t\tplaysinline\n\t\t\tclass=\"video-with-controls relative max-h-full max-w-full rounded-2xl bg-black\"\n\t\t></video>\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\taria-label=\"close\"\n\t\t\tonclick={() => (mediaPopup = null)}\n\t\t\tclass=\"fixed top-5 right-5 z-10 flex size-11 cursor-pointer items-center justify-center rounded-full bg-white/10 text-sm text-[var(--st-text)] backdrop-blur-md transition-colors hover:bg-white/20\"\n\t\t>\n\t\t\t✕\n\t\t</button>\n\t</div>\n{/if}\n\n{#if filmPopup}\n\t<div\n\t\tclass=\"fixed inset-0 z-[60] flex flex-col items-center justify-center gap-4 bg-black/92 px-6 py-12 backdrop-blur-[28px]\"\n\t\trole=\"dialog\"\n\t\taria-modal=\"true\"\n\t\taria-label=\"the finished film\"\n\t>\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\taria-label=\"close\"\n\t\t\tonclick={() => (filmPopup = null)}\n\t\t\tclass=\"absolute top-5 right-5 flex size-11 cursor-pointer items-center justify-center rounded-full bg-white/10 text-sm text-[var(--st-text)] backdrop-blur-md transition-colors hover:bg-white/20\"\n\t\t>\n\t\t\t✕\n\t\t</button>\n\t\t<!-- svelte-ignore a11y_media_has_caption -->\n\t\t<video\n\t\t\tsrc={still(fileUrl(filmPopup.workspace, filmPopup.artifact, filmPopup.file))}\n\t\t\tcontrols\n\t\t\tautoplay\n\t\t\tplaysinline\n\t\t\tclass=\"video-with-controls max-h-[70vh] max-w-full rounded-2xl bg-black shadow-[0_24px_70px_rgba(0,0,0,.6)]\"\n\t\t></video>\n\t\t<p class=\"text-xs text-[var(--st-faint)] tabular-nums\">\n\t\t\t{filmPopup.parts} shots · {Math.round(filmPopup.seconds)}s\n\t\t</p>\n\t</div>\n{/if}\n\n<!-- Everything that has been finished, at once. -->\n{#if mediaOpen}\n\t<!-- The picker. The front page is a list somebody made, so there has to be a\n\t\t place to make it — and the only honest one is everything the studio has,\n\t\t with a mark on what is already up. A rule cannot do this job: length says\n\t\t a thing was assembled, never that it was any good, and the front page is\n\t\t the one surface where that difference is the whole point. -->\n\t<div\n\t\tclass=\"fixed inset-0 z-[60] overflow-y-auto bg-black/94 px-6 py-14 backdrop-blur-[28px]\"\n\t\trole=\"dialog\"\n\t\taria-modal=\"true\"\n\t\taria-label=\"choose what is on the front page\"\n\t>\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\taria-label=\"close\"\n\t\t\tonclick={() => (mediaOpen = false)}\n\t\t\tclass=\"fixed top-5 right-5 z-10 flex size-11 cursor-pointer items-center justify-center rounded-full bg-white/10 text-sm text-[var(--st-text)] backdrop-blur-md transition-colors hover:bg-white/20\"\n\t\t>\n\t\t\t✕\n\t\t</button>\n\t\t<h2 class=\"mb-1 text-center font-display text-lg font-semibold\">Choose the front page</h2>\n\t\t<p class=\"mb-5 text-center text-xs text-[var(--st-faint)]\">\n\t\t\t{pins.length} pinned of {pool.length}\n\t\t</p>\n\t\t<div class=\"mx-auto grid max-w-6xl grid-cols-3 gap-2 sm:grid-cols-5 lg:grid-cols-7\">\n\t\t\t{#each pool as m (m.id)}\n\t\t\t\t{@const on = pins.includes(m.id)}\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\taria-pressed={on}\n\t\t\t\t\taria-label=\"{on ? 'remove from' : 'add to'} the front page\"\n\t\t\t\t\tonclick={() => togglePin(m.id)}\n\t\t\t\t\tclass=\"group relative aspect-square cursor-pointer overflow-hidden rounded-xl bg-[var(--st-surface)] transition-opacity {on\n\t\t\t\t\t\t? 'opacity-100 ring-2 ring-[var(--st-text)]'\n\t\t\t\t\t\t: 'opacity-55 hover:opacity-90'}\"\n\t\t\t\t>\n\t\t\t\t\t<!-- svelte-ignore a11y_media_has_caption -->\n\t\t\t\t\t<video\n\t\t\t\t\t\tuse:whenSeen={still(`/studio/api/media?id=${m.id}`)}\n\t\t\t\t\t\tmuted\n\t\t\t\t\t\tplaysinline\n\t\t\t\t\t\tpreload=\"metadata\"\n\t\t\t\t\t\tclass=\"h-full w-full object-cover\"\n\t\t\t\t\t></video>\n\t\t\t\t\t<span\n\t\t\t\t\t\tclass=\"pointer-events-none absolute right-1 bottom-1 rounded bg-black/60 px-1 font-mono text-[0.6rem] leading-4 text-white\"\n\t\t\t\t\t\t>{clipClock(m.seconds)}</span\n\t\t\t\t\t>\n\t\t\t\t\t{#if on}\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tclass=\"pointer-events-none absolute top-1 left-1 rounded-full bg-[var(--st-text)] px-1.5 text-[0.6rem] leading-4 font-semibold text-black\"\n\t\t\t\t\t\t\t>pinned</span\n\t\t\t\t\t\t>\n\t\t\t\t\t{/if}\n\t\t\t\t</button>\n\t\t\t{/each}\n\t\t</div>\n\t</div>\n{/if}\n\n<style>\n\t/** On the front page the wall does not stop above the composer, it runs under\n\t *  it. Stopping short leaves a band of empty black between the last tile and\n\t *  the field, which is the seam this was trying to lose — the picture should\n\t *  carry on behind the controls and be hidden by them, the way a library\n\t *  scrolls under its own search bar.\n\t *\n\t *  The negative margin is what does it: in a flex column it shrinks the item's\n\t *  outer box, so the column hands the element that height back and the next\n\t *  sibling — the composer — moves up over it. The padding underneath gives the\n\t *  last row somewhere to go, so it can be scrolled clear of the field rather\n\t *  than being permanently behind it.\n\t *\n\t *  And no fade on this one. The mask above exists for an edge with nothing\n\t *  past it; here there is something past it — the composer — and a picture\n\t *  that dissolves just before reaching it never arrives underneath, which is\n\t *  the whole effect. A library scrolls under its own search bar and is hidden\n\t *  by it, not faded out in front of it. */\n\t/** The scrollbar belongs to the window's edge, and should be almost invisible.\n\t *\n\t *  Two faults, one rule. It sat twenty points in from the right because the\n\t *  column it lives in is padded, so the bar floated in the middle of the page\n\t *  with content on both sides of it — a scrollbar is furniture and furniture\n\t *  goes against the wall. The negative margin pushes the scroll box out to the\n\t *  edge and the matching padding puts the content back where it was, so\n\t *  nothing moves but the bar.\n\t *\n\t *  And it was the brightest thing on a page of dark pictures. Seven points\n\t *  wide, seven per cent white, no track at all: present when you look for it,\n\t *  gone when you are looking at a film. It lifts to fourteen on hover, which\n\t *  is the only moment anybody is aiming at it.\n\t *\n\t *  Desktop only. A phone has overlay scrollbars that already do all of this\n\t *  and reserve no width, and styling them would take width away from the one\n\t *  screen that has none to give. */\n\t@media (min-width: 640px) {\n\t\t.stagescroll {\n\t\t\tmargin-right: -1.25rem;\n\t\t\tpadding-right: 1.25rem;\n\t\t\tscrollbar-width: thin;\n\t\t\tscrollbar-color: rgba(255, 255, 255, 0.07) transparent;\n\t\t}\n\t\t.stagescroll::-webkit-scrollbar {\n\t\t\twidth: 7px;\n\t\t}\n\t\t.stagescroll::-webkit-scrollbar-track {\n\t\t\tbackground: transparent;\n\t\t}\n\t\t.stagescroll::-webkit-scrollbar-thumb {\n\t\t\tbackground: rgba(255, 255, 255, 0.07);\n\t\t\tborder-radius: 99px;\n\t\t}\n\t\t.stagescroll:hover::-webkit-scrollbar-thumb {\n\t\t\tbackground: rgba(255, 255, 255, 0.14);\n\t\t}\n\t}\n\n\t/* The row scrolls but never shows a bar: the peeking card and the dots\n\t   already say it moves, and a scrollbar under three cards is furniture. */\n\t.starterrow {\n\t\tscrollbar-width: none;\n\t}\n\t.starterrow::-webkit-scrollbar {\n\t\tdisplay: none;\n\t}\n\n\t/** The shot card is glass too, lightly.\n\t *\n\t *  On the stage this card sits over the picture, and a solid panel there is a\n\t *  hole cut in the clip you are about to extend. Translucent, it stays a layer\n\t *  on top of the thing rather than a replacement for it. In the transcript\n\t *  there is only black behind it and the blur costs nothing — the same rule\n\t *  reads correctly in both places, which is why it is one rule.\n\t *\n\t *  The specular line along the top is the whole difference between glass and\n\t *  a grey box: it is where light would catch a real edge, and without it the\n\t *  material reads as transparency rather than as a surface. One pixel, inset,\n\t *  fourteen per cent.\n\t *\n\t *  Seventy per cent, not less. This card carries the sentence somebody is\n\t *  agreeing to and a button that spends money; anything more transparent puts\n\t *  a moving picture behind the words at the moment they matter most. */\n\t.shotglass {\n\t\tbackground: color-mix(in srgb, var(--st-surface) 96%, transparent);\n\t}\n\t@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {\n\t\t.shotglass {\n\t\t\tbackground: color-mix(in srgb, var(--st-surface) 58%, transparent);\n\t\t\t-webkit-backdrop-filter: blur(44px) saturate(1.9);\n\t\t\tbackdrop-filter: blur(44px) saturate(1.9);\n\t\t\t/* Two edges, both inside. The top catches the light and the bottom\n\t\t\t   returns a little of it — that pair is what makes the pane read as a\n\t\t\t   surface rather than as transparency.\n\t\t\t   No outer shadow. It pooled under the card and darkened the strip the\n\t\t\t   button sits in, which is the one thing here that must never be hard\n\t\t\t   to see: a control that spends money should not be the dimmest thing\n\t\t\t   on its own card. Depth on a dark page comes from the edge, not from\n\t\t\t   a shadow nobody can see against black anyway. */\n\t\t\tbox-shadow:\n\t\t\t\tinset 0 1px 0 rgba(255, 255, 255, 0.2),\n\t\t\t\tinset 0 -1px 0 rgba(255, 255, 255, 0.05);\n\t\t}\n\t}\n\n\t/** The chips are the same glass as the field under them.\n\t *\n\t *  Left solid they were six opaque lozenges floating on a translucent card —\n\t *  the one part of the bottom of the page that still read as laid on top of\n\t *  the picture rather than resting in it. Same treatment, one notch further\n\t *  open: they are small, they carry two or three words, and a chip is read in\n\t *  a glance rather than dwelt on.\n\t *\n\t *  Their hover state stays opaque on purpose. Hover is the moment a control\n\t *  says it is about to be used, and the clearest way to say it here is to\n\t *  stop being glass. */\n\t.pillglass {\n\t\tbackground: var(--st-surface);\n\t}\n\t@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {\n\t\t.pillglass {\n\t\t\tbackground: color-mix(in srgb, var(--st-surface) 70%, transparent);\n\t\t\t-webkit-backdrop-filter: blur(26px) saturate(1.7);\n\t\t\tbackdrop-filter: blur(26px) saturate(1.7);\n\t\t\tbox-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.16);\n\t\t}\n\t}\n\n\t/** The field sits ON the wall, so it lets a little of it through.\n\t *\n\t *  Solid, it was a bar laid across the picture and the shelf appeared to stop\n\t *  at its top edge — which is the seam this stopped having. A touch of the\n\t *  tiles showing through says the wall carries on underneath, which is true,\n\t *  and it is the difference between a panel and a piece of glass over one.\n\t *\n\t *  Eighty-four per cent, not less: the placeholder and the chips have to stay\n\t *  legible over whatever happens to be behind them, and behind them is\n\t *  moving video. The blur is what makes that safe — it takes the detail out\n\t *  of the backdrop and leaves only its brightness, so text keeps its contrast\n\t *  no matter which tile is playing. Without the blur this would be unreadable\n\t *  every time a bright frame passed under it.\n\t *\n\t *  `color-mix` rather than an opacity modifier so the surface token stays the\n\t *  single source of the colour, and a plain background for anybody whose\n\t *  browser has no backdrop-filter — where translucency without the blur is\n\t *  exactly the unreadable case. */\n\t.composerglass {\n\t\tbackground: var(--st-surface);\n\t}\n\t@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {\n\t\t.composerglass {\n\t\t\tbackground: color-mix(in srgb, var(--st-surface) 84%, transparent);\n\t\t\t-webkit-backdrop-filter: blur(22px) saturate(1.15);\n\t\t\tbackdrop-filter: blur(22px) saturate(1.15);\n\t\t}\n\t}\n\n\t/* The one moving thing on the page, and it earns it: during a render nothing\n\t   else changes for minutes, so stillness would read as a hang. Anyone who has\n\t   asked the system to stop animating gets a static ring instead — the state\n\t   is already carried by the shape and the word beside it. */\n\t/* The waiting ground for a clip that continues nothing.\n\t *\n\t * A field of dots that breathes rather than a spinner filling the frame: the\n\t * pill already says how far along it is, and this is only meant to keep the\n\t * rectangle from reading as a picture that failed to load. */\n\t.stage-dots {\n\t\tbackground-image: radial-gradient(currentColor 1px, transparent 1px);\n\t\tbackground-size: 22px 22px;\n\t\tcolor: var(--st-surface-2);\n\t\tanimation: stage-dots 3.2s ease-in-out infinite;\n\t}\n\t@keyframes stage-dots {\n\t\t0%,\n\t\t100% {\n\t\t\topacity: 0.5;\n\t\t\tbackground-position: 0 0;\n\t\t}\n\t\t50% {\n\t\t\topacity: 1;\n\t\t\tbackground-position: 11px 11px;\n\t\t}\n\t}\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.stage-dots {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t/* The pass of light. A wide, very soft band of white at four per cent,\n\t * travelling the diagonal — no edge hard enough to read as a shape, and no\n\t * colour, because the only colour on this surface is the beacon and it is\n\t * already spoken for. 4.2s with a rest at each end: a sweep that restarts the\n\t * instant it finishes reads as a machine, and this is meant to read as\n\t * something breathing. */\n\t.stage-sweep {\n\t\tbackground: linear-gradient(\n\t\t\t115deg,\n\t\t\ttransparent 38%,\n\t\t\trgba(255, 255, 255, 0.04) 50%,\n\t\t\ttransparent 62%\n\t\t);\n\t\tbackground-size: 260% 260%;\n\t\tanimation: stage-sweep 4.2s ease-in-out infinite;\n\t}\n\t@keyframes stage-sweep {\n\t\t0% {\n\t\t\tbackground-position: 100% 0;\n\t\t}\n\t\t70%,\n\t\t100% {\n\t\t\tbackground-position: 0 100%;\n\t\t}\n\t}\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.stage-sweep {\n\t\t\tanimation: none;\n\t\t\tbackground: none;\n\t\t}\n\t}\n\t.spin {\n\t\tanimation: st-spin 0.9s linear infinite;\n\t}\n\n\t/* The takes grid, in :global for the reason the sidebar spacer above\n\t   documents twice over: the pruner drops scoped attribute selectors and the\n\t   Tailwind scanner does not reliably find a conditionally built variant.\n\t   Both failed silently the last time. A phone gets two columns whatever the\n\t   count — three across at 375px is a 113px tile, too small to judge motion\n\t   in — and above that the grid is as wide as the batch. */\n\t:global(.st-takes) {\n\t\tdisplay: grid;\n\t\tgap: 0.375rem;\n\t\tgrid-template-columns: repeat(2, 1fr);\n\t}\n\t@media (min-width: 640px) {\n\t\t:global(.st-takes[data-n='3']) {\n\t\t\tgrid-template-columns: repeat(3, 1fr);\n\t\t}\n\t\t:global(.st-takes[data-n='4']) {\n\t\t\tgrid-template-columns: repeat(4, 1fr);\n\t\t}\n\t}\n\n\t/* The viewer's filmstrip is a way back to the other takes, not a second place\n\t   to judge them — the picture above it is that. So its tiles are capped and\n\t   the row is centred: without the cap a batch of two put two 509px thumbnails\n\t   under a 1024px stage, which reads as three players stacked rather than one\n\t   picture and its index. Four takes are unaffected; they were already 251. */\n\t:global(.st-takes.st-film) {\n\t\tjustify-content: center;\n\t\tgrid-template-columns: repeat(2, minmax(0, 16rem));\n\t}\n\t@media (min-width: 640px) {\n\t\t:global(.st-takes.st-film[data-n='3']) {\n\t\t\tgrid-template-columns: repeat(3, minmax(0, 16rem));\n\t\t}\n\t\t:global(.st-takes.st-film[data-n='4']) {\n\t\t\tgrid-template-columns: repeat(4, minmax(0, 16rem));\n\t\t}\n\t}\n\n\t/* Two tiny frames — a reel, at chip scale. */\n\t.reelmark {\n\t\tposition: relative;\n\t\twidth: 0.85rem;\n\t\theight: 0.6rem;\n\t\tdisplay: inline-block;\n\t}\n\t.reelmark::before,\n\t.reelmark::after {\n\t\tcontent: '';\n\t\tposition: absolute;\n\t\ttop: 0;\n\t\tbottom: 0;\n\t\twidth: 0.36rem;\n\t\tborder-radius: 2px;\n\t\tbackground: var(--st-muted);\n\t}\n\t.reelmark::before {\n\t\tleft: 0;\n\t}\n\t.reelmark::after {\n\t\tright: 0;\n\t}\n\n\t/* The seam between two shots. A hairline gap where they match, a dotted rule\n\t   where they do not — the cut will jump there, and playback should not be the\n\t   first place that becomes apparent. */\n\t.seam-jump::after {\n\t\tcontent: '';\n\t\tposition: absolute;\n\t\tleft: 50%;\n\t\ttop: 14%;\n\t\tbottom: 14%;\n\t\twidth: 1px;\n\t\tbackground: repeating-linear-gradient(var(--st-faint) 0 2px, transparent 2px 5px);\n\t}\n\n\t/* The reel scrolls without a bar of its own. */\n\t.reel {\n\t\tscrollbar-width: none;\n\t}\n\t.reel::-webkit-scrollbar {\n\t\tdisplay: none;\n\t}\n\n\t/* Same for the settings row above the composer. */\n\t.railstrip {\n\t\tscrollbar-width: none;\n\t}\n\t.railstrip::-webkit-scrollbar {\n\t\tdisplay: none;\n\t}\n\n\t/* A take still on the GPU. Global, because the class is only ever produced by\n\t   an expression and Svelte's pruner drops what it cannot see in the markup —\n\t   the same silent failure the sidebar spacer above documents. */\n\t:global(.st-waiting) {\n\t\tanimation: st-breathe 2.6s ease-in-out infinite;\n\t}\n\t@keyframes st-breathe {\n\t\t0%,\n\t\t100% {\n\t\t\topacity: 0.45;\n\t\t}\n\t\t50% {\n\t\t\topacity: 0.8;\n\t\t}\n\t}\n\n\t/* 64rem is 1024px, which is the width the clips are rendered at, so the\n\t   viewer shows one at 1:1 and never resamples it. The transcript column tops\n\t   out at 720. Below that the height decides: the picture gives way first so\n\t   the bar and the filmstrip cannot be pushed off a short window — a viewer\n\t   whose controls are off-screen is worse than no viewer. */\n\t.stage {\n\t\twidth: min(64rem, 100%, calc((100dvh - 19rem) * 16 / 9));\n\t}\n\n\t/* The sheet curve: slow out, no bounce. */\n\t.lift {\n\t\tanimation: st-lift 0.26s cubic-bezier(0.32, 0.72, 0, 1) both;\n\t}\n\t@keyframes st-lift {\n\t\tfrom {\n\t\t\topacity: 0;\n\t\t\ttransform: scale(0.965);\n\t\t}\n\t}\n\t@media (prefers-reduced-motion: reduce) {\n\t\t:global(.st-waiting),\n\t\t.lift {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\t@keyframes st-spin {\n\t\tto {\n\t\t\ttransform: rotate(360deg);\n\t\t}\n\t}\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.spin {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t/* The studio wears the ratemyd brand: the app's own near-black surfaces, coral\n\t * accent and type pairing. The --st-* names stay as the component's internal\n\t * vocabulary so the markup never hardcodes a colour — only this block maps\n\t * them onto the app tokens, which is also what makes a future re-theme a\n\t * six-line edit. --st-surface-2 and --st-faint have no app-level counterpart:\n\t * they are the one step of lift above a card and the one step below muted,\n\t * derived from the same ramp. No glows anywhere: depth is fills and spacing,\n\t * per the house rules. */\n\t.studio {\n\t\t--st-bg: var(--color-bg);\n\t\t--st-surface: var(--color-surface);\n\t\t--st-surface-2: var(--color-surface-2);\n\t\t--st-line: var(--color-border);\n\t\t/* The one border that has to be seen rather than felt: where a 1px edge is\n\t\t * the only thing identifying a control. Decoration keeps --st-line. */\n\t\t--st-line-control: #4c4c52;\n\t\t--st-text: var(--color-text);\n\t\t--st-muted: var(--color-muted);\n\t\t/* One step below muted. A bare var(--color-faint) was wrong in a host that\n\t\t * has no such token: an unresolvable var() invalidates the whole\n\t\t * declaration, so every faint line inherited full text colour and the third\n\t\t * step of the hierarchy silently did not exist. The fallback is the\n\t\t * studio's own value, so it looks the same wherever it is mounted. */\n\t\t--st-faint: var(--color-faint, #86868b);\n\t\t--st-green: var(--color-green, #22c55e);\n\t\t/* The accent is white, and it is the studio's own rather than the host's.\n\t\t *\n\t\t * It read var(--color-coral) — the host's accent — and in an app whose\n\t\t * coral is a hot pink the one control that spends GPU time became a hot\n\t\t * pink pill on black, which reads as a tube site rather than a studio and\n\t\t * put the loudest thing on the view on a button instead of on the picture\n\t\t * the view exists for. The standalone app has always had this right: its\n\t\t * own accent token IS white. So the studio names white directly, spends it\n\t\t * once per view, and keeps colour for the semantic layer — the activity\n\t\t * dots, the error card — where it means something. */\n\t\t--st-accent: var(--st-text);\n\t\t--st-accent-strong: #fff;\n\t\t--st-on-accent: var(--st-bg);\n\t\tbackground: var(--st-bg);\n\t\tcolor: var(--st-text);\n\t\tfont-family: var(--font-body);\n\t}\n\n\t/* ── buttons ──────────────────────────────────────────────────────────────\n\t * Four kinds and no fifth. These live in the studio's own stylesheet in the\n\t * standalone app and did not come across with it, so all twenty-six buttons\n\t * in here rendered as bare text on a bare background: the one control that\n\t * spends GPU time looked like a bolder sentence, which is exactly the thing\n\t * a primary button exists not to be.\n\t *\n\t *   .btn-primary    the one thing on a view that spends GPU time or cannot\n\t *                   be undone. Filled with the accent. ONE per view.\n\t *   .btn-secondary  the ordinary action. Filled with a surface, so it reads\n\t *                   as a control without competing with the primary.\n\t *   .btn-quiet      dismiss, cancel, \"not now\". No fill until you reach it.\n\t *   .btn-danger     destroys something that cost money. The only red.\n\t *\n\t * One height, one radius, one weight per kind. .btn-sm is the only size\n\t * modifier, for controls sitting inside a card's own furniture. */\n\t.btn {\n\t\tdisplay: inline-flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tgap: 0.5rem;\n\t\tmin-height: 2.25rem;\n\t\tpadding-inline: 1.125rem;\n\t\tborder-radius: 9999px;\n\t\tfont-family: var(--font-body);\n\t\tfont-size: 0.875rem;\n\t\tfont-weight: 500;\n\t\twhite-space: nowrap;\n\t\tcursor: pointer;\n\t\ttransition: background-color 0.2s cubic-bezier(0.32, 0.72, 0, 1);\n\t}\n\t.btn:disabled {\n\t\tcursor: default;\n\t\topacity: 0.4;\n\t}\n\t.btn-sm {\n\t\tmin-height: 2rem;\n\t\tpadding-inline: 0.875rem;\n\t\tfont-size: 0.8125rem;\n\t}\n\t/* The accent, filled, and the strip already taught this language: the clip you\n\t * are looking at is the one lit with --st-text. */\n\t.btn-primary {\n\t\tbackground: var(--st-accent);\n\t\tcolor: var(--st-on-accent);\n\t\tfont-weight: 600;\n\t}\n\t.btn-primary:hover:not(:disabled) {\n\t\tbackground: var(--st-accent-strong);\n\t}\n\t/* A button that cannot be pressed still has to be readable.\n\t *\n\t *  Disabled was the faintest token on the darkest surface — the one pairing\n\t *  the contrast audit already measured at 2.51:1 — and this button spends\n\t *  money, so its busy state is exactly when somebody is staring at it waiting\n\t *  for the clock to move. Muted instead: still plainly not pressable, still\n\t *  legible while it counts. */\n\t.btn-primary:disabled {\n\t\tbackground: var(--st-surface-2);\n\t\tcolor: var(--st-muted);\n\t\topacity: 1;\n\t}\n\t.btn-secondary {\n\t\tbackground: var(--st-surface-2);\n\t\t/* Muted, not full text. On a filled pill the label was as bright as the\n\t\t * primary's, so a row of them read as a row of equals and the eye had to\n\t\t * work out which one mattered. */\n\t\tcolor: var(--st-muted);\n\t}\n\t.btn-secondary:hover:not(:disabled) {\n\t\tbackground: var(--st-line);\n\t\tcolor: var(--st-text);\n\t}\n\t.btn-quiet {\n\t\tbackground: none;\n\t\tcolor: var(--st-muted);\n\t}\n\t.btn-quiet:hover:not(:disabled) {\n\t\tbackground: var(--st-surface);\n\t\tcolor: var(--st-text);\n\t}\n\t.btn-danger {\n\t\tbackground: var(--st-surface-2);\n\t\tcolor: #f2938a;\n\t}\n\t.btn-danger:hover:not(:disabled) {\n\t\tbackground: #5c2f24;\n\t\tcolor: #f7ded9;\n\t}\n\n\t/* Headings and the wordmark carry the display face, the way they do across\n\t * the product. Body copy — including the generated prose, which is the thing\n\t * people actually read here — stays on the body face. */\n\t.studio :global(h1),\n\t.studio :global(h2),\n\t.studio :global(h3) {\n\t\tfont-family: var(--font-display);\n\t}\n\n\t/* The two scroll regions: a thin, unobtrusive bar that matches the surface\n\t * rather than the OS default light one. Firefox gets the standard property,\n\t * WebKit/Blink the pseudo-elements. */\n\t/* Opacity only — a colour glow would be a neon halo, which the house rules\n\t   forbid. Slow enough to read as breathing, not as an alarm. */\n\t.beacon {\n\t\tanimation: beacon 1.8s ease-in-out infinite;\n\t}\n\t@keyframes beacon {\n\t\t0%,\n\t\t100% {\n\t\t\topacity: 1;\n\t\t}\n\t\t50% {\n\t\t\topacity: 0.35;\n\t\t}\n\t}\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.beacon {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t/* The cursor at the end of a sentence that is still arriving. Opacity only,\n\t   same rule as the beacon — and a blinking block is the oldest signal there\n\t   is for \"there is more of this coming\". */\n\t.caret {\n\t\tdisplay: inline-block;\n\t\twidth: 0.45em;\n\t\theight: 1em;\n\t\tmargin-left: 0.12em;\n\t\tvertical-align: -0.14em;\n\t\tbackground: var(--st-muted);\n\t\tanimation: caret 1.1s steps(1, end) infinite;\n\t}\n\t@keyframes caret {\n\t\t0%,\n\t\t55% {\n\t\t\topacity: 1;\n\t\t}\n\t\t56%,\n\t\t100% {\n\t\t\topacity: 0;\n\t\t}\n\t}\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.caret {\n\t\t\tanimation: none;\n\t\t\topacity: 0.6;\n\t\t}\n\t}\n\n\t.scroller {\n\t\tscrollbar-width: thin;\n\t\tscrollbar-color: var(--st-line) transparent;\n\t}\n\t.scroller::-webkit-scrollbar {\n\t\twidth: 10px;\n\t}\n\t.scroller::-webkit-scrollbar-track {\n\t\tbackground: transparent;\n\t}\n\t.scroller::-webkit-scrollbar-thumb {\n\t\tbackground: var(--st-line);\n\t\tborder: 3px solid transparent;\n\t\tbackground-clip: content-box;\n\t\tborder-radius: 999px;\n\t}\n\n\t/* Generated text is markdown-ish prose: keep the author's line breaks, keep\n\t * the measure readable. */\n\t.doc {\n\t\twhite-space: pre-wrap;\n\t\toverflow-wrap: anywhere;\n\t}\n\n\t/* Collapsed document: roughly twelve lines, then a fade into the card's own\n\t * fill. The gradient is the surface color, not a colored halo. */\n\t.clamp {\n\t\tmax-height: 21rem;\n\t\toverflow: hidden;\n\t}\n\t.clamp::after {\n\t\tcontent: '';\n\t\tposition: absolute;\n\t\tinset-inline: 0;\n\t\tbottom: 0;\n\t\theight: 4.5rem;\n\t\tbackground: linear-gradient(to bottom, transparent, var(--st-surface));\n\t\tpointer-events: none;\n\t}\n\n\t/* Things arrive during a long wait; they should settle in rather than pop.\n\t * One animation for the whole page, and none of it for anyone who asked the\n\t * OS to stop moving things. */\n\t.enter {\n\t\tanimation: enter 0.45s cubic-bezier(0.25, 1, 0.5, 1) both;\n\t}\n\n\t/* A folded setting unfolding.\n\t   Each option arrives from where the fold was and a beat after the one\n\t   before it, so the group reads as opening rather than as being replaced —\n\t   which is what an instant swap looked like, and why the row appeared to\n\t   flicker when a bubble was tapped. Short: this sits under a thumb that is\n\t   already moving to the next tap. */\n\t.pillopen {\n\t\tanimation: pillopen 0.26s cubic-bezier(0.25, 1, 0.5, 1) both;\n\t}\n\n\t/* The panel growing out of the bubble that was pressed.\n\t   Scale rather than a slide, and from an origin set on the element itself:\n\t   a box that merely appears above the row reads as a different object,\n\t   while one that grows from under your finger reads as the same one\n\t   opening. The overshoot is small — it is a 144px panel, not a sheet. */\n\t.pillrise {\n\t\tanimation: pillrise 0.32s cubic-bezier(0.2, 1.15, 0.4, 1) both;\n\t}\n\t@keyframes pillrise {\n\t\tfrom {\n\t\t\topacity: 0;\n\t\t\ttransform: translateY(8px) scale(0.82);\n\t\t}\n\t\tto {\n\t\t\topacity: 1;\n\t\t\ttransform: none;\n\t\t}\n\t}\n\t@keyframes pillopen {\n\t\tfrom {\n\t\t\topacity: 0;\n\t\t\ttransform: translateX(-7px) scale(0.93);\n\t\t}\n\t\tto {\n\t\t\topacity: 1;\n\t\t\ttransform: none;\n\t\t}\n\t}\n\t@keyframes enter {\n\t\tfrom {\n\t\t\topacity: 0;\n\t\t\ttransform: translateY(8px);\n\t\t}\n\t\tto {\n\t\t\topacity: 1;\n\t\t\ttransform: none;\n\t\t}\n\t}\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.enter,\n\t\t.pillopen,\n\t\t.pillrise {\n\t\t\tanimation: none;\n\t\t}\n\t}\n</style>\n"],"version":3}