/* Daymetric — the app's own instrument-panel language on the web.
   Carbon ramp for everything structural, the burn gradient for the single
   accent. Values mirror Sources/Design/Palette.swift. */

@import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=IBM+Plex+Sans:wght@400;500;600&display=swap");

:root {
  --void: #060607;
  --carbon: #0d0d0f;
  --graphite: #16161a;
  --ash: #2a2a31;
  --smoke: #6c6c78;
  --bone: #f2f0ec;
  --ignition: #e23b2f;
  --afterburner: #f5813f;
  --burn: linear-gradient(135deg, var(--ignition), var(--afterburner));
  --burn-flat: linear-gradient(90deg, var(--ignition), var(--afterburner));

  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --sans: "IBM Plex Sans", ui-sans-serif, -apple-system, system-ui, sans-serif;

  --gutter: clamp(20px, 5vw, 40px);
  --radius: 14px;
  --maxw: 940px;
}

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

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

/* Safety net: no image may ever be wider than its column, whatever else
   does or does not load. A stale stylesheet should degrade to plain, not
   to a page bleeding off the right edge. */
img,
picture,
svg,
video {
  max-width: 100%;
  height: auto;
}

body {
  margin: 0;
  background: var(--void);
  color: var(--bone);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  overflow-x: hidden;
  /* A cold pool of light behind the panel, so the carbon has somewhere to sit. */
  background-image:
    radial-gradient(900px 520px at 50% -10%, rgba(226, 59, 47, 0.09), transparent 70%),
    radial-gradient(700px 400px at 90% 8%, rgba(245, 129, 63, 0.05), transparent 70%);
  background-repeat: no-repeat;
}

/* Machined grain. Pure CSS so there is no asset to ship. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 100;
  opacity: 0.035;
  background-image: repeating-linear-gradient(
      0deg,
      rgba(255, 255, 255, 0.6) 0 1px,
      transparent 1px 3px
    );
  mix-blend-mode: overlay;
}

.wrap {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

/* ---------------------------------------------------------------- chrome */

.rule {
  height: 1px;
  border: 0;
  margin: 0;
  background: linear-gradient(90deg, transparent, var(--ash) 12%, var(--ash) 88%, transparent);
}

.stencil {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--smoke);
}

.pip {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--burn);
  vertical-align: middle;
  margin-right: 8px;
  box-shadow: 0 0 10px rgba(226, 59, 47, 0.65);
}

/* ------------------------------------------------------------ dot matrix */

.matrix {
  display: grid;
  grid-template-columns: repeat(var(--cols), 1fr);
  gap: var(--dot-gap, 2px);
  width: fit-content;
  max-width: 100%;
}

.matrix .dot {
  aspect-ratio: 1;
  width: var(--dot, 7px);
  border-radius: 50%;
  background: var(--ash);
  opacity: 0.55;
}

.matrix .dot.on {
  background: var(--burn);
  opacity: 1;
  box-shadow: 0 0 8px rgba(226, 59, 47, 0.45);
}

.matrix.scanning .dot.on {
  animation: ignite 420ms cubic-bezier(0.2, 0.9, 0.25, 1) both;
}

