mirror of
https://github.com/Felitendo/Tanken.git
synced 2026-11-01 17:03:25 +01:00
6211 lines
142 KiB
CSS
6211 lines
142 KiB
CSS
/* === Reset & Base === */
|
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
|
|
:root {
|
|
--safe-bottom: env(safe-area-inset-bottom, 0px);
|
|
--tab-height: 56px;
|
|
|
|
/* Light mode (default) */
|
|
--color-text: #000;
|
|
--color-text-2: #3c3c43;
|
|
--color-bg: #fff;
|
|
--color-bg-secondary: #f2f2f7;
|
|
--color-bg-elevated: #ffffff;
|
|
--color-hint: #8e8e93;
|
|
--color-separator: rgba(60,60,67,0.12);
|
|
--color-accent: #007aff;
|
|
--color-accent-text: #fff;
|
|
--color-favorite: #ffb800;
|
|
--color-good: #34c759;
|
|
--color-okay: #ff9500;
|
|
--color-bad: #ff3b30;
|
|
|
|
/* Spacing scale — every padding/margin/gap should snap to this. */
|
|
--sp-1: 4px;
|
|
--sp-2: 8px;
|
|
--sp-3: 12px;
|
|
--sp-4: 16px;
|
|
--sp-5: 20px;
|
|
--sp-6: 24px;
|
|
--sp-7: 32px;
|
|
--sp-8: 40px;
|
|
|
|
/* Border-radius scale. r-md is the workhorse for cards/inputs;
|
|
r-pill is for chips and the round status pills. */
|
|
--r-xs: 6px;
|
|
--r-sm: 8px;
|
|
--r-md: 12px;
|
|
--r-lg: 16px;
|
|
--r-xl: 20px;
|
|
--r-pill: 999px;
|
|
|
|
/* Elevation — light mode keeps shadows soft so the secondary surface
|
|
stays the primary visual anchor on cards. */
|
|
--shadow-1: 0 1px 2px rgba(0,0,0,0.04), 0 1px 3px rgba(0,0,0,0.05);
|
|
--shadow-2: 0 2px 8px rgba(0,0,0,0.08), 0 1px 3px rgba(0,0,0,0.04);
|
|
--shadow-3: 0 6px 18px rgba(0,0,0,0.12), 0 2px 6px rgba(0,0,0,0.06);
|
|
--shadow-4: 0 16px 48px rgba(0,0,0,0.16), 0 4px 12px rgba(0,0,0,0.08);
|
|
|
|
color-scheme: light dark;
|
|
}
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
:root:not([data-theme="light"]) {
|
|
--color-text: #ffffff;
|
|
--color-text-2: #ebebf5;
|
|
--color-bg: #000000;
|
|
--color-bg-secondary: #1c1c1e;
|
|
--color-bg-elevated: #2c2c2e;
|
|
--color-hint: #98989e;
|
|
--color-separator: rgba(255,255,255,0.1);
|
|
--color-accent: #0a84ff;
|
|
--color-accent-text: #fff;
|
|
--color-favorite: #ffb800;
|
|
|
|
--shadow-1: 0 1px 2px rgba(0,0,0,0.4);
|
|
--shadow-2: 0 2px 8px rgba(0,0,0,0.35);
|
|
--shadow-3: 0 6px 18px rgba(0,0,0,0.45);
|
|
--shadow-4: 0 16px 48px rgba(0,0,0,0.55);
|
|
}
|
|
}
|
|
|
|
html[data-theme="dark"] {
|
|
--color-text: #ffffff;
|
|
--color-text-2: #ebebf5;
|
|
--color-bg: #000000;
|
|
--color-bg-secondary: #1c1c1e;
|
|
--color-bg-elevated: #2c2c2e;
|
|
--color-hint: #98989e;
|
|
--color-separator: rgba(255,255,255,0.1);
|
|
--color-accent: #0a84ff;
|
|
--color-accent-text: #fff;
|
|
|
|
--shadow-1: 0 1px 2px rgba(0,0,0,0.4);
|
|
--shadow-2: 0 2px 8px rgba(0,0,0,0.35);
|
|
--shadow-3: 0 6px 18px rgba(0,0,0,0.45);
|
|
--shadow-4: 0 16px 48px rgba(0,0,0,0.55);
|
|
|
|
color-scheme: dark;
|
|
}
|
|
|
|
html[data-theme="light"] {
|
|
color-scheme: light;
|
|
}
|
|
|
|
html, body {
|
|
height: 100%;
|
|
overflow: hidden;
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
font-size: 15px;
|
|
line-height: 1.4;
|
|
color: var(--color-text);
|
|
background: var(--color-bg);
|
|
-webkit-font-smoothing: antialiased;
|
|
-webkit-tap-highlight-color: transparent;
|
|
-webkit-overflow-scrolling: touch;
|
|
}
|
|
|
|
/* === Accessibility: Focus-visible === */
|
|
:where(button, a, [role="button"], input, select, textarea):focus { outline: none; }
|
|
|
|
:where(button, a, [role="button"]):focus-visible {
|
|
outline: 2px solid var(--color-accent);
|
|
outline-offset: 2px;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
:where(input, select, textarea):focus-visible {
|
|
outline: 2px solid var(--color-accent);
|
|
outline-offset: 1px;
|
|
}
|
|
|
|
/* === Accessibility: Reduced motion === */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*, *::before, *::after {
|
|
animation-duration: 0.01ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-duration: 0.01ms !important;
|
|
scroll-behavior: auto !important;
|
|
}
|
|
}
|
|
|
|
/* === App Container === */
|
|
#app {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: calc(var(--tab-height) + var(--safe-bottom));
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* === Tab Views === */
|
|
.tab-view {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
-webkit-overflow-scrolling: touch;
|
|
overscroll-behavior: contain;
|
|
opacity: 0;
|
|
visibility: hidden;
|
|
transform: translateY(6px) scale(0.985);
|
|
pointer-events: none;
|
|
/* Outgoing: fade out quickly, then hide */
|
|
transition: opacity 0.18s ease-in,
|
|
transform 0.18s ease-in,
|
|
visibility 0s linear 0.18s;
|
|
will-change: opacity, transform;
|
|
}
|
|
|
|
.tab-view.active {
|
|
opacity: 1;
|
|
visibility: visible;
|
|
transform: translateY(0) scale(1);
|
|
pointer-events: auto;
|
|
/* Incoming: wait for outgoing to clear, then ease in with a soft decel */
|
|
transition: opacity 0.3s cubic-bezier(0.22, 1, 0.36, 1) 0.08s,
|
|
transform 0.38s cubic-bezier(0.22, 1, 0.36, 1) 0.08s,
|
|
visibility 0s linear 0s;
|
|
}
|
|
|
|
/* === Sections (iOS Settings Style) === */
|
|
.section {
|
|
padding: 0 var(--sp-4);
|
|
margin-bottom: var(--sp-3);
|
|
}
|
|
|
|
.section-header {
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--color-hint);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.6px;
|
|
padding: var(--sp-5) var(--sp-1) var(--sp-2);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--sp-2);
|
|
}
|
|
|
|
.section-sub-header {
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
color: var(--color-hint);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.6px;
|
|
padding: var(--sp-5) var(--sp-1) var(--sp-2);
|
|
}
|
|
|
|
.section-hint {
|
|
margin: 0 var(--sp-1) var(--sp-3);
|
|
font-size: 12px;
|
|
line-height: 1.5;
|
|
color: var(--color-hint);
|
|
}
|
|
|
|
.sync-badge {
|
|
display: none;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: #fff;
|
|
opacity: 0.45;
|
|
cursor: help;
|
|
width: 14px;
|
|
height: 14px;
|
|
position: relative;
|
|
transition: opacity 0.25s ease, color 0.3s ease;
|
|
}
|
|
body.logged-in .sync-badge {
|
|
display: inline-flex;
|
|
}
|
|
.sync-badge svg {
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
.sync-badge .sync-icon-idle { opacity: 1; }
|
|
.sync-badge .sync-icon-ok { opacity: 0; }
|
|
/* The rotating arrow caused visible wobble because the icon is asymmetric;
|
|
sync state is now signalled by an opacity pulse on the cloud itself, so
|
|
the spin icon is permanently hidden (markup kept for back-compat). */
|
|
.sync-badge .sync-icon-spin { display: none; }
|
|
|
|
/* Syncing: cloud stays put and pulses gently. No rotation, no swap. */
|
|
.sync-badge.syncing { opacity: 1; }
|
|
.sync-badge.syncing .sync-icon-idle {
|
|
animation: sync-pulse 0.7s ease-in-out infinite;
|
|
}
|
|
|
|
/* Synced: cloud crossfades into the cloud+check variant with a green tint,
|
|
plus a tiny pop so success feels noticed. */
|
|
.sync-badge.synced .sync-icon-idle { opacity: 0; }
|
|
.sync-badge.synced .sync-icon-ok {
|
|
opacity: 1;
|
|
animation: sync-check-pop 0.28s ease-out;
|
|
}
|
|
.sync-badge.synced {
|
|
opacity: 0.95;
|
|
color: #34c759;
|
|
}
|
|
|
|
@keyframes sync-pulse {
|
|
0%, 100% { opacity: 0.55; }
|
|
50% { opacity: 1; }
|
|
}
|
|
|
|
@keyframes sync-check-pop {
|
|
0% { transform: scale(0.7); }
|
|
60% { transform: scale(1.08); }
|
|
100% { transform: scale(1); }
|
|
}
|
|
|
|
.cloud-sync-hint {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--sp-2);
|
|
margin: var(--sp-3) var(--sp-4) var(--sp-1);
|
|
padding: var(--sp-2) var(--sp-3);
|
|
border-radius: var(--r-sm);
|
|
background: var(--color-bg-secondary);
|
|
font-size: 12px;
|
|
color: var(--color-hint);
|
|
line-height: 1.4;
|
|
}
|
|
.cloud-sync-hint svg {
|
|
flex-shrink: 0;
|
|
opacity: 0.5;
|
|
}
|
|
body.logged-in .cloud-sync-hint {
|
|
display: none;
|
|
}
|
|
|
|
/* === Cards (shadcn-style: 1 px border + subtle background) === */
|
|
.card {
|
|
background: var(--color-bg-secondary);
|
|
border: 1px solid var(--color-separator);
|
|
border-radius: var(--r-md);
|
|
padding: var(--sp-3) var(--sp-4);
|
|
margin-bottom: var(--sp-2);
|
|
}
|
|
|
|
.card-list {
|
|
background: var(--color-bg-secondary);
|
|
border: 1px solid var(--color-separator);
|
|
border-radius: var(--r-md);
|
|
overflow: hidden;
|
|
margin-bottom: var(--sp-2);
|
|
}
|
|
|
|
.card-list .card-row {
|
|
padding: var(--sp-3) var(--sp-4);
|
|
border-bottom: 0.5px solid var(--color-separator);
|
|
}
|
|
|
|
/* Inline card header (used inside a .card to act as a section title for the
|
|
card's contents). Title is bigger and more emphatic than .section-header,
|
|
the description sits muted right beneath. */
|
|
.card-head {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--sp-1);
|
|
padding-bottom: var(--sp-3);
|
|
margin-bottom: var(--sp-3);
|
|
border-bottom: 1px solid var(--color-separator);
|
|
}
|
|
.card-head-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: var(--sp-3);
|
|
}
|
|
.card-title-lg {
|
|
font-size: 17px;
|
|
font-weight: 600;
|
|
color: var(--color-text);
|
|
letter-spacing: -0.01em;
|
|
line-height: 1.25;
|
|
}
|
|
.card-description {
|
|
font-size: 13px;
|
|
color: var(--color-hint);
|
|
line-height: 1.45;
|
|
}
|
|
|
|
/* === Page header (sits at the top of each non-map tab) === */
|
|
.page-header {
|
|
padding: var(--sp-5) var(--sp-4) var(--sp-4);
|
|
border-bottom: 1px solid var(--color-separator);
|
|
margin-bottom: var(--sp-4);
|
|
}
|
|
.page-title {
|
|
font-size: 28px;
|
|
font-weight: 700;
|
|
letter-spacing: -0.02em;
|
|
color: var(--color-text);
|
|
line-height: 1.15;
|
|
}
|
|
.page-description {
|
|
font-size: 14px;
|
|
color: var(--color-hint);
|
|
margin-top: var(--sp-1);
|
|
line-height: 1.45;
|
|
}
|
|
|
|
/* === Metric (KPI) card — used for the 3-up dashboard tiles in history
|
|
and stats. Tighter than .card, more visual weight on the value. */
|
|
.metric-card {
|
|
background: var(--color-bg-secondary);
|
|
border: 1px solid var(--color-separator);
|
|
border-radius: var(--r-md);
|
|
padding: var(--sp-3) var(--sp-4);
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
gap: var(--sp-1);
|
|
min-height: 84px;
|
|
min-width: 0;
|
|
}
|
|
.metric-label {
|
|
font-size: 12px;
|
|
color: var(--color-hint);
|
|
font-weight: 500;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
.metric-value {
|
|
font-size: 24px;
|
|
font-weight: 700;
|
|
color: var(--color-text);
|
|
letter-spacing: -0.01em;
|
|
font-variant-numeric: tabular-nums;
|
|
line-height: 1.1;
|
|
}
|
|
.metric-trend {
|
|
font-size: 12px;
|
|
color: var(--color-hint);
|
|
margin-top: var(--sp-1);
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
}
|
|
.metric-trend.up { color: var(--color-bad); }
|
|
.metric-trend.down { color: var(--color-good); }
|
|
.metric-trend.flat { color: var(--color-hint); }
|
|
|
|
.metric-row {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
gap: var(--sp-3);
|
|
margin-bottom: var(--sp-3);
|
|
}
|
|
.stats-overview-row {
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
}
|
|
@media (max-width: 600px) {
|
|
.metric-row {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
|
|
/* === Extreme cards (lowest / highest price in history summary) === */
|
|
.extreme-card {
|
|
background: var(--color-bg-secondary);
|
|
border: 1px solid var(--color-separator);
|
|
border-radius: var(--r-md);
|
|
padding: var(--sp-4) var(--sp-4);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--sp-1);
|
|
min-width: 0;
|
|
}
|
|
.extreme-label {
|
|
font-size: 12px;
|
|
color: var(--color-hint);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
font-weight: 500;
|
|
}
|
|
.extreme-value {
|
|
font-size: 28px;
|
|
font-weight: 700;
|
|
color: var(--color-text);
|
|
letter-spacing: -0.01em;
|
|
font-variant-numeric: tabular-nums;
|
|
line-height: 1.1;
|
|
}
|
|
.extreme-value.good { color: var(--color-good); }
|
|
.extreme-value.bad { color: var(--color-bad); }
|
|
.extreme-card.is-empty .extreme-value { color: var(--color-hint); opacity: 0.6; }
|
|
.extreme-station {
|
|
font-size: 13px;
|
|
color: var(--color-hint);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.empty-state-inline {
|
|
text-align: center;
|
|
padding: var(--sp-5) 0 var(--sp-2);
|
|
opacity: 0.5;
|
|
font-size: 13px;
|
|
color: var(--color-hint);
|
|
}
|
|
|
|
/* === Stats hero (overall avg headline) === */
|
|
.stats-hero {
|
|
background: var(--color-bg-secondary);
|
|
border: 1px solid var(--color-separator);
|
|
border-radius: var(--r-md);
|
|
padding: var(--sp-5) var(--sp-5);
|
|
margin-bottom: var(--sp-3);
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
justify-content: center;
|
|
gap: var(--sp-1);
|
|
min-height: 120px;
|
|
}
|
|
.stats-hero-label {
|
|
font-size: 12px;
|
|
color: var(--color-hint);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
font-weight: 500;
|
|
}
|
|
.stats-hero-value {
|
|
font-size: 40px;
|
|
font-weight: 800;
|
|
color: var(--color-text);
|
|
letter-spacing: -0.02em;
|
|
font-variant-numeric: tabular-nums;
|
|
line-height: 1.05;
|
|
}
|
|
|
|
/* === Best times (cheapest day / hour) === */
|
|
.best-time-grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: var(--sp-3);
|
|
}
|
|
.best-time-card {
|
|
background: var(--color-bg-secondary);
|
|
border: 1px solid var(--color-separator);
|
|
border-radius: var(--r-md);
|
|
padding: var(--sp-4) var(--sp-4);
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
gap: var(--sp-1);
|
|
min-height: 112px;
|
|
min-width: 0;
|
|
}
|
|
.best-time-label {
|
|
font-size: 12px;
|
|
color: var(--color-hint);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
font-weight: 500;
|
|
}
|
|
.best-time-value {
|
|
font-size: 20px;
|
|
font-weight: 700;
|
|
color: var(--color-text);
|
|
letter-spacing: -0.01em;
|
|
}
|
|
.best-time-price {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
margin-top: var(--sp-1);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.best-time-price.good { color: var(--color-good); }
|
|
.best-time-price.bad { color: var(--color-bad); }
|
|
|
|
@media (max-width: 480px) {
|
|
.best-time-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
|
|
#stats-content {
|
|
width: 100%;
|
|
}
|
|
|
|
#stats-content > .metric-row {
|
|
margin-left: var(--sp-4);
|
|
margin-right: var(--sp-4);
|
|
}
|
|
|
|
/* === Stats hero card — overall avg with price spread visualisation === */
|
|
.stats-hero-section {
|
|
padding-bottom: 0;
|
|
}
|
|
.stats-hero-card {
|
|
position: relative;
|
|
background: linear-gradient(135deg, var(--color-bg-secondary) 0%, color-mix(in srgb, var(--color-accent) 7%, var(--color-bg-secondary)) 100%);
|
|
border: 1px solid var(--color-separator);
|
|
border-radius: var(--r-md);
|
|
padding: var(--sp-5);
|
|
margin-bottom: var(--sp-3);
|
|
overflow: hidden;
|
|
}
|
|
.stats-hero-card::after {
|
|
content: '';
|
|
position: absolute;
|
|
inset: 0;
|
|
background: radial-gradient(circle at 92% -10%, color-mix(in srgb, var(--color-accent) 18%, transparent) 0%, transparent 55%);
|
|
pointer-events: none;
|
|
}
|
|
.stats-hero-top {
|
|
position: relative;
|
|
z-index: 1;
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
gap: var(--sp-3);
|
|
}
|
|
.stats-hero-headline {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
}
|
|
.stats-hero-label {
|
|
font-size: 11px;
|
|
color: var(--color-hint);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.7px;
|
|
font-weight: 600;
|
|
}
|
|
.stats-hero-value {
|
|
font-size: 36px;
|
|
font-weight: 800;
|
|
color: var(--color-text);
|
|
letter-spacing: -0.02em;
|
|
font-variant-numeric: tabular-nums;
|
|
line-height: 1.05;
|
|
}
|
|
.stats-hero-glyph {
|
|
flex-shrink: 0;
|
|
width: 44px;
|
|
height: 44px;
|
|
border-radius: 12px;
|
|
background: color-mix(in srgb, var(--color-accent) 14%, transparent);
|
|
color: var(--color-accent);
|
|
display: grid;
|
|
place-items: center;
|
|
}
|
|
|
|
.stats-hero-period {
|
|
position: relative;
|
|
z-index: 1;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
margin-top: 10px;
|
|
padding: 4px 10px;
|
|
border-radius: 999px;
|
|
background: color-mix(in srgb, var(--color-accent) 12%, transparent);
|
|
color: var(--color-accent);
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
letter-spacing: 0.1px;
|
|
width: fit-content;
|
|
cursor: default;
|
|
}
|
|
|
|
.stats-spread {
|
|
position: relative;
|
|
z-index: 1;
|
|
margin-top: var(--sp-4);
|
|
}
|
|
.stats-spread-track {
|
|
position: relative;
|
|
height: 6px;
|
|
border-radius: 3px;
|
|
background: var(--color-separator);
|
|
overflow: visible;
|
|
}
|
|
.stats-spread-fill {
|
|
position: absolute;
|
|
inset: 0;
|
|
border-radius: 3px;
|
|
background: linear-gradient(90deg, var(--color-good) 0%, #ff9500 50%, var(--color-bad) 100%);
|
|
opacity: 0.85;
|
|
}
|
|
.stats-spread-marker {
|
|
position: absolute;
|
|
top: 50%;
|
|
width: 14px;
|
|
height: 14px;
|
|
margin-left: -7px;
|
|
border-radius: 50%;
|
|
background: var(--color-text);
|
|
border: 2px solid var(--color-bg-secondary);
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.28);
|
|
transform: translateY(-50%);
|
|
transition: left 0.7s cubic-bezier(0.22, 1, 0.36, 1);
|
|
}
|
|
.stats-spread-labels {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: var(--sp-2);
|
|
margin-top: 8px;
|
|
font-size: 11px;
|
|
color: var(--color-hint);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.stats-spread-low { color: var(--color-good); font-weight: 600; }
|
|
.stats-spread-high { color: var(--color-bad); font-weight: 600; }
|
|
.stats-spread-caption {
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.6px;
|
|
font-weight: 600;
|
|
font-size: 10px;
|
|
color: var(--color-hint);
|
|
}
|
|
|
|
/* === Stats facts row — compact 3-up below the hero === */
|
|
.stats-facts-row {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
gap: 8px;
|
|
}
|
|
.stats-fact-card {
|
|
background: var(--color-bg-secondary);
|
|
border: 1px solid var(--color-separator);
|
|
border-radius: var(--r-md);
|
|
padding: 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
min-width: 0;
|
|
}
|
|
.stats-fact-icon {
|
|
flex-shrink: 0;
|
|
width: 30px;
|
|
height: 30px;
|
|
border-radius: 9px;
|
|
display: grid;
|
|
place-items: center;
|
|
}
|
|
.stats-fact-icon-good { background: color-mix(in srgb, var(--color-good) 16%, transparent); color: var(--color-good); }
|
|
.stats-fact-icon-bad { background: color-mix(in srgb, var(--color-bad) 16%, transparent); color: var(--color-bad); }
|
|
.stats-fact-icon-accent { background: color-mix(in srgb, var(--color-accent) 16%, transparent); color: var(--color-accent); }
|
|
.stats-fact-body {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0;
|
|
min-width: 0;
|
|
}
|
|
.stats-fact-label {
|
|
font-size: 10px;
|
|
color: var(--color-hint);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
font-weight: 600;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
.stats-fact-value {
|
|
font-size: 16px;
|
|
font-weight: 700;
|
|
letter-spacing: -0.01em;
|
|
font-variant-numeric: tabular-nums;
|
|
line-height: 1.15;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
/* Best-time header + savings badge */
|
|
.best-time-head {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
margin-bottom: 2px;
|
|
}
|
|
.best-time-icon-wrap {
|
|
flex-shrink: 0;
|
|
width: 26px;
|
|
height: 26px;
|
|
border-radius: 8px;
|
|
background: color-mix(in srgb, var(--color-accent) 14%, transparent);
|
|
color: var(--color-accent);
|
|
display: grid;
|
|
place-items: center;
|
|
}
|
|
.best-time-savings {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
margin-top: 6px;
|
|
padding: 3px 8px;
|
|
border-radius: 999px;
|
|
background: color-mix(in srgb, var(--color-good) 16%, transparent);
|
|
color: var(--color-good);
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
font-variant-numeric: tabular-nums;
|
|
align-self: flex-start;
|
|
width: fit-content;
|
|
}
|
|
|
|
.ranking-medal .ranking-pos {
|
|
font-size: 17px;
|
|
line-height: 1;
|
|
}
|
|
|
|
/* === Weekday tile row — 7 chronological cards with a coloured
|
|
cheapness bar at the bottom. Background stays neutral so the
|
|
price reads cleanly; the bar carries the rank signal. The
|
|
cheapest day gets a subtle tinted surface + coloured border. === */
|
|
.stats-tile-grid {
|
|
display: grid;
|
|
gap: 6px;
|
|
}
|
|
.stats-tile-grid-7 {
|
|
grid-template-columns: repeat(7, minmax(0, 1fr));
|
|
}
|
|
.stats-tile {
|
|
position: relative;
|
|
border-radius: 12px;
|
|
padding: 11px 4px 9px;
|
|
background: var(--color-bg-secondary);
|
|
border: 1px solid var(--color-separator);
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: flex-start;
|
|
gap: 2px;
|
|
min-height: 64px;
|
|
min-width: 0;
|
|
transition: transform 0.18s cubic-bezier(0.2, 0, 0, 1), border-color 0.2s ease, background 0.2s ease;
|
|
}
|
|
.stats-tile.is-empty .stats-tile-name,
|
|
.stats-tile.is-empty .stats-tile-value { opacity: 0.4; }
|
|
.stats-tile.is-best {
|
|
border-color: color-mix(in srgb, var(--tile-color) 65%, transparent);
|
|
background: color-mix(in srgb, var(--tile-color) 9%, var(--color-bg-secondary));
|
|
}
|
|
.stats-tile-name {
|
|
font-size: 10px;
|
|
font-weight: 700;
|
|
color: var(--color-hint);
|
|
letter-spacing: 0.5px;
|
|
text-transform: uppercase;
|
|
line-height: 1;
|
|
}
|
|
.stats-tile-value {
|
|
font-size: 13px;
|
|
font-weight: 700;
|
|
color: var(--tile-color, var(--color-text));
|
|
font-variant-numeric: tabular-nums;
|
|
letter-spacing: -0.02em;
|
|
line-height: 1.1;
|
|
margin-top: 2px;
|
|
max-width: 100%;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
.stats-tile.is-empty .stats-tile-value {
|
|
color: var(--color-hint);
|
|
}
|
|
.stats-tile-bar {
|
|
margin-top: auto;
|
|
width: calc(100% - 12px);
|
|
height: 3px;
|
|
background: var(--color-separator);
|
|
border-radius: 999px;
|
|
overflow: hidden;
|
|
align-self: center;
|
|
}
|
|
.stats-tile-bar-fill {
|
|
height: 100%;
|
|
width: 0;
|
|
background: var(--tile-color, var(--color-separator));
|
|
border-radius: 999px;
|
|
transition: width 0.7s cubic-bezier(0.22, 1, 0.36, 1);
|
|
}
|
|
.stats-tile.is-empty .stats-tile-bar-fill { background: var(--color-separator); }
|
|
.stats-tile-crown {
|
|
position: absolute;
|
|
top: 4px;
|
|
right: 5px;
|
|
font-size: 10px;
|
|
color: var(--tile-color);
|
|
z-index: 2;
|
|
line-height: 1;
|
|
}
|
|
|
|
/* === Settings: account hero card === */
|
|
.account-hero {
|
|
position: relative;
|
|
background: linear-gradient(135deg, var(--color-bg-secondary) 0%, color-mix(in srgb, var(--color-accent) 8%, var(--color-bg-secondary)) 100%);
|
|
border: 1px solid var(--color-separator);
|
|
border-radius: var(--r-md);
|
|
padding: var(--sp-5);
|
|
overflow: hidden;
|
|
}
|
|
.account-hero::after {
|
|
content: '';
|
|
position: absolute;
|
|
inset: 0;
|
|
background: radial-gradient(circle at 92% -10%, color-mix(in srgb, var(--color-accent) 20%, transparent) 0%, transparent 55%);
|
|
pointer-events: none;
|
|
}
|
|
.account-hero-top {
|
|
position: relative;
|
|
z-index: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--sp-4);
|
|
min-width: 0;
|
|
}
|
|
.account-hero-avatar {
|
|
flex-shrink: 0;
|
|
width: 60px;
|
|
height: 60px;
|
|
border-radius: 50%;
|
|
background: color-mix(in srgb, var(--color-accent) 18%, var(--color-bg-secondary));
|
|
color: color-mix(in srgb, var(--color-accent) 80%, var(--color-text));
|
|
display: grid;
|
|
place-items: center;
|
|
overflow: hidden;
|
|
border: 1.5px solid color-mix(in srgb, var(--color-accent) 30%, transparent);
|
|
transition: border-color 0.25s ease, background 0.25s ease;
|
|
}
|
|
.account-hero.is-signed-in .account-hero-avatar {
|
|
background: var(--color-bg);
|
|
border-color: color-mix(in srgb, var(--color-accent) 55%, transparent);
|
|
color: var(--color-accent);
|
|
}
|
|
.account-hero-avatar .account-avatar-img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
.account-hero-avatar .account-avatar-fallback {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: grid;
|
|
place-items: center;
|
|
font-size: 24px;
|
|
font-weight: 700;
|
|
color: var(--color-accent);
|
|
background: color-mix(in srgb, var(--color-accent) 14%, transparent);
|
|
}
|
|
.account-hero-text {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
}
|
|
.account-hero-label {
|
|
font-size: 10px;
|
|
color: var(--color-hint);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.7px;
|
|
font-weight: 700;
|
|
}
|
|
.account-hero-name {
|
|
font-size: 22px;
|
|
font-weight: 800;
|
|
color: var(--color-text);
|
|
letter-spacing: -0.02em;
|
|
line-height: 1.15;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
.account-hero-subline {
|
|
font-size: 13px;
|
|
color: var(--color-hint);
|
|
line-height: 1.35;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
.account-hero-actions {
|
|
position: relative;
|
|
z-index: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--sp-3);
|
|
margin-top: var(--sp-4);
|
|
flex-wrap: wrap;
|
|
}
|
|
.account-hero-sync-hint {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
font-size: 11px;
|
|
color: var(--color-hint);
|
|
line-height: 1.35;
|
|
}
|
|
.account-hero-sync-hint svg {
|
|
flex-shrink: 0;
|
|
color: var(--color-accent);
|
|
opacity: 0.85;
|
|
}
|
|
.account-hero-btn {
|
|
flex-shrink: 0;
|
|
padding: 9px 18px;
|
|
border-radius: 999px;
|
|
background: var(--color-accent);
|
|
color: var(--color-accent-text);
|
|
border: none;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
font-family: inherit;
|
|
cursor: pointer;
|
|
-webkit-tap-highlight-color: transparent;
|
|
transition: transform 0.15s ease, box-shadow 0.2s ease, background 0.2s ease;
|
|
}
|
|
.account-hero-btn:hover {
|
|
box-shadow: 0 4px 12px color-mix(in srgb, var(--color-accent) 35%, transparent);
|
|
}
|
|
.account-hero-btn:active {
|
|
transform: scale(0.96);
|
|
}
|
|
.account-hero.is-signed-in .account-hero-btn {
|
|
background: color-mix(in srgb, var(--color-text) 8%, transparent);
|
|
color: var(--color-text);
|
|
}
|
|
.account-hero.is-signed-in .account-hero-btn:hover {
|
|
background: color-mix(in srgb, var(--color-text) 14%, transparent);
|
|
box-shadow: none;
|
|
}
|
|
|
|
/* === Settings rows: icon chip in front of the label === */
|
|
.settings-icon-chip {
|
|
flex-shrink: 0;
|
|
width: 32px;
|
|
height: 32px;
|
|
border-radius: 9px;
|
|
display: grid;
|
|
place-items: center;
|
|
background: color-mix(in srgb, var(--color-accent) 14%, transparent);
|
|
color: var(--color-accent);
|
|
}
|
|
.settings-icon-chip.settings-icon-good {
|
|
background: color-mix(in srgb, var(--color-good) 14%, transparent);
|
|
color: var(--color-good);
|
|
}
|
|
.settings-icon-chip.settings-icon-warn {
|
|
background: color-mix(in srgb, var(--color-bad) 14%, transparent);
|
|
color: var(--color-bad);
|
|
}
|
|
.settings-card .settings-row-left {
|
|
gap: var(--sp-3);
|
|
}
|
|
.settings-card .settings-row-label {
|
|
font-size: 15px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* === Segmented control (theme, history-default range) === */
|
|
.seg-control {
|
|
position: relative;
|
|
display: flex;
|
|
gap: 4px;
|
|
margin: 0 var(--sp-4) var(--sp-3);
|
|
padding: 4px;
|
|
border-radius: 12px;
|
|
background: color-mix(in srgb, var(--color-text) 6%, transparent);
|
|
border: 1px solid var(--color-separator);
|
|
}
|
|
.seg-thumb {
|
|
position: absolute;
|
|
top: 4px;
|
|
bottom: 4px;
|
|
left: 0;
|
|
width: 0;
|
|
background: var(--color-bg);
|
|
border-radius: 9px;
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 1px rgba(0, 0, 0, 0.04);
|
|
transition: transform 0.28s cubic-bezier(0.22, 1, 0.36, 1),
|
|
width 0.28s cubic-bezier(0.22, 1, 0.36, 1);
|
|
pointer-events: none;
|
|
z-index: 1;
|
|
}
|
|
html[data-theme="dark"] .seg-thumb,
|
|
html:not([data-theme]) .seg-thumb { /* dark via prefers-color-scheme */
|
|
background: var(--color-bg-secondary);
|
|
}
|
|
@media (prefers-color-scheme: dark) {
|
|
html:not([data-theme="light"]) .seg-thumb {
|
|
background: var(--color-bg-secondary);
|
|
}
|
|
}
|
|
.seg-option {
|
|
position: relative;
|
|
z-index: 2;
|
|
flex: 1;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 6px;
|
|
padding: 9px 10px;
|
|
border: none;
|
|
background: none;
|
|
color: var(--color-hint);
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
font-family: inherit;
|
|
cursor: pointer;
|
|
border-radius: 9px;
|
|
-webkit-tap-highlight-color: transparent;
|
|
transition: color 0.2s ease;
|
|
white-space: nowrap;
|
|
min-width: 0;
|
|
}
|
|
.seg-option svg {
|
|
flex-shrink: 0;
|
|
opacity: 0.65;
|
|
transition: opacity 0.2s ease, color 0.2s ease;
|
|
}
|
|
.seg-option:hover { color: var(--color-text); }
|
|
.seg-option:hover svg { opacity: 0.85; }
|
|
.seg-option.active {
|
|
color: var(--color-text);
|
|
font-weight: 700;
|
|
}
|
|
.seg-option.active svg {
|
|
color: var(--color-accent);
|
|
opacity: 1;
|
|
}
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.seg-thumb { transition: none; }
|
|
}
|
|
|
|
/* === About hero — bigger app-icon centerpiece === */
|
|
.about-hero {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--sp-4);
|
|
padding: var(--sp-5);
|
|
background: var(--color-bg-secondary);
|
|
border: 1px solid var(--color-separator);
|
|
border-radius: var(--r-md);
|
|
margin-bottom: var(--sp-3);
|
|
}
|
|
.about-hero .about-app-icon {
|
|
flex-shrink: 0;
|
|
width: 68px;
|
|
height: 68px;
|
|
border-radius: 16px;
|
|
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);
|
|
cursor: pointer;
|
|
}
|
|
.about-hero-info {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1px;
|
|
}
|
|
.about-hero-info .about-app-name {
|
|
font-size: 22px;
|
|
font-weight: 800;
|
|
color: var(--color-text);
|
|
letter-spacing: -0.02em;
|
|
}
|
|
.about-hero-info .about-app-version {
|
|
font-size: 12px;
|
|
color: var(--color-hint);
|
|
font-variant-numeric: tabular-nums;
|
|
margin-bottom: 4px;
|
|
}
|
|
.about-hero-tag {
|
|
font-size: 13px;
|
|
color: var(--color-hint);
|
|
line-height: 1.35;
|
|
}
|
|
|
|
/* === History tab — hero card with current avg + trend pills === */
|
|
.history-hero-card {
|
|
position: relative;
|
|
background: linear-gradient(135deg, var(--color-bg-secondary) 0%, color-mix(in srgb, var(--color-accent) 7%, var(--color-bg-secondary)) 100%);
|
|
border: 1px solid var(--color-separator);
|
|
border-radius: var(--r-md);
|
|
padding: var(--sp-5);
|
|
overflow: hidden;
|
|
}
|
|
.history-hero-card::after {
|
|
content: '';
|
|
position: absolute;
|
|
inset: 0;
|
|
background: radial-gradient(circle at 92% -10%, color-mix(in srgb, var(--color-accent) 18%, transparent) 0%, transparent 55%);
|
|
pointer-events: none;
|
|
}
|
|
.history-hero-top {
|
|
position: relative;
|
|
z-index: 1;
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
gap: var(--sp-3);
|
|
}
|
|
.history-hero-headline {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
}
|
|
.history-hero-label {
|
|
font-size: 11px;
|
|
color: var(--color-hint);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.7px;
|
|
font-weight: 600;
|
|
}
|
|
.history-hero-value {
|
|
font-size: 36px;
|
|
font-weight: 800;
|
|
color: var(--color-text);
|
|
letter-spacing: -0.02em;
|
|
font-variant-numeric: tabular-nums;
|
|
line-height: 1.05;
|
|
}
|
|
.history-hero-period {
|
|
position: relative;
|
|
z-index: 1;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
margin-top: 6px;
|
|
padding: 4px 10px;
|
|
border-radius: 999px;
|
|
background: color-mix(in srgb, var(--color-accent) 12%, transparent);
|
|
color: var(--color-accent);
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
width: fit-content;
|
|
}
|
|
.history-hero-glyph {
|
|
flex-shrink: 0;
|
|
width: 44px;
|
|
height: 44px;
|
|
border-radius: 12px;
|
|
background: color-mix(in srgb, var(--glyph-color, var(--color-accent)) 14%, transparent);
|
|
color: var(--glyph-color, var(--color-accent));
|
|
display: grid;
|
|
place-items: center;
|
|
}
|
|
.history-hero-pills {
|
|
position: relative;
|
|
z-index: 1;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
margin-top: 14px;
|
|
}
|
|
.history-hero-pill {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 5px 11px;
|
|
border-radius: 999px;
|
|
background: color-mix(in srgb, var(--pill-color) 16%, transparent);
|
|
color: var(--pill-color);
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
font-variant-numeric: tabular-nums;
|
|
letter-spacing: 0.1px;
|
|
}
|
|
.history-hero-pill-arrow { font-weight: 700; }
|
|
.history-hero-pill-label {
|
|
color: color-mix(in srgb, var(--pill-color) 75%, var(--color-text));
|
|
opacity: 0.7;
|
|
font-weight: 500;
|
|
margin-left: 2px;
|
|
letter-spacing: 0.2px;
|
|
}
|
|
|
|
/* === History extreme cards — clickable, opens centred station popup === */
|
|
.history-extreme-row {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 8px;
|
|
}
|
|
.history-extreme-card {
|
|
position: relative;
|
|
background: var(--color-bg-secondary);
|
|
border: 1px solid var(--color-separator);
|
|
border-radius: 14px;
|
|
padding: 12px 14px 12px 18px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
text-align: left;
|
|
cursor: pointer;
|
|
font-family: inherit;
|
|
color: var(--color-text);
|
|
overflow: hidden;
|
|
-webkit-tap-highlight-color: transparent;
|
|
transition: transform 0.18s cubic-bezier(0.2, 0, 0, 1),
|
|
box-shadow 0.2s ease,
|
|
border-color 0.2s ease;
|
|
}
|
|
.history-extreme-card::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: 0;
|
|
top: 14%;
|
|
bottom: 14%;
|
|
width: 3px;
|
|
border-radius: 0 2px 2px 0;
|
|
background: var(--accent);
|
|
}
|
|
.history-extreme-card:hover {
|
|
border-color: color-mix(in srgb, var(--accent) 35%, var(--color-separator));
|
|
box-shadow: 0 4px 14px color-mix(in srgb, var(--accent) 12%, transparent);
|
|
}
|
|
.history-extreme-card:active {
|
|
transform: scale(0.98);
|
|
}
|
|
.history-extreme-card.is-empty {
|
|
cursor: default;
|
|
opacity: 0.55;
|
|
}
|
|
.history-extreme-card.is-empty:hover {
|
|
border-color: var(--color-separator);
|
|
box-shadow: none;
|
|
}
|
|
.history-extreme-card.is-empty:active {
|
|
transform: none;
|
|
}
|
|
.history-extreme-icon {
|
|
flex-shrink: 0;
|
|
width: 34px;
|
|
height: 34px;
|
|
border-radius: 10px;
|
|
background: color-mix(in srgb, var(--accent) 16%, transparent);
|
|
color: var(--accent);
|
|
display: grid;
|
|
place-items: center;
|
|
}
|
|
.history-extreme-body {
|
|
min-width: 0;
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1px;
|
|
overflow: hidden;
|
|
}
|
|
.history-extreme-label {
|
|
font-size: 10px;
|
|
color: var(--color-hint);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
font-weight: 600;
|
|
}
|
|
.history-extreme-value {
|
|
font-size: 18px;
|
|
font-weight: 800;
|
|
color: var(--accent);
|
|
letter-spacing: -0.02em;
|
|
font-variant-numeric: tabular-nums;
|
|
line-height: 1.15;
|
|
}
|
|
.history-extreme-station {
|
|
font-size: 12px;
|
|
color: var(--color-hint);
|
|
line-height: 1.25;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
max-width: 100%;
|
|
}
|
|
|
|
/* === History chart custom tooltip === */
|
|
.history-tooltip {
|
|
position: absolute;
|
|
pointer-events: none;
|
|
z-index: 99997;
|
|
background: rgba(20, 20, 20, 0.94);
|
|
border-radius: 12px;
|
|
padding: 10px 14px;
|
|
transform: translate(-50%, calc(-100% - 14px)) scale(0.94);
|
|
transform-origin: bottom center;
|
|
opacity: 0;
|
|
transition: opacity 0.16s ease, transform 0.18s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
box-shadow: 0 8px 26px rgba(0, 0, 0, 0.28);
|
|
white-space: nowrap;
|
|
font-family: -apple-system, BlinkMacSystemFont, Roboto, sans-serif;
|
|
text-align: center;
|
|
min-width: 130px;
|
|
}
|
|
.history-tooltip.show {
|
|
opacity: 1;
|
|
transform: translate(-50%, calc(-100% - 14px)) scale(1);
|
|
}
|
|
.history-tooltip-time {
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
color: rgba(255, 255, 255, 0.62);
|
|
letter-spacing: 0.3px;
|
|
margin-bottom: 2px;
|
|
}
|
|
.history-tooltip-price {
|
|
font-size: 18px;
|
|
font-weight: 800;
|
|
letter-spacing: -0.02em;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.history-tooltip-meta {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 12px;
|
|
margin-top: 5px;
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
color: rgba(255, 255, 255, 0.78);
|
|
font-variant-numeric: tabular-nums;
|
|
letter-spacing: 0.1px;
|
|
}
|
|
.history-tooltip-hint {
|
|
margin-top: 6px;
|
|
padding-top: 6px;
|
|
border-top: 1px solid rgba(255, 255, 255, 0.14);
|
|
font-size: 10px;
|
|
color: rgba(255, 255, 255, 0.55);
|
|
letter-spacing: 0.2px;
|
|
}
|
|
|
|
/* Hour chart section header — drilled-down day + close button */
|
|
#hour-chart-label {
|
|
display: flex !important;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 8px;
|
|
}
|
|
.hour-chart-day {
|
|
flex: 1;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
.hour-chart-close {
|
|
flex-shrink: 0;
|
|
border: none;
|
|
background: color-mix(in srgb, var(--color-text) 8%, transparent);
|
|
color: var(--color-hint);
|
|
width: 24px;
|
|
height: 24px;
|
|
border-radius: 50%;
|
|
display: grid;
|
|
place-items: center;
|
|
cursor: pointer;
|
|
padding: 0;
|
|
-webkit-tap-highlight-color: transparent;
|
|
transition: background 0.18s ease, color 0.18s ease;
|
|
}
|
|
.hour-chart-close:hover {
|
|
background: color-mix(in srgb, var(--color-text) 14%, transparent);
|
|
color: var(--color-text);
|
|
}
|
|
.hour-chart-close:active { transform: scale(0.92); }
|
|
|
|
/* Soft warm drop-shadow on both history canvases so they sit on the
|
|
page like the stats hour chart does. */
|
|
#price-chart, #hour-chart {
|
|
filter: drop-shadow(0 4px 14px rgba(255, 149, 0, 0.07));
|
|
}
|
|
|
|
/* === Hour line chart — custom HTML tooltip so the price text can pick
|
|
up the hovered hour's rank colour. === */
|
|
.stats-hour-tooltip {
|
|
position: absolute;
|
|
pointer-events: none;
|
|
z-index: 99997;
|
|
background: rgba(20, 20, 20, 0.94);
|
|
border-radius: 12px;
|
|
padding: 9px 14px;
|
|
transform: translate(-50%, calc(-100% - 14px)) scale(0.94);
|
|
transform-origin: bottom center;
|
|
opacity: 0;
|
|
transition: opacity 0.16s ease, transform 0.18s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
box-shadow: 0 8px 26px rgba(0, 0, 0, 0.28);
|
|
white-space: nowrap;
|
|
font-family: -apple-system, BlinkMacSystemFont, Roboto, sans-serif;
|
|
text-align: center;
|
|
}
|
|
.stats-hour-tooltip.show {
|
|
opacity: 1;
|
|
transform: translate(-50%, calc(-100% - 14px)) scale(1);
|
|
}
|
|
.stats-hour-tooltip-time {
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
color: rgba(255, 255, 255, 0.62);
|
|
letter-spacing: 0.3px;
|
|
margin-bottom: 2px;
|
|
}
|
|
.stats-hour-tooltip-price {
|
|
font-size: 17px;
|
|
font-weight: 800;
|
|
letter-spacing: -0.02em;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
/* === Hour line chart — Chart.js canvas wrapper === */
|
|
.stats-hour-chart-wrap {
|
|
position: relative;
|
|
height: 160px;
|
|
margin-bottom: 4px;
|
|
animation: stats-chart-enter 0.55s cubic-bezier(0.22, 1, 0.36, 1) both;
|
|
}
|
|
.stats-hour-chart-wrap canvas {
|
|
filter: drop-shadow(0 4px 14px rgba(255, 149, 0, 0.10));
|
|
}
|
|
@keyframes stats-chart-enter {
|
|
from { opacity: 0; transform: translateY(6px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.stats-hour-chart-wrap { animation: none; }
|
|
}
|
|
/* === Station ranking — coloured left accent stripe === */
|
|
.stats-station-list .station-ranking-item {
|
|
position: relative;
|
|
padding-left: calc(var(--sp-4) + 6px);
|
|
}
|
|
.stats-station-list .station-ranking-item::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: 6px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
width: 3px;
|
|
height: 60%;
|
|
border-radius: 2px;
|
|
background: var(--rank-color, var(--color-separator));
|
|
}
|
|
.stats-station-list .station-ranking-item.ranking-medal::before {
|
|
height: 70%;
|
|
width: 4px;
|
|
}
|
|
/* Small "Ø" mark in the ranking price so users understand the number is
|
|
the 30-day average rather than the current price. Hover-title row
|
|
carries min + sample count for the full picture. */
|
|
.ranking-price-avg-mark {
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
margin-right: 4px;
|
|
opacity: 0.65;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
|
|
@media (max-width: 360px) {
|
|
.stats-fact-value { font-size: 14px; }
|
|
.stats-fact-icon { width: 26px; height: 26px; border-radius: 8px; }
|
|
.stats-hero-value { font-size: 32px; }
|
|
}
|
|
|
|
.card-list .card-row:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.card-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: var(--sp-3);
|
|
position: relative;
|
|
cursor: pointer;
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
}
|
|
|
|
.card-row-left {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--sp-3);
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.account-avatar {
|
|
display: none;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.account-avatar.visible {
|
|
display: block;
|
|
}
|
|
|
|
.account-avatar-img {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 50%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.account-avatar-fallback {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 50%;
|
|
background: var(--color-tint, #007aff);
|
|
color: #fff;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-weight: 600;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.card-icon {
|
|
font-size: 20px;
|
|
flex-shrink: 0;
|
|
width: 28px;
|
|
text-align: center;
|
|
}
|
|
|
|
.card-title {
|
|
font-size: 15px;
|
|
font-weight: 500;
|
|
color: var(--color-text);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.card-subtitle {
|
|
font-size: 13px;
|
|
color: var(--color-hint);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.card-value {
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: var(--color-text);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.card-value.good { color: var(--color-good); }
|
|
.card-value.okay { color: var(--color-okay); }
|
|
.card-value.bad { color: var(--color-bad); }
|
|
|
|
.card-action {
|
|
background: var(--color-accent);
|
|
color: var(--color-accent-text);
|
|
border: none;
|
|
border-radius: var(--r-sm);
|
|
padding: var(--sp-2) var(--sp-4);
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
flex-shrink: 0;
|
|
transition: transform 0.15s cubic-bezier(0.2, 0, 0, 1), opacity 0.15s ease;
|
|
}
|
|
.card-action:hover { opacity: 0.92; }
|
|
|
|
.card-action:active {
|
|
transform: scale(0.95);
|
|
}
|
|
|
|
/* Ripple effect */
|
|
.ripple {
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.ripple::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
width: 0;
|
|
height: 0;
|
|
border-radius: 50%;
|
|
background: var(--color-hint);
|
|
transform: translate(-50%, -50%);
|
|
opacity: 0;
|
|
}
|
|
|
|
.ripple:active::after {
|
|
width: 200%;
|
|
height: 200%;
|
|
opacity: 0.15;
|
|
transition: width 0.3s, height 0.3s, opacity 0.3s;
|
|
}
|
|
|
|
/* === Chips === */
|
|
.chip-row {
|
|
display: flex;
|
|
gap: var(--sp-2);
|
|
flex-wrap: wrap;
|
|
padding-bottom: var(--sp-1);
|
|
}
|
|
|
|
.chip {
|
|
background: var(--color-bg-secondary);
|
|
color: var(--color-text);
|
|
border: none;
|
|
border-radius: var(--r-pill);
|
|
padding: var(--sp-2) var(--sp-4);
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
transition: background 0.15s, color 0.15s, transform 0.1s, box-shadow 0.15s;
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.chip:active {
|
|
transform: scale(0.95);
|
|
}
|
|
|
|
.chip.active {
|
|
background: var(--color-accent);
|
|
color: var(--color-accent-text);
|
|
box-shadow: var(--shadow-1);
|
|
}
|
|
|
|
.country-chip-row {
|
|
margin-bottom: var(--sp-3);
|
|
}
|
|
|
|
.country-chip-row .chip {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: var(--sp-2);
|
|
}
|
|
|
|
.country-flag {
|
|
display: inline-block;
|
|
flex-shrink: 0;
|
|
border-radius: 1.5px;
|
|
box-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.15);
|
|
vertical-align: middle;
|
|
}
|
|
|
|
/* === Map === */
|
|
#map-container {
|
|
position: relative;
|
|
height: 55vh;
|
|
min-height: 250px;
|
|
}
|
|
|
|
#map-container::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: calc(env(safe-area-inset-top, 0px) + 56px);
|
|
background: #000;
|
|
z-index: 2;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.top-cta-section {
|
|
margin-top: 12px;
|
|
}
|
|
|
|
#map {
|
|
width: 100%;
|
|
height: 100%;
|
|
z-index: 1;
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
-webkit-touch-callout: none;
|
|
}
|
|
|
|
.map-overlay {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: var(--color-bg);
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 12px;
|
|
z-index: 10;
|
|
color: var(--color-hint);
|
|
font-size: 14px;
|
|
transition: opacity 0.3s;
|
|
}
|
|
|
|
.map-overlay.hidden {
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* === Map Search Bar === */
|
|
#map-search-wrapper {
|
|
position: absolute;
|
|
top: 12px;
|
|
left: 12px;
|
|
right: 12px;
|
|
z-index: 1000;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
pointer-events: none;
|
|
}
|
|
#map-search-wrapper > * {
|
|
pointer-events: auto;
|
|
}
|
|
|
|
/* Top row holds the search box (grows) and the "Hier suchen" pill (right,
|
|
only visible after the map has moved). When the pill is hidden the box
|
|
takes the full width on its own. */
|
|
#map-search-top-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--sp-2);
|
|
}
|
|
/* Scan-picker mode hides the search box — keep the confirm/cancel pills
|
|
centred in the row instead of stuck to the left. */
|
|
#map-search-top-row:has(#map-search-box.hidden) {
|
|
justify-content: center;
|
|
}
|
|
|
|
#map-search-box {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
background: var(--color-bg);
|
|
border-radius: 12px;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
|
padding: 0 12px;
|
|
height: 44px;
|
|
gap: 8px;
|
|
}
|
|
|
|
.map-search-icon {
|
|
color: var(--color-hint);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
#map-search-input {
|
|
flex: 1;
|
|
border: none;
|
|
background: transparent;
|
|
color: var(--color-text);
|
|
font-size: 15px;
|
|
outline: none;
|
|
min-width: 0;
|
|
}
|
|
|
|
#map-search-input::placeholder {
|
|
color: var(--color-hint);
|
|
}
|
|
|
|
.map-search-clear {
|
|
border: none;
|
|
background: none;
|
|
color: var(--color-hint);
|
|
font-size: 20px;
|
|
cursor: pointer;
|
|
line-height: 1;
|
|
flex-shrink: 0;
|
|
width: 32px;
|
|
height: 32px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 50%;
|
|
transition: background 0.15s;
|
|
}
|
|
.map-search-clear:hover,
|
|
.map-search-clear:focus-visible {
|
|
background: var(--color-bg-secondary);
|
|
}
|
|
|
|
.map-search-clear.hidden {
|
|
display: none;
|
|
}
|
|
|
|
#map-search-results {
|
|
margin-top: 4px;
|
|
background: var(--color-bg);
|
|
border-radius: 12px;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
|
overflow: hidden;
|
|
max-height: 240px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
#map-search-results.hidden {
|
|
display: none;
|
|
}
|
|
|
|
.map-search-result-item {
|
|
padding: 12px 14px;
|
|
font-size: 14px;
|
|
color: var(--color-text);
|
|
cursor: pointer;
|
|
border-bottom: 1px solid var(--color-separator);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
.map-search-result-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.map-search-result-item:active {
|
|
background: var(--color-bg-secondary);
|
|
}
|
|
|
|
.map-search-station-item {
|
|
gap: 12px;
|
|
}
|
|
|
|
.map-search-station-rank {
|
|
width: 26px;
|
|
height: 26px;
|
|
border-radius: 8px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
color: #fff;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.map-search-station-info {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
}
|
|
|
|
.map-search-station-name {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: var(--color-text);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.map-search-station-addr {
|
|
font-size: 12px;
|
|
color: var(--color-hint);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.map-search-station-price {
|
|
font-size: 16px;
|
|
font-weight: 700;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* ─── Route planner chip + panel ────────────────────────────── */
|
|
#map-route-chip-row {
|
|
display: flex;
|
|
justify-content: flex-start;
|
|
margin-top: 6px;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.map-route-chip {
|
|
pointer-events: auto;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
background: var(--color-bg);
|
|
border: none;
|
|
border-radius: 999px;
|
|
padding: 6px 12px;
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
color: var(--color-text);
|
|
cursor: pointer;
|
|
box-shadow: 0 2px 6px rgba(0,0,0,0.12);
|
|
transition: transform 0.12s ease, background 0.15s;
|
|
}
|
|
.map-route-chip:hover { background: var(--color-bg-secondary); }
|
|
.map-route-chip:active { transform: scale(0.96); }
|
|
.map-route-chip[aria-expanded="true"] {
|
|
background: var(--color-accent);
|
|
color: #fff;
|
|
}
|
|
.map-route-chip[aria-expanded="true"] svg { color: #fff; }
|
|
|
|
.map-route-panel {
|
|
margin-top: 6px;
|
|
background: var(--color-bg);
|
|
border-radius: 12px;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
|
padding: 10px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
}
|
|
.map-route-panel.hidden { display: none; }
|
|
|
|
.map-route-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
.map-route-dot {
|
|
flex-shrink: 0;
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 50%;
|
|
}
|
|
.map-route-dot-start { background: var(--color-accent); }
|
|
.map-route-dot-dest {
|
|
background: transparent;
|
|
border: 2px solid var(--color-text);
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.map-route-input-wrapper {
|
|
flex: 1;
|
|
position: relative;
|
|
min-width: 0;
|
|
}
|
|
|
|
.map-route-input {
|
|
width: 100%;
|
|
border: 1px solid var(--color-separator);
|
|
background: var(--color-bg);
|
|
color: var(--color-text);
|
|
border-radius: 8px;
|
|
padding: 8px 10px;
|
|
font-size: 14px;
|
|
outline: none;
|
|
}
|
|
.map-route-input:focus {
|
|
border-color: var(--color-accent);
|
|
}
|
|
|
|
.map-route-suggest {
|
|
position: absolute;
|
|
top: calc(100% + 2px);
|
|
left: 0;
|
|
right: 0;
|
|
z-index: 1001;
|
|
background: var(--color-bg);
|
|
border-radius: 10px;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
|
overflow: hidden;
|
|
max-height: 200px;
|
|
overflow-y: auto;
|
|
}
|
|
.map-route-suggest.hidden { display: none; }
|
|
|
|
.map-route-suggest-item {
|
|
padding: 10px 12px;
|
|
font-size: 13px;
|
|
color: var(--color-text);
|
|
cursor: pointer;
|
|
border-bottom: 1px solid var(--color-separator);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
.map-route-suggest-item:last-child { border-bottom: none; }
|
|
.map-route-suggest-item:active { background: var(--color-bg-secondary); }
|
|
.map-route-suggest-item.is-current-location {
|
|
color: var(--color-accent);
|
|
font-weight: 500;
|
|
}
|
|
|
|
.map-route-actions {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 8px;
|
|
margin-top: 2px;
|
|
}
|
|
.map-route-close {
|
|
background: none;
|
|
border: none;
|
|
color: var(--color-hint);
|
|
font-size: 13px;
|
|
cursor: pointer;
|
|
padding: 6px 10px;
|
|
border-radius: 6px;
|
|
}
|
|
.map-route-close:hover { background: var(--color-bg-secondary); }
|
|
|
|
.map-route-go {
|
|
background: var(--color-accent);
|
|
color: #fff;
|
|
border: none;
|
|
border-radius: 8px;
|
|
padding: 7px 14px;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
}
|
|
.map-route-go[disabled] {
|
|
opacity: 0.4;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.map-route-summary {
|
|
font-size: 12px;
|
|
color: var(--color-hint);
|
|
padding: 4px 2px 0;
|
|
}
|
|
.map-route-summary.hidden { display: none; }
|
|
|
|
.route-exit-btn {
|
|
position: absolute;
|
|
top: calc(12px + 44px + 6px);
|
|
right: 12px;
|
|
z-index: 1001;
|
|
background: var(--color-bg);
|
|
border: none;
|
|
border-radius: 999px;
|
|
padding: 6px 12px;
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
color: var(--color-text);
|
|
cursor: pointer;
|
|
box-shadow: 0 2px 6px rgba(0,0,0,0.15);
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
}
|
|
.route-exit-btn.hidden { display: none; }
|
|
|
|
/* ── Route scan dots ─────────────────────────────────────────────── */
|
|
/* Yellow markers that appear along the route where live scans will run. */
|
|
.route-scan-dot {
|
|
pointer-events: none;
|
|
}
|
|
.route-scan-dot-inner {
|
|
width: 18px;
|
|
height: 18px;
|
|
border-radius: 50%;
|
|
background: #f5c518;
|
|
border: 2px solid #fff;
|
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35);
|
|
transition: background 0.3s ease, transform 0.3s ease, opacity 0.3s ease;
|
|
}
|
|
.route-scan-dot.pending .route-scan-dot-inner {
|
|
animation: route-scan-pulse 1.6s ease-in-out infinite;
|
|
}
|
|
.route-scan-dot.scanning .route-scan-dot-inner {
|
|
animation: route-scan-pulse 0.7s ease-in-out infinite;
|
|
background: #ffd84d;
|
|
}
|
|
.route-scan-dot.done .route-scan-dot-inner {
|
|
background: #4caf50;
|
|
animation: none;
|
|
}
|
|
.route-scan-dot.empty .route-scan-dot-inner {
|
|
background: #9e9e9e;
|
|
animation: none;
|
|
opacity: 0.7;
|
|
}
|
|
.route-scan-dot.error .route-scan-dot-inner {
|
|
background: #f44336;
|
|
animation: none;
|
|
}
|
|
@keyframes route-scan-pulse {
|
|
0%, 100% { transform: scale(1); opacity: 1; }
|
|
50% { transform: scale(1.35); opacity: 0.75; }
|
|
}
|
|
|
|
#map-search-box.hidden { display: none; }
|
|
|
|
.map-search-no-results {
|
|
padding: 14px;
|
|
font-size: 13px;
|
|
color: var(--color-hint);
|
|
text-align: center;
|
|
}
|
|
|
|
/* === Map Location Banner === */
|
|
#map-location-banner {
|
|
position: absolute;
|
|
top: 64px;
|
|
right: 12px;
|
|
z-index: 1000;
|
|
background: rgba(255, 204, 0, 0.75);
|
|
backdrop-filter: blur(10px);
|
|
-webkit-backdrop-filter: blur(10px);
|
|
color: #5a4500;
|
|
border-radius: 10px;
|
|
padding: 8px 12px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
max-width: 220px;
|
|
}
|
|
|
|
#map-location-banner.hidden {
|
|
display: none;
|
|
}
|
|
|
|
#map-location-banner-close {
|
|
border: none;
|
|
background: none;
|
|
color: #5a4500;
|
|
font-size: 18px;
|
|
line-height: 1;
|
|
cursor: pointer;
|
|
flex-shrink: 0;
|
|
opacity: 0.7;
|
|
width: 28px;
|
|
height: 28px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 50%;
|
|
transition: opacity 0.15s, background 0.15s;
|
|
}
|
|
|
|
#map-location-banner-close:hover,
|
|
#map-location-banner-close:focus-visible {
|
|
opacity: 1;
|
|
background: rgba(0, 0, 0, 0.08);
|
|
}
|
|
|
|
#map-location-banner-close:active {
|
|
opacity: 1;
|
|
}
|
|
|
|
.spinner {
|
|
width: 28px;
|
|
height: 28px;
|
|
border: 3px solid var(--color-bg-secondary);
|
|
border-top-color: var(--color-accent);
|
|
border-radius: 50%;
|
|
animation: spin 0.8s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
|
|
@keyframes slide-in-up {
|
|
from { opacity: 0; transform: translateY(14px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
.station-item.anim-in,
|
|
.ranking-item.anim-in,
|
|
.stat-card.anim-in,
|
|
.metric-card.anim-in,
|
|
.best-time-card.anim-in,
|
|
.stats-hero-card.anim-in,
|
|
.stats-fact-card.anim-in,
|
|
.stats-tile.anim-in,
|
|
.history-hero-card.anim-in,
|
|
.history-extreme-card.anim-in {
|
|
animation: slide-in-up 0.42s cubic-bezier(0.22, 1, 0.36, 1) both;
|
|
}
|
|
|
|
/* Hour bars grow via their inner fill (height transition driven by JS),
|
|
no scaleY animation on the column container — that would squash the
|
|
layout since the cell now anchors content to the bottom. */
|
|
|
|
/* === Confetti burst — decorative click effect === */
|
|
.confetti-burst {
|
|
position: fixed;
|
|
inset: 0;
|
|
pointer-events: none;
|
|
z-index: 99999;
|
|
overflow: visible;
|
|
}
|
|
.confetti-piece {
|
|
position: absolute;
|
|
will-change: transform, opacity;
|
|
pointer-events: none;
|
|
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.18));
|
|
}
|
|
.confetti-trigger {
|
|
cursor: pointer;
|
|
user-select: none;
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
.confetti-trigger.stats-hero-glyph,
|
|
.confetti-trigger.stats-fact-icon,
|
|
.confetti-trigger.best-time-icon-wrap,
|
|
.confetti-trigger.about-heart,
|
|
.confetti-trigger.about-app-icon {
|
|
transition: transform 0.15s cubic-bezier(0.34, 1.4, 0.64, 1);
|
|
}
|
|
.confetti-trigger.stats-hero-glyph:hover,
|
|
.confetti-trigger.stats-fact-icon:hover,
|
|
.confetti-trigger.best-time-icon-wrap:hover,
|
|
.confetti-trigger.about-heart:hover,
|
|
.confetti-trigger.about-app-icon:hover {
|
|
transform: scale(1.08);
|
|
}
|
|
.about-heart {
|
|
display: inline-block;
|
|
cursor: pointer;
|
|
user-select: none;
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
.about-app-icon {
|
|
cursor: pointer;
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
|
|
/* === Floating value chip — fades in above tiny tap targets to surface
|
|
their label (hour heatmap cells, weekday tiles, …) === */
|
|
.floating-value {
|
|
position: fixed;
|
|
pointer-events: none;
|
|
z-index: 99998;
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
padding: 5px 10px;
|
|
border-radius: 999px;
|
|
background: var(--color-bg-secondary);
|
|
border: 1px solid currentColor;
|
|
font-variant-numeric: tabular-nums;
|
|
opacity: 0;
|
|
transform: translate(-50%, calc(-100% - 14px)) translateY(8px) scale(0.9);
|
|
transition: transform 0.28s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.2s ease;
|
|
box-shadow: 0 4px 18px rgba(0, 0, 0, 0.18);
|
|
white-space: nowrap;
|
|
}
|
|
.floating-value.show {
|
|
opacity: 1;
|
|
transform: translate(-50%, calc(-100% - 14px)) translateY(0) scale(1);
|
|
}
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.floating-value {
|
|
transition: opacity 0.15s linear;
|
|
transform: translate(-50%, calc(-100% - 14px));
|
|
}
|
|
.floating-value.show {
|
|
transform: translate(-50%, calc(-100% - 14px));
|
|
}
|
|
}
|
|
@keyframes confetti-pop {
|
|
0% { transform: scale(1); }
|
|
45% { transform: scale(1.22); }
|
|
100% { transform: scale(1); }
|
|
}
|
|
.confetti-pop {
|
|
animation: confetti-pop 0.42s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
}
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.confetti-burst { display: none; }
|
|
.confetti-pop { animation: none; }
|
|
}
|
|
|
|
/* === Skeleton Loading === */
|
|
@keyframes shimmer {
|
|
0% { background-position: -200% 0; }
|
|
100% { background-position: 200% 0; }
|
|
}
|
|
|
|
.skeleton-item {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 14px 16px;
|
|
gap: 12px;
|
|
border-bottom: 0.5px solid var(--color-separator);
|
|
}
|
|
|
|
.skeleton-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.skeleton-bone {
|
|
background: linear-gradient(90deg, var(--color-bg-secondary) 25%, var(--color-separator) 50%, var(--color-bg-secondary) 75%);
|
|
background-size: 200% 100%;
|
|
animation: shimmer 1.5s ease-in-out infinite;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
.skeleton-rank {
|
|
width: 28px;
|
|
height: 28px;
|
|
border-radius: 8px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.skeleton-text-long {
|
|
height: 14px;
|
|
width: 70%;
|
|
margin-bottom: 6px;
|
|
}
|
|
|
|
.skeleton-text-short {
|
|
height: 12px;
|
|
width: 45%;
|
|
}
|
|
|
|
.skeleton-price {
|
|
width: 52px;
|
|
height: 20px;
|
|
margin-left: auto;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* Fills the history price-chart box while the first fetch is in flight.
|
|
Sits over the (still-blank) canvas and is removed once the chart draws. */
|
|
.chart-skeleton {
|
|
position: absolute;
|
|
inset: var(--sp-4) var(--sp-2) var(--sp-2);
|
|
border-radius: 10px;
|
|
}
|
|
|
|
/* === Station Sort === */
|
|
.station-sort-bar {
|
|
display: grid;
|
|
grid-template-columns: 1fr auto 1fr;
|
|
align-items: center;
|
|
padding: 12px 16px 4px;
|
|
gap: 8px;
|
|
}
|
|
|
|
.station-sort-actions {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
justify-self: start;
|
|
}
|
|
|
|
.station-sort-label {
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--color-hint);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.3px;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
justify-self: center;
|
|
text-align: center;
|
|
}
|
|
|
|
.station-sort-label.is-route {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
line-height: 1.15;
|
|
white-space: normal;
|
|
overflow: visible;
|
|
text-align: center;
|
|
}
|
|
|
|
.station-sort-label.is-route .station-count-sub {
|
|
font-size: 10px;
|
|
font-weight: 500;
|
|
letter-spacing: 0.2px;
|
|
opacity: 0.85;
|
|
margin-top: 2px;
|
|
}
|
|
|
|
.station-sort-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 6px 12px;
|
|
border: none;
|
|
border-radius: 8px;
|
|
background: var(--color-bg-secondary);
|
|
color: var(--color-text);
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
white-space: nowrap;
|
|
transition: opacity 0.15s, transform 0.15s cubic-bezier(0.2, 0, 0, 1);
|
|
flex-shrink: 0;
|
|
justify-self: end;
|
|
}
|
|
|
|
.station-sort-btn:active {
|
|
transform: scale(0.95);
|
|
}
|
|
|
|
.station-sort-btn svg {
|
|
flex-shrink: 0;
|
|
color: var(--color-accent);
|
|
}
|
|
|
|
/* === Station List (below map) === */
|
|
.station-list {
|
|
padding: var(--sp-2) 0;
|
|
margin: 0 var(--sp-4);
|
|
background: var(--color-bg-secondary);
|
|
border-radius: var(--r-md);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.station-item {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: var(--sp-3) var(--sp-4);
|
|
gap: var(--sp-3);
|
|
-webkit-tap-highlight-color: transparent;
|
|
transition: transform 0.15s cubic-bezier(0.2, 0, 0, 1);
|
|
position: relative;
|
|
border-bottom: 0.5px solid var(--color-separator);
|
|
}
|
|
|
|
.station-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.station-item:active {
|
|
transform: scale(0.97);
|
|
}
|
|
|
|
.station-rank {
|
|
width: 28px;
|
|
height: 28px;
|
|
border-radius: 8px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 13px;
|
|
font-weight: 700;
|
|
color: #fff;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.station-info {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.station-name {
|
|
font-size: 15px;
|
|
font-weight: 500;
|
|
color: var(--color-text);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.station-addr {
|
|
font-size: 13px;
|
|
color: var(--color-hint);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.station-price {
|
|
font-size: 20px;
|
|
font-weight: 700;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.station-price sup {
|
|
font-size: 13px;
|
|
vertical-align: super;
|
|
}
|
|
|
|
.fav-btn {
|
|
background: none;
|
|
border: none;
|
|
cursor: pointer;
|
|
padding: 6px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
|
|
.fav-btn {
|
|
position: relative;
|
|
overflow: visible;
|
|
}
|
|
|
|
.fav-btn svg {
|
|
transition: transform 0.3s cubic-bezier(0.2, 0, 0, 1);
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.fav-btn:active svg {
|
|
transform: scale(0.8);
|
|
}
|
|
|
|
.fav-btn svg path {
|
|
fill: none;
|
|
stroke: var(--color-hint);
|
|
stroke-width: 1.5;
|
|
transition: fill 0.25s, stroke 0.25s;
|
|
}
|
|
|
|
.fav-btn.active svg path {
|
|
fill: var(--color-favorite);
|
|
stroke: var(--color-favorite);
|
|
}
|
|
|
|
/* Sparkle particles container */
|
|
.fav-sparkles {
|
|
position: absolute;
|
|
inset: -8px;
|
|
pointer-events: none;
|
|
z-index: 0;
|
|
}
|
|
|
|
.fav-sparkle {
|
|
position: absolute;
|
|
width: 4px;
|
|
height: 4px;
|
|
border-radius: 50%;
|
|
top: 50%;
|
|
left: 50%;
|
|
opacity: 0;
|
|
}
|
|
|
|
.fav-btn.anim-pop svg {
|
|
animation: fav-sparkle-star 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
|
}
|
|
|
|
.fav-btn.anim-pop .fav-sparkle:nth-child(1) { animation: fav-spark-out 0.55s 0.05s ease-out forwards; --spark-x: -14px; --spark-y: -12px; background: var(--color-favorite); }
|
|
.fav-btn.anim-pop .fav-sparkle:nth-child(2) { animation: fav-spark-out 0.5s 0.08s ease-out forwards; --spark-x: 12px; --spark-y: -14px; background: #ffcc4d; }
|
|
.fav-btn.anim-pop .fav-sparkle:nth-child(3) { animation: fav-spark-out 0.6s 0.03s ease-out forwards; --spark-x: 15px; --spark-y: 4px; background: var(--color-favorite); }
|
|
.fav-btn.anim-pop .fav-sparkle:nth-child(4) { animation: fav-spark-out 0.5s 0.1s ease-out forwards; --spark-x: -12px; --spark-y: 10px; background: #ffcc4d; }
|
|
.fav-btn.anim-pop .fav-sparkle:nth-child(5) { animation: fav-spark-out 0.55s 0.06s ease-out forwards; --spark-x: 4px; --spark-y: 15px; background: #ffd983; }
|
|
.fav-btn.anim-pop .fav-sparkle:nth-child(6) { animation: fav-spark-out 0.5s 0.12s ease-out forwards; --spark-x: -16px; --spark-y: -2px; background: #ffd983; }
|
|
|
|
.fav-btn.anim-pop .fav-sparkle-ring {
|
|
animation: fav-ring-burst 0.5s 0.02s ease-out forwards;
|
|
}
|
|
|
|
.fav-sparkle-ring {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
width: 0;
|
|
height: 0;
|
|
border-radius: 50%;
|
|
border: 2px solid var(--color-favorite);
|
|
transform: translate(-50%, -50%);
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
@keyframes fav-sparkle-star {
|
|
0% { transform: scale(0.3); }
|
|
30% { transform: scale(1.2); }
|
|
60% { transform: scale(0.9); }
|
|
100% { transform: scale(1); }
|
|
}
|
|
|
|
@keyframes fav-spark-out {
|
|
0% { opacity: 1; transform: translate(0, 0) scale(1); }
|
|
50% { opacity: 1; transform: translate(var(--spark-x), var(--spark-y)) scale(1.2); }
|
|
100% { opacity: 0; transform: translate(calc(var(--spark-x) * 1.3), calc(var(--spark-y) * 1.3)) scale(0); }
|
|
}
|
|
|
|
@keyframes fav-ring-burst {
|
|
0% { width: 0; height: 0; opacity: 0.8; border-width: 3px; }
|
|
60% { opacity: 0.4; border-width: 1.5px; }
|
|
100% { width: 36px; height: 36px; opacity: 0; border-width: 0.5px; }
|
|
}
|
|
|
|
.fav-btn.anim-unpop svg {
|
|
animation: fav-unpop 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
}
|
|
|
|
@keyframes fav-unpop {
|
|
0% { transform: scale(1); }
|
|
40% { transform: scale(0.7); }
|
|
100% { transform: scale(1); }
|
|
}
|
|
|
|
.station-dist {
|
|
font-size: 12px;
|
|
color: var(--color-hint);
|
|
text-align: right;
|
|
}
|
|
|
|
/* Toolbar toggles (favourites pin + group-by-price) sit in the sort bar
|
|
and match the sort button's chip-style theming so the row reads as a
|
|
cohesive control strip. Inactive sits on the secondary surface, active
|
|
fills with the accent and flips the icon to accent-text. */
|
|
.station-fav-btn {
|
|
background: var(--color-bg-secondary);
|
|
border: none;
|
|
padding: 7px;
|
|
cursor: pointer;
|
|
color: var(--color-hint);
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 8px;
|
|
transition: color 0.15s, background 0.15s, transform 0.15s cubic-bezier(0.2, 0, 0, 1);
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
.station-fav-btn:hover {
|
|
background: var(--color-separator);
|
|
color: var(--color-text);
|
|
}
|
|
.station-fav-btn:active { transform: scale(0.94); }
|
|
.station-fav-btn.active {
|
|
background: var(--color-accent);
|
|
color: var(--color-accent-text);
|
|
}
|
|
.station-fav-btn.active:hover {
|
|
background: var(--color-accent);
|
|
color: var(--color-accent-text);
|
|
filter: brightness(1.05);
|
|
}
|
|
|
|
/* Cloud-sync indicator sits right beside the toolbar toggles. Only visible
|
|
during the syncing → synced burst, then fades back out — idle has no
|
|
point taking permanent visual space. */
|
|
body.logged-in .station-fav-sync {
|
|
display: inline-flex;
|
|
opacity: 0;
|
|
transition: opacity 0.25s ease;
|
|
pointer-events: none;
|
|
}
|
|
body.logged-in .station-fav-sync.syncing,
|
|
body.logged-in .station-fav-sync.synced {
|
|
opacity: 1;
|
|
pointer-events: auto;
|
|
}
|
|
|
|
/* === Chart === */
|
|
.chart-container {
|
|
background: var(--color-bg-secondary);
|
|
border: 1px solid var(--color-separator);
|
|
border-radius: var(--r-md);
|
|
padding: var(--sp-4) var(--sp-2) var(--sp-2);
|
|
height: 280px;
|
|
position: relative;
|
|
}
|
|
|
|
.chart-container canvas {
|
|
width: 100% !important;
|
|
height: 100% !important;
|
|
}
|
|
|
|
/* === Bottom Sheet === */
|
|
.bottom-sheet {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
z-index: 1000;
|
|
transition: visibility 0.3s;
|
|
}
|
|
|
|
.bottom-sheet.hidden {
|
|
visibility: hidden;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.bottom-sheet-backdrop {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: rgba(0,0,0,0.4);
|
|
opacity: 0;
|
|
transition: opacity 0.3s;
|
|
}
|
|
|
|
.bottom-sheet:not(.hidden) .bottom-sheet-backdrop {
|
|
opacity: 1;
|
|
}
|
|
|
|
.bottom-sheet-content {
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
background: var(--color-bg);
|
|
border-radius: 16px 16px 0 0;
|
|
max-height: 85vh;
|
|
overflow-y: auto;
|
|
-webkit-overflow-scrolling: touch;
|
|
overscroll-behavior: contain;
|
|
touch-action: pan-y;
|
|
transform: translateY(100%);
|
|
transition: transform 0.35s cubic-bezier(0.32, 0.72, 0, 1),
|
|
max-height 0.35s cubic-bezier(0.32, 0.72, 0, 1);
|
|
padding-bottom: var(--safe-bottom);
|
|
}
|
|
|
|
.bottom-sheet:not(.hidden) .bottom-sheet-content {
|
|
transform: translateY(0);
|
|
}
|
|
|
|
.bottom-sheet-content.expanded {
|
|
max-height: 95vh;
|
|
border-radius: 8px 8px 0 0;
|
|
}
|
|
|
|
.bottom-sheet-content.dragging {
|
|
transition: none !important;
|
|
overflow-y: hidden;
|
|
}
|
|
|
|
.bottom-sheet-content.snapping {
|
|
transition: transform 0.35s cubic-bezier(0.32, 0.72, 0, 1) !important;
|
|
}
|
|
|
|
.bottom-sheet-handle-area {
|
|
padding: 10px 0 6px;
|
|
cursor: grab;
|
|
touch-action: none;
|
|
}
|
|
|
|
.bottom-sheet-handle-area:active {
|
|
cursor: grabbing;
|
|
}
|
|
|
|
.bottom-sheet-handle {
|
|
width: 36px;
|
|
height: 4px;
|
|
border-radius: 2px;
|
|
background: var(--color-hint);
|
|
margin: 0 auto;
|
|
opacity: 0.5;
|
|
}
|
|
|
|
/* === Station detail modal — used when the user opens a station from
|
|
the Stats Tankstellen-Ranking or the Verlauf cheapest/most-expensive
|
|
cards. Same internal layout as the map bottom-sheet, but presented
|
|
as a floating card in the middle of the screen instead of a
|
|
drag-to-dismiss sheet glued to the bottom edge. */
|
|
.bottom-sheet.detail-modal {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 24px;
|
|
box-sizing: border-box;
|
|
}
|
|
.bottom-sheet.detail-modal .bottom-sheet-backdrop {
|
|
background: rgba(0, 0, 0, 0.55);
|
|
backdrop-filter: blur(6px);
|
|
-webkit-backdrop-filter: blur(6px);
|
|
}
|
|
.bottom-sheet.detail-modal .bottom-sheet-content {
|
|
position: relative;
|
|
bottom: auto;
|
|
left: auto;
|
|
right: auto;
|
|
width: min(440px, 100%);
|
|
max-height: 86vh;
|
|
border-radius: 20px;
|
|
padding-bottom: 8px;
|
|
transform-origin: center center;
|
|
transform: scale(0.86);
|
|
opacity: 0;
|
|
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.32), 0 8px 24px rgba(0, 0, 0, 0.18);
|
|
transition: transform 0.28s cubic-bezier(0.22, 1, 0.36, 1),
|
|
opacity 0.2s ease;
|
|
}
|
|
.bottom-sheet.detail-modal:not(.hidden) .bottom-sheet-content {
|
|
transform: scale(1);
|
|
opacity: 1;
|
|
/* Tiny overshoot on the way in: scale up to 1.02 mid-flight, settle
|
|
back to 1. Soft "pop" without the awkward translate-up. */
|
|
animation: detail-modal-pop 0.36s cubic-bezier(0.22, 1, 0.36, 1) both;
|
|
}
|
|
@keyframes detail-modal-pop {
|
|
0% { transform: scale(0.86); opacity: 0; }
|
|
60% { transform: scale(1.02); opacity: 1; }
|
|
100% { transform: scale(1); opacity: 1; }
|
|
}
|
|
.bottom-sheet.detail-modal .bottom-sheet-handle-area {
|
|
display: none;
|
|
}
|
|
.bottom-sheet.detail-modal .sheet-expand-btn {
|
|
display: none;
|
|
}
|
|
.detail-modal-close {
|
|
position: absolute;
|
|
top: 12px;
|
|
right: 12px;
|
|
z-index: 2;
|
|
width: 32px;
|
|
height: 32px;
|
|
border-radius: 50%;
|
|
background: color-mix(in srgb, var(--color-text) 8%, transparent);
|
|
border: none;
|
|
color: var(--color-text);
|
|
cursor: pointer;
|
|
display: grid;
|
|
place-items: center;
|
|
transition: background 0.18s ease, transform 0.15s ease;
|
|
padding: 0;
|
|
}
|
|
.detail-modal-close:hover {
|
|
background: color-mix(in srgb, var(--color-text) 14%, transparent);
|
|
}
|
|
.detail-modal-close:active {
|
|
transform: scale(0.92);
|
|
}
|
|
|
|
/* Skeleton placeholders shown while we fetch the full detail data. */
|
|
.detail-skeleton {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
padding: 4px 20px 20px;
|
|
}
|
|
.detail-skeleton-line,
|
|
.detail-skeleton-block {
|
|
border-radius: 8px;
|
|
background: linear-gradient(
|
|
90deg,
|
|
color-mix(in srgb, var(--color-text) 8%, transparent) 0%,
|
|
color-mix(in srgb, var(--color-text) 14%, transparent) 50%,
|
|
color-mix(in srgb, var(--color-text) 8%, transparent) 100%
|
|
);
|
|
background-size: 200% 100%;
|
|
animation: detail-skeleton-shimmer 1.4s ease-in-out infinite;
|
|
}
|
|
.detail-skeleton-line {
|
|
height: 14px;
|
|
width: 100%;
|
|
}
|
|
.detail-skeleton-line.short {
|
|
width: 60%;
|
|
}
|
|
.detail-skeleton-line.detail-skeleton-price {
|
|
height: 36px;
|
|
width: 50%;
|
|
margin-bottom: 6px;
|
|
}
|
|
.detail-skeleton-block {
|
|
height: 140px;
|
|
margin-top: 8px;
|
|
}
|
|
@keyframes detail-skeleton-shimmer {
|
|
0% { background-position: 200% 0; }
|
|
100% { background-position: -200% 0; }
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.bottom-sheet.detail-modal .bottom-sheet-content,
|
|
.bottom-sheet.detail-modal:not(.hidden) .bottom-sheet-content {
|
|
transition: opacity 0.18s ease;
|
|
transform: none;
|
|
animation: none;
|
|
}
|
|
.detail-skeleton-line,
|
|
.detail-skeleton-block {
|
|
animation: none;
|
|
}
|
|
}
|
|
|
|
/* Sheet station detail */
|
|
.sheet-station-header {
|
|
padding: 12px 20px 8px;
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 8px;
|
|
}
|
|
|
|
.sheet-fav-btn {
|
|
margin-top: 2px;
|
|
}
|
|
|
|
.sheet-station-name {
|
|
font-size: 18px;
|
|
font-weight: 700;
|
|
color: var(--color-text);
|
|
}
|
|
|
|
.sheet-station-brand {
|
|
font-size: 14px;
|
|
color: var(--color-hint);
|
|
margin-top: 2px;
|
|
}
|
|
|
|
.sheet-station-price {
|
|
font-size: 36px;
|
|
font-weight: 800;
|
|
padding: 8px 20px;
|
|
}
|
|
|
|
.sheet-station-price sup {
|
|
font-size: 18px;
|
|
vertical-align: super;
|
|
}
|
|
|
|
.sheet-info-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 10px 20px;
|
|
font-size: 14px;
|
|
color: var(--color-text);
|
|
border-top: 0.5px solid var(--color-separator);
|
|
}
|
|
|
|
.sheet-info-icon {
|
|
font-size: 18px;
|
|
width: 24px;
|
|
text-align: center;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* Sheet opening hours */
|
|
.sheet-hours-section {
|
|
overflow: hidden;
|
|
}
|
|
|
|
.sheet-hours-toggle {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 10px 20px;
|
|
cursor: pointer;
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
font-size: 14px;
|
|
color: var(--color-text);
|
|
border-top: 0.5px solid var(--color-separator);
|
|
transition: background 0.15s;
|
|
}
|
|
|
|
.sheet-hours-toggle:active {
|
|
background: var(--color-bg-secondary);
|
|
}
|
|
|
|
.sheet-hours-chevron {
|
|
transition: transform 0.25s cubic-bezier(0.25, 0.1, 0.25, 1);
|
|
}
|
|
|
|
.sheet-hours-section.expanded .sheet-hours-chevron {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
.sheet-hours-list {
|
|
max-height: 0;
|
|
overflow: hidden;
|
|
transition: max-height 0.3s cubic-bezier(0.25, 0.1, 0.25, 1),
|
|
opacity 0.25s ease;
|
|
opacity: 0;
|
|
padding: 0 20px;
|
|
}
|
|
|
|
.sheet-hours-section.expanded .sheet-hours-list {
|
|
max-height: 300px;
|
|
opacity: 1;
|
|
padding: 0 20px 10px;
|
|
}
|
|
|
|
.sheet-hours-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
/* 24 px icon slot + 10 px gap = 34 px text indent, matching the toggle row. */
|
|
padding: 5px 0 5px 34px;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.sheet-hours-day {
|
|
color: var(--color-hint);
|
|
font-weight: 500;
|
|
}
|
|
|
|
.sheet-hours-time {
|
|
color: var(--color-text);
|
|
font-weight: 600;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
/* Sheet chart */
|
|
.sheet-chart-section {
|
|
padding: 0 20px 8px;
|
|
}
|
|
|
|
.sheet-chart-header-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 12px 0 8px;
|
|
}
|
|
|
|
.sheet-chart-header {
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
color: var(--color-hint);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
|
|
.sheet-chart-toggle {
|
|
display: flex;
|
|
gap: 4px;
|
|
background: var(--color-bg-secondary);
|
|
border-radius: 8px;
|
|
padding: 2px;
|
|
}
|
|
|
|
.sheet-toggle-btn {
|
|
border: none;
|
|
background: transparent;
|
|
color: var(--color-hint);
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
padding: 4px 10px;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.sheet-toggle-btn.active {
|
|
background: var(--color-accent);
|
|
color: #fff;
|
|
}
|
|
|
|
.sheet-toggle-btn.disabled {
|
|
opacity: 0.35;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.sheet-chart-container {
|
|
background: var(--color-bg-secondary);
|
|
border-radius: 12px;
|
|
padding: 12px 8px 8px;
|
|
height: 120px;
|
|
position: relative;
|
|
transition: height 0.35s cubic-bezier(0.32, 0.72, 0, 1);
|
|
}
|
|
|
|
.bottom-sheet-content.expanded .sheet-chart-container {
|
|
height: 28vh;
|
|
}
|
|
|
|
.sheet-chart-container canvas {
|
|
width: 100% !important;
|
|
height: 100% !important;
|
|
}
|
|
|
|
.sheet-chart-empty {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 100%;
|
|
color: var(--color-hint);
|
|
font-size: 13px;
|
|
}
|
|
|
|
/* Friendly empty-state for German stations that aren't in the scanner — points
|
|
the user at the location-request flow. */
|
|
.sheet-chart-empty-msg {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
text-align: center;
|
|
gap: 6px;
|
|
padding: 12px 16px;
|
|
height: 100%;
|
|
max-width: 320px;
|
|
margin: 0 auto;
|
|
}
|
|
.sheet-chart-empty-title {
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--color-text);
|
|
}
|
|
.sheet-chart-empty-hint {
|
|
font-size: 12px;
|
|
color: var(--color-hint);
|
|
line-height: 1.4;
|
|
}
|
|
.sheet-chart-empty-btn {
|
|
margin-top: 6px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 8px 14px;
|
|
border-radius: 999px;
|
|
border: none;
|
|
background: var(--color-accent);
|
|
color: var(--color-accent-text);
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
.sheet-chart-empty-btn:active { opacity: 0.85; transform: scale(0.97); }
|
|
|
|
/* Subtle pill for manual-scan stations — ephemeral data (15 min TTL).
|
|
Freshness colour comes from --scan-color which is set on
|
|
#bottom-sheet-body each tick (green → yellow → red). The pill itself
|
|
stays low-key: tinted background, hint text, small pulsing dot,
|
|
hairline progress strip along the inner-bottom edge. */
|
|
#bottom-sheet-body {
|
|
--scan-color: #22c55e;
|
|
}
|
|
.sheet-manual-scan-banner-wrap {
|
|
padding: 4px 20px 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
}
|
|
|
|
/* Small pill button in the sheet header that rescans the 25 km circle
|
|
* around the current station — same action as "Hier suchen" but anchored
|
|
* to the tapped pin. Sits next to the freshness banner when there is one,
|
|
* on its own otherwise. */
|
|
.sheet-refresh-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 5px 11px 6px;
|
|
border-radius: 999px;
|
|
border: 1px solid var(--color-border);
|
|
background: var(--color-bg-secondary);
|
|
color: var(--color-text);
|
|
font-size: 11.5px;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
transition: background 0.15s ease, border-color 0.15s ease, opacity 0.15s ease;
|
|
}
|
|
.sheet-refresh-btn:hover:not(:disabled) {
|
|
background: var(--color-border);
|
|
}
|
|
.sheet-refresh-btn:active:not(:disabled) {
|
|
transform: scale(0.97);
|
|
}
|
|
.sheet-refresh-btn:disabled {
|
|
opacity: 0.45;
|
|
cursor: not-allowed;
|
|
}
|
|
.sheet-refresh-btn svg {
|
|
flex-shrink: 0;
|
|
}
|
|
.sheet-manual-scan-banner {
|
|
--scan-track: rgba(148, 163, 184, 0.18);
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 7px;
|
|
padding: 5px 11px 6px;
|
|
border-radius: 999px;
|
|
background: color-mix(in srgb, var(--scan-color) 10%, transparent);
|
|
font-size: 11.5px;
|
|
color: var(--color-hint);
|
|
max-width: 100%;
|
|
position: relative;
|
|
overflow: hidden;
|
|
transition: background 1s linear;
|
|
}
|
|
.sheet-manual-scan-text {
|
|
min-width: 0;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
.sheet-manual-scan-countdown {
|
|
font-variant-numeric: tabular-nums;
|
|
font-weight: 600;
|
|
color: var(--color-text);
|
|
}
|
|
|
|
/* Small pulsing dot. */
|
|
.sheet-manual-scan-dot {
|
|
width: 6px;
|
|
height: 6px;
|
|
border-radius: 50%;
|
|
background: var(--scan-color);
|
|
flex-shrink: 0;
|
|
animation: manualScanPulse 1.8s ease-in-out infinite;
|
|
transition: background 1s linear;
|
|
}
|
|
@keyframes manualScanPulse {
|
|
0%, 100% { opacity: 1; transform: scale(1); }
|
|
50% { opacity: 0.55; transform: scale(0.8); }
|
|
}
|
|
|
|
/* Hairline progress strip hugging the inside-bottom of the pill. */
|
|
.sheet-manual-scan-progress {
|
|
position: absolute;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
height: 2px;
|
|
background: var(--scan-track);
|
|
}
|
|
.sheet-manual-scan-progress-bar {
|
|
height: 100%;
|
|
width: 100%;
|
|
background: var(--scan-color);
|
|
transition: width 0.9s linear, background 1s linear;
|
|
}
|
|
|
|
/* Last minute: only the dot pulses faster. */
|
|
.sheet-manual-scan-banner.urgent .sheet-manual-scan-dot {
|
|
animation-duration: 0.9s;
|
|
}
|
|
|
|
.sheet-manual-scan-banner.expired {
|
|
opacity: 0.55;
|
|
}
|
|
.sheet-manual-scan-banner.expired .sheet-manual-scan-dot {
|
|
animation: none;
|
|
}
|
|
|
|
.sheet-expand-btn {
|
|
display: none;
|
|
position: absolute;
|
|
top: 12px;
|
|
right: 16px;
|
|
width: 32px;
|
|
height: 32px;
|
|
border: none;
|
|
background: var(--color-bg-secondary);
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: var(--color-text);
|
|
z-index: 2;
|
|
transition: background 0.15s;
|
|
}
|
|
|
|
.sheet-expand-btn:hover {
|
|
background: var(--color-border);
|
|
}
|
|
|
|
.sheet-expand-btn svg {
|
|
width: 18px;
|
|
height: 18px;
|
|
}
|
|
|
|
@media (hover: hover) and (pointer: fine) {
|
|
.sheet-expand-btn {
|
|
display: flex;
|
|
}
|
|
.sheet-station-header {
|
|
padding-right: 52px;
|
|
}
|
|
}
|
|
|
|
.sheet-nav-buttons {
|
|
display: flex;
|
|
gap: 10px;
|
|
padding: 16px 20px;
|
|
}
|
|
|
|
.sheet-nav-btn {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
padding: 13px 16px;
|
|
border-radius: 14px;
|
|
border: none;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
text-decoration: none;
|
|
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
|
}
|
|
|
|
.sheet-nav-btn:active {
|
|
transform: scale(0.95);
|
|
}
|
|
|
|
.sheet-nav-btn.gmaps,
|
|
.sheet-nav-btn.amaps {
|
|
background: var(--color-bg-secondary);
|
|
color: var(--color-text);
|
|
border: 1px solid var(--color-separator);
|
|
}
|
|
|
|
.sheet-nav-icon {
|
|
border-radius: 5px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
|
|
/* === Slider === */
|
|
.slider-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
|
|
.slider-row span {
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
min-width: 50px;
|
|
color: var(--color-text);
|
|
}
|
|
|
|
.slider {
|
|
flex: 1;
|
|
-webkit-appearance: none;
|
|
appearance: none;
|
|
height: 4px;
|
|
border-radius: 2px;
|
|
background: var(--color-hint);
|
|
outline: none;
|
|
}
|
|
|
|
.slider::-webkit-slider-thumb {
|
|
-webkit-appearance: none;
|
|
width: 28px;
|
|
height: 28px;
|
|
border-radius: 50%;
|
|
background: var(--color-accent);
|
|
cursor: pointer;
|
|
box-shadow: 0 2px 6px rgba(0,0,0,0.2);
|
|
}
|
|
|
|
/* === Stats === */
|
|
.stat-big {
|
|
text-align: center;
|
|
padding: var(--sp-5) var(--sp-4) var(--sp-3);
|
|
}
|
|
|
|
.stat-big-value {
|
|
font-size: 42px;
|
|
font-weight: 800;
|
|
color: var(--color-text);
|
|
letter-spacing: -0.02em;
|
|
}
|
|
|
|
.stat-big-label {
|
|
font-size: 14px;
|
|
color: var(--color-hint);
|
|
margin-top: var(--sp-1);
|
|
}
|
|
|
|
.stat-row {
|
|
display: flex;
|
|
gap: var(--sp-2);
|
|
padding: 0 var(--sp-4);
|
|
margin-bottom: var(--sp-2);
|
|
}
|
|
|
|
.stat-card {
|
|
flex: 1;
|
|
background: var(--color-bg-secondary);
|
|
border: 1px solid var(--color-separator);
|
|
border-radius: var(--r-md);
|
|
padding: var(--sp-3) var(--sp-3);
|
|
text-align: center;
|
|
}
|
|
|
|
.stat-card-value {
|
|
font-size: 20px;
|
|
font-weight: 700;
|
|
color: var(--color-text);
|
|
letter-spacing: -0.01em;
|
|
}
|
|
|
|
.stat-card-label {
|
|
font-size: 12px;
|
|
color: var(--color-hint);
|
|
margin-top: var(--sp-1);
|
|
}
|
|
|
|
.ranking-item {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: var(--sp-3) var(--sp-4);
|
|
gap: var(--sp-3);
|
|
border-bottom: 0.5px solid var(--color-separator);
|
|
transition: transform 0.15s cubic-bezier(0.2, 0, 0, 1), background 0.15s ease;
|
|
}
|
|
|
|
.ranking-item:hover {
|
|
background: var(--color-separator);
|
|
}
|
|
|
|
.ranking-item:active {
|
|
transform: scale(0.98);
|
|
}
|
|
|
|
.ranking-pos {
|
|
font-size: 13px;
|
|
font-weight: 700;
|
|
color: var(--color-hint);
|
|
width: 24px;
|
|
text-align: center;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
.ranking-name {
|
|
flex: 1;
|
|
font-size: 14px;
|
|
color: var(--color-text);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.ranking-price {
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
flex-shrink: 0;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
/* === Tab Bar === */
|
|
.tab-bar {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: calc(var(--tab-height) + var(--safe-bottom));
|
|
padding-bottom: var(--safe-bottom);
|
|
background: var(--color-bg-secondary);
|
|
border-top: 0.5px solid var(--color-separator);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-around;
|
|
z-index: 100;
|
|
}
|
|
|
|
.tab-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 2px;
|
|
background: none;
|
|
border: none;
|
|
cursor: pointer;
|
|
padding: 6px 14px;
|
|
border-radius: 10px;
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
color: var(--color-hint);
|
|
transition: color 0.2s ease, background 0.2s ease;
|
|
}
|
|
|
|
.tab-item.active {
|
|
color: var(--color-accent);
|
|
background: color-mix(in srgb, var(--color-accent) 12%, transparent);
|
|
}
|
|
|
|
@keyframes tab-bounce {
|
|
0% { transform: scale(1); }
|
|
35% { transform: scale(1.18); }
|
|
65% { transform: scale(0.94); }
|
|
100% { transform: scale(1); }
|
|
}
|
|
|
|
.tab-item.active .tab-icon {
|
|
animation: tab-bounce 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
}
|
|
|
|
.tab-icon {
|
|
width: 24px;
|
|
height: 24px;
|
|
fill: currentColor;
|
|
}
|
|
|
|
.tab-item span {
|
|
font-size: 10px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.tab-item.active span {
|
|
font-weight: 600;
|
|
}
|
|
|
|
/* === Location chip in settings === */
|
|
/* Leaflet popup override */
|
|
.leaflet-popup-content-wrapper {
|
|
border-radius: 12px !important;
|
|
box-shadow: 0 4px 20px rgba(0,0,0,0.15) !important;
|
|
}
|
|
|
|
.leaflet-popup-content {
|
|
margin: 10px 14px !important;
|
|
font-family: inherit !important;
|
|
font-size: 14px !important;
|
|
}
|
|
|
|
/* Map bubble markers (tankste! style) */
|
|
.map-bubble {
|
|
position: relative;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
padding: 3px 8px 4px;
|
|
border-radius: 8px;
|
|
color: #fff;
|
|
white-space: nowrap;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.4);
|
|
text-align: center;
|
|
min-width: 48px;
|
|
}
|
|
.map-bubble-brand {
|
|
font-size: 9px;
|
|
font-weight: 500;
|
|
opacity: 0.9;
|
|
letter-spacing: 0.3px;
|
|
line-height: 1.1;
|
|
}
|
|
.map-bubble-price {
|
|
font-size: 15px;
|
|
font-weight: 800;
|
|
line-height: 1.2;
|
|
letter-spacing: -0.3px;
|
|
}
|
|
.map-bubble-price sup {
|
|
font-size: 9px;
|
|
vertical-align: super;
|
|
}
|
|
.map-bubble-arrow {
|
|
position: absolute;
|
|
bottom: -6px;
|
|
left: 50%;
|
|
margin-left: -6px;
|
|
width: 0;
|
|
height: 0;
|
|
border-left: 6px solid transparent;
|
|
border-right: 6px solid transparent;
|
|
border-top: 6px solid;
|
|
}
|
|
|
|
|
|
/* Cluster marker overrides */
|
|
.marker-cluster-small,
|
|
.marker-cluster-medium,
|
|
.marker-cluster-large {
|
|
background: none !important;
|
|
}
|
|
.marker-cluster-small div,
|
|
.marker-cluster-medium div,
|
|
.marker-cluster-large div {
|
|
background: none !important;
|
|
}
|
|
|
|
/* Empty state */
|
|
.empty-state {
|
|
text-align: center;
|
|
padding: 40px 20px;
|
|
color: var(--color-hint);
|
|
}
|
|
|
|
.auth-hero-card {
|
|
background: linear-gradient(135deg, rgba(79,195,247,0.12), rgba(52,199,89,0.08));
|
|
border: 1px solid rgba(127,127,127,0.14);
|
|
border-radius: 18px;
|
|
padding: 18px;
|
|
display: grid;
|
|
gap: 14px;
|
|
}
|
|
|
|
.auth-hero-icon {
|
|
width: 44px;
|
|
height: 44px;
|
|
border-radius: 14px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: var(--color-accent);
|
|
color: var(--color-accent-text);
|
|
font-size: 22px;
|
|
box-shadow: 0 8px 18px rgba(0,0,0,0.12);
|
|
}
|
|
|
|
.auth-hero-title {
|
|
font-size: 18px;
|
|
font-weight: 700;
|
|
color: var(--color-text);
|
|
}
|
|
|
|
.auth-hero-text,
|
|
.auth-hero-note {
|
|
color: var(--color-hint);
|
|
font-size: 14px;
|
|
}
|
|
|
|
.auth-hero-actions {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
}
|
|
|
|
.google-login-slot {
|
|
min-height: 44px;
|
|
}
|
|
|
|
.primary-btn,
|
|
.secondary-btn {
|
|
border: none;
|
|
border-radius: 999px;
|
|
padding: 10px 16px;
|
|
font-size: 14px;
|
|
font-weight: 700;
|
|
cursor: pointer;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.primary-btn {
|
|
background: var(--color-accent);
|
|
color: var(--color-accent-text);
|
|
}
|
|
|
|
.secondary-btn {
|
|
background: var(--color-bg-secondary);
|
|
color: var(--color-text);
|
|
border: 1px solid rgba(127,127,127,0.16);
|
|
}
|
|
|
|
.secondary-btn.compact {
|
|
padding: 8px 14px;
|
|
}
|
|
|
|
.login-hint-pill {
|
|
margin-bottom: var(--sp-3);
|
|
padding: var(--sp-2) var(--sp-3);
|
|
border-radius: var(--r-sm);
|
|
background: color-mix(in srgb, var(--color-okay) 15%, transparent);
|
|
color: var(--color-okay);
|
|
font-size: 13px;
|
|
text-align: center;
|
|
}
|
|
|
|
.empty-state-icon {
|
|
font-size: 48px;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.empty-state-text {
|
|
font-size: 15px;
|
|
}
|
|
|
|
/* Map floating action button — sits above the bottom sheet's resting peek
|
|
on mobile by leaving a generous bottom inset. On desktop the sheet is a
|
|
side panel, so the FAB returns to a tighter offset (overridden below). */
|
|
.map-fab {
|
|
position: absolute;
|
|
bottom: 20px;
|
|
right: 16px;
|
|
z-index: 1000;
|
|
width: 46px;
|
|
height: 46px;
|
|
border-radius: var(--r-md);
|
|
border: none;
|
|
background: var(--color-bg);
|
|
color: var(--color-accent);
|
|
box-shadow: var(--shadow-3);
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
pointer-events: auto;
|
|
-webkit-tap-highlight-color: transparent;
|
|
touch-action: manipulation;
|
|
transition: transform 0.18s cubic-bezier(0.2, 0, 0, 1), opacity 0.15s ease;
|
|
}
|
|
.map-fab:active { opacity: 0.7; transform: scale(0.92); }
|
|
|
|
/* Row that holds the "Search here" pill (and, in scan-picker mode, a round
|
|
cancel button to its left). Lives inside #map-search-top-row, inline to
|
|
the right of the search box, so it sits on the same line as the search
|
|
bar. Hidden (display:none below) until the map has moved, at which point
|
|
the box shrinks to make room. */
|
|
.map-search-here-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
flex: 0 0 auto;
|
|
margin-top: 0;
|
|
pointer-events: none;
|
|
}
|
|
.map-search-here-row > * {
|
|
pointer-events: auto;
|
|
}
|
|
.map-search-here-row:empty,
|
|
.map-search-here-row:has(> .map-search-here.hidden:only-child) {
|
|
display: none;
|
|
}
|
|
|
|
.map-search-here {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 10px 18px;
|
|
border-radius: 999px;
|
|
border: none;
|
|
background: var(--color-accent);
|
|
color: var(--color-accent-text);
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
box-shadow: 0 4px 14px rgba(0,0,0,0.2);
|
|
cursor: pointer;
|
|
-webkit-tap-highlight-color: transparent;
|
|
touch-action: manipulation;
|
|
animation: searchHerePopIn 0.26s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
white-space: nowrap;
|
|
}
|
|
.map-search-here:active { opacity: 0.85; transform: scale(0.96); }
|
|
.map-search-here.hidden { display: none; }
|
|
.map-search-here[disabled] {
|
|
background: var(--color-hint);
|
|
color: var(--color-bg);
|
|
opacity: 0.75;
|
|
cursor: not-allowed;
|
|
box-shadow: 0 2px 6px rgba(0,0,0,0.12);
|
|
}
|
|
.map-search-here[disabled]:active { transform: none; opacity: 0.75; }
|
|
|
|
@keyframes searchHerePopIn {
|
|
from { opacity: 0; transform: scale(0.8); }
|
|
to { opacity: 1; transform: scale(1); }
|
|
}
|
|
|
|
/* "Confirm" state of the search-here pill (during scan-picker mode) */
|
|
.map-search-here.is-confirm {
|
|
background: var(--color-accent);
|
|
color: var(--color-accent-text);
|
|
box-shadow: 0 6px 18px rgba(0, 122, 255, 0.35);
|
|
}
|
|
|
|
/* Round cancel pill placed left of the confirm button while picking.
|
|
JS inserts it as a sibling of the search-here pill inside
|
|
.map-search-here-row, so flex centering handles the side-by-side layout. */
|
|
.map-scan-cancel {
|
|
width: 38px;
|
|
height: 38px;
|
|
border-radius: 50%;
|
|
border: none;
|
|
background: var(--color-bg);
|
|
color: var(--color-text);
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
|
|
cursor: pointer;
|
|
-webkit-tap-highlight-color: transparent;
|
|
touch-action: manipulation;
|
|
animation: searchHerePopIn 0.22s ease-out;
|
|
flex-shrink: 0;
|
|
}
|
|
.map-scan-cancel:active { opacity: 0.85; transform: scale(0.94); }
|
|
|
|
/* Draggable pin shown in the centre of the scan-radius picker circle */
|
|
.scan-pin {
|
|
width: 28px;
|
|
height: 28px;
|
|
border-radius: 50%;
|
|
background: color-mix(in srgb, var(--scan-accent, #007aff) 18%, transparent);
|
|
border: 2px solid var(--scan-accent, #007aff);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
|
|
cursor: grab;
|
|
}
|
|
.scan-pin:active { cursor: grabbing; }
|
|
.scan-pin-dot {
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 50%;
|
|
background: var(--scan-accent, #007aff);
|
|
box-shadow: 0 0 0 3px var(--color-bg);
|
|
animation: scanPinPulse 1.6s ease-in-out infinite;
|
|
}
|
|
@keyframes scanPinPulse {
|
|
0%, 100% { transform: scale(1); opacity: 1; }
|
|
50% { transform: scale(0.78); opacity: 0.85; }
|
|
}
|
|
|
|
/* Yellow pin shown for queued scan requests waiting on the cooldown.
|
|
Click to remove the pin (and its queue entry). */
|
|
.scan-queue-pin {
|
|
width: 24px;
|
|
height: 24px;
|
|
border-radius: 50%;
|
|
background: rgba(255, 193, 7, 0.22);
|
|
border: 2px solid #ffc107;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
|
|
cursor: pointer;
|
|
}
|
|
.scan-queue-pin-dot {
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 50%;
|
|
background: #ffc107;
|
|
box-shadow: 0 0 0 2px var(--color-bg);
|
|
animation: scanQueuePinPulse 1.4s ease-in-out infinite;
|
|
}
|
|
@keyframes scanQueuePinPulse {
|
|
0%, 100% { transform: scale(1); opacity: 1; }
|
|
50% { transform: scale(0.65); opacity: 0.65; }
|
|
}
|
|
|
|
/* The scan UI follows what the user is LOOKING at (map centre), not where
|
|
their pin sits — so searching Salzburg then panning to Munich should
|
|
still show the "Hier suchen" pill. Keyed off data-view-country. */
|
|
[data-view-country="at"] .map-search-here,
|
|
[data-view-country="at"] .map-scan-cancel,
|
|
[data-view-country="at"] .map-search-here-row,
|
|
[data-view-country="at"] .scan-queue-pin {
|
|
display: none !important;
|
|
}
|
|
|
|
/* Brighten dark map labels for readability */
|
|
.bright-labels {
|
|
filter: brightness(1.8) contrast(1.1);
|
|
}
|
|
|
|
/* Lift dark-mode tiles so place names stay readable without washing
|
|
out the dark background — brightness pulls labels up, lower contrast
|
|
prevents them from collapsing back into the background. */
|
|
.map-tiles-dark {
|
|
filter: brightness(1.75) contrast(0.95);
|
|
}
|
|
|
|
/* Match the Leaflet container background to the active tile palette so
|
|
loading tiles don't flash white before they paint. */
|
|
[data-theme="dark"] .leaflet-container {
|
|
background: #313131;
|
|
}
|
|
@media (prefers-color-scheme: dark) {
|
|
html:not([data-theme="light"]) .leaflet-container {
|
|
background: #313131;
|
|
}
|
|
}
|
|
|
|
/* Disable touch/select quirks on Leaflet chrome */
|
|
.leaflet-container,
|
|
.leaflet-pane,
|
|
.leaflet-control-container,
|
|
.leaflet-control-attribution,
|
|
.leaflet-control-zoom {
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
-webkit-touch-callout: none;
|
|
}
|
|
|
|
/* Hidden attribution for cleaner UX */
|
|
.leaflet-control-attribution {
|
|
display: none !important;
|
|
}
|
|
|
|
/* Settings Row (label + dropdown) */
|
|
.settings-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: var(--sp-3) var(--sp-4);
|
|
gap: var(--sp-3);
|
|
}
|
|
.settings-row-left {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--sp-3);
|
|
min-width: 0;
|
|
}
|
|
.settings-row-left span {
|
|
text-transform: none;
|
|
letter-spacing: 0;
|
|
}
|
|
.settings-select {
|
|
-webkit-appearance: none;
|
|
appearance: none;
|
|
background: var(--color-bg-secondary);
|
|
color: var(--color-text);
|
|
border: 1px solid var(--color-separator);
|
|
border-radius: 8px;
|
|
padding: 8px 36px 8px 12px;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
font-family: inherit;
|
|
cursor: pointer;
|
|
outline: none;
|
|
transition: border-color 0.15s;
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2398989e' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
|
|
background-repeat: no-repeat;
|
|
background-position: right 10px center;
|
|
background-size: 16px;
|
|
min-width: 0;
|
|
max-width: 160px;
|
|
}
|
|
.settings-select:focus {
|
|
border-color: var(--color-accent);
|
|
}
|
|
.settings-select option {
|
|
background: var(--color-bg);
|
|
color: var(--color-text);
|
|
}
|
|
|
|
/* Theme Segmented Control */
|
|
.theme-seg {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 6px;
|
|
padding: 8px 0;
|
|
border: none;
|
|
border-radius: 8px;
|
|
background: transparent;
|
|
color: var(--color-hint);
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: background 0.2s, color 0.2s, box-shadow 0.2s;
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
}
|
|
|
|
.theme-seg.active {
|
|
background: rgba(127,127,127,0.25);
|
|
color: var(--color-text);
|
|
box-shadow: 0 1px 4px rgba(0,0,0,0.15);
|
|
}
|
|
|
|
.theme-seg:active {
|
|
transform: scale(0.97);
|
|
}
|
|
|
|
.theme-seg svg {
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* Alert Channel Picker (reuses theme-seg pattern) */
|
|
.alert-ch-seg {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 6px;
|
|
padding: 8px 0;
|
|
border: none;
|
|
border-radius: 8px;
|
|
background: transparent;
|
|
color: var(--color-hint);
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: background 0.18s, color 0.18s;
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
}
|
|
.alert-ch-seg:hover:not(.active) {
|
|
color: var(--color-text-2);
|
|
}
|
|
.alert-ch-seg.active {
|
|
background: var(--color-accent);
|
|
color: var(--color-accent-text);
|
|
box-shadow: 0 2px 8px color-mix(in srgb, var(--color-accent) 30%, transparent);
|
|
}
|
|
.alert-ch-seg:active { transform: scale(0.97); }
|
|
.alert-ch-seg svg { flex-shrink: 0; }
|
|
|
|
/* Toggle Switch */
|
|
.toggle-switch { position: relative; display: inline-block; width: 51px; height: 31px; flex-shrink: 0; }
|
|
.toggle-switch input { opacity: 0; width: 0; height: 0; }
|
|
.toggle-track {
|
|
position: absolute; cursor: pointer; inset: 0;
|
|
background: var(--color-hint);
|
|
border-radius: 31px; transition: 0.2s;
|
|
}
|
|
.toggle-track:before {
|
|
content: ""; position: absolute;
|
|
width: 27px; height: 27px; left: 2px; bottom: 2px;
|
|
background: white; border-radius: 50%;
|
|
transition: 0.2s; box-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
|
}
|
|
.toggle-switch input:checked + .toggle-track { background: #34c759; }
|
|
.toggle-switch input:checked + .toggle-track:before { transform: translateX(20px); }
|
|
|
|
/* === Price Alert card — redesigned for the settings tab === */
|
|
.alert-card {
|
|
padding: 0;
|
|
overflow: hidden;
|
|
transition: border-color 0.25s ease, background 0.25s ease;
|
|
}
|
|
.alert-card.is-armed {
|
|
border-color: color-mix(in srgb, var(--color-accent) 35%, var(--color-separator));
|
|
background: linear-gradient(180deg, color-mix(in srgb, var(--color-accent) 5%, var(--color-bg-secondary)) 0%, var(--color-bg-secondary) 60%);
|
|
}
|
|
|
|
.alert-toggle-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: var(--sp-3);
|
|
padding: var(--sp-4);
|
|
}
|
|
.alert-toggle-left {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--sp-3);
|
|
min-width: 0;
|
|
}
|
|
.alert-toggle-text {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
min-width: 0;
|
|
}
|
|
.alert-toggle-title {
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
color: var(--color-text);
|
|
}
|
|
.alert-toggle-sub {
|
|
font-size: 11px;
|
|
color: var(--color-hint);
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.alert-config-body {
|
|
border-top: 1px solid var(--color-separator);
|
|
padding: var(--sp-4);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--sp-4);
|
|
}
|
|
|
|
.alert-config-group { display: flex; flex-direction: column; gap: 6px; }
|
|
.alert-config-label {
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
color: var(--color-hint);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
|
|
/* Threshold hero — integrated into the card body, no competing inner panel */
|
|
.alert-threshold {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
gap: 14px;
|
|
padding: 4px 0 2px;
|
|
}
|
|
.alert-threshold-head {
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
color: var(--color-hint);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.6px;
|
|
text-align: center;
|
|
}
|
|
.alert-threshold-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 22px;
|
|
}
|
|
.alert-step-btn {
|
|
width: 36px;
|
|
height: 36px;
|
|
border-radius: 50%;
|
|
border: 1px solid transparent;
|
|
background: color-mix(in srgb, var(--color-text) 6%, transparent);
|
|
color: var(--color-text);
|
|
display: grid;
|
|
place-items: center;
|
|
cursor: pointer;
|
|
font-family: inherit;
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
-webkit-tap-highlight-color: transparent;
|
|
transition: background 0.15s, transform 0.12s, color 0.15s;
|
|
}
|
|
.alert-step-btn:hover {
|
|
background: color-mix(in srgb, var(--color-accent) 14%, transparent);
|
|
color: var(--color-accent);
|
|
}
|
|
.alert-step-btn:active { transform: scale(0.92); }
|
|
.alert-step-btn svg { width: 18px; height: 18px; }
|
|
.alert-threshold-display {
|
|
font-size: 42px;
|
|
font-weight: 700;
|
|
color: var(--color-text);
|
|
letter-spacing: -0.03em;
|
|
font-variant-numeric: tabular-nums;
|
|
min-width: 132px;
|
|
width: 132px;
|
|
text-align: center;
|
|
line-height: 1;
|
|
font-family: inherit;
|
|
background: transparent;
|
|
border: 0;
|
|
outline: 0;
|
|
padding: 6px 4px;
|
|
border-radius: 10px;
|
|
box-sizing: border-box;
|
|
caret-color: var(--color-accent);
|
|
-webkit-appearance: none;
|
|
appearance: none;
|
|
transition: background 0.15s;
|
|
}
|
|
.alert-threshold-display:focus {
|
|
background: color-mix(in srgb, var(--color-accent) 12%, transparent);
|
|
}
|
|
.alert-threshold-display::-webkit-outer-spin-button,
|
|
.alert-threshold-display::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
|
|
|
|
.alert-threshold-bar {
|
|
position: relative;
|
|
margin-top: 2px;
|
|
cursor: pointer;
|
|
touch-action: none;
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
}
|
|
.alert-threshold-bar:focus { outline: none; }
|
|
.alert-threshold-bar:focus-visible .alert-threshold-bar-marker {
|
|
box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-accent) 22%, transparent),
|
|
0 0 0 7px color-mix(in srgb, var(--color-accent) 36%, transparent),
|
|
0 2px 8px rgba(0, 0, 0, 0.22);
|
|
}
|
|
.alert-threshold-bar[hidden] { display: none; }
|
|
.alert-threshold-bar-track {
|
|
height: 5px;
|
|
border-radius: 999px;
|
|
background: color-mix(in srgb, var(--color-text) 10%, transparent);
|
|
}
|
|
.alert-threshold-bar-current,
|
|
.alert-threshold-bar-marker {
|
|
position: absolute;
|
|
top: 2.5px; /* center of the 5px-tall track, regardless of label height below */
|
|
transform: translate(-50%, -50%);
|
|
}
|
|
.alert-threshold-bar-current {
|
|
width: 7px;
|
|
height: 7px;
|
|
border-radius: 50%;
|
|
background: var(--color-hint);
|
|
border: 2px solid var(--color-bg-secondary);
|
|
}
|
|
.alert-threshold-bar-marker {
|
|
width: 16px;
|
|
height: 16px;
|
|
border-radius: 50%;
|
|
background: var(--color-accent);
|
|
border: 2px solid var(--color-bg-secondary);
|
|
box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-accent) 22%, transparent),
|
|
0 2px 8px rgba(0, 0, 0, 0.22);
|
|
transition: left 0.22s cubic-bezier(0.22, 1, 0.36, 1);
|
|
cursor: grab;
|
|
}
|
|
.alert-threshold-bar.is-dragging { cursor: grabbing; }
|
|
.alert-threshold-bar.is-dragging .alert-threshold-bar-marker {
|
|
transition: none;
|
|
cursor: grabbing;
|
|
}
|
|
.alert-threshold-bar-labels {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-top: 12px;
|
|
font-size: 11px;
|
|
color: var(--color-text-2);
|
|
font-variant-numeric: tabular-nums;
|
|
letter-spacing: 0.2px;
|
|
opacity: 0.7;
|
|
}
|
|
|
|
.alert-ref-price {
|
|
text-align: center;
|
|
font-size: 12px;
|
|
color: var(--color-hint);
|
|
margin-top: 4px;
|
|
letter-spacing: 0.1px;
|
|
}
|
|
|
|
/* Channel section */
|
|
.alert-channel-section {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
}
|
|
|
|
.alert-channel-picker {
|
|
display: flex;
|
|
border-radius: 10px;
|
|
background: color-mix(in srgb, var(--color-text) 5%, transparent);
|
|
padding: 3px;
|
|
gap: 3px;
|
|
}
|
|
|
|
.alert-input {
|
|
width: 100%;
|
|
padding: 10px 12px;
|
|
border-radius: 10px;
|
|
border: 1px solid var(--color-separator);
|
|
background: var(--color-bg-secondary);
|
|
color: var(--color-text);
|
|
font-size: 14px;
|
|
font-family: inherit;
|
|
box-sizing: border-box;
|
|
transition: border-color 0.15s, box-shadow 0.15s;
|
|
}
|
|
.alert-input:focus-visible {
|
|
border-color: var(--color-accent);
|
|
box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 15%, transparent);
|
|
outline: none;
|
|
}
|
|
.alert-input-hint {
|
|
font-size: 11px;
|
|
color: var(--color-hint);
|
|
margin-top: 6px;
|
|
line-height: 1.45;
|
|
}
|
|
|
|
/* Status / background-check panel */
|
|
.alert-status-panel {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
padding: 14px 16px;
|
|
border-radius: 10px;
|
|
background: color-mix(in srgb, var(--color-text) 5%, transparent);
|
|
transition: background 0.2s;
|
|
}
|
|
.alert-status-panel.is-armed {
|
|
background: color-mix(in srgb, var(--color-good) 8%, transparent);
|
|
}
|
|
.alert-status-panel.is-below {
|
|
background: color-mix(in srgb, var(--color-accent) 9%, transparent);
|
|
}
|
|
.alert-status-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 14px;
|
|
font-size: 13px;
|
|
color: var(--color-text);
|
|
font-weight: 500;
|
|
}
|
|
.alert-status-row-secondary {
|
|
font-size: 11.5px;
|
|
color: var(--color-hint);
|
|
font-weight: 500;
|
|
letter-spacing: 0.1px;
|
|
}
|
|
.alert-status-row-secondary[hidden] { display: none; }
|
|
.alert-status-dot {
|
|
display: inline-block;
|
|
width: 9px;
|
|
height: 9px;
|
|
border-radius: 50%;
|
|
background: var(--color-hint);
|
|
box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-hint) 18%, transparent);
|
|
}
|
|
.alert-status-row.is-armed .alert-status-dot {
|
|
background: var(--color-good);
|
|
box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-good) 22%, transparent);
|
|
animation: alert-status-pulse 2.6s ease-in-out infinite;
|
|
}
|
|
.alert-status-row.is-below .alert-status-dot {
|
|
background: var(--color-accent);
|
|
box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-accent) 26%, transparent);
|
|
animation: none;
|
|
}
|
|
.alert-status-icon {
|
|
flex-shrink: 0;
|
|
opacity: 0.7;
|
|
}
|
|
@keyframes alert-status-pulse {
|
|
0%, 100% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-good) 18%, transparent); }
|
|
50% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--color-good) 28%, transparent); }
|
|
}
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.alert-status-dot { animation: none; }
|
|
}
|
|
|
|
/* Action buttons — clear primary/tertiary hierarchy */
|
|
.alert-actions {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
gap: 14px;
|
|
margin-top: 8px;
|
|
}
|
|
.alert-save-btn {
|
|
width: 100%;
|
|
padding: 13px;
|
|
border-radius: 12px;
|
|
border: none;
|
|
background: var(--color-accent);
|
|
color: var(--color-accent-text);
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
font-family: inherit;
|
|
cursor: pointer;
|
|
-webkit-tap-highlight-color: transparent;
|
|
transition: box-shadow 0.18s ease, transform 0.12s ease, filter 0.15s ease;
|
|
}
|
|
.alert-save-btn:hover {
|
|
box-shadow: 0 6px 18px color-mix(in srgb, var(--color-accent) 35%, transparent);
|
|
filter: brightness(1.05);
|
|
}
|
|
.alert-save-btn:active { transform: scale(0.98); }
|
|
|
|
.alert-test-btn {
|
|
align-self: center;
|
|
padding: 8px 12px;
|
|
border-radius: 8px;
|
|
border: none;
|
|
background: transparent;
|
|
color: var(--color-hint);
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
font-family: inherit;
|
|
cursor: pointer;
|
|
-webkit-tap-highlight-color: transparent;
|
|
transition: color 0.15s, background 0.15s;
|
|
}
|
|
.alert-test-btn:hover {
|
|
color: var(--color-text);
|
|
background: color-mix(in srgb, var(--color-text) 5%, transparent);
|
|
}
|
|
|
|
.alert-active-info {
|
|
margin-top: 10px;
|
|
padding: 10px 12px;
|
|
border-radius: 8px;
|
|
background: var(--color-bg-secondary);
|
|
font-size: 13px;
|
|
color: var(--color-hint);
|
|
text-align: center;
|
|
}
|
|
|
|
.settings-row-label {
|
|
font-size: 15px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.card-toggle-row {
|
|
padding: 12px 16px;
|
|
}
|
|
|
|
/* === Location picker select === */
|
|
.location-picker {
|
|
width: 100%;
|
|
padding: var(--sp-2) var(--sp-3);
|
|
border-radius: var(--r-sm);
|
|
border: 1px solid var(--color-separator);
|
|
background: var(--color-bg-secondary);
|
|
color: var(--color-text);
|
|
font-size: 14px;
|
|
font-family: inherit;
|
|
box-sizing: border-box;
|
|
transition: border-color 0.15s, box-shadow 0.15s;
|
|
}
|
|
.location-picker-row {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
}
|
|
|
|
/* === Custom desktop dropdown — desktop replaces the native <select>
|
|
with a styled trigger + animated panel. On mobile the native
|
|
select stays (the OS picker is the right affordance there). === */
|
|
.custom-select { position: relative; width: 100%; }
|
|
.custom-select-trigger,
|
|
.custom-select-panel { display: none; }
|
|
@media (min-width: 900px) {
|
|
.custom-select > select { display: none; }
|
|
.custom-select-trigger {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 8px;
|
|
width: 100%;
|
|
padding: 9px 14px;
|
|
border-radius: 10px;
|
|
border: 1px solid var(--color-separator);
|
|
background: var(--color-bg-secondary);
|
|
color: var(--color-text);
|
|
font-size: 14px;
|
|
font-family: inherit;
|
|
cursor: pointer;
|
|
text-align: left;
|
|
transition: border-color 0.2s var(--dt-ease),
|
|
box-shadow 0.2s var(--dt-ease);
|
|
}
|
|
.custom-select-trigger:hover {
|
|
border-color: var(--color-hint);
|
|
}
|
|
.custom-select.open .custom-select-trigger {
|
|
border-color: var(--color-accent);
|
|
box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.15);
|
|
}
|
|
.custom-select-value {
|
|
flex: 1;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
.custom-select-caret {
|
|
flex-shrink: 0;
|
|
color: var(--color-hint);
|
|
transition: transform 0.22s var(--dt-ease);
|
|
}
|
|
.custom-select.open .custom-select-caret {
|
|
transform: rotate(180deg);
|
|
}
|
|
.custom-select-panel {
|
|
display: block;
|
|
position: absolute;
|
|
left: 0;
|
|
right: 0;
|
|
top: calc(100% + 6px);
|
|
z-index: 100;
|
|
border-radius: 12px;
|
|
background: var(--color-bg-secondary);
|
|
border: 1px solid var(--color-separator);
|
|
box-shadow: var(--dt-shadow-md);
|
|
overflow: hidden;
|
|
max-height: 280px;
|
|
overflow-y: auto;
|
|
opacity: 0;
|
|
transform: translateY(-6px) scale(0.98);
|
|
transform-origin: top center;
|
|
pointer-events: none;
|
|
transition: opacity 0.18s var(--dt-ease), transform 0.2s var(--dt-ease);
|
|
}
|
|
.custom-select.open .custom-select-panel {
|
|
opacity: 1;
|
|
transform: translateY(0) scale(1);
|
|
pointer-events: auto;
|
|
}
|
|
.custom-select-option {
|
|
padding: 9px 14px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
color: var(--color-text);
|
|
line-height: 1.3;
|
|
transition: background 0.15s var(--dt-ease);
|
|
user-select: none;
|
|
}
|
|
.custom-select-option:hover {
|
|
background: color-mix(in srgb, var(--color-accent) 9%, transparent);
|
|
}
|
|
.custom-select-option.selected {
|
|
background: color-mix(in srgb, var(--color-accent) 14%, transparent);
|
|
color: var(--color-accent);
|
|
font-weight: 600;
|
|
}
|
|
/* In the settings row, the picker sits to the right of an icon + label
|
|
and should match the existing settings-select width. */
|
|
.settings-row .custom-select {
|
|
width: auto;
|
|
min-width: 132px;
|
|
}
|
|
}
|
|
.location-auto-hint {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
font-size: 11px;
|
|
color: var(--color-hint);
|
|
padding-left: 4px;
|
|
letter-spacing: 0.1px;
|
|
}
|
|
.location-auto-hint[hidden] { display: none; }
|
|
.location-auto-hint svg { opacity: 0.75; flex-shrink: 0; }
|
|
.location-picker:hover {
|
|
border-color: var(--color-hint);
|
|
}
|
|
.location-picker:focus-visible {
|
|
border-color: var(--color-accent);
|
|
outline: none;
|
|
box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 18%, transparent);
|
|
}
|
|
|
|
.request-location-btn {
|
|
width: 100%;
|
|
padding: 14px 16px;
|
|
background: transparent;
|
|
border: none;
|
|
cursor: pointer;
|
|
color: var(--color-accent);
|
|
font-size: 15px;
|
|
font-weight: 500;
|
|
border-top: 1px solid var(--color-separator);
|
|
font-family: inherit;
|
|
transition: background 0.15s;
|
|
}
|
|
.request-location-btn:hover {
|
|
background: var(--color-separator);
|
|
}
|
|
|
|
.account-card {
|
|
display: grid;
|
|
gap: 14px;
|
|
}
|
|
|
|
.account-main-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 14px;
|
|
}
|
|
|
|
.account-avatar {
|
|
width: 56px;
|
|
height: 56px;
|
|
border-radius: 18px;
|
|
overflow: hidden;
|
|
background: var(--color-bg-secondary);
|
|
display: none;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 20px;
|
|
font-weight: 700;
|
|
color: var(--color-accent);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.account-avatar.visible {
|
|
display: flex;
|
|
}
|
|
|
|
.account-avatar.has-image {
|
|
background: transparent;
|
|
}
|
|
|
|
.account-avatar img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.account-meta {
|
|
min-width: 0;
|
|
flex: 1;
|
|
}
|
|
|
|
.account-name-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.account-subline {
|
|
margin-top: 4px;
|
|
white-space: normal;
|
|
}
|
|
|
|
.account-auth-badge {
|
|
background: rgba(52,199,89,0.14);
|
|
color: #34c759;
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
padding: 4px 8px;
|
|
border-radius: 999px;
|
|
}
|
|
|
|
.account-actions {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.toast {
|
|
position: fixed;
|
|
left: 50%;
|
|
bottom: calc(var(--tab-height) + var(--safe-bottom) + 18px);
|
|
transform: translateX(-50%) translateY(20px);
|
|
background: rgba(17,17,17,0.92);
|
|
color: #fff;
|
|
padding: 12px 16px;
|
|
border-radius: 12px;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
box-shadow: 0 10px 24px rgba(0,0,0,0.24);
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
transition: opacity 0.22s ease, transform 0.22s ease;
|
|
z-index: 1200;
|
|
max-width: min(92vw, 420px);
|
|
text-align: center;
|
|
}
|
|
|
|
.toast.visible {
|
|
opacity: 1;
|
|
transform: translateX(-50%) translateY(0);
|
|
}
|
|
|
|
.is-hidden {
|
|
display: none !important;
|
|
}
|
|
|
|
.admin-page {
|
|
position: fixed;
|
|
inset: 0;
|
|
overflow-y: auto;
|
|
background:
|
|
radial-gradient(circle at top left, rgba(0,122,255,0.12), transparent 30%),
|
|
radial-gradient(circle at top right, rgba(52,199,89,0.12), transparent 28%),
|
|
linear-gradient(180deg, #f4f8ff 0%, #eef3f7 100%);
|
|
color: #142033;
|
|
}
|
|
|
|
.admin-shell {
|
|
width: min(980px, calc(100vw - 32px));
|
|
margin: 0 auto;
|
|
padding: 32px 0 48px;
|
|
display: grid;
|
|
gap: 18px;
|
|
}
|
|
|
|
.admin-hero {
|
|
padding: 8px 2px 0;
|
|
}
|
|
|
|
.admin-eyebrow {
|
|
font-size: 12px;
|
|
font-weight: 800;
|
|
letter-spacing: 0.14em;
|
|
text-transform: uppercase;
|
|
color: #3d6cb9;
|
|
}
|
|
|
|
.admin-hero h1 {
|
|
font-size: clamp(30px, 4vw, 44px);
|
|
line-height: 1;
|
|
letter-spacing: -0.04em;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.admin-lead {
|
|
margin-top: 12px;
|
|
max-width: 680px;
|
|
font-size: 16px;
|
|
color: #49617d;
|
|
}
|
|
|
|
.admin-feedback {
|
|
display: none;
|
|
padding: 14px 16px;
|
|
border-radius: 14px;
|
|
font-weight: 600;
|
|
box-shadow: 0 16px 28px rgba(20, 32, 51, 0.08);
|
|
}
|
|
|
|
.admin-feedback.visible {
|
|
display: block;
|
|
}
|
|
|
|
.admin-feedback.info {
|
|
background: #e8f1ff;
|
|
color: #214d91;
|
|
}
|
|
|
|
.admin-feedback.success {
|
|
background: #e5f6eb;
|
|
color: #1b6b3b;
|
|
}
|
|
|
|
.admin-feedback.error {
|
|
background: #ffe8e8;
|
|
color: #982e2e;
|
|
}
|
|
|
|
.admin-card {
|
|
background: rgba(255,255,255,0.9);
|
|
border: 1px solid rgba(61,108,185,0.12);
|
|
border-radius: 24px;
|
|
padding: 24px;
|
|
box-shadow: 0 18px 44px rgba(20, 32, 51, 0.1);
|
|
backdrop-filter: blur(14px);
|
|
}
|
|
|
|
.admin-card-head {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.admin-card-head h2 {
|
|
font-size: 24px;
|
|
letter-spacing: -0.03em;
|
|
}
|
|
|
|
.admin-card-head p {
|
|
margin-top: 6px;
|
|
color: #5d738c;
|
|
}
|
|
|
|
.admin-form {
|
|
display: grid;
|
|
gap: 18px;
|
|
}
|
|
|
|
.admin-form.narrow {
|
|
max-width: 420px;
|
|
}
|
|
|
|
.admin-form-block {
|
|
display: grid;
|
|
gap: 14px;
|
|
padding-top: 4px;
|
|
}
|
|
|
|
.admin-form-block h3 {
|
|
font-size: 15px;
|
|
font-weight: 800;
|
|
letter-spacing: 0.06em;
|
|
text-transform: uppercase;
|
|
color: #587291;
|
|
}
|
|
|
|
.admin-grid {
|
|
display: grid;
|
|
gap: 14px;
|
|
}
|
|
|
|
.admin-grid.two {
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
}
|
|
|
|
.admin-field {
|
|
display: grid;
|
|
gap: 8px;
|
|
}
|
|
|
|
.admin-field.full {
|
|
grid-column: 1 / -1;
|
|
}
|
|
|
|
.admin-field span {
|
|
font-size: 13px;
|
|
font-weight: 700;
|
|
color: #40556f;
|
|
}
|
|
|
|
.admin-field input,
|
|
.admin-field select,
|
|
.admin-field textarea {
|
|
width: 100%;
|
|
border: 1px solid rgba(61,108,185,0.18);
|
|
border-radius: 14px;
|
|
background: #fff;
|
|
padding: 12px 14px;
|
|
font: inherit;
|
|
color: #142033;
|
|
box-shadow: inset 0 1px 0 rgba(255,255,255,0.6);
|
|
}
|
|
|
|
.admin-field textarea {
|
|
resize: vertical;
|
|
min-height: 168px;
|
|
font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
|
|
font-size: 13px;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.admin-field input:focus,
|
|
.admin-field select:focus,
|
|
.admin-field textarea:focus {
|
|
outline: 2px solid rgba(0,122,255,0.2);
|
|
border-color: rgba(0,122,255,0.45);
|
|
}
|
|
|
|
.admin-actions {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 10px;
|
|
}
|
|
|
|
@media (max-width: 720px) {
|
|
.admin-shell {
|
|
width: min(100vw - 20px, 980px);
|
|
padding-top: 20px;
|
|
}
|
|
|
|
.admin-card {
|
|
padding: 18px;
|
|
border-radius: 18px;
|
|
}
|
|
|
|
.admin-grid.two {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
|
|
/* === PWA Install Popup === */
|
|
.pwa-popup {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 10000;
|
|
display: flex;
|
|
align-items: flex-end;
|
|
justify-content: center;
|
|
}
|
|
.pwa-popup.hidden { display: none; }
|
|
|
|
.pwa-popup-backdrop {
|
|
position: absolute;
|
|
inset: 0;
|
|
background: rgba(0,0,0,0.45);
|
|
backdrop-filter: blur(8px);
|
|
-webkit-backdrop-filter: blur(8px);
|
|
}
|
|
|
|
.pwa-popup-sheet {
|
|
position: relative;
|
|
width: 100%;
|
|
max-width: 420px;
|
|
background: var(--color-bg);
|
|
border-radius: 20px 20px 0 0;
|
|
padding: 28px 24px calc(24px + var(--safe-bottom));
|
|
box-shadow: 0 -4px 32px rgba(0,0,0,0.18);
|
|
animation: pwa-slide-up 0.35s cubic-bezier(0.32, 0.72, 0, 1);
|
|
text-align: center;
|
|
}
|
|
|
|
@keyframes pwa-slide-up {
|
|
from { transform: translateY(100%); }
|
|
to { transform: translateY(0); }
|
|
}
|
|
|
|
.pwa-popup-icon {
|
|
margin-bottom: 16px;
|
|
}
|
|
.pwa-popup-icon img {
|
|
width: 72px;
|
|
height: 72px;
|
|
border-radius: 16px;
|
|
box-shadow: 0 2px 12px rgba(0,0,0,0.12);
|
|
}
|
|
html[data-theme="dark"] .pwa-popup-icon img,
|
|
:root:not([data-theme="light"]) .pwa-popup-icon img {
|
|
background: #1c1c1e;
|
|
}
|
|
@media (prefers-color-scheme: light) {
|
|
:root:not([data-theme="dark"]) .pwa-popup-icon img {
|
|
background: transparent;
|
|
}
|
|
}
|
|
|
|
.pwa-popup-title {
|
|
font-size: 22px;
|
|
font-weight: 700;
|
|
color: var(--color-text);
|
|
margin-bottom: 6px;
|
|
letter-spacing: -0.3px;
|
|
}
|
|
|
|
.pwa-popup-desc {
|
|
font-size: 14px;
|
|
color: var(--color-hint);
|
|
line-height: 1.45;
|
|
margin-bottom: 20px;
|
|
max-width: 320px;
|
|
margin-left: auto;
|
|
margin-right: auto;
|
|
}
|
|
|
|
.pwa-popup-tabs {
|
|
display: flex;
|
|
background: var(--color-bg-secondary);
|
|
border-radius: 10px;
|
|
padding: 3px;
|
|
margin-bottom: 16px;
|
|
gap: 2px;
|
|
}
|
|
|
|
.pwa-popup-tab {
|
|
flex: 1;
|
|
padding: 7px 8px;
|
|
border: none;
|
|
background: transparent;
|
|
border-radius: 8px;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--color-hint);
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
.pwa-popup-tab.active {
|
|
background: var(--color-bg);
|
|
color: var(--color-text);
|
|
box-shadow: 0 1px 4px rgba(0,0,0,0.08);
|
|
}
|
|
|
|
.pwa-popup-panel {
|
|
display: none;
|
|
}
|
|
.pwa-popup-panel.active {
|
|
display: block;
|
|
animation: pwa-fade-in 0.2s ease;
|
|
}
|
|
|
|
@keyframes pwa-fade-in {
|
|
from { opacity: 0; transform: translateY(4px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
.pwa-popup-steps {
|
|
list-style: none;
|
|
text-align: left;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
.pwa-popup-steps li {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 14px;
|
|
padding: 12px 14px;
|
|
border-radius: 12px;
|
|
background: var(--color-bg-secondary);
|
|
margin-bottom: 8px;
|
|
font-size: 14px;
|
|
color: var(--color-text);
|
|
line-height: 1.4;
|
|
}
|
|
.pwa-popup-steps li:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.pwa-step-icon {
|
|
flex-shrink: 0;
|
|
width: 32px;
|
|
height: 32px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 8px;
|
|
background: var(--color-accent);
|
|
}
|
|
.pwa-step-icon svg {
|
|
fill: #fff;
|
|
}
|
|
|
|
.pwa-popup-btn {
|
|
display: block;
|
|
width: 100%;
|
|
margin-top: 20px;
|
|
padding: 15px;
|
|
border: none;
|
|
border-radius: 14px;
|
|
background: var(--color-accent);
|
|
color: #fff;
|
|
font-size: 17px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: opacity 0.15s;
|
|
letter-spacing: -0.2px;
|
|
}
|
|
.pwa-popup-btn:active {
|
|
opacity: 0.8;
|
|
}
|
|
|
|
/* === Pull-to-Refresh === */
|
|
#ptr-container {
|
|
position: fixed;
|
|
top: env(safe-area-inset-top, 0px);
|
|
left: 0;
|
|
right: 0;
|
|
display: flex;
|
|
justify-content: center;
|
|
pointer-events: none;
|
|
z-index: 90;
|
|
padding-top: 12px;
|
|
}
|
|
|
|
.native-spinner {
|
|
width: 20px;
|
|
height: 20px;
|
|
opacity: 0;
|
|
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Cg fill='%23000'%3E%3Crect x='9' y='1' width='2' height='5' rx='1' opacity='.12'/%3E%3Crect x='9' y='1' width='2' height='5' rx='1' opacity='.25' transform='rotate(45 10 10)'/%3E%3Crect x='9' y='1' width='2' height='5' rx='1' opacity='.37' transform='rotate(90 10 10)'/%3E%3Crect x='9' y='1' width='2' height='5' rx='1' opacity='.50' transform='rotate(135 10 10)'/%3E%3Crect x='9' y='1' width='2' height='5' rx='1' opacity='.62' transform='rotate(180 10 10)'/%3E%3Crect x='9' y='1' width='2' height='5' rx='1' opacity='.75' transform='rotate(225 10 10)'/%3E%3Crect x='9' y='1' width='2' height='5' rx='1' opacity='.87' transform='rotate(270 10 10)'/%3E%3Crect x='9' y='1' width='2' height='5' rx='1' opacity='1' transform='rotate(315 10 10)'/%3E%3C/g%3E%3C/svg%3E");
|
|
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Cg fill='%23000'%3E%3Crect x='9' y='1' width='2' height='5' rx='1' opacity='.12'/%3E%3Crect x='9' y='1' width='2' height='5' rx='1' opacity='.25' transform='rotate(45 10 10)'/%3E%3Crect x='9' y='1' width='2' height='5' rx='1' opacity='.37' transform='rotate(90 10 10)'/%3E%3Crect x='9' y='1' width='2' height='5' rx='1' opacity='.50' transform='rotate(135 10 10)'/%3E%3Crect x='9' y='1' width='2' height='5' rx='1' opacity='.62' transform='rotate(180 10 10)'/%3E%3Crect x='9' y='1' width='2' height='5' rx='1' opacity='.75' transform='rotate(225 10 10)'/%3E%3Crect x='9' y='1' width='2' height='5' rx='1' opacity='.87' transform='rotate(270 10 10)'/%3E%3Crect x='9' y='1' width='2' height='5' rx='1' opacity='1' transform='rotate(315 10 10)'/%3E%3C/g%3E%3C/svg%3E");
|
|
-webkit-mask-size: contain;
|
|
mask-size: contain;
|
|
background: var(--color-text);
|
|
animation: native-spin 0.8s steps(8) infinite;
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
|
|
@keyframes native-spin {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
|
|
/* Shift app content during pull */
|
|
#app.ptr-pulling {
|
|
transition: none;
|
|
}
|
|
|
|
#app.ptr-snapping {
|
|
transition: transform 0.45s cubic-bezier(0.2, 0.9, 0.3, 1.2);
|
|
}
|
|
|
|
#app.ptr-refreshing {
|
|
transition: transform 0.35s cubic-bezier(0.25, 0.1, 0.25, 1);
|
|
}
|
|
|
|
/* === About Section === */
|
|
.about-card {
|
|
padding: 0 !important;
|
|
overflow: hidden;
|
|
}
|
|
.about-app-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 14px;
|
|
padding: 16px;
|
|
}
|
|
.about-app-icon {
|
|
border-radius: 14px;
|
|
flex-shrink: 0;
|
|
}
|
|
.about-app-name {
|
|
font-size: 17px;
|
|
font-weight: 600;
|
|
color: var(--color-text);
|
|
line-height: 1.2;
|
|
}
|
|
.about-app-version {
|
|
font-size: 13px;
|
|
color: var(--color-hint);
|
|
margin-top: 2px;
|
|
}
|
|
.about-links-card {
|
|
margin-top: 0;
|
|
}
|
|
.about-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
padding: 13px 16px;
|
|
width: 100%;
|
|
text-decoration: none;
|
|
color: var(--color-text);
|
|
background: none;
|
|
border: none;
|
|
font-family: inherit;
|
|
cursor: pointer;
|
|
text-align: left;
|
|
box-sizing: border-box;
|
|
-webkit-tap-highlight-color: transparent;
|
|
transition: background 0.15s;
|
|
}
|
|
.about-row:active {
|
|
background: var(--color-bg-secondary);
|
|
}
|
|
.about-row-icon {
|
|
color: var(--color-hint);
|
|
flex-shrink: 0;
|
|
}
|
|
.about-row-label {
|
|
font-size: 15px;
|
|
font-weight: 500;
|
|
flex: 1;
|
|
}
|
|
.about-row-chevron {
|
|
opacity: 0.35;
|
|
flex-shrink: 0;
|
|
transition: transform 0.3s ease;
|
|
}
|
|
.about-row-divider {
|
|
height: 1px;
|
|
background: var(--color-border);
|
|
margin-left: 48px;
|
|
}
|
|
.about-contributors-chevron.open {
|
|
transform: rotate(90deg);
|
|
}
|
|
.about-contributors-list {
|
|
max-height: 0;
|
|
overflow: hidden;
|
|
transition: max-height 0.3s ease;
|
|
}
|
|
.about-contributors-list.open {
|
|
max-height: 300px;
|
|
}
|
|
.about-contributor {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
padding: 10px 16px 10px 48px;
|
|
text-decoration: none;
|
|
color: var(--color-text);
|
|
-webkit-tap-highlight-color: transparent;
|
|
transition: background 0.15s;
|
|
}
|
|
.about-contributor:active {
|
|
background: var(--color-bg-secondary);
|
|
}
|
|
.about-contributor-avatar {
|
|
border-radius: 50%;
|
|
flex-shrink: 0;
|
|
}
|
|
.about-contributor-info {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
.about-contributor-name {
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: var(--color-text);
|
|
line-height: 1.3;
|
|
}
|
|
.about-contributor-role {
|
|
font-size: 12px;
|
|
color: var(--color-hint);
|
|
line-height: 1.3;
|
|
}
|
|
.about-footer {
|
|
text-align: center;
|
|
padding: 8px 0 24px;
|
|
font-size: 11px;
|
|
color: var(--color-hint);
|
|
opacity: 0.5;
|
|
}
|
|
|
|
/* === Desktop Station Panel (transparent wrapper on mobile) === */
|
|
.desktop-station-panel {
|
|
display: contents;
|
|
}
|
|
|
|
/* === Desktop Layout === */
|
|
@media (min-width: 900px) {
|
|
|
|
/* --- Desktop-specific custom properties --- */
|
|
:root {
|
|
--sidebar-w: 88px;
|
|
--panel-w: 360px;
|
|
--sheet-w: 440px;
|
|
--content-max: 920px;
|
|
--content-max-narrow: 660px;
|
|
--dt-ease: cubic-bezier(0.22, 1, 0.36, 1);
|
|
--dt-ease-bounce: cubic-bezier(0.34, 1.4, 0.64, 1);
|
|
--dt-duration: 0.4s;
|
|
--dt-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
--dt-shadow-md: 0 4px 16px rgba(0, 0, 0, 0.08), 0 1px 4px rgba(0, 0, 0, 0.04);
|
|
--dt-shadow-lg: 0 12px 40px rgba(0, 0, 0, 0.12), 0 4px 12px rgba(0, 0, 0, 0.06);
|
|
--dt-shadow-sheet: 0 16px 64px rgba(0, 0, 0, 0.14), 0 6px 20px rgba(0, 0, 0, 0.08);
|
|
--dt-blur: 20px;
|
|
}
|
|
|
|
html[data-theme="dark"] {
|
|
--dt-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.2);
|
|
--dt-shadow-md: 0 4px 16px rgba(0, 0, 0, 0.25);
|
|
--dt-shadow-lg: 0 12px 40px rgba(0, 0, 0, 0.35);
|
|
--dt-shadow-sheet: 0 16px 64px rgba(0, 0, 0, 0.45);
|
|
}
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
:root:not([data-theme="light"]) {
|
|
--dt-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.2);
|
|
--dt-shadow-md: 0 4px 16px rgba(0, 0, 0, 0.25);
|
|
--dt-shadow-lg: 0 12px 40px rgba(0, 0, 0, 0.35);
|
|
--dt-shadow-sheet: 0 16px 64px rgba(0, 0, 0, 0.45);
|
|
}
|
|
}
|
|
|
|
/* --- Sidebar Navigation --- */
|
|
.tab-bar {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
bottom: 0;
|
|
right: auto;
|
|
width: var(--sidebar-w);
|
|
height: 100%;
|
|
flex-direction: column;
|
|
justify-content: flex-start;
|
|
padding: 16px 8px;
|
|
border-top: none;
|
|
border-right: 1px solid var(--color-separator);
|
|
gap: 2px;
|
|
z-index: 100;
|
|
background: var(--color-bg-secondary);
|
|
}
|
|
|
|
.tab-item {
|
|
padding: 10px 0;
|
|
width: 100%;
|
|
gap: 3px;
|
|
border-radius: 10px;
|
|
position: relative;
|
|
transition: color 0.2s ease,
|
|
background 0.2s ease;
|
|
}
|
|
|
|
.tab-item span {
|
|
font-size: 10px;
|
|
font-weight: 500;
|
|
letter-spacing: 0.01em;
|
|
transition: opacity 0.2s ease;
|
|
}
|
|
|
|
.tab-item:hover {
|
|
background: var(--color-separator);
|
|
}
|
|
|
|
.tab-item:active {
|
|
background: var(--color-separator);
|
|
opacity: 0.7;
|
|
}
|
|
|
|
.tab-item.active {
|
|
color: var(--color-accent);
|
|
background: rgba(0, 122, 255, 0.1);
|
|
}
|
|
|
|
.tab-item.active span {
|
|
font-weight: 600;
|
|
}
|
|
|
|
.tab-icon {
|
|
transition: transform 0.3s var(--dt-ease-bounce);
|
|
}
|
|
|
|
.tab-item.active .tab-icon {
|
|
animation: dt-tab-pop 0.4s var(--dt-ease-bounce);
|
|
}
|
|
|
|
@keyframes dt-tab-pop {
|
|
0% { transform: scale(1); }
|
|
35% { transform: scale(1.18); }
|
|
65% { transform: scale(0.94); }
|
|
100% { transform: scale(1); }
|
|
}
|
|
|
|
/* --- App container shifts right --- */
|
|
#app {
|
|
left: var(--sidebar-w);
|
|
bottom: 0;
|
|
}
|
|
|
|
/* --- Tab view transitions (desktop-refined) --- */
|
|
.tab-view {
|
|
transition: opacity var(--dt-duration) var(--dt-ease),
|
|
transform var(--dt-duration) var(--dt-ease);
|
|
transform: scale(0.97) translateY(8px);
|
|
}
|
|
|
|
.tab-view.active {
|
|
transform: scale(1) translateY(0);
|
|
}
|
|
|
|
/* --- Non-map views: centered content with padding --- */
|
|
#view-history,
|
|
#view-stats,
|
|
#view-settings {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
padding: 0 var(--sp-6) var(--sp-7);
|
|
}
|
|
|
|
/* On desktop the view already provides side padding, so sections shed
|
|
their inner 16 px gutter and let the centred max-width column do the
|
|
work. Cards/headers align flush to that column. */
|
|
#view-history > .section,
|
|
#view-stats > .section,
|
|
#view-settings > .section {
|
|
padding-left: 0;
|
|
padding-right: 0;
|
|
}
|
|
|
|
/* Stat-row's own 16 px gutter is now redundant for the same reason. */
|
|
#view-history .stat-row,
|
|
#view-stats .stat-row {
|
|
padding-left: 0;
|
|
padding-right: 0;
|
|
}
|
|
|
|
/* Page header sits flush with the content column and gets a more
|
|
generous vertical rhythm on desktop. */
|
|
.page-header {
|
|
padding: var(--sp-7) 0 var(--sp-5);
|
|
}
|
|
.page-title {
|
|
font-size: 32px;
|
|
}
|
|
.page-description {
|
|
font-size: 15px;
|
|
margin-top: var(--sp-2);
|
|
}
|
|
|
|
#view-history.active,
|
|
#view-stats.active,
|
|
#view-settings.active {
|
|
display: flex;
|
|
}
|
|
|
|
#view-history > *,
|
|
#view-stats > *,
|
|
#view-settings > * {
|
|
width: 100%;
|
|
max-width: var(--content-max);
|
|
}
|
|
|
|
/* Bigger chart on desktop — the extra horizontal space deserves a
|
|
taller graph instead of a 280px squish. Step it up at the tablet
|
|
breakpoint so 13" laptops don't get a 400px chart that dominates
|
|
the viewport. */
|
|
#view-history .chart-container,
|
|
#view-stats .chart-container {
|
|
height: 340px;
|
|
}
|
|
@media (min-width: 1200px) {
|
|
#view-history .chart-container,
|
|
#view-stats .chart-container {
|
|
height: 400px;
|
|
}
|
|
}
|
|
|
|
/* Pair the two summary cards side-by-side when there's room. The
|
|
section-header above spans both columns. */
|
|
#history-summary {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
column-gap: var(--sp-3);
|
|
row-gap: var(--sp-2);
|
|
align-items: start;
|
|
}
|
|
/* Any non-extreme-card child (section-header, empty-state message) spans
|
|
the full width of the summary grid. */
|
|
#history-summary > *:not(.extreme-card) {
|
|
grid-column: 1 / -1;
|
|
}
|
|
#history-summary > .extreme-card {
|
|
padding: var(--sp-5) var(--sp-5);
|
|
}
|
|
#history-summary > .extreme-card .extreme-value {
|
|
font-size: 32px;
|
|
}
|
|
|
|
/* Desktop card refinements — border + a hint of shadow. The border does
|
|
the visual heavy lifting (shadcn-style), shadow just adds depth on hover. */
|
|
.card {
|
|
border-radius: var(--r-lg);
|
|
padding: var(--sp-4) var(--sp-5);
|
|
transition: box-shadow 0.25s var(--dt-ease),
|
|
border-color 0.25s var(--dt-ease);
|
|
}
|
|
|
|
.card:hover {
|
|
box-shadow: var(--shadow-1);
|
|
}
|
|
|
|
.card-list {
|
|
border-radius: var(--r-lg);
|
|
transition: box-shadow 0.25s var(--dt-ease);
|
|
}
|
|
|
|
.card-list:hover {
|
|
box-shadow: var(--shadow-1);
|
|
}
|
|
|
|
.metric-card,
|
|
.best-time-card,
|
|
.extreme-card,
|
|
.stats-hero,
|
|
.stats-hero-card,
|
|
.stats-fact-card {
|
|
border-radius: var(--r-lg);
|
|
transition: box-shadow 0.25s var(--dt-ease), transform 0.25s var(--dt-ease);
|
|
}
|
|
.metric-card { padding: var(--sp-4) var(--sp-5); }
|
|
.best-time-card { padding: var(--sp-5) var(--sp-5); }
|
|
.extreme-card { padding: var(--sp-5) var(--sp-5); }
|
|
.stats-hero { padding: var(--sp-6) var(--sp-6); }
|
|
.stats-hero-card { padding: var(--sp-6); }
|
|
.stats-fact-card { padding: var(--sp-4); gap: var(--sp-3); }
|
|
.stats-fact-card .stats-fact-icon { width: 38px; height: 38px; border-radius: 11px; }
|
|
.stats-fact-card .stats-fact-icon svg { width: 18px; height: 18px; }
|
|
.stats-fact-value { font-size: 18px; }
|
|
.stats-hero-value { font-size: 44px; }
|
|
.stats-hero-glyph { width: 52px; height: 52px; border-radius: 14px; }
|
|
.stats-hero-glyph svg { width: 28px; height: 28px; }
|
|
.stats-facts-row { gap: var(--sp-3); }
|
|
#stats-content > .metric-row {
|
|
margin-left: 0;
|
|
margin-right: 0;
|
|
}
|
|
#stats-content > .section {
|
|
padding-left: 0;
|
|
padding-right: 0;
|
|
}
|
|
#stats-content > .stats-overview-row {
|
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
}
|
|
#stats-content .metric-card,
|
|
#stats-content .best-time-card {
|
|
min-height: 88px;
|
|
padding: var(--sp-4) var(--sp-5);
|
|
}
|
|
|
|
.metric-card:hover,
|
|
.best-time-card:hover,
|
|
.extreme-card:hover,
|
|
.stats-hero:hover,
|
|
.stats-fact-card:hover {
|
|
box-shadow: var(--shadow-1);
|
|
}
|
|
.stats-hero-card:hover {
|
|
box-shadow: var(--dt-shadow-md);
|
|
}
|
|
|
|
/* Desktop: roomier tiles & bar chart */
|
|
.stats-tile-grid { gap: 8px; }
|
|
.stats-tile {
|
|
min-height: 80px;
|
|
padding: 14px 4px 12px;
|
|
border-radius: 14px;
|
|
transition: transform 0.18s var(--dt-ease), box-shadow 0.2s var(--dt-ease), border-color 0.2s var(--dt-ease), background 0.2s var(--dt-ease);
|
|
}
|
|
.stats-tile:hover {
|
|
transform: translateY(-1px);
|
|
box-shadow: var(--shadow-1);
|
|
}
|
|
.stats-tile-name { font-size: 11px; }
|
|
.stats-tile-value { font-size: 16px; }
|
|
.stats-tile-bar { height: 4px; }
|
|
|
|
.stats-hour-chart-wrap { height: 210px; }
|
|
.stats-hour-chart-wrap canvas {
|
|
filter: drop-shadow(0 6px 22px rgba(255, 149, 0, 0.12));
|
|
}
|
|
|
|
/* History tab: roomier hero card, bigger headline value, and lift
|
|
the extreme cards on hover so they read as obviously clickable. */
|
|
.history-hero-card {
|
|
padding: var(--sp-6);
|
|
border-radius: var(--r-lg);
|
|
transition: box-shadow 0.25s var(--dt-ease);
|
|
}
|
|
.history-hero-card:hover {
|
|
box-shadow: var(--dt-shadow-md);
|
|
}
|
|
.history-hero-value { font-size: 44px; }
|
|
.history-hero-glyph {
|
|
width: 52px;
|
|
height: 52px;
|
|
border-radius: 14px;
|
|
}
|
|
.history-hero-glyph svg { width: 28px; height: 28px; }
|
|
.history-hero-pills { gap: 10px; margin-top: 18px; }
|
|
|
|
.history-extreme-row { gap: var(--sp-3); }
|
|
.history-extreme-card {
|
|
border-radius: var(--r-lg);
|
|
padding: 14px 16px 14px 20px;
|
|
transition: transform 0.18s var(--dt-ease),
|
|
box-shadow 0.22s var(--dt-ease),
|
|
border-color 0.22s var(--dt-ease);
|
|
}
|
|
.history-extreme-card:hover {
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 6px 18px color-mix(in srgb, var(--accent) 16%, transparent);
|
|
}
|
|
.history-extreme-icon {
|
|
width: 38px;
|
|
height: 38px;
|
|
border-radius: 11px;
|
|
}
|
|
.history-extreme-value { font-size: 22px; }
|
|
|
|
#price-chart, #hour-chart {
|
|
filter: drop-shadow(0 6px 20px rgba(255, 149, 0, 0.10));
|
|
}
|
|
|
|
.stats-station-list .station-ranking-item {
|
|
padding-left: calc(var(--sp-4) + 10px);
|
|
}
|
|
.stats-station-list .station-ranking-item::before {
|
|
left: 8px;
|
|
width: 4px;
|
|
}
|
|
|
|
.metric-value { font-size: 28px; }
|
|
.stats-hero-value { font-size: 48px; }
|
|
|
|
/* Desktop chip refinements */
|
|
.chip {
|
|
transition: background 0.2s var(--dt-ease),
|
|
color 0.2s var(--dt-ease),
|
|
transform 0.15s var(--dt-ease),
|
|
box-shadow 0.2s var(--dt-ease);
|
|
}
|
|
|
|
.chip:hover:not(.active) {
|
|
background: var(--color-separator);
|
|
}
|
|
|
|
.chip.active {
|
|
box-shadow: none;
|
|
}
|
|
|
|
/* Desktop chart container — matches card radius so the chart reads as a
|
|
hero card in the same family. Border carries the surface; shadow only
|
|
appears on hover for affordance. */
|
|
.chart-container {
|
|
border-radius: var(--r-lg);
|
|
padding: var(--sp-5) var(--sp-3) var(--sp-3);
|
|
transition: box-shadow 0.3s var(--dt-ease);
|
|
}
|
|
|
|
.chart-container:hover {
|
|
box-shadow: var(--shadow-1);
|
|
}
|
|
|
|
/* Stats card hover */
|
|
.stat-card {
|
|
border-radius: var(--r-lg);
|
|
padding: var(--sp-4) var(--sp-3);
|
|
transition: box-shadow 0.25s var(--dt-ease);
|
|
}
|
|
|
|
.stat-card:hover {
|
|
box-shadow: var(--shadow-1);
|
|
}
|
|
|
|
/* Stat row gets a touch more breathing room between cards on desktop. */
|
|
.stat-row {
|
|
gap: var(--sp-3);
|
|
}
|
|
|
|
/* Section header reads a little louder on desktop so the hierarchy
|
|
between header → cards is unmistakable. */
|
|
.section-header {
|
|
font-size: 12px;
|
|
padding: var(--sp-6) var(--sp-1) var(--sp-2);
|
|
letter-spacing: 0.7px;
|
|
}
|
|
.section-header:first-child,
|
|
.section > .section-header:first-child {
|
|
padding-top: var(--sp-3);
|
|
}
|
|
|
|
/* Section header sync-badge sits slightly higher to align with the
|
|
bolder header text. */
|
|
.section-header .sync-badge {
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
/* Ranking item hover */
|
|
.ranking-item {
|
|
transition: background 0.2s var(--dt-ease),
|
|
transform 0.15s var(--dt-ease);
|
|
border-radius: 0;
|
|
}
|
|
|
|
.ranking-item:hover {
|
|
background: var(--color-bg-secondary);
|
|
}
|
|
|
|
.ranking-item:active {
|
|
transform: scale(0.99);
|
|
}
|
|
|
|
/* Settings row hover */
|
|
.settings-row {
|
|
border-radius: 0;
|
|
transition: background 0.2s var(--dt-ease);
|
|
}
|
|
|
|
.settings-row:hover {
|
|
background: color-mix(in srgb, var(--color-text) 5%, transparent);
|
|
}
|
|
|
|
/* --- Map view: full width with side panel (panel on right) --- */
|
|
#view-map {
|
|
display: flex;
|
|
flex-direction: row;
|
|
overflow: hidden;
|
|
}
|
|
|
|
#view-map.active {
|
|
display: flex;
|
|
}
|
|
|
|
/* Remove the dark bar above the map on desktop */
|
|
#map-container::before {
|
|
display: none;
|
|
}
|
|
|
|
#map-container {
|
|
flex: 1;
|
|
height: 100%;
|
|
min-height: unset;
|
|
}
|
|
|
|
/* Map search bar refinement */
|
|
#map-search-wrapper {
|
|
top: 16px;
|
|
left: 16px;
|
|
max-width: 400px;
|
|
}
|
|
|
|
/* Desktop: the "Hier suchen" pill sits inline to the right of the search
|
|
box, same as mobile. Widen the wrapper only while the pill is visible so
|
|
the search input keeps its width. */
|
|
.map-search-here-row {
|
|
position: static;
|
|
transform: none;
|
|
width: auto;
|
|
margin-top: 0;
|
|
}
|
|
#map-search-wrapper:has(.map-search-here:not(.hidden)) {
|
|
max-width: 520px;
|
|
}
|
|
|
|
#map-search-box {
|
|
border-radius: 12px;
|
|
box-shadow: var(--dt-shadow-md);
|
|
background: var(--color-bg);
|
|
transition: box-shadow 0.25s var(--dt-ease);
|
|
height: 46px;
|
|
}
|
|
|
|
#map-search-box:focus-within {
|
|
box-shadow: var(--dt-shadow-lg), 0 0 0 3px rgba(0, 122, 255, 0.2);
|
|
}
|
|
|
|
#map-search-results {
|
|
border-radius: 12px;
|
|
box-shadow: var(--dt-shadow-lg);
|
|
background: var(--color-bg);
|
|
margin-top: 6px;
|
|
}
|
|
|
|
.map-search-result-item {
|
|
transition: background 0.15s ease;
|
|
}
|
|
|
|
.map-search-result-item:hover {
|
|
background: var(--color-bg-secondary);
|
|
}
|
|
|
|
/* Map FAB refinement */
|
|
.map-fab {
|
|
border-radius: 12px;
|
|
box-shadow: var(--dt-shadow-md);
|
|
background: var(--color-bg);
|
|
transition: box-shadow 0.25s var(--dt-ease),
|
|
transform 0.2s var(--dt-ease);
|
|
width: 46px;
|
|
height: 46px;
|
|
}
|
|
|
|
.map-fab:hover {
|
|
box-shadow: var(--dt-shadow-lg);
|
|
transform: scale(1.05);
|
|
}
|
|
|
|
.map-fab:active {
|
|
transform: scale(0.94);
|
|
opacity: 1;
|
|
}
|
|
|
|
/* --- Station panel (right sidebar on map) --- */
|
|
.desktop-station-panel {
|
|
display: flex;
|
|
flex-direction: column;
|
|
width: var(--panel-w);
|
|
flex-shrink: 0;
|
|
height: 100%;
|
|
overflow-y: auto;
|
|
border-left: 1px solid var(--color-separator);
|
|
background: var(--color-bg);
|
|
}
|
|
|
|
.desktop-station-panel .station-sort-bar {
|
|
padding: 16px 16px 8px;
|
|
position: sticky;
|
|
top: 0;
|
|
background: var(--color-bg);
|
|
z-index: 2;
|
|
backdrop-filter: blur(var(--dt-blur));
|
|
-webkit-backdrop-filter: blur(var(--dt-blur));
|
|
}
|
|
|
|
.desktop-station-panel .station-list {
|
|
margin: 0;
|
|
border-radius: 0;
|
|
flex: 1;
|
|
background: transparent;
|
|
overflow: visible;
|
|
}
|
|
|
|
/* Station list item hover on desktop */
|
|
.station-item {
|
|
transition: background 0.2s var(--dt-ease),
|
|
transform 0.15s var(--dt-ease);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.station-item:hover {
|
|
background: var(--color-bg-secondary);
|
|
}
|
|
|
|
.station-item:active {
|
|
transform: scale(0.99);
|
|
}
|
|
|
|
/* Station item stagger animation */
|
|
.station-item.anim-in {
|
|
animation: dt-station-slide 0.35s var(--dt-ease) both;
|
|
}
|
|
|
|
@keyframes dt-station-slide {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateX(16px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateX(0);
|
|
}
|
|
}
|
|
|
|
/* --- Bottom sheet becomes right side panel --- */
|
|
.bottom-sheet {
|
|
left: var(--sidebar-w);
|
|
}
|
|
|
|
.bottom-sheet-backdrop {
|
|
background: rgba(0, 0, 0, 0.25);
|
|
transition: opacity 0.35s var(--dt-ease);
|
|
}
|
|
|
|
.bottom-sheet-content {
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
left: auto;
|
|
width: var(--sheet-w);
|
|
max-width: var(--sheet-w);
|
|
max-height: 100%;
|
|
border-radius: 0;
|
|
border-left: 1px solid var(--color-separator);
|
|
box-shadow: var(--dt-shadow-sheet);
|
|
transform: translateX(100%);
|
|
transition: transform 0.45s var(--dt-ease);
|
|
padding-bottom: 0;
|
|
}
|
|
|
|
.bottom-sheet:not(.hidden) .bottom-sheet-content {
|
|
transform: translateX(0);
|
|
}
|
|
|
|
.bottom-sheet-content.expanded {
|
|
max-height: 100%;
|
|
border-radius: 0;
|
|
}
|
|
|
|
.bottom-sheet-handle-area {
|
|
display: none;
|
|
}
|
|
|
|
/* Sheet station header spacing for desktop (space for close button only) */
|
|
.sheet-station-header {
|
|
padding: 20px 20px 8px;
|
|
padding-right: 48px;
|
|
}
|
|
|
|
.sheet-station-name {
|
|
font-size: 20px;
|
|
}
|
|
|
|
.sheet-station-price {
|
|
font-size: 40px;
|
|
padding: 12px 20px;
|
|
}
|
|
|
|
/* Sheet chart container */
|
|
.sheet-chart-container {
|
|
height: 160px;
|
|
border-radius: 14px;
|
|
box-shadow: var(--dt-shadow-sm);
|
|
}
|
|
|
|
.bottom-sheet-content.expanded .sheet-chart-container {
|
|
height: 240px;
|
|
}
|
|
|
|
/* Sheet nav buttons */
|
|
.sheet-nav-btn {
|
|
border-radius: 12px;
|
|
transition: background 0.15s ease;
|
|
}
|
|
|
|
.sheet-nav-btn:hover {
|
|
background: var(--color-separator);
|
|
}
|
|
|
|
.sheet-nav-btn:active {
|
|
opacity: 0.7;
|
|
}
|
|
|
|
/* --- Desktop close button (Apple xmark.circle.fill style) --- */
|
|
.sheet-desktop-close {
|
|
position: absolute;
|
|
top: 14px;
|
|
right: 14px;
|
|
width: 28px;
|
|
height: 28px;
|
|
border-radius: 50%;
|
|
border: none;
|
|
background: var(--color-bg-secondary);
|
|
color: var(--color-hint);
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 10;
|
|
transition: background 0.2s ease,
|
|
color 0.2s ease,
|
|
opacity 0.2s ease;
|
|
opacity: 0.7;
|
|
line-height: 1;
|
|
}
|
|
|
|
.sheet-desktop-close:hover {
|
|
opacity: 1;
|
|
background: var(--color-separator);
|
|
color: var(--color-text);
|
|
}
|
|
|
|
.sheet-desktop-close:active {
|
|
opacity: 0.5;
|
|
}
|
|
|
|
/* The desktop close button overlays the top-right corner — give the fav
|
|
star enough right-side breathing room so they don't visually collide. */
|
|
.sheet-fav-btn {
|
|
margin-right: 36px;
|
|
}
|
|
|
|
/* --- Hide expand button on desktop (close button handles dismiss) --- */
|
|
.sheet-expand-btn {
|
|
display: none !important;
|
|
}
|
|
|
|
/* --- Hide pull-to-refresh on desktop --- */
|
|
#ptr-container {
|
|
display: none;
|
|
}
|
|
|
|
/* --- PWA popup centers on desktop --- */
|
|
.pwa-popup {
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.pwa-popup-sheet {
|
|
border-radius: 20px;
|
|
box-shadow: var(--dt-shadow-lg);
|
|
max-width: 440px;
|
|
}
|
|
|
|
/* --- Toast positioning (account for sidebar) --- */
|
|
.toast {
|
|
border-radius: 14px;
|
|
box-shadow: var(--dt-shadow-lg);
|
|
backdrop-filter: blur(var(--dt-blur));
|
|
-webkit-backdrop-filter: blur(var(--dt-blur));
|
|
}
|
|
|
|
.toast.visible {
|
|
left: calc(var(--sidebar-w) + 50%);
|
|
transform: translateX(-50%) translateX(calc(var(--sidebar-w) / -2));
|
|
}
|
|
|
|
/* --- About card refinements --- */
|
|
.about-card {
|
|
border-radius: 14px;
|
|
box-shadow: var(--dt-shadow-sm);
|
|
transition: box-shadow 0.25s var(--dt-ease);
|
|
}
|
|
|
|
.about-card:hover {
|
|
box-shadow: var(--dt-shadow-md);
|
|
}
|
|
|
|
.about-row {
|
|
transition: background 0.2s var(--dt-ease);
|
|
}
|
|
|
|
.about-row:hover {
|
|
background: rgba(127, 127, 127, 0.06);
|
|
}
|
|
|
|
/* Auth hero card */
|
|
.auth-hero-card {
|
|
border-radius: 20px;
|
|
box-shadow: var(--dt-shadow-md);
|
|
transition: box-shadow 0.3s var(--dt-ease);
|
|
}
|
|
|
|
.auth-hero-card:hover {
|
|
box-shadow: var(--dt-shadow-lg);
|
|
}
|
|
|
|
/* Section header subtle refinement */
|
|
.section-header {
|
|
padding-top: 20px;
|
|
font-size: 12px;
|
|
letter-spacing: 0.8px;
|
|
}
|
|
|
|
/* Location picker dropdown */
|
|
.location-picker {
|
|
border-radius: 10px;
|
|
transition: border-color 0.2s var(--dt-ease),
|
|
box-shadow 0.2s var(--dt-ease);
|
|
}
|
|
|
|
.location-picker:focus {
|
|
box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.15);
|
|
}
|
|
|
|
/* Scrollbar styling for panels */
|
|
.desktop-station-panel::-webkit-scrollbar,
|
|
.bottom-sheet-content::-webkit-scrollbar,
|
|
#view-history::-webkit-scrollbar,
|
|
#view-stats::-webkit-scrollbar,
|
|
#view-settings::-webkit-scrollbar {
|
|
width: 6px;
|
|
}
|
|
|
|
.desktop-station-panel::-webkit-scrollbar-track,
|
|
.bottom-sheet-content::-webkit-scrollbar-track,
|
|
#view-history::-webkit-scrollbar-track,
|
|
#view-stats::-webkit-scrollbar-track,
|
|
#view-settings::-webkit-scrollbar-track {
|
|
background: transparent;
|
|
}
|
|
|
|
.desktop-station-panel::-webkit-scrollbar-thumb,
|
|
.bottom-sheet-content::-webkit-scrollbar-thumb,
|
|
#view-history::-webkit-scrollbar-thumb,
|
|
#view-stats::-webkit-scrollbar-thumb,
|
|
#view-settings::-webkit-scrollbar-thumb {
|
|
background: var(--color-separator);
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.desktop-station-panel::-webkit-scrollbar-thumb:hover,
|
|
.bottom-sheet-content::-webkit-scrollbar-thumb:hover,
|
|
#view-history::-webkit-scrollbar-thumb:hover,
|
|
#view-stats::-webkit-scrollbar-thumb:hover,
|
|
#view-settings::-webkit-scrollbar-thumb:hover {
|
|
background: var(--color-hint);
|
|
}
|
|
|
|
/* Firefox scrollbar */
|
|
.desktop-station-panel,
|
|
.bottom-sheet-content,
|
|
#view-history,
|
|
#view-stats,
|
|
#view-settings {
|
|
scrollbar-width: thin;
|
|
scrollbar-color: var(--color-separator) transparent;
|
|
}
|
|
|
|
/* --- Slider refinement --- */
|
|
.slider::-webkit-slider-thumb {
|
|
width: 22px;
|
|
height: 22px;
|
|
box-shadow: var(--dt-shadow-md);
|
|
transition: box-shadow 0.2s var(--dt-ease),
|
|
transform 0.15s var(--dt-ease);
|
|
}
|
|
|
|
.slider::-webkit-slider-thumb:hover {
|
|
box-shadow: var(--dt-shadow-lg);
|
|
transform: scale(1.12);
|
|
}
|
|
|
|
/* Toggle switch hover */
|
|
.toggle-switch {
|
|
transition: opacity 0.2s ease;
|
|
}
|
|
|
|
.toggle-switch:hover {
|
|
opacity: 0.85;
|
|
}
|
|
|
|
/* --- Button hover refinements (Apple-style: subtle opacity, no lifts) --- */
|
|
.card-action {
|
|
transition: opacity 0.15s ease;
|
|
}
|
|
|
|
.card-action:hover {
|
|
opacity: 0.85;
|
|
}
|
|
|
|
.card-action:active {
|
|
opacity: 0.65;
|
|
}
|
|
|
|
.primary-btn {
|
|
transition: opacity 0.15s ease;
|
|
}
|
|
|
|
.primary-btn:hover {
|
|
opacity: 0.85;
|
|
}
|
|
|
|
.secondary-btn {
|
|
transition: background 0.2s ease;
|
|
}
|
|
|
|
.secondary-btn:hover {
|
|
background: var(--color-separator);
|
|
}
|
|
|
|
/* Sort button hover */
|
|
.station-sort-btn {
|
|
transition: background 0.2s ease;
|
|
}
|
|
|
|
.station-sort-btn:hover {
|
|
background: var(--color-separator);
|
|
}
|
|
|
|
.station-sort-btn:active {
|
|
opacity: 0.7;
|
|
}
|
|
|
|
/* Map location banner refinement */
|
|
#map-location-banner {
|
|
border-radius: 12px;
|
|
box-shadow: var(--dt-shadow-md);
|
|
backdrop-filter: blur(var(--dt-blur));
|
|
-webkit-backdrop-filter: blur(var(--dt-blur));
|
|
}
|
|
}
|
|
|
|
/* ── History locations (Standorte mit Verlaufsdaten) ───────────────── */
|
|
.hl-summary-top {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 14px;
|
|
width: 100%;
|
|
padding: 14px 16px;
|
|
background: transparent;
|
|
border: none;
|
|
text-align: left;
|
|
cursor: pointer;
|
|
-webkit-tap-highlight-color: transparent;
|
|
transition: background 0.15s;
|
|
}
|
|
.hl-summary-top:hover { background: color-mix(in srgb, var(--color-text) 4%, transparent); }
|
|
.hl-total-num {
|
|
font-size: 26px;
|
|
font-weight: 700;
|
|
line-height: 1;
|
|
color: var(--color-text);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.hl-total-label {
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
letter-spacing: 0.4px;
|
|
text-transform: uppercase;
|
|
color: var(--color-hint);
|
|
}
|
|
.hl-chips { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
.hl-chip {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
padding: 4px 9px;
|
|
border-radius: 999px;
|
|
background: color-mix(in srgb, var(--color-text) 8%, transparent);
|
|
font-size: 12px;
|
|
color: var(--color-text);
|
|
}
|
|
.hl-chip b { font-weight: 700; }
|
|
.hl-chevron { transition: transform 0.2s ease; }
|
|
.hl-group-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 9px 16px;
|
|
border-top: 1px solid var(--color-separator);
|
|
background: color-mix(in srgb, var(--color-text) 4%, transparent);
|
|
}
|
|
.hl-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
padding: 10px 16px;
|
|
border-top: 1px solid var(--color-separator);
|
|
transition: background 0.15s;
|
|
}
|
|
.hl-row:hover { background: color-mix(in srgb, var(--color-text) 3%, transparent); }
|
|
.hl-row-name {
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: var(--color-text);
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
.hl-row-sub { font-size: 11px; color: var(--color-hint); margin-top: 2px; }
|
|
.hl-pin-btn {
|
|
flex-shrink: 0;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 34px;
|
|
height: 34px;
|
|
border-radius: 50%;
|
|
border: none;
|
|
background: rgba(0, 122, 255, 0.12);
|
|
color: #007aff;
|
|
cursor: pointer;
|
|
-webkit-tap-highlight-color: transparent;
|
|
transition: background 0.15s, transform 0.15s;
|
|
}
|
|
.hl-pin-btn:hover { background: rgba(0, 122, 255, 0.22); transform: scale(1.06); }
|
|
.hl-pin-btn:active { transform: scale(0.94); }
|
|
|
|
/* "Standort anfragen" reads as the primary action of the locations section. */
|
|
.request-location-btn { color: var(--color-accent); font-weight: 600; }
|
|
.request-location-btn svg { color: var(--color-accent); }
|