/* Dark, whatever the browser is set to.
   This is a camera application: the screen it is used beside is part of the scene, and on a laptop that opened light
   while its neighbour opened dark, the two devices were being asked to meter quite different rooms. A theme that
   follows a machine setting is also a variable nobody records, which is the last thing a test bench needs. */
:root {
  color-scheme: dark;
  --fg: #e6e6e8;
  --muted: #a2a2ac;
  --bg: #161618;
  --panel-bg: #202024;
  --border: #3a3a40;
  --accent: #7aa2ff;
  --backdrop: rgb(0 0 0 / 65%);
  --shadow: rgb(0 0 0 / 45%);
  --screen-padding: 1.25rem;
}

html {
  /* Keep text at the author's size when a phone is rotated to landscape. */
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
}

/* Fills the visible viewport on a phone, where dynamic browser chrome makes 100vh overshoot. */
.app {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  box-sizing: border-box;
  width: 100%;
  max-width: 32rem;
  height: 100vh;
  height: 100dvh;
  margin: 0 auto;
  padding:
    calc(1rem + env(safe-area-inset-top))
    calc(1.25rem + env(safe-area-inset-right))
    calc(1.5rem + env(safe-area-inset-bottom))
    calc(1.25rem + env(safe-area-inset-left));
}

/* The bench is the one screen not meant for a phone. Every other view is deliberately held to a hand's width, and
   that frame is what left a portrait strip in the middle of a desktop window. */
.app.wide {
  max-width: none;
}

.content {
  display: flex;
  flex: 1;
  flex-direction: column;
  justify-content: center;
  /* Without this a flex child refuses to shrink below its content, and the traces would be pushed off-screen. */
  min-height: 0;
}

button,
.login-button {
  /* Suppresses the 300ms tap delay and the grey flash some mobile browsers paint on tap. */
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

.home {
  text-align: center;
}

/* The only part of the app a visitor sees before signing in, so it has to carry the whole explanation on its own. */
.intro {
  margin: 0 auto 2rem;
  max-width: 26rem;
  text-align: left;
}

.intro-title {
  margin: 0 0 0.25rem;
  font-size: 1.75rem;
  font-weight: 600;
  text-align: center;
}

.intro-lead {
  margin: 0 0 1.25rem;
  font-size: 1.05rem;
  text-align: center;
}

.intro-detail {
  margin: 0 0 0.9rem;
  color: var(--muted);
  font-size: 0.95rem;
  line-height: 1.5;
}

/* Centred by the box rather than by its padding: as an inline-block, whatever the minimum height added beyond the
   line box fell entirely below the text, which sat high in the button. */
.login-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 2.75rem;
  padding: 0.75rem 1.5rem;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--fg);
  font-size: 1rem;
  font-weight: 600;
  text-decoration: none;
}

.user-actions {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: flex-end;
  gap: 1.25rem;
  min-height: 2.75rem;
}

.welcome {
  margin: 0 0 0.35rem;
  font-size: 1.5rem;
  font-weight: 600;
  text-align: center;
}

.selection-prompt {
  margin: 0 0 1.5rem;
  color: var(--muted);
  text-align: center;
}

.mode-choices {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}

@media (min-width: 34rem) {
  .mode-choices {
    grid-template-columns: 1fr 1fr;
  }
}