@keyframes ignite {
  from {
    opacity: 0;
    transform: scale(0.4);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

.matrix--hero {
  --dot: clamp(5px, 1.55vw, 13px);
  --dot-gap: clamp(2px, 0.5vw, 4px);
}

.matrix--small {
  --dot: 3px;
  --dot-gap: 1px;
}

.matrix--small .dot.on {
  background: var(--bone);
  box-shadow: none;
}

/* ---------------------------------------------------------------- header */

header.bar {
  position: sticky;
  top: 0;
  z-index: 50;
  backdrop-filter: blur(14px);
  background: rgba(6, 6, 7, 0.82);
  border-bottom: 1px solid var(--ash);
}

.bar__inner {
  display: flex;
  align-items: center;
  gap: 16px;
  height: 58px;
}

.bar__mark {
  font-family: var(--mono);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-size: 13px;
  color: var(--bone);
  text-decoration: none;
}

.bar__mark span {
  background: var(--burn);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.bar__spacer {
  flex: 1;
}

.bar nav {
  display: flex;
  align-items: center;
  gap: 22px;
}

.bar nav a {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--smoke);
  text-decoration: none;
  transition: color 160ms ease;
}

.bar nav a:hover {
  color: var(--bone);
}

.bar__clock {
  display: none;
}

@media (min-width: 720px) {
  .bar__clock {
    display: grid;
  }
}

/* ------------------------------------------------------------------ hero */

.hero {
  padding: clamp(56px, 11vw, 104px) 0 clamp(40px, 7vw, 64px);
}

.hero__eyebrow {
  margin: 0 0 26px;
}

.hero__matrix {
  margin-bottom: 30px;
}

.hero__line {
  font-family: var(--sans);
  font-size: clamp(21px, 3.3vw, 30px);
  font-weight: 500;
  line-height: 1.32;
  letter-spacing: -0.015em;
  margin: 0 0 18px;
  max-width: 20ch;
}

.hero__sub {
  color: var(--smoke);
  max-width: 56ch;
  margin: 0 0 34px;
  font-size: 16.5px;
}

.hero__sub strong {
  color: var(--bone);
  font-weight: 500;
}

/* ----------------------------------------------------------------- CTA */

.cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
}

/* Buttons carry a real checkbox, at its real size.
 *
 * The first attempt spread a dot field across the whole button and the label
 * had to sit on top of it — that reads as halftone noise, not as cells you
 * can count. In the app the grid IS the control, so here it is the control
 * too: a 5x5 cell beside the label, unchecked as an outline of dots around an
 * empty slot, and on press current arrives ring by ring from the centre.
 *
 * Square shoulders at the app's own 6px card radius. The pill was the tell.
 */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 15px 24px;
  border-radius: 6px;
  border: 1px solid var(--ash);
  background: var(--carbon);
  color: var(--bone);
  transition:
    transform 140ms cubic-bezier(0.2, 0.9, 0.25, 1),
    border-color 180ms ease,
    background-color 180ms ease,
    box-shadow 180ms ease;
}

.btn:hover {
  border-color: var(--smoke);
  background: var(--graphite);
}

/* Motion.arm — the one curve in the app that overshoots, kept for the moment
   of commitment and nowhere else. */
.btn:active {
  transform: scale(0.97);
  transition-timing-function: cubic-bezier(0.2, 1.6, 0.4, 1);
}

.btn:focus-visible {
  outline: 2px solid var(--afterburner);
  outline-offset: 3px;
}

.btn__cell {
  display: grid;
  grid-template-columns: repeat(5, 2.6px);
  gap: 1.9px;
  flex: none;
}

.btn__cell i {
  width: 2.6px;
  height: 2.6px;
  border-radius: 50%;
  background: var(--smoke);
  /* Only the perimeter is drawn at rest; the middle is the empty slot. */
  opacity: var(--edge);
  transition:
    background-color 90ms linear,
    opacity 90ms linear,
    box-shadow 90ms linear;
}

.btn:hover .btn__cell i {
  opacity: calc(var(--edge) + 0.3);
}

/* Ring by ring. The delay is the dot's own distance from centre, which is
   exactly the test DotCheckbox runs against its growing front. */
.btn.is-firing .btn__cell i {
  opacity: 1;
  /* Inner ignition, outer afterburner: the burn gradient falling across the
     cell rather than being painted inside every dot. */
  background: color-mix(in oklab, var(--ignition), var(--afterburner) calc(var(--d) * 34%));
  box-shadow: 0 0 4px rgba(226, 59, 47, 0.9);
  transition-delay: calc(var(--d) * 58ms);
}

/* Primary carries current already: warm border, a little bloom, and a cell
   whose outline is lit, so it still reads as the thing to press. */
.btn--burn {
  border-color: rgba(226, 59, 47, 0.6);
  box-shadow: 0 6px 26px -14px rgba(226, 59, 47, 0.9);
}

.btn--burn .btn__cell i {
  background: var(--ignition);
}

.btn--burn:hover {
  border-color: var(--ignition);
  background: rgba(226, 59, 47, 0.07);
  box-shadow: 0 10px 30px -14px rgba(226, 59, 47, 1);
}

/* Secondary is the empty slot: a dashed edge, no current in it yet. */
.btn--ghost {
  border-style: dashed;
}


.cta__meta {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--smoke);
  letter-spacing: 0.06em;
  flex-basis: 100%;
  margin-top: 2px;
}

/* ----------------------------------------------------------- screenshot */

