Make app fully responsive for smartphones

Layout:
- Remove body min-width: 1280px
- Sidebar hidden on mobile (<768px); replaced by a fixed bottom navigation bar (BottomNav component) with icon + label for all 7 routes
- PageShell main gets className="app-main"; padding overridden to 0 on mobile (68px bottom clearance for bottom nav)
- All page root divs get className="page-body"; mobile override to 12px 14px padding

Dashboard:
- 4-col stat grid → 2×2 on mobile (dash-stat-grid)
- 3-col timing/targets/forecast → single column (dash-three-col)
- 2-col monthly/best-nights → single column (dash-two-col)
- Run order timeline: overflow-x: auto for narrow screens

Targets:
- Filter bar rows get className="filter-row": horizontal scroll on mobile (no-wrap + overflow-x: auto)
- Table gets className="targets-table": columns 3-7 (Size/Fill/Mosaic/Mag/Diff) and 10+ (Vis/Int/Goal/Compare) hidden on mobile, keeping only Type, Name, Filter, Alt

Calendar:
- Split view gets dash-two-col (stacks on mobile)
- Month grid: minWidth 308px + overflowX auto; cell minWidth 44px for touch targets

Compare modal: columns stack vertically on mobile (compare-body / compare-col classes)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-17 07:35:10 +02:00
parent 2bb80a8475
commit 561de4f13b
11 changed files with 141 additions and 84 deletions
+3 -2
View File
@@ -1,5 +1,5 @@
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import Sidebar from './Sidebar'; import Sidebar, { BottomNav } from './Sidebar';
interface Props { interface Props {
children: ReactNode; children: ReactNode;
@@ -9,7 +9,7 @@ export default function PageShell({ children }: Props) {
return ( return (
<div style={{ display: 'flex', height: '100vh', overflow: 'hidden' }}> <div style={{ display: 'flex', height: '100vh', overflow: 'hidden' }}>
<Sidebar /> <Sidebar />
<main style={{ <main className="app-main" style={{
flex: 1, flex: 1,
overflow: 'auto', overflow: 'auto',
background: 'var(--bg-void)', background: 'var(--bg-void)',
@@ -17,6 +17,7 @@ export default function PageShell({ children }: Props) {
}}> }}>
{children} {children}
</main> </main>
<BottomNav />
</div> </div>
); );
} }
+32 -56
View File
@@ -17,27 +17,41 @@ const navItems = [
{ path: '/dashboard', label: 'Dashboard', icon: '⬡' }, { path: '/dashboard', label: 'Dashboard', icon: '⬡' },
{ path: '/targets', label: 'Targets', icon: '✦' }, { path: '/targets', label: 'Targets', icon: '✦' },
{ path: '/calendar', label: 'Calendar', icon: '◫' }, { path: '/calendar', label: 'Calendar', icon: '◫' },
{ path: '/stats', label: 'Statistics', icon: '▤' }, { path: '/stats', label: 'Stats', icon: '▤' },
{ path: '/gallery', label: 'Gallery', icon: '⬚' }, { path: '/gallery', label: 'Gallery', icon: '⬚' },
{ path: '/solar-system', label: 'Solar System', icon: '◉' }, { path: '/solar-system', label: 'Solar', icon: '◉' },
{ path: '/settings', label: 'Settings', icon: '⚙' }, { path: '/settings', label: 'Settings', icon: '⚙' },
]; ];
function fmtTime(utc?: string): string { function fmtTime(utc?: string): string {
if (!utc) return '—'; if (!utc) return '—';
return new Date(utc).toLocaleTimeString('fr-FR', { return new Date(utc).toLocaleTimeString('fr-FR', {
hour: '2-digit', hour: '2-digit', minute: '2-digit', timeZone: 'Europe/Paris',
minute: '2-digit',
timeZone: 'Europe/Paris',
}); });
} }
export function BottomNav() {
return (
<nav className="bottom-nav">
{navItems.map(item => (
<NavLink
key={item.path}
to={item.path}
className={({ isActive }) => isActive ? 'active' : ''}
>
<span className="bnav-icon">{item.icon}</span>
{item.label}
</NavLink>
))}
</nav>
);
}
export default function Sidebar() { export default function Sidebar() {
const { data: tonight } = useTonight(); const { data: tonight } = useTonight();
const { data: weather } = useWeather(); const { data: weather } = useWeather();
const { data: forecast } = useForecast(); const { data: forecast } = useForecast();
// First forecast slot = current/nearest 3-hour window
const slot = (forecast as { dataseries?: { seeing?: number; transparency?: number; cloudcover?: number }[] })?.dataseries?.[0]; const slot = (forecast as { dataseries?: { seeing?: number; transparency?: number; cloudcover?: number }[] })?.dataseries?.[0];
const darkStart = tonight?.true_dark_start_utc; const darkStart = tonight?.true_dark_start_utc;
@@ -50,13 +64,8 @@ export default function Sidebar() {
? (weather.temp_c - weather.dew_point_c).toFixed(1) ? (weather.temp_c - weather.dew_point_c).toFixed(1)
: null; : null;
const seeingMap: Record<number, string> = {
1: '0.5″', 2: '0.75″', 3: '1.0″', 4: '1.25″',
5: '1.5″', 6: '2.0″', 7: '2.5″', 8: '>3″',
};
return ( return (
<aside style={{ <aside className="app-sidebar" style={{
width: 220, width: 220,
minWidth: 220, minWidth: 220,
background: 'var(--bg-deep)', background: 'var(--bg-deep)',
@@ -69,14 +78,10 @@ export default function Sidebar() {
overflow: 'hidden', overflow: 'hidden',
}}> }}>
{/* Logo */} {/* Logo */}
<div style={{ <div style={{ padding: '20px 20px 16px', borderBottom: '1px solid var(--border)' }}>
padding: '20px 20px 16px',
borderBottom: '1px solid var(--border)',
}}>
<div style={{ <div style={{
fontFamily: 'var(--font-display)', fontFamily: 'var(--font-display)',
fontSize: 16, fontSize: 16, fontWeight: 700,
fontWeight: 700,
color: 'var(--amber)', color: 'var(--amber)',
letterSpacing: '0.06em', letterSpacing: '0.06em',
textTransform: 'uppercase', textTransform: 'uppercase',
@@ -92,16 +97,13 @@ export default function Sidebar() {
key={item.path} key={item.path}
to={item.path} to={item.path}
style={({ isActive }) => ({ style={({ isActive }) => ({
display: 'flex', display: 'flex', alignItems: 'center', gap: 10,
alignItems: 'center',
gap: 10,
padding: '9px 20px', padding: '9px 20px',
color: isActive ? 'var(--text-hi)' : 'var(--text-mid)', color: isActive ? 'var(--text-hi)' : 'var(--text-mid)',
background: isActive ? 'var(--bg-hover)' : 'transparent', background: isActive ? 'var(--bg-hover)' : 'transparent',
borderLeft: `2px solid ${isActive ? 'var(--amber)' : 'transparent'}`, borderLeft: `2px solid ${isActive ? 'var(--amber)' : 'transparent'}`,
fontFamily: 'var(--font-display)', fontFamily: 'var(--font-display)',
fontSize: 13, fontSize: 13, fontWeight: isActive ? 700 : 400,
fontWeight: isActive ? 700 : 400,
letterSpacing: '0.05em', letterSpacing: '0.05em',
textDecoration: 'none', textDecoration: 'none',
transition: 'color 0.1s', transition: 'color 0.1s',
@@ -114,18 +116,8 @@ export default function Sidebar() {
</nav> </nav>
{/* Tonight widget */} {/* Tonight widget */}
<div style={{ <div style={{ borderTop: '1px solid var(--border)', padding: '12px 16px' }}>
borderTop: '1px solid var(--border)', <div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', color: 'var(--text-lo)', letterSpacing: '0.1em', marginBottom: 8, textTransform: 'uppercase' }}>
padding: '12px 16px',
}}>
<div style={{
fontSize: 10,
fontFamily: 'var(--font-mono)',
color: 'var(--text-lo)',
letterSpacing: '0.1em',
marginBottom: 8,
textTransform: 'uppercase',
}}>
Tonight Tonight
</div> </div>
<table style={{ width: '100%', borderCollapse: 'collapse' }}> <table style={{ width: '100%', borderCollapse: 'collapse' }}>
@@ -144,9 +136,7 @@ export default function Sidebar() {
<td style={{ color: 'var(--text-lo)', fontSize: 11, fontFamily: 'var(--font-mono)' }}>Moon</td> <td style={{ color: 'var(--text-lo)', fontSize: 11, fontFamily: 'var(--font-mono)' }}>Moon</td>
<td style={{ textAlign: 'right', display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: 4 }}> <td style={{ textAlign: 'right', display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: 4 }}>
<span style={{ color: 'var(--text-mid)', fontSize: 11, fontFamily: 'var(--font-mono)' }}> <span style={{ color: 'var(--text-mid)', fontSize: 11, fontFamily: 'var(--font-mono)' }}>
{tonight?.moon_illumination != null {tonight?.moon_illumination != null ? `${Math.round(tonight.moon_illumination * 100)}%` : '—'}
? `${Math.round(tonight.moon_illumination * 100)}%`
: '—'}
</span> </span>
{tonight?.moon_illumination != null && ( {tonight?.moon_illumination != null && (
<MoonPhaseIcon illumination={tonight.moon_illumination} size={14} /> <MoonPhaseIcon illumination={tonight.moon_illumination} size={14} />
@@ -158,18 +148,8 @@ export default function Sidebar() {
</div> </div>
{/* Conditions widget */} {/* Conditions widget */}
<div style={{ <div style={{ borderTop: '1px solid var(--border)', padding: '12px 16px' }}>
borderTop: '1px solid var(--border)', <div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', color: 'var(--text-lo)', letterSpacing: '0.1em', marginBottom: 8, textTransform: 'uppercase' }}>
padding: '12px 16px',
}}>
<div style={{
fontSize: 10,
fontFamily: 'var(--font-mono)',
color: 'var(--text-lo)',
letterSpacing: '0.1em',
marginBottom: 8,
textTransform: 'uppercase',
}}>
Conditions Conditions
</div> </div>
<div style={{ marginBottom: 6 }}> <div style={{ marginBottom: 6 }}>
@@ -186,12 +166,8 @@ export default function Sidebar() {
<tr key={label}> <tr key={label}>
<td style={{ color: 'var(--text-lo)', fontSize: 11, fontFamily: 'var(--font-mono)', paddingBottom: 3 }}>{label}</td> <td style={{ color: 'var(--text-lo)', fontSize: 11, fontFamily: 'var(--font-mono)', paddingBottom: 3 }}>{label}</td>
<td style={{ <td style={{
color: label === 'Dew Δ' && dewMargin && parseFloat(dewMargin) < 4 color: label === 'Dew Δ' && dewMargin && parseFloat(dewMargin) < 4 ? 'var(--danger)' : 'var(--text-mid)',
? 'var(--danger)' fontSize: 11, fontFamily: 'var(--font-mono)', textAlign: 'right',
: 'var(--text-mid)',
fontSize: 11,
fontFamily: 'var(--font-mono)',
textAlign: 'right',
}}>{value as string}</td> }}>{value as string}</td>
</tr> </tr>
))} ))}
@@ -230,7 +230,7 @@ export default function CompareModal({ targets, onClose }: Props) {
> >
<div style={{ <div style={{
background: 'var(--bg-panel)', border: '1px solid var(--border-hi)', background: 'var(--bg-panel)', border: '1px solid var(--border-hi)',
borderRadius: 8, width: '100%', maxWidth: 1100, maxHeight: '90vh', borderRadius: 8, width: '100%', maxWidth: 1100, maxHeight: '95vh',
display: 'flex', flexDirection: 'column', overflow: 'hidden', display: 'flex', flexDirection: 'column', overflow: 'hidden',
}}> }}>
{/* Header */} {/* Header */}
@@ -250,11 +250,11 @@ export default function CompareModal({ targets, onClose }: Props) {
</div> </div>
{/* Body */} {/* Body */}
<div style={{ display: 'flex', gap: 0, overflow: 'auto', flex: 1 }}> <div className="compare-body" style={{ display: 'flex', gap: 0, overflow: 'auto', flex: 1 }}>
<div style={{ flex: 1, padding: 20, overflow: 'auto', borderRight: '1px solid var(--border)' }}> <div className="compare-col" style={{ flex: 1, padding: 20, overflow: 'auto', borderRight: '1px solid var(--border)' }}>
<TargetColumn target={targets[0]} /> <TargetColumn target={targets[0]} />
</div> </div>
<div style={{ flex: 1, padding: 20, overflow: 'auto' }}> <div className="compare-col" style={{ flex: 1, padding: 20, overflow: 'auto' }}>
<TargetColumn target={targets[1]} /> <TargetColumn target={targets[1]} />
</div> </div>
</div> </div>
+4 -4
View File
@@ -214,7 +214,7 @@ export default function Calendar() {
const months = [today, new Date(today.getFullYear(), today.getMonth() + 1), new Date(today.getFullYear(), today.getMonth() + 2)]; const months = [today, new Date(today.getFullYear(), today.getMonth() + 1), new Date(today.getFullYear(), today.getMonth() + 2)];
return ( return (
<div> <div className="page-body">
<div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 20 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 20 }}>
<h1 style={{ fontFamily: 'var(--font-display)', fontSize: 22 }}>Calendar</h1> <h1 style={{ fontFamily: 'var(--font-display)', fontSize: 22 }}>Calendar</h1>
<button <button
@@ -237,7 +237,7 @@ export default function Calendar() {
{newMoonView ? ( {newMoonView ? (
<NewMoonTimeline days={calData12?.days ?? []} /> <NewMoonTimeline days={calData12?.days ?? []} />
) : ( ) : (
<div style={{ display: 'grid', gridTemplateColumns: selectedDate ? '2fr 1fr' : '1fr', gap: 20 }}> <div className="dash-two-col" style={{ display: 'grid', gridTemplateColumns: selectedDate ? '2fr 1fr' : '1fr', gap: 20 }}>
<div> <div>
{months.map(month => { {months.map(month => {
const monthStart = startOfMonth(month); const monthStart = startOfMonth(month);
@@ -246,11 +246,11 @@ export default function Calendar() {
const firstDow = monthStart.getDay(); const firstDow = monthStart.getDay();
return ( return (
<div key={month.toISOString()} style={{ marginBottom: 28 }}> <div key={month.toISOString()} style={{ marginBottom: 28, overflowX: 'auto' }}>
<div style={{ fontFamily: 'var(--font-display)', fontSize: 16, fontWeight: 700, marginBottom: 10, color: 'var(--text-hi)' }}> <div style={{ fontFamily: 'var(--font-display)', fontSize: 16, fontWeight: 700, marginBottom: 10, color: 'var(--text-hi)' }}>
{format(month, 'MMMM yyyy')} {format(month, 'MMMM yyyy')}
</div> </div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 3 }}> <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, minmax(44px, 1fr))', gap: 3, minWidth: 308 }}>
{['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'].map(d => ( {['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'].map(d => (
<div key={d} style={{ textAlign: 'center', fontFamily: 'var(--font-mono)', fontSize: 9, color: 'var(--text-lo)', paddingBottom: 4 }}> <div key={d} style={{ textAlign: 'center', fontFamily: 'var(--font-mono)', fontSize: 9, color: 'var(--text-lo)', paddingBottom: 4 }}>
{d} {d}
+5 -5
View File
@@ -314,7 +314,7 @@ function RunOrderCard({ items, dusk, dawn }: {
); );
return ( return (
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}> <div style={{ display: 'flex', flexDirection: 'column', gap: 6, overflowX: 'auto' }}>
{/* Slew optimizer toggle */} {/* Slew optimizer toggle */}
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 4 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 4 }}>
<button <button
@@ -420,7 +420,7 @@ export default function Dashboard() {
const slots = (forecast as { dataseries?: { cloudcover?: number; seeing?: number; timepoint?: number }[] })?.dataseries?.slice(0, 8) ?? []; const slots = (forecast as { dataseries?: { cloudcover?: number; seeing?: number; timepoint?: number }[] })?.dataseries?.slice(0, 8) ?? [];
return ( return (
<div style={{ padding: '24px 28px' }}> <div className="page-body" style={{ padding: '24px 28px' }}>
<div style={{ display: 'flex', alignItems: 'baseline', gap: 12, marginBottom: 20 }}> <div style={{ display: 'flex', alignItems: 'baseline', gap: 12, marginBottom: 20 }}>
<h1 style={{ fontFamily: 'var(--font-display)', fontSize: 20, fontWeight: 700, color: 'var(--text-hi)', letterSpacing: '0.04em' }}> <h1 style={{ fontFamily: 'var(--font-display)', fontSize: 20, fontWeight: 700, color: 'var(--text-hi)', letterSpacing: '0.04em' }}>
Dashboard Dashboard
@@ -465,7 +465,7 @@ export default function Dashboard() {
)} )}
{/* Stat cards row */} {/* Stat cards row */}
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 12, marginBottom: 20 }}> <div className="dash-stat-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 12, marginBottom: 20 }}>
{/* Go/No-go */} {/* Go/No-go */}
<div style={{ background: 'var(--bg-panel)', border: '1px solid var(--border)', borderRadius: 6, padding: '14px 16px' }}> <div style={{ background: 'var(--bg-panel)', border: '1px solid var(--border)', borderRadius: 6, padding: '14px 16px' }}>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--text-lo)', letterSpacing: '0.1em', textTransform: 'uppercase', marginBottom: 8 }}>Tonight</div> <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--text-lo)', letterSpacing: '0.1em', textTransform: 'uppercase', marginBottom: 8 }}>Tonight</div>
@@ -529,7 +529,7 @@ export default function Dashboard() {
)} )}
{/* Tonight timing + top targets + forecast */} {/* Tonight timing + top targets + forecast */}
<div style={{ display: 'grid', gridTemplateColumns: '200px 1fr 1fr', gap: 16, marginBottom: 20 }}> <div className="dash-three-col" style={{ display: 'grid', gridTemplateColumns: '200px 1fr 1fr', gap: 16, marginBottom: 20 }}>
{/* Tonight timing */} {/* Tonight timing */}
<div style={{ background: 'var(--bg-panel)', border: '1px solid var(--border)', borderRadius: 6, padding: '14px 16px' }}> <div style={{ background: 'var(--bg-panel)', border: '1px solid var(--border)', borderRadius: 6, padding: '14px 16px' }}>
@@ -648,7 +648,7 @@ export default function Dashboard() {
)} )}
{/* Monthly highlights + best nights */} {/* Monthly highlights + best nights */}
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginBottom: 20 }}> <div className="dash-two-col" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginBottom: 20 }}>
<div> <div>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--text-lo)', letterSpacing: '0.1em', textTransform: 'uppercase', marginBottom: 10 }}> <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--text-lo)', letterSpacing: '0.1em', textTransform: 'uppercase', marginBottom: 10 }}>
Monthly Highlights Monthly Highlights
+1 -1
View File
@@ -44,7 +44,7 @@ export default function Gallery() {
} }
return ( return (
<div style={{ padding: '24px 28px' }}> <div className="page-body" style={{ padding: '24px 28px' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 20, marginBottom: 24 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 20, marginBottom: 24 }}>
<h1 style={{ <h1 style={{
fontFamily: 'var(--font-display)', fontFamily: 'var(--font-display)',
+1 -1
View File
@@ -343,7 +343,7 @@ export default function Settings() {
}; };
return ( return (
<div> <div className="page-body">
<h1 style={{ fontFamily: 'var(--font-display)', fontSize: 22, marginBottom: 24 }}>Settings</h1> <h1 style={{ fontFamily: 'var(--font-display)', fontSize: 22, marginBottom: 24 }}>Settings</h1>
{/* Equipment Profiles */} {/* Equipment Profiles */}
+1 -1
View File
@@ -161,7 +161,7 @@ export default function SolarSystem() {
}; };
return ( return (
<div style={{ padding: '24px 28px' }}> <div className="page-body" style={{ padding: '24px 28px' }}>
<div style={{ display: 'flex', alignItems: 'baseline', gap: 12, marginBottom: 20 }}> <div style={{ display: 'flex', alignItems: 'baseline', gap: 12, marginBottom: 20 }}>
<h1 style={{ fontFamily: 'var(--font-display)', fontSize: 20, fontWeight: 700, color: 'var(--text-hi)', letterSpacing: '0.04em' }}> <h1 style={{ fontFamily: 'var(--font-display)', fontSize: 20, fontWeight: 700, color: 'var(--text-hi)', letterSpacing: '0.04em' }}>
Solar System Solar System
+1 -1
View File
@@ -292,7 +292,7 @@ export default function Stats() {
const totalH = (stats.total_integration_min / 60).toFixed(1); const totalH = (stats.total_integration_min / 60).toFixed(1);
return ( return (
<div> <div className="page-body">
<div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 20 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 20 }}>
<h1 style={{ fontFamily: 'var(--font-display)', fontSize: 22 }}>Statistics</h1> <h1 style={{ fontFamily: 'var(--font-display)', fontSize: 22 }}>Statistics</h1>
<button <button
+4 -4
View File
@@ -174,7 +174,7 @@ export default function Targets() {
}; };
return ( return (
<div> <div className="page-body">
<h1 style={{ fontFamily: 'var(--font-display)', fontSize: 22, marginBottom: 12 }}>Targets</h1> <h1 style={{ fontFamily: 'var(--font-display)', fontSize: 22, marginBottom: 12 }}>Targets</h1>
{/* Filter bar */} {/* Filter bar */}
@@ -188,7 +188,7 @@ export default function Targets() {
marginBottom: 10, marginBottom: 10,
}}> }}>
{/* Row 1: object types (multi-select) */} {/* Row 1: object types (multi-select) */}
<div style={{ display: 'flex', gap: 4, flexWrap: 'wrap', marginBottom: 6, alignItems: 'center' }}> <div className="filter-row" style={{ display: 'flex', gap: 4, flexWrap: 'wrap', marginBottom: 6, alignItems: 'center' }}>
<Chip <Chip
active={typeFilters.length === 0} active={typeFilters.length === 0}
color="var(--amber)" color="var(--amber)"
@@ -214,7 +214,7 @@ export default function Targets() {
</div> </div>
{/* Row 2: filters + sort + status + search */} {/* Row 2: filters + sort + status + search */}
<div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', alignItems: 'center' }}> <div className="filter-row" style={{ display: 'flex', gap: 6, flexWrap: 'wrap', alignItems: 'center' }}>
{/* Filter fit */} {/* Filter fit */}
<div style={{ display: 'flex', gap: 4, alignItems: 'center' }}> <div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
<span style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--text-lo)', marginRight: 2 }}>FILTER</span> <span style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--text-lo)', marginRight: 2 }}>FILTER</span>
@@ -352,7 +352,7 @@ export default function Targets() {
{/* Table */} {/* Table */}
{data && ( {data && (
<table style={{ width: '100%', borderCollapse: 'collapse' }}> <table className="targets-table" style={{ width: '100%', borderCollapse: 'collapse' }}>
<thead> <thead>
<tr style={{ borderBottom: '1px solid var(--border-hi)' }}> <tr style={{ borderBottom: '1px solid var(--border-hi)' }}>
{[...COL_HEADERS, ''].map((h, i) => ( {[...COL_HEADERS, ''].map((h, i) => (
+81 -1
View File
@@ -18,7 +18,6 @@ body {
font-size: 14px; font-size: 14px;
line-height: 1.5; line-height: 1.5;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
min-width: 1280px;
} }
h1, h2, h3, h4 { h1, h2, h3, h4 {
@@ -64,6 +63,87 @@ input:focus, select:focus, textarea:focus {
::-webkit-scrollbar-thumb { background: var(--muted); border-radius: 3px; } ::-webkit-scrollbar-thumb { background: var(--muted); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--text-lo); } ::-webkit-scrollbar-thumb:hover { background: var(--text-lo); }
/* ─── Bottom navigation (mobile only, hidden on desktop) ───── */
.bottom-nav {
display: none;
position: fixed;
bottom: 0; left: 0; right: 0;
height: 58px;
background: var(--bg-deep);
border-top: 1px solid var(--border);
z-index: 200;
align-items: stretch;
}
.bottom-nav a {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 3px;
color: var(--text-lo);
font-family: var(--font-mono);
font-size: 8px;
letter-spacing: 0.06em;
text-transform: uppercase;
text-decoration: none;
padding: 8px 0;
border-top: 2px solid transparent;
transition: color 0.1s;
}
.bottom-nav a.active {
color: var(--amber);
border-top-color: var(--amber);
}
.bottom-nav a .bnav-icon {
font-size: 18px;
line-height: 1;
}
/* ─── Responsive ────────────────────────────────────────────── */
@media (max-width: 767px) {
body { min-width: 0; }
/* Sidebar hidden; bottom nav shown */
.app-sidebar { display: none !important; }
.bottom-nav { display: flex !important; }
/* Main content: zero out PageShell padding; bottom-nav clearance */
.app-main { padding: 0 0 68px 0 !important; }
/* Page body: reduced padding on mobile */
.page-body { padding: 12px 14px !important; }
/* Dashboard: 4-col stat row → 2×2 */
.dash-stat-grid { grid-template-columns: 1fr 1fr !important; }
/* Dashboard: 2-col grids → single column */
.dash-two-col { grid-template-columns: 1fr !important; }
/* Dashboard: 3-col timing/targets/forecast → single column */
.dash-three-col { grid-template-columns: 1fr !important; }
/* Targets filter bar rows: horizontal scroll */
.filter-row {
flex-wrap: nowrap !important;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
padding-bottom: 4px;
}
.filter-row::-webkit-scrollbar { display: none; }
/* Targets table: show only Type(1) Name(2) Filter(8) Alt(9) */
/* Hide cols 3-7 */
.targets-table th:nth-child(n+3):nth-child(-n+7),
.targets-table td:nth-child(n+3):nth-child(-n+7) { display: none !important; }
/* Hide cols 10 and beyond */
.targets-table th:nth-child(n+10),
.targets-table td:nth-child(n+10) { display: none !important; }
/* Compare modal: stack columns */
.compare-body { flex-direction: column !important; }
.compare-col { border-right: none !important; border-bottom: 1px solid var(--border) !important; }
}
/* Utility classes */ /* Utility classes */
.text-hi { color: var(--text-hi); } .text-hi { color: var(--text-hi); }
.text-mid { color: var(--text-mid); } .text-mid { color: var(--text-mid); }