/* danielhan.dev — the hub for Daniel's published apps.
   Family tokens (danielhan-website-design §1): dark room, silver light, Barlow only. */

@font-face { font-family: "Barlow"; font-style: normal; font-weight: 400; font-display: swap; src: url("/assets/fonts/barlow-latin-400-normal.woff2") format("woff2"); }
@font-face { font-family: "Barlow"; font-style: normal; font-weight: 500; font-display: swap; src: url("/assets/fonts/barlow-latin-500-normal.woff2") format("woff2"); }
@font-face { font-family: "Barlow"; font-style: normal; font-weight: 600; font-display: swap; src: url("/assets/fonts/barlow-latin-600-normal.woff2") format("woff2"); }

:root {
  --bg: #0c0c0e;
  --surface: #161618;
  --surface-raised: #1e1e22;
  --border: #2a2a30;
  --border-light: #38383f;
  --text: #e2e2e6;
  --text-muted: #9a9aa8;
  --text-dim: #8a8a97; /* raised from the family #636370 for small text, same as Schedule Keeper (4.5:1 floor) */
  --accent: #a8b0be;
  --accent-bright: #c8d0de;

  --font: "Barlow", system-ui, -apple-system, "Segoe UI", Roboto, "PingFang SC", "Microsoft YaHei", sans-serif;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --gutter: 24px;
  --max: 1200px;

  /* Page ground, chrome and shadows: these switch with the system theme. */
  --ground-lift: #17181c;
  --ground-deep: #060607;
  --bar: rgba(12, 12, 14, 0.86);
  --text-strong: #fff;
  --selection: rgba(168, 176, 190, 0.3);
  --cover-shadow: 0 1px 2px rgba(0, 0, 0, 0.5), 0 18px 40px -12px rgba(0, 0, 0, 0.7);

  /* The cover band: the same graphite in both themes, so a cover is one object day and night. */
  --cover-band: #1e1e22;
  --cover-ink: #e2e2e6;
  --display: clamp(2.1rem, 3.3vw, 2.85rem); /* the one display size: headline and app names */

  color-scheme: dark;
}

/* Light theme: a flat light grey ground (the macOS light dialog grey), dark ink. Follows the system setting; no toggle. */
@media (prefers-color-scheme: light) {
  :root {
    --bg: #f7f7f7;
    --surface: #efefef;
    --surface-raised: #ffffff;
    --border: #e2e2e2;
    --border-light: #c9c9cc;
    --text: #1e1e22;
    --text-muted: #474a54;
    --text-dim: #50545e;
    --accent: #6a717e;
    --accent-bright: #2c313a;

    --ground-lift: #f7f7f7;
    --ground-deep: #f7f7f7;
    --bar: rgba(247, 247, 247, 0.86);
    --text-strong: #000;
    --selection: rgba(106, 113, 126, 0.28);
    --cover-shadow: 0 1px 2px rgba(20, 24, 32, 0.2), 0 18px 36px -14px rgba(20, 24, 32, 0.38);

    color-scheme: light;
  }
}

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

html { -webkit-text-size-adjust: 100%; scrollbar-color: var(--border-light) var(--bg); scrollbar-width: thin; }

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  font-family: var(--font);
  font-size: 17px;
  line-height: 1.55;
  color: var(--text);
  background: var(--bg) radial-gradient(120% 90% at 50% 0%, var(--ground-lift) 0%, var(--bg) 55%, var(--ground-deep) 100%) fixed;
  caret-color: var(--accent-bright);
}

::selection { background: var(--selection); color: var(--text-strong); }

a { color: inherit; }
a:focus-visible { outline: 2px solid var(--accent-bright); outline-offset: 2px; border-radius: 4px; }

svg { stroke: currentColor; fill: none; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }

.skip {
  position: absolute; left: 12px; top: -48px; z-index: 20;
  padding: 8px 14px; border-radius: 10px;
  background: var(--surface-raised); box-shadow: inset 0 0 0 1px var(--border);
  text-decoration: none; font-weight: 500;
}
.skip:focus { top: 12px; }

