/*
 * Colours live here and nowhere else, so a repaint never means touching logic.
 *
 * The palette is devops.pink's, and taken the way that site builds it rather
 * than by eyedropper: one hue, everything else derived from it in OKLCH.
 * Change --hue and the whole interface moves with it, greys included — they
 * carry a trace of the brand rather than being neutral.
 */
:root {
  --hue: 340;

  --bg: oklch(0.95 0.01 var(--hue));
  --surface: #ffffff;
  --border: oklch(0.92 0.01 var(--hue));
  --text: oklch(0.25 0.02 var(--hue));
  --text-muted: oklch(0.4 0.02 var(--hue));

  --primary: oklch(0.45 0.14 var(--hue));
  --primary-soft: oklch(0.95 0.025 var(--hue));
  --primary-soft-hover: oklch(0.9 0.05 var(--hue));

  /*
   * The traffic light is the one thing the brand does not get to own. Its job
   * is to be read at a glance, and a red that has drifted towards the pink it
   * sits beside stops being a warning. So the three keep their own hues, held
   * well away from 340 and from each other.
   *
   * What makes them look like one family instead of three borrowed colours is
   * the single lightness they share. It also happens to be what makes them
   * legible: at L 0.54 every one of them clears 5:1 on a white card, so the
   * palette and the contrast requirement are satisfied by the same decision
   * rather than fighting each other.
   *
   * The fourth state is not a colour of its own. A card with no verdict is
   * not making a quiet judgement, it is making none, so it borrows the muted
   * text colour and reads as what it is: metadata.
   */
  --good: oklch(0.54 0.15 150);
  --watch: oklch(0.54 0.13 80);
  --alert: oklch(0.54 0.19 25);
  --collecting: var(--text-muted);
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: oklch(0.16 0.014 var(--hue));
    --surface: oklch(0.23 0.015 var(--hue));
    --border: oklch(0.29 0.014 var(--hue));
    --text: oklch(0.85 0.01 var(--hue));
    --text-muted: oklch(0.72 0.01 var(--hue));

    --primary: oklch(0.75 0.14 var(--hue));
    --primary-soft: oklch(0.33 0.035 var(--hue));
    --primary-soft-hover: oklch(0.38 0.04 var(--hue));

    /* Lifted for a dark background: same three hues, again one shared
       lightness between them. */
    --good: oklch(0.78 0.15 150);
    --watch: oklch(0.78 0.13 80);
    --alert: oklch(0.78 0.17 25);
    --collecting: var(--text-muted);
  }
}

* {
  box-sizing: border-box;
}

/* The browser hides [hidden] with `display: none`, but that is an ordinary
   rule of the same weight as any class here — so `.cards { display: grid }`
   further down quietly wins and the element stays on screen. */
[hidden] {
  display: none !important;
}

body {
  margin: 0;
  min-height: 100vh;
  padding: 2rem 1.25rem 3rem;
  /* The brand's quiet wash: transparent at the top, deepening into the hue
     towards the bottom. Fixed, so it stays put while the page scrolls, and
     static by choice — no motion. */
  background-color: var(--bg);
  background-image: linear-gradient(
    160deg,
    transparent 0%,
    color-mix(in oklch, transparent, var(--primary) 14%) 100%
  );
  background-attachment: fixed;
  color: var(--text);
  /* Roboto is the site's face. It is used if the reader happens to have it and
     quietly skipped if not — a web font would mean a network request, and this
     app makes none. */
  font-family: Roboto, -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui,
    sans-serif;
  line-height: 1.5;
}

.app {
  max-width: 46rem;
  margin: 0 auto;
}

/* --- Summary line ------------------------------------------------------ */

/* The ring first, then the words. On a phone the ring drops above them
   rather than squeezing the sentence into a column. */
.summary {
  display: flex;
  align-items: center;
  gap: 1.125rem;
  margin-bottom: 1.75rem;
}

.summary__words {
  min-width: 0;
}

.summary__ring {
  flex: none;
  line-height: 0;
}

.ring__part {
  fill: none;
  stroke: var(--collecting);
  stroke-width: 9;
  /* Square-cut, so the gaps between segments survive. See donut.js. */
  stroke-linecap: butt;
}