.shot {
  margin: 0 0 clamp(30px, 6vw, 56px);
  /* The window is dark on a dark page, so it needs an edge and a shadow to
     read as an object rather than a hole. */
  border: 1px solid var(--ash);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--carbon);
  box-shadow:
    0 30px 70px -30px rgba(0, 0, 0, 0.95),
    0 0 0 1px rgba(226, 59, 47, 0.06);
  position: relative;
  /* Scroll-driven below. `will-change` keeps the scale off the main thread;
     the shot is a large image and jank here is very visible. */
  will-change: transform;
  transform-origin: 50% 55%;
  /* Fallback path: motion.js drives --shot-scale from the same geometry.
     Defaults to 1, so with neither CSS support nor JS the shot sits at
     column width — the state everything else on the page is aligned to. */
  transform: scale(var(--shot-scale, 1));
}

/* The shot starts at the width of the text column and grows past it as you
   come down from the hero, reaching full size the moment it is centred — the
   arrival is the point, so nothing happens after. `cover 50%` is exactly
   "element centre meets viewport centre", which is why this is one
   declaration rather than a scroll handler.

   Growing beyond the column is the effect, so the page must tolerate an
   element wider than its gutter; body already hides overflow-x. On narrow
   screens there is no room to grow into, so the travel shrinks to almost
   nothing rather than shoving the shot under the bezel. */
/* How far it may grow, by how much room there is to grow into. The column is
   940px wide minus gutters, so at 1200px+ a 1.15 scale still clears the
   viewport by a wide margin; narrow screens get a hint of the same move
   rather than a clipped edge. */
.shot {
  --shot-max: 1.04;
}

@media (min-width: 1025px) {
  .shot {
    --shot-max: 1.1;
  }
}

@media (min-width: 1200px) {
  .shot {
    --shot-max: 1.15;
  }
}

@keyframes deckRise {
  from {
    transform: scale(1);
  }
  to {
    transform: scale(var(--shot-max));
  }
}

@supports (animation-timeline: view()) {
  .shot {
    transform: none;
    animation: deckRise linear both;
    animation-timeline: view();
    animation-range: entry 0% cover 50%;
  }
}

.shot img {
  display: block;
  width: 100%;
  height: auto;
}

.shot figcaption {
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: 0.06em;
  color: var(--smoke);
  padding: 13px 16px;
  border-top: 1px solid var(--ash);
  background: var(--void);
}


/* --------------------------------------------------------------- panels */

section {
  padding: clamp(46px, 8vw, 76px) 0;
}

.section__head {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin-bottom: 30px;
}

.section__head h2 {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--bone);
  margin: 0;
}

.section__head .count {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--smoke);
}

.grid {
  display: grid;
  gap: 14px;
  grid-template-columns: 1fr;
}

@media (min-width: 700px) {
  .grid--2 {
    grid-template-columns: repeat(2, 1fr);
  }
  .grid--3 {
    grid-template-columns: repeat(3, 1fr);
  }
}

.panel {
  background: var(--carbon);
  border: 1px solid var(--ash);
  border-radius: var(--radius);
  padding: 22px;
  position: relative;
  overflow: hidden;
}

.panel__label {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--smoke);
  margin: 0 0 12px;
}

.panel h3 {
  font-family: var(--sans);
  font-size: 17px;
  font-weight: 600;
  margin: 0 0 9px;
  letter-spacing: -0.01em;
}

.panel p {
  margin: 0;
  color: var(--smoke);
  font-size: 14.5px;
  line-height: 1.62;
}

.panel p code,
.prose code {
  font-family: var(--mono);
  font-size: 0.9em;
  color: var(--bone);
  background: var(--graphite);
  border: 1px solid var(--ash);
  border-radius: 5px;
  padding: 1px 5px;
}

/* A lit top edge, the way the app lights an active bezel. */
.panel--lit::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  background: var(--burn-flat);
}

/* ----------------------------------------------------------- lede + spec */

.lede {
  font-family: var(--sans);
  font-size: clamp(20px, 2.7vw, 26px);
  font-weight: 600;
  letter-spacing: -0.018em;
  line-height: 1.28;
  margin: 0 0 12px;
  max-width: 22ch;
}

.lede__body {
  color: var(--smoke);
  font-size: 16px;
  max-width: 54ch;
  margin: 0 0 26px;
}

.aside {
  color: var(--smoke);
  font-size: 14.5px;
  max-width: 62ch;
  margin: 22px 0 0;
}

