*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
}

html {
  height: 100%;
}

/* min-height: a fixed 100% clips the 100dvh containers when a mobile toolbar
   retracts. Relative so the corner buttons anchor to the document. */
body {
  position: relative;
  min-height: 100%;
}

/* Registered so a hover can transition it: the fill is a gradient layer, and
   those do not interpolate. */
@property --fill {
  syntax: "<color>";
  inherits: false;
  initial-value: transparent;
}

:root {
  /* One page gutter. Everything at a page edge derives from this. */
  --gutter: 24px;

  /* Every clock measurement is a ratio of this, so it scales as one object.
     188px is the designed size and cap; the other terms pull it down.
       50vw - 34px - g   two fields plus 60px of chrome inside the gutters,
                         8px spare so rounding cannot cause a scrollbar
       42vh              landscape phones pass the width term at ~390px tall */
  --clock-field: min(188px, calc(50vw - 34px - var(--gutter)), 42vh);

  /* 144/188 of the field. */
  --clock-type: calc(var(--clock-field) * 0.766);

  /* The one accent: focus rings and ::selection. Zinc, so it marks without
     colouring. */
  --accent: #71717a;

  /* The one press curve, shared by every transition that carries a press.
     Steeper than any built-in, which flatten a movement this small to nothing. */
  --ease-press: cubic-bezier(0.25, 0.46, 0.45, 0.94);

  /* Fast start, slow settle: what appears in the feedback corner. */
  --ease-arrive: cubic-bezier(0.19, 1, 0.22, 1);

  /* Each button's 1px border: light at the top, fading toward the foot. */
  --edge-bright: linear-gradient(#ffffff59, #ffffff14);
  --edge-dark: linear-gradient(#ffffff1f, #ffffff05);

  /* Tailwind's shadow-md, under every button. */
  --shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;

  color-scheme: dark;

  --page: #09090b;
  --ink: #fafafa;
  /* 5.81:1 on --field, 4.07:1 on --field-hover. */
  --caption: #a1a1aa;

  --field: #27272a;
  --field-hover: #3f3f46;

  /* Separates the dark buttons from the dark page (1.12:1 without it). */
  --edge-ring: #0009;

  /* 6.40:1 on the dark pill. */
  --error: #f87171;
}

/* Self-hosted: Google Fonts' build has no cv01, which the clock needs.
   Subset to Latin with the two features used, 71 KB instead of 344:
     pyftsubset InterVariable.woff2 --output-file=InterVariable.woff2 \
       --flavor=woff2 --layout-features+=tnum,cv01 \
       --unicodes="U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,\
U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,\
U+2212,U+2215,U+FEFF,U+FFFD"
   Licence beside the file. */
@font-face {
  font-family: InterVariable;
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/assets/InterVariable-0f0fad49.woff2") format("woff2");
}

body {
  font-family: InterVariable, system-ui, sans-serif;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  color: var(--ink);
  background-color: var(--page);
}

button {
  font: inherit;
}

/* Ink pinned: selection overrides whatever colour the element has. */
::selection {
  background-color: var(--accent);
  color: #fafafa;
}

/* No selection paint on the digits: the ring already marks the unit.
   Transparent, not user-select: none — a drag still has to select for typing
   to replace it. Covers the .numeral cells and the swapped-in editor. */
.digits::selection,
.digits *::selection {
  background-color: transparent;
  color: var(--ink);
}

/* --- Editable clock --- */

/* The ring goes on every unit, transparent until focused, and out of every
   transition, so focus lands at full strength on frame one — the way
   .control:focus-visible leaves its colour out. Declared here rather than on
   the .editable rule below because that class lands after the first paint: an
   outline arriving with it would fade up from currentColor, a white ring. */
.minutes,
.seconds {
  border-radius: calc(var(--clock-field) * 0.128);
  outline: 2px solid transparent;
  outline-offset: 4px;
}

/* A grey field invites the click. Keyed off the class the controller adds, not
   the page: the dashboard clock is editable only while idle. */
.minutes:has(.digits.editable),
.seconds:has(.digits.editable) {
  background-color: var(--field);
}

/* Hover only, so keyed off a class that lands after the field has been painted
   once: .editable itself arrives after the first paint, and transitioning that
   would fade the grey in on load — a fourth entrance, timed by whenever the
   module happened to resolve. */
:is(.minutes, .seconds):has(.digits.editable.painted) {
  transition: background-color 150ms ease;
}

/* Gated: on a touch screen a tap fires hover, and the brighter grey would sit
   there until something else was tapped. */
@media (hover: hover) and (pointer: fine) {
  :is(.minutes, .seconds):has(.digits.editable):hover {
    background-color: var(--field-hover);
  }
}

/* The class the controller toggles, not :focus-within: focus sits on the
   editor it builds. */
:is(.minutes, .seconds):has(.digits.editing) {
  outline-color: var(--accent);
}

/* Under the corner buttons, where the page is empty: in the flow it pushes the
   button down, and reserved room is wasted on every page without a message.
   44px is the buttons' height, 12px the gap. */
.error {
  position: absolute;
  top: calc(max(var(--gutter), env(safe-area-inset-top)) + 44px + 12px);
  left: 0;
  right: 0;
  width: max-content;
  max-width: calc(100vw - var(--gutter) * 2);
  margin: 0 auto;
  /* Same object as a corner button: dark pill, dark edge. */
  /* The border is the eighth and twentieth pixel. */
  padding: 7px 19px;
  border-radius: 999px;
  --fill: #18181b;
  --edge: var(--edge-dark);
  box-shadow:
    0 0 0 1px var(--edge-ring),
    var(--shadow);
  font-size: 14px;
  line-height: 20px;
  text-align: center;
  text-wrap: balance;
  color: var(--error);
  /* Same function as the keyframe, so the two interpolate directly. */
  transform: translate(0, 0);
}

/* An animation, not a transition: hidden removes the element with display,
   and nothing animates across that. */
@keyframes error-arrive {
  from {
    opacity: 0;
    transform: translate(0, -4px);
  }
}

.error:not([hidden]) {
  animation: error-arrive 0.15s ease-out;
}

@media (prefers-reduced-motion: reduce) {
  .error:not([hidden]) {
    animation: none;
  }
}


/* --- Dashboard --- */

.dashboard-page {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--gutter);
}

.dashboard {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Gives way before the clock: on a landscape phone the clock is already at
     its 42vh floor. */
  gap: clamp(24px, 6vh, 48px);
}

/* --- Clock --- */

.clock {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Dragging over the clock only highlights it. Prefixed everywhere it appears:
   unprefixed user-select only reached Safari 17, no autoprefixer. */
.clock {
  -webkit-user-select: none;
  user-select: none;
}

.minutes,
.seconds {
  position: relative;
  display: flex;
  flex-direction: column;
  /* On every page, so the numbers do not move after Iniciar. */
  padding: 6px;
}

.digits {
  font-size: var(--clock-type);
  /* Full height, so the glyphs centre in the field. */
  line-height: var(--clock-field);
  font-weight: 700;
  color: var(--ink);
  /* Fixed advance: no shift while typing, no jitter while running. */
  font-variant-numeric: tabular-nums;
  /* cv01's "1" at the same advance. Separate line: "tnum" here would override
     font-variant-numeric. */
  font-feature-settings: "cv01" 1;
  /* Square with the line box, so the field cannot collapse while edited. */
  width: var(--clock-field);
  text-align: center;
}

/* A ticking clock swaps its text for one cell per place. See timer_controller. */
.digits:has(.digit) {
  display: flex;
  justify-content: center;
}

/* One figure wide, so the cells sit where the paused text's glyphs do. Halving
   the field put each one a few pixels off, and pausing jumped. */
.digit {
  position: relative;
  height: var(--clock-field);
  overflow: hidden;
}

/* The width of a tabular figure, from the font itself. */
.digit::before {
  content: "0";
  visibility: hidden;
}

/* Two numerals, not one: a cell always holds one, so :has(.numeral) left the
   mask on and the cell composited while still. A second only exists while the
   two cross. */
.digit:has(.numeral + .numeral) {
  /* Fades the two edges a numeral crosses, so the clip does not cut it off.
     Inset because the caption overlaps this box near its foot. */
  --fade: calc(var(--clock-field) * 0.117);
  --fade-inset: calc(var(--clock-field) * 0.085);
  mask-image: linear-gradient(
    to bottom,
    transparent var(--fade-inset),
    #000 calc(var(--fade-inset) + var(--fade)),
    #000 calc(100% - var(--fade-inset) - var(--fade)),
    transparent calc(100% - var(--fade-inset))
  );
}

/* Outgoing and incoming numerals stack while they cross. */
.numeral {
  position: absolute;
  inset: 0;
}

/* The editor swapped in while idle. An input inherits neither font nor ink,
   and fills the span's box so the digits sit where the text did. */
.digits input {
  display: block;
  width: 100%;
  height: var(--clock-field);
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  color: inherit;
  text-align: center;
  /* The field already shows a ring. */
  outline: none;
  /* A keystroke replaces the whole value; a caret would offer an insertion
     point that does not exist. */
  caret-color: transparent;
  /* .clock turns selection off to keep iOS's long-press Copy menu away, and
     WebKit lets that reach into the field; typing needs it back. */
  -webkit-user-select: text;
  user-select: text;
  /* iOS's tap flash, drawn over the glyphs. */
  -webkit-tap-highlight-color: transparent;
}

/* Absolute, so it adds no height and the numbers stay put. */
.label {
  position: absolute;
  left: 0;
  right: 0;
  /* Same inner edge as the field's 6px padding. */
  bottom: 6px;
  width: 100%;
  /* 14/188 of the field, floored where it stops being readable. */
  font-size: max(12px, calc(var(--clock-field) * 0.0745));
  font-weight: 500;
  color: var(--caption);
  /* Captions, not content: out of a drag aimed at the numbers. */
  -webkit-user-select: none;
  user-select: none;
  text-align: center;
  text-transform: uppercase;
}

/* Drawn dots, not ":": a colon rests on the baseline and would need an offset
   to centre. */
.separator {
  align-self: stretch;
  width: 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: calc(var(--clock-field) * 0.101);
}

/* The 20px column is chrome --clock-field budgets for; the dots scale. */
.separator::before,
.separator::after {
  content: "";
  width: calc(var(--clock-field) * 0.053);
  height: calc(var(--clock-field) * 0.053);
  border-radius: 50%;
  background-color: var(--caption);
}

/* --- Controls --- */

.controls {
  width: 100%;
  display: flex;
  justify-content: center;
  /* Backstop for a scaled page or wider font: stack, not sideways scroll. */
  flex-wrap: wrap;
  gap: 12px;
}

/* Each control is a button_to, so a <form> sits between .controls and its
   button. display: contents makes the buttons the flex items. */
.controls .button_to {
  display: contents;
}

.control {
  /* Fixed: the first button cycles Iniciar/Pausar/Reanudar, and sizing to the
     label shifted the row. */
  width: 128px;
  /* The border is the 20th pixel. */
  padding: 0 19px;
  border: none;
  border-radius: 64px;
  font-size: 16px;
  /* 42px plus the border is the 44px touch target. */
  line-height: 42px;
  font-weight: 500;
  color: #09090b;
  cursor: pointer;
  /* Transparent from the start: outline-style does not animate. */
  outline: 2px solid transparent;
  outline-offset: 0;
  /* One speed for every surface that answers a pointer, the corner buttons
     included — they take this shorthand rather than restating it. 150ms
     because a click is over in about 100, and at 0.2s the press never landed. */
  transition:
    --fill 150ms ease,
    transform 150ms var(--ease-press),
    outline-offset 150ms ease-out;
  --edge: var(--edge-bright);
  box-shadow:
    0 0 0 1px var(--edge-ring),
    var(--shadow);
}

/* Every pill: the fill inside the border, the edge gradient under it. */
.control,
.error,
.feedback-thanks {
  border: 1px solid transparent;
  background:
    linear-gradient(var(--fill) 0 0) padding-box,
    var(--edge) border-box var(--fill);
}

/* Felt, not seen: 0.95 collapsed the 128px pill by 6.4px across. */
.control:active:not(:disabled) {
  transform: scale(0.97);
}

/* One focus treatment: 4.12:1 on the page. Colour
   left out of the transition, so the ring is full strength on frame one. */
.control:focus-visible {
  outline-color: var(--accent);
  outline-offset: 4px;
}

/* At its final offset there is nothing to animate. Covers the icon buttons
   too, which carry .control. */
@media (prefers-reduced-motion: reduce) {
  .control {
    outline-offset: 4px;
  }
}

/* Bright fills that brighten one step on hover. The dark ink clears 4.5:1 on
   each, in both states. */
.control.start {
  --fill: #84cc16;
}

.control.pause {
  --fill: #eab308;
}

.control.reset {
  --fill: #0ea5e9;
}

/* Gated, like every hover here: a tap on a touch screen fires hover too, and
   the brighter fill would stay until something else was tapped. */
@media (hover: hover) and (pointer: fine) {
  .control.start:hover:not(:disabled) {
    --fill: #a3e635;
  }

  .control.pause:hover:not(:disabled) {
    --fill: #facc15;
  }

  .control.reset:hover:not(:disabled) {
    --fill: #38bdf8;
  }
}

/* Chrome, not a state: the corner buttons' dark pill rather than a fourth
   bright fill. The edge is the dark one, for the same reason theirs is —
   .control's is calibrated for bright fills. */
.control.deadline {
  /* Nothing cycles here, so .control's fixed 128px is not needed: it sizes to
     its parts, which are each a whole number. The height is still the 44px
     line box. */
  width: auto;
  /* What the chevron measures from. */
  position: relative;
  display: flex;
  align-items: center;
  /* With the caption's slack, the same as the hour's room before the arrow. */
  gap: 6px;
  --fill: #18181b;
  color: #fafafa;
  --edge: var(--edge-dark);
  box-shadow:
    0 0 0 1px var(--edge-ring),
    var(--shadow);
}

/* :has(), because the focus lands on the field, not on the label around it.
   Colour out of the transition, like .control:focus-visible. */
.control.deadline:has(select:focus-visible) {
  outline-color: var(--accent);
  outline-offset: 4px;
}

@media (hover: hover) and (pointer: fine) {
  .control.deadline:hover {
    --fill: #212124;
  }
}

/* The only measured thing in the pill, so the only one that can hand it a
   fractional width and take the whole row off the pixel grid. Boxed to a whole
   number, with the slack falling into the gap after it. min-, because this
   text never changes: the width settles at load, on whichever face the swap
   leaves it in, so a wide fallback growing the box moves nothing. One number
   per language, since the word is: "Hasta las" is 70px in Inter, "Until" 35. */
.deadline .caption {
  min-width: 72px;
}

.deadline .caption:lang(en) {
  min-width: 36px;
}

/* A select inherits neither font nor ink; the pill shows the ring. Its own
   arrow goes, so the chevron below can be one the page drew. */
.deadline select {
  -webkit-appearance: none;
  appearance: none;
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  color: inherit;
  outline: none;
  /* Fixed, not the caption's min-, because this text does change: a box free
     to grow would walk Iniciar sideways on every pick. Sized to the widest hour
     on the list: 72px for "9:00 a.m.", 84px once a "12:45 p.m." is on it
     (.wide). The other 16px are the chevron's, which lies over this. A wide
     fallback face clips here rather than growing the box. */
  width: 88px;
  padding-right: 16px;
  /* Fixed advance, so the hours do not shuffle as the list is walked. */
  font-variant-numeric: tabular-nums;
  text-align: center;
  cursor: pointer;
}

.deadline select.wide {
  width: 100px;
}

/* The blank has no figures to line up, and tabular would give each of its
   dashes a digit's advance and spread them apart. */
.deadline select:has(option[value=""]:checked) {
  font-variant-numeric: normal;
}

/* The open list is painted in the options' own colours. */
.deadline option {
  background-color: #18181b;
  color: #fafafa;
}

/* Dimmer than an hour, because it is the empty value and not one. On the
   option rather than the select, so the list dims this row alone and the hours
   under it stay at full strength. */
.deadline option[value=""] {
  color: var(--caption);
}

/* Over the select's own right end, because a click on the arrow has to reach
   the select for the list to open. The arrow is the middle third of its box,
   so this offset brings its right edge to the pill's padding. */
.deadline .chevron {
  position: absolute;
  right: 12px;
  top: 50%;
  width: 24px;
  height: 24px;
  margin-top: -12px;
  opacity: 0.55;
  pointer-events: none;
}

/* --- Corner buttons --- */

/* Two of one object: Proyectar and fullscreen. The deadline
   pill takes the same dark fill, without the corner. Each carries .control for
   the edge, ring and press; the corner and disc are added here.
   The edge is the dark one: .control's is calibrated for bright fills. */
.watch,
.fullscreen {
  /* Against the body: out of the flex column, cannot push the clock off
     centre. */
  position: absolute;
  /* max(): a notch pushes it in, never less than the gutter. */
  top: max(var(--gutter), env(safe-area-inset-top));
  display: flex;
  align-items: center;
  justify-content: center;
  /* Both sides stated, padding zeroed: .control's 128px is sized to a label.
     44px keeps the touch target. */
  width: 44px;
  height: 44px;
  padding: 0;
  border-radius: 50%;
  --fill: #18181b;
  color: #fafafa;
  --edge: var(--edge-dark);
  box-shadow:
    0 0 0 1px var(--edge-ring),
    var(--shadow);
}

.watch,
.fullscreen {
  right: max(var(--gutter), env(safe-area-inset-right));
}

/* The only one that is a link. */
.watch {
  text-decoration: none;
}

@media (hover: hover) and (pointer: fine) {
  .watch:hover,
  .fullscreen:hover {
    --fill: #212124;
  }
}

/* A percentage scale walks an edge by a fraction of the box: .control's 0.97
   is 3.8px on the pill but 1.3px on a 44px disc. 0.92 keeps the travel
   visible, and the glyph rides it without going pale: a uniform scale takes
   the stroke down by the same 8% as the icon. :not(:disabled) is what lifts
   this over .control:active — the two tie on specificity, so without it the
   press turns on nothing but which block the file happens to end with. It is
   also the guard the pill already has. */
.control.watch:active:not(:disabled),
.control.fullscreen:active:not(:disabled) {
  transform: scale(0.92);
}

.watch .icon,
.fullscreen .icon {
  width: 24px;
  height: 24px;
}

/* --- Feedback --- */

/* The corner buttons' dark pill, in the bottom corner. Fixed in place: the
   entry page scrolls, and the button stays in the corner while it does. */
.feedback-open,
.feedback-thanks,
.feedback-card {
  position: fixed;
  right: max(var(--gutter), env(safe-area-inset-right));
  bottom: max(var(--gutter), env(safe-area-inset-bottom));
  color: #fafafa;
  font-size: 14px;
  line-height: 20px;
}

.feedback-open,
.feedback-thanks {
  height: 44px;
  border-radius: 999px;
  --fill: #18181b;
  --edge: var(--edge-dark);
  box-shadow:
    0 0 0 1px var(--edge-ring),
    var(--shadow);
}

/* .control's edge, ring and press, sized to its label. */
.feedback-open {
  width: auto;
  padding: 0 15px;
  font-size: 14px;
  line-height: 42px;
}

/* Hidden once the card is opaque over it, so the corner is never empty. */
.feedback:has(.feedback-card:popover-open) .feedback-open {
  visibility: hidden;
  transition: visibility 0s 200ms;
}

@media (hover: hover) and (pointer: fine) {
  .feedback-open:hover {
    --fill: #212124;
  }
}

.feedback-thanks {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 16px 0 12px;
  transform-origin: bottom right;
}

/* One animation, not a transition in and one out: Firefox drops the
   transition while the other waits. In for 150ms, read for 3s, out for 200. */
@keyframes thanks {
  0% {
    opacity: 0;
    transform: scale(0.97);
    animation-timing-function: var(--ease-arrive);
  }

  4.4% {
    opacity: 1;
    transform: none;
  }

  94.1% {
    opacity: 1;
    animation-timing-function: ease-out;
  }

  100% {
    opacity: 0;
    visibility: hidden;
  }
}

@keyframes button-return {
  from {
    opacity: 0;
    visibility: hidden;
  }
}

.feedback.thanked .feedback-thanks {
  animation: thanks 3.4s linear forwards;
}

.feedback.thanked .feedback-open {
  animation: button-return 200ms var(--ease-arrive) 3.2s backwards;
}

.feedback-thanks svg {
  width: 24px;
  height: 24px;
  color: #84cc16;
}

/* A popover sits in the top layer, so these replace the browser's centred
   box. */
.feedback-card {
  position: fixed;
  inset: auto;
  right: max(var(--gutter), env(safe-area-inset-right));
  bottom: max(var(--gutter), env(safe-area-inset-bottom));
  width: min(320px, 100vw - var(--gutter) * 2);
  margin: 0;
  padding: 16px;
  border: none;
  border-radius: 16px;
  background-color: #18181b;
  box-shadow:
    inset 0 0 0 1px #ffffff14,
    0 0 0 1px var(--edge-ring),
    0 25px 50px -12px #00000040;
  transform-origin: bottom right;
  transition:
    opacity 200ms var(--ease-arrive),
    transform 200ms var(--ease-arrive);
}

/* Closed is hidden, not display: none, because Firefox cannot transition a
   popover's display, and the close has to fade. Closing is the faster half. */
.feedback-card:not(:popover-open) {
  display: block;
  visibility: hidden;
  opacity: 0;
  transform: scale(0.96);
  transition:
    opacity 120ms ease-out,
    transform 120ms ease-out,
    visibility 0s 120ms;
}

.feedback-card form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.feedback-trap {
  position: absolute;
  left: -9999px;
}

.feedback-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.feedback-header h2 {
  font-size: 16px;
  line-height: 24px;
  font-weight: 500;
}

/* A 44px target around the 24px icon, without taking 44px of the header. */
.feedback-close {
  display: flex;
  padding: 10px;
  margin: -10px;
  border: none;
  border-radius: 12px;
  background: none;
  color: #a1a1aa;
  cursor: pointer;
  outline: 2px solid transparent;
  outline-offset: -8px;
}

.feedback-close:focus-visible {
  outline-color: var(--accent);
}

@media (hover: hover) and (pointer: fine) {
  .feedback-close:hover {
    color: #fafafa;
  }
}

.feedback-close svg {
  width: 24px;
  height: 24px;
}

.feedback-card textarea,
.feedback-card input[type="email"] {
  display: block;
  width: 100%;
  padding: 10px 12px;
  border: none;
  border-radius: 8px;
  background-color: #27272a;
  font: inherit;
  color: inherit;
  outline: none;
}

/* The lime edge is the focus ring. */
.feedback-card textarea:focus,
.feedback-card input[type="email"]:focus {
  box-shadow: inset 0 0 0 2px #84cc16;
}

.feedback-card ::placeholder {
  color: #71717a;
  opacity: 1;
}

.feedback-card textarea {
  height: 96px;
  resize: none;
}

/* iOS zooms into any field under 16px. */
@media (pointer: coarse) {
  .feedback-card textarea,
  .feedback-card input[type="email"] {
    font-size: 16px;
  }
}

.feedback-footer {
  display: flex;
  align-items: center;
  gap: 12px;
}

.feedback-error {
  flex: 1;
  color: var(--error);
}

.feedback-send {
  height: 32px;
  padding: 0 16px;
  border: none;
  border-radius: 999px;
  background-color: #84cc16;
  font-weight: 500;
  line-height: 20px;
  color: #09090b;
  cursor: pointer;
  outline: 2px solid transparent;
  outline-offset: 2px;
  transition:
    background-color 150ms ease,
    transform 150ms var(--ease-press);
}

.feedback-send:focus-visible {
  outline-color: var(--accent);
}

.feedback-send:active {
  transform: scale(0.97);
}

@media (hover: hover) and (pointer: fine) {
  .feedback-send:hover {
    background-color: #a3e635;
  }
}

/* --- About --- */

/* A column of reading width under the first screen. */
.about {
  max-width: 36rem;
  margin: 0 auto;
  /* At the foot, room for the feedback button, which stays put over the last
     lines. */
  padding: 48px var(--gutter)
    calc(max(var(--gutter), env(safe-area-inset-bottom)) * 2 + 44px);
  font-size: 16px;
  line-height: 1.6;
  color: var(--caption);
}

.about h2 {
  margin: 40px 0 12px;
  font-size: 20px;
  line-height: 28px;
  font-weight: 600;
  color: var(--ink);
}

.about h2:first-child {
  margin-top: 0;
}

.about p + p {
  margin-top: 12px;
}

.about a,
.about-feedback {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--accent);
  text-underline-offset: 3px;
}

/* A button that reads as a link: it opens the feedback card. */
.about-feedback {
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}

.keys {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 8px 16px;
  align-items: baseline;
}

.keys dd {
  margin: 0;
}

kbd {
  display: inline-block;
  min-width: 28px;
  padding: 0 8px;
  border-radius: 6px;
  background-color: var(--field);
  font: inherit;
  font-size: 14px;
  line-height: 28px;
  font-weight: 500;
  text-align: center;
  color: var(--ink);
}

.about .byline {
  margin-top: 40px;
}

/* An external link: the arrow says so, and the underline waits for hover. */
.about .byline a {
  display: inline-flex;
  align-items: center;
  border-radius: 2px;
  text-decoration: none;
  outline: 2px solid transparent;
  outline-offset: 2px;
}

.about .byline a:focus-visible {
  outline-color: var(--accent);
}

@media (hover: hover) and (pointer: fine) {
  .about .byline a:hover {
    text-decoration: underline;
    text-underline-offset: 4px;
  }
}

.about .byline .icon {
  width: 16px;
  height: 16px;
}

/* --- Watch page --- */

/* 100% and dvh, not 100vw/100vh: vw counts the scrollbar gutter and vh the
   tall toolbar, so this scrolled sideways on desktop and sat under a phone's
   URL bar. */
.timer-container {
  position: relative;
  width: 100%;
  height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Nothing reaches it today, but the clock's width budget assumes it. */
  padding: var(--gutter);
  overflow: hidden;
  /* So the clock can measure the room it has inside the padding. */
  container-type: size;
}

/* The digits take the largest step of Tailwind's type scale, extended for
   TVs, that fits two thirds of the room. On the clock because a container
   cannot query itself. The box keeps the dashboard's proportion to the
   digits, on Tailwind's grid. */
.timer-container .clock {
  position: relative;
  --clock-type: 36px;
  --clock-field: calc(round(var(--clock-type) * 200 / 144, 4px) - 12px);
  /* Tailwind's smallest type, until the steps for TVs. */
  --clock-caption: 12px;
}

/* The room each step needs: one and a half times the clock, plus padding. */
@container (width >= 246px) and (height >= 106px) {
  .timer-container .clock { --clock-type: 48px; }
}

@container (width >= 296px) and (height >= 130px) {
  .timer-container .clock { --clock-type: 60px; }
}

@container (width >= 345px) and (height >= 153px) {
  .timer-container .clock { --clock-type: 72px; }
}

@container (width >= 444px) and (height >= 200px) {
  .timer-container .clock { --clock-type: 96px; }
}

@container (width >= 576px) and (height >= 263px) {
  .timer-container .clock { --clock-type: 128px; }
}

@container (width >= 840px) and (height >= 388px) {
  .timer-container .clock { --clock-type: 192px; --clock-caption: 18px; }
}

@container (width >= 1104px) and (height >= 514px) {
  .timer-container .clock { --clock-type: 256px; --clock-caption: 24px; }
}

@container (width >= 1632px) and (height >= 764px) {
  .timer-container .clock { --clock-type: 384px; --clock-caption: 36px; }
}

@container (width >= 2160px) and (height >= 1015px) {
  .timer-container .clock { --clock-type: 512px; --clock-caption: 48px; }
}

/* The dashboard's proportions to the field, on Tailwind's grid. */
.timer-container .separator {
  width: round(calc(var(--clock-field) * 0.106), 4px);
  gap: round(calc(var(--clock-field) * 0.101), 4px);
}

.timer-container .separator::before,
.timer-container .separator::after {
  width: round(calc(var(--clock-field) * 0.053), 4px);
  height: round(calc(var(--clock-field) * 0.053), 4px);
}

.timer-container .minutes,
.timer-container .seconds {
  border-radius: round(calc(var(--clock-field) * 0.128), 4px);
}

.timer-container .label {
  font-size: var(--clock-caption);
}

/* --- Fullscreen toggle --- */

/* The corner transition plus opacity: this is the one that withdraws.
   Arriving is the fast half — it answers a gesture that already happened. */
.fullscreen {
  transition:
    opacity 150ms ease-out,
    --fill 150ms ease,
    transform 150ms var(--ease-press),
    outline-offset 150ms ease-out;
}

/* Withdrawn, not removed, so it comes back without moving anything.
   pointer-events keeps an invisible button from throwing the watch page into
   fullscreen. Leaving is the slow half: a quick disappearance at the edge of a
   still page pulls the eye. */
.fullscreen.idle {
  opacity: 0;
  pointer-events: none;
  /* linear, not ease-in: nothing about a withdrawal should accelerate. A curve
     that holds near full and then drops away pulls the eye at the very moment
     it is trying not to. */
  transition: opacity 0.4s linear;
}

/* Off the document: Escape leaves fullscreen without this button hearing. */
:root:not(:fullscreen) .fullscreen .collapse,
:root:fullscreen .fullscreen .expand {
  display: none;
}

/* The pointer is chrome too: once the button has withdrawn, an arrow on the
   wall is the one thing that is not the clock. */
:root:fullscreen:has(.fullscreen.idle) {
  cursor: none;
}

/* --- Entrance --- */

/* One gesture on load: page fades, clock rises, corners follow. Only elements
   no broadcast replaces — on #timer_clock or #timer_controls it would replay
   on every start, pause and reset. */
@keyframes arrive {
  from {
    opacity: 0;
  }
}

@keyframes rise {
  from {
    transform: translate(0, 12px);
  }
}

.dashboard-page {
  /* Opacity only: a transform would become the containing block for the
     corner buttons, which measure from the body. */
  animation: arrive 0.3s ease-out;
}

/* The clock moves but does not fade on its own: the page already does, and a
   second fade over the same moment holds it back. */
.editable-clock {
  /* Stated, like .error's, so the two interpolate directly. Also why .error
     renders outside this element: a transform contains absolute descendants. */
  transform: translate(0, 0);
  animation: rise 0.4s ease-out;
}

/* The watch page has no wrapper between page and clock, so one element takes
   both halves. */
.timer-container {
  transform: translate(0, 0);
  animation:
    arrive 0.3s ease-out,
    rise 0.4s ease-out;
}

/* Chrome last, so the corner settles after the clock. backwards spends the
   wait invisible. */
.control.watch,
.control.fullscreen,
.feedback-open {
  animation: arrive 0.3s ease-out 0.15s backwards;
}

@media (prefers-reduced-motion: reduce) {
  .dashboard-page,
  .editable-clock,
  .timer-container,
  .control.watch,
  .control.fullscreen,
  .feedback-open {
    animation: none;
  }

  /* The fades stay; only the scale goes. */
  .feedback-card:not(:popover-open) {
    transform: none;
  }

  @keyframes thanks {
    0% {
      opacity: 0;
    }

    4.4%,
    94.1% {
      opacity: 1;
    }

    100% {
      opacity: 0;
      visibility: hidden;
    }
  }
}

/* One declaration moves the page padding, the corner buttons and the clock,
   which all budget against this. */
@media (width < 30rem) {
  :root {
    --gutter: 16px;
  }

  /* Iniciar and the pill do not fit a phone at the wide chrome, and .controls
     wraps rather than scroll, which put the pill under the button. What gives
     is the chrome: the fixed widths inside the pill stay, because they are
     what keeps an hour from walking Iniciar sideways. */
  .controls {
    gap: 8px;
  }

  .control.deadline {
    padding: 0 15px;
  }

  /* The padding lost 4px, so the arrow gives them back to the hour. */
  .deadline .chevron {
    right: 8px;
  }

  /* Only the row that carries the pill, where Start never cycles: the width
     that keeps Start, Pause and Resume from shifting the row buys nothing
     here — and the pill needs those pixels. */
  .controls:has(.deadline) .control.start {
    width: 108px;
  }
}
