fix: viewport-fixed layout on all pages for mobile

- Replace 100vh with 100dvh everywhere (accounts for mobile browser chrome)
- DocsPage: viewport-fixed with tabs pinned, tab content scrolls internally
- BotProfilePage: overflow-hidden outer, internal scroll container
- JoinBoutPage: viewport-fixed with internal overflow scroll
- HumanFightPage: viewport-fixed, flex-1 content area with internal scroll
- All other pages: vh → dvh for correct mobile sizing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-08 15:25:34 +00:00
co-authored by Claude Opus 4.6
parent 8a345f5e56
commit 896e81960f
10 changed files with 85 additions and 40 deletions
+60 -19
View File
@@ -59,6 +59,22 @@ const tierColors: Record<number, string> = {
0: '#8a8a9a', 1: '#4ade80', 2: '#38bdf8', 3: '#a855f7', 4: '#f97316', 5: '#ef4444',
}
function isCreator(bot: BotInfo | null): boolean {
return bot?.archetype === 'the_creator'
}
function getTierName(bot: BotInfo | null): string {
if (!bot) return ''
if (isCreator(bot)) return '₿ CREATOR'
return tierNames[bot.tier ?? 0]
}
function getTierColor(bot: BotInfo | null): string {
if (!bot) return '#8a8a9a'
if (isCreator(bot)) return '#ffd700'
return tierColors[bot.tier ?? 0]
}
function selectFight(fightId: string) {
const fight = fights.value.find(f => f.id === fightId)
if (!fight || !fight.botA || !fight.botB) return
@@ -95,7 +111,7 @@ onUnmounted(() => {
</script>
<template>
<div class="h-[calc(100vh-4rem)] flex flex-col overflow-hidden relative">
<div class="h-[calc(100dvh-4rem)] flex flex-col overflow-hidden relative">
<!-- Page background -->
<div class="absolute inset-0 overflow-hidden pointer-events-none">
@@ -196,7 +212,7 @@ onUnmounted(() => {
<div class="flex-1 flex flex-col items-center min-w-0">
<div v-if="fighterA" class="relative fighter-bob">
<!-- Circular glow behind fighter -->
<div class="fighter-glow fighter-glow-cyan" />
<div class="fighter-glow" :class="isCreator(fighterA) ? 'fighter-glow-gold' : 'fighter-glow-cyan'" />
<SpritePreview
:key="`a-${featured.id}-${fighterA.avatarSeed}`"
:seed="fighterA.avatarSeed || fighterA.name"
@@ -212,7 +228,7 @@ onUnmounted(() => {
<div class="flex-1 flex flex-col items-center min-w-0 -ml-6 sm:-ml-12 lg:-ml-20">
<div v-if="fighterB" class="relative fighter-bob-delayed" style="transform: scaleX(-1)">
<!-- Circular glow behind fighter -->
<div class="fighter-glow fighter-glow-pink" />
<div class="fighter-glow" :class="isCreator(fighterB) ? 'fighter-glow-gold' : 'fighter-glow-pink'" />
<SpritePreview
:key="`b-${featured.id}-${fighterB.avatarSeed}`"
:seed="fighterB.avatarSeed || fighterB.name"
@@ -230,14 +246,16 @@ onUnmounted(() => {
<!-- Names with VS orb in center -->
<div class="flex items-stretch w-full max-w-3xl mt-1 sm:mt-2 lg:mt-3 shrink-0 relative">
<!-- Fighter A cyan -->
<div class="flex-1 min-w-0 name-block-cyan border-2 border-neon-cyan/40 rounded-l-lg px-2 sm:px-3 pr-16 sm:pr-20 lg:pr-24 py-1.5 sm:py-2 lg:py-3 text-center">
<p class="font-funky text-base sm:text-2xl lg:text-4xl tracking-widest text-neon-cyan truncate pb-0.5 poster-name-cyan uppercase">
<!-- Fighter A -->
<div class="flex-1 min-w-0 border-2 rounded-l-lg px-2 sm:px-3 pr-16 sm:pr-20 lg:pr-24 py-1.5 sm:py-2 lg:py-3 text-center"
:class="isCreator(fighterA) ? 'name-block-gold border-yellow-500/40' : 'name-block-cyan border-neon-cyan/40'">
<p class="font-funky text-base sm:text-2xl lg:text-4xl tracking-widest truncate pb-0.5 uppercase"
:class="isCreator(fighterA) ? 'text-yellow-400 poster-name-gold' : 'text-neon-cyan poster-name-cyan'">
{{ fighterA?.name || '???' }}
</p>
<p class="font-pixel text-[6px] sm:text-[8px] lg:text-[11px] tracking-[0.2em]"
:style="{ color: tierColors[fighterA?.tier ?? 0] }">
{{ tierNames[fighterA?.tier ?? 0] }}
:style="{ color: getTierColor(fighterA) }">
{{ getTierName(fighterA) }}
</p>
</div>
@@ -253,14 +271,16 @@ onUnmounted(() => {
</div>
</div>
<!-- Fighter B pink -->
<div class="flex-1 min-w-0 name-block-pink border-2 border-neon-pink/40 border-l-0 rounded-r-lg px-2 sm:px-3 pl-16 sm:pl-20 lg:pl-24 py-1.5 sm:py-2 lg:py-3 text-center">
<p class="font-funky text-base sm:text-2xl lg:text-4xl tracking-widest text-neon-pink truncate pb-0.5 poster-name-pink uppercase">
<!-- Fighter B -->
<div class="flex-1 min-w-0 border-2 border-l-0 rounded-r-lg px-2 sm:px-3 pl-16 sm:pl-20 lg:pl-24 py-1.5 sm:py-2 lg:py-3 text-center"
:class="isCreator(fighterB) ? 'name-block-gold border-yellow-500/40' : 'name-block-pink border-neon-pink/40'">
<p class="font-funky text-base sm:text-2xl lg:text-4xl tracking-widest truncate pb-0.5 uppercase"
:class="isCreator(fighterB) ? 'text-yellow-400 poster-name-gold' : 'text-neon-pink poster-name-pink'">
{{ fighterB?.name || '???' }}
</p>
<p class="font-pixel text-[6px] sm:text-[8px] lg:text-[11px] tracking-[0.2em]"
:style="{ color: tierColors[fighterB?.tier ?? 0] }">
{{ tierNames[fighterB?.tier ?? 0] }}
:style="{ color: getTierColor(fighterB) }">
{{ getTierName(fighterB) }}
</p>
</div>
</div>
@@ -295,7 +315,10 @@ onUnmounted(() => {
</div>
<div class="flex items-center">
<div class="flex-1 flex items-center justify-end gap-1.5 min-w-0">
<p class="font-display font-bold text-xs text-neon-cyan truncate">{{ mainEvent.botA.name }}</p>
<p class="font-display font-bold text-xs truncate"
:class="isCreator(mainEvent.botA) ? 'text-yellow-400' : 'text-neon-cyan'">
{{ isCreator(mainEvent.botA) ? '₿ ' : '' }}{{ mainEvent.botA.name }}
</p>
<SpritePreview
:seed="mainEvent.botA.avatarSeed || mainEvent.botA.name"
:archetype="mainEvent.botA.archetype || 'standard'"
@@ -314,7 +337,10 @@ onUnmounted(() => {
class="shrink-0"
style="transform: scaleX(-1)"
/>
<p class="font-display font-bold text-xs text-neon-pink truncate">{{ mainEvent.botB.name }}</p>
<p class="font-display font-bold text-xs truncate"
:class="isCreator(mainEvent.botB) ? 'text-yellow-400' : 'text-neon-pink'">
{{ mainEvent.botB.name }}{{ isCreator(mainEvent.botB) ? ' ₿' : '' }}
</p>
</div>
</div>
</button>
@@ -346,8 +372,8 @@ onUnmounted(() => {
>
<div class="flex-1 flex items-center justify-end gap-1.5 min-w-0">
<p class="font-display font-bold text-[11px] truncate"
:class="fight.botA ? 'text-text-primary' : 'text-text-muted'">
{{ fight.botA?.name || '???' }}
:class="isCreator(fight.botA) ? 'text-yellow-400' : fight.botA ? 'text-text-primary' : 'text-text-muted'">
{{ isCreator(fight.botA) ? '₿ ' : '' }}{{ fight.botA?.name || '???' }}
</p>
<SpritePreview
v-if="fight.botA"
@@ -370,8 +396,8 @@ onUnmounted(() => {
style="transform: scaleX(-1)"
/>
<p class="font-display font-bold text-[11px] truncate"
:class="fight.botB ? 'text-text-primary' : 'text-text-muted'">
{{ fight.botB?.name || '???' }}
:class="isCreator(fight.botB) ? 'text-yellow-400' : fight.botB ? 'text-text-primary' : 'text-text-muted'">
{{ fight.botB?.name || '???' }}{{ isCreator(fight.botB) ? ' ₿' : '' }}
</p>
</div>
<div class="ml-1.5 shrink-0">
@@ -469,6 +495,14 @@ onUnmounted(() => {
0 0 30px rgba(255, 45, 120, 0.3),
0 0 60px rgba(255, 45, 120, 0.15);
}
.poster-name-gold {
-webkit-text-stroke: 1px rgba(200, 160, 0, 0.6);
text-shadow:
2px 2px 0 rgba(130, 100, 0, 0.8),
0 0 10px rgba(255, 215, 0, 0.6),
0 0 30px rgba(255, 215, 0, 0.3),
0 0 60px rgba(255, 180, 0, 0.15);
}
.poster-title {
text-shadow:
0 0 15px rgba(255, 45, 120, 0.6),
@@ -483,6 +517,9 @@ onUnmounted(() => {
.name-block-pink {
background: linear-gradient(225deg, rgba(255, 45, 120, 0.08) 0%, rgba(255, 45, 120, 0.02) 100%);
}
.name-block-gold {
background: linear-gradient(180deg, rgba(255, 215, 0, 0.12) 0%, rgba(255, 180, 0, 0.04) 100%);
}
/* ═══ VS Orb — 3D circle ═══ */
.vs-orb {
@@ -578,6 +615,10 @@ onUnmounted(() => {
background: radial-gradient(circle, rgba(255, 45, 120, 0.25) 0%, rgba(255, 45, 120, 0.10) 35%, rgba(255, 45, 120, 0.03) 60%, transparent 80%);
box-shadow: 0 0 60px rgba(255, 45, 120, 0.15), 0 0 120px rgba(255, 45, 120, 0.05);
}
.fighter-glow-gold {
background: radial-gradient(circle, rgba(255, 215, 0, 0.35) 0%, rgba(255, 215, 0, 0.15) 35%, rgba(255, 180, 0, 0.05) 60%, transparent 80%);
box-shadow: 0 0 60px rgba(255, 215, 0, 0.25), 0 0 120px rgba(255, 215, 0, 0.10);
}
/* Fighter bobbing */
.fighter-bob {