/* Light, quiet, and phone first. The one thing on the page is the streak: one
   number, larger than everything else put together, over a list of weeks that
   is three dots wide. No borders, no shadows, no gradients — the only colour
   that does any work is the red of a session that happened, and how deep that
   red is says how hard the session was. */

:root {
  --ground: #ffffff;
  --ink: #1c1c1e;

  /* Everything that is not the answer, in one tone rather than two: the date,
     the `+1`, the week's peak, the captions, the footer. 4.53:1 on white, so
     the 12px lines clear AA — because the captions are where the page explains
     itself (what makes a week, what the shades mean, what was not counted),
     and that is the text most likely to be needed and least likely to be
     readable on a phone in daylight. The `#b4b4b9` they used to be was 2.06:1.

     There is no third, fainter step any more. Nothing here is decoration you
     can skip: the `+1` is a session that happened and the footer is when the
     phone last exported, which is how a stall gets noticed. A tone that says
     "you may not be able to read this" is only honest about text nobody needs,
     and this page has none. */
  --quiet: #767679;

  --field: #f2f2f5;

  /* A slot nothing has filled is grey, not a pale green: "empty" and "done"
     must not be two shades of the same thing. */
  --empty: #e4e4e9;
  /* Before the phone was exporting at all — lighter still, and not a judgement
     on anybody. */
  --nodata: #f4f4f6;
  --accent: #12904a;

  /* A session that happened, with no idea how hard it was: the watch was on the
     bedside table. Neutral rather than a shade of the ramp, and lighter than
     every one of them, so a dot never loses its "filled" by gaining a colour.
     It is filled, and that is all it claims.

     It is as loud as the lightest step of the ramp and no louder — 3.0:1
     against the ground, 2.4:1 against an empty dot, the same two numbers
     `--hr1` holds below. A session nobody measured is not a lesser session,
     and the `#a8a8ad` this used to be said it was: L* 69, which is 2.37:1 on
     white and 1.87:1 against `--empty`, on a 13px circle with no text and no
     shape to fall back on. A week the watch was off looked close to a week
     with nothing in it, which is the one thing these dots must never do.

     3:1 against `--empty` is out of reach rather than skipped: it wants L* 55,
     which is darker than `--hr1`, and the ordering is worth more than the
     number. So this sits as close to the ramp as that ordering allows — L*
     61.5 against `--hr1`'s L* 61.2 — and the margin is thin on purpose. What
     tells this dot from an `hr1` one is the hue, not the weight; what it has
     to out-weigh is the empty dot. */
  --unknown: #949499;

  /* How hard it was, in five steps of one hue — Apple's red, which is where
     heart rate lives. Staying inside one family is what keeps this readable
     for someone who cannot tell red from green: the steps fall from 30% to 5%
     relative luminance in even strides of about 9 points of L*, so the ramp
     runs light-to-dark in greyscale and under all three dichromacies.

     Every step clears 3:1 against the white ground, which is what a graphic
     carrying meaning is asked for, and every step is darker than `--unknown`
     above — so gaining a colour never costs a dot its "filled". At the light
     end that is now a third of a point of L* rather than a step, so `--hr1`
     does not move without `--unknown` moving with it. Against the empty dot
     the lightest step is 2.4:1, and that number understates it: `--empty` is
     L* 91 and this is L* 61, which is not a near miss.

     Ordering is load-bearing between these two lines and the `.on` rule below,
     which is why the shades are written to win on specificity rather than on
     source order. */
  --hr1: #f26858;
  --hr2: #ec2f1a;
  --hr3: #c32513;
  --hr4: #a02012;
  --hr5: #7e1509;
}

/* The page is light by construction: `<meta name="color-scheme" content="light">`
   pins the canvas and opts out of the browser's own auto-darkening, so a phone
   in dark mode gets exactly the palette above rather than an inverted guess at
   it. The ramp is drawn for that white ground and does not survive being moved
   to a dark one — against `--ink` the deepest step is 1.96:1, so dark would
   need the ramp re-derived rather than re-tinted, and that is a change to the
   whole page and not to five hexes. */

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  font-size: 16px;
  line-height: 1.45;
}

