/* rowntree.dev — one page, hand-written, no build step.
 *
 * Design brief: an art gallery, not a landing page. Each app is hung as a plate with a
 * placard beneath it. Whitespace and type do the work; there is no accent colour, no
 * shadow, no gradient and no motion beyond a link underline. The only decoration in the
 * entire stylesheet is a hairline rule.
 *
 * Type is the system stack on purpose. On the devices this page is most often opened on
 * it resolves to SF Pro, which is the reference the design is drawn against — and it
 * costs zero requests, zero layout shift and zero third-party contact, which is the same
 * claim the apps below make about themselves. A web font would undercut the argument.
 */

/* ============================== TOKENS ============================== */

:root {
  color-scheme: light dark;

  --paper:      #ffffff;
  --ink:        #111214;
  --muted:      #6b6f76;
  --rule:       #e6e6e8;
  --field:      #fafafa;
  --field-line: #d8d9dc;

  --font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto,
               "Helvetica Neue", Arial, system-ui, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  /* One measure for text, one wider bound for plates. Everything on the page is aligned
     to the same left edge, so the column is set once here and never overridden locally. */
  --measure: 34rem;
  --frame:   58rem;
  --gutter:  clamp(1.5rem, 6vw, 4rem);

  /* Vertical rhythm between hung works. Deliberately large — the empty space between two
     pieces is what tells you they are separate pieces. */
  --hang: clamp(5.5rem, 13vw, 10rem);
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper:      #0b0b0c;
    --ink:        #f2f2f4;
    --muted:      #9a9da4;
    --rule:       #232427;
    --field:      #141416;
    --field-line: #2e3034;
  }
}

/* ============================== RESET ============================== */

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

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 17px;
  line-height: 1.62;
  letter-spacing: -0.006em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

img { max-width: 100%; height: auto; display: block; }

h1, h2, h3 { font-weight: 600; letter-spacing: -0.028em; line-height: 1.06; margin: 0; }

p { margin: 0; }

code {
  font-family: var(--font-mono);
  font-size: 0.92em;
  letter-spacing: 0;
}

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
  border-radius: 2px;
}

.skip {
  position: absolute;
  left: -9999px;
}
.skip:focus {
  left: var(--gutter);
  top: 1rem;
  z-index: 10;
  background: var(--paper);
  padding: 0.6rem 0.9rem;
  border: 1px solid var(--ink);
  text-decoration: none;
  color: var(--ink);
}

/* ============================== LINKS ==============================
   Underline is the only affordance. It sits away from the baseline so descenders stay
   readable, and thickens rather than moves on hover — nothing on this page should jump. */

a {
  color: inherit;
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.22em;
  text-decoration-color: color-mix(in srgb, currentColor 32%, transparent);
  transition: text-decoration-color 120ms ease;
}
a:hover { text-decoration-color: currentColor; }

/* ============================== SHELL ============================== */

