diff --git a/src/static/dashboard.html b/src/static/dashboard.html index 45551c3..7e21cac 100644 --- a/src/static/dashboard.html +++ b/src/static/dashboard.html @@ -36,19 +36,38 @@ } * { box-sizing: border-box; } html, body { margin: 0; min-height: 100%; } + /* Nothing may push the page sideways on a phone: wide content scrolls + inside its own container instead (the map and chart already do). */ + html { overflow-x: hidden; } body { color: var(--ink); + overflow-x: hidden; background: radial-gradient(circle at 8% 0%, rgba(56, 180, 213, .12), transparent 25rem), var(--paper); - font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + /* Thai families first so Thai text uses a proper Thai face where one + is installed (Android/iOS/Windows all ship one); Latin falls + through to Inter. No webfont download — this page is served over a + bandwidth-constrained uplink. */ + /* Thai faces sit AFTER the Latin stack: a Thai family listed ahead + of system-ui would also claim the Latin glyphs and restyle the + whole English page. Latin resolves first, Thai falls through. */ + font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, + "Segoe UI", "Noto Sans Thai", "Sarabun", "Leelawadee UI", "Thonburi", sans-serif; } + /* Thai has no inter-word spaces: let the browser segment, and allow a + break anywhere rather than letting a long phrase force a scrollbar. */ + html[lang="th"] body { line-break: normal; overflow-wrap: anywhere; } + html[lang="th"] .stat-value, html[lang="th"] .flow-value { overflow-wrap: normal; } + .lang-toggle { padding: 9px 12px; font-size: .78rem; font-weight: 800; white-space: nowrap; } + .lang-toggle[data-active-lang="th"] { background: var(--mint); border-color: #b6dfc9; color: #146644; } .shell { max-width: 1500px; margin: 0 auto; padding: 24px; } header { display: flex; align-items: center; justify-content: space-between; gap: 20px; - margin-bottom: 20px; + margin-bottom: 20px; flex-wrap: wrap; } - .brand { display: flex; align-items: center; gap: 14px; } + .brand { display: flex; align-items: center; gap: 14px; min-width: 0; } + .brand > div { min-width: 0; } .brand-mark { width: 48px; height: 48px; border-radius: 15px; display: grid; place-items: center; color: white; font-size: 25px; background: linear-gradient(145deg, #0a91b9, #076787); @@ -56,7 +75,7 @@ } h1 { margin: 0; font-size: clamp(1.4rem, 2.5vw, 2rem); letter-spacing: -.035em; } .subtitle { margin: 4px 0 0; color: var(--muted); font-size: .92rem; } - .header-actions { display: flex; align-items: center; gap: 12px; } + .header-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; } .live-pill { display: flex; gap: 8px; align-items: center; padding: 9px 13px; border-radius: 999px; background: var(--mint); color: #146644; font-weight: 750; font-size: .8rem; @@ -193,22 +212,52 @@ /* No fixed cap: the sensors expander must stay reachable below the RID list */ .side-card { max-height: 70vh; } #river-flow { min-height: 160px; } + /* Landscape phones are ~400 px tall: a fixed 640 px map filled more + than the screen and buried everything under it. */ + #station-map { height: min(640px, 78vh); min-height: 320px; } } @media (max-width: 560px) { .shell { padding: 14px; } - header { align-items: flex-start; } + /* Portrait phones: brand on its own row, controls spread on the next. + Side by side the nowrap buttons overflowed the viewport (~64 px at + 412 px wide) and squeezed the title into three lines. */ + header { align-items: stretch; flex-direction: column; gap: 12px; margin-bottom: 16px; } + .brand-mark { width: 40px; height: 40px; border-radius: 13px; font-size: 21px; } + /* Two deliberate rows instead of a ragged wrap: the compact controls + share the first, the long replay label owns the second. The DOM + is already in this order, so focus order matches what is seen. */ + .header-actions { width: 100%; justify-content: flex-start; gap: 8px; } + #refresh-button { flex: 1 1 auto; text-align: center; } + #replay-2024 { flex: 1 1 100%; text-align: center; } .subtitle { display: none; } .live-pill { padding: 8px; } #live-pill-text { display: none; } /* dot stays: live vs replay/simulation signal */ #replay-2024 { white-space: nowrap; padding: 9px 11px; font-size: .72rem; } .stats { gap: 8px; } .stat { min-height: 96px; padding: 14px; } - .stat-value { font-size: 1.25rem; } - #station-map { height: 540px; } + .stat-value { font-size: 1.25rem; white-space: normal; } + #station-map { height: 62vh; min-height: 320px; max-height: 560px; } + } + /* Landscape phones are 380-430 px TALL but 850-960 px wide, so the width + breakpoints above never catch them and a 640 px map filled the screen. + Gate this one on height. */ + @media (max-height: 520px) and (orientation: landscape) { + #station-map { height: 80vh; min-height: 0; max-height: none; } + .workspace { min-height: 0; } + .side-card { max-height: 80vh; } .map-overlay { left: 46px; } + /* The description box covered a quarter of the map on a phone */ + .map-heading { display: none; } #legend-toggle { display: inline-flex; } .legend { display: none; } .legend.open { display: block; max-height: 42vh; overflow-y: auto; } + /* Range controls: dropdown on one row, the two dates sharing the next */ + .history-controls { width: 100%; } + .history-controls select { flex: 1 1 100%; } + .history-controls input[type="date"] { flex: 1 1 0; min-width: 0; } + .forecast-grid { grid-template-columns: 1fr; } + .stage-chip { flex: 1 1 auto; min-width: 66px; } + footer { flex-direction: column; align-items: flex-start; gap: 10px; } } @@ -218,14 +267,15 @@
Current water level and discharge across Northern Thailand
+Current water level and discharge across Northern Thailand