/* ════════════════════════════════════════════════════════════════
   iCheckBuddy v1: one stylesheet shared by index.html (participant)
   and report.html (manager).

   DESIGN INTENT: the product principle is "accountability with
   dignity". Nothing here should feel like a scolding. So: white
   background, generous whitespace, one friendly green as the single
   accent, and RED RESERVED STRICTLY for missing / X states. A gray
   row is the normal "not done yet" state; red only appears once a
   deadline has actually passed.

   MOBILE FIRST: both people use this on a phone. Base font is 17px,
   tap targets are at least 44px tall, and the page itself never
   scrolls sideways (only the report's photo strips do).

   No frameworks, no CDN fonts, no build step. The system font stack
   below is what iOS and Android already have, so text renders
   instantly and nothing is fetched over the network.
   ════════════════════════════════════════════════════════════════ */

:root {
  /* The single accent. Used for completion, primary buttons, focus. */
  --green: #2f9e5f;
  --green-dark: #23794a;
  --green-soft: #e9f5ee;

  /* Completed but with no photo. Amber, not red: it is still a win,
     it just lacks evidence. */
  --yellow: #d19a1a;

  /* Reserved for missing / X only. Never used for decoration. */
  --red: #c0413a;
  --red-soft: #fbeded;

  --ink: #16181c;
  --muted: #6d7480;
  --faint: #9aa1ab;
  --line: #e4e7ea;
  --bg: #ffffff;
  --card: #f7f8f9;

  --radius: 12px;
  /* Minimum height for anything a thumb has to hit. Apple and Google
     both recommend 44px; we use a little more on rows. */
  --tap: 44px;
}

/* ── Page basics ───────────────────────────────────────────────── */

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--ink);
  /* overflow-x hidden is a belt-and-braces guarantee for the "no
     horizontal page scroll" rule. Individual scrollers (photo strips)
     set their own overflow-x and are unaffected. */
  overflow-x: hidden;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  font-size: 17px;
  line-height: 1.4;
  -webkit-text-size-adjust: 100%;
}

.page {
  max-width: 640px;
  margin: 0 auto;
  /* env(safe-area-inset-*) keeps content clear of the iPhone notch and
     the home indicator when the page is added to the home screen. */
  padding: 16px 16px calc(32px + env(safe-area-inset-bottom, 0px));
}

button,
input,
select {
  font-family: inherit;
  font-size: inherit;
}

/* Anything hidden with the HTML `hidden` attribute must really be
   gone. Some of our elements are display:flex, and a flex display
   rule would otherwise beat the browser default for [hidden]. */
[hidden] {
  display: none !important;
}

/* Present to assistive tech and still clickable, but invisible. Used
   for the file input, which is triggered by its <label>. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ── Error banner (never a blank page) ─────────────────────────── */

.banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  padding: 12px 16px calc(12px + env(safe-area-inset-top, 0px));
  background: var(--red-soft);
  color: var(--red);
  border-bottom: 1px solid #f0cfcd;
  font-size: 15px;
  text-align: center;
}

/* ── Friendly full-page messages (no key, not configured) ──────── */

.gate {
  padding: 64px 24px;
  text-align: center;
  color: var(--muted);
  font-size: 17px;
}

.loading {
  padding: 48px 24px;
  text-align: center;
  color: var(--faint);
}

/* ════════════════════════════════════════════════════════════════
   PARTICIPANT PAGE
   ════════════════════════════════════════════════════════════════ */

.app-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 12px;
}

.header-date {
  font-size: 15px;
  letter-spacing: 0.06em;
  color: var(--muted);
  font-weight: 600;
}

.header-name {
  font-size: 30px;
  font-weight: 700;
  letter-spacing: 0.02em;
  margin-top: 2px;
  /* The mock shows the name in caps (DAVID). Doing it in CSS rather
     than in JS keeps the stored display name untouched. */
  text-transform: uppercase;
}

.icon-btn {
  min-width: var(--tap);
  min-height: var(--tap);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--muted);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}

.icon-btn:active {
  background: var(--card);
}

/* The refresh glyph spins while a request is in flight, which is the
   only "something is happening" signal on a slow phone connection. */