.mode-button {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  min-height: 5.5rem;
  padding: 1.1rem;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: 12px;
  color: var(--fg);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.mode-button:active {
  border-color: var(--accent);
}

.mode-title {
  font-size: 1.05rem;
  font-weight: 600;
}

.mode-description {
  color: var(--muted);
  font-size: 0.9rem;
  line-height: 1.35;
}

.screen {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: var(--screen-padding);
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: 12px;
}

.screen-title {
  margin: 0;
  font-size: 1.25rem;
  font-weight: 600;
}

.back-button {
  align-self: flex-start;
  min-height: 2.75rem;
  padding: 0.65rem 1.2rem;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--fg);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

.camera {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

/* The frame carries the stream's own aspect ratio (set from the track), so the video fills it exactly with no
   letterboxing. That is what lets the quadrant lines below sit at a true 50% of what the camera actually sees.
   max-height shrinks the whole box, width included, rather than cropping it. */
/* The box takes the camera's own shape, whatever that is: the aspect ratio is set from the live stream, and both
   limits are maxima rather than a fixed width. Pinning the width and capping the height made the box wider than the
   frame it held, and `cover` then cropped the picture to fit -- so the quadrant lines drawn over it marked
   quadrants that were not the ones being sampled, on a phone standing portrait especially. */
.camera-frame {
  position: relative;
  /* Sized by whichever limit binds first -- the column's width or the height allowance -- with the aspect ratio
     held either way. A plain `max-height` cannot do this: it clips the box without narrowing it, leaving the frame
     wider than the picture it holds. The live stream's shape arrives as --frame-aspect. */
  --frame-aspect: 1.7778;
  --camera-frame-height: 30vh;
  --camera-frame-height: 30dvh;
  width: min(100%, calc(var(--camera-frame-height) * var(--frame-aspect)));
  max-height: var(--camera-frame-height);
  height: auto;
  aspect-ratio: var(--frame-aspect);
  margin: 0 auto;
  background: #000;
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
}

.camera-video {
  display: block;
  width: 100%;
  height: 100%;
  /* Contained, never cropped. The box already carries the frame's proportions, so there is nothing to letterbox in
     practice -- but if the two ever disagree, the honest failure is a visible margin rather than a hidden crop. */
  object-fit: contain;
}

/* The last good frame covers the brief automatic-to-fixed transition. Some Android camera drivers first paint a
   value-less manual exposure before accepting the values that belong to it; that frame is useful to the verifier but
   not to the person lining up a movement. Kept in the same box as the video so changing camera shapes cannot disturb
   the layout or add any overflow. */
.camera-freeze {
  position: absolute;
  z-index: 1;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  background: #000;
  object-fit: contain;
  pointer-events: none;
  visibility: hidden;
}

.camera-freeze.visible {
  visibility: visible;
}

/* Only the picture is flipped. The quadrant lines are drawn by a sibling, so they do not move, and the sampler
   flips the frame to match — Q1 stays the top-right of whatever is on screen. */
.camera-frame.mirrored .camera-video,
.camera-frame.mirrored .camera-freeze {
  transform: scaleX(-1);
}

/* Two hairlines dividing the frame into the four quadrants that are actually sampled. */
.quadrant-lines {
  position: absolute;
  z-index: 2;
  inset: 0;
  pointer-events: none;
}

.quadrant-lines::before,
.quadrant-lines::after {
  content: "";
  position: absolute;
  background: #22c55e;
  /* Readable against both a bright and a dark scene. */
  box-shadow: 0 0 2px rgb(0 0 0 / 70%);
}

.quadrant-lines::before {
  top: 50%;
  right: 0;
  left: 0;
  height: 2px;
  transform: translateY(-1px);
}

.quadrant-lines::after {
  top: 0;
  bottom: 0;
  left: 50%;
  width: 2px;
  transform: translateX(-1px);
}

/* The foot of the panel: reset at the left, the lock reading filling the rest. Sharing the row rather than
   floating over it means the button occupies the corner without costing height or covering the text. */
/* Three tracks: the control, the line, and the gauge. The outer two are the same width, so the middle one lands on
   the centre of the panel by construction -- where the flex row needed a spacer element kept in step with the button
   by hand, and the reading drifted off centre whenever the two fell out of step. */
.control-row {
  display: grid;
  grid-template-columns: var(--control-size) minmax(0, 1fr) var(--control-size);
  align-items: center;
  gap: 0.75rem;
}

/* The room above the movement, in the counterweight's track. Sized by content, so a gauge with nothing to say takes
   no height and stays centred on the control opposite. */
.gauge {
  display: grid;
  place-items: center;
  width: var(--control-size);
  min-height: 1.15rem;
  border-radius: 6px;
  font-size: 0.7rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: transparent;
}

.control-row .lock-state {
  min-width: 0;
  /* The standalone label carried a bottom margin; inside the row it would lift the text off the button's centre. */
  margin-bottom: 0;
}

/* Colour carries the band and the number carries the value, so neither alone has to be relied on: green means
   "this will not stop you counting", red means "this will". */
.signal-badge.strong {
  background: rgb(53 196 106 / 18%);
  color: #35c46a;
}

.signal-badge.adequate {
  background: rgb(245 158 11 / 18%);
  color: #f59e0b;
}

.signal-badge.weak {
  background: rgb(239 68 68 / 18%);
  color: #ef4444;
}

/* One size for the control and the gauge opposite it, named so the grid's outer tracks and the button cannot fall
   out of step -- which is what the old spacer element existed to prevent, and sometimes did not. */
:root {
  --control-size: 2.5rem;
}

.workout-button {
  width: var(--control-size);
  height: var(--control-size);
}

.workout-button {
  display: grid;
  place-items: center;
  padding: 0;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 50%;
  color: var(--fg);
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

.workout-button:active {
  background: var(--bg);
}

@media (hover: hover) {
  .workout-button:hover {
    border-color: var(--fg);
  }
}

.camera-status {
  margin: 0;
  color: var(--muted);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
}

.camera-status .error {
  margin: 0 0 0.75rem;
  font-size: 0.95rem;
  line-height: 1.4;
}

/* The dashboard has a palette of its own, and deliberately not the one the rest of the app is painted in.
   It is read from across a room rather than held at arm's length, usually on a tablet propped against something, and
   what it has to do at that distance is separate two figures at a glance. Amber is the movement and cyan the energy;
   everything else on the screen is greyscale so that neither has anything to compete with. */
.dashboard-view {
  display: grid;
  grid-template-rows: minmax(0, 1fr);
  /* Dynamic viewport height, so a phone's collapsing address bar does not leave the panel short. */
  height: 100dvh;
  min-height: 0;
  box-sizing: border-box;

  --dash-bg: #0b0d0f;
  --dash-panel: #111517;
  --dash-line: #1d2326;
  --dash-fg: #e9edec;
  --dash-muted: #798887;
  --dash-reps: #e8b04b;
  --dash-calories: #5ec8d8;
  /* Replaced at runtime with the smallest single-line fit across every member of each group. The fallback is itself
     viewport-relative so the dashboard never falls back to a device-dependent fixed text size. */
  --dashboard-label-size: 2vw;
  --dashboard-title-size: var(--dashboard-label-size);
  --dashboard-clock-size: var(--dashboard-label-size);
  --dashboard-footnote-label-size: var(--dashboard-label-size);
  --dashboard-secondary-value-size: 4vw;
  --dashboard-detail-size: 1.2vw;
  --dashboard-figure-size: clamp(4rem, 17vmin, 13rem);
}

.screen.dashboard {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: 1.25rem;
  /* So the cards below can be told to take what is left, rather than being pushed past the bottom. */
  min-height: 0;
  background: var(--dash-bg);
  border-color: var(--dash-line);
  color: var(--dash-fg);
  overflow: hidden;
}

.dashboard-header {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 32vw);
  align-items: start;
  gap: 1rem;
}

/* A phone needs a little more of the header for the clock. The clock still participates in the shared label-size
   calculation; this only stops its narrow grid track from being the reason every label is reduced. */
@media (max-width: 768px) {
  .dashboard-header {
    grid-template-columns: minmax(0, 1fr) minmax(0, 38vw);
    align-items: start;
    gap: 1vw;
  }

  .screen.dashboard .dashboard-clock {
    align-items: start;
    gap: 1vw;
  }

  /* Match the clock's visible glyph top to the smaller title's cap height. The two grid boxes already share a top
     edge, but the font's larger ascent otherwise makes the clock appear several pixels lower. */
  .screen.dashboard .clock-value {
    transform: translateY(-0.14em);
  }

  .screen.dashboard .screen-title {
    letter-spacing: 0.04em;
    text-transform: none;
  }

  .figure-card {
    padding-right: 3vw;
    padding-left: 3vw;
  }

  .figure-footnotes {
    gap: 2vw;
  }

  .figure-footnote + .figure-footnote {
    padding-left: 2vw;
  }
}

.dashboard-title {
  display: grid;
  grid-template-rows: auto auto;
  gap: 0.2rem;
  min-width: 0;
}

.dashboard .screen-title {
  display: block;
  width: 100%;
  font-size: var(--dashboard-label-size);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  /* A long exercise name must not push the clock off the screen. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dashboard-basis {
  color: var(--dash-muted);
  font-size: var(--dashboard-detail-size);
  white-space: nowrap;
}

.dashboard-clock {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.75rem;
}

.clock-value {
  display: block;
  width: 100%;
  color: var(--dash-fg);
  font-size: var(--dashboard-label-size);
  font-variant-numeric: tabular-nums;
  text-align: center;
  white-space: nowrap;
}

/* Two cards, side by side when there is width for them and stacked when there is not. The dashboard runs on a tablet
   that gets turned over, so this is the one place the layout genuinely has to answer to the shape of the screen --
   and auto-fit does it from the available width rather than from a guess about which device is in use. */
.figure-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 1rem;
  min-height: 0;
}

/* Stacked when the tablet is stood up, from the orientation itself rather than from a width threshold: a portrait
   tablet is wide enough for two columns and should still not use them, because two tall narrow cards give a figure
   less room than one wide short one does. */
@media (orientation: portrait) {
  .figure-cards {
    grid-template-columns: minmax(0, 1fr);
  }
}

.figure-card {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  min-height: 0;
  /* So the figure inside can be sized against the card it has to fit in rather than against the window. A calorie
     total is routinely six characters wide -- reps times a body weight times a factor -- and a numeral sized from
     the viewport alone ran off the side of its own card at exactly the moment the figure got interesting. */
  container-type: inline-size;
  padding: 1.4rem 1.5rem;
  background: var(--dash-panel);
  border: 1px solid var(--dash-line);
  border-radius: 18px;
  overflow: hidden;
}

.figure-label {
  display: block;
  width: 100%;
  color: var(--dash-muted);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* Sized from the viewport rather than in rem: the whole point of this screen is to be legible from further away than
   a phone is held, and on a tablet in landscape that is a much larger number than any fixed size would give.
   Centred in whatever the label and the footnotes leave, rather than sitting under the label with the rest of the
   card empty beneath it -- which is what a card stretched to the height of a tablet does to a number of its own size. */
.figure-value {
  display: grid;
  place-items: center;
  min-height: 0;
  width: 100%;
  font-size: clamp(4rem, 17vmin, 13rem);
  /* Second, so a browser that does not know container units keeps the line above. */
  font-size: min(clamp(4rem, 18vmin, 13rem), 20cqi);
  /* Halfway between the ordinary 400 and bold 700 weights: visibly firmer without becoming a bold display face. */
  font-weight: 550;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.03em;
  line-height: 1.02;
  text-align: center;
  white-space: nowrap;
}

.reps-card .figure-value {
  color: var(--dash-reps);
}

.calories-card .figure-value {
  color: var(--dash-calories);
}

.figure-footnotes {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem;
  /* At the foot of the card, so the two cards' footnotes sit on one line however tall the numerals above them have
     been drawn. */
  padding-top: 1.2rem;
}

.figure-footnote {
  display: grid;
  grid-template-rows: auto auto;
  gap: 0.25rem;
  min-width: 0;
}

.figure-footnote + .figure-footnote {
  padding-left: 1rem;
  border-left: 1px solid var(--dash-line);
}

.footnote-label {
  display: block;
  width: 100%;
  color: var(--dash-muted);
  font-weight: 500;
  letter-spacing: 0.14em;
  line-height: 1.1;
  text-transform: uppercase;
  white-space: nowrap;
}

.figure-label,
.dashboard .screen-title {
  font-size: var(--dashboard-label-size);
}

.dashboard .screen-title {
  font-size: var(--dashboard-title-size);
}

.clock-value {
  font-size: var(--dashboard-clock-size);
}

.footnote-label {
  font-size: var(--dashboard-footnote-label-size);
}

.footnote-value {
  display: block;
  width: 100%;
  font-size: var(--dashboard-secondary-value-size);
  font-weight: 300;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.reps-card .footnote-value {
  color: var(--dash-reps);
}

.calories-card .footnote-value {
  color: var(--dash-calories);
}

.dashboard-status {
  display: grid;
  grid-template-columns: var(--control-size) minmax(0, 1fr) var(--control-size);
  align-items: center;
  gap: 0.75rem;
}

.dashboard-status .lock-state {
  margin: 0;
  color: var(--dash-muted);
  font-size: var(--dashboard-detail-size);
  text-align: center;
}

.dashboard .workout-button {
  display: grid;
  place-items: center;
  border-color: var(--dash-line);
  color: var(--dash-fg);
  font-size: var(--dashboard-label-size);
}

.dashboard .menu-button {
  --hamburger-width: 7vw;
  display: grid;
  place-items: center;
  width: 12vw;
  height: 12vw;
  min-width: 12vw;
  min-height: 12vw;
  border-radius: 2vw;
}

@media (min-width: 48rem) {
  .dashboard .menu-button {
    --hamburger-width: 3.5vw;
    width: 6vw;
    height: 6vw;
    min-width: 6vw;
    min-height: 6vw;
    border-radius: 1vw;
  }
}

@media (min-width: 80rem) {
  .dashboard .menu-button {
    --hamburger-width: 2.8vw;
    width: 4.8vw;
    height: 4.8vw;
    min-width: 4.8vw;
    min-height: 4.8vw;
    border-radius: 0.8vw;
  }
}

@media (min-width: 100rem) {
  .dashboard .menu-button {
    --hamburger-width: 2vw;
    width: 3.5vw;
    height: 3.5vw;
    min-width: 3.5vw;
    min-height: 3.5vw;
    border-radius: 0.6vw;
  }
}

@media (min-width: 160rem) {
  .dashboard .menu-button {
    --hamburger-width: 1.3vw;
    width: 2.2vw;
    height: 2.2vw;
    min-width: 2.2vw;
    min-height: 2.2vw;
    border-radius: 0.4vw;
  }
}

/* At and below the breakpoint, every dashboard dimension follows one continuous measured scale. The former 12vw
   button became 6vw one pixel later, visibly moving the clock; the clamp meets the wide rule at the boundary and keeps
   a usable touch target as the viewport narrows. The headline is part of the same scale as the labels and footnotes,
   so their proportions cannot drift while the window is resized. */
@media (max-width: 768px) {
  .figure-value {
    font-size: var(--dashboard-figure-size);
  }

  .dashboard .menu-button {
    --hamburger-width: clamp(1.2rem, 3.5vw, 1.7rem);
    width: clamp(2.75rem, 6vw, 3rem);
    height: clamp(2.75rem, 6vw, 3rem);
    min-width: clamp(2.75rem, 6vw, 3rem);
    min-height: clamp(2.75rem, 6vw, 3rem);
    border-radius: clamp(0.5rem, 1vw, 0.65rem);
  }
}

.dashboard .menu-sheet.open {
  display: grid;
}

/* The bench is the one screen built for a desktop in landscape: the canvas is what a camera on a tripod frames, so
   it is given the room, and everything the operator reads sits beside it rather than over it. Anything drawn on top
   of the canvas would be movement the detector could count. */
.bench-view {
  display: flex;
  flex-direction: row;
  align-items: stretch;
  gap: 1.5rem;
  /* The height its container gives it, rather than the whole viewport: it already sits inside the app's own frame
     and padding, and taking the viewport again would push its own bottom off the screen. */
  height: 100%;
  min-height: 0;
  box-sizing: border-box;
}

/* Square where there is room for it: the trajectories are stated as fractions and survive a stretched panel, but a
   camera framing a square loosely in one axis makes the error obvious in either. Height drives the width, since with
   `flex: 1` the basis wins and the aspect ratio is quietly ignored.

   It gives way rather than pushing the columns beside it off the screen, which is what a hard `flex: 0 0 auto` did:
   the results table was squeezed to a single letter. The figure stays correct when it does -- it is drawn on a square
   field centred in whatever the canvas turns out to be -- and the four crosses now mark that field, which is a better
   framing target than the panel's own edges ever were. */
.bench-stage {
  position: relative;
  flex: 0 1 auto;
  height: 100%;
  max-width: 100%;
  min-width: 0;
  aspect-ratio: 1;
  align-self: center;
}

.bench-canvas {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 8px;
}

/* Centred over the canvas but not covering it: the canvas now carries the framing crosses, and a scrim across the
   whole stage would dim the one thing the camera is being aimed at. The text takes its own plate instead. */
.bench-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  padding: 2rem;
  box-sizing: border-box;
  text-align: center;
}

.bench-overlay p {
  margin: 0;
  padding: 0.6rem 0.9rem;
  border-radius: 8px;
  background: rgb(0 0 0 / 80%);
  color: #ffffff;
}

.bench-readouts {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.5rem;
  flex: 0 1 16rem;
  /* Never narrower than its widest row of controls, so a menu or a button is not squeezed below its own label. */
  min-width: min-content;
}

/* What was actually shown: the truth the other number is being judged against. */
.bench-reference .rep-count-value,
.bench-reference .rep-count-label {
  color: #ffffff;
}

/* The detector's answer, green while it is defensible and red once it is not. Colour is not the only signal --
   the number itself is beside its reference — so this reads the same to anyone who cannot separate the two. */
.bench-acquired .rep-count-value,
.bench-acquired .rep-count-label {
  color: #35c46a;
}

.bench-acquired.astray .rep-count-value,
.bench-acquired.astray .rep-count-label {
  color: #ef4444;
}

/* Each figure against its own label, so none of them can be read as counting the wrong thing. */
.bench-progress {
  display: grid;
  grid-template-columns: auto auto;
  gap: 0.15rem 1rem;
  justify-content: center;
  margin-top: 1.5rem;
  color: var(--muted);
  font-size: 0.85rem;
}

.bench-stat {
  display: contents;
}

.bench-stat-label {
  text-align: right;
}

.bench-stat-value {
  text-align: left;
  color: var(--fg);
  font-variant-numeric: tabular-nums;
}

/* Both controls beside the readouts rather than over the panel: reaching for a button on the panel puts a hand and a
   pointer in front of the camera, and the detector counts whatever the camera sees. Side by side and of one size,
   because stacking two different shapes reads as a main action and an afterthought, which is not the relationship --
   one starts a quarter of an hour of measurement and the other leaves. */
/* Menus and buttons share one rule for their rows: as many equal columns as there are controls, each as wide as the
   widest control in the row. The row is sized to its content, and equal fractions in a grid sized that way all
   resolve to the largest item, so no control is squeezed below its own label and none is wider than another. */
.bench-choices,
.bench-controls {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 0.5rem;
  margin-top: 1rem;
  width: max-content;
  align-self: center;
}

/* Matched to .bench-control so the menus and the buttons read as one block of controls. The native arrow is left
   in place: it is what marks these as choices rather than buttons, which sit directly beneath them. */
/* Stretched to its column: a select is a replaced element, and a grid cell leaves those at their own width otherwise. */
.bench-choice {
  justify-self: stretch;
  padding: 0.55rem 1rem;
  text-align: center;
  text-align-last: center;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--panel-bg);
  color: var(--fg);
  font: inherit;
}

.bench-choice:disabled {
  opacity: 0.45;
  cursor: default;
}

.bench-control {
  padding: 0.7rem 1rem;
  text-align: center;
  white-space: nowrap;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--panel-bg);
  color: var(--fg);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

.bench-control:disabled {
  opacity: 0.45;
  cursor: default;
}

.takeover-dialog p {
  margin: 0 0 1rem;
  color: var(--muted);
}

.takeover-actions {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

/* Plain buttons rather than the tall role cards the class is shaped for: these carry one line, not a title and a
   description, and at card height they read as a menu of modes rather than an answer to a question. */
.takeover-actions .mode-button {
  display: block;
  padding: 0.7rem 1rem;
  min-height: 0;
  text-align: center;
  font-weight: 600;
}

/* The third column, and the one a finished run is actually read from. The tally says how many failed; only this
   says which, and whether the failures share a figure, a theme, or nothing. */
.bench-results {
  display: flex;
  flex-direction: column;
  justify-content: center;
  flex: 0 1 17rem;
  min-width: 0;
  overflow-y: auto;
}

.bench-results-table {
  display: grid;
  grid-template-columns: auto 1fr auto auto auto;
  gap: 0.15rem 0.7rem;
  align-content: center;
  font-size: 0.85rem;
}

.bench-result {
  display: contents;
}

/* Wraps rather than ellipsing. There are only six rows, so a second line costs nothing, and "square, li..." hides
   exactly the half that says which of the two runs of that figure this was. */
.bench-result-name {
  color: var(--muted);
  overflow-wrap: anywhere;
}

.bench-result-number {
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--fg);
}

/* The header row reads as labels, not as a result of its own. */
.bench-result.heading span {
  color: var(--muted);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  border-bottom: 1px solid var(--border);
  padding-bottom: 0.2rem;
  margin-bottom: 0.2rem;
}

.bench-result-pick {
  display: grid;
  align-items: center;
}

.bench-result-pick input {
  margin: 0;
}

/* A test left out of the suite stays listed, so it can be ticked back in. */
.bench-result.excluded .bench-result-name,
.bench-result.excluded .bench-result-number {
  text-decoration: line-through;
  opacity: 0.35;
}

/* A test still to come holds its place rather than appearing later and shifting everything below it. */
.bench-result.pending span {
  color: var(--muted);
  opacity: 0.55;
}

.bench-result.passed .bench-result-number {
  color: #35c46a;
}

.bench-result.failed .bench-result-name,
.bench-result.failed .bench-result-number {
  color: #ef4444;
}

.acquirer-view {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 0.75rem;
  /* Nothing here scrolls: the panel takes what it needs and the traces take whatever is left. */
  min-height: 0;
  overflow: hidden;
}

.acquirer-view .screen {
  position: relative;
  flex: none;
}

.acquirer-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
}

.menu-button {
  --hamburger-width: 1.2rem;
  display: grid;
  place-items: center;
  min-width: 2.75rem;
  min-height: 2.75rem;
  padding: 0;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--fg);
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
}

