| <!DOCTYPE html> |
| <html lang="zh-CN"> |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" /> |
| <title>FateForge</title> |
| <link rel="icon" type="image/png" href="/static/assets/favicon.png" /> |
| |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script> |
| tailwind.config = { |
| theme: { |
| extend: { |
| colors: { |
| ink: { 50:'#f4ede0', 100:'#e6dcc4', 200:'#c9bfa1', 300:'#8a7e62', 400:'#5a4f3d', 500:'#2a241c', 600:'#1a1612', 700:'#0f0c09' }, |
| skyq: { 500:'#5d7a8a', 600:'#3d5a6a' }, |
| jade: { 500:'#7ba88a' }, |
| bloodred: { 500:'#7a2a1a' }, |
| icelake: { 500:'#3a6878' }, |
| |
| burntea: { 500: '#5a4a36', 600: '#3a2e22' }, |
| |
| inkgreen: { 500: '#3a5848' }, |
| |
| goldink: { 500: '#b8923c', 600: '#8a6c28' }, |
| }, |
| fontFamily: { |
| shu: ['"Ma Shan Zheng"','"STKaiti"','"KaiTi"','"STZhongsong"','serif'], |
| kai: ['"KaiTi"','"STKaiti"','serif'], |
| sans: ['"Noto Serif SC"','"Noto Serif"','serif'], |
| serif:['"Noto Serif SC"','"Noto Serif"','serif'], |
| }, |
| } |
| } |
| } |
| </script> |
| |
| <link rel="preconnect" href="https://fonts.googleapis.com" /> |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> |
| <link href="https://fonts.googleapis.com/css2?family=Ma+Shan+Zheng&family=Noto+Serif+SC:wght@300;500;700&family=Noto+Serif:ital,wght@0,400;0,600;1,400&family=ZCOOL+XiaoWei&display=swap" rel="stylesheet" /> |
| <link rel="stylesheet" href="/static/style.css" /> |
| |
| <script src="https://unpkg.com/vue@3.4.27/dist/vue.global.prod.js"></script> |
| </head> |
| <body :class="lang === 'en' ? 'lang-en' : 'lang-zh'" class="font-sans antialiased select-none overflow-hidden h-screen"> |
|
|
| <div id="app" class="relative w-full h-full overflow-hidden"> |
|
|
| |
| <div class="mist-layer" aria-hidden="true"> |
| <div v-for="i in 8" :key="'m'+i" class="mist-blob" |
| :style="mistStyle(i)"></div> |
| </div> |
|
|
| |
| <nav v-if="scene === 'yuanqi'" class="top-menu" aria-label="top menu"> |
| <span class="top-menu-bracket" aria-hidden="true">〔</span> |
| <button @click="openBook" class="top-menu-btn" :title="t('menu_book')"> |
| <span class="top-menu-glyph">☰</span> |
| <span class="top-menu-label">{{ t('menu_book') }}</span> |
| </button> |
| <span class="top-menu-divider" aria-hidden="true"></span> |
| <button @click="toggleLang" class="top-menu-btn top-menu-lang" :title="t('menu_lang')"> |
| {{ t('menu_lang') }} |
| </button> |
| <span class="top-menu-divider" aria-hidden="true"></span> |
| <button @click="openAbout" class="top-menu-btn" :title="t('menu_about')"> |
| {{ t('menu_about') }} |
| </button> |
| <span class="top-menu-bracket" aria-hidden="true">〕</span> |
| </nav> |
|
|
| |
| <transition name="fade-slow"> |
| <section v-if="scene === 'yuanqi'" class="scene bg-element-wood flex flex-col items-center justify-center px-6"> |
| <h1 class="text-2xl md:text-3xl text-ink-100 font-shu text-center leading-loose tracking-widest opacity-90 mb-12 ink-seep whitespace-pre-line"> |
| {{ t('yuanqi_h1') }} |
| </h1> |
| <textarea v-model="question" |
| rows="3" |
| maxlength="120" |
| :placeholder="t('yuanqi_placeholder')" |
| class="yuanqi-textarea whitespace-pre-line"></textarea> |
| <div class="mt-2 text-xs text-ink-300/60 tracking-wider">{{ question.length }} / 120</div> |
| <button @click="startDivination" |
| :disabled="!question.trim()" |
| :class="['cast-btn', 'mt-10 px-12 py-3 font-shu text-xl tracking-[0.4em]', |
| question.trim() ? 'is-active' : 'is-disabled']"> |
| <span class="cast-btn-text">{{ t('yuanqi_start') }}</span> |
| </button> |
| </section> |
| </transition> |
|
|
| |
| <transition name="fade-slow"> |
| <section v-if="scene === 'kuiji'" class="scene bg-element-earth flex flex-col items-center justify-center"> |
|
|
| |
| <div v-if="phase === 'turtle'" class="flex flex-col items-center"> |
| <p class="mb-6 text-ink-200/80 font-kai tracking-widest kuiji-text text-center"> |
| {{ t('kuiji_turtle_hint_p1') }}<span class="text-ink-50"> {{ t('kuiji_turtle_hint_p2') }}</span> |
| </p> |
| <p class="mb-2 text-ink-300/70 font-kai kuiji-subtext"> |
| {{ t('kuiji_clicked') }} <span class="text-ink-50 kuiji-number mx-1">{{ clicks }}</span> / 6 {{ t('yao_unit') }} |
| </p> |
| |
| <div class="turtle-wrap" @click="onTurtleClick" :class="{ shake: shaking }"> |
| <img :src="$root.assets.turtle" alt="龟壳" class="turtle-img" draggable="false" |
| @error="$event.target.src='/static/assets/turtle.svg'"/> |
| <div class="click-hint">{{ t('kuiji_touch') }}</div> |
| </div> |
| </div> |
|
|
| |
| <div v-if="phase === 'coins'" class="flex flex-col items-center"> |
| <p class="mb-8 text-ink-200/80 font-kai tracking-widest kuiji-text"> |
| {{ t('kuiji_yao') }} <span class="text-ink-50 kuiji-number">{{ clicks }}</span> {{ t('yao_unit') }} |
| </p> |
| <div class="flex gap-6"> |
| <div v-for="(c, i) in currentCoins" :key="i" |
| :class="['coin', c.flipped ? 'flipped' : '']"> |
| <div class="coin-inner"> |
| <div class="coin-face"> |
| <img :src="c.face === 3 ? $root.assets.coinHead : $root.assets.coinTail" |
| :alt="c.face === 3 ? t('kuiji_coin_head') : t('kuiji_coin_tail')" |
| @error="$event.target.style.display='none'"/> |
| </div> |
| </div> |
| </div> |
| </div> |
| <p class="mt-8 text-ink-300/70 font-kai kuiji-subtext"> |
| <span v-if="lastLineValue !== null"> |
| <span v-if="lastLineValue === 9" class="text-jade-500">━ ○ {{ t('kuiji_old_yang') }}</span> |
| <span v-else-if="lastLineValue === 8" class="text-ink-50">━━━ {{ t('kuiji_young_yang') }}</span> |
| <span v-else-if="lastLineValue === 6" class="text-bloodred-500">━ × {{ t('kuiji_old_yin') }}</span> |
| <span v-else class="text-ink-300">━ ━ {{ t('kuiji_young_yin') }}</span> |
| </span> |
| </p> |
| </div> |
|
|
| |
| <div v-if="phase === 'hexagon'" class="flex flex-col items-center"> |
| <div class="ink-paper ink-paper-rounded w-full max-w-2xl p-8 md:p-12 flex flex-col items-center"> |
| <p class="mb-4 text-burntea-500 font-shu kuiji-section-title ink-seep">{{ t('hex_done') }}</p> |
| <div class="hex-lines"> |
| <div v-for="(g, idx) in lines" :key="idx" |
| :class="['hex-line', g.yang ? 'yang' : 'yin', g.moving ? 'moving' : '']"> |
| <span v-if="g.moving" class="line-mark">{{ g.value === 9 ? '○' : '×' }}</span> |
| </div> |
| </div> |
| <p class="mt-8 text-ink-700 font-shu text-5xl tracking-widest ink-seep">{{ displayHexName }}</p> |
| <p class="mt-3 text-burntea-500 font-shu hex-judgment-text">{{ t('hex_judgment') }}{{ displayHexJudgment }}</p> |
| <button @click="goScene3" class="reveal-btn mt-10 px-10 py-3 font-shu text-xl tracking-[0.4em]"> |
| <span class="reveal-btn-text">{{ t('hex_reveal') }}</span> |
| </button> |
| </div> |
| </div> |
|
|
| </section> |
| </transition> |
|
|
| |
| <transition name="fade-slow"> |
| <section v-if="scene === 'pomi'" |
| :class="['scene flex flex-col items-center px-6', pomiBgClass, ominousClass, levelToneClass]"> |
|
|
| <div class="hex-seal mb-4"> |
| <div :class="auspiciousLevelClass">{{ displayLevel }}</div> |
| <div class="text-ink-700 font-shu mt-2 text-3xl tracking-widest">{{ displayHexName }}</div> |
| <div v-if="lang === 'zh'" class="text-burntea-500 font-kai mt-1 kuiji-subtext tracking-widest"> |
| {{ upperTrigram }}上 · {{ lowerTrigram }}下 · {{ t('hex_ordinal_prefix') }} {{ hexOrder }} {{ t('hex_ordinal_suffix') }} |
| </div> |
| <div v-else class="text-burntea-500 font-kai mt-1 kuiji-subtext tracking-widest"> |
| {{ upperTrigram }} / {{ lowerTrigram }} · {{ displayLevel }} |
| </div> |
| </div> |
|
|
| <div class="w-full max-w-2xl text-center mt-4 mb-6"> |
| <div class="text-burntea-500 font-kai kuiji-subtext tracking-widest"> |
| {{ t('pomi_question_prefix') }} <span class="text-ink-700">{{ question }}</span> |
| </div> |
| </div> |
|
|
| |
| <div :class="['ink-paper ink-paper-rounded ink-paper-floating ink-paper-scrollable flex flex-col items-center', |
| 'w-full max-w-3xl xl:max-w-4xl p-10 md:p-14', |
| lang === 'en' ? 'xl:max-w-5xl 2xl:max-w-6xl md:p-12' : '']" |
| @scroll="onPomiScroll" |
| ref="pomiScrollRoot"> |
|
|
| |
| <div v-if="!destinyText && !readingError" |
| class="text-center text-burntea-500 font-kai kuiji-subtext tracking-widest ink-seep-loop py-6"> |
| {{ t('pomi_loading') }} |
| </div> |
|
|
| |
| <div v-else-if="readingError" |
| class="text-bloodred-500 font-kai text-center kuiji-subtext"> |
| {{ readingError }} |
| </div> |
|
|
| |
| <template v-else-if="isOminousFlow"> |
| |
| <div v-if="pomiStage === 'obscure' || pomiStage === 'loading'" class="w-full"> |
| <div class="reading-text font-kai whitespace-pre-wrap text-burntea-500 leading-loose hex-card-text" |
| :class="pomiStage === 'obscure' ? 'reading-text-blurred' : ''"> |
| {{ destinyText }} |
| </div> |
| <div v-if="pomiStage === 'obscure'" class="mt-8 text-center text-burntea-500 font-kai kuiji-section-title ink-seep"> |
| {{ t('pomi_obscure_l1') }} |
| </div> |
| <div v-if="pomiStage === 'obscure'" class="mt-3 text-center text-burntea-500 font-kai kuiji-section-title ink-seep"> |
| {{ t('pomi_obscure_l2') }} |
| </div> |
| </div> |
| |
| <div v-else-if="pomiStage === 'reveal_btn'" class="w-full relative"> |
| |
| <div v-if="remedyText" class="w-full"> |
| <h3 class="font-shu text-2xl text-center tracking-widest text-burntea-500 mb-4 ink-seep"> |
| {{ t('pomi_xintai') }} · {{ displayRemedy }} |
| </h3> |
| <div class="reading-text font-kai whitespace-pre-wrap hex-card-text" |
| :class="previewCleared ? 'text-ink-700' : 'text-burntea-500'"> |
| {{ remedyText }} |
| </div> |
| <div class="w-full mt-12 mb-2"></div> |
| </div> |
| |
| <div class="reveal-veil" :class="{ 'is-lifted': previewCleared }" aria-hidden="true"></div> |
| |
| <div class="reveal-one-center"> |
| <button @click="onRevealOne" class="reveal-one-btn" |
| :class="{ 'is-fading-out': fadingOut }"> |
| {{ t('pomi_reveal_btn') }} |
| </button> |
| </div> |
| </div> |
| |
| <div v-else-if="pomiStage === 'revealing' && !burning" class="w-full" :class="{ 'is-dispersing': dispersing }"> |
| <h3 class="font-shu text-2xl text-center tracking-widest text-burntea-500 mb-4 ink-seep"> |
| {{ t('pomi_xintai') }} · {{ displayRemedy }} |
| </h3> |
| <div class="reading-text font-kai whitespace-pre-wrap text-ink-700 leading-loose hex-card-text typing-text"> |
| {{ remedyText }} |
| </div> |
| <div class="w-full mt-10 text-center"> |
| <button @click="onBreakFree" class="breakfree-btn"> |
| {{ t('pomi_breakfree') }} |
| </button> |
| </div> |
| </div> |
| |
| <div v-else-if="pomiStage === 'revealing' && burning" class="w-full"> |
| <div class="burn-flash is-burning" aria-hidden="true"></div> |
| <div class="burn-mist is-burning" aria-hidden="true"></div> |
| <div class="text-center text-burntea-500 font-kai kuiji-section-title ink-seep"> |
| {{ t('pomi_burning') }} |
| </div> |
| </div> |
| </template> |
|
|
| |
| <template v-else> |
| <div v-if="destinyText" class="w-full"> |
| <div class="reading-text font-kai whitespace-pre-wrap text-ink-700 leading-loose hex-card-text"> |
| {{ destinyText }} |
| </div> |
| </div> |
| <div v-if="remedyText" class="w-full mt-6"> |
| <h3 class="font-shu text-2xl text-center tracking-widest text-burntea-500 mb-4 ink-seep"> |
| {{ t('pomi_xintai') }} · {{ displayRemedy }} |
| </h3> |
| <div class="reading-text font-kai whitespace-pre-wrap text-ink-700 leading-loose hex-card-text"> |
| {{ remedyText }} |
| </div> |
| </div> |
| |
| <div class="w-full text-center mt-10"> |
| <button @click="departToHome" class="depart-btn px-10 py-3 font-shu text-xl tracking-[0.4em]"> |
| {{ t('pomi_depart') }} |
| </button> |
| </div> |
| </template> |
| </div> |
| </section> |
| </transition> |
|
|
| |
| <transition name="fade-slow"> |
| <section v-if="scene === 'zidu'" :class="['scene bg-element-water flex flex-col items-center px-6 overflow-y-auto', levelToneClass]"> |
| <h1 class="text-xl md:text-2xl font-shu tracking-widest text-center mt-4 ink-seep" |
| :class="auspicious === '吉' ? 'text-jade-500 jade-glow' : auspicious === '凶' ? 'text-bloodred-500' : 'text-ink-50'"> |
| {{ auspicious === '吉' ? '天时已至,顺势而为。' : auspicious === '凶' ? '命虽天定,心能转境。' : '时机未至,静水流深。' }}<br/> |
| {{ ctaSub }} |
| </h1> |
|
|
| <div class="ink-paper ink-paper-rounded w-full max-w-2xl p-8 md:p-10 mt-6"> |
| <h2 class="font-shu text-2xl tracking-[0.4em] mb-2 text-center" |
| :class="auspicious === '吉' ? 'text-jade-500' : auspicious === '凶' ? 'text-bloodred-500' : 'text-ink-50'"> |
| {{ remedyTag }} |
| </h2> |
| <div class="text-ink-700 font-kai leading-loose text-base whitespace-pre-line">{{ remedyText }}</div> |
|
|
| <div class="mt-6 pt-4 border-t border-ink-300/30 text-xs text-burntea-500 font-kai text-center opacity-70"> |
| {{ t('zidu_summary') }}{{ hexName }} ({{ upperTrigram }}上 · {{ lowerTrigram }}下) · {{ t('zidu_asked') }}{{ question }} |
| </div> |
| </div> |
|
|
| <div class="w-full max-w-2xl mt-8 mb-12 text-center select-none touch-none"> |
| <p class="text-burntea-500 font-kai kuiji-subtext tracking-widest mb-3"> |
| {{ t('zidu_swipe_hint_p1') }}<span class="text-ink-700"> {{ t('zidu_swipe_hint_p2') }} </span>{{ t('zidu_swipe_hint_p3') }} |
| </p> |
| <div class="swipe-zone" |
| @pointerdown="onSwipeStart" |
| @pointermove="onSwipeMove" |
| @pointerup="onSwipeEnd" |
| @pointercancel="onSwipeEnd" |
| @click="onSwipeClick" |
| ref="swipeZone" |
| role="button" :aria-label="t('zidu_swipe_aria')"> |
| <div class="swipe-track" :style="swipeTrackStyle"></div> |
| <div class="swipe-handle" :style="swipeHandleStyle"> |
| <span class="swipe-handle-glyph">{{ t('zidu_swipe_glyph') }}</span> |
| </div> |
| </div> |
| </div> |
| </section> |
| </transition> |
|
|
| |
| <transition name="fade"> |
| <div v-if="bookOpen" class="fixed inset-0 z-50 bg-white/95 backdrop-blur-sm overflow-y-auto" |
| @click.self="closeBook"> |
| <div class="max-w-3xl mx-auto px-6 py-12"> |
| <div class="flex items-center justify-between mb-8"> |
| <h2 class="text-2xl font-shu text-ink-700 tracking-widest">{{ t('book_title') }}</h2> |
| <button @click="closeBook" class="text-burntea-500 hover:text-ink-700 kuiji-subtext font-kai">{{ t('book_close') }}</button> |
| </div> |
| <p class="text-burntea-500 font-kai text-xs mb-6 tracking-widest opacity-70"> |
| {{ t('book_local_only') }}{{ history.length }} {{ t('book_count_unit') }} |
| </p> |
| <div v-if="!history.length" class="text-center text-burntea-500 font-kai mt-20 tracking-widest opacity-60"> |
| {{ t('book_empty') }} |
| </div> |
| <article v-for="h in history" :key="h.id" |
| class="book-entry ink-paper ink-paper-rounded p-6 mb-4 cursor-pointer transition" |
| @click="toggleHistory(h.id)"> |
| <div class="flex justify-between items-baseline mb-2"> |
| <h3 class="text-ink-700 font-shu text-lg tracking-widest"> |
| {{ h.hexName }}<span class="text-burntea-500 kuiji-subtext font-kai ml-2 opacity-60">{{ h.date }}</span> |
| </h3> |
| <span class="flex items-center gap-2"> |
| <span :class="['text-xs font-kai tracking-widest px-2 py-0.5 rounded-sm', |
| h.auspicious === '吉' ? 'text-jade-500 border border-jade-500/30' |
| : h.auspicious === '凶' ? 'text-bloodred-500 border border-bloodred-500/30' |
| : 'text-burntea-500 border border-ink-300/30']"> |
| {{ h.auspicious }} |
| </span> |
| <span class="text-burntea-500 text-xs font-kai opacity-60">{{ expandedId === h.id ? t('book_collapse') : t('book_expand') }}</span> |
| </span> |
| </div> |
| <p class="text-burntea-500 font-kai text-xs mb-3 opacity-70">{{ t('zidu_asked') }}{{ h.question }}</p> |
|
|
| <div v-if="expandedId !== h.id" class="text-burntea-500 font-kai kuiji-subtext leading-loose italic opacity-70"> |
| <span v-if="h.destinyText">{{ h.destinyText.slice(0, 60) }}…</span> |
| <span v-else>({{ t('book_no_reading') }})</span> |
| </div> |
|
|
| <div v-else class="space-y-4 ink-seep"> |
| <div> |
| <h4 class="text-ink-700 font-shu text-base tracking-widest mb-2">{{ t('book_hex_label') }}</h4> |
| <div class="text-burntea-500 font-kai kuiji-subtext"> |
| <span v-if="h.upper && h.lower">{{ h.upper }}上 · {{ h.lower }}下</span> |
| <span v-if="h.changedName"> · {{ t('book_changed_to') }} {{ h.changedName }}</span> |
| <span v-if="h.moving && h.moving.length"> · {{ t('book_moving') }} {{ h.moving.map(i=>['初','二','三','四','五','上'][i-1]+'爻').join('、') }}</span> |
| </div> |
| <div v-if="h.judgment" class="text-burntea-500 font-kai text-xs mt-1 opacity-70">{{ t('hex_judgment') }}{{ h.judgment }}</div> |
| </div> |
| <div v-if="h.destinyText"> |
| <h4 class="text-ink-700 font-shu text-base tracking-widest mb-2">{{ t('book_destiny_label') }}</h4> |
| <p class="text-ink-700 font-kai kuiji-subtext leading-loose whitespace-pre-wrap">{{ h.destinyText }}</p> |
| </div> |
| <div v-if="h.remedyText"> |
| <h4 class="text-jade-500 font-shu text-base tracking-widest mb-2">{{ t('book_remedy_label') }}</h4> |
| <p class="text-ink-700 font-kai kuiji-subtext leading-loose whitespace-pre-wrap">{{ h.remedyText }}</p> |
| </div> |
| <div v-if="!h.destinyText && !h.remedyText && h.text" |
| class="text-ink-700 font-kai kuiji-subtext leading-loose whitespace-pre-wrap"> |
| <h4 class="text-ink-700 font-shu text-base tracking-widest mb-2">{{ t('book_reading_label') }}</h4> |
| {{ h.text }} |
| </div> |
| <div class="text-right pt-2 border-t border-ink-300/20"> |
| <button @click.stop="deleteHistory(h.id)" |
| class="text-burntea-500 hover:text-bloodred-500 text-xs font-kai opacity-60 hover:opacity-100"> |
| {{ t('book_burn') }} |
| </button> |
| </div> |
| </div> |
| </article> |
| <div v-if="history.length" class="text-center mt-8"> |
| <button @click="clearHistory" class="text-burntea-500 hover:text-bloodred-500 text-xs font-kai tracking-widest opacity-60 hover:opacity-100"> |
| {{ t('book_clear_all') }} |
| </button> |
| </div> |
| </div> |
| </div> |
| </transition> |
|
|
| |
| <transition name="fade"> |
| <div v-if="aboutOpen" class="fixed inset-0 z-50 flex items-center justify-center px-6" |
| @click.self="closeAbout"> |
| <div class="about-card ink-paper ink-paper-rounded ink-paper-floating w-full max-w-lg p-8 md:p-10"> |
| <div class="flex items-center justify-between mb-6"> |
| <h2 class="font-shu text-2xl text-ink-700 tracking-widest">{{ t('about_title') }}</h2> |
| <button @click="closeAbout" class="text-burntea-500 hover:text-ink-700 kuiji-subtext font-kai">{{ t('book_close') }}</button> |
| </div> |
| <article class="space-y-5 text-ink-700 font-kai leading-loose kuiji-subtext whitespace-pre-line"> |
| <section> |
| <h3 class="font-shu text-base text-ink-700 tracking-widest mb-2">{{ t('about_priv_title') }}</h3> |
| <p>{{ t('about_priv_body') }}</p> |
| </section> |
| <section> |
| <h3 class="font-shu text-base text-ink-700 tracking-widest mb-2">{{ t('about_disc_title') }}</h3> |
| <p>{{ t('about_disc_body') }}</p> |
| </section> |
| </article> |
| </div> |
| </div> |
| </transition> |
|
|
| |
| <transition> |
| <div v-if="departing" class="depart-overlay" aria-hidden="true"></div> |
| </transition> |
|
|
| |
| <transition name="fade"> |
| <div v-if="confirmDialog.open" class="fixed inset-0 z-[70] flex items-center justify-center px-6 ink-modal-mask" |
| @click.self="confirmDialogResolve(false)"> |
| <div class="ink-modal" role="dialog" aria-modal="true"> |
| <p class="ink-modal-text">{{ confirmDialog.message }}</p> |
| <div class="ink-modal-actions"> |
| <button @click="confirmDialogResolve(false)" class="ink-modal-btn ink-modal-btn-cancel"> |
| {{ confirmDialog.cancelLabel }} |
| </button> |
| <button @click="confirmDialogResolve(true)" class="ink-modal-btn ink-modal-btn-confirm"> |
| {{ confirmDialog.confirmLabel }} |
| </button> |
| </div> |
| </div> |
| </div> |
| </transition> |
|
|
| |
| <transition name="fade"> |
| <div v-if="choiceDialog.open" class="fixed inset-0 z-[70] flex items-center justify-center px-6 ink-modal-mask" |
| @click.self="choiceDialogResolve('secondary')"> |
| <div class="ink-modal" role="dialog" aria-modal="true"> |
| <p class="ink-modal-text">{{ choiceDialog.message }}</p> |
| <div class="ink-modal-actions"> |
| <button @click="choiceDialogResolve('secondary')" class="ink-modal-btn ink-modal-btn-cancel"> |
| {{ choiceDialog.secondaryLabel }} |
| </button> |
| <button @click="choiceDialogResolve('primary')" class="ink-modal-btn ink-modal-btn-confirm"> |
| {{ choiceDialog.primaryLabel }} |
| </button> |
| </div> |
| </div> |
| </div> |
| </transition> |
|
|
| |
| <transition name="fade-slow"> |
| <div v-if="mistCleared" class="finale-overlay"> |
| <div class="finale-quote"> |
| <p v-for="(l, i) in finalLines" :key="i" class="finale-line font-shu">{{ l }}</p> |
| <button @click="reset" |
| class="mt-16 px-10 py-2 font-shu text-base tracking-[0.4em] border border-jade-500/40 text-jade-500 hover:bg-jade-500/10 rounded-sm opacity-0" |
| style="animation: finaleLine 2s ease-out 5.5s forwards;"> |
| {{ t('finale_restart') }} |
| </button> |
| </div> |
| </div> |
| </transition> |
|
|
| </div> |
|
|
| <script src="/static/app.js"></script> |
| </body> |
| </html> |
|
|