/* The home indicator sits over the bottom of the viewport in standalone mode,
   and the notch over the top. */
main {
  max-width: 780px;
  margin: 0 auto;
  padding:
    calc(env(safe-area-inset-top) + 26px)
    calc(env(safe-area-inset-right) + 20px)
    calc(env(safe-area-inset-bottom) + 36px)
    calc(env(safe-area-inset-left) + 20px);
}

h1 {
  margin: 0;
  font-size: 28px;
  font-weight: 600;
  letter-spacing: -0.02em;
}

.when {
  margin: 2px 0 26px;
  color: var(--quiet);
  font-size: 15px;
}

/* -------------------------------------------------------------------- streak

   The answer to the question, and nothing beside it: a number a phone shows at
   arm's length, the words a person would say under it, and where this week has
   got to in grey. */

.streak {
  margin: 34px 0 30px;
}

.count {
  margin: 0;
  font-size: clamp(72px, 24vw, 96px);
  font-weight: 600;
  letter-spacing: -0.045em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.unit {
  margin: 8px 0 0;
  font-size: 19px;
  font-weight: 500;
  letter-spacing: -0.01em;
}

.note {
  margin: 3px 0 0;
  color: var(--quiet);
  font-size: 14px;
}

/* --------------------------------------------------------------------- weeks

   One row a week, most recent first. The dots sit in their own column so they
   line up under each other on a phone and stay beside the date on a desktop
   rather than drifting off to the right of a 780px page. */

.weeks {
  margin: 0;
  padding: 0;
  /* Wide enough for a phone to use all of it, narrow enough that the peak at
     the end of a row does not drift half a page away from the dots it belongs
     to on a desktop. */
  max-width: 420px;
  list-style: none;
}

/* Four columns: the week, its dots, the quiet `+1`, and the week's peak on the
   right where it lines up down the page and one week can be read against
   another. The peak column is `auto`, so it is as wide as the widest number
   and no wider — three digits at 13px, on a 390px phone. */
.weeks li {
  display: grid;
  grid-template-columns: 84px auto 1fr auto;
  align-items: center;
  gap: 0 14px;
  padding: 7px 0;
}

.weeks .w {
  font-size: 13px;
  color: var(--quiet);
  font-variant-numeric: tabular-nums;
}

/* The week he is in, without a border or a background to say so. */
.weeks .now .w {
  color: var(--ink);
  font-weight: 500;
}

.dots {
  display: flex;
  gap: 9px;
}

.dots i {
  display: block;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--empty);
}

/* Filled. On its own — no heart rate to colour it with — it is the neutral
   one; the five shades below are the second layer, and each of them is darker
   than this, so nothing loses its "filled" by gaining a colour. Barely darker,
   at the light end: an unmeasured hour and the easiest measured one weigh the
   same on the page and are told apart by the colour, which is what the second
   layer is for.

   Two class names each, so a shade beats `.on` on specificity. Written as
   `.dots i.hr1` they would tie with it and win only by being further down the
   file, and the day somebody sorted these rules every dot on the page would go
   quietly grey — which does not look broken, it looks like a man who never
   wears his watch. */
.dots i.on { background: var(--unknown); }

.dots i.on.hr1 { background: var(--hr1); }
.dots i.on.hr2 { background: var(--hr2); }
.dots i.on.hr3 { background: var(--hr3); }
.dots i.on.hr4 { background: var(--hr4); }
.dots i.on.hr5 { background: var(--hr5); }

/* Before the phone was exporting: lighter still, and not a judgement on
   anybody. An empty slot and a week nothing knows about are not the same. */
.dots i.no { background: var(--nodata); }

