feat: optimize card sorting for mobile displays

Mobile optimizations for Card Sorting game:

**Spectator Mode Bottom Sheet (iPhone):**
- Convert right sidebar to compact bottom sheet (120px tall)
- 3-column horizontal grid layout for stats (Time/Cards/Accuracy)
- Compact sizing: 8px padding, 10px labels, 16px values
- Dual-orientation toggle: ▲/▼ on mobile, ◀/▶ on desktop
- Emoji-only labels on mobile to save space
- Full viewport width on mobile (no wasted space)

**Results Screen:**
- Vertical layout on mobile (score panel above cards)
- Dynamic card sizing based on count (5→130%, 15→105%)
- Horizontal action buttons with equal widths
- 130px top padding to avoid mini app nav
- Compact score display (80px circle vs 160px desktop)

**Mini App Nav:**
- Prevent text wrapping with whiteSpace: nowrap
- Consistent height regardless of content length

**Bug Fixes:**
- Remove dead "Reveal Numbers" button code
- Fix checkSolution onClick handler type error

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock
2025-10-24 11:44:49 -05:00
parent 0cced47a0f
commit b443ee9cdc
5 changed files with 351 additions and 223 deletions

View File

@@ -180,32 +180,9 @@ export function PlayingPhase() {
</div>
<div className={css({ display: 'flex', gap: '0.5rem' })}>
{state.showNumbers && !state.numbersRevealed && (
<button
type="button"
onClick={revealNumbers}
disabled={isSpectating}
className={css({
padding: '0.5rem 1rem',
borderRadius: '0.375rem',
background: isSpectating ? 'gray.300' : 'orange.500',
color: 'white',
fontSize: 'sm',
fontWeight: '600',
border: 'none',
cursor: isSpectating ? 'not-allowed' : 'pointer',
opacity: isSpectating ? 0.5 : 1,
_hover: {
background: isSpectating ? 'gray.300' : 'orange.600',
},
})}
>
Reveal Numbers
</button>
)}
<button
type="button"
onClick={checkSolution}
onClick={() => checkSolution()}
disabled={!canCheckSolution || isSpectating}
className={css({
padding: '0.5rem 1rem',
@@ -337,23 +314,6 @@ export function PlayingPhase() {
},
})}
/>
{state.numbersRevealed && (
<div
className={css({
position: 'absolute',
top: '5px',
right: '5px',
background: '#ffc107',
color: '#333',
borderRadius: '4px',
padding: '2px 8px',
fontSize: '14px',
fontWeight: 'bold',
})}
>
{card.number}
</div>
)}
</div>
))}
</div>

View File

