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:
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,29 +15,43 @@ const TRANSP_LABELS: Record<number, string> = {
|
|||||||
|
|
||||||
const navItems = [
|
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>
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)',
|
||||||
|
|||||||
@@ -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 */}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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) => (
|
||||||
|
|||||||
@@ -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); }
|
||||||
|
|||||||
Reference in New Issue
Block a user