/* Hallmark · shape: app screen · genre: playful · theme: custom · redesign: full · idea: "the glass is the screen: one mahogany hourglass shot on a seamless backdrop" · vibe: "quiet studio product shot, pixel-made, warm"
 * paper: oklch(97.6% 0.004 80) · accent: oklch(40% 0.1 32) · display: Recursive · body: Recursive · controls: monogram (pixel, CC0; pixel is the material, matching the glass)
 * nav: kept (title and hint) · signature: the pixel hourglass at full height · states: running, flat, on its side, time's up, tilt error
 * axes: light/grotesk-sans/warm · posture: Restrained · contrast: pass · v2.0.0 */
/* Hallmark · pre-emit critique: P4 H4 E4 S5 R4 V4 · weakest: E, the caption column is plain on purpose; the object carries the screen */
/* Hallmark · finish-review: subagent · 5 findings · 5 resolved (controls beside the glass, one dominant highlight, soft contact shadow, lit cap rims in dark, motion-blurred stream) · gap: none */
/* Hallmark · reworded "Sand Clock" to "Hourglass" · the owner's rename (the correct English name) */
/* Hallmark · answered F4 · the secondary controls are neutral surface chips (--color-button), quiet beside the one mahogany Flip; not a second primary */
/* Hallmark · waive 72 · measured box is monogram's 25px ascent box at 32px, visible 14px glyphs sit ~15px from each edge of the 44px controls (close-ups checked) · the empty font pixels above the capitals are part of the face, not crowding */
/* Hallmark · waive 48 · --sand-*, --glass-*, --wood-* are declared at :root and read by js/main.js · the simulation colours are tokens, not literals */

@font-face {
  font-family: "Recursive";
  src: url("fonts/recursive-latin.woff2") format("woff2");
  font-weight: 300 1000;
  font-display: swap;
}

@font-face {
  font-family: "monogram";
  src: url("fonts/monogram.ttf") format("truetype");
  font-display: block; /* 10 kB, preloaded and cached offline: no flash of a fallback face */
}

:root {
  color-scheme: light dark;

  --color-paper: oklch(97.6% 0.004 80);
  --color-paper-2: oklch(91.5% 0.008 70); /* the backdrop's falloff towards the edges */
  --color-ink: oklch(24% 0.02 40);
  --color-muted: oklch(47% 0.02 45);
  --color-button: oklch(99.5% 0.002 80);
  --color-button-hover: oklch(95% 0.008 70);
  --color-accent: oklch(40% 0.1 32); /* the mahogany of the caps */
  --color-accent-hover: oklch(34% 0.09 32);
  --color-accent-ink: oklch(97.5% 0.01 70);
  --color-focus: oklch(52% 0.14 45);
  --color-disabled: oklch(90% 0.006 70);
  --color-disabled-ink: oklch(45% 0.01 60);
  --color-shadow: rgb(60 40 30 / 0.18);

  --font-body: "Recursive", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  /* monogram is drawn on a 16-units-per-em pixel grid, so it is crisp only where one font pixel is a
     whole number of device pixels: 32px (2 CSS px per font pixel) works at 1×, 2× and 3×. */
  --font-pixel: "monogram", ui-monospace, monospace;
  --text-pixel: 32px;
  --pixel-advance: calc(var(--text-pixel) * 6 / 16); /* one monogram character */

  --text-sm: 0.875rem;
  --text-md: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.5rem;
  --text-time: clamp(2.5rem, 7vw, 4.5rem);
  --space-2xs: 0.25rem;
  --space-xs: 0.5rem;
  --space-sm: 0.75rem;
  --space-md: 1rem;
  --space-lg: 1.5rem;
  --radius: 0.6rem;
  --dur-micro: 120ms;

  /* Read by js/main.js to paint the simulation. */
  --sand-light: oklch(86% 0.055 80);
  --sand-dark: oklch(78% 0.07 72);
  --glass-fill: rgb(255 255 255 / 0.3);
  --glass-shine: rgb(255 255 255 / 0.95);
  --glass-edge: rgb(150 160 166 / 0.45);
  --glass-rim: rgb(128 138 144 / 0.7);
  --glass-shadow: rgb(70 50 40 / 0.2);
  --wood-light: oklch(44% 0.1 32);
  --wood-dark: oklch(24% 0.06 28);
  --wood-shine: oklch(62% 0.09 38);
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-paper: oklch(21% 0.01 55);
    --color-paper-2: oklch(13% 0.006 50);
    --color-ink: oklch(93% 0.012 70);
    --color-muted: oklch(73% 0.02 60);
    --color-button: oklch(25% 0.012 55);
    --color-button-hover: oklch(30% 0.014 55);
    --color-accent: oklch(74% 0.09 52);
    --color-accent-hover: oklch(80% 0.08 55);
    --color-accent-ink: oklch(18% 0.02 40);
    --color-focus: oklch(80% 0.11 65);
    --color-disabled: oklch(28% 0.008 55);
    --color-disabled-ink: oklch(68% 0.01 60);
    --color-shadow: rgb(0 0 0 / 0.5);

    --sand-light: oklch(82% 0.06 78);
    --sand-dark: oklch(72% 0.07 70);
    --glass-fill: rgb(255 255 255 / 0.035);
    --glass-shine: rgb(255 255 255 / 0.45);
    --glass-edge: rgb(205 215 220 / 0.16);
    --glass-rim: rgb(215 225 230 / 0.42);
    --glass-shadow: rgb(0 0 0 / 0.5);
    --wood-light: oklch(46% 0.1 32);
    --wood-dark: oklch(25% 0.06 28);
    --wood-shine: oklch(70% 0.09 42);
  }
}

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