/* The SVG's square viewBox and equally spaced strokes centre the visible drawing itself, independent of platform font
   metrics. `display: block` also removes the baseline gap that an inline SVG would otherwise reserve underneath. */
.menu-icon {
  display: block;
  width: var(--hamburger-width);
  height: var(--hamburger-width);
  fill: none;
  stroke: currentColor;
  stroke-width: 2.25;
  stroke-linecap: round;
  pointer-events: none;
}

/* Catches a tap anywhere outside the menu. Behind the menu, in front of everything else. */
.menu-backdrop {
  position: fixed;
  z-index: 900;
  inset: 0;
}

/* Two by two, in the same arrangement as the quadrants they show. */
.signal-graphs {
  display: grid;
  flex: 1;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  gap: 0.5rem;
  min-height: 0;
}

/* Each trace boxed separately so one quadrant's movement cannot be mistaken for another's. */
.signal-pane {
  position: relative;
  /* Sized by the grid cell, so the traces shrink to whatever room the panel leaves them. */
  min-height: 0;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
}

.signal-canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* The quadrant the count is taken from, or the one corroborating it. One position for both roles -- a quadrant is
   never both -- so the marker sits in the same place on every pane and only its colour says which. Top-right,
   opposite the label, and without a transition: a change of leader is the event worth seeing, and a fade smears it. */
