/*
 * Krizzel's one page. The app is ink on paper and nothing else, so the page
 * is too: a white sheet, a black stroke, and the six crayon colours only
 * where something is meant to be touched.
 */

:root {
  --paper: #ffffff;
  --ink: #1c1c1e;
  --ink-soft: #6b6b70;
  --hairline: rgba(28, 28, 30, 0.12);

  /*
   * The accent is the ink itself - white on the black sheet, black on the
   * white one. A literal white would vanish on white paper.
   */
  --accent: var(--ink);

  /* The six the colour wheel opens on. Crayons, not brand colours. */
  --red: #ff3b30;
  --yellow: #ffcc00;
  --green: #34c759;
  --blue: #007aff;
  --violet: #af52de;

  --font-ui: ui-rounded, "SF Pro Rounded", "Segoe UI Rounded", "Nunito",
    "Varela Round", system-ui, -apple-system, sans-serif;

  /*
   * Two curves for the whole page. `settle` overshoots a little and comes
   * back, the way a hand lands on paper; `soft` only slows in and out, for
   * anything that must not draw attention to itself.
   */
  --settle: cubic-bezier(0.22, 1.4, 0.36, 1);
  --soft: cubic-bezier(0.33, 0, 0.15, 1);

  --page: min(1080px, 100% - 2.5rem);
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper: #000000;
    --ink: #f2f2f7;
    --ink-soft: #9a9aa0;
    --hairline: rgba(242, 242, 247, 0.16);
  }
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

/*
 * A real brush under the pointer, at 64px - wooden handle, metal ferrule,
 * dark tip - because the page is about drawing. It is outlined in white so
 * it stays visible on either sheet, the hotspot sits on the tip that marks
 * the paper, and `crosshair` is the fallback where an image cursor is
 * refused. Past 128px browsers ignore a cursor image entirely.
 */
body {
  cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='64' height='64' viewBox='0 0 64 64'%3E%3Cg stroke='%23fff' stroke-width='3.5' stroke-linejoin='round'%3E%3Cpath d='M53.5 5.5 L58.5 10.5 L37.5 33.5 L30.5 26.5 Z' fill='%23a2845e'/%3E%3Cpath d='M31.5 25.5 L38.5 32.5 L31.9 39.9 L24.1 32.1 Z' fill='%238e8e93'/%3E%3Cpath d='M32.6 40.6 C30 50 20 55.5 8 57 C10 45 15 37 23.4 31.4 Z' fill='%231c1c1e'/%3E%3C/g%3E%3C/svg%3E") 8 56, crosshair;
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: clamp(1rem, 0.95rem + 0.3vw, 1.15rem);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1,
h2,
h3 {
  margin: 0;
  line-height: 1.05;
  letter-spacing: -0.03em;
}

p {
  margin: 0;
}

a {
  color: inherit;
}

/* A brush over a link would hide what the link does. */
a {
  cursor: pointer;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  padding: 0.75rem 1rem;
  background: var(--ink);
  color: var(--paper);
  border-radius: 0 0 1rem 0;
  z-index: 10;
}

.skip:focus {
  left: 0;
}

:focus-visible {
  outline: 3px solid var(--blue);
  outline-offset: 3px;
}

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

.hero {
  position: relative;
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding: clamp(3rem, 8vh, 6rem) 0;
  overflow: hidden;
}

.hero__inner {
  position: relative;
  width: var(--page);
  margin: 0 auto;
  max-width: 44rem;
  text-align: center;
}

.hero__mark {
  width: clamp(64px, 12vw, 96px);
  height: auto;
  border-radius: 28%;
  box-shadow: 0 10px 30px rgba(28, 28, 30, 0.14);
}

@media (prefers-color-scheme: dark) {
  .hero__mark {
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.6);
  }
}

/*
 * The headline is the wordmark being written, so the h1 is only a box for
 * the drawing: no type size, and no line box to add space under it.
 */
.hero__title {
  position: relative;
  margin: 1.25rem 0 0;
  line-height: 0;
}

.hero__word {
  display: block;
  width: min(88%, 32rem);
  height: auto;
  margin: 0 auto;
  color: var(--ink);
  overflow: visible;
}

/*
 * The word gathers before the stroke starts and rebounds once the flourish
 * lands - anticipation and follow-through, so the writing reads as one
 * motion rather than a line appearing. Same curve and length as the app's
 * splash screen.
 *
 * It is tied to .is-writing rather than to page load: the hand that writes
 * the word is the WebGL canvas, and that cannot start until three.js has
 * loaded. Run on load, the settle played against the finished SVG word and
 * the stroke was then drawn a second time by the card.
 */