html,
body {
  overflow-x: clip;
}

html {
  scrollbar-color: var(--color-muted) var(--color-paper);
}

::selection {
  background: color-mix(in oklab, var(--color-accent) 25%, transparent);
  color: var(--color-ink);
}

[hidden] {
  display: none !important;
}

body {
  margin: 0;
  overscroll-behavior: none; /* no pull-to-refresh or rubber-banding, it's an app */
  min-height: 100svh;
  min-height: var(--app-height, 100dvh);
  background-color: var(--color-paper);
  background-image: radial-gradient(ellipse 70% 80% at 50% 45%, var(--color-paper) 35%, var(--color-paper-2));
  background-attachment: fixed;
  color: var(--color-ink);
  font: var(--text-md) / 1.5 var(--font-body);
  -webkit-font-smoothing: antialiased;
}

/* Phones: title on top, the glass taking every pixel left, readout and controls below. */
.app {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto auto;
  grid-template-areas: "head" "stage" "readout" "controls";
  gap: var(--space-sm);
  /* The visible height. dvh follows browser toolbars as they show and hide; js/main.js refines it
     to visualViewport's height (--app-height), because Firefox for Android's bottom toolbar can
     cover the page without being counted in any viewport unit. */
  height: 100svh;
  height: var(--app-height, 100dvh);
  padding: max(var(--space-sm), env(safe-area-inset-top)) max(var(--space-md), env(safe-area-inset-right))
    max(var(--space-md), env(safe-area-inset-bottom)) max(var(--space-md), env(safe-area-inset-left));
}

.head {
  grid-area: head;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  column-gap: var(--space-md);
}

h1 {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: 650;
  letter-spacing: -0.01em;
  overflow-wrap: break-word;
  min-width: 0;
  text-wrap: balance;
}

.hint {
  margin: 0;
  color: var(--color-muted);
  font-size: var(--text-sm);
  text-wrap: pretty;
}

@media not (any-pointer: fine) {
  .hint .keys {
    display: none;
  }
}

.stage {
  grid-area: stage;
  position: relative;
  display: grid;
  place-items: center;
  min-height: 0;
  contain: size; /* the canvases are sized from the stage, never the other way round */
  touch-action: none;
  cursor: grab;
  border-radius: var(--radius);
  user-select: none;
  -webkit-user-select: none;
}

.stage > * {
  grid-area: 1 / 1;
}

.stage.dragging {
  cursor: grabbing;
}

.stage:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 4px;
}

.glass {
  /* Sized and turned by js/main.js. */
  will-change: transform;
}

.glass canvas,
.shadow {
  display: block;
  width: 100%;
  height: 100%;
  image-rendering: pixelated;
}

.shadow {
  opacity: var(--shadow, 1);
  transform: translateY(calc(var(--shadow-y, 0px) * var(--fit, 1))) scale(var(--fit, 1));
}

.noscript {
  max-width: 20rem;
  margin: 0;
  text-align: center;
  color: var(--color-muted);
}

.readout {
  grid-area: readout;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: var(--space-md);
}

.time {
  font-size: var(--text-time);
  font-weight: 600;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}

.status {
  margin: 0;
  min-height: 1.5em;
  color: var(--color-muted);
  font-size: var(--text-sm);
}

.controls {
  grid-area: controls;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs);
}

button,
select {
  touch-action: manipulation; /* no double-tap zoom on quick taps */
  font: inherit;
  font-family: var(--font-pixel);
  font-size: var(--text-pixel);
  font-weight: normal;
  font-synthesis: none; /* a synthesised bold would smear the pixels */
  line-height: 1;
  -webkit-font-smoothing: none;
  color: var(--color-ink);
  background: var(--color-button);
  border: 1px solid var(--color-muted);
  border-bottom-width: 2px; /* a key's lip: it presses down */
  border-radius: var(--radius);
  min-height: 2.75rem;
  padding: 0 var(--space-md);
  cursor: pointer;
  transition:
    background-color var(--dur-micro) ease-out,
    border-color var(--dur-micro) ease-out;
}