.channel-dot {
  position: absolute;
  top: 0.3rem;
  right: 0.3rem;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
}

/* Blue against the green trace, so it reads as a marker rather than as part of the signal. */
.channel-dot.leader {
  background: #3b82f6;
  opacity: 1;
}

/* Purple: next to blue in hue, so the pair reads as a pair, and nowhere near the green trace or a failing red. */
.channel-dot.partner {
  background: #a855f7;
  opacity: 1;
}


/* Three columns with equal, flexible outer ones: the middle column lands on the centre of the panel whatever the
   label beside it is, so the number itself is centred rather than the number-and-label pair. minmax(0, ...) keeps a
   long label from stealing width from column three and pushing the number off centre on a narrow screen. */
.rep-count {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.25rem 0;
}

/* Readable at arm's length on a phone propped against something, which is how this gets used — but no longer so
   large that it crowds the traces below it. */
.rep-count-value {
  grid-column: 2;
  font-size: clamp(2.75rem, 18vw, 4.5rem);
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.rep-count-label {
  grid-column: 3;
  /* Left-justified, so it keeps the gap it had beside the number instead of drifting out with the column. */
  justify-self: start;
  color: var(--muted);
  font-size: 1rem;
}

.lock-state {
  margin: 0 0 0.5rem;
  color: var(--muted);
  font-size: 0.85rem;
  text-align: center;
}

.menu-sheet {
  --menu-text-size: 4.5vw;
  position: fixed;
  z-index: 1000;
  top: calc(var(--menu-anchor-top, 0px) + 0.75vw);
  right: var(--menu-anchor-right, 0px);
  display: none;
  grid-auto-flow: row;
  align-items: stretch;
  min-width: 12rem;
  padding: 0.35rem;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 0.5rem 1.5rem var(--shadow);
}

.menu-sheet.open {
  display: grid;
}

.menu-item {
  /* Buttons and links share this class: an entry that opens a page and one that runs an action are the same thing
     to whoever is reading the menu, so the underline that marks one of them as a link has to go. */
  text-decoration: none;
  padding: 0.7rem 0.9rem;
  background: transparent;
  border: 0;
  border-radius: 6px;
  color: var(--fg);
  font: inherit;
  font-size: var(--menu-text-size);
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
}

@media (min-width: 48rem) {
  .menu-sheet {
    --menu-text-size: 2.1vw;
  }
}

@media (min-width: 80rem) {
  .menu-sheet {
    --menu-text-size: 1.5vw;
  }
}

@media (min-width: 100rem) {
  .menu-sheet {
    --menu-text-size: 1.05vw;
  }
}

@media (min-width: 160rem) {
  .menu-sheet {
    --menu-text-size: 0.75vw;
  }
}

.menu-item:active {
  background: var(--bg);
}

@media (hover: hover) {
  .menu-item:hover {
    background: var(--bg);
  }
}

@media (max-height: 720px) {
  .camera-frame {
    /* The same shape, given a little less room. Expressed as a width because that is what sizes this box now;
       a `max-height` here would clip the frame rather than narrow it. */
    --camera-frame-height: 26vh;
    --camera-frame-height: 26dvh;
  }

  .rep-count-value {
    font-size: clamp(2.5rem, 14vw, 3.5rem);
  }
}

/* A phone on its side has very little block space. Keep every control and a useful trace area in the viewport by
   compacting only this screen; the camera retains its exact aspect ratio and still shows the complete frame. */
@media (max-height: 500px) and (orientation: landscape) {
  .screen.acquirer {
    gap: 0.4rem;
    padding: 0.65rem;
  }

  .acquirer-header .screen-title {
    font-size: 1rem;
  }

  .acquirer-header .menu-button {
    min-width: 2.25rem;
    min-height: 2.25rem;
  }

  .camera {
    gap: 0.25rem;
  }

  .watcher-dots {
    min-height: 0.75rem;
  }

  .rep-count {
    padding: 0;
  }

  .rep-count-value {
    font-size: 2rem;
  }

  .rep-count-label,
  .lock-state {
    font-size: 0.75rem;
  }

  .acquirer-view {
    --control-size: 2.15rem;
  }
}

.menu-sheet .back-button {
  align-self: auto;
}

/* Hover styling only where a real pointer exists, so a tap on a phone does not leave a control stuck in its
   hover state until the next tap elsewhere. */
@media (hover: hover) {
  .login-button:hover,
  .mode-button:hover,
  .back-button:hover {
    border-color: var(--accent);
  }

}

.logout-error {
  text-align: center;
}

.about-overlay {
  position: fixed;
  z-index: 1000;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  background: var(--backdrop);
}

.about-dialog {
  width: min(28rem, 100%);
  padding: 1.25rem;
  box-sizing: border-box;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 0.75rem 2rem var(--shadow);
}

.about-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1rem;
}

