/* Not My First Codeo — site styles.
   Values come from the Not My First Codeo design system (tokens.json, dark theme). */

/* Geist, Latin characters, one variable file for both weights (Google Fonts v5).
   Licence in fonts/OFL.txt. */
@font-face {
  font-family: "Geist";
  src: url("fonts/geist-v5-latin.woff2") format("woff2");
  font-weight: 400 500;
  font-style: normal;
  font-display: swap;
}

/* Geist Mono, digits only, for the numbers in the series list (Google Fonts v6).
   Browsers only download it once a second app is listed. */
@font-face {
  font-family: "Geist Mono";
  src: url("fonts/geist-mono-v6-digits.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0030-0039;
}

:root {
  color-scheme: dark;

  /* colour */
  --bg: #0a0a0b;
  --bg-raised: #111113;
  --line: #222226;
  --ink: #f2f2f3;
  --ink-2: #a1a1a8;
  --ink-3: #8e8e96;

  /* type */
  --font-sans: "Geist", -apple-system, "Helvetica Neue", sans-serif;
  --font-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* space */
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-16: 64px;

  /* page frame */
  --gutter-x: clamp(20px, 6vw, 88px);
  --gutter-y: clamp(24px, 4vw, 48px);
  --page-max: 1600px;

  --ease: cubic-bezier(0.2, 0, 0, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

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

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 24px;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a {
  color: inherit;
  text-decoration: none;
  /* no grey box flashing over links on tap; :active gives the feedback */
  -webkit-tap-highlight-color: transparent;
}

::selection {
  background: var(--ink);
  color: var(--bg);
}

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

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

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

.page {
  width: 100%;
  max-width: var(--page-max);
  margin: 0 auto;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--space-16);
  padding:
    max(var(--gutter-y), env(safe-area-inset-top))
    max(var(--gutter-x), env(safe-area-inset-right))
    max(var(--gutter-y), env(safe-area-inset-bottom))
    max(var(--gutter-x), env(safe-area-inset-left));
}

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

.site-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-4);
  font-size: 14px;
  line-height: 20px;
}

.lockup {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-weight: 500;
  letter-spacing: -0.01em;
  white-space: nowrap;
}

.lockup-mark {
  width: 20px;
  height: 20px;
  flex: none;
}

.nav-link,
.footer-link {
  position: relative;
  color: var(--ink-3);
  padding: var(--space-3) 0;
  transition: color 150ms var(--ease);
}

/* underline draws in from the left on hover and leaves to the right */
.nav-link::after,
.footer-link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 8px;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 400ms var(--ease-out);
}

.nav-link:active,
.footer-link:active {
  color: var(--ink);
}

/* Hover effects only where there's a real pointer, so they don't stick
   after a tap on a phone. */
@media (hover: hover) {
  .nav-link:hover,
  .footer-link:hover {
    color: var(--ink);
  }

  .nav-link:hover::after,
  .footer-link:hover::after {
    transform: scaleX(1);
    transform-origin: left;
  }
}

/* ---------------------------------------------------------------- apps */

.apps,
.legal {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.eyebrow {
  margin: 0;
  font-size: 13px;
  line-height: 16px;
  font-weight: 400;
  letter-spacing: 0.02em;
  color: var(--ink-3);
}

.app-list {
  position: relative;
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid transparent;
}

.app-list > li {
  position: relative;
  border-bottom: 1px solid transparent;
}

/* The rules are drawn by pseudo-elements laid over the (transparent)
   borders, so they can animate with transform alone. */
.app-list::before,
.app-list > li::before,
.legal-list::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--line);
  transform-origin: left;
}

.app-list::before,
.legal-list::before {
  top: -1px;
}

.app-list > li::before {
  bottom: -1px;
}

.app-row {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-4) var(--space-6);
  padding: 28px 0;
}

/* lights the row's rule on hover, sweeping left to right */
.app-row::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 1px;
  background: var(--ink);
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 600ms var(--ease-out);
}

.app-name {
  font-size: clamp(64px, 10vw, 136px);
  font-weight: 500;
  letter-spacing: -0.05em;
  line-height: 0.88;
  /* optical nudge: big Geist sits a hair right of the column edge */
  margin-left: -0.03em;
}

.app-meta {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  color: var(--ink-3);
  transition: color 150ms var(--ease);
}

.app-arrow {
  width: 32px;
  height: 32px;
  flex: none;
  color: var(--ink);
}

.app-row:is(:focus-visible, :active) .app-meta {
  color: var(--ink);
}

.app-row:is(:focus-visible, :active)::after {
  transform: scaleX(1);
  transform-origin: left;
}

@media (hover: hover) {
  .app-row:hover .app-meta {
    color: var(--ink);
  }

  .app-row:hover::after {
    transform: scaleX(1);
    transform-origin: left;
  }
}

/* The series: once the list holds a second app it numbers itself (01, 02, …)
   and the names get smaller. From the design system's AppRow. */
.app-list:has(> li:nth-child(2)) {
  counter-reset: app;
}

.app-list:has(> li:nth-child(2)) > li {
  counter-increment: app;
}

.app-list:has(> li:nth-child(2)) .app-row {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr) auto;
  gap: var(--space-3);
  padding: 22px 0;
}

.app-list:has(> li:nth-child(2)) .app-row::before {
  content: counter(app, decimal-leading-zero);
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 16px;
  color: var(--ink-3);
}

.app-list:has(> li:nth-child(2)) .app-name {
  font-size: clamp(48px, 7vw, 96px);
  line-height: 0.9;
}