button:hover,
select:hover {
  background: var(--color-button-hover);
  border-color: var(--color-ink);
}

button:active,
select:active {
  background: var(--color-paper-2);
  border-bottom-width: 1px;
  padding-top: 1px;
}

button:focus-visible,
select:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
  transition: none;
}

button.primary {
  background: var(--color-accent);
  border-color: var(--color-accent);
  border-bottom-width: 1px;
  color: var(--color-accent-ink);
  font-family: var(--font-body);
  font-size: var(--text-lg);
  font-weight: 650;
  -webkit-font-smoothing: antialiased;
  padding: 0 var(--space-lg);
}

button.primary:hover {
  background: var(--color-accent-hover);
  border-color: var(--color-accent-hover);
}

button.primary:active {
  padding-top: 0;
  background: var(--color-ink);
  border-color: var(--color-ink);
}

button[aria-pressed="true"] {
  border-color: var(--color-accent);
  box-shadow: inset 0 0 0 1px var(--color-accent);
}

button:disabled {
  background: var(--color-disabled);
  border-color: var(--color-disabled);
  color: var(--color-disabled-ink);
  cursor: progress;
}

.duration {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  color: var(--color-muted);
}

.duration select {
  appearance: none;
  -webkit-appearance: none;
  min-inline-size: calc(6 * var(--pixel-advance) + 4 * var(--space-md)); /* "10 min" fits, so the row never shifts */
  padding-inline: var(--space-md) calc(var(--space-md) + var(--space-md));
  font-variant-numeric: tabular-nums;
}

/* Pixel glyphs, drawn cell by cell with box-shadows in the text colour so they follow the theme
   and sit beside the pixel glass. --px is one glyph cell. */
.controls {
  --px: 2px;
}

/* The select's chevron. */
.duration::after {
  content: "";
  position: absolute;
  right: calc(var(--space-md) + 4 * var(--px));
  top: 50%;
  width: var(--px);
  height: var(--px);
  margin-top: calc(-1.5 * var(--px));
  color: var(--color-ink);
  background: currentColor; /* cell (0, 0): a box-shadow never paints under its own box */
  box-shadow:
    calc(0 * var(--px)) calc(0 * var(--px)),
    calc(4 * var(--px)) calc(0 * var(--px)),
    calc(1 * var(--px)) calc(1 * var(--px)),
    calc(3 * var(--px)) calc(1 * var(--px)),
    calc(2 * var(--px)) calc(2 * var(--px));
  pointer-events: none;
}

/* Refill: a small hourglass with a full top bulb. */
#refill {
  display: inline-flex;
  align-items: center;
}

#refill::before {
  content: "";
  width: var(--px);
  height: var(--px);
  margin-right: calc(4 * var(--px) + var(--space-xs));
  transform: translate(calc(-1 * var(--px)), calc(-3 * var(--px)));
  background: currentColor;
  box-shadow:
    calc(0 * var(--px)) calc(0 * var(--px)),
    calc(1 * var(--px)) calc(0 * var(--px)),
    calc(2 * var(--px)) calc(0 * var(--px)),
    calc(3 * var(--px)) calc(0 * var(--px)),
    calc(4 * var(--px)) calc(0 * var(--px)),
    calc(1 * var(--px)) calc(1 * var(--px)),
    calc(2 * var(--px)) calc(1 * var(--px)),
    calc(3 * var(--px)) calc(1 * var(--px)),
    calc(2 * var(--px)) calc(2 * var(--px)),
    calc(2 * var(--px)) calc(3 * var(--px)),
    calc(1 * var(--px)) calc(4 * var(--px)),
    calc(3 * var(--px)) calc(4 * var(--px)),
    calc(0 * var(--px)) calc(5 * var(--px)),
    calc(4 * var(--px)) calc(5 * var(--px)),
    calc(0 * var(--px)) calc(6 * var(--px)),
    calc(1 * var(--px)) calc(6 * var(--px)),
    calc(2 * var(--px)) calc(6 * var(--px)),
    calc(3 * var(--px)) calc(6 * var(--px)),
    calc(4 * var(--px)) calc(6 * var(--px));
}

/* Browsers without a customisable select keep their native list: at body size, not the pixel size. */
option {
  background: var(--color-button);
  color: var(--color-ink);
  font: var(--text-md) / 1.5 var(--font-body);
}

/* The pixel-font copy of the select's value, used only where the list can't be styled (below). */
.duration-value {
  display: none;
}

