diff --git a/app/public/index.html b/app/public/index.html index 46ea61f..33c49e2 100644 --- a/app/public/index.html +++ b/app/public/index.html @@ -13,7 +13,7 @@ - + @@ -22,13 +22,6 @@

Weather

- Open-Meteo ยท RainViewer
@@ -275,6 +268,29 @@ + + diff --git a/app/public/style.css b/app/public/style.css index cc0a2b7..9733e8f 100644 --- a/app/public/style.css +++ b/app/public/style.css @@ -1,1000 +1,936 @@ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } :root { - --ink: #1a1a1a; - --ink-mid: #555; - --ink-light: #999; - --paper: #f5f2ec; - --paper-dark: #ece7db; - --paper-darker: #ddd6c7; - --border: #c8bfaf; - --accent: #c25a2a; - --cool: #2a6b8a; - --mono: 'DM Mono', monospace; - --serif: 'Playfair Display', Georgia, serif; + --ink: #102539; + --ink-soft: #476a82; + --ink-muted: #6f90a4; + --paper: #e7f2f8; + --paper-deep: #d6e7f1; + --surface: rgba(255, 255, 255, 0.9); + --surface-soft: rgba(255, 255, 255, 0.74); + --line: rgba(96, 138, 166, 0.35); + --brand: #f0722f; + --brand-strong: #d75f1f; + --info: #1d87bf; + --ok: #1f9c79; + --danger: #c84b2f; + --radius-xl: 22px; + --radius-lg: 16px; + --radius-md: 12px; + --shadow-lg: 0 14px 36px rgba(11, 35, 52, 0.16); + --shadow-md: 0 8px 22px rgba(11, 35, 52, 0.12); + --ui: 'Manrope', 'Avenir Next', 'Segoe UI', sans-serif; + --display: 'Playfair Display', Georgia, serif; } @media (prefers-color-scheme: dark) { :root { - --ink: #ece7dc; - --ink-mid: #a09880; - --ink-light: #6a6258; - --paper: #191510; - --paper-dark: #201c16; - --paper-darker: #2b2620; - --border: #3a342a; - --accent: #e07848; - --cool: #5ab0d8; + --ink: #ecf6fd; + --ink-soft: #bed1dd; + --ink-muted: #92aebf; + --paper: #0b1721; + --paper-deep: #122433; + --surface: rgba(26, 46, 61, 0.88); + --surface-soft: rgba(28, 49, 65, 0.7); + --line: rgba(118, 152, 176, 0.34); + --brand: #ff8d4d; + --brand-strong: #ef7330; + --info: #63c2ef; + --ok: #38c89f; + --danger: #f77257; + --shadow-lg: 0 16px 40px rgba(0, 0, 0, 0.45); + --shadow-md: 0 9px 22px rgba(0, 0, 0, 0.35); } } html, body { min-height: 100%; - background: var(--paper); + font-family: var(--ui); + font-size: 18px; + line-height: 1.45; color: var(--ink); - font-family: var(--mono); - font-size: 14px; - line-height: 1.6; + background: var(--paper); overflow-x: hidden; } -.app { - max-width: 900px; - margin: 0 auto; - padding: 2rem 1.5rem 4rem; +body { + background: + radial-gradient(circle at 8% -6%, rgba(255, 255, 255, 0.86), transparent 46%), + radial-gradient(circle at 100% 0%, rgba(111, 184, 222, 0.25), transparent 35%), + linear-gradient(180deg, var(--paper) 0%, var(--paper-deep) 100%); } -a { color: var(--cool); text-decoration: none; } +a { color: var(--info); text-decoration: none; } a:hover { text-decoration: underline; } +.hidden { display: none !important; } -/* โ”€โ”€ Masthead โ”€โ”€ */ +.app { + width: 100%; + max-width: 100%; + padding: calc(0.7rem + env(safe-area-inset-top, 0px)) 0.75rem calc(6.9rem + env(safe-area-inset-bottom, 0px)); +} + +/* Header */ .masthead { - display: grid; - grid-template-columns: auto 1fr auto; - align-items: center; - gap: 1.5rem; - border-bottom: 1.5px solid var(--ink); - padding-bottom: 0.6rem; - margin-bottom: 1.5rem; + position: sticky; + top: env(safe-area-inset-top, 0px); + z-index: 220; + padding: 0.55rem 0.55rem 0.45rem; + border: 1px solid var(--line); + border-radius: var(--radius-xl); + background: linear-gradient(180deg, var(--surface) 0%, var(--surface-soft) 100%); + backdrop-filter: blur(9px); + box-shadow: var(--shadow-md); + margin-bottom: 0.6rem; } .masthead h1 { - font-family: var(--serif); - font-size: 2.2rem; - font-weight: 400; - letter-spacing: -0.5px; -} - -.tagline { - font-size: 0.62rem; - letter-spacing: 0.12em; - color: var(--ink-light); - text-transform: uppercase; - white-space: nowrap; -} - -/* โ”€โ”€ Tabs โ”€โ”€ */ -.tabs { - display: flex; - gap: 0; - border: 1px solid var(--border); -} - -.tab { - background: transparent; - border: none; - border-right: 1px solid var(--border); - padding: 0.4rem 1rem; - font-family: var(--mono); - font-size: 0.72rem; - letter-spacing: 0.08em; - text-transform: uppercase; - color: var(--ink-mid); - cursor: pointer; - transition: background 0.15s, color 0.15s; -} - -.tab:last-child { border-right: none; } -.tab:hover { background: var(--paper-dark); color: var(--ink); } -.tab.active { background: var(--ink); color: var(--paper); } - -/* โ”€โ”€ Search โ”€โ”€ */ -.search-wrap { - position: relative; - margin-bottom: 2rem; -} - -.search-row { display: flex; } - -.search-row input { - flex: 1; - background: var(--paper-dark); - border: 1px solid var(--border); - border-right: none; - padding: 0.6rem 1rem; - font-family: var(--mono); - font-size: 0.85rem; - color: var(--ink); - outline: none; - border-radius: 0; - transition: background 0.15s; -} - -.search-row input::placeholder { color: var(--ink-light); } -.search-row input:focus { background: var(--paper-darker); } - -.search-row button { - background: var(--ink); - color: var(--paper); - border: 1px solid var(--ink); - padding: 0.6rem 1.1rem; - font-family: var(--mono); - font-size: 0.75rem; - letter-spacing: 0.08em; - text-transform: uppercase; - cursor: pointer; - border-radius: 0; - transition: opacity 0.15s; - white-space: nowrap; -} - -.search-row button:hover { opacity: 0.72; } - -#locate-btn { - background: var(--paper-dark) !important; - color: var(--ink) !important; - border-left: none !important; - font-size: 1.2rem !important; - letter-spacing: 0 !important; - padding: 0.6rem 0.9rem !important; -} - -#units-btn { - background: var(--paper-dark) !important; - color: var(--ink) !important; - border-left: none !important; - min-width: 76px; -} - -.saved-wrap { margin-top: 0.6rem; } -.saved-head { - display: flex; - justify-content: space-between; - align-items: center; - font-size: 0.62rem; - letter-spacing: 0.08em; - text-transform: uppercase; - color: var(--ink-light); + font-family: var(--display); + font-size: 1.7rem; + letter-spacing: -0.3px; margin-bottom: 0.35rem; } -.saved-locations { +.tagline { + display: block; + margin-top: 0.35rem; + font-size: 0.76rem; + color: var(--ink-muted); + letter-spacing: 0.04em; +} + +/* Bottom nav */ +.tabs { + position: fixed; + left: 0; + right: 0; + bottom: 0; + width: 100vw; + max-width: 100vw; + z-index: 300; display: grid; - gap: 0.35rem; - font-size: 0.72rem; - color: var(--ink-mid); + grid-template-columns: repeat(5, minmax(0, 1fr)); + gap: 0.22rem; + padding: 0.45rem 0.28rem calc(0.45rem + env(safe-area-inset-bottom, 0px)); + background: rgba(9, 28, 40, 0.92); + backdrop-filter: blur(12px); + border-top: 1px solid rgba(121, 165, 191, 0.36); +} + +@supports (bottom: env(safe-area-inset-bottom)) { + .tabs { + bottom: 0; + padding-bottom: calc(0.45rem + env(safe-area-inset-bottom)); + } +} + +.tab { + border: none; + border-radius: 12px; + min-width: 0; + min-height: 58px; + padding: 0.32rem 0.15rem; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0.24rem; + background: rgba(255, 255, 255, 0.04); + color: #d8eaf4; + cursor: pointer; + font-family: var(--ui); +} + +.tab-icon { + font-family: 'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji', sans-serif; + font-size: 1.18rem; + line-height: 1; +} + +.tab-text { + font-size: 0.67rem; + line-height: 1; + letter-spacing: 0.02em; + font-weight: 600; +} + +.tab.active { + color: #fff; + background: linear-gradient(180deg, var(--brand) 0%, var(--brand-strong) 100%); +} + +/* Search */ +.search-wrap { + margin-bottom: 0.65rem; +} + +.search-row { + display: grid; + grid-template-columns: 1fr 64px 80px; + gap: 0.45rem; +} + +button, input, select { font-family: var(--ui); } + +.search-row input, +.search-row button, +.maps-control select, +.settings-btn { + font-size: 16px; /* prevent iOS focus zoom */ +} + +.search-row input { + grid-column: 1 / -1; + min-height: 54px; + border-radius: var(--radius-lg); + border: 1px solid var(--line); + background: var(--surface); + color: var(--ink); + padding: 0 0.95rem; + box-shadow: var(--shadow-md); +} + +.search-row button { + min-height: 50px; + border-radius: 14px; + border: 1px solid var(--line); + cursor: pointer; + box-shadow: var(--shadow-md); +} + +#search-btn { + grid-column: 1 / -1; + background: linear-gradient(180deg, #15445f 0%, #11344c 100%); + color: #f2f8fc; + text-transform: uppercase; + letter-spacing: 0.05em; + font-weight: 700; +} + +#locate-btn, +#units-btn { + background: var(--surface) !important; + color: var(--ink) !important; +} + +#locate-btn { + font-size: 1.25rem !important; +} + +.suggestions { + list-style: none; + margin-top: 0.3rem; + border-radius: var(--radius-lg); + overflow: hidden; + border: 1px solid var(--line); + background: var(--surface); + box-shadow: var(--shadow-lg); +} + +.suggestions li { + padding: 0.72rem 0.8rem; + border-bottom: 1px solid var(--line); + font-size: 0.88rem; + cursor: pointer; +} + +.suggestions li:last-child { border-bottom: none; } +.suggestions li:hover { background: rgba(29, 135, 191, 0.09); } +.sug-country { color: var(--ink-muted); font-size: 0.8rem; margin-left: 0.35rem; } + +.error { + margin-top: 0.35rem; + color: var(--danger); + font-size: 0.84rem; +} + +/* Generic cards */ +.tab-panel { + width: 100%; + min-height: calc(100dvh - 11.6rem); +} + +.loc-header, +.radar-embed-wrap, +.radar-full-wrap, +.maps-wrap, +.alerts-wrap, +.panel-note, +.settings-wrap, +.daily-list, +.hourly-scroll, +.stats-bar, +.astro-grid, +.aq-grid { + border: 1px solid var(--line); + border-radius: var(--radius-xl); + background: var(--surface-soft); + box-shadow: var(--shadow-md); +} + +.loading { + display: flex; + align-items: center; + gap: 0.65rem; + color: var(--ink-soft); + font-size: 0.88rem; + padding: 1rem 0.35rem; +} + +.loading-dot { + width: 11px; + height: 11px; + border-radius: 50%; + background: var(--brand); + animation: pulse 1.1s ease-in-out infinite; +} + +@keyframes pulse { + 0%, 100% { opacity: 0.4; transform: scale(0.9); } + 50% { opacity: 1; transform: scale(1.08); } +} + +.loc-header { + padding: 0.9rem; + margin-bottom: 0.75rem; + display: flex; + flex-direction: column; + gap: 0.4rem; +} + +.loc-name { + font-family: var(--display); + font-size: 1.5rem; + line-height: 1.1; +} + +.loc-sub, +.loc-time { + font-size: 0.8rem; + color: var(--ink-muted); +} + +.loc-actions { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 0.32rem; +} + +.link-btn { + border: none; + background: none; + color: var(--info); + cursor: pointer; + font-size: 0.84rem; +} + +.section-head { + display: flex; + flex-direction: column; + gap: 0.22rem; + margin: 0.35rem 0.45rem 0.5rem; +} + +.section-label { + font-size: 0.74rem; + color: var(--ink-soft); + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 700; +} + +.section-note { + color: var(--ink-muted); + font-size: 0.79rem; +} + +.section-actions { + display: inline-flex; + flex-wrap: wrap; + gap: 0.62rem; +} + +/* Current */ +.current-grid { + display: grid; + gap: 0.6rem; + margin-bottom: 0.75rem; +} + +.current-grid > * { + border-radius: var(--radius-lg); + border: 1px solid var(--line); + background: var(--surface); + padding: 1rem 0.85rem; + box-shadow: var(--shadow-md); +} + +.temp-big { + font-family: var(--display); + font-size: 3.75rem; + line-height: 0.92; + letter-spacing: -1.5px; +} + +.deg { + font-size: 1.35rem; + color: var(--ink-soft); + vertical-align: super; +} + +.feels, +.hi-lo, +.cond-sub { + font-size: 0.84rem; + color: var(--ink-soft); +} + +.current-cond { display: flex; flex-direction: column; justify-content: center; } +.cond-icon { font-size: 2.2rem; margin-bottom: 0.15rem; } +.cond-desc { font-size: 1.08rem; font-weight: 700; } + +/* Stat blocks */ +.stats-bar, +.astro-grid, +.aq-grid { + display: grid; + gap: 0.52rem; + border: none; + background: transparent; + box-shadow: none; + margin-bottom: 0.78rem; +} + +.stats-bar { grid-template-columns: repeat(2, minmax(0, 1fr)); } + +.stat, +.astro-card, +.aq-card, +.maps-card, +.settings-row, +.alert-item { + border: 1px solid var(--line); + border-radius: var(--radius-md); + background: var(--surface); + box-shadow: var(--shadow-md); +} + +.stat { padding: 0.78rem 0.58rem; text-align: center; } + +.stat-label { + font-size: 0.64rem; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--ink-muted); + margin-bottom: 0.2rem; + font-weight: 700; +} + +.stat-val { font-size: 1.02rem; font-weight: 700; } +.unit { font-size: 0.72rem; color: var(--ink-soft); } + +.astro-card, +.aq-card, +.maps-card { padding: 0.74rem 0.7rem; } +.astro-val, +.aq-val, +.maps-value { font-size: 0.98rem; font-weight: 700; } + +/* Radar & maps */ +.radar-embed-wrap, +.radar-full-wrap, +.maps-wrap, +.alerts-wrap, +.settings-wrap { + padding: 0.7rem; + margin-bottom: 0.85rem; +} + +.radar-map, +.map-overview { + width: 100%; + height: 235px; + border-radius: var(--radius-md); + border: 1px solid var(--line); + overflow: hidden; +} + +.radar-map-full { + height: calc(100dvh - 20rem); + min-height: 310px; +} + +.radar-controls { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.45rem; + margin-top: 0.62rem; +} + +.radar-btn { + width: 46px; + height: 46px; + border: none; + border-radius: 12px; + background: #0f3248; + color: #f5fbfe; + cursor: pointer; + font-size: 0.86rem; +} + +.layer-btn { background: var(--brand); } + +.radar-timeline { + order: 3; + flex-basis: 100%; + height: 32px; + display: flex; + gap: 2px; +} + +.tl-tick { + flex: 1; + border-radius: 7px; + border: 1px solid var(--line); + background: rgba(255, 255, 255, 0.52); + cursor: pointer; +} + +.tl-tick.active { background: var(--info); border-color: var(--info); } +.tl-tick.forecast { opacity: 0.6; } + +.radar-ts { font-size: 0.75rem; color: var(--ink-soft); } +.radar-legend { display: inline-flex; align-items: center; gap: 0.35rem; } +.legend-label { font-size: 0.64rem; color: var(--ink-muted); } +.legend-bar { + width: 84px; + height: 10px; + border-radius: 999px; + border: 1px solid var(--line); + background: linear-gradient(to right, #00d8ff, #00a0e0, #0070d0, #00c800, #ffff00, #ff6000, #ff0000); +} + +.radar-attribution, +.footer-note { + font-size: 0.74rem; + color: var(--ink-muted); + margin-bottom: 0.8rem; +} + +/* Hourly */ +.hourly-summary { + margin: 0.22rem 0.38rem 0.45rem; + font-size: 0.8rem; + color: var(--ink-soft); +} + +.hourly-scroll { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + margin-bottom: 0.85rem; + padding: 0.42rem; +} + +.hourly-track { display: flex; min-width: max-content; } + +.hour-cell { + min-width: 90px; + padding: 0.72rem 0.45rem; + text-align: center; + border-radius: 12px; + border: 1px solid var(--line); + background: var(--surface); + margin-right: 0.3rem; +} + +.hour-cell.now { + background: linear-gradient(180deg, rgba(29, 135, 191, 0.18) 0%, var(--surface) 100%); +} + +.hour-time { font-size: 0.7rem; color: var(--ink-muted); margin-bottom: 0.28rem; } +.hour-icon { font-size: 1.35rem; margin-bottom: 0.18rem; } +.hour-temp { font-size: 1rem; font-weight: 700; } +.hour-feels, +.hour-precip { font-size: 0.68rem; color: var(--ink-soft); } + +/* Daily */ +.daily-list { + padding: 0.4rem; + margin-bottom: 0.9rem; +} + +.day-row { + display: grid; + grid-template-columns: 1fr auto; + grid-template-areas: + 'name temps' + 'desc desc'; + gap: 0.28rem 0.55rem; + border-radius: 12px; + border: 1px solid var(--line); + background: var(--surface); + padding: 0.72rem; + margin-bottom: 0.34rem; +} + +.day-row:last-child { margin-bottom: 0; } +.day-row.today { outline: 1px solid rgba(29, 135, 191, 0.45); } +.day-name { grid-area: name; font-size: 0.9rem; font-weight: 700; } +.day-icon { display: none; } +.day-desc { + grid-area: desc; + font-size: 0.78rem; + color: var(--ink-soft); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.day-temps { grid-area: temps; font-size: 0.88rem; font-weight: 700; } +.day-lo { color: var(--ink-muted); } +.day-meta { display: none; } + +/* Lists and settings */ +.panel-note { + padding: 0.75rem; + font-size: 0.82rem; + color: var(--ink-soft); +} + +.alerts-list, +.saved-locations, +.maps-controls, +.maps-meta { + display: grid; + gap: 0.5rem; +} + +.alert-item { + padding: 0.72rem; + display: grid; + gap: 0.22rem; + font-size: 0.82rem; +} + +.maps-control { + display: grid; + gap: 0.3rem; + font-size: 0.68rem; + color: var(--ink-muted); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.maps-control input, +.maps-control select, +.settings-btn { + min-height: 44px; + border: 1px solid var(--line); + border-radius: 12px; + background: var(--surface); + color: var(--ink); + padding: 0.35rem 0.55rem; +} + +.wind-legend { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 0.42rem; + margin-top: 0.52rem; + font-size: 0.71rem; + color: var(--ink-muted); +} + +.wind-legend-scale { + width: 126px; + height: 10px; + border-radius: 999px; + border: 1px solid var(--line); + background: linear-gradient(to right, #d2f0ff, #84ccff, #3f9fff, #1879df); +} + +.saved-wrap { margin-top: 0.1rem; } + +.saved-head { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 0.3rem; + margin-bottom: 0.32rem; + color: var(--ink-muted); + text-transform: uppercase; + letter-spacing: 0.07em; + font-size: 0.67rem; } .saved-help { - font-size: 0.62rem; - color: var(--ink-light); - margin-bottom: 0.45rem; -} - -.auto-detect-toggle { - display: inline-flex; - align-items: center; - gap: 0.45rem; - margin-bottom: 0.45rem; - font-size: 0.68rem; - color: var(--ink-mid); - cursor: pointer; - user-select: none; -} - -.auto-detect-toggle input { - accent-color: var(--accent); -} - -.toggle-label { - display: inline-flex; - align-items: center; - gap: 0.45rem; - margin-bottom: 0.45rem; - font-size: 0.68rem; - color: var(--ink-mid); - cursor: pointer; - user-select: none; -} - -.toggle-label input { - accent-color: var(--accent); -} - -.toggle-label span { - min-width: 24px; + margin-bottom: 0.38rem; + font-size: 0.74rem; + color: var(--ink-muted); } .saved-item { display: grid; - grid-template-columns: 28px 1fr 28px 28px 28px auto; - align-items: center; - border: 1px solid var(--border); - background: var(--paper-dark); + grid-template-columns: 36px 1fr 36px 36px 36px; + border: 1px solid var(--line); + border-radius: 12px; + background: var(--surface); + overflow: hidden; } .saved-chip, .saved-remove, .saved-default, .saved-move { + min-height: 44px; border: none; background: transparent; color: var(--ink); - padding: 0.35rem 0.45rem; - font-family: var(--mono); cursor: pointer; } .saved-chip { + padding: 0 0.58rem; text-align: left; - border-left: 1px solid var(--border); - border-right: 1px solid var(--border); + border-left: 1px solid var(--line); + border-right: 1px solid var(--line); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } -.saved-default { color: var(--accent); } - -.saved-move { border-right: 1px solid var(--border); } +.saved-default { color: var(--brand); } +.saved-move { border-right: 1px solid var(--line); } +.saved-remove { border-right: 1px solid var(--line); color: var(--ink-muted); } .saved-move:disabled { opacity: 0.35; cursor: not-allowed; } +.saved-meta { display: none; } -.saved-remove { - border-right: 1px solid var(--border); - color: var(--ink-light); -} - -.saved-meta { - padding: 0 0.55rem; - font-size: 0.6rem; - color: var(--ink-light); -} - -/* Suggestions */ -.suggestions { - position: absolute; - top: 100%; - left: 0; right: 0; - background: var(--paper-darker); - border: 1px solid var(--border); - border-top: none; - z-index: 200; - list-style: none; -} - -.suggestions li { - padding: 0.6rem 1rem; - cursor: pointer; - font-size: 0.82rem; - border-bottom: 0.5px solid var(--border); - transition: background 0.1s; -} - -.suggestions li:last-child { border-bottom: none; } -.suggestions li:hover { background: var(--paper-dark); } -.sug-country { color: var(--ink-light); margin-left: 0.5rem; font-size: 0.75rem; } - -/* โ”€โ”€ Error / Loading โ”€โ”€ */ -.error { font-size: 0.78rem; color: var(--accent); margin-top: 0.5rem; } - -.loading { - display: flex; - align-items: center; - gap: 0.75rem; - padding: 3rem 0; - font-size: 0.78rem; - color: var(--ink-light); - letter-spacing: 0.08em; -} - -.loading-dot { - width: 6px; height: 6px; - border-radius: 50%; - background: var(--ink-light); - animation: pulse 1.2s ease-in-out infinite; -} - -@keyframes pulse { 0%,100%{opacity:.2} 50%{opacity:1} } - -.hidden { display: none !important; } - -/* โ”€โ”€ Tab panels โ”€โ”€ */ -.tab-panel { width: 100%; } - -/* โ”€โ”€ Location header โ”€โ”€ */ -.loc-header { - display: flex; - justify-content: space-between; - align-items: flex-end; - margin-bottom: 1.5rem; - padding-bottom: 0.6rem; - border-bottom: 0.5px solid var(--border); -} - -.loc-name { font-family: var(--serif); font-size: 1.6rem; font-weight: 400; line-height: 1.1; } -.loc-sub { font-size: 0.68rem; color: var(--ink-light); letter-spacing: 0.06em; margin-top: 0.2rem; text-transform: uppercase; } -.loc-time { font-size: 0.68rem; color: var(--ink-light); text-align: right; letter-spacing: 0.04em; line-height: 1.5; } -.loc-actions { display: flex; flex-direction: column; align-items: flex-end; gap: 0.35rem; } - -/* โ”€โ”€ Current hero โ”€โ”€ */ -.current-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 1px; - background: var(--border); - border: 1px solid var(--border); - margin-bottom: 1px; -} - -.current-grid > * { background: var(--paper); padding: 1.75rem 1.5rem; } - -.temp-big { - font-family: var(--serif); - font-size: 5.5rem; - font-weight: 400; - line-height: 1; - letter-spacing: -3px; -} - -.deg { font-family: var(--mono); font-size: 1.6rem; color: var(--ink-mid); vertical-align: super; letter-spacing: 0; } -.feels { font-size: 0.72rem; color: var(--ink-mid); margin-top: 0.5rem; } -.hi-lo { font-size: 0.72rem; color: var(--ink-mid); margin-top: 0.2rem; } - -.current-cond { display: flex; flex-direction: column; justify-content: center; } -.cond-icon { font-size: 2.8rem; margin-bottom: 0.4rem; line-height: 1; } -.cond-desc { font-size: 1rem; font-weight: 500; } -.cond-sub { font-size: 0.72rem; color: var(--ink-mid); margin-top: 0.3rem; } - -/* โ”€โ”€ Stats bar โ”€โ”€ */ -.stats-bar { - display: grid; - grid-template-columns: repeat(6, 1fr); - gap: 1px; - background: var(--border); - border: 1px solid var(--border); - border-top: none; - margin-bottom: 2rem; -} - -.astro-grid, -.aq-grid { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 1px; - background: var(--border); - border: 1px solid var(--border); - margin-bottom: 1rem; -} - -.astro-card, -.aq-card { - background: var(--paper-dark); - padding: 0.85rem 0.75rem; -} - -.astro-val, -.aq-val { - font-size: 0.95rem; - font-weight: 500; -} - -.alerts-wrap { - border: 1px solid var(--border); - padding: 0.75rem; - margin-bottom: 1.2rem; - background: var(--paper-dark); -} - -.alerts-list { - display: grid; - gap: 0.5rem; -} - -.alert-item { - display: grid; - gap: 0.2rem; - border: 1px solid var(--border); - padding: 0.55rem; - background: var(--paper); - font-size: 0.72rem; -} - -.panel-note { - font-size: 0.72rem; - color: var(--ink-light); - border: 1px solid var(--border); - padding: 0.75rem; - background: var(--paper-dark); -} - -.settings-wrap { - border: 1px solid var(--border); - background: var(--paper-dark); - padding: 0.9rem; - display: grid; - gap: 1rem; -} +.settings-wrap { display: grid; gap: 0.62rem; } .settings-row { display: flex; - align-items: center; - justify-content: space-between; - border: 1px solid var(--border); - background: var(--paper); - padding: 0.65rem 0.75rem; + flex-direction: column; + align-items: flex-start; + gap: 0.45rem; + padding: 0.74rem; } .settings-label { font-size: 0.72rem; - color: var(--ink-mid); + color: var(--ink-muted); text-transform: uppercase; - letter-spacing: 0.08em; + letter-spacing: 0.05em; + font-weight: 700; } -.settings-value { - font-size: 0.8rem; - color: var(--ink); - text-align: right; -} +.settings-value { font-size: 0.88rem; color: var(--ink); } -.settings-btn { - border: 1px solid var(--border); - background: var(--paper-darker); - color: var(--ink); - padding: 0.45rem 0.7rem; - font-family: var(--mono); - font-size: 0.72rem; - cursor: pointer; -} - -.settings-btn:hover { background: var(--paper-dark); } - -.maps-wrap { - border: 1px solid var(--border); - background: var(--paper-dark); - padding: 0.75rem; -} - -.map-overview { - height: 360px; - border: 1px solid var(--border); -} - -.maps-controls { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 0.75rem; - margin-top: 0.75rem; -} - -.maps-control { - display: grid; - gap: 0.35rem; - font-size: 0.66rem; - color: var(--ink-mid); - text-transform: uppercase; - letter-spacing: 0.06em; -} - -.maps-control input, -.maps-control select { - width: 100%; - border: 1px solid var(--border); - background: var(--paper); - color: var(--ink); - font-family: var(--mono); - font-size: 0.72rem; - padding: 0.35rem 0.45rem; -} - -.maps-meta { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 1px; - margin-top: 0.75rem; - background: var(--border); - border: 1px solid var(--border); -} - -.maps-card { - background: var(--paper); - padding: 0.7rem; -} - -.maps-value { - font-size: 0.85rem; - color: var(--ink); -} - -.wind-legend { - display: flex; - align-items: center; - gap: 0.55rem; - font-size: 0.62rem; - color: var(--ink-light); - margin-top: 0.6rem; -} - -.wind-legend-scale { - width: 120px; - height: 8px; - border: 1px solid var(--border); - background: linear-gradient(to right, #d2f0ff, #84ccff, #3f9fff, #1879df); -} - -.stat { background: var(--paper-dark); padding: 0.9rem 0.75rem; text-align: center; } -.stat-label { font-size: 0.58rem; letter-spacing: 0.1em; color: var(--ink-light); text-transform: uppercase; margin-bottom: 0.3rem; } -.stat-val { font-size: 1rem; font-weight: 500; } -.unit { font-size: 0.6rem; color: var(--ink-mid); margin-left: 1px; } - -/* โ”€โ”€ Section heads โ”€โ”€ */ -.section-head { - display: flex; - justify-content: space-between; - align-items: baseline; - margin-bottom: 0.6rem; -} - -.section-actions { +.auto-detect-toggle, +.toggle-label { display: inline-flex; align-items: center; - gap: 0.6rem; - flex-wrap: wrap; - justify-content: flex-end; + gap: 0.48rem; + color: var(--ink-soft); + font-size: 0.9rem; } -.section-label { font-size: 0.62rem; letter-spacing: 0.14em; color: var(--ink-light); text-transform: uppercase; } -.section-note { font-size: 0.6rem; color: var(--ink-light); font-style: italic; } - -.link-btn { - background: none; - border: none; - font-family: var(--mono); - font-size: 0.6rem; - color: var(--cool); - cursor: pointer; - padding: 0; - font-style: italic; -} - -.link-btn:hover { text-decoration: underline; } - -/* โ”€โ”€ Radar maps โ”€โ”€ */ -.radar-embed-wrap { - border: 1px solid var(--border); - margin-bottom: 2rem; - background: #1a1f2e; -} - -.radar-map { - width: 100%; - height: 320px; -} - -.radar-map-full { - height: calc(100vh - 280px); - min-height: 420px; -} - -.radar-full-wrap { - border: 1px solid var(--border); - background: #1a1f2e; - margin-bottom: 0.5rem; -} - -/* โ”€โ”€ Radar controls โ”€โ”€ */ -.radar-controls { - display: flex; - align-items: center; - gap: 0.75rem; - flex-wrap: wrap; - padding: 0.6rem 0.75rem; - background: var(--paper-darker); - border-top: 1px solid var(--border); -} - -.radar-controls-full { padding: 0.75rem 1rem; } - -.radar-btn { - background: var(--ink); - color: var(--paper); - border: none; - width: 28px; height: 28px; - display: flex; align-items: center; justify-content: center; - font-size: 0.7rem; - cursor: pointer; - flex-shrink: 0; - transition: opacity 0.15s; -} - -.radar-btn:hover { opacity: 0.72; } -.layer-btn { background: var(--accent); } - -.radar-timeline { - flex: 1; - display: flex; - gap: 2px; - align-items: center; - height: 28px; -} - -.tl-tick { - flex: 1; - height: 100%; - background: var(--paper-dark); - border: 1px solid var(--border); - cursor: pointer; - transition: background 0.1s; - position: relative; -} - -.tl-tick:hover { background: var(--paper); } -.tl-tick.active { background: var(--cool); border-color: var(--cool); } -.tl-tick.forecast { opacity: 0.55; } - -.radar-ts { - font-size: 0.62rem; - color: var(--ink-mid); - letter-spacing: 0.04em; - white-space: nowrap; - min-width: 80px; - text-align: right; - margin-left: auto; -} - -/* Radar legend */ -.radar-legend { - display: flex; - align-items: center; - gap: 0.4rem; - margin-left: 0.5rem; -} - -.legend-label { font-size: 0.58rem; color: var(--ink-light); letter-spacing: 0.06em; } - -.legend-bar { - width: 80px; - height: 8px; - background: linear-gradient(to right, - #00d8ff, #00a0e0, #0070d0, - #00c800, #80e000, #ffff00, - #ffc000, #ff6000, #ff0000, #c00000 - ); - border: 0.5px solid var(--border); -} - -.radar-attribution { - font-size: 0.62rem; - color: var(--ink-light); - padding-top: 0.5rem; -} - -/* โ”€โ”€ Hourly scroll โ”€โ”€ */ -.hourly-scroll { - overflow-x: auto; - margin-bottom: 2rem; - border: 1px solid var(--border); - scrollbar-width: thin; - scrollbar-color: var(--border) transparent; -} - -.hourly-track { display: flex; min-width: max-content; } - -.hourly-summary { - font-size: 0.68rem; - color: var(--ink-mid); - margin-bottom: 0.45rem; -} - -.hour-cell { - min-width: 72px; - padding: 0.75rem 0.5rem; - text-align: center; - border-right: 0.5px solid var(--border); - background: var(--paper); - transition: background 0.12s; -} - -.hour-cell:last-child { border-right: none; } -.hour-cell:hover { background: var(--paper-dark); } -.hour-cell.now { background: var(--paper-darker); } - -.hour-time { font-size: 0.6rem; color: var(--ink-light); letter-spacing: 0.06em; margin-bottom: 0.35rem; text-transform: uppercase; } -.hour-icon { font-size: 1.4rem; margin-bottom: 0.3rem; } -.hour-temp { font-size: 0.88rem; font-weight: 500; } -.hour-feels { font-size: 0.56rem; color: var(--ink-mid); margin-top: 0.15rem; } -.hour-precip { font-size: 0.6rem; color: var(--cool); margin-top: 0.2rem; } - -/* โ”€โ”€ Daily forecast โ”€โ”€ */ -.daily-list { - border: 1px solid var(--border); - margin-bottom: 2rem; -} - -.day-row { - display: grid; - grid-template-columns: 120px 44px 1fr 80px 60px; - align-items: center; - gap: 1rem; - padding: 0.85rem 1.1rem; - border-bottom: 0.5px solid var(--border); - transition: background 0.12s; -} - -.day-row:last-child { border-bottom: none; } -.day-row:hover { background: var(--paper-dark); } -.day-row.today { background: var(--paper-dark); } - -.day-name { font-size: 0.8rem; font-weight: 500; } -.day-icon { font-size: 1.5rem; text-align: center; } -.day-desc { font-size: 0.72rem; color: var(--ink-mid); } -.day-temps { text-align: right; font-size: 0.82rem; font-weight: 500; white-space: nowrap; } -.day-lo { color: var(--ink-light); } -.day-meta { text-align: right; font-size: 0.62rem; color: var(--ink-light); line-height: 1.6; } - -#daily-forecast-toggle { - font-size: 0.66rem; - letter-spacing: 0.08em; - text-transform: uppercase; -} - -/* โ”€โ”€ Footer โ”€โ”€ */ -.footer-note { - font-size: 0.62rem; - color: var(--ink-light); - letter-spacing: 0.04em; - border-top: 0.5px solid var(--border); - padding-top: 0.75rem; -} - -/* โ”€โ”€ PWA: safe area insets (iPhone notch / home bar) โ”€โ”€ */ -@supports (padding: env(safe-area-inset-bottom)) { - .app { - padding-left: max(1.5rem, calc(1rem + env(safe-area-inset-left))); - padding-right: max(1.5rem, calc(1rem + env(safe-area-inset-right))); - padding-bottom: max(4rem, calc(2rem + env(safe-area-inset-bottom))); - } - .radar-map-full { - height: calc(100vh - 280px - env(safe-area-inset-top) - env(safe-area-inset-bottom)); - } -} - -/* โ”€โ”€ PWA: status bar area when running standalone โ”€โ”€ */ -@media (display-mode: standalone) { - .masthead { padding-top: env(safe-area-inset-top); } -} - -/* โ”€โ”€ Touch-friendly tap targets (min 44 ร— 44 px) โ”€โ”€ */ -.tab { - min-height: 44px; - display: inline-flex; - align-items: center; - justify-content: center; -} -.search-row input, -.search-row button { min-height: 44px; } -.radar-btn { width: 44px; height: 44px; font-size: 0.8rem; } -.tl-tick { min-height: 36px; } - -/* iOS momentum scrolling */ -.hourly-scroll { -webkit-overflow-scrolling: touch; } - -/* โ”€โ”€ Leaflet overrides โ”€โ”€ */ -.leaflet-container { background: #1a1f2e; } -.leaflet-control-zoom a { - background: var(--paper-dark) !important; - color: var(--ink) !important; - border-color: var(--border) !important; -} - -/* UI polish: strong contrast toggle */ -body.high-contrast { - --ink: #0a0a0a; - --ink-mid: #1f1f1f; - --ink-light: #3b3b3b; - --paper: #ffffff; - --paper-dark: #f2f2f2; - --paper-darker: #e8e8e8; - --border: #111111; - --accent: #a43600; - --cool: #005f8a; -} +.auto-detect-toggle input, +.toggle-label input { accent-color: var(--brand); } button:focus-visible, input:focus-visible, select:focus-visible { - outline: 2px solid var(--accent); + outline: 2px solid var(--brand); outline-offset: 2px; } -/* โ”€โ”€ Responsive โ”€โ”€ */ -@media (max-width: 640px) { +/* Leaflet */ +.leaflet-container { background: #1a2430; } +.leaflet-control-zoom a { + background: var(--surface) !important; + color: var(--ink) !important; + border-color: var(--line) !important; +} + +/* High contrast */ +body.high-contrast { + --ink: #0a0a0a; + --ink-soft: #1f1f1f; + --ink-muted: #3b3b3b; + --paper: #fff; + --paper-deep: #f2f2f2; + --surface: #fff; + --surface-soft: #fff; + --line: #111; + --brand: #a93a00; + --brand-strong: #8d2f00; + --info: #005a82; +} + +/* Desktop second */ +@media (min-width: 940px) { + html, body { font-size: 16px; } + .app { - padding: 1rem 0.8rem max(3.5rem, calc(1.75rem + env(safe-area-inset-bottom, 0px))); + max-width: 1120px; + margin: 0 auto; + padding: 1.35rem 1.3rem calc(6.9rem + env(safe-area-inset-bottom, 0px)); } .masthead { - grid-template-columns: 1fr; - gap: 0.8rem; + position: static; + display: grid; + grid-template-columns: auto 1fr; + align-items: center; + gap: 1rem; margin-bottom: 1rem; + padding: 0; + border: none; + box-shadow: none; + background: transparent; + backdrop-filter: none; } - .masthead h1 { font-size: 1.7rem; } - .tagline { display: none; } + + .masthead h1 { margin: 0; font-size: 2rem; } .tabs { + position: fixed; + left: 0; + right: 0; + bottom: 0; + width: 100vw; + max-width: 100vw; + z-index: 300; display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 0; - width: 100%; - } - .tab { - min-width: 0; - min-height: 42px; - font-size: 0.66rem; - padding: 0.35rem 0.25rem; - letter-spacing: 0.05em; + grid-template-columns: repeat(5, minmax(0, 1fr)); + gap: 0.22rem; + padding: 0.45rem 0.28rem calc(0.45rem + env(safe-area-inset-bottom, 0px)); + background: rgba(9, 28, 40, 0.92); + backdrop-filter: blur(12px); + border-top: 1px solid rgba(121, 165, 191, 0.36); + border-radius: 0; } - .search-wrap { margin-bottom: 1.25rem; } + .tab { + min-height: 58px; + flex-direction: column; + border-radius: 12px; + border-right: none; + padding: 0.32rem 0.15rem; + background: rgba(255, 255, 255, 0.04); + color: #d8eaf4; + gap: 0.24rem; + } + + .tab-icon { + font-family: 'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji', sans-serif; + font-size: 1.18rem; + line-height: 1; + } + + .tab-text { + font-size: 0.67rem; + line-height: 1; + letter-spacing: 0.02em; + font-weight: 600; + } + + .tab.active { + color: #fff; + background: linear-gradient(180deg, var(--brand) 0%, var(--brand-strong) 100%); + } + + .tagline { margin: 0; } + .search-row { - display: grid; - grid-template-columns: 1fr 1fr 1fr; - gap: 0.35rem; + grid-template-columns: 1fr auto auto auto; } + .search-row input { - grid-column: 1 / -1; - border-right: 1px solid var(--border); - font-size: 0.95rem; - } - .search-row button { - border: 1px solid var(--border); - padding: 0.55rem 0.4rem; - font-size: 0.7rem; + grid-column: auto; + min-height: 48px; } + #search-btn { - min-width: 0; - width: 100%; - } - #locate-btn, - #units-btn { - border-left: 1px solid var(--border) !important; - min-width: 0; - width: 100%; - padding: 0.55rem 0.4rem !important; + grid-column: auto; + min-width: 140px; } .loc-header { - flex-direction: column; - align-items: flex-start; - gap: 0.45rem; + flex-direction: row; + justify-content: space-between; + align-items: flex-end; } - .loc-name { font-size: 1.35rem; } - .loc-actions { align-items: flex-start; } - .current-grid > * { padding: 1rem 0.85rem; } - .current-grid { grid-template-columns: 1fr; } - .temp-big { - font-size: 3.4rem; - letter-spacing: -1.5px; - } - .cond-icon { font-size: 2.1rem; } + .loc-actions { align-items: flex-end; } + + .current-grid { grid-template-columns: 1fr 1fr; } + .stats-bar { grid-template-columns: repeat(6, minmax(0, 1fr)); } - .stats-bar { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .stat { padding: 0.75rem 0.45rem; } - .stat-val { font-size: 0.9rem; } .astro-grid, - .aq-grid { grid-template-columns: 1fr; } + .aq-grid, + .maps-meta, + .maps-controls { grid-template-columns: repeat(3, minmax(0, 1fr)); } - .section-head { - align-items: flex-start; - flex-direction: column; - gap: 0.35rem; - } - .section-actions { justify-content: flex-start; } - - .radar-map { height: 220px; } - .radar-map-full { - height: 52vh; - min-height: 280px; - } - .radar-controls { - gap: 0.5rem; - padding: 0.5rem; - } - .radar-timeline { - order: 3; - width: 100%; - flex-basis: 100%; - } - .radar-ts { - order: 2; - margin-left: 0; - min-width: 0; - text-align: left; - } - - .hour-cell { min-width: 64px; } - .hour-temp { font-size: 0.8rem; } - .hour-time, - .hour-precip, - .hour-feels { font-size: 0.55rem; } + .maps-controls { grid-template-columns: 1fr 1fr; } + .alerts-list { grid-template-columns: 1fr 1fr; } .day-row { - grid-template-columns: 1fr auto; - grid-template-areas: - 'name temps' - 'desc desc'; - gap: 0.25rem 0.6rem; - padding: 0.65rem 0.7rem; - align-items: start; + grid-template-columns: 120px 44px 1fr 84px 64px; + grid-template-areas: none; + align-items: center; + gap: 0.8rem; } - .day-name { - grid-area: name; - font-size: 0.74rem; - } - .day-icon { display: none; } - .day-desc { - grid-area: desc; - font-size: 0.66rem; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - .day-temps { - grid-area: temps; - font-size: 0.72rem; - } - .day-meta { display: none; } - .map-overview { height: 250px; } - .maps-controls, - .maps-meta { grid-template-columns: 1fr; } - .wind-legend { - flex-wrap: wrap; - gap: 0.35rem; + .day-name { grid-area: auto; } + .day-icon { display: block; font-size: 1.36rem; text-align: center; } + .day-desc { grid-area: auto; white-space: normal; } + .day-temps { grid-area: auto; text-align: right; } + .day-meta { + display: block; + text-align: right; + font-size: 0.67rem; + color: var(--ink-muted); + line-height: 1.35; } - .legend-bar { width: 56px; } - .settings-wrap { - padding: 0.65rem; - gap: 0.75rem; + .saved-item { grid-template-columns: 36px 1fr 36px 36px 36px auto; } + .saved-meta { + display: block; + padding: 0 0.58rem; + font-size: 0.68rem; + color: var(--ink-muted); } + .settings-row { - flex-direction: column; - align-items: flex-start; - gap: 0.45rem; - padding: 0.55rem 0.6rem; + flex-direction: row; + justify-content: space-between; + align-items: center; } - .settings-value { text-align: left; } - .saved-head { - align-items: flex-start; - flex-direction: column; - gap: 0.25rem; + .section-head { + flex-direction: row; + justify-content: space-between; + align-items: center; + margin-left: 0; + margin-right: 0; } - .saved-item { - grid-template-columns: 28px 1fr 28px 28px 28px; - } - .saved-chip { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - .saved-meta { display: none; } -} -@media (max-width: 420px) { - .tabs { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .tab { font-size: 0.64rem; } - .loc-name { font-size: 1.2rem; } - .temp-big { font-size: 3rem; } - .stat-label { font-size: 0.54rem; } - .section-label { font-size: 0.58rem; } + .radar-timeline { + order: 0; + flex-basis: auto; + flex: 1; + min-width: 180px; + } + + .radar-ts { margin-left: auto; } + + .radar-map-full { + height: calc(100vh - 300px); + min-height: 440px; + } + + .tab-panel { min-height: auto; } }