/* * Dark theme for a screen someone stares at while suffering (FR-9.12). * * Rules this stylesheet follows: * - Near-black ground, bright data. Contrast comes from the numbers, not * from boxes. * - No borders unless they carry meaning. Structure comes from space. * - Numbers are tabular so they do not jitter as digits change. * - Type is sized from a *measurement*, not from `vw`: the primary readouts * must stay legible from a riding position on a 6" phone clipped to the * bars and on a 27" monitor a metre away (FR-9.5, FR-9.16). * * The `--type-*`, `--cols-*`, `--edge`, `--gap` and `--touch-min` properties * and the `data-size` / `data-height` / `data-orientation` / `data-input` * attributes on are all written by ui/src/lib/viewport.svelte.ts. The * values below are fallbacks for the first frame and for anything that renders * this stylesheet without the script (a snapshot test, a plain `vite preview`). * Do not restate a breakpoint here as a media query — one definition of * "compact", in viewport.ts, is the whole point. */ :root { --bg: #05070a; --bg-lift: #0b0f15; --hairline: #161c25; --ink: #f4f7fb; --ink-soft: #9fb0c2; --ink-dim: #5d6c7d; --ink-faint: #313d4a; --route: #45d0ff; --route-deep: #123a4d; --climb: #ff9a3c; --climb-deep: #3d2712; --ok: #35d9a0; --warn: #ffcf4a; --bad: #ff5a52; --power: #dfe8f3; --power-raw: #3f4d5d; /* Fallbacks; viewport.svelte.ts overwrites all of these on . */ --gap: 24px; --edge: 44px; --touch-min: 32px; --route-min: 130px; --type-hero: 58px; --type-big: 42px; --type-mid: 26px; --type-small: 17px; --type-label: 11px; --type-sub: 13px; --cols-primary: 5; --cols-detail: 5; --cols-effort: 7; color-scheme: dark; font-synthesis: none; -webkit-font-smoothing: antialiased; } * { box-sizing: border-box; } html, body { height: 100%; margin: 0; overflow: hidden; /* Android's URL bar makes `100%` of taller than what is painted at rest. `dvh` tracks the bar; the `100%` above is the fallback for anything that does not support it. */ height: 100dvh; } body { background: var(--bg); color: var(--ink); font-family: 'Inter var', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', system-ui, sans-serif; font-variant-numeric: tabular-nums; font-feature-settings: 'tnum' 1, 'cv01' 1; letter-spacing: -0.01em; user-select: none; -webkit-user-select: none; } #app { height: 100%; /* Notches, punch-holes and the gesture bar. `viewport-fit=cover` in index.html is what makes these non-zero; without the padding the control bar's End button sits under the Android gesture handle. */ padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left); } button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; padding: 0; } /* ---------- shared primitives ---------------------------------------- */ .label { font-size: var(--type-label); font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-dim); white-space: nowrap; } .hairline { border-top: 1px solid var(--hairline); } .chip { display: inline-flex; align-items: center; gap: 0.45em; padding: 0.3em 0.7em; border-radius: 999px; background: var(--bg-lift); font-size: 0.8rem; font-weight: 600; letter-spacing: 0.02em; color: var(--ink-soft); white-space: nowrap; } .dot { width: 0.5em; height: 0.5em; border-radius: 50%; background: currentColor; flex: none; } .tone-ok { color: var(--ok); } .tone-warn { color: var(--warn); } .tone-bad { color: var(--bad); } .tone-idle { color: var(--ink-dim); } .btn { display: inline-flex; align-items: center; justify-content: center; gap: 0.5em; padding: 0.62em 1.05em; border-radius: 0.5rem; background: var(--bg-lift); color: var(--ink-soft); font-size: 0.92rem; font-weight: 600; /* 48 px on touch (Android's accessibility floor), 32 elsewhere. A rider out of the saddle in gloves is exactly the case that number exists for. */ min-height: var(--touch-min); transition: background 120ms ease, color 120ms ease, transform 90ms ease; } /* Hover is a lie on a touchscreen: the style sticks after a tap and leaves a button looking focused that is not. */ @media (hover: hover) { .btn:hover { background: #131a24; color: var(--ink); } } .btn:active { transform: translateY(1px); } .btn.primary { background: var(--route); color: #04121a; } .btn.ghost { background: transparent; } @media (hover: hover) { .btn.primary:hover { background: #6cdcff; color: #04121a; } .btn.ghost:hover { background: var(--bg-lift); } .btn.danger:hover { background: #2a1113; color: var(--bad); } } .btn[disabled] { opacity: 0.35; pointer-events: none; } .kbd { display: inline-block; min-width: 1.5em; padding: 0.1em 0.35em; border-radius: 0.28em; background: #10161f; color: var(--ink-dim); font-size: 0.68rem; font-weight: 700; text-align: center; letter-spacing: 0.02em; } /* * A keyboard shortcut is an instruction on a device with no keyboard. Hiding * them also buys back the width the compact layouts need — which is why every * control that carries one also carries a word. This is the stylesheet's half * of `LayoutPlan.show.keyHints`. */ [data-input='touch'] .kbd { display: none; } /* ---------- uPlot, restyled for the dark theme ------------------------ */ .uplot, .u-wrap { width: 100% !important; } .u-title, .u-legend { display: none; } .u-axis { color: var(--ink-dim); } .u-select { background: rgba(69, 208, 255, 0.12); } .u-cursor-x, .u-cursor-y { border-color: var(--ink-faint) !important; } ::-webkit-scrollbar { width: 10px; height: 10px; } ::-webkit-scrollbar-thumb { background: #1b232e; border-radius: 6px; } ::-webkit-scrollbar-track { background: transparent; }