/* Firefox and Safari show a native menu sized from the select's own font: keep that at body size
   (otherwise the menu and its checkmark grow to pixel size), hide the select's text and show the
   value in the pixel face on top. js/main.js keeps .duration-value in sync. */
@supports not (appearance: base-select) {
  .duration select {
    font: var(--text-md) / 1 var(--font-body);
    -webkit-font-smoothing: antialiased;
    color: transparent;
  }

  .duration-value {
    display: flex;
    align-items: center;
    position: absolute;
    inset-block: 0;
    inset-inline-start: var(--space-md);
    pointer-events: none;
    color: var(--color-ink);
    font-family: var(--font-pixel);
    font-size: var(--text-pixel);
    line-height: 1;
    -webkit-font-smoothing: none;
  }
}

/* Customisable select: the list itself is styled, with the real <select> underneath (keyboard,
   screen readers and the 'change' event main.js listens to all stay native). */
@supports (appearance: base-select) {
  .duration select,
  .duration select::picker(select) {
    appearance: base-select;
  }

  .duration select {
    display: inline-flex;
    align-items: center;
  }

  .duration select::picker-icon {
    display: none; /* the pixel chevron on .duration::after stands in */
  }

  .duration select::picker(select) {
    margin-block: var(--space-xs);
    padding: var(--space-xs);
    background: var(--color-button);
    color: var(--color-ink);
    border: 1px solid var(--color-muted);
    border-radius: var(--radius);
    box-shadow: 0 var(--space-2xs) 0 var(--color-shadow); /* a hard pixel drop, like the keys' lip */
  }

  .duration option {
    font-family: var(--font-pixel);
    font-size: var(--text-pixel);
    line-height: 1;
    -webkit-font-smoothing: none;
    padding: var(--space-2xs) var(--space-sm);
    border-radius: calc(var(--radius) - var(--space-xs) / 2);
    font-variant-numeric: tabular-nums;
    cursor: pointer;
  }

  .duration option::checkmark {
    display: none;
  }

  .duration option:hover {
    background: var(--color-button-hover);
  }

  .duration option:checked {
    background: color-mix(in oklab, var(--color-accent) 18%, var(--color-button));
  }

  .duration option:focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: -2px;
  }
}

@media (max-width: 30rem) {
  .controls {
    gap: var(--space-xs);
  }

  /* Four controls in a pixel face don't fit one row on a phone: two by two, each a big target. */
  .controls:has(#tilt:not([hidden])) {
    display: grid;
    grid-template-columns: 1fr 1fr;
  }

  .controls:has(#tilt:not([hidden])) > *,
  .controls:has(#tilt:not([hidden])) select {
    width: 100%;
    justify-content: center;
  }

  button,
  select {
    padding-inline: var(--space-sm);
  }

  button.primary {
    padding-inline: var(--space-md);
  }

  .duration select {
    padding-inline: var(--space-sm) calc(var(--space-sm) + var(--space-md));
  }

  .duration::after {
    right: calc(var(--space-sm) + 4 * var(--px));
  }

  .duration-value {
    inset-inline-start: var(--space-sm);
  }
}

/* The select's value says what it is; the label stays for screen readers. */
.duration-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Wide screens: the glass stands in the middle at full height, the title top left, and the
   readout and controls beside the glass at the height of its neck. */
@media (orientation: landscape) and (min-width: 48rem) {
  .app {
    --side: minmax(14rem, 1fr);
    grid-template-columns: var(--side) min(0.7 * var(--app-height, 100dvh), 100vw - 30rem) var(--side);
    grid-template-rows: auto minmax(0, 1fr) auto auto minmax(0, 1fr);
    grid-template-areas:
      "head stage ."
      ". stage ."
      ". stage readout"
      ". stage controls"
      ". stage .";
    column-gap: var(--space-lg);
    row-gap: var(--space-md);
    padding-block: max(var(--space-lg), env(safe-area-inset-top)) max(var(--space-lg), env(safe-area-inset-bottom));
    padding-inline: max(var(--space-lg), env(safe-area-inset-left)) max(var(--space-lg), env(safe-area-inset-right));
  }

  .head {
    flex-direction: column;
    row-gap: var(--space-xs);
  }

  h1 {
    font-size: 1.5rem;
  }

  .readout {
    flex-direction: column;
    row-gap: var(--space-xs);
  }

  /* Flip on its own row as the main action, the timer settings under it. */
  .controls {
    display: grid;
    grid-template-columns: auto auto;
    justify-content: start;
  }

  .controls > * {
    width: 100%;
    justify-content: center;
  }

  #flip {
    grid-column: 1 / -1;
    min-height: 3.25rem;
    font-size: var(--text-xl);
  }
}

@media (prefers-reduced-motion: reduce) {
  button,
  select {
    transition: none;
  }
}