/* The task line, shown rather than described. It scrolls on its own so a
   narrow screen never widens the page. */
.spec {
  border: 1px solid var(--ash);
  border-radius: var(--radius);
  background: var(--carbon);
  overflow: hidden;
}

.spec__line {
  margin: 0;
  padding: 20px;
  overflow-x: auto;
  border-bottom: 1px solid var(--ash);
  background: var(--void);
}

.spec__line code {
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.7;
  white-space: pre;
  color: var(--smoke);
}

/* Semantic tags carrying colour, so the legend below can point at them
   without a wrapper div per token. */
.spec__line b {
  color: var(--bone);
  font-weight: 500;
}
.spec__line i {
  color: #8ea9c9;
  font-style: normal;
}
.spec__line u {
  color: var(--afterburner);
  text-decoration: none;
}
.spec__line s {
  color: #d4a03c;
  text-decoration: none;
}
.spec__line em {
  color: var(--ignition);
  font-style: normal;
}

.spec__keys {
  margin: 0;
  padding: 6px 20px 18px;
  display: grid;
  gap: 0;
}

.spec__keys > div {
  display: grid;
  grid-template-columns: 96px 1fr;
  gap: 16px;
  padding: 12px 0;
  border-bottom: 1px solid var(--ash);
  align-items: baseline;
}

.spec__keys > div:last-child {
  border-bottom: 0;
}

.spec__keys dt {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.04em;
}

.spec__keys dd {
  margin: 0;
  color: var(--smoke);
  font-size: 14.5px;
  line-height: 1.55;
}

.k-effort {
  color: var(--afterburner);
}
.k-deadline {
  color: #d4a03c;
}
.k-priority {
  color: var(--ignition);
}
.k-project {
  color: #8ea9c9;
}

@media (max-width: 560px) {
  .spec__keys > div {
    grid-template-columns: 1fr;
    gap: 4px;
  }
}

/* --------------------------------------------------- phrase + tool list */

.phrase {
  font-family: var(--mono);
  font-size: clamp(16px, 2.4vw, 21px);
  color: var(--bone);
  margin: 0 0 26px;
  padding: 18px 22px;
  border-left: 2px solid transparent;
  border-image: var(--burn-flat) 1;
  background: var(--carbon);
}

.phrase__q {
  color: var(--ignition);
}

.tools {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
}

.tools code {
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: 0.02em;
  color: var(--smoke);
  background: var(--graphite);
  border: 1px solid var(--ash);
  border-radius: 3px;
  padding: 6px 9px;
  transition: color 160ms ease, border-color 160ms ease;
}

.tools code:hover {
  color: var(--bone);
  border-color: var(--smoke);
}

/* ---------------------------------------------------------------- prose */

.prose {
  max-width: 68ch;
}

.prose h1 {
  font-family: var(--mono);
  font-size: clamp(22px, 4vw, 30px);
  font-weight: 600;
  letter-spacing: 0.02em;
  margin: 0 0 10px;
}

.prose .updated {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--smoke);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin: 0 0 34px;
}

.prose h2 {
  font-family: var(--mono);
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--bone);
  margin: 38px 0 12px;
  padding-top: 20px;
  border-top: 1px solid var(--ash);
}

.prose p,
.prose li {
  color: #c9c6c0;
  font-size: 15.5px;
}

.prose ul {
  padding-left: 20px;
}

.prose li {
  margin-bottom: 8px;
}

.prose a {
  color: var(--afterburner);
}

.prose strong {
  color: var(--bone);
}

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

footer {
  border-top: 1px solid var(--ash);
  padding: 34px 0 44px;
  margin-top: 40px;
}

.footer__inner {
  display: flex;
  flex-wrap: wrap;
  gap: 16px 26px;
  align-items: center;
}

footer a {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--smoke);
  text-decoration: none;
}

footer a:hover {
  color: var(--bone);
}

.footer__copy {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--ash);
  flex-basis: 100%;
  letter-spacing: 0.05em;
}

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

  /* A scroll-driven animation has no duration to zero out, so the blanket
     rule above does not reach it. Kill the timeline and pin it full size.
     motion.js checks the same query and never writes --shot-scale. */
  .shot {
    animation: none !important;
    transform: none !important;
  }

  /* Keep the press feedback, drop the travel: the cell lights all at once
     instead of ring by ring. */
  .btn.is-firing .btn__cell i {
    transition-delay: 0ms !important;
  }
}
