Core ride logic, FTMS client, FIT encoder and probe CLI
Adds backing state for Resistance and Erg control modes, which had no value to hold and so could never satisfy FR-4.3/FR-4.6. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+230
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* 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 scales with the viewport: the primary readouts must be legible from
|
||||
* a riding position about a metre away (FR-9.5).
|
||||
*/
|
||||
|
||||
: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;
|
||||
|
||||
--gap: clamp(0.75rem, 1.4vw, 1.5rem);
|
||||
--edge: clamp(1rem, 2.4vw, 2.75rem);
|
||||
|
||||
color-scheme: dark;
|
||||
font-synthesis: none;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
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%;
|
||||
}
|
||||
|
||||
button {
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* ---------- shared primitives ---------------------------------------- */
|
||||
|
||||
.label {
|
||||
font-size: clamp(0.6rem, 0.72vw, 0.78rem);
|
||||
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;
|
||||
transition:
|
||||
background 120ms ease,
|
||||
color 120ms ease,
|
||||
transform 90ms ease;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: #131a24;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.btn.primary {
|
||||
background: var(--route);
|
||||
color: #04121a;
|
||||
}
|
||||
|
||||
.btn.primary:hover {
|
||||
background: #6cdcff;
|
||||
color: #04121a;
|
||||
}
|
||||
|
||||
.btn.ghost {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
/* ---------- 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;
|
||||
}
|
||||
Reference in New Issue
Block a user