/* ── Top bar: family glass chrome ─────────────────────────── */
.topbar {
  position: sticky; top: 0; z-index: 10;
  background: var(--bar);
  -webkit-backdrop-filter: blur(20px);
  backdrop-filter: blur(20px);
  border-bottom: 1px solid var(--border);
}
.topbar-inner {
  max-width: var(--max); margin: 0 auto; padding: 0 var(--gutter);
  height: 56px; display: flex; align-items: center; justify-content: space-between;
}
.wordmark { font-weight: 500; font-size: 1.02rem; letter-spacing: 0.01em; text-decoration: none; color: var(--text); }
.topbar nav { display: flex; gap: 28px; }
.topbar nav a {
  position: relative; text-decoration: none; font-size: 0.95rem;
  color: var(--text-muted); transition: color 0.18s;
}
.topbar nav a:hover { color: var(--text); }
.topbar nav a[aria-current="page"] { color: var(--text); }
.topbar nav a[aria-current="page"]::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -18px; height: 1px; background: var(--accent);
}

/* ── Intro ─────────────────────────────────────────────────── */
main { flex: 1; }

.intro {
  max-width: var(--max); margin: 0 auto;
  padding: clamp(40px, 5vw, 60px) var(--gutter) clamp(32px, 3.5vw, 44px);
  text-align: center;
}
.intro h1 {
  margin: 0 auto;
  max-width: 20ch;
  font-size: var(--display);
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: -0.015em;
  text-wrap: balance;
}
.intro p {
  margin: 14px auto 0; max-width: 72ch;
  color: var(--text-muted); font-size: 1.08rem; text-wrap: balance;
}
.more {
  color: var(--text); text-decoration: underline; text-decoration-color: var(--border-light);
  text-underline-offset: 4px; white-space: nowrap; transition: text-decoration-color 0.18s;
}
.more:hover { text-decoration-color: var(--accent); }
.more svg, .visit svg { width: 1em; height: 1em; vertical-align: -0.14em; }

/* ── The shelf of covers ───────────────────────────────────── */
.shelf {
  max-width: var(--max); margin: 0 auto;
  padding: 0 var(--gutter) clamp(64px, 8vw, 104px);
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: clamp(40px, 4vw, 48px) clamp(24px, 3vw, 40px);
}

.app { display: flex; flex-direction: column; min-width: 0; }

.cover {
  display: block; text-decoration: none; color: inherit;
  border-radius: 8px; overflow: hidden;
  background: var(--cover-band);
  box-shadow: inset 0 1px 0 rgba(200, 208, 222, 0.08), var(--cover-shadow);
  position: relative;
  transition: transform 0.5s var(--ease-out), box-shadow 0.5s var(--ease-out);
}
/* Hover and keyboard focus: the cover comes slightly towards the viewer and glows in
   its own app's colour, as if the art's light spills out around it. Scale and glow
   share one duration and curve, so they move as one. --glow is set per app below. */
.cover:hover,
.cover:focus-visible {
  z-index: 1;
  transform: scale(1.025);
  box-shadow:
    inset 0 1px 0 rgba(200, 208, 222, 0.12),
    0 0 0 1px rgb(var(--glow) / 0.4),
    0 0 26px 2px rgb(var(--glow) / 0.42),
    0 18px 50px -6px rgb(var(--glow) / 0.32),
    var(--cover-shadow);
}
.app--mathquest   { --glow: 108 192 255; } /* the meadow's sky */
.app--overtext    { --glow: 232 66 92; }   /* the icon's coral-to-crimson, middle */
.app--multiplings { --glow: 255 150 50; }  /* warm orange, between the sunset coral and the sun, so it reads apart from Overtext */
.cover:focus-visible { outline-offset: 4px; border-radius: 8px; }