.topbar,
main,
footer {
  max-width: var(--frame);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.topbar {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  padding-block: clamp(1.5rem, 4vw, 2.5rem);
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.01em;
}
/* The mark is a link but carries no underline — it is a glyph, and an underline under a
   14px square reads as a rule rather than an affordance. Opacity does the hover work. */
.mark {
  display: flex;
  text-decoration: none;
  color: var(--ink);
  opacity: 0.85;
  transition: opacity 120ms ease;
  align-self: center;
}
.mark:hover { opacity: 1; }

/* Used by 404.html, which has no wordmark <h1> to collide with and so spells the name out. */
.wordmark { font-weight: 500; }

.topbar__link { color: var(--muted); }

/* ============================== HERO ============================== */

.hero {
  padding-block: clamp(3.5rem, 12vw, 8rem) clamp(4rem, 12vw, 8rem);
  max-width: var(--measure);
}

.hero h1 {
  font-size: clamp(2.75rem, 9vw, 4.5rem);
  letter-spacing: -0.038em;
  margin-bottom: clamp(1.5rem, 4vw, 2.25rem);
  text-wrap: balance;
}

.lead {
  font-size: clamp(1.0625rem, 2.4vw, 1.1875rem);
  line-height: 1.55;
  letter-spacing: -0.012em;
  text-wrap: pretty;
}
.lead--sub {
  margin-top: 1.15rem;
  color: var(--muted);
}
.lead--sub strong { color: var(--ink); font-weight: 500; }

/* ============================== SECTION LABELS ============================== */

.section-label {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
  padding-block: 1.1rem;
  border-top: 1px solid var(--rule);
  margin: 0;
}

/* ============================== WORK ============================== */

.work {
  padding-block: var(--hang);
  border-bottom: 1px solid var(--rule);
}

/* The plate. An object (the watch) is hung on bare paper with no frame, because it is a
   photograph of a thing; a screen is hung inside a hairline frame, because it is a window.
   Same treatment the difference deserves in a gallery. */
.plate {
  margin: 0 0 clamp(2.5rem, 6vw, 4rem);
}

/* Left-aligned, not centred. Every other element on the page hangs off the same left
   rail; a centred object breaks that rail and reads as a stray illustration rather than
   as the piece the placard beneath it describes. */
.plate--object img {
  width: min(100%, 20rem);
}

.plate--screen img {
  width: 100%;
  border: 1px solid var(--rule);
  border-radius: 6px;
}
.plate--narrow img {
  max-width: 26rem;
}

@media (prefers-color-scheme: dark) {
  /* Both screenshots are of light-mode sites. Rather than dim them (which misrepresents
     the product) or blaze them (which hurts at night), take the smallest step that keeps
     them honest: soften the whites slightly and restore full fidelity on hover. */
  .plate--screen img {
    filter: brightness(0.9);
    transition: filter 200ms ease;
  }
  .plate--screen img:hover { filter: none; }
}

.placard { max-width: var(--measure); }

.index {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.13em;
  color: var(--muted);
  margin-bottom: 0.9rem;
}

.placard h3 {
  font-size: clamp(1.75rem, 5vw, 2.375rem);
  letter-spacing: -0.03em;
}

.dek {
  margin-top: 0.7rem;
  font-size: clamp(1.0625rem, 2.4vw, 1.1875rem);
  letter-spacing: -0.012em;
  color: var(--muted);
  text-wrap: pretty;
}

.prose {
  margin-top: clamp(1.75rem, 4vw, 2.5rem);
  display: grid;
  gap: 1.15rem;
}
.prose p { text-wrap: pretty; }

/* Runs into the paragraph rather than sitting above it — a lead-in, not a subheading.
   Keeps the block a single visual object at the cost of nothing. */
.label {
  font-weight: 600;
  letter-spacing: -0.01em;
}

.facts {
  margin: clamp(1.75rem, 4vw, 2.5rem) 0 0;
  display: grid;
  gap: 0.55rem;
  font-size: 0.9375rem;
  line-height: 1.5;
}
.facts > div {
  display: grid;
  grid-template-columns: 5.5rem 1fr;
  gap: 1rem;
  align-items: baseline;
}
.facts dt {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--muted);
  padding-top: 0.25rem;
}
.facts dd { margin: 0; }

@media (max-width: 34rem) {
  .facts > div { grid-template-columns: 1fr; gap: 0.1rem; }
  .facts dt { padding-top: 0; }
}

.stats {
  margin-top: 1.5rem;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.005em;
  color: var(--muted);
}
.stats__asof { opacity: 0.65; }
.stats__star {
  font-family: var(--font-sans);
  font-size: 1.08em;
  letter-spacing: 0;
  padding-left: 0.05em;
}

.links {
  margin-top: clamp(1.75rem, 4vw, 2.25rem);
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 1.75rem;
  font-size: 0.9375rem;
}
/* The arrow is generated, so it can never be copied into a paste or read aloud by a
   screen reader as "north east arrow" in the middle of a sentence. */
.links a::after {
  content: " ↗";
  color: var(--muted);
  text-decoration: none;
  display: inline-block;
}