/* A fourth session, mentioned rather than drawn. */
.plus {
  font-size: 12px;
  color: var(--quiet);
  font-variant-numeric: tabular-nums;
}

/* The week's hardest beat. Detail, not headline: 12px against a streak of 96,
   and the digits tabular so the column is a column.

   `--quiet`, like the `+1` beside it and the date at the other end, and
   `--ink` in the current week's row. A row is one weight the whole way across
   because every part of it is the same kind of thing: something he came here
   to read. */
.peak {
  display: flex;
  align-items: center;
  gap: 4px;
  justify-content: flex-end;
  font-size: 12px;
  color: var(--quiet);
  font-variant-numeric: tabular-nums;
}

.weeks .now .peak { color: var(--ink); }

.peak .hb {
  width: 10px;
  height: 10px;
  fill: currentColor;
  /* The glyph's own mass sits high in its box; this drops it onto the
     baseline-ish middle of 12px digits. */
  margin-top: -1px;
}

/* Where the page explains itself: what makes a week, what the shades mean,
   what was counted and what was not. Small, because it is detail — but not
   faint, because it is the text he needs at exactly the moment a number on the
   page looks wrong. */
.caption {
  margin: 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--quiet);
}

.caption:first-of-type {
  margin-top: 18px;
}

/* -------------------------------------------------------------------- footer */

footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px 16px;
  margin-top: 44px;
  font-size: 12px;
  color: var(--quiet);
}

footer p { margin: 0; }
footer form { margin: 0; }

/* The only control on the page that is a word rather than a button. It used to
   be told apart from the footer beside it by being the darker of two greys;
   now that they are one grey, it is told apart by being underlined — which is
   what a thing you can press has always looked like, and does not ask anybody
   to notice a shade. */
.link {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--quiet);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

/* --------------------------------------------------------------------- login */

.login-page main.login {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 100vh;
  min-height: 100dvh;
  max-width: 380px;
}

.login h1 {
  margin-bottom: 6px;
}

.lede {
  margin: 0 0 24px;
  color: var(--quiet);
  font-size: 15px;
}

label {
  display: block;
  margin-bottom: 6px;
  font-size: 13px;
  color: var(--quiet);
}

input[type='email'],
input[type='text'] {
  width: 100%;
  padding: 14px 16px;
  border: 0;
  border-radius: 12px;
  background: var(--field);
  color: var(--ink);
  /* 17px because anything under 16 makes iOS zoom the page on focus. */
  font: inherit;
  font-size: 17px;
}

input.code {
  text-align: center;
  letter-spacing: 0.32em;
  font-size: 26px;
  font-variant-numeric: tabular-nums;
}

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

button[type='submit'] {
  width: 100%;
  margin-top: 16px;
  padding: 14px 16px;
  border: 0;
  border-radius: 12px;
  background: var(--accent);
  color: #ffffff;
  font: inherit;
  font-size: 17px;
  font-weight: 600;
  cursor: pointer;
}

/* Tapped once. The button loses the accent — which is what said "press me" —
   and takes the "Sending…" label, and it is that label rather than a faded one
   that the person is meant to read, so the text stays at full contrast instead
   of dimming out. There is one colour scheme here (`color-scheme: light`, at
   the top of this file), so there is one disabled style. */
button[type='submit']:disabled {
  background: var(--empty);
  color: var(--ink);
  cursor: default;
}

.secondary {
  margin-top: 18px;
  text-align: center;
}

.secondary button {
  width: auto;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--quiet);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
}

/* The word, not the button: it is told apart from the text by its underline
   (see `.link` above), so losing the underline is what says it is no longer
   something to press. A grey block here would be a new shape appearing. */
.secondary button:disabled {
  background: none;
  color: var(--quiet);
  text-decoration: none;
  cursor: default;
}

.msg {
  margin: 0 0 18px;
  font-size: 14px;
}

.msg.bad { color: #b3261e; }
.msg.ok { color: var(--accent); }
