styles.css
| 1 | /* One stylesheet, system fonts, dark by preference. No framework. */ |
| 2 | :root { |
| 3 | --bg: #fff; |
| 4 | --fg: #16181d; |
| 5 | --muted: #6a7280; |
| 6 | --line: #dde1e7; |
| 7 | --accent: #1d6fd8; |
| 8 | color-scheme: light dark; |
| 9 | } |
| 10 | @media (prefers-color-scheme: dark) { |
| 11 | :root { --bg: #14161a; --fg: #e6e8ec; --muted: #949aa5; --line: #2a2e36; } |
| 12 | } |
| 13 | |
| 14 | * { box-sizing: border-box; } |
| 15 | body { |
| 16 | margin: 0; |
| 17 | font: 14px/1.45 system-ui, sans-serif; |
| 18 | background: var(--bg); |
| 19 | color: var(--fg); |
| 20 | } |
| 21 | button, input { |
| 22 | font: inherit; |
| 23 | color: inherit; |
| 24 | background: transparent; |
| 25 | border: 1px solid var(--line); |
| 26 | border-radius: 6px; |
| 27 | padding: 6px 10px; |
| 28 | } |
| 29 | button { cursor: pointer; } |
| 30 | button:hover:not(:disabled) { border-color: var(--accent); } |
| 31 | button:disabled { cursor: default; opacity: 0.6; } |
| 32 | button.active { border-color: var(--accent); color: var(--accent); } |
| 33 | a { color: var(--accent); } |
| 34 | |
| 35 | /* The nav is <a> now that routes are real URLs, so it has to look like the |
| 36 | buttons it replaced: middle-click and "copy link" only work on an anchor. */ |
| 37 | header nav a { |
| 38 | border: 1px solid var(--line); |
| 39 | border-radius: 6px; |
| 40 | padding: 6px 10px; |
| 41 | color: inherit; |
| 42 | text-decoration: none; |
| 43 | } |
| 44 | header nav a:hover { border-color: var(--accent); } |
| 45 | header nav a.active { border-color: var(--accent); color: var(--accent); } |
| 46 | |
| 47 | .center { min-height: 100dvh; display: grid; place-content: center; gap: 10px; } |
| 48 | .card { border: 1px solid var(--line); border-radius: 10px; padding: 16px; } |
| 49 | .login { width: min(320px, 90vw); } |
| 50 | .login h1 { margin: 0 0 4px; font-size: 18px; } |
| 51 | .error { color: #d8461d; margin: 0; } |
| 52 | .muted { color: var(--muted); } |
| 53 | .banner { padding: 6px 12px; background: #d8461d22; border-bottom: 1px solid var(--line); } |
| 54 | |
| 55 | .shell { display: flex; flex-direction: column; height: 100dvh; } |
| 56 | header { |
| 57 | display: flex; |
| 58 | align-items: center; |
| 59 | gap: 10px; |
| 60 | padding: 8px 12px; |
| 61 | border-bottom: 1px solid var(--line); |
| 62 | } |
| 63 | header nav { display: flex; gap: 6px; } |
| 64 | header .who { margin-left: auto; color: var(--muted); } |
| 65 | |
| 66 | .split { flex: 1; display: grid; grid-template-columns: 260px 1fr; min-height: 0; } |
| 67 | aside { border-right: 1px solid var(--line); overflow-y: auto; padding: 8px; display: grid; gap: 6px; align-content: start; } |
| 68 | .map { height: 100%; } |
| 69 | |
| 70 | .person { display: grid; gap: 2px; text-align: left; width: 100%; } |
| 71 | .person .name { font-weight: 600; } |
| 72 | .person .meta { color: var(--muted); font-size: 12px; display: flex; align-items: center; gap: 4px; } |
| 73 | .dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; } |
| 74 | .dot.fresh { background: #2e9e4f; } |
| 75 | .dot.stale { background: #d8a11d; } |
| 76 | .dot.old { background: #d8461d; } |
| 77 | |
| 78 | .settings { padding: 16px; display: grid; gap: 16px; max-width: 640px; } |
| 79 | .settings h2 { margin: 0 0 8px; font-size: 15px; } |
| 80 | .token { display: flex; align-items: center; gap: 12px; justify-content: space-between; padding: 8px 0; border-top: 1px solid var(--line); } |
| 81 | .password { display: grid; gap: 8px; margin-top: 12px; } |
| 82 | |
| 83 | /* Leaflet's own attribution must stay legible in dark mode; it is not optional. */ |
| 84 | .leaflet-container { background: var(--bg); } |
| 85 | .leaflet-control-attribution { background: #ffffffcc !important; color: #16181d !important; } |
| 86 | .leaflet-control-attribution a { color: #1d6fd8 !important; } |
| 87 |