@keyframes hero-word-settle {
  0% {
    transform: scale(0.985);
  }
  7% {
    transform: scale(1);
  }
  85% {
    transform: scale(1);
  }
  91% {
    transform: scale(1.03);
  }
  100% {
    transform: scale(1);
  }
}

.js .hero__title {
  transform-origin: center;
}

.js .hero__title.is-writing {
  animation: hero-word-settle 2300ms cubic-bezier(0.33, 1, 0.68, 1) forwards;
}

/*
 * Until a writer has claimed the word there is nothing to see: the SVG holds
 * the heading's name and its size, but showing its finished stroke before
 * the pen has started is the word arriving twice. The fallback un-hides it
 * again the moment it takes over the writing itself.
 */
.js .hero__word {
  opacity: 0;
}

/*
 * The page opens as a black sheet with nothing on it but the word being
 * written. Staging: one thing at a time, and the one thing is the hand.
 *
 * The curtain is the page's own two colours held at their dark values, not
 * a lid laid over the top - so lifting it is the sheet turning white under
 * the word rather than something being taken away, and the word (drawn in
 * currentColor, by the card as much as by the SVG) is white on it without
 * being told to be.
 */
.js.is-curtain {
  --paper: #000000;
  --ink: #f2f2f7;
  --ink-soft: #9a9aa0;
  --hairline: rgba(242, 242, 247, 0.16);
  overflow: hidden;
}

.js.is-curtain body {
  overflow: hidden;
}

/*
 * Nothing but the hero exists while the word is being written. The hero
 * itself keeps its layout - the pieces around the word are transparent, not
 * removed - so when they arrive the word does not shift under them.
 */
.js.is-curtain main,
.js.is-curtain footer,
.js.is-curtain .skip {
  visibility: hidden;
}

/*
 * The sheet takes longer to turn than anything landing on it: it is the
 * whole page, and weight is time. It also starts first, so the paper is
 * already coming up when the hero settles onto it.
 */
.js body {
  transition: background-color 760ms var(--soft), color 760ms var(--soft);
}

.js .hero__word.is-writing {
  opacity: 1;
}

/*
 * Where WebGL takes over, the SVG stays in the flow - it holds the heading's
 * name and its size - and the canvas is laid over it, drawing the same box.
 */
.hero__word.is-replaced {
  opacity: 0;
}

.hero__gl {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  display: block;
  width: min(88%, 32rem);
  height: auto;
}

.hero__claim {
  margin-top: 0.35rem;
  font-size: clamp(1.25rem, 4vw, 1.9rem);
  font-weight: 700;
  /*
   * The claim is underlined by hand: one crayon stroke drawn under the words
   * when the page loads, the same gesture the icon is made of.
   */
  display: inline-block;
  padding-bottom: 0.55rem;
  background-image: linear-gradient(var(--accent), var(--accent));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% 0.28em;
  border-radius: 0.2em;
}

.hero__lead {
  margin: 1.25rem auto 0;
  max-width: 34rem;
  color: var(--ink-soft);
  text-wrap: pretty;
}

/*
 * The rest of the hero waits for the word to finish and then fades in - the
 * only other thing that moves there. Without JavaScript it is simply there.
 */
.js .hero__mark,
.js .hero__claim,
.js .hero__lead,
.js .hero .hero__actions {
  opacity: 0;
  transform: translateY(12px);
  transition:
    opacity 520ms var(--soft) var(--lit-delay, 0ms),
    transform 620ms var(--settle) var(--lit-delay, 0ms);
}

/*
 * They do not arrive together. Each piece has its own beat and its own
 * distance, the way parts of one body stop one after another rather than
 * in lockstep - and each rises on the settle curve, so it overshoots its
 * place by a hair and comes back rather than stopping dead on it.
 */
.js .hero__mark {
  --lit-delay: 200ms;
  transform: translateY(14px) scale(0.92);
}

.js .hero__claim {
  --lit-delay: 320ms;
}

.js .hero__lead {
  --lit-delay: 420ms;
}

.js .hero .hero__actions {
  --lit-delay: 540ms;
}

.js .hero__mark.is-lit,
.js .hero__claim.is-lit,
.js .hero__lead.is-lit,
.js .hero .hero__actions.is-lit {
  opacity: 1;
  transform: none;
}

.hero__actions {
  margin-top: 2rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  justify-content: center;
}

/* --------------------------------------------------------------- buttons -- */

.button {
  --lift: 0;
  --squash: 1;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.95rem 1.6rem;
  border: 2px solid var(--ink);
  border-radius: 999px;
  font: inherit;
  font-weight: 700;
  text-decoration: none;
  transform: translateY(var(--lift)) scaleY(var(--squash));
  transform-origin: bottom center;
  transition:
    transform 260ms var(--settle),
    box-shadow 260ms var(--soft),
    background-color 200ms var(--soft);
}

.button--play {
  background: var(--ink);
  color: var(--paper);
  box-shadow: 0 8px 0 -2px var(--accent);
}

.button--ghost {
  background: transparent;
  color: var(--ink);
}

/* Anticipation: the button dips before it rises, so the press has a windup. */
.button:hover {
  --lift: -3px;
}

.button--play:hover {
  box-shadow: 0 12px 0 -2px var(--accent);
}

.button:active {
  --lift: 2px;
  --squash: 0.94;
  transition-duration: 90ms;
}

.button--play:active {
  box-shadow: 0 4px 0 -2px var(--accent);
}

/* Nothing to click yet: the store link has not been filled in. */
.button[aria-disabled="true"] {
  background: transparent;
  color: var(--ink-soft);
  border-style: dashed;
  border-color: var(--hairline);
  box-shadow: none;
  cursor: default;
}

.button[aria-disabled="true"]:hover,
.button[aria-disabled="true"]:active {
  --lift: 0;
  --squash: 1;
  box-shadow: none;
}

/* -------------------------------------------------------------- sections -- */

.section {
  width: var(--page);
  margin: 0 auto;
  padding: clamp(4rem, 12vh, 8rem) 0;
  border-top: 1px solid var(--hairline);
}

.section__title {
  font-size: clamp(1.75rem, 5vw, 2.75rem);
  font-weight: 800;
  margin-bottom: 2.5rem;
}

.cards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 1.25rem;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}

.card {
  padding: 1.75rem;
  border: 2px solid var(--ink);
  border-radius: 1.75rem;
  color: var(--ink-soft);
}

/* Each card gets one crayon, in wheel order, so the set reads as a set. */
.cards .card:nth-child(1) {
  box-shadow: 0 10px 0 -4px var(--red);
}
.cards .card:nth-child(2) {
  box-shadow: 0 10px 0 -4px var(--green);
}
.cards .card:nth-child(3) {
  box-shadow: 0 10px 0 -4px var(--blue);
}

.card__title {
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--ink);
  margin-bottom: 0.6rem;
}

.shots {
  display: flex;
  gap: 1.25rem;
  overflow-x: auto;
  padding-bottom: 1rem;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
}

/*
 * The shots are phone screens, so they are far taller than they are wide.
 * Bounding the height rather than the width keeps the strip one screen tall
 * instead of four.
 */
.shot {
  flex: 0 0 auto;
  margin: 0;
  scroll-snap-align: center;
}

.shot img {
  height: min(58vh, 460px);
  width: auto;
  display: block;
  border: 2px solid var(--ink);
  border-radius: 1.5rem;
  background: var(--paper);
}

.shot figcaption {
  margin-top: 0.75rem;
  font-size: 0.9rem;
  color: var(--ink-soft);
  text-align: center;
}

.end__text {
  max-width: 34rem;
  color: var(--ink-soft);
  margin-bottom: 2rem;
}

.section--end .hero__actions {
  justify-content: flex-start;
  margin-top: 0;
}

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

.footer {
  width: var(--page);
  margin: 0 auto;
  padding: 3rem 0 4rem;
  border-top: 1px solid var(--hairline);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem 1.5rem;
  color: var(--ink-soft);
  font-size: 0.9rem;
}

.footer__mark {
  border-radius: 30%;
}

.footer__links {
  display: flex;
  gap: 1.25rem;
}

.footer__note {
  margin-left: auto;
}

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

/*
 * Everything that arrives comes in from below along a slight arc - a small
 * rotation paired with the rise - and the pieces of one group are staggered
 * rather than moved in lockstep. `.is-in` is put on by doodle.js.
 */
.reveal {
  opacity: 0;
  transform: translateY(22px) rotate(var(--tilt, -0.6deg));
  transition:
    opacity 500ms var(--soft),
    transform 700ms var(--settle);
  transition-delay: var(--delay, 0ms);
}

.reveal.is-in {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .reveal,
  .reveal.is-in {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .button {
    transition: none;
  }

  .js .hero__title.is-writing {
    animation: none;
  }

  .js .hero__mark,
  .js .hero__claim,
  .js .hero__lead,
  .js .hero .hero__actions {
    opacity: 1;
    transform: none;
    transition: none;
  }

  /* No curtain either: a black page that turns white is the motion. */
  .js body {
    transition: none;
  }
}