.icon-btn.is-busy {
  animation: spin 900ms linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* ── Show All / Remaining toggle ───────────────────────────────── */

.view-toggle {
  display: flex;
  gap: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: 20px;
}

.toggle-btn {
  flex: 1 1 50%;
  min-height: var(--tap);
  border: 0;
  background: var(--bg);
  color: var(--muted);
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
}

.toggle-btn.is-active {
  background: var(--green-soft);
  color: var(--green-dark);
}

/* ── Task sections ─────────────────────────────────────────────── */

.task-section {
  margin-bottom: 26px;
}

.section-title {
  margin: 0 0 6px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: var(--muted);
}

.all-done {
  margin: 18px 0;
  text-align: center;
  color: var(--faint);
  font-size: 17px;
}

/* ── One task row ──────────────────────────────────────────────── */

.task-row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 52px;
  padding: 10px 6px;
  border: 0;
  border-bottom: 1px solid var(--line);
  background: transparent;
  text-align: left;
  color: inherit;
  cursor: pointer;
}

.task-row:active {
  background: var(--card);
}

.task-text {
  /* min-width:0 lets a long task name wrap instead of forcing the row
     wider than the screen (the classic flexbox overflow trap). */
  min-width: 0;
  flex: 1 1 auto;
}

.task-name {
  /* position:relative anchors the animated strikethrough line below. */
  position: relative;
  font-size: 17px;
}

.task-deadline {
  color: var(--faint);
  font-size: 15px;
}

/* A completed row is quiet: gray and struck through. It has already
   had its moment (the celebration); now it just confirms. */
.task-row.is-complete .task-name,
.task-row.is-complete .task-deadline {
  color: var(--faint);
  text-decoration: line-through;
}

/* In "Show All" mode a completed row stays visible but recedes. */
.task-row.is-dimmed {
  opacity: 0.65;
}

/* Small marker for "there are photos here but it is not marked
   complete", so a half-finished submission is not invisible. */
.photo-flag {
  flex: 0 0 auto;
  font-size: 13px;
  color: var(--faint);
}

/* ── The checkbox indicator ────────────────────────────────────── */

.checkbox {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border: 2px solid var(--line);
  border-radius: 7px;
  background: var(--bg);
}

.checkbox .check {
  width: 20px;
  height: 20px;
  /* Hidden for an empty (not submitted) box; the state classes below
     turn it on. */
  opacity: 0;
}

.check-path {
  fill: none;
  stroke: currentColor;
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* 24 is a little more than the real path length (about 21), so the
     dash fully covers the stroke when offset is 24 and fully reveals
     it at 0. That is what makes the "drawn in" animation possible. */
  stroke-dasharray: 24;
  stroke-dashoffset: 0;
}

/* Completed WITH at least one photo. */
.checkbox--green {
  border-color: var(--green);
  color: var(--green);
}

.checkbox--green .check {
  opacity: 1;
}

/* Completed with NO photo: still done, but unevidenced. */
.checkbox--yellow {
  border-color: var(--yellow);
  color: var(--yellow);
}

.checkbox--yellow .check {
  opacity: 1;
}

/* ── Completion animation (about 2 seconds total) ──────────────── */
/*
   The row is rendered in its OLD (incomplete) state and then animated
   into the new one, so the participant actually sees the change
   happen. Step 1 draws the check, step 2 strikes the name through.
   JS adds these classes on a timer; see runCelebration() in app.js.
*/

.task-row.anim-check .checkbox {
  border-color: var(--green);
  color: var(--green);
}

.task-row.anim-check .check {
  opacity: 1;
  animation: draw-check 420ms ease forwards;
}

@keyframes draw-check {
  from {
    stroke-dashoffset: 24;
  }
  to {
    stroke-dashoffset: 0;
  }
}

.task-row.anim-strike .task-name::after {
  content: "";
  position: absolute;
  left: 0;
  top: 55%;
  height: 2px;
  width: 0;
  background: var(--faint);
  animation: strike-through 420ms ease forwards;
}

@keyframes strike-through {
  to {
    width: 100%;
  }
}

.task-row.anim-strike .task-name,
.task-row.anim-strike .task-deadline {
  color: var(--faint);
  transition: color 420ms ease;
}

/* ── "Good job!" celebration overlay ───────────────────────────── */

.celebrate {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  /* The overlay is purely decorative and must never eat a tap. */
  pointer-events: none;
}

.celebrate.is-fading {
  animation: celebrate-fade 400ms ease forwards;
}

@keyframes celebrate-fade {
  to {
    opacity: 0;
  }
}

.celebrate-text {
  position: relative;
  z-index: 2;
  font-size: 34px;
  font-weight: 700;
  color: var(--green-dark);
  animation: grow-in 420ms cubic-bezier(0.2, 1.4, 0.4, 1) both;
}