.band {
  display: block;
  padding: 20px 20px 18px;
  background: var(--cover-band);
  border-bottom: 1px solid #000;
}
.name {
  margin: 0; font-size: var(--display); font-weight: 600;
  line-height: 1; letter-spacing: -0.015em; color: var(--cover-ink);
}

.art {
  position: relative; display: block; overflow: hidden;
  aspect-ratio: 10 / 7;
}
:where(.art img) { position: absolute; display: block; max-width: none; height: auto; }

/* Descriptions under each cover */
.about { padding-top: 22px; }
.about > * { padding-left: 2px; padding-right: 2px; }

.tagline { margin: 0; font-size: 1.22rem; font-weight: 600; line-height: 1.25; color: var(--text); }
.pitch { margin: 8px 0 0; color: var(--text-muted); max-width: 46ch; }
.platforms {
  margin: 14px 0 0; font-size: 0.74rem; font-weight: 500;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-dim);
}
.visit {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 14px;
  font-weight: 500; text-decoration: none; color: var(--accent-bright);
  transition: color 0.18s, gap 0.3s var(--ease-out);
}
.visit:hover { color: var(--text-strong); gap: 10px; }

/* Line the description rows up across a row of covers: tagline, pitch, platforms and
   the Visit link sit at the same height in every column, whatever each pitch's length.
   Browsers without subgrid keep the plain stacked layout above. */
@supports (grid-template-rows: subgrid) {
  .shelf { row-gap: 0; }
  .app {
    display: grid; grid-row: span 5; grid-template-rows: subgrid; row-gap: 0;
    margin-bottom: clamp(40px, 4vw, 48px);
  }
  .about { display: contents; }
  .tagline { margin-top: 22px; }
  .visit { justify-self: start; align-self: start; }
}