.ring__part--good {
  stroke: var(--good);
}
.ring__part--watch {
  stroke: var(--watch);
}
.ring__part--alert {
  stroke: var(--alert);
}

@media (max-width: 30rem) {
  .summary {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.875rem;
  }
}

.summary__nav {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  margin-bottom: 0.25rem;
}

/* The one place the brand colour is spent on text: a date carries no verdict,
   so tinting it says "this is the app" without colouring a judgement. */
.summary__date {
  margin: 0;
  color: var(--primary);
  font-size: 0.875rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.daybtn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0.375rem;
  color: var(--text-muted);
  font-size: 1.125rem;
  line-height: 1;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.daybtn:hover:not(:disabled) {
  background: var(--primary-soft);
  color: var(--primary);
}

/* Kept in place rather than hidden at the ends of the history, so the date
   does not shuffle sideways on the first and last day. */
.daybtn:disabled {
  opacity: 0.3;
  cursor: default;
}

#day-latest {
  margin-left: 0.25rem;
  font-size: 0.8125rem;
  color: var(--text-muted);
}

/* Long enough to wrap on a phone, so it is sized to be read as a sentence
   rather than shouted as a headline. `balance` keeps a two-line verdict from
   leaving one word stranded on the second line. */
.summary__verdict {
  margin: 0;
  font-size: 1.25rem;
  font-weight: 600;
  line-height: 1.35;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

/* --- Cards ------------------------------------------------------------- */

/* Six cards in one column is a scroll rather than a glance. Two columns
   where there is room; one on a phone, where a card needs the full width for
   its sentence. */
.cards {
  display: grid;
  gap: 0.75rem;
  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
}

/* Following the site's own card treatment: 1rem corners, and a shape defined
   by a soft shadow on light backgrounds where a hard outline would be noise,
   by a hairline in the dark where a shadow cannot be seen. */
.card {
  position: relative;
  padding: 1.125rem 1.25rem 1.125rem 2.5rem;
  background: var(--surface);
  border: 1px solid transparent;
  border-radius: 1rem;
  box-shadow: 0 1px 2px oklch(0.25 0.02 var(--hue) / 6%),
    0 6px 16px oklch(0.25 0.02 var(--hue) / 4%);
}

@media (prefers-color-scheme: dark) {
  .card {
    border-color: var(--border);
    box-shadow: none;
  }
}

/* The traffic light itself. One rule per status keeps the mapping obvious. */
.card__dot {
  position: absolute;
  top: 1.5rem;
  left: 1.25rem;
  width: 0.625rem;
  height: 0.625rem;
  border-radius: 50%;
  background: var(--collecting);
}

.card--good .card__dot {
  background: var(--good);
}
.card--watch .card__dot {
  background: var(--watch);
}
.card--alert .card__dot {
  background: var(--alert);
}

.card__label {
  margin: 0;
  font-size: 0.9375rem;
  font-weight: 500;
  color: var(--text-muted);
}

/*
 * Plain mode: a name, a colour, a sentence. Nothing else.
 *
 * The figures were added one at a time, each defensible on its own — the
 * reading, the baseline, the percentage, thirty days of marks, two lines of
 * statistics. Together they buried the thing the app is for, and left a
 * person looking at "40 ms · -10%" with no way to tell whether that was good
 * news. They are all still here, one click away, but they are not the point
 * and they no longer open the page.
 */
.app[data-numbers='off'] .card__value,
.app[data-numbers='off'] .card__baseline,
.app[data-numbers='off'] .trend,
.app[data-numbers='off'] .trend__note {
  display: none;
}

/* With the number gone the sentence becomes the card, so it gets the weight
   the number used to have. */
.app[data-numbers='off'] .card {
  padding-bottom: 1.25rem;
}

.app[data-numbers='off'] .card__status {
  margin-top: 0.375rem;
  font-size: 1.0625rem;
  line-height: 1.4;
}

.card__value {
  margin: 0.125rem 0 0;
  font-size: 2rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.card__unit {
  margin-left: 0.25rem;
  font-size: 0.9375rem;
  font-weight: 500;
  color: var(--text-muted);
}

.card__baseline {
  margin: 0.25rem 0 0;
  font-size: 0.8125rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.card__status {
  margin: 0.25rem 0 0;
  font-size: 0.9375rem;
}

/* --- The last 30 days --------------------------------------------------- */

/* One mark per day, coloured the way that day's card would have been. There
   is nothing to decode: the red days can be counted. */
.trend {
  display: flex;
  gap: 2px;
  margin-top: 0.875rem;
}

.trend__day {
  flex: 1;
  height: 0.75rem;
  border-radius: 1px;
  background: var(--text-muted);
  opacity: 0.18;
}

.trend__day--good {
  background: var(--good);
  opacity: 0.85;
}
.trend__day--watch {
  background: var(--watch);
  opacity: 0.9;
}
.trend__day--alert {
  background: var(--alert);
  opacity: 0.9;
}

/* Today sits at the right-hand end, marked by height rather than colour so
   it stays legible whatever verdict it carries. */
.trend__day:last-child {
  height: 1.125rem;
  margin-top: -0.1875rem;
}

.trend__note {
  margin: 0.5rem 0 0;
  font-size: 0.8125rem;
  color: var(--text-muted);
}

.trend__note:empty {
  display: none;
}

.card--good .card__status {
  color: var(--good);
}
.card--watch .card__status {
  color: var(--watch);
}
.card--alert .card__status {
  color: var(--alert);
}
.card--collecting .card__status {
  color: var(--collecting);
}

/* --- Import ------------------------------------------------------------ */

.import__title {
  margin: 0 0 0.5rem;
  font-size: 1.5rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.import__lead {
  margin: 0 0 1.5rem;
  color: var(--text-muted);
}

/* The site's button: a soft wash of the brand hue rather than a solid block
   of it, with the deeper shade kept for the label. */
.import__button {
  display: inline-block;
  padding: 0.75rem 1.25rem;
  background: var(--primary-soft);
  color: var(--primary);
  border: 0;
  border-radius: 0.75rem;
  font-weight: 500;
  transition: background-color 0.15s ease;
  cursor: pointer;
}

.import__button:hover {
  background: var(--primary-soft-hover);
}

/* The label is the button: clicking anywhere on it opens the file chooser,
   so the input itself never needs to be seen. */
.import__button input {
  display: none;
}

.import__hint,
.import__sample,
.import__status {
  margin: 1rem 0 0;
  font-size: 0.875rem;
  color: var(--text-muted);
}

.import__status {
  font-variant-numeric: tabular-nums;
}

/* Dashed underlines are the site's link signature. */
.link {
  padding: 0;
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  text-decoration: underline;
  text-decoration-style: dashed;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.25rem;
  transition: color 0.15s ease;
  cursor: pointer;
}

.link:hover {
  color: var(--primary);
}

:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: 0.25rem;
}

/* --- What was recorded -------------------------------------------------- */

.data__title {
  margin: 0 0 0.5rem;
  font-size: 1.25rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.data__lead {
  margin: 0 0 1.5rem;
  color: var(--text-muted);
  font-size: 0.9375rem;
}

.data__table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9375rem;
}

.data__table caption {
  padding-bottom: 0.5rem;
  color: var(--text-muted);
  font-size: 0.8125rem;
  text-align: left;
}

.data__table th,
.data__table td {
  padding: 0.5rem 0.75rem 0.5rem 0;
  border-top: 1px solid var(--border);
  text-align: left;
  vertical-align: middle;
}

.data__table th {
  font-weight: 500;
}

.data__count {
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* A bar rather than a second number: the point of this table is which rows
   are short, and that is a length, not a figure to be read. */
.data__bar {
  width: 30%;
  min-width: 4rem;
}

.data__bar span {
  display: block;
  height: 0.5rem;
  min-width: 2px;
  background: var(--primary);
  border-radius: 1px;
}

.data__total {
  color: var(--text-muted);
  font-size: 0.8125rem;
}

.data__note {
  margin: 1.5rem 0 0;
  font-size: 0.9375rem;
  line-height: 1.6;
}

@media (max-width: 34rem) {
  .data__total {
    display: none;
  }
}

/* --- Footer ------------------------------------------------------------ */

.footer {
  margin-top: 2rem;
  color: var(--text-muted);
  font-size: 0.8125rem;
  text-align: center;
}

.footer p {
  margin: 0.375rem 0;
}