/* Garmin's store badge. It is artwork, not text, so it drops the underline every other
   link on the page carries — an underline under a bordered badge reads as a rendering
   fault. Sized by width alone; the intrinsic 278x95 on the <img> keeps the aspect ratio
   and reserves the box, so nothing reflows when the SVG lands. */
.badge {
  display: inline-block;
  margin-top: 1.25rem;
  text-decoration: none;
  transition: opacity 120ms ease;
  /* The badge is one click target, so it should read as one. cursor is inherited, so
     setting it here covers the image and the whitespace inside the anchor alike; without
     it the caret flips to an I-beam over parts of the box and the badge stops looking
     clickable. user-select stops a drag across it selecting the anchor's text nodes. */
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
}
.badge img {
  display: block;
  width: 180px;
  height: auto;
}
.badge:hover { opacity: 0.75; }

@media (prefers-reduced-motion: reduce) {
  .badge { transition: none; }
}

/* ============================== CONTACT ============================== */

.contact {
  padding-block: var(--hang);
  max-width: var(--measure);
}

.section-label--inline {
  border-top: 0;
  padding-top: 0;
  padding-bottom: 1.25rem;
}

.contact__intro {
  color: var(--muted);
  text-wrap: pretty;
  margin-bottom: clamp(2rem, 5vw, 2.75rem);
}

form { display: grid; gap: 1.25rem; }

.field { display: grid; gap: 0.45rem; }

.field label {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--muted);
}

input[type="text"],
input[type="email"],
textarea {
  font: inherit;
  font-size: 1rem;
  color: var(--ink);
  background: var(--field);
  border: 1px solid var(--field-line);
  border-radius: 4px;
  padding: 0.7rem 0.8rem;
  width: 100%;
  transition: border-color 120ms ease;
}
input:hover, textarea:hover { border-color: var(--muted); }
textarea { resize: vertical; min-height: 8rem; line-height: 1.55; }

/* Honeypot. Off-screen rather than display:none — some bots skip hidden inputs, but a
   positioned field is still in the layout and still gets filled. aria-hidden and
   tabindex="-1" in the markup keep it away from assistive tech and keyboard users. */
.nope {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.turnstile:not(:empty) { margin-top: 0.25rem; }

button[type="submit"] {
  justify-self: start;
  font: inherit;
  font-size: 0.9375rem;
  font-weight: 500;
  letter-spacing: -0.005em;
  color: var(--paper);
  background: var(--ink);
  border: 1px solid var(--ink);
  border-radius: 4px;
  padding: 0.65rem 1.6rem;
  cursor: pointer;
  transition: opacity 120ms ease;
}
button[type="submit"]:hover { opacity: 0.82; }
button[type="submit"][disabled] { opacity: 0.4; cursor: default; }

.form-status {
  font-size: 0.9375rem;
  color: var(--muted);
  min-height: 1.5rem;
  text-wrap: pretty;
}
.form-status[data-state="error"] { color: var(--ink); }
.form-status[data-state="sent"]  { color: var(--ink); }

/* ============================== FOOTER ============================== */

footer {
  padding-block: clamp(2.5rem, 6vw, 3.5rem) clamp(3.5rem, 8vw, 5rem);
  border-top: 1px solid var(--rule);
  display: grid;
  gap: 1rem;
}
footer nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 1.75rem;
  font-size: 0.9375rem;
}
.byline {
  font-size: 0.9375rem;
  color: var(--muted);
}
.byline strong { color: var(--ink); font-weight: 500; }
.colophon {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  line-height: 1.6;
  color: var(--muted);
  text-wrap: pretty;
  /* The footer spans the full frame so its rule does, but its text must stay on the same
     measure as everything above it — otherwise the last line on the page is the one line
     that breaks the left-rail column. */
  max-width: var(--measure);
}

/* ============================== MOTION ============================== */

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

/* ============================== PRINT ============================== */

@media print {
  .topbar__link, form, .skip { display: none; }
  body { font-size: 11pt; }
  .work { break-inside: avoid; }
  a::after { content: " (" attr(href) ")"; font-size: 9pt; color: #666; }
  .links a::after { content: " (" attr(href) ")"; }
}