.about-header h2 {
  margin: 0;
  font-size: 1.25rem;
}

.about-close {
  min-width: 2.75rem;
  min-height: 2.75rem;
  padding: 0;
  background: transparent;
  border: 0;
  color: var(--fg);
  font: inherit;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}

/* The account sits above the build information and is separated from it: they answer different questions, and
   one of them is available immediately while the other is fetched. */
.about-account {
  margin-bottom: 0.75rem;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--border);
}

.about-details {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: 0.65rem 1rem;
  margin: 0;
  font-size: 0.9rem;
}

.about-details dt {
  font-weight: 600;
}

.about-details dd {
  min-width: 0;
  margin: 0;
  overflow-wrap: anywhere;
}

.about-status {
  margin: 0;
}

.session-expired-message {
  margin: 0;
}

.error {
  color: #ff7c6b;
}

/* Camera controls as a sheet at the foot of the screen: what the sliders do is only visible in the picture, so the
   picture has to stay on screen while they move. */
.camera-sheet {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  display: grid;
  gap: 0.4rem;
  padding: 0.6rem 0.75rem calc(0.75rem + env(safe-area-inset-bottom));
  background: var(--panel-bg);
  border-top: 1px solid var(--border);
  box-shadow: 0 -8px 24px rgb(0 0 0 / 45%);
}