/* ---------------------------------------------------------------- legal page */

.legal-list {
  position: relative;
  margin: 0;
  border-top: 1px solid transparent;
}

.legal-list > div {
  display: flex;
  flex-direction: column;
  padding: var(--space-5) 0;
  border-bottom: 1px solid var(--line);
}

.legal-list dt {
  color: var(--ink-3);
}

.legal-list dd {
  margin: 0;
  text-wrap: pretty;
}

.legal-list a {
  text-decoration: underline;
  text-decoration-color: var(--ink-3);
  text-underline-offset: 4px;
  transition: text-decoration-color 150ms var(--ease);
}

@media (hover: hover) {
  .legal-list a:hover {
    text-decoration-color: var(--ink);
  }
}

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

.site-footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-2) var(--space-6);
  font-size: 12px;
  line-height: 18px;
  color: var(--ink-3);
}

.site-footer p {
  margin: 0;
}

/* the padding gives a finger-sized target without making the footer taller */
.footer-link {
  margin-block: calc(-1 * var(--space-3));
}

/* ---------------------------------------------------------------- motion */

/* The page comes in from the top, piece by piece: each piece fades in as it
   settles down into place, the rules draw across in turn, and the app name
   drops out of the rule above it. Only transform, translate, opacity and
   clip-path animate, so nothing reflows and the layout never shifts. */

@media (prefers-reduced-motion: no-preference) {
  .lockup,
  .nav-link,
  .eyebrow,
  .app-meta,
  .app-list:has(> li:nth-child(2)) .app-row::before,
  .legal-list > div,
  .site-footer > * {
    animation:
      fade-in 900ms var(--ease) var(--delay) backwards,
      drop-in 1200ms var(--ease-out) var(--delay) backwards;
  }

  .lockup {
    --delay: 0ms;
  }

  .nav-link {
    --delay: 70ms;
  }

  .eyebrow {
    --delay: 180ms;
  }

  .app-list::before,
  .legal-list::before {
    animation: draw 1200ms var(--ease-out) 220ms backwards;
  }

  /* each row comes in a beat after the one above */
  :is(.app-list, .legal-list) > :nth-child(2) {
    --row: 1;
  }

  :is(.app-list, .legal-list) > :nth-child(3) {
    --row: 2;
  }

  :is(.app-list, .legal-list) > :nth-child(4) {
    --row: 3;
  }

  :is(.app-list, .legal-list) > :nth-child(n + 5) {
    --row: 4;
  }

  .app-name {
    animation: drop-out-of-rule 1200ms var(--ease-out) calc(260ms + var(--row, 0) * 80ms) backwards;
  }

  .app-list:has(> li:nth-child(2)) .app-row::before {
    --delay: calc(300ms + var(--row, 0) * 80ms);
  }

  .app-meta {
    --delay: calc(340ms + var(--row, 0) * 80ms);
  }

  .app-list > li::before {
    animation: draw 1200ms var(--ease-out) calc(360ms + var(--row, 0) * 80ms) backwards;
  }

  .legal-list > div {
    --delay: calc(260ms + var(--row, 0) * 60ms);
  }

  .site-footer > :first-child {
    --delay: 440ms;
  }

  .site-footer > :last-child {
    --delay: 500ms;
  }

  .app-row:focus-visible .app-arrow {
    animation: arrow-loop 600ms;
  }

  @media (hover: hover) {
    .app-row:hover .app-arrow {
      animation: arrow-loop 600ms;
    }
  }
}

@keyframes fade-in {
  from {
    opacity: 0;
  }
}

@keyframes drop-in {
  from {
    translate: 0 -16px;
  }
}

@keyframes draw {
  from {
    transform: scaleX(0);
  }
}

/* The name slides down out of a slot at the rule above it. The clip's top
   edge moves down exactly as fast as the text does, so the slot stays put. */
@keyframes drop-out-of-rule {
  from {
    translate: 0 -1.3em;
    clip-path: inset(1.1em -0.1em -0.3em -0.1em);
  }
  to {
    translate: 0 0;
    clip-path: inset(-0.2em -0.1em -0.3em -0.1em);
  }
}

/* The arrow flies out to the top right and comes back in from the bottom left. */
@keyframes arrow-loop {
  0% {
    translate: 0 0;
    opacity: 1;
    animation-timing-function: cubic-bezier(0.5, 0, 1, 1);
  }
  40% {
    translate: 60% -60%;
    opacity: 0;
  }
  41% {
    translate: -60% 60%;
    opacity: 0;
    animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
  }
  100% {
    translate: 0 0;
    opacity: 1;
  }
}

/* ---------------------------------------------------------------- wider screens */

/* Everything above is the phone layout; this adds the tablet and desktop
   differences on top. */
@media (width > 640px) {
  .app-meta {
    gap: var(--space-5);
  }

  .app-arrow {
    width: 40px;
    height: 40px;
  }

  .app-list:has(> li:nth-child(2)) .app-row {
    grid-template-columns: 48px minmax(0, 1fr) auto;
    gap: var(--space-5);
  }

  .app-list:has(> li:nth-child(2)) .app-arrow {
    width: 32px;
    height: 32px;
  }

  /* label on the left and the detail on the right, like the app rows */
  .legal-list > div {
    flex-direction: row;
    justify-content: space-between;
    gap: var(--space-6);
  }

  .legal-list dd {
    text-align: right;
  }
}

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