@keyframes grow-in {
  from {
    transform: scale(0.2);
    opacity: 0;
  }
  to {
    transform: scale(1);
    opacity: 1;
  }
}

/* The starburst: a soft green glow that expands once behind the text.
   A radial gradient is enough; no canvas, no library. */
.starburst {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 180px;
  height: 180px;
  margin: -90px 0 0 -90px;
  border-radius: 50%;
  background: radial-gradient(
    circle,
    rgba(47, 158, 95, 0.35) 0%,
    rgba(47, 158, 95, 0.12) 45%,
    rgba(47, 158, 95, 0) 70%
  );
  animation: burst-glow 700ms ease-out forwards;
}

@keyframes burst-glow {
  from {
    transform: scale(0.2);
    opacity: 1;
  }
  to {
    transform: scale(1.7);
    opacity: 0;
  }
}

/* Fireworks particles. JS spawns a dozen of these and sets --angle
   and --dist on each; the keyframes below fling them outward. */
.particle {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  animation: particle-fly 900ms ease-out forwards;
}

@keyframes particle-fly {
  from {
    /* rotate first, then translate along X: the rotation decides the
       direction, the translate decides how far. */
    transform: translate(-50%, -50%) rotate(var(--angle)) translateX(0) scale(1);
    opacity: 1;
  }
  to {
    transform: translate(-50%, -50%) rotate(var(--angle))
      translateX(var(--dist)) scale(0.25);
    opacity: 0;
  }
}

/* ── Detail overlay ────────────────────────────────────────────── */

.overlay {
  position: fixed;
  inset: 0;
  z-index: 150;
  background: rgba(22, 24, 28, 0.35);
  display: flex;
  align-items: flex-end;
  justify-content: center;
}

.overlay-card {
  width: 100%;
  max-width: 640px;
  max-height: 92vh;
  overflow-y: auto;
  background: var(--bg);
  border-radius: 16px 16px 0 0;
  padding: 18px 16px calc(20px + env(safe-area-inset-bottom, 0px));
}

.overlay-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
}

.overlay-title {
  font-size: 22px;
  font-weight: 700;
}

.overlay-subtitle {
  font-size: 15px;
  color: var(--faint);
  margin-top: 2px;
}

.overlay-body {
  display: block;
}

.field-label {
  display: block;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--muted);
  margin-bottom: 6px;
}

.text-input {
  display: block;
  width: 100%;
  min-height: var(--tap);
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--ink);
  /* 16px or more stops iOS Safari from zooming in when the field is
     focused, which would otherwise shift the whole layout. */
  font-size: 17px;
}

.text-input:focus {
  outline: 2px solid var(--green);
  outline-offset: 1px;
}

.overlay-actions {
  display: flex;
  gap: 10px;
  margin-top: 20px;
}

.primary-btn,
.secondary-btn {
  flex: 1 1 auto;
  min-height: var(--tap);
  padding: 12px 16px;
  border-radius: var(--radius);
  font-size: 17px;
  font-weight: 600;
  cursor: pointer;
  text-align: center;
}

.primary-btn {
  border: 1px solid var(--green);
  background: var(--green);
  color: #ffffff;
}

.primary-btn:disabled {
  opacity: 0.55;
  cursor: default;
}

.secondary-btn {
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--ink);
}

/* The <label> that opens the photo picker is styled as a button, so
   it needs the block layout a real button would have. */
.file-label {
  display: block;
  margin-top: 16px;
}

/* "Take photo" and "Add photos" side by side (build 005). Two equal
   halves so neither looks like the secondary choice. */
.photo-buttons {
  display: flex;
  gap: 10px;
  margin-top: 16px;
}

.photo-buttons .file-label {
  flex: 1 1 0;
  min-width: 0;
  margin-top: 0;
}

.danger-outline {
  border-color: #e6c3c1;
  color: var(--red);
}

.complete-row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: var(--tap);
  margin-top: 18px;
  font-size: 17px;
  cursor: pointer;
}

.complete-row input {
  width: 24px;
  height: 24px;
  accent-color: var(--green);
}

.progress-note {
  margin: 12px 0 0;
  font-size: 15px;
  color: var(--muted);
}

.done-time {
  margin: 0 0 10px;
  font-size: 15px;
  color: var(--green-dark);
}

.view-desc {
  margin: 0 0 14px;
  font-size: 17px;
  /* Descriptions are free text from a phone keyboard; a long word or
     a pasted URL must wrap rather than widen the page. */
  overflow-wrap: anywhere;
}

/* ── Thumbnails in the detail overlay ──────────────────────────── */

