:root{--ink:var(--ps-black);--panel:var(--ps-panel);--line:var(--ps-line);--green:var(--ps-green);--muted:var(--ps-silver);--white:var(--ps-white)}*{box-sizing:border-box}[hidden]{display:none!important}body{margin:0;background:var(--ink);color:var(--white);font:16px/1.5 var(--ps-font-body);min-height:100vh}.topbar{height:72px;padding:0 max(24px,calc((100vw - 1180px)/2));display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--line);background:rgba(0,0,0,.9);position:sticky;top:0;z-index:3}.brand{display:flex;align-items:center;gap:12px;color:inherit;text-decoration:none;font:700 18px var(--ps-font-display);letter-spacing:.08em;text-transform:uppercase}.brand img{width:44px;filter:grayscale(1) brightness(3)}.brand b{color:var(--green)}.account{display:flex;align-items:center;gap:18px;color:var(--muted);font-size:14px}main{width:min(1180px,100%);padding:54px 24px 100px;margin:auto}h1,h2,h3{font-family:var(--ps-font-display);text-transform:uppercase;line-height:1.05;margin:.2em 0}h1{font-size:clamp(42px,7vw,72px)}h2{font-size:clamp(28px,4vw,40px)}p{color:var(--muted)}.eyebrow{margin:0;color:var(--green);font:600 11px var(--ps-font-ui);letter-spacing:.18em;text-transform:uppercase}.welcome{display:flex;align-items:end;justify-content:space-between;padding-bottom:40px}.welcome p:last-child{font-size:18px}.tabs{display:flex;gap:8px;border-bottom:1px solid var(--line);margin-bottom:36px}.tabs button{border:0;border-bottom:2px solid transparent;padding:14px 20px;background:none;color:var(--muted);font:600 13px var(--ps-font-ui);text-transform:uppercase;letter-spacing:.12em}.tabs button.active{color:var(--white);border-color:var(--green)}.panel{display:none}.panel.active{display:block}.section-head{display:flex;align-items:center;justify-content:space-between;gap:20px;margin-bottom:24px}.button,.text-button{border:0;cursor:pointer;font:700 12px var(--ps-font-ui);text-transform:uppercase;letter-spacing:.1em}.button{display:inline-block;padding:13px 18px;background:var(--green);color:#041006;text-decoration:none;border-radius:3px}.text-button{color:var(--green);background:none;padding:8px}.note{padding:16px;border-left:2px solid var(--green);background:var(--panel);max-width:760px}.card-list{display:grid;gap:12px}.data-card{display:flex;justify-content:space-between;align-items:center;gap:20px;padding:22px;border:1px solid var(--line);background:var(--panel);border-radius:4px}.session-card{position:relative;padding-top:54px}.session-badges{position:absolute;top:17px;left:22px;right:22px;display:flex;justify-content:space-between;gap:16px;color:var(--muted);font:600 10px var(--ps-font-ui);letter-spacing:.1em;text-transform:uppercase}.session-type{color:var(--green)}.session-roles{text-align:right}.data-card h3{margin:0 0 5px}.data-card p{margin:0;font-size:14px}.meta{display:flex;gap:8px;flex-wrap:wrap}.chip{padding:4px 8px;border:1px solid var(--line);border-radius:99px;color:var(--muted);font-size:11px;text-transform:uppercase}.empty{padding:48px 22px;border:1px dashed var(--line);text-align:center}.runs{margin-top:10px}.run-card{border:1px solid var(--line);background:var(--panel);padding:18px;max-width:560px}.run-card .replay{margin:12px 0}.run-card h3{font-size:20px}.table-scroll{overflow-x:auto}.run-table{width:100%;max-width:1000px;table-layout:fixed;border-collapse:collapse;font-size:14px;text-align:center;white-space:nowrap}.c-num{width:6%}.c-rider{width:26%}.c-react{width:10%}.run-table th{padding:6px 8px;color:var(--muted);font:600 10px var(--ps-font-ui);letter-spacing:.1em;text-transform:uppercase;font-weight:600}.run-table thead th{border-bottom:1px solid var(--line)}.run-table thead th span{display:block;letter-spacing:0;text-transform:none;opacity:.75}.run-table thead tr:first-child th[colspan]{color:var(--green);border-left:1px solid var(--line)}.run-table thead tr:last-child th:nth-child(odd){border-left:1px solid var(--line)}.run-table td{position:relative;padding:7px 8px;font-variant-numeric:tabular-nums}.run-table tbody tr:nth-child(4n+3) td,.run-table tbody tr:nth-child(4n+3) th{background:rgba(255,255,255,.022)}.run-table td:nth-child(n+4):nth-child(even){border-left:1px solid var(--line)}.run-table .rider{text-align:left;overflow:hidden;text-overflow:ellipsis;color:var(--muted)}.run-table thead th:nth-child(2){text-align:left}.run-table .verdict{color:var(--ps-yellow);font-weight:700;font-size:.8em;text-transform:uppercase;letter-spacing:0}.run-toggle{width:100%;border:0;background:none;color:var(--green);font:700 14px var(--ps-font-ui);cursor:pointer;padding:2px 4px}.run-toggle::after{content:" ▸";font-size:10px;opacity:.7}.run-toggle[aria-expanded="true"]::after{content:" ▾"}.run-row:hover td,.run-row:hover th{background:rgba(46,201,75,.07)}.run-row.is-open td,.run-row.is-open th{background:rgba(46,201,75,.14)}.run-row.is-open th:first-child{border-left:2px solid var(--green)}.run-replay td{padding:14px 0 18px;text-align:left;white-space:normal}.run-replay.is-open td{border-left:2px solid var(--green);background:rgba(46,201,75,.05)}.stats-note{margin:12px 0 0;font-size:11px;color:var(--muted);white-space:normal}.chip.warn{border-color:var(--ps-yellow);color:var(--ps-yellow)}.download{color:var(--green);font-weight:700}dialog{width:min(480px,calc(100% - 32px));border:1px solid var(--line);border-radius:5px;padding:28px;background:#111;color:var(--white)}dialog::backdrop{background:rgba(0,0,0,.8)}dialog form{position:relative}.close{position:absolute;right:-10px;top:-18px;border:0;background:none;color:var(--muted);font-size:28px}label{display:grid;gap:8px;margin:18px 0;color:var(--muted)}input{width:100%;padding:13px;border:1px solid var(--line);background:#050505;color:var(--white);font:inherit}.form-error{color:#ff8c83}
.card-dialog{width:min(520px,calc(100% - 16px));padding:20px}
.print-card{display:flex;flex-direction:column;align-items:center;gap:18px;width:460px;max-width:100%;margin:0 auto 22px;padding:18px;background:#fff;color:#000;text-align:center;border:2px solid #000}
.print-card strong{display:block;width:100%;font:800 18px Arial,sans-serif;letter-spacing:.07em}
.print-card h2{font:800 25px Arial,sans-serif;margin:0}
.print-card .qr{display:block;width:100%;height:auto;margin:0}
.print-card .label-logo{display:block;width:75px;height:32px;object-fit:contain;filter:grayscale(1) brightness(0);margin:0}
#toast{position:fixed;right:20px;bottom:20px;padding:12px 18px;background:var(--green);color:#000;font-weight:700;opacity:0;pointer-events:none;transition:.2s}#toast.show{opacity:1}.sign-in{max-width:700px;padding-top:10vh}.sign-in p{font-size:18px}.sign-in .button{margin-top:18px}.sign-in .text-button{margin-top:14px}#sign-in-status{margin-top:22px;font-size:15px}.welcome-actions{display:flex;align-items:center;gap:14px;flex-wrap:wrap}.run-card .local-play{margin:12px 0;padding-left:0}@media(max-width:650px){.run-table{font-size:11px}.run-table th,.run-table td{padding-inline:2px;overflow:hidden}.run-table td{padding-block:6px}.run-table thead th{font-size:8px;letter-spacing:0}.run-table thead tr:last-child th{font-size:7px}.run-table .pb{padding:0 3px;font-size:7px}.c-num{width:8.5%}.c-rider{width:26%}.c-react{width:11.5%}.run-toggle{font-size:11px;padding-inline:0}.run-toggle::after{content:""}.run-replay td{padding-block:10px 14px}.welcome-actions{margin-top:16px}.account span{display:none}.welcome{display:block}.data-card{align-items:flex-start;flex-direction:column}.section-head{align-items:flex-start}.tabs{overflow:auto}.tabs button{padding-inline:14px}.brand span{font-size:14px}.card-dialog{padding:14px}.print-card{padding:12px;gap:16px}}@media print{body *{visibility:hidden}.card-dialog[open],.card-dialog[open] *,.print-card,.print-card *{visibility:visible}.card-dialog[open]{position:absolute;inset:0;border:0;padding:0}.card-dialog form{width:100%}.print-card{position:absolute;left:0;top:0;margin:0}.card-dialog button{display:none}}

/* Historic rider assignment: the row's own name is the control, and the sheet
   it opens is full height on the portrait capture phone. */
.assign-rider{width:100%;padding:0;border:0;background:none;color:inherit;font:inherit;text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:pointer;text-decoration:underline;text-decoration-color:var(--line);text-underline-offset:3px}.assign-rider:hover,.assign-rider:focus-visible{color:var(--green);text-decoration-color:var(--green)}select{width:100%;padding:13px;border:1px solid var(--line);background:#050505;color:var(--white);font:inherit}.assign-dialog{width:min(560px,calc(100% - 20px));max-height:calc(100dvh - 20px);overflow:auto}.or{display:flex;align-items:center;gap:12px;margin:24px 0 14px;color:var(--muted);font-size:12px;text-transform:uppercase;letter-spacing:.1em}.or:before,.or:after{content:"";height:1px;background:var(--line);flex:1}.scanner{position:relative;aspect-ratio:1;overflow:hidden;border-radius:5px;background:#050505}.scanner video{width:100%;height:100%;object-fit:cover}.scan-frame{position:absolute;inset:13%;border:3px solid var(--green);box-shadow:0 0 0 999px rgba(0,0,0,.35)}.scanner p{position:absolute;left:12px;right:12px;bottom:6px;margin:0;padding:8px;background:rgba(0,0,0,.75);color:#fff;text-align:center;font-size:13px}@media(max-width:600px){.assign-dialog{height:calc(100dvh - 20px);padding:24px 18px}.topbar{padding:0 14px}.account span{display:none}main{padding-left:16px;padding-right:16px}}

/* Account agreement: the adult sees the short key points and the exact,
   versioned documents before the gate opens. The dialog itself scrolls on a
   phone while each document keeps its own readable viewport. */
.check{display:grid;grid-template-columns:auto 1fr;align-items:start;gap:10px}.check input{width:auto;margin-top:.3em}.agreement-dialog{width:min(760px,calc(100% - 32px));max-height:calc(100vh - 32px);overflow:auto}.agreement-copy{padding:4px 16px;border:1px solid var(--line);background:#080808}.agreement-copy a{color:var(--green)}.agreement-summary-label{color:var(--green);font-size:.78rem;letter-spacing:.08em;text-transform:uppercase}.agreement-documents{display:grid;gap:8px;margin:12px 0 16px}.agreement-documents details{border:1px solid var(--line);background:#111}.agreement-documents summary{cursor:pointer;font-weight:700;padding:12px 14px}.agreement-documents summary a{float:right;color:var(--green);font-size:.82rem;font-weight:600}.agreement-documents iframe{display:block;width:100%;height:min(48vh,430px);border:0;border-top:1px solid var(--line);background:#fff}

/* The replay's own controls (web/replay-player.js).
 *
 * The browser's controls are a bar for a long film; a start is four seconds
 * and gets reviewed a frame at a time, so the picture carries its own. Three
 * pieces, and what makes each one appear and go away is the whole of the
 * design:
 *
 *  - the bar along the bottom: play, the timeline, the clock, full screen.
 *    It sits over the bottom of the picture, which is where
 *    pipeline/overlay.py draws the telemetry band — so it fades a couple of
 *    seconds after the last touch, leaving the numbers a rider stopped on
 *    clean underneath.
 *  - the frame steppers, in the top two corners, up for as long as the replay
 *    is paused. They do *not* fade, and being in the corners is what earns
 *    them that: the fade is there to uncover the telemetry band across the
 *    bottom quarter, and these are nowhere near it. A paused rider is a rider
 *    stepping, and the steppers used to disappear two seconds into the job.
 *  - the big centre button, shown only until the replay is first played.
 *
 * Full screen has a second, labelled button outside the picture, in the card
 * under it (`.replay-actions`) — the bar's own fades, and a phone has no
 * hover to bring it back. Nothing may be added *inside* the frame, so an
 * always-visible control has to live where the frame is not.
 *
 * The player box keeps the card's 16/9 shape whatever the clip is: a portrait
 * recording letterboxes inside it rather than resizing the row it opened from.
 */
.replay{position:relative;width:100%;aspect-ratio:16/9;background:#050505;overflow:hidden;
  /* Vertical drags still scroll the page; horizontal ones are ours to scrub. */
  touch-action:pan-y;-webkit-user-select:none;user-select:none}
.replay video{display:block;width:100%;height:100%;aspect-ratio:auto;object-fit:contain;background:#050505}
.replay:focus-visible{outline:2px solid var(--green);outline-offset:2px}
.rp-chrome{opacity:0;pointer-events:none;transition:opacity .18s ease}
.replay.rp-awake .rp-bar{opacity:1;pointer-events:auto}
.replay.rp-paused .rp-steps{opacity:1;pointer-events:auto}
.rp-bar{position:absolute;left:0;right:0;bottom:0;display:flex;align-items:center;gap:10px;padding:9px 12px;
  background:linear-gradient(to top,rgba(0,0,0,.85),rgba(0,0,0,0))}
.rp-play{flex:none;width:32px;height:32px;padding:0;border:0;background:none;color:var(--green);font-size:15px;line-height:1;cursor:pointer}
.rp-play::before{content:"\25B6"}
.replay:not(.rp-paused) .rp-play::before{content:"\275A\275A"}
.replay.rp-ended .rp-play::before{content:"\21BA"}
.rp-track{position:relative;flex:1;height:24px;cursor:pointer;touch-action:none}
.rp-track::before{content:"";position:absolute;top:50%;left:0;right:0;height:3px;margin-top:-1.5px;background:rgba(255,255,255,.3)}
.rp-fill{position:absolute;top:50%;left:0;width:0;height:3px;margin-top:-1.5px;background:var(--green)}
.rp-thumb{position:absolute;top:50%;left:0;width:13px;height:13px;margin-top:-6.5px;border-radius:50%;background:var(--green);transform:translateX(-50%)}
.rp-clock{flex:none;min-width:84px;text-align:right;color:var(--white);font:600 12px var(--ps-font-ui);
  font-variant-numeric:tabular-nums;letter-spacing:.03em}
.rp-steps{position:absolute;top:0;left:0;right:0;display:flex;justify-content:space-between;padding:8px}
.rp-step{width:38px;height:38px;padding:0;border:1px solid var(--ps-line-green);border-radius:50%;
  background:rgba(0,0,0,.6);color:var(--green);font:600 13px var(--ps-font-ui);cursor:pointer}
.rp-step:active{background:var(--ps-wash-green)}
.rp-poster{position:absolute;inset:0;display:grid;place-items:center;pointer-events:none}
.replay.rp-started .rp-poster{display:none}
.rp-big{width:64px;height:64px;padding:0 0 0 5px;border:1px solid var(--ps-line-green);border-radius:50%;
  background:rgba(0,0,0,.55);color:var(--green);font-size:24px;line-height:1;cursor:pointer;pointer-events:auto}
.rp-big::before{content:"\25B6"}
.rp-full{flex:none;width:32px;height:32px;padding:0;border:0;background:none;color:var(--green);font-size:17px;line-height:1;cursor:pointer}
.rp-full::before{content:"\2922"}
.replay.rp-fullscreen .rp-full::before{content:"\2921"}
@media(max-width:650px){.rp-clock{min-width:76px;font-size:11px}.rp-step{width:42px;height:42px}}

/* The buttons beside the picture: coaching feedback, full screen, download.
 *
 * This is the half of the full-screen control that a rider on a phone can
 * actually find — the bar's own fades, and there is no hover on a touch
 * screen to bring it back. It is a word rather than a glyph because it is the
 * only place the feature is named, and it is *here*, outside the frame,
 * because nothing may be drawn inside it.
 *
 * A row under the picture while the picture is as wide as the card; a column
 * to the right of it on a phone held sideways, where the picture is as wide as
 * its *height* allows and the card beside it is empty (see the landscape block
 * below). `.replay-frame` is the pair of them, and exists only so the two can
 * change places without the card's heading joining in. */
.replay-frame{display:block}
.replay-actions{display:flex;align-items:center;gap:18px;flex-wrap:wrap;margin:10px 0 0}
.replay-full{padding-left:0;padding-right:0;display:inline-flex;align-items:center;gap:8px}
.replay-full::before{content:"\2922";font-size:15px;line-height:1}
.replay-full.is-full::before{content:"\2921"}

/* Full screen.
 *
 * A card in a session list is the right size for picking a start out of a list
 * and too small for reading one, and a phone turned sideways should fill up
 * rather than letterbox into the same 16/9 card. So the player drops its card
 * shape and takes the screen, with its own controls still on it — the browser's
 * full screen where that exists, and a fixed cover where it does not (an
 * iPhone, where only the <video> can go full screen and handing it over would
 * swap in the operating system's controls).
 *
 * The picture still letterboxes inside whatever shape the screen is, because
 * `object-fit: contain` is the one thing that must not change: cropping a
 * replay would cut the telemetry band off the bottom of it.
 */
.replay.rp-fullscreen,.replay:fullscreen,.replay:-webkit-full-screen{width:100%;height:100%;max-height:none;aspect-ratio:auto;background:#000}
.replay.rp-cover{position:fixed;inset:0;z-index:60;margin:0;width:100vw;height:100dvh}
html.rp-covered,html.rp-covered body{overflow:hidden}
.replay.rp-fullscreen .rp-steps{padding:max(10px,env(safe-area-inset-top)) max(14px,env(safe-area-inset-right)) 0 max(14px,env(safe-area-inset-left))}
.replay.rp-fullscreen .rp-step{width:54px;height:54px;font-size:15px}
.replay.rp-fullscreen .rp-bar{padding:12px max(14px,env(safe-area-inset-right)) max(12px,env(safe-area-inset-bottom)) max(14px,env(safe-area-inset-left))}

/* Bringing a start on screen when its row is opened.
 *
 * `web/dashboard.js` scrolls the *picture* into view, not the panel round it,
 * and asks for the least movement that does the job (`block: 'nearest'`).
 * These margins are what "in view" means: clear of the sticky top bar above,
 * and far enough off the bottom to bring the card's own full-screen and
 * download buttons up with the picture rather than leaving them a scroll
 * away — which for the button that is meant to be the discoverable one would
 * miss the point entirely. */
.replay{scroll-margin-top:96px;scroll-margin-bottom:52px}
.session-run-head{margin-top:12px}.session-run-head h2{margin:0}

/* A phone held sideways, with the replay still in its row.
 *
 * 411 CSS pixels tall, 72 of them under a sticky top bar: the card has about
 * 340 to live in, and it was asking for 400 — the eyebrow, the heading and
 * the card's own padding stacked a hundred pixels above a picture whose only
 * limit was `100dvh` less a guess. So a start ran off the bottom of the
 * screen, and full screen — the right answer, one button away — was below
 * the fold.
 *
 * So: the eyebrow and the heading go on one line, everything loses padding,
 * and the picture is sized from what is left over rather than from the
 * viewport. `--replay-chrome` is that leftover, and it is one number in one
 * place so it can be checked against a real phone rather than rediscovered.
 * Width comes from it too — a 16/9 box the full width of a landscape phone
 * would be taller than the screen, so the picture is as wide as its height
 * allows, rather than letterboxed inside a box the wrong shape.
 *
 * Which leaves the rest of the row empty, and the buttons stacked underneath
 * eating the height the picture wanted. So sideways they sit in that empty
 * column instead: the picture keeps the left of the card, the buttons take the
 * right, and `--replay-chrome` comes down by the height of the row they used
 * to be — the picture is that much taller for it. The card gives up its
 * 560 px cap here, because that cap was the width of a picture that no longer
 * has anything beside it. */
@media(orientation:landscape) and (max-height:560px){
  .run-replay td{padding:8px 0 10px}
  .run-card{padding:10px 14px;max-width:none;--replay-chrome:148px}
  .run-card .eyebrow{display:inline;margin-right:12px}
  .run-card h3{display:inline;font-size:15px}
  /* The picture is as wide as its height allows, which on a phone held
     sideways leaves a column of card doing nothing beside it. That column is
     where the buttons go: side by side they cost the picture no height at
     all, which is the whole scarce thing here — every pixel they are not
     stacked under is a pixel of start. `--replay-chrome` is what is left for
     them to come out of, and it is smaller than it was for exactly that
     reason. Stacked again in portrait, where the width is the scarce side. */
  .replay-frame{display:flex;flex-wrap:wrap;align-items:flex-start;gap:14px}
  /* `:not(.rp-fullscreen)` because this sizes the picture *in its row*. A
     real fullscreen element is rescued by the browser's own `!important`
     sizing, but the stand-in cover used where the Fullscreen API is missing
     (an iPhone) is plain CSS, and a 393 px replay is not full screen. */
  .run-card .replay:not(.rp-fullscreen){flex:0 0 auto;margin:8px 0 0;
    width:min(100%,calc((100dvh - var(--replay-chrome))*16/9));
    max-height:calc(100dvh - var(--replay-chrome))}
  .replay{scroll-margin-top:84px;scroll-margin-bottom:46px}
  /* A column, not a row: one thumb-sized target under the next, each as wide
     as the column, so none of them is a word to aim at. */
  .replay-actions{flex:1 1 150px;flex-direction:column;align-items:stretch;
    gap:8px;min-width:150px;max-width:240px;margin:8px 0 0}
  .replay-actions .button{padding:11px 12px;text-align:center}
  .replay-actions .text-button,.replay-actions .download{padding:9px 4px;text-align:center}
  .replay-full{justify-content:center}
  .stats-note{margin-top:8px}
}

/* A club and its coaches.
 *
 * The card stops being one line the moment it has to hold a list of people, so
 * the club's own facts sit on the first row — name, what this account is to it,
 * whether the licence is live — and the coaches go underneath in a list that
 * reads down. Each coach is a name over their role and address: on a phone the
 * two stack rather than the address pushing the remove button off the side. */
.club-card{display:block}.club-head{display:flex;justify-content:space-between;align-items:flex-start;gap:16px}.club-actions{display:flex;flex-wrap:wrap;gap:4px;justify-content:flex-end}.club-actions .text-button{padding-right:0}.club-coaches{margin-top:18px;padding-top:14px;border-top:1px solid var(--line)}.coach-list{list-style:none;margin:10px 0 0;padding:0;display:grid;gap:2px}.coach-list li{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:7px 0}.coach-list li+li{border-top:1px solid rgba(255,255,255,.05)}.coach-list span{display:grid;gap:2px;min-width:0}.coach-list em{color:var(--muted);font-size:12px;font-style:normal;text-transform:uppercase;letter-spacing:.06em;overflow:hidden;text-overflow:ellipsis}.coach-list .chip{padding:2px 7px}.coach-empty{margin:10px 0 0;font-size:14px}

/* Coach review is metadata over the finished server-rendered replay; it never
   receives model points or recreates the telemetry drawing. */
.coach-ink{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:1}.coach-ink.editable{pointer-events:auto;touch-action:none;cursor:crosshair}.angle-label{font:700 30px var(--ps-font-ui);paint-order:stroke;stroke:#050505;stroke-width:5px}.coach-review{margin-top:18px;border-top:1px solid var(--line);padding-top:18px}.save-status{min-height:1.5em;margin:6px 0 0;font-size:13px}.coach-notes{margin-top:16px}.coach-note{padding:14px 0;border-bottom:1px solid var(--line)}.coach-note>p{margin:5px 0}.timed-note{display:block;width:100%;margin-top:7px;padding:10px 12px;border:1px solid var(--line);background:#080808;color:var(--muted);text-align:left;cursor:pointer}.timed-note:hover{border-color:var(--green)}.timed-note b{color:var(--green);margin-right:8px}

/* The studio: full screen, two modes, and one bar along the bottom.
 *
 * Draw mode gives the picture to the ink. Time mode (the clock in the toolbar)
 * takes it back: the tools grey out, `.coach-ink` loses `.editable` so a touch
 * on the picture does nothing, and the bar grows the buttons that mark the
 * first and last frame. Only one of the two is ever live, which is the whole
 * point — the previous version asked one finger to mean two things.
 *
 * `.coach-time` is the studio's own timeline and it replaces the player's for
 * as long as the studio is open (`.coach-editing .rp-bar` is hidden). It is
 * 44 px of touchable height rather than the player's 3 px line, it is the top
 * thing on the screen where it sits, and it carries its own pointer handlers —
 * which is why it can be dragged at all. Its height is what the frame-step
 * buttons are lifted clear of, so the two never overlap.
 *
 * A box around a toolbar button means "this is what the finger is for", and
 * nothing else may draw one: an outline on every meaning button made all three
 * read as chosen at once. Colour, not a box, is what tells them apart. */
.coach-studio{position:absolute;inset:0;z-index:4;pointer-events:none}
.coach-toolbar{position:absolute;top:max(8px,env(safe-area-inset-top));left:50%;transform:translateX(-50%);display:flex;align-items:center;gap:5px;max-width:calc(100% - 18px);padding:6px;border:1px solid var(--line);border-radius:7px;background:rgba(5,5,5,.9);overflow-x:auto;pointer-events:auto}.coach-toolbar button{flex:0 0 38px;display:grid;place-items:center;width:38px;height:38px;padding:0;border:1px solid transparent;border-radius:4px;background:#151515;color:var(--ps-white);font:700 19px var(--ps-font-ui);cursor:pointer}.coach-toolbar button.active{border-color:var(--green);box-shadow:inset 0 0 0 1px var(--green)}.coach-toolbar button[disabled]{opacity:.32;cursor:default}.coach-toolbar i{flex:0 0 1px;height:26px;background:var(--line)}.coach-toolbar .meaning.current{color:var(--ps-white)}.coach-toolbar .meaning.target{color:var(--ps-green)}.coach-toolbar .meaning.attention{color:var(--ps-red)}.coach-toolbar .save-annotations{color:var(--ps-green)}.coach-toolbar button svg{width:20px;height:20px;overflow:visible}.coach-toolbar .exit-annotations{font-size:27px;color:var(--ps-red)}
.coach-comments{position:absolute;top:62px;right:max(10px,env(safe-area-inset-right));width:min(360px,calc(100% - 20px));max-height:calc(100% - 214px);overflow:auto;padding:14px;border:1px solid var(--line);background:rgba(5,5,5,.96);pointer-events:auto}.coach-comments label{display:block;margin:8px 0}.coach-comments textarea{width:100%;min-height:62px;padding:9px;border:1px solid var(--line);background:#111;color:var(--ps-white);font:inherit}.comment-scope{margin:8px 0 0;color:var(--ps-silver);font:600 12px var(--ps-font-ui)}
.coach-editing .rp-poster,.coach-editing .rp-bar{display:none}
.coach-time{position:absolute;left:0;right:0;bottom:0;z-index:6;display:flex;flex-direction:column;gap:6px;padding:7px max(12px,env(safe-area-inset-right)) max(7px,env(safe-area-inset-bottom)) max(12px,env(safe-area-inset-left));background:linear-gradient(to top,rgba(0,0,0,.9),rgba(0,0,0,0));pointer-events:auto}
.time-row{display:flex;align-items:center;gap:8px;min-height:34px}.time-readout{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ps-white);font:600 13px var(--ps-font-ui);font-variant-numeric:tabular-nums}.time-buttons{display:flex;align-items:center;gap:6px;overflow-x:auto}.time-buttons button{flex:none;min-height:34px;padding:6px 11px;border:1px solid var(--line);border-radius:4px;background:#151515;color:var(--ps-white);font:600 12px var(--ps-font-ui);white-space:nowrap;cursor:pointer}.time-buttons .leave-time{border-color:var(--ps-green);color:var(--ps-green)}
.time-strip{position:relative;height:44px;cursor:pointer;touch-action:none}.time-strip:focus-visible{outline:2px solid var(--green);outline-offset:2px}.time-strip::before{content:"";position:absolute;top:50%;left:0;right:0;height:4px;margin-top:-2px;border-radius:2px;background:rgba(255,255,255,.28)}.time-fill{position:absolute;top:50%;left:0;width:0;height:4px;margin-top:-2px;border-radius:2px;background:var(--ps-green)}.time-thumb{position:absolute;top:50%;left:0;width:18px;height:18px;margin-top:-9px;border-radius:50%;background:var(--ps-green);box-shadow:0 0 0 2px #050505;transform:translateX(-50%)}
.time-selection{position:absolute;top:50%;height:16px;margin-top:-8px;min-width:4px;border:1px solid var(--ps-white);border-radius:8px;background:rgba(46,201,75,.45)}
.annotation-marks{position:absolute;inset:0;pointer-events:none}.annotation-marks i{position:absolute;top:5px;width:6px;height:12px;margin-left:-3px;border-radius:2px;background:var(--ps-silver)}.annotation-marks .annotation-range{top:7px;height:8px;margin-left:0;min-width:6px;border-radius:4px;background:rgba(161,161,160,.75)}
.coach-editing .rp-steps{top:auto;bottom:calc(96px + env(safe-area-inset-bottom));padding:0 max(10px,env(safe-area-inset-right)) 0 max(10px,env(safe-area-inset-left));z-index:5;opacity:1;pointer-events:none}.coach-editing .rp-step{width:44px;height:44px;pointer-events:auto}
@media(max-width:700px){.coach-toolbar{left:8px;right:8px;transform:none}.coach-toolbar button{flex-basis:36px;width:36px;height:36px}.coach-comments{top:58px;bottom:148px;max-height:none}.time-readout{flex:0 0 auto}.time-row{overflow-x:auto}}

/* The podium, and a rider's own best.
 *
 * Two marks on the same figures, and a cell can carry both — the fastest 1 m
 * time of the evening that is also the fastest this rider has ever ridden is a
 * gold cell with a PB on it. `web/dashboard.js` decides which cell gets what;
 * this is only how they are drawn, and both are drawn under one constraint:
 * **a row must stay one line of text tall.** Anything that grows a row costs a
 * coach a start off the bottom of the screen (docs/DASHBOARD_UI.md), so
 * neither mark may occupy a pixel of layout.
 *
 * A medal is therefore an `outline`, which is drawn outside the layout
 * entirely, pulled *inwards* by a negative offset so the box sits a few pixels
 * inside the cell. A border would have moved every number in the table across
 * by a pixel and would have fought the column dividers, which are borders too;
 * a ring on the cell's own edge butted straight into its neighbour's, so a row
 * that won six of them read as one long bar rather than six medals. The gap
 * and the corner radius are what make them separate things. The wash behind
 * them is an inset shadow with a spread far larger than any cell, so it fills
 * whatever width the column turns out to be, and it is faint: the ring is the
 * signal and a wash strong enough to notice on its own turned a good start
 * into a highlighted row.
 *
 * The figure inside stays white. Yellow *type* in this table already means a
 * verdict — JUMP, NO GO — so first place is yellow as an *edge* and nothing
 * else, and the two never read as each other. The colours are the countdown's
 * own lamps either side of the guide's silver; web/brand.css says why, and why
 * bronze is held back off full strength.
 *
 * The PB pill overlays the cell's top-right corner. A cell is wider than the
 * four characters in it, and the corner is space the table already has —
 * whereas a pill *beside* the number would need width the phone has not got
 * and a pill above it would need height nothing here may take. It clears the
 * digits because it is shorter than the line box they are centred in: at 9 px
 * it ends where their ink begins. Green because a personal best is the good
 * outcome and green is the colour that means one; it is also the one colour no
 * medal uses, so a gold cell with a PB on it stays two legible marks rather
 * than one muddy one. */
.run-table td.medal-gold,.run-table td.medal-silver,.run-table td.medal-bronze{border-radius:6px;outline-offset:-4px}
.run-table td.medal-gold{outline:1px solid var(--ps-gold);box-shadow:inset 0 0 0 999px var(--ps-wash-gold)}
.run-table td.medal-silver{outline:1px solid var(--ps-silver);box-shadow:inset 0 0 0 999px var(--ps-wash-silver)}
.run-table td.medal-bronze{outline:1px solid var(--ps-bronze);box-shadow:inset 0 0 0 999px var(--ps-wash-bronze)}
/* Inline by default, so the same pill can be shown inside the note under the
   table; absolute in a cell, where it is a corner overlay. `text-decoration`
   because <abbr title> is underlined by default and this is a badge, not a
   word in a sentence — the element is an <abbr> so that "PB" carries what it
   stands for to a screen reader without the row growing a label. */
.pb{display:inline-block;padding:0 4px;border-radius:99px;background:var(--ps-green);color:var(--ps-black);
  font:700 9px/1.25 var(--ps-font-ui);letter-spacing:.04em;text-transform:uppercase;
  text-decoration:none;vertical-align:.1em}
.run-table .pb{position:absolute;top:0;right:0}

.rider-run{align-items:flex-start}.rider-run>div{width:100%}.rider-run video{display:block;width:min(100%,42rem);margin-top:1rem;border-radius:8px;background:#000}

.setup-lead{max-width:760px;font-size:18px}.setup-lead strong,.mounting-guide strong{color:var(--white)}.marker-sheets{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px;margin:30px 0}.marker-sheet{display:grid;grid-template-columns:minmax(150px,42%) 1fr;align-items:center;gap:20px;padding:18px;border:1px solid var(--line);border-radius:4px;background:var(--panel)}.marker-sheet h3{margin-bottom:18px}.marker-preview{aspect-ratio:1.414/1;overflow:hidden;border:1px solid #555;background:#fff}.marker-preview img{display:block;width:100%;height:100%;object-fit:contain}.mounting-guide{max-width:860px;padding:26px;border-left:3px solid var(--green);background:var(--panel)}.mounting-guide ol{margin:18px 0;padding-left:22px;color:var(--white)}.mounting-guide li{margin:10px 0;padding-left:6px}.responsibility{margin:22px 0 0;padding-top:18px;border-top:1px solid var(--line)}
@media(max-width:760px){.marker-sheets{grid-template-columns:1fr}.marker-sheet{grid-template-columns:1fr}.marker-preview{width:100%}.mounting-guide{padding:20px}}