.camera-sheet-head {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  font-size: 0.8rem;
  color: var(--muted);
}

.camera-sheet-close {
  border: none;
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: 1rem;
  line-height: 1;
  padding: 0.2rem 0.35rem;
  cursor: pointer;
}

/* Label, slider, value. The slider takes whatever is left, so the row still fits a 320px screen. */
.camera-adjust-row {
  display: grid;
  grid-template-columns: 4.5rem 1fr 3.5rem;
  align-items: center;
  gap: 0.5rem;
}

.camera-adjust-label {
  font-size: 0.8rem;
  color: var(--muted);
}

.camera-adjust-slider {
  width: 100%;
  min-width: 0;
}

.camera-adjust-value {
  font-size: 0.75rem;
  color: var(--muted);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.camera-sheet-auto {
  margin-top: 0.15rem;
  padding: 0.5rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: transparent;
  color: var(--fg);
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
}

/* One bullet per device reading this counter directly, where the capture resolution used to be. Orange against the
   green trace and the blue and purple of the quadrant markers, so it reads as its own thing. */
.watcher-dots {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 0.35rem;
  /* The height the resolution line had, so the picture above does not shift as devices come and go. */
  min-height: 1.2em;
  pointer-events: none;
}

.watcher-dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: #f97316;
}