.thumb-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 14px;
}

.thumb {
  position: relative;
  width: 84px;
  height: 84px;
  border-radius: 10px;
  overflow: hidden;
  background: var(--card);
  border: 1px solid var(--line);
}

.thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Remove button on a staged (not yet uploaded) photo. Uploaded photos
   have no remove control: submissions are permanent by design. */
.thumb-remove {
  position: absolute;
  top: 2px;
  right: 2px;
  width: 28px;
  height: 28px;
  border: 0;
  border-radius: 50%;
  background: rgba(22, 24, 28, 0.62);
  color: #ffffff;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
}

.thumb-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--faint);
  font-size: 12px;
  text-align: center;
  padding: 4px;
}

.empty-note {
  margin: 14px 0 0;
  color: var(--faint);
  font-size: 15px;
}

/* Completed-task view: a thumbnail with the address it was taken at
   in small print underneath (build 005). Same width as the thumbnail
   so the grid stays tidy; long addresses wrap. */
.thumb-with-caption {
  width: 84px;
}

.thumb-caption {
  margin-top: 4px;
  font-size: 11px;
  line-height: 1.3;
  color: var(--faint);
  overflow-wrap: anywhere;
}

/* ════════════════════════════════════════════════════════════════
   REPORT PAGE (manager, read only)
   ════════════════════════════════════════════════════════════════ */

.report-title {
  margin: 0 0 16px;
  font-size: 22px;
  font-weight: 700;
  letter-spacing: 0.1em;
}

.controls {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px;
  margin-bottom: 22px;
  background: var(--card);
}

.control-label {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--muted);
  margin-bottom: 8px;
}

.filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
  margin-bottom: 14px;
}

.filter-check {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: var(--tap);
  font-size: 17px;
  cursor: pointer;
  /* SHOW [x] DAVID [x] MICHELLE, per the mock. */
  text-transform: uppercase;
}

.filter-check input {
  width: 22px;
  height: 22px;
  accent-color: var(--green);
}

.date-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.date-input {
  flex: 1 1 130px;
  min-width: 0;
  min-height: var(--tap);
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg);
  color: var(--ink);
  font-size: 16px;
}

.date-sep {
  color: var(--muted);
  font-size: 15px;
}

.load-btn {
  flex: 0 0 auto;
  min-height: var(--tap);
  padding: 10px 20px;
  border: 1px solid var(--green);
  border-radius: 10px;
  background: var(--green);
  color: #ffffff;
  font-weight: 600;
  cursor: pointer;
}

.load-btn:disabled {
  opacity: 0.55;
  cursor: default;
}

/* ── One participant-day block ─────────────────────────────────── */

.day-block {
  margin-bottom: 26px;
}

.bookend {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 0;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--muted);
  /* START - DAVID - SAT JAN 1, 2026 */
  text-transform: uppercase;
}

/* The day's X box. Empty means the day was clean. */
.xbox {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border: 2px solid var(--line);
  border-radius: 6px;
  font-size: 14px;
  line-height: 1;
  color: transparent;
}

.xbox--on {
  border-color: var(--red);
  color: var(--red);
}

.day-items {
  border-left: 2px solid var(--line);
  margin-left: 10px;
  padding: 6px 0 6px 18px;
}

.report-item {
  padding: 8px 0;
}