@@ -929,8 +929,9 @@ export function PlayingPhaseDrag() {
const getEffectiveViewportWidth = () => {
if (typeof window === 'undefined') return 1000
const baseWidth = window.innerWidth
if (isSpectating && !spectatorStatsCollapsed) {
return baseWidth - 280 // Subtract stats sidebar width
// Sidebar is hidden on mobile (< 768px), narrower on desktop
if (isSpectating && !spectatorStatsCollapsed && baseWidth >= 768) {
return baseWidth - 240 // Subtract stats sidebar width on desktop
}
return baseWidth
}
@@ -938,8 +939,10 @@ export function PlayingPhaseDrag() {
const getEffectiveViewportHeight = () => {
if (typeof window === 'undefined') return 800
const baseHeight = window.innerHeight
const baseWidth = window.innerWidth
if (isSpectating) {
return baseHeight - 56 // Subtract banner height
// Banner is 170px on mobile (130px mini nav + 40px spectator banner), 56px on desktop
return baseHeight - (baseWidth < 768 ? 170 : 56)
}
return baseHeight
}
@@ -1457,19 +1460,19 @@ export function PlayingPhaseDrag() {
<div
className={css({
position: 'fixed',
top: 0,
top: { base: '130px', md: 0 },
left: 0,
right: 0,
height: '56px',
height: { base: '40px', md: '56px' },
background: 'linear-gradient(135deg, #6366f1, #8b5cf6)',
color: 'white',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '0 24px',
padding: { base: '0 8px', md: '0 24px' },
zIndex: 100,
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
gap: '16px',
gap: { base: '8px', md: '16px' },
})}
>
{/* Player info */}
@@ -1477,12 +1480,12 @@ export function PlayingPhaseDrag() {
className={css({
display: 'flex',
alignItems: 'center',
gap: '12px',
fontSize: '16px',
gap: { base: '6px', md: '12px' },
fontSize: { base: '12px', md: '16px' },
fontWeight: '600',
})}
>
<span>👀 Spectating:</span>
<span className={css({ display: { base: 'none', sm: 'inline' } })}>👀 Spectating:</span>
<span>
{state.playerMetadata.emoji} {state.playerMetadata.name}
</span>
@@ -1493,22 +1496,22 @@ export function PlayingPhaseDrag() {
className={css({
display: 'flex',
alignItems: 'center',
gap: '16px',
fontSize: '14px',
gap: { base: '8px', md: '16px' },
fontSize: { base: '11px', md: '14px' },
})}
>
<div
className={css({
display: 'flex',
alignItems: 'center',
gap: '8px',
gap: { base: '4px', md: '8px' },
})}
>
<span>Progress:</span>
<span className={css({ fontWeight: '600', fontSize: '16px' })}>
<span className={css({ display: { base: 'none', sm: 'inline' } })}>Progress:</span>
<span className={css({ fontWeight: '600', fontSize: { base: '12px', md: '16px' } })}>
{state.placedCards.filter((c) => c !== null).length}/{state.cardCount}
</span>
<span>cards</span>
<span className={css({ display: { base: 'none', sm: 'inline' } })}>cards</span>
</div>
{/* Educational Mode Toggle */}
@@ -1518,15 +1521,15 @@ export function PlayingPhaseDrag() {
className={css({
display: 'flex',
alignItems: 'center',
gap: '8px',
padding: '6px 12px',
gap: { base: '4px', md: '8px' },
padding: { base: '4px 8px', md: '6px 12px' },
borderRadius: '20px',
border: '2px solid rgba(255, 255, 255, 0.3)',
background: spectatorEducationalMode
? 'rgba(255, 255, 255, 0.2)'
: 'rgba(255, 255, 255, 0.1)',
color: 'white',
fontSize: '14px',
fontSize: { base: '11px', md: '14px' },
fontWeight: '500',
cursor: 'pointer',
transition: 'all 0.2s',
@@ -1537,24 +1540,32 @@ export function PlayingPhaseDrag() {
})}
>
<span>{spectatorEducationalMode ? '✅' : '📚'}</span>
<span>Educational Mode</span>
<span className={css({ display: { base: 'none', sm: 'inline' } })}>
Educational Mode
</span>
</button>
</div>
</div>
)}
{/* Spectator Stats Sidebar */}
{/* Spectator Stats Sidebar/Bottom Sheet */}
{isSpectating && (
<div
className={css({
position: 'fixed',
top: '56px', // Below banner
right: spectatorStatsCollapsed ? '-280px' : '0',
width: '280px',
height: 'calc(100vh - 56px)',
// Mobile: bottom sheet, Desktop: right sidebar
top: { base: 'auto', md: '56px' },
bottom: { base: spectatorStatsCollapsed ? '-120px' : '0', md: 'auto' },
right: { base: '0', md: spectatorStatsCollapsed ? '-240px' : '0' },
left: { base: '0', md: 'auto' },
width: { base: '100%', md: '240px' },
height: { base: '120px', md: 'calc(100vh - 56px)' },
background: 'rgba(255, 255, 255, 0.95)',
boxShadow: '-2px 0 12px rgba(0, 0, 0, 0.1)',
transition: 'right 0.3s ease',
boxShadow: {
base: '0 -2px 12px rgba(0, 0, 0, 0.1)',
md: '-2px 0 12px rgba(0, 0, 0, 0.1)',
},
transition: { base: 'bottom 0.3s ease', md: 'right 0.3s ease' },
zIndex: 90,
display: 'flex',
flexDirection: 'column',
@@ -1566,118 +1577,199 @@ export function PlayingPhaseDrag() {
onClick={() => setSpectatorStatsCollapsed(!spectatorStatsCollapsed)}
className={css({
position: 'absolute',
left: '-40px',
top: '50%',
transform: 'translateY(-50%)',
width: '40px',
height: '80px',
// Mobile: top center, Desktop: left middle
left: { base: '50%', md: '-40px' },
top: { base: '-30px', md: '50%' },
transform: { base: 'translateX(-50%)', md: 'translateY(-50%)' },
width: { base: '80px', md: '40px' },
height: { base: '30px', md: '80px' },
background: 'rgba(255, 255, 255, 0.95)',
border: 'none',
borderRadius: '8px 0 0 8px',
boxShadow: '-2px 0 8px rgba(0, 0, 0, 0.1)',
borderRadius: { base: '8px 8px 0 0', md: '8px 0 0 8px' },
boxShadow: {
base: '0 -2px 8px rgba(0, 0, 0, 0.1)',
md: '-2px 0 8px rgba(0, 0, 0, 0.1)',
},
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '20px',
fontSize: { base: '16px', md: '20px' },
transition: 'all 0.2s',
_hover: {
background: 'rgba(255, 255, 255, 1)',
width: '44px',
},
})}
>
{spectatorStatsCollapsed ? '◀' : ''}
<span className={css({ display: { base: 'inline', md: 'none' } })}>
{spectatorStatsCollapsed ? '▲' : '▼'}
</span>
<span className={css({ display: { base: 'none', md: 'inline' } })}>
{spectatorStatsCollapsed ? '◀' : '▶'}
</span>
</button>
{/* Stats Content */}
<div
className={css({
padding: '24px',
padding: { base: '8px 12px', md: '24px' },
overflowY: 'auto',
flex: 1,
})}
>
<h3
className={css({
fontSize: '18px',
fontSize: { base: '12px', md: '18px' },
fontWeight: '700',
marginBottom: '20px',
marginBottom: { base: '6px', md: '20px' },
color: '#1e293b',
borderBottom: '2px solid #e2e8f0',
paddingBottom: '8px',
paddingBottom: { base: '3px', md: '8px' },
})}
>
📊 Live Stats
<span className={css({ display: { base: 'none', md: 'inline' } })}>
📊 Live Stats
</span>
<span className={css({ display: { base: 'inline', md: 'none' } })}>📊 Stats</span>
</h3>
{/* Time Elapsed */}
{/* Mobile: horizontal layout, Desktop: vertical layout */}
<div
className={css({
marginBottom: '16px',
padding: '12px',
background: 'linear-gradient(135deg, #dbeafe, #bfdbfe)',
borderRadius: '8px',
border: '1px solid #93c5fd',
display: { base: 'grid', md: 'block' },
gridTemplateColumns: { base: 'repeat(3, 1fr)', md: 'none' },
gap: { base: '8px', md: '0' },
})}
>
<div className={css({ fontSize: '12px', color: '#1e40af', marginBottom: '4px' })}>
Time Elapsed
{/* Time Elapsed */}
<div
className={css({
marginBottom: { base: '0', md: '16px' },
padding: { base: '8px', md: '12px' },
background: 'linear-gradient(135deg, #dbeafe, #bfdbfe)',
borderRadius: { base: '6px', md: '8px' },
border: '1px solid #93c5fd',
})}
>
<div
className={css({
fontSize: { base: '10px', md: '12px' },
color: '#1e40af',
marginBottom: '4px',
})}
>
<span className={css({ display: { base: 'none', md: 'inline' } })}>
Time Elapsed
</span>
<span className={css({ display: { base: 'inline', md: 'none' } })}></span>
</div>
<div
className={css({
fontSize: { base: '16px', md: '24px' },
fontWeight: '700',
color: '#1e3a8a',
})}
>
{Math.floor(elapsedTime / 60)}:{(elapsedTime % 60).toString().padStart(2, '0')}
</div>
</div>
<div className={css({ fontSize: '24px', fontWeight: '700', color: '#1e3a8a' })}>
{Math.floor(elapsedTime / 60)}:{(elapsedTime % 60).toString().padStart(2, '0')}
</div>
</div>
{/* Cards Placed */}
<div
className={css({
marginBottom: '16px',
padding: '12px',
background: 'linear-gradient(135deg, #dcfce7, #bbf7d0)',
borderRadius: '8px',
border: '1px solid #86efac',
})}
>
<div className={css({ fontSize: '12px', color: '#15803d', marginBottom: '4px' })}>
🎯 Cards Placed
{/* Cards Placed */}
<div
className={css({
marginBottom: { base: '0', md: '16px' },
padding: { base: '8px', md: '12px' },
background: 'linear-gradient(135deg, #dcfce7, #bbf7d0)',
borderRadius: { base: '6px', md: '8px' },
border: '1px solid #86efac',
})}
>
<div
className={css({
fontSize: { base: '10px', md: '12px' },
color: '#15803d',
marginBottom: '4px',
})}
>
<span className={css({ display: { base: 'none', md: 'inline' } })}>
🎯 Cards Placed
</span>
<span className={css({ display: { base: 'inline', md: 'none' } })}>🎯</span>
</div>
<div
className={css({
fontSize: { base: '16px', md: '24px' },
fontWeight: '700',
color: '#14532d',
})}
>
{state.placedCards.filter((c) => c !== null).length} / {state.cardCount}
</div>
<div
className={css({
fontSize: '11px',
color: '#15803d',
marginTop: '4px',
display: { base: 'none', md: 'block' },
})}
>
{Math.round(
(state.placedCards.filter((c) => c !== null).length / state.cardCount) * 100
)}
% complete
</div>
</div>
<div className={css({ fontSize: '24px', fontWeight: '700', color: '#14532d' })}>
{state.placedCards.filter((c) => c !== null).length} / {state.cardCount}
</div>
<div className={css({ fontSize: '11px', color: '#15803d', marginTop: '4px' })}>
{Math.round(
(state.placedCards.filter((c) => c !== null).length / state.cardCount) * 100
)}
% complete
</div>
</div>
{/* Current Accuracy */}
<div
className={css({
marginBottom: '16px',
padding: '12px',
background: 'linear-gradient(135deg, #fef3c7, #fde68a)',
borderRadius: '8px',
border: '1px solid #fbbf24',
})}
>
<div className={css({ fontSize: '12px', color: '#92400e', marginBottom: '4px' })}>
Current Accuracy
</div>
<div className={css({ fontSize: '24px', fontWeight: '700', color: '#78350f' })}>
{(() => {
const placedCards = state.placedCards.filter((c): c is SortingCard => c !== null)
if (placedCards.length === 0) return '0%'
const correctCount = placedCards.filter(
(c, i) => state.correctOrder[i]?.id === c.id
).length
return `${Math.round((correctCount / placedCards.length) * 100)}%`
})()}
</div>
<div className={css({ fontSize: '11px', color: '#92400e', marginTop: '4px' })}>
Cards in correct position
{/* Current Accuracy */}
<div
className={css({
marginBottom: { base: '0', md: '16px' },
padding: { base: '8px', md: '12px' },
background: 'linear-gradient(135deg, #fef3c7, #fde68a)',
borderRadius: { base: '6px', md: '8px' },
border: '1px solid #fbbf24',
})}
>
<div
className={css({
fontSize: { base: '10px', md: '12px' },
color: '#92400e',
marginBottom: '4px',
})}
>
<span className={css({ display: { base: 'none', md: 'inline' } })}>
Current Accuracy
</span>
<span className={css({ display: { base: 'inline', md: 'none' } })}></span>
</div>
<div
className={css({
fontSize: { base: '16px', md: '24px' },
fontWeight: '700',
color: '#78350f',
})}
>
{(() => {
const placedCards = state.placedCards.filter(
(c): c is SortingCard => c !== null
)
if (placedCards.length === 0) return '0%'
const correctCount = placedCards.filter(
(c, i) => state.correctOrder[i]?.id === c.id
).length
return `${Math.round((correctCount / placedCards.length) * 100)}%`
})()}
</div>
<div
className={css({
fontSize: '11px',
color: '#92400e',
marginTop: '4px',
display: { base: 'none', md: 'block' },
})}
>
Cards in correct position
</div>
</div>
</div>
</div>
@@ -1791,15 +1883,31 @@ export function PlayingPhaseDrag() {
<div
ref={containerRef}
className={css({
width: isSpectating && !spectatorStatsCollapsed ? 'calc(100vw - 280px)' : '100vw',
height: isSpectating ? 'calc(100vh - 56px)' : '100vh',
position: 'absolute',
top: isSpectating ? '56px' : 0,
left: 0,
background: 'linear-gradient(135deg, #f0f9ff, #e0f2fe)',
overflow: 'hidden',
transition: 'width 0.3s ease, height 0.3s ease, top 0.3s ease',
})}
style={{
width:
isSpectating &&
!spectatorStatsCollapsed &&
typeof window !== 'undefined' &&
window.innerWidth >= 768
? 'calc(100vw - 240px)'
: '100vw',
height: isSpectating
? typeof window !== 'undefined' && window.innerWidth < 768
? 'calc(100vh - 170px)'
: 'calc(100vh - 56px)'
: '100vh',
top: isSpectating
? typeof window !== 'undefined' && window.innerWidth < 768
? '170px'
: '56px'
: '0',
}}
>
{/* Render continuous curved path through the entire sequence */}
{inferredSequence.length > 1 && (

View File

@@ -95,7 +95,7 @@ export function ResultsPhase() {
width: '100%',
height: '100%',
display: 'flex',
flexDirection: { base: 'column-reverse', md: 'row' },
flexDirection: { base: 'column', md: 'row' },
position: 'fixed',
top: 0,
left: 0,
@@ -103,6 +103,8 @@ export function ResultsPhase() {
bottom: 0,
background: 'linear-gradient(135deg, #f0f9ff, #e0f2fe)',
overflow: 'auto',
paddingTop: { base: '130px', md: 0 },
paddingBottom: { base: '70px', md: 0 },
})}
>
{/* Cards Grid Area */}
@@ -110,17 +112,23 @@ export function ResultsPhase() {
className={css({
flex: { base: '0 0 auto', md: 1 },
display: 'flex',
alignItems: 'center',
alignItems: 'flex-start',
justifyContent: 'center',
padding: { base: '16px 12px', md: '40px' },
overflow: { base: 'visible', md: 'auto' },
padding: { base: '8px', md: '40px' },
overflow: { base: 'auto', md: 'auto' },
order: { base: 2, md: 1 },
maxHeight: { base: '50vh', md: 'none' },
})}
>
<div
className={css({
display: 'grid',
gridTemplateColumns: { base: 'repeat(2, 1fr)', sm: 'repeat(3, 1fr)', md: 'repeat(3, 1fr)' },
gap: { base: '12px', md: '16px' },
gridTemplateColumns: {
base: 'repeat(3, 1fr)',
sm: 'repeat(3, 1fr)',
md: 'repeat(3, 1fr)',
},
gap: state.cardCount <= 5 ? { base: '8px', md: '16px' } : { base: '6px', md: '16px' },
maxWidth: '600px',
width: '100%',
})}
@@ -132,11 +140,18 @@ export function ResultsPhase() {
return (
<div
key={card.id}
className={css({
style={{
position: 'relative',
width: '100%',
paddingBottom: '125%', // 5:4 aspect ratio
})}
paddingBottom:
state.cardCount <= 5
? '130%'
: state.cardCount <= 8
? '120%'
: state.cardCount <= 12
? '110%'
: '105%',
}}
>
{/* Card */}
<div
@@ -170,16 +185,31 @@ export function ResultsPhase() {
<div
className={css({
position: 'absolute',
top: { base: '-8px', md: '-12px' },
right: { base: '-8px', md: '-12px' },
width: { base: '24px', md: '32px' },
height: { base: '24px', md: '32px' },
top:
state.cardCount > 8
? { base: '-6px', md: '-12px' }
: { base: '-8px', md: '-12px' },
right:
state.cardCount > 8
? { base: '-6px', md: '-12px' }
: { base: '-8px', md: '-12px' },
width:
state.cardCount > 8
? { base: '20px', md: '32px' }
: { base: '24px', md: '32px' },
height:
state.cardCount > 8
? { base: '20px', md: '32px' }
: { base: '24px', md: '32px' },
borderRadius: '50%',
background: isCorrect ? '#22c55e' : '#ef4444',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: { base: '16px', md: '20px' },
fontSize:
state.cardCount > 8
? { base: '12px', md: '20px' }
: { base: '16px', md: '20px' },
color: 'white',
fontWeight: 'bold',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.2)',
@@ -194,14 +224,23 @@ export function ResultsPhase() {
<div
className={css({
position: 'absolute',
bottom: { base: '-6px', md: '-8px' },
bottom:
state.cardCount > 8
? { base: '-5px', md: '-8px' }
: { base: '-6px', md: '-8px' },
left: '50%',
transform: 'translateX(-50%)',
background: isCorrect ? '#22c55e' : showCorrections ? '#ef4444' : '#0369a1',
color: 'white',
padding: { base: '3px 6px', md: '4px 8px' },
borderRadius: { base: '8px', md: '12px' },
fontSize: { base: '10px', md: '12px' },
padding:
state.cardCount > 8
? { base: '2px 5px', md: '4px 8px' }
: { base: '3px 6px', md: '4px 8px' },
borderRadius: { base: '6px', md: '12px' },
fontSize:
state.cardCount > 8
? { base: '9px', md: '12px' }
: { base: '10px', md: '12px' },
fontWeight: 'bold',
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.2)',
})}
@@ -221,30 +260,32 @@ export function ResultsPhase() {
background: 'rgba(255, 255, 255, 0.95)',
borderLeft: { base: 'none', md: '3px solid rgba(59, 130, 246, 0.3)' },
borderBottom: { base: '3px solid rgba(59, 130, 246, 0.3)', md: 'none' },
padding: { base: '20px 16px', md: '40px' },
padding: { base: '12px', md: '40px' },
overflow: 'auto',
display: 'flex',
flexDirection: 'column',
gap: { base: '16px', md: '24px' },
gap: { base: '10px', md: '24px' },
boxShadow: {
base: '0 4px 20px rgba(0, 0, 0, 0.1)',
md: '-4px 0 20px rgba(0, 0, 0, 0.1)',
},
order: { base: 1, md: 2 },
})}
>
{/* Score Circle */}
<div
className={css({
display: 'flex',
flexDirection: 'column',
flexDirection: { base: 'row', md: 'column' },
alignItems: 'center',
gap: { base: '12px', md: '16px' },
justifyContent: { base: 'space-between', md: 'center' },
gap: { base: '10px', md: '16px' },
})}
>
<div
className={css({
width: { base: '120px', md: '160px' },
height: { base: '120px', md: '160px' },
width: { base: '80px', md: '160px' },
height: { base: '80px', md: '160px' },
borderRadius: '50%',
background: isPerfect
? 'linear-gradient(135deg, #fbbf24, #f59e0b)'
@@ -261,6 +302,7 @@ export function ResultsPhase() {
animation: isPerfect
? 'perfectCelebrate 0.6s ease-in-out'
: 'scoreReveal 0.6s ease-out',
flexShrink: 0,
})}
style={{
animationName: isPerfect ? 'perfectCelebrate' : 'scoreReveal',
@@ -268,7 +310,7 @@ export function ResultsPhase() {
>
<div
className={css({
fontSize: { base: '48px', md: '64px' },
fontSize: { base: '32px', md: '64px' },
fontWeight: 'bold',
color: 'white',
lineHeight: 1,
@@ -279,7 +321,7 @@ export function ResultsPhase() {
</div>
<div
className={css({
fontSize: { base: '16px', md: '20px' },
fontSize: { base: '12px', md: '20px' },
fontWeight: '600',
color: 'white',
opacity: 0.9,
@@ -289,50 +331,61 @@ export function ResultsPhase() {
</div>
</div>
{/* Team/Solo Label */}
{isCollaborative && (
<div
className={css({
display: 'flex',
flexDirection: 'column',
alignItems: { base: 'flex-start', md: 'center' },
gap: { base: '6px', md: '12px' },
flex: { base: 1, md: 'none' },
})}
>
{/* Team/Solo Label */}
{isCollaborative && (
<div
className={css({
padding: { base: '4px 10px', md: '6px 16px' },
background: 'linear-gradient(135deg, #a78bfa, #8b5cf6)',
borderRadius: '20px',
fontSize: { base: '11px', md: '13px' },
fontWeight: '700',
color: 'white',
textTransform: 'uppercase',
letterSpacing: '0.5px',
boxShadow: '0 2px 8px rgba(139, 92, 246, 0.3)',
})}
>
👥 Team Score
</div>
)}
<div
className={css({
padding: { base: '5px 12px', md: '6px 16px' },
background: 'linear-gradient(135deg, #a78bfa, #8b5cf6)',
borderRadius: '20px',
fontSize: { base: '12px', md: '13px' },
fontWeight: '700',
color: 'white',
textTransform: 'uppercase',
letterSpacing: '0.5px',
boxShadow: '0 2px 8px rgba(139, 92, 246, 0.3)',
textAlign: { base: 'left', md: 'center' },
fontSize: { base: '13px', md: '18px' },
fontWeight: '600',
color: '#0c4a6e',
lineHeight: 1.2,
display: { base: 'none', md: 'block' },
})}
>
👥 Team Score
{getMessage(scoreBreakdown.finalScore)}
</div>
)}
<div
className={css({
textAlign: 'center',
fontSize: { base: '15px', md: '18px' },
fontWeight: '600',
color: '#0c4a6e',
lineHeight: 1.3,
})}
>
{getMessage(scoreBreakdown.finalScore)}
</div>
{/* Time Badge */}
<div
className={css({
padding: { base: '6px 16px', md: '8px 20px' },
background: 'rgba(59, 130, 246, 0.1)',
border: '2px solid rgba(59, 130, 246, 0.3)',
borderRadius: '20px',
fontSize: { base: '14px', md: '16px' },
fontWeight: '600',
color: '#0c4a6e',
})}
>
{formatTime(scoreBreakdown.elapsedTime)}
{/* Time Badge */}
<div
className={css({
padding: { base: '4px 12px', md: '8px 20px' },
background: 'rgba(59, 130, 246, 0.1)',
border: '2px solid rgba(59, 130, 246, 0.3)',
borderRadius: '20px',
fontSize: { base: '12px', md: '16px' },
fontWeight: '600',
color: '#0c4a6e',
})}
>
{formatTime(scoreBreakdown.elapsedTime)}
</div>
</div>
</div>
@@ -523,8 +576,8 @@ export function ResultsPhase() {
<div
className={css({
display: 'flex',
flexDirection: 'column',
gap: { base: '8px', md: '10px' },
flexDirection: { base: 'row', md: 'column' },
gap: { base: '6px', md: '10px' },
marginTop: 'auto',
})}
>
@@ -532,11 +585,11 @@ export function ResultsPhase() {
type="button"
onClick={startGame}
className={css({
padding: { base: '12px 20px', md: '14px 24px' },
padding: { base: '10px 8px', md: '14px 24px' },
background: 'linear-gradient(135deg, #86efac, #22c55e)',
border: { base: '2px solid #22c55e', md: '3px solid #22c55e' },
borderRadius: { base: '10px', md: '12px' },
fontSize: { base: '14px', md: '16px' },
borderRadius: { base: '8px', md: '12px' },
fontSize: { base: '11px', md: '16px' },
fontWeight: '700',
color: 'white',
cursor: 'pointer',
@@ -544,61 +597,64 @@ export function ResultsPhase() {
boxShadow: '0 4px 12px rgba(34, 197, 94, 0.3)',
textTransform: 'uppercase',
letterSpacing: '0.5px',
flex: { base: 1, md: 'none' },
_hover: {
transform: 'translateY(-2px)',
boxShadow: '0 6px 20px rgba(34, 197, 94, 0.4)',
},
})}
>
🎮 Play Again
<span className={css({ display: { base: 'none', md: 'inline' } })}>🎮 </span>Play
</button>
<button
type="button"
onClick={goToSetup}
className={css({
padding: { base: '10px 16px', md: '12px 20px' },
padding: { base: '10px 8px', md: '12px 20px' },
background: 'white',
border: '2px solid rgba(59, 130, 246, 0.3)',
borderRadius: { base: '10px', md: '12px' },
fontSize: { base: '13px', md: '14px' },
borderRadius: { base: '8px', md: '12px' },
fontSize: { base: '11px', md: '14px' },
fontWeight: '700',
color: '#0c4a6e',
cursor: 'pointer',
transition: 'all 0.2s ease',
textTransform: 'uppercase',
letterSpacing: '0.5px',
flex: { base: 1, md: 'none' },
_hover: {
borderColor: 'rgba(59, 130, 246, 0.5)',
background: 'rgba(59, 130, 246, 0.05)',
},
})}
>
Settings
<span className={css({ display: { base: 'none', md: 'inline' } })}> </span>Settings
</button>
<button
type="button"
onClick={exitSession}
className={css({
padding: { base: '10px 16px', md: '12px 20px' },
padding: { base: '10px 8px', md: '12px 20px' },
background: 'white',
border: '2px solid rgba(239, 68, 68, 0.3)',
borderRadius: { base: '10px', md: '12px' },
fontSize: { base: '13px', md: '14px' },
borderRadius: { base: '8px', md: '12px' },
fontSize: { base: '11px', md: '14px' },
fontWeight: '700',
color: '#991b1b',
cursor: 'pointer',
transition: 'all 0.2s ease',
textTransform: 'uppercase',
letterSpacing: '0.5px',
flex: { base: 1, md: 'none' },
_hover: {
borderColor: 'rgba(239, 68, 68, 0.5)',
background: 'rgba(239, 68, 68, 0.05)',
},
})}
>
🚪 Exit
<span className={css({ display: { base: 'none', md: 'inline' } })}>🚪 </span>Exit
</button>
</div>
</div>

View File

@@ -503,6 +503,8 @@ function MinimalNav({
transition: 'opacity 0.3s ease',
pointerEvents: 'auto',
maxWidth: 'calc(100% - 128px)', // Leave space for hamburger + margin
whiteSpace: 'nowrap',
overflow: 'hidden',
}}
onMouseEnter={(e) => {
e.currentTarget.style.opacity = '1'

View File

@@ -183,6 +183,8 @@ export function GameContextNav({
gap: '20px',
alignItems: 'center',
width: 'auto',
whiteSpace: 'nowrap',
overflow: 'hidden',
}}
>
{/* Game Title Section - Always mounted, hidden when in room */}