/* The settings panel, over whatever screen asked for it. It borrows the dashboard's palette rather than the app's:
   these are the numbers that dashboard reports, and reading them against a different background than the screen they
   govern makes two things out of one. */
.settings-overlay {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: max(1rem, env(safe-area-inset-top)) 1rem max(1rem, env(safe-area-inset-bottom));
  background: var(--backdrop);
  box-sizing: border-box;
}

.settings-screen {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  width: min(60rem, 100%);
  max-height: 100%;
  padding: 1.5rem;
  box-sizing: border-box;
  background: #0b0d0f;
  border: 1px solid #1d2326;
  border-radius: 18px;
  box-shadow: 0 18px 48px var(--shadow);
  color: #e9edec;

  --dash-panel: #111517;
  --dash-line: #1d2326;
  --dash-muted: #798887;
  --dash-accent: #e8b04b;
}

.settings-header {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
}

.settings-header h2 {
  margin: 0;
  font-size: 1rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.settings-note {
  flex: 1;
  color: var(--dash-muted);
  font-size: 0.8rem;
}

/* The one part that scrolls. The header and the actions stay put, so Save is reachable without hunting for it at the
   bottom of a long list of exercises. */
.settings-body {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  min-height: 0;
  overflow-y: auto;
}

.settings-card {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  padding: 1.25rem;
  background: var(--dash-panel);
  border: 1px solid var(--dash-line);
  border-radius: 14px;
}

.settings-field {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.field-label {
  color: var(--dash-muted);
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.field-hint {
  margin: 0;
  color: var(--dash-muted);
  font-size: 0.8rem;
}

.settings-weight {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.settings-number,
.settings-text {
  min-height: 2.9rem;
  padding: 0 0.9rem;
  background: #0b0d0f;
  border: 1px solid #262e31;
  border-radius: 10px;
  color: inherit;
  font: inherit;
}

.settings-number {
  width: 7rem;
  font-size: 1.3rem;
  font-variant-numeric: tabular-nums;
}

.settings-text {
  width: 100%;
  box-sizing: border-box;
}

.settings-equipment {
  font-size: 0.85rem;
}

.settings-toggle {
  display: flex;
  overflow: hidden;
  background: #0b0d0f;
  border: 1px solid #262e31;
  border-radius: 10px;
}

.toggle-option {
  min-width: 3.4rem;
  min-height: 2.9rem;
  padding: 0 0.9rem;
  background: transparent;
  border: 0;
  color: var(--dash-muted);
  font: inherit;
  cursor: pointer;
}

.toggle-option.on {
  background: var(--dash-accent);
  color: #12150f;
  font-weight: 600;
}

.settings-stepper {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.stepper-button {
  width: 2.9rem;
  height: 2.9rem;
  background: transparent;
  border: 1px solid #262e31;
  border-radius: 10px;
  color: inherit;
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
}

.settings-factor {
  width: 5rem;
  font-size: 1.2rem;
  text-align: center;
}

.settings-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.add-exercise {
  padding: 0.4rem 0.2rem;
  background: transparent;
  border: 0;
  color: var(--dash-accent);
  font: inherit;
  cursor: pointer;
}

.exercise-list {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

/* Wraps rather than scrolling sideways: a tablet in portrait has room for the name and then for the two fields
   beneath it, and a row that had to be dragged horizontally would hide the factor -- which is the field most likely
   to be the reason the screen was opened. */
.exercise-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 1rem;
  padding: 1rem;
  background: #14171a;
  border: 1px solid var(--dash-line);
  border-radius: 14px;
  cursor: pointer;
}

.exercise-row.chosen {
  border-color: var(--dash-accent);
}

.exercise-identity {
  display: flex;
  flex: 1 1 14rem;
  flex-direction: column;
  gap: 0.4rem;
  min-width: 0;
}

.exercise-field {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.exercise-remove {
  width: 2.4rem;
  height: 2.4rem;
  background: transparent;
  border: 0;
  color: var(--dash-muted);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
}

.settings-explainer {
  margin: 0;
  color: var(--dash-muted);
  font-size: 0.8rem;
  line-height: 1.45;
}

.settings-error {
  margin: 0;
}

.settings-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.75rem;
}

/* Plain buttons rather than the tall role cards the class is shaped for, for the same reason the takeover dialog's
   are: one line each, and at card height they would read as a menu of modes rather than as Cancel and Save. */
.settings-actions .mode-button {
  display: block;
  min-height: 0;
  padding: 0.75rem 1.6rem;
  background: var(--dash-accent);
  border-color: var(--dash-accent);
  color: #12150f;
  font-weight: 600;
  text-align: center;
}

.settings-actions .mode-button:disabled {
  opacity: 0.6;
  cursor: default;
}

.settings-actions .back-button {
  align-self: auto;
  min-height: 0;
  padding: 0.75rem 1.4rem;
  border-color: #262e31;
  color: inherit;
}

/* The privacy and terms links. Quiet by design: they belong on the login screen and in the About panel because
   people look for them there, not because they are somewhere anyone should be sent. */
.document-links {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 1.25rem;
}

.document-link {
  color: var(--muted);
  font-size: 0.85rem;
}

.document-separator {
  color: var(--muted);
  font-size: 0.85rem;
}

/* The history. It borrows the settings panel's frame, because it is the same kind of thing: a screen about the
   account rather than about the set in front of the camera. */
.history-screen {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  --history-heading-size: clamp(1.1rem, 5vw, 1.8rem);
  /* Replaced at runtime with the common single-line fit across every history item. */
  --history-primary-size: 1rem;
  --history-secondary-size: 0.6rem;
  font-size: var(--history-primary-size);
}

@media (min-width: 769px) {
  .history-screen {
    --history-heading-size: clamp(1.1rem, 2.4vw, 1.8rem);
  }
}

@media (min-width: 80rem) {
  .history-screen {
    --history-heading-size: clamp(1.1rem, 1.8vw, 1.8rem);
  }
}

@media (min-width: 100rem) {
  .history-screen {
    --history-heading-size: clamp(1.1rem, 1.2vw, 1.8rem);
  }
}

.history-screen .settings-header {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 0.75rem;
}

.history-screen .settings-header h2 {
  font-size: var(--history-heading-size);
}

.history-screen .settings-note,
.history-screen .field-hint {
  font-size: var(--history-secondary-size);
}

.history-screen .about-close {
  font-size: var(--history-heading-size);
}

.history-body {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  gap: 0.75rem;
}

.workout-list {
  display: grid;
  align-content: start;
  gap: 0.6rem;
}

.workout-row {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) max-content;
  grid-template-rows: auto auto;
  align-items: baseline;
  column-gap: clamp(1.5rem, 4vw, 4rem);
  row-gap: 0.45rem;
  padding: 0.85rem 1rem;
  background: var(--dash-panel);
  border: 1px solid var(--dash-line);
  border-radius: 12px;
}

/* The one still being counted into. Marked rather than hidden: it is the workout the numbers on the dashboard
   behind this panel belong to, and leaving it out would read as the app having lost it. */
.workout-row.running {
  border-color: var(--dash-accent);
}

.workout-date,
.workout-figures,
.workout-duration,
.workout-meta {
  width: 80%;
  min-width: 0;
  overflow: hidden;
  line-height: 1.25;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.workout-date {
  grid-column: 1;
  grid-row: 1;
  font-size: var(--history-primary-size);
  font-variant-numeric: tabular-nums;
  justify-self: start;
}

.workout-duration {
  grid-column: 1;
  grid-row: 2;
  font-size: var(--history-primary-size);
  font-variant-numeric: tabular-nums;
  justify-self: start;
}

.workout-meta {
  grid-column: 2;
  grid-row: 2;
  justify-self: end;
  color: var(--dash-muted);
  font-size: var(--history-secondary-size);
  text-align: right;
}

.workout-figures {
  grid-column: 2;
  grid-row: 1;
  display: grid;
  grid-template-columns: max-content max-content;
  justify-content: start;
  gap: clamp(0.8rem, 2vw, 1.5rem);
  font-size: var(--history-primary-size);
  justify-self: start;
}

.workout-figure {
  display: grid;
  grid-template-columns: max-content max-content;
  align-items: baseline;
  gap: 0.3rem;
  font-size: inherit;
  font-variant-numeric: tabular-nums;
}

.workout-amount,
.workout-unit {
  min-width: max-content;
}

.workout-unit {
  color: var(--dash-muted);
  font-size: inherit;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.workout-remove {
  grid-column: 3;
  grid-row: 1;
  align-self: center;
  width: 2.2rem;
  height: 2.2rem;
  padding: 0.4rem;
  background: transparent;
  border: 0;
  color: var(--dash-muted);
  cursor: pointer;
}

.workout-remove-icon {
  display: block;
  width: 100%;
  height: 100%;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.workout-confirm {
  position: absolute;
  z-index: 1;
  top: 0.55rem;
  right: 0.55rem;
  display: grid;
  grid-auto-flow: column;
  gap: 0.4rem;
  padding: 0.25rem;
  background: var(--dash-panel);
  border-radius: 9px;
}

.workout-yes,
.workout-no {
  min-height: 2.2rem;
  padding: 0 0.75rem;
  border-radius: 8px;
  font: inherit;
  font-size: var(--history-secondary-size);
  cursor: pointer;
}

.workout-yes {
  background: #ef4444;
  border: 1px solid #ef4444;
  color: #fff;
  font-weight: 600;
}

.workout-no {
  background: transparent;
  border: 1px solid #262e31;
  color: inherit;
}

/* A phone keeps the icon in its own narrow track and puts every piece of content in the first. The fourth row spans
   the content track only, leaving the same empty icon column as the rows above it. */
@media (max-width: 768px) {
  .workout-row {
    grid-template-columns: minmax(0, 1fr) 2.2rem;
    grid-template-rows: repeat(4, auto);
    align-items: start;
    column-gap: 1rem;
    row-gap: 0.35rem;
  }

  .workout-date,
  .workout-figures,
  .workout-duration,
  .workout-meta {
    width: 100%;
  }

  .workout-figures {
    column-gap: 0.6rem;
  }

  .workout-figure {
    column-gap: 0.2rem;
  }

  .workout-unit {
    letter-spacing: 0.06em;
  }

  .workout-date {
    grid-column: 1;
    grid-row: 1;
  }

  .workout-figures {
    grid-column: 1;
    grid-row: 2;
  }

  .workout-duration {
    grid-column: 1;
    grid-row: 3;
  }

  .workout-meta {
    grid-column: 1;
    grid-row: 4;
    justify-self: start;
    text-align: left;
  }

  .workout-remove {
    position: absolute;
    top: 0;
    right: 1rem;
    grid-column: 2;
    grid-row: 1;
    align-self: start;
    display: grid;
    place-items: start center;
    padding: 0;
  }

  .workout-remove-icon {
    width: 1.35rem;
    height: 1.35rem;
  }
}

/* The one control in the app that destroys something irrecoverable, coloured like it. */
.forget-confirm {
  background: #ef4444;
  border-color: #ef4444;
  color: #fff;
}