.report-item-head {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.status-icon {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  line-height: 1;
}

.status-icon svg {
  width: 18px;
  height: 18px;
  color: var(--green);
}

.report-item-main {
  min-width: 0;
  flex: 1 1 auto;
}

.report-item-title {
  font-size: 17px;
  overflow-wrap: anywhere;
}

/* Red only here: the task whose deadline passed undone. */
.report-item--missing .report-item-title {
  color: var(--red);
}

.report-item--missing .status-icon {
  color: var(--red);
}

.report-item--pending .report-item-title,
.report-item--pending .status-icon {
  color: var(--faint);
}

.status-text {
  font-size: 14px;
  color: inherit;
  opacity: 0.85;
}

.report-item-note {
  margin-top: 2px;
  font-size: 15px;
  color: var(--muted);
  overflow-wrap: anywhere;
}

.report-item-time {
  margin-top: 2px;
  font-size: 14px;
  color: var(--faint);
}

/* ── Horizontally scrollable photo strip ───────────────────────── */

.photo-strip {
  display: flex;
  gap: 8px;
  margin-top: 8px;
  /* The ONLY sideways scroller on the page. -webkit-overflow-scrolling
     gives it momentum on iOS. */
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  padding-bottom: 4px;
}

.photo-strip a {
  flex: 0 0 auto;
  display: block;
  width: 72px;
  height: 72px;
  border-radius: 8px;
  overflow: hidden;
  background: var(--card);
  border: 1px solid var(--line);
}

/* Placeholder for a photo whose signed URL is null or broken. It
   keeps .thumb-empty's own centered flex layout, so no display
   property is set here. */
.photo-strip .thumb-empty {
  flex: 0 0 auto;
  width: 72px;
  height: 72px;
  border-radius: 8px;
  background: var(--card);
  border: 1px solid var(--line);
}

.photo-strip img {
  width: 72px;
  height: 72px;
  object-fit: cover;
  display: block;
}

/* ── Photo evidence details (build 005) ────────────────────────── */
/*
   Each photo in the strip is a cell: the 72px thumbnail, then a few
   lines of small print (taken time, address, stale note). The cell is
   wider than the thumbnail so an address fits in two or three lines;
   the strip already scrolls sideways, so the page itself never does.
*/

.photo-cell {
  flex: 0 0 auto;
  width: 132px;
}

.photo-details {
  margin-top: 4px;
  font-size: 12px;
  line-height: 1.3;
  color: var(--muted);
  overflow-wrap: anywhere;
}

.photo-taken {
  color: var(--muted);
}

.photo-address {
  color: var(--faint);
}

.photo-address-hint {
  font-style: italic;
}

/* Stale note. The design rule keeps true red for missing / X only, so
   this uses a softer, desaturated rose: noticeable, but it reads as
   "look closer" rather than as a verdict. */
.photo-stale {
  margin-top: 2px;
  color: #a5605a;
}

/* Map thumbnail: a 160x110 window onto a mosaic of 256px OSM tiles
   that report.js positions so the photo's location is dead center.
   Size must match MAP_WIDTH_PX / MAP_HEIGHT_PX in report.js. */
.map-wrap {
  margin-top: 8px;
  width: 160px;
  max-width: 100%;
}

.map-thumb {
  position: relative;
  display: block;
  width: 160px;
  max-width: 100%;
  height: 110px;
  overflow: hidden;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--card);
}

.map-tile {
  position: absolute;
  width: 256px;
  height: 256px;
  max-width: none;
  display: block;
}

/* A green dot with a white ring, so it stands out on any map
   coloring. Centered by offsetting half its own size. */
.map-marker {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 14px;
  height: 14px;
  margin: -7px 0 0 -7px;
  border-radius: 50%;
  background: var(--green);
  border: 2px solid #ffffff;
  box-shadow: 0 0 0 1px rgba(22, 24, 28, 0.35);
}

/* Required attribution (OpenStreetMap tile usage policy). */
.map-attrib {
  margin-top: 2px;
  font-size: 10px;
  color: var(--faint);
}

.report-empty {
  padding: 40px 8px;
  text-align: center;
  color: var(--faint);
}

/* ════════════════════════════════════════════════════════════════
   Reduced motion
   ════════════════════════════════════════════════════════════════
   Some people get motion sick, and some just do not want confetti.
   When the OS says so, every animation collapses to its end state:
   the check is simply there, the strikethrough is simply drawn. JS
   also checks this same media query and skips spawning particles at
   all, so nothing is merely invisible-but-running.
*/

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }

  .task-row.anim-strike .task-name::after {
    width: 100%;
  }
}


/* ════════════════════════════════════════════════════════════════
   BUILD NUMBER (build-check.js)
   The footer keeps the live build one glance away so dls can verify
   what is deployed. The mismatch bar is deliberately loud: it only
   appears after a silent auto-refresh has already failed to bring
   the page up to the server's build, meaning the page is stale in a
   way that needs a human.
   ════════════════════════════════════════════════════════════════ */

.build-footer {
  text-align: center;
  color: #b0b6ad;
  font-size: 12px;
  padding: 20px 0 calc(12px + env(safe-area-inset-bottom));
  user-select: all; /* one tap selects the whole string for copying */
}

.build-mismatch {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 300; /* above banner (which sits at a lower z) and overlays */
  background: #c62828;
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  padding: 12px 16px calc(12px);
  padding-top: calc(12px + env(safe-area-inset-top));
  text-align: center;
}

/* Inline top placement of the build number (inside the report title
   and beside the participant name). Small and dim so it informs
   without competing with the real content. */
.build-top {
  font-size: 13px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: #b0b6ad;
  margin-left: 6px;
  white-space: nowrap;
}