/* ── MathQuest cover: the meadow, the gate, the jump ───────── */
.app--mathquest .art { background: #b8e0ff; }
.mq-sky { inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 100%; }
.mq-cloud { opacity: 0.95; }
.mq-cloud-1 { width: 36%; left: -4%; top: 10%; }
.mq-cloud-2 { width: 26%; right: 4%; top: 4%; }
.mq-gate { height: 50%; width: auto; right: 9%; bottom: 6%; }
.mq-bubble {
  position: absolute; right: 2%; top: 16%;
  padding: 0.35em 0.7em; border-radius: 10px;
  background: #fff; box-shadow: inset 0 0 0 2px #2a8fc4, 0 3px 8px rgba(0, 0, 0, 0.16);
  color: #155b84; font-weight: 600; font-size: clamp(0.9rem, 1.7vw, 1.15rem);
  font-variant-numeric: tabular-nums; white-space: nowrap;
  display: grid;
}
.mq-bubble > span { grid-area: 1 / 1; }
.mq-bubble .a { opacity: 0; }
.mq-block { width: 15%; height: auto; bottom: 5%; }
.mq-block-12 { left: 26%; }
.mq-block-13 { left: 43%; }
.mq-block-14 { left: 60%; }
.mq-star { width: 9%; height: auto; left: 46%; bottom: 26%; opacity: 0; transform: scale(0.2); }
.mq-cat { position: absolute; width: 17%; aspect-ratio: 1; left: 5%; bottom: 4%; }
.mq-cat img { inset: 0; width: 100%; height: 100%; }
.mq-cat-jump { opacity: 0; }

.app--mathquest.play .mq-cat { animation: mq-jump 3.4s both; }
.app--mathquest.play .mq-cat-jump { animation: mq-sprite 3.4s both; }
.app--mathquest.play .mq-cat-idle { animation: mq-sprite-idle 3.4s both; }
.app--mathquest.play .mq-star { animation: mq-star 3.4s both; }
.app--mathquest.play .mq-bubble .q { animation: swap-out 3.4s both; }
.app--mathquest.play .mq-bubble .a { animation: swap-in 3.4s both; }

/* Jump arc onto the 13, celebrate, then the level resets to the question. */
@keyframes mq-jump {
  0%   { left: 5%;  bottom: 4%;  opacity: 1; animation-timing-function: cubic-bezier(0.3, 0.6, 0.5, 1); }
  14%  { left: 24%; bottom: 44%; animation-timing-function: cubic-bezier(0.5, 0, 0.8, 0.5); }
  27%  { left: 42%; bottom: 20%; opacity: 1; }
  78%  { left: 42%; bottom: 20%; opacity: 1; }
  86%  { left: 42%; bottom: 20%; opacity: 0; }
  87%  { left: 5%;  bottom: 4%;  opacity: 0; }
  100% { left: 5%;  bottom: 4%;  opacity: 1; }
}
@keyframes mq-sprite      { 0%, 1% { opacity: 0; } 2%, 26% { opacity: 1; } 27%, 100% { opacity: 0; } }
@keyframes mq-sprite-idle { 0%, 1% { opacity: 1; } 2%, 26% { opacity: 0; } 27%, 100% { opacity: 1; } }
@keyframes mq-star {
  0%, 27% { opacity: 0; transform: translateY(0) scale(0.2); }
  35%     { opacity: 1; transform: translateY(-60%) scale(1.1); animation-timing-function: var(--ease-out); }
  70%     { opacity: 1; transform: translateY(-80%) scale(1); }
  80%, 100% { opacity: 0; transform: translateY(-90%) scale(0.9); }
}
@keyframes swap-out { 0%, 27% { opacity: 1; } 31%, 84% { opacity: 0; } 90%, 100% { opacity: 1; } }
@keyframes swap-in  { 0%, 27% { opacity: 0; } 31%, 84% { opacity: 1; } 90%, 100% { opacity: 0; } }

/* ── Overtext cover: the translation laid over the original ── */
.app--overtext .art { background: linear-gradient(150deg, #ff6a3a 0%, #e0405a 52%, #c2185b 100%); }
.ot-phone {
  position: absolute; left: 57%; top: 13%;
  width: 44%; aspect-ratio: 786 / 1608;
  transform: translateX(-50%) rotate(4deg);
  border-radius: 22px; padding: 2.4%;
  background: #141416;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08), 0 24px 40px -10px rgba(60, 0, 20, 0.55);
}
.ot-phone img {
  position: absolute; left: 3%; top: 1.5%; width: 94%; height: auto;
  border-radius: 16px;
}
.ot-phone--back {
  left: 36%; top: 8%;
  transform: translateX(-50%) rotate(-8deg);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06), 0 16px 30px -10px rgba(60, 0, 20, 0.5);
}
.ot-phone--back img { filter: saturate(0.8) brightness(0.9); }
.ot-translated { clip-path: inset(0 0 0 0); }
.app--overtext.play .ot-translated { animation: ot-lay 3.2s both; }
@keyframes ot-lay {
  0%   { clip-path: inset(0 0 0 0); animation-timing-function: cubic-bezier(0.7, 0, 0.84, 0); }
  22%  { clip-path: inset(0 0 100% 0); }
  52%  { clip-path: inset(0 0 100% 0); animation-timing-function: var(--ease-out); }
  82%, 100% { clip-path: inset(0 0 0 0); }
}

/* ── Multiplings cover: a tray you can pick up and turn ──────
   Same media as the app and its site: the site draws its sun and clouds in CSS/SVG,
   the app fills tray cells with system emoji, so the visitor's own emoji font draws the fish. */
.app--multiplings .art { background: linear-gradient(180deg, #ff7f73 0%, #ff6b6b 72%); }
.mp-sun {
  position: absolute; width: 40%; aspect-ratio: 1; right: -12%; top: -18%; border-radius: 50%;
  background:
    radial-gradient(circle, rgba(255, 240, 190, 0.95) 0 38%, transparent 39%),
    radial-gradient(circle, rgba(255, 217, 61, 0.34) 0 52%, rgba(255, 217, 61, 0.12) 66%, transparent 72%);
}
.mp-cloud { position: absolute; fill: #fff; fill-opacity: 0.3; stroke: none; }
.mp-cloud-1 { width: 30%; left: 5%; top: 7%; }
.mp-cloud-2 { width: 20%; right: 22%; top: 34%; fill-opacity: 0.2; }
.mp-hill {
  position: absolute; left: -10%; right: -10%; bottom: -30%; height: 52%;
  border-radius: 50% 50% 0 0 / 40% 40% 0 0;
  background: #ff9b8e;
}
.mp-tray {
  position: absolute; left: 50%; top: 46%;
  display: grid; grid-template-columns: repeat(6, 1fr); gap: 2.4%;
  width: 46%; padding: 3%;
  background: #fff; border-radius: 12px;
  box-shadow: 0 14px 26px -8px rgba(80, 20, 20, 0.45), 0 2px 4px rgba(80, 20, 20, 0.18);
  transform: translate(-50%, -50%);
}
.mp-tray i {
  display: grid; place-items: center; aspect-ratio: 1; border-radius: 22%;
  background: #e6efff; font-style: normal; line-height: 1;
  font-size: clamp(0.62rem, 1.05vw, 0.95rem);
}
.mp-label {
  position: absolute; left: 50%; bottom: 7%; transform: translateX(-50%);
  display: grid; padding: 0.3em 0.9em; border-radius: 999px;
  background: #fff8ec; color: #2b2d42; font-weight: 600;
  font-size: clamp(0.9rem, 1.7vw, 1.12rem); font-variant-numeric: tabular-nums; white-space: nowrap;
  box-shadow: 0 4px 10px -2px rgba(80, 20, 20, 0.3);
}
.mp-label > span { grid-area: 1 / 1; }
.mp-label .a { opacity: 0; }

.app--multiplings.play .mp-tray { animation: mp-turn 3.4s both; }
.app--multiplings.play .mp-label .q { animation: mp-out 3.4s both; }
.app--multiplings.play .mp-label .a { animation: mp-in 3.4s both; }
@keyframes mp-turn {
  0%   { transform: translate(-50%, -50%) rotate(0deg); animation-timing-function: cubic-bezier(0.34, 0, 0.2, 1); }
  10%  { transform: translate(-50%, -54%) rotate(-4deg) scale(1.03); animation-timing-function: var(--ease-out); }
  34%  { transform: translate(-50%, -50%) rotate(90deg) scale(0.92); }
  70%  { transform: translate(-50%, -50%) rotate(90deg) scale(0.92); animation-timing-function: var(--ease-out); }
  96%, 100% { transform: translate(-50%, -50%) rotate(0deg); }
}
@keyframes mp-out { 0%, 26% { opacity: 1; } 32%, 72% { opacity: 0; } 80%, 100% { opacity: 1; } }
@keyframes mp-in  { 0%, 26% { opacity: 0; } 32%, 72% { opacity: 1; } 80%, 100% { opacity: 0; } }

/* ── Footer ───────────────────────────────────────────────── */
.footer { border-top: 1px solid var(--border); }
.footer-inner {
  max-width: var(--max); margin: 0 auto; padding: 22px var(--gutter) 28px;
  display: flex; justify-content: space-between; gap: 16px;
  font-size: 0.86rem; color: var(--text-muted);
}
.footer a { text-decoration: none; transition: color 0.18s; }
.footer a:hover { color: var(--text); }

@media (min-width: 1024px) {
  .intro h1 { max-width: none; }
}

/* ── Small screens ────────────────────────────────────────── */
@media (max-width: 640px) {
  :root { --gutter: 16px; }
  .topbar nav { gap: 20px; }
  .intro { text-align: left; }
  .intro h1 { margin: 0; }
  .intro p { margin-left: 0; }
  .shelf { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
  /* No movement: the glow still shows, the cover stays its size (re-stated, not "none"). */
  .cover:hover, .cover:focus-visible { transform: scale(1); }
}
