diff --git a/dashboard/src/gpsntp_dashboard/static/index.html b/dashboard/src/gpsntp_dashboard/static/index.html index 46d1086..0d8ef86 100644 --- a/dashboard/src/gpsntp_dashboard/static/index.html +++ b/dashboard/src/gpsntp_dashboard/static/index.html @@ -174,7 +174,7 @@ - + How this clock works diff --git a/dashboard/src/gpsntp_dashboard/static/style.css b/dashboard/src/gpsntp_dashboard/static/style.css index a172bea..0e3071b 100644 --- a/dashboard/src/gpsntp_dashboard/static/style.css +++ b/dashboard/src/gpsntp_dashboard/static/style.css @@ -100,8 +100,22 @@ svg { width: 1.15em; height: 1.15em; fill: none; stroke: currentColor; stroke-wi @keyframes pulse { 70% { box-shadow: 0 0 0 7px rgba(52, 211, 153, 0); } 100% { box-shadow: 0 0 0 0 rgba(52, 211, 153, 0); } } /* ---------- layout ---------- */ -main { max-width: 1180px; margin: 0 auto; padding: 20px; display: grid; gap: 18px; } +/* `grid-template-columns: minmax(0, 1fr)` is load-bearing, not decoration. + * + * A grid column defaults to `auto`, whose MINIMUM is min-content — and grid items + * refuse to shrink below it. The sources table has seven columns, giving it a + * ~700px min-content, and that number propagates up and pins `main` to 771px + * inside a 375px phone. Every panel then overflows and the document gets a + * horizontal scrollbar. `.table-hold { overflow-x: auto }` cannot rescue it, + * because the wrapper's own ancestors still won't shrink. + * + * The 0 in minmax(0, 1fr) overrides that automatic min-content floor. Same reason + * .panel needs min-width: 0. Remove either and mobile breaks again. + */ +main { max-width: 1180px; margin: 0 auto; padding: 20px; + display: grid; grid-template-columns: minmax(0, 1fr); gap: 18px; } .panel { + min-width: 0; background: linear-gradient(var(--panel), var(--bg-2)); border: 1px solid var(--border-soft); border-radius: 16px; padding: 18px; box-shadow: var(--shadow); @@ -225,7 +239,7 @@ main { max-width: 1180px; margin: 0 auto; padding: 20px; display: grid; gap: 18p * Clockmakers signed the backplate — the brass face only a repairer sees once * the case is open. This footer is that plate: a double hairline for the plate * edge, engraved small-caps for the name, and nothing that moves. A signature - * should be quiet. Same markup as the docs site at escapement.warehack.ing, + * should be quiet. Same markup as the docs site at cuckoo.warehack.ing, * recolored from brass to this dashboard's cyan. */ .ss-plate__link { @@ -254,10 +268,77 @@ main { max-width: 1180px; margin: 0 auto; padding: 20px; display: grid; gap: 18p .ss-plate__cta svg { width: 14px; height: 14px; transition: transform .2s; } .ss-plate__link:hover .ss-plate__cta svg { transform: translateX(2px); } +/* ---------- mobile ---------- + * + * This is a status page. On a phone you are almost always asking ONE question: + * "is the clock still good?" So the hero answer — time, stratum, lock — must be + * readable without scrolling, and everything else can wait below the fold. + * + * The tables and the signal bars scroll sideways WITHIN their panels rather than + * being crushed into unreadability. That's deliberate: a squeezed IPv6 address + * wrapped over four lines is worse than one you swipe. + */ +@media (max-width: 700px) { + main { padding: 14px; gap: 14px; } + .panel { padding: 14px; border-radius: 14px; } + + /* A panel head is a title plus its readout. Side by side they fight for a + narrow line and the title wraps mid-phrase; stacked, both stay whole. */ + .panel-head { flex-direction: column; align-items: flex-start; gap: 6px; } + .spark-meta { font-size: 11px; } + + /* Stack the clock above the stratum badge, and left-align the badge so the + eye travels straight down instead of ping-ponging left-right. */ + .hero { flex-direction: column; align-items: flex-start; gap: 14px; } + .verdict { text-align: left; width: 100%; } + .ref-line { margin-top: 6px; } + + /* clamp() already scales this, but 11vw on a narrow phone leaves the seconds + hard against the edge. Give it a slightly bigger floor and tighter tracking. */ + .clock { font-size: clamp(38px, 13vw, 60px); letter-spacing: 0; } + + /* Two columns, not one: six stat cards single-file is a lot of thumb. */ + .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; } + .card { padding: 12px; } + .card-value { font-size: 21px; } + + .spark-big { height: 90px; } + .snr { min-height: 150px; } + + /* Swipe the source table rather than wrap it. The shadow hint tells the reader + there IS more to the right — otherwise a cut-off column just looks broken. */ + .table-hold { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + background: + linear-gradient(to right, var(--panel) 30%, transparent) left / 24px 100% no-repeat, + linear-gradient(to left, var(--panel) 30%, transparent) right / 24px 100% no-repeat, + radial-gradient(farthest-side at 0 50%, rgba(0,0,0,.35), transparent) left / 12px 100% no-repeat, + radial-gradient(farthest-side at 100% 50%, rgba(0,0,0,.35), transparent) right / 12px 100% no-repeat; + background-attachment: local, local, scroll, scroll; + } + .tbl { min-width: 560px; } + + .topbar { padding: 12px 14px; } + .brand h1 { font-size: 17px; } + .brand-sub { font-size: 11.5px; } + .brand-icon { width: 26px; height: 26px; } + + .foot { padding: 10px 14px 24px; } + /* Three items on one line is unreadable at 375px — let them stack. */ + .foot-meta { flex-direction: column; align-items: flex-start; gap: 6px; } +} + @media (max-width: 560px) { .ss-plate__link { flex-direction: column; align-items: flex-start; } } +/* Comfortable thumb targets. The WCAG 2.2 minimum is 24px; 44px is the iOS HIG + figure and the one that actually feels right when you're standing up. */ +@media (pointer: coarse) { + .foot-docs, .ss-plate__link { min-height: 44px; } +} + @media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } } /* "approx" marker when GPSNTP_POSITION=coarse */