/* Oh Life! Studios
   One pixel grid for everything: --px is the size of one art pixel.
   Type is Departure Mono at 11 art pixels, so its letters sit on the same grid
   as the landscape, the wordmark and the little scenes. */

@font-face {
  font-family: "Departure Mono";
  src: url("/assets/departure-mono.woff2") format("woff2");
  font-display: swap;
}

:root {
  --px: 2px;
  --scene: 190;   /* art pixels from the foot of the page to the top of the clouds */
  --stack: 61;    /* height of what hangs in the sky on the centre line */
  --d: 205;       /* how far the neurons and the lesson stand from the centre line */
  --edge: 8;      /* the least room kept above the lettering */
  --paper: #fcf4df;
  --ink: #223629;
  /* the top of the lettering: it hangs in the middle of the open sky */
  --hang: max(var(--edge) * var(--px), (100% - (var(--scene) + var(--stack)) * var(--px)) / 2);
}

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

html { background: var(--paper); overflow-x: hidden; }

body {
  position: relative;
  min-height: 100vh;
  min-height: max(100svh, calc((var(--scene) + var(--stack) + 2 * var(--edge)) * var(--px)));
  margin: 0;
  overflow-x: hidden;
  overflow-x: clip;
  color: var(--ink);
  font: calc(11 * var(--px)) / calc(14 * var(--px)) "Departure Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  -webkit-font-smoothing: none;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

img {
  display: block;
  max-width: none;
  image-rendering: pixelated;
}

h1 { margin: 0; font: inherit; }

/* ---------------------------------------------------------------- sky
   The lettering shares its centre line with the land. Its two lines are the
   same width: 138 art pixels of ink. */
.sky {
  position: absolute;
  z-index: 3;
  top: calc(var(--hang));
  top: round(down, var(--hang), var(--px));
  left: 50%;
  left: round(down, 50%, var(--px));
  width: 0;
}

.say img {
  width: calc(142 * var(--px));
  height: calc(42 * var(--px));
  margin-left: calc(-71 * var(--px));
}
.line {
  display: block;
  width: calc(140 * var(--px));
  margin: calc(5 * var(--px)) 0 0 calc(-70 * var(--px));
  white-space: nowrap;
}

.maker {
  position: absolute;
  z-index: 3;
  top: calc(6 * var(--px));
  right: calc(10 * var(--px));
  padding: calc(6 * var(--px));
}
.maker img { width: calc(10 * var(--px)); height: calc(11 * var(--px)); }

/* Preserve the existing error page when it shares this stylesheet. */
.lost { min-height: 100svh; display: grid; place-items: center; text-align: center; padding: 40px; }
.lost img { margin: 0 auto 24px; width: 64px; }
.lost h1 { font-size: 40px; margin-bottom: 16px; }
.lost p { margin: 0 0 24px; }
.lost a { color: var(--ink); }

/* ---------------------------------------------------------------- scene
   A zero-size anchor at the bottom centre of the page. Everything in the
   landscape is placed from it in art pixels: --x to the right, --y up. */
.scene {
  position: absolute;
  bottom: 0;
  left: 50%;
  left: round(down, 50%, var(--px));
  width: 0;
  height: 0;
}

.world { z-index: 1; pointer-events: none; }
.world img { position: absolute; }

.land   { left: calc(-640 * var(--px)); bottom: 0; width: calc(1280 * var(--px)); height: calc(240 * var(--px)); }
.clouds { left: calc(-640 * var(--px)); bottom: calc(118 * var(--px)); width: calc(1280 * var(--px)); height: calc(110 * var(--px)); }
.clouds { animation: breeze 48s steps(24) infinite alternate; }
@keyframes breeze {
  from { transform: translateX(calc(-12 * var(--px))); }
  to { transform: translateX(calc(12 * var(--px))); }
}

.places { z-index: 2; }

/* ---------------------------------------------------------------- places
   Three small scenes, each with its name set beneath it: neurons in the sky,
   a bus stop on the lane, a lesson on the grass. */
.place {
  position: absolute;
  left: calc(var(--x) * var(--px));
  bottom: calc(var(--y) * var(--px));
  display: flex;
  flex-direction: column;
  align-items: center;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}
.name { display: block; }

.place:hover .name,
.place:focus-visible .name {
  text-decoration: underline;
  text-decoration-thickness: var(--px);
  text-underline-offset: calc(2 * var(--px));
}

a:focus-visible {
  outline: var(--px) solid var(--ink);
  outline-offset: calc(2 * var(--px));
}

/* Features: round neurons, turning so that the net folds through itself. It is
   as tall as the wordmark, so beside the lettering its name shares a line
   with "I'm so glad I got in". */
.place--features {
  z-index: 3;
  top: calc(var(--hang));
  top: round(down, var(--hang), var(--px));
  bottom: auto;
  left: calc(50% - (var(--d) + 32) * var(--px));
  left: round(down, 50% - (var(--d) + 32) * var(--px), var(--px));
}
.neurons {
  display: block;
  width: calc(64 * var(--px));
  height: calc(44 * var(--px));
  margin-bottom: calc(3 * var(--px));
  overflow: hidden;
}
.neurons img {
  width: calc(3840 * var(--px));
  height: calc(44 * var(--px));
  animation: turn 12s steps(60) infinite;
}
@keyframes turn { to { transform: translateX(-100%); } }

/* Portudo: the stop stands on the near verge of the lane with its name
   beneath, and the buses pass behind it. */
.place--portudo { --x: -24; --y: 60; z-index: 2; width: calc(49 * var(--px)); }
.place--portudo img {
  width: calc(7 * var(--px));
  height: calc(13 * var(--px));
  margin-bottom: calc(3 * var(--px));
}

.bus {
  position: absolute;
  left: 0;
  bottom: 0;
  z-index: 1;
  animation: drive 110s steps(1318) infinite;
}
.bus span {
  display: block;
  padding: calc(6 * var(--px)) 0;
  margin-bottom: calc(-6 * var(--px));
  animation: lane-wide 110s step-end infinite;
}
.bus img { width: calc(22 * var(--px)); height: calc(11 * var(--px)); }
.bus--2, .bus--2 span { animation-delay: -36.667s; }
.bus--3, .bus--3 span { animation-delay: -73.333s; }
.bus:hover, .bus:hover span { animation-play-state: paused; }
@keyframes drive {
  from { transform: translateX(calc(-670 * var(--px))); }
  to   { transform: translateX(calc(648 * var(--px))); }
}

/* Português com a Inês: teacher and learner share a book; "olá" passes
   from one to the other. */
.place--ines { --x: calc(var(--d) - 70); --y: 8; width: calc(140 * var(--px)); }
.lesson {
  position: relative;
  display: block;
  margin-bottom: calc(2 * var(--px));
}
.lesson img { width: calc(60 * var(--px)); height: calc(30 * var(--px)); }
.lesson .bubble {
  position: absolute;
  left: calc(2 * var(--px));
  bottom: calc(31 * var(--px));
  width: calc(23 * var(--px));
  height: calc(18 * var(--px));
  animation: say 9s step-end infinite;
}
@keyframes say {
  0%   { transform: none; opacity: 1; }
  38%  { transform: none; opacity: 0; }
  50%  { transform: translateX(calc(28 * var(--px))); opacity: 1; }
  88%  { transform: translateX(calc(28 * var(--px))); opacity: 0; }
  100% { transform: none; opacity: 1; }
}

/* ---------------------------------------------------------------- widths */
@media (max-width: 1439px) { :root { --d: 165; } }
@media (max-width: 1023px) {
  :root { --d: 128; }
  .place--ines { --x: 34; }
}

/* ---------------------------------------------------------------- narrow
   On a phone everything stacks on the centre line: lettering, neurons, the
   lane, the lesson. */
@media (max-width: 719px) {
  :root { --scene: 176; --stack: 130; --d: 0; --edge: 12; }
  .maker { top: 0; right: 0; }

  .land   { left: calc(-180 * var(--px)); width: calc(360 * var(--px)); height: calc(232 * var(--px)); }
  .clouds { left: calc(-180 * var(--px)); bottom: calc(114 * var(--px)); width: calc(360 * var(--px)); }

  .place--features {
    top: calc(var(--hang) + 69 * var(--px));
    top: round(down, var(--hang) + 69 * var(--px), var(--px));
  }
  .place--portudo { --x: 28; --y: 54; }
  .place--ines { --x: -70; --y: 6; }

  .bus { animation: drive-tall 33s steps(398) infinite; }
  .bus span { animation-name: lane-tall; animation-duration: 33s; }
  .bus--2, .bus--2 span { animation-delay: -16.5s; }
  .bus--3 { display: none; }
}
@keyframes drive-tall {
  from { transform: translateX(calc(-210 * var(--px))); }
  to   { transform: translateX(calc(188 * var(--px))); }
}

/* ---------------------------------------------------------------- big screens */
@media (min-width: 1800px) and (min-height: 1000px), (min-width: 2561px) {
  :root { --px: 3px; }
}
@media (min-width: 3841px) {
  :root { --px: 4px; }
}

/* ---------------------------------------------------------------- stillness
   With motion turned down: the neurons hold, one bus waits at the stop and
   the teacher keeps the word. */
@media (prefers-reduced-motion: reduce) {
  .neurons img, .lesson .bubble, .clouds { animation: none; }
  .bus { animation: none; transform: translateX(calc(-30 * var(--px))); }
  .bus span { animation: none; transform: translateY(calc(-78 * var(--px))); }
  .bus--2, .bus--3 { display: none; }
}
@media (prefers-reduced-motion: reduce) and (max-width: 719px) {
  .bus { transform: translateX(calc(25 * var(--px))); }
  .bus span { transform: translateY(calc(-73 * var(--px))); }
}

/* ---------------------------------------------------------------- the lane
   Generated by tools/landscape.py --lane-css: how far the buses rise and
   fall as they follow the lane across each picture. */
/* wide: the bus runs from -670 to 648 (1318 steps) */
@keyframes lane-wide {
  0.000% { transform: translateY(calc(-55 * var(--px))); }
  1.973% { transform: translateY(calc(-56 * var(--px))); }
  2.656% { transform: translateY(calc(-57 * var(--px))); }
  3.414% { transform: translateY(calc(-58 * var(--px))); }
  4.097% { transform: translateY(calc(-59 * var(--px))); }
  4.780% { transform: translateY(calc(-60 * var(--px))); }
  5.463% { transform: translateY(calc(-61 * var(--px))); }
  6.146% { transform: translateY(calc(-62 * var(--px))); }
  6.829% { transform: translateY(calc(-63 * var(--px))); }
  7.511% { transform: translateY(calc(-64 * var(--px))); }
  8.194% { transform: translateY(calc(-65 * var(--px))); }
  8.877% { transform: translateY(calc(-66 * var(--px))); }
  9.560% { transform: translateY(calc(-67 * var(--px))); }
  10.319% { transform: translateY(calc(-68 * var(--px))); }
  11.153% { transform: translateY(calc(-69 * var(--px))); }
  11.988% { transform: translateY(calc(-70 * var(--px))); }
  12.974% { transform: translateY(calc(-71 * var(--px))); }
  14.416% { transform: translateY(calc(-72 * var(--px))); }
  20.637% { transform: translateY(calc(-71 * var(--px))); }
  23.445% { transform: translateY(calc(-70 * var(--px))); }
  25.266% { transform: translateY(calc(-69 * var(--px))); }
  26.783% { transform: translateY(calc(-68 * var(--px))); }
  28.528% { transform: translateY(calc(-67 * var(--px))); }
  34.067% { transform: translateY(calc(-68 * var(--px))); }
  35.660% { transform: translateY(calc(-69 * var(--px))); }
  37.102% { transform: translateY(calc(-70 * var(--px))); }
  38.467% { transform: translateY(calc(-71 * var(--px))); }
  40.061% { transform: translateY(calc(-72 * var(--px))); }
  41.654% { transform: translateY(calc(-73 * var(--px))); }
  43.020% { transform: translateY(calc(-74 * var(--px))); }
  44.310% { transform: translateY(calc(-75 * var(--px))); }
  45.524% { transform: translateY(calc(-76 * var(--px))); }
  46.813% { transform: translateY(calc(-77 * var(--px))); }
  48.103% { transform: translateY(calc(-78 * var(--px))); }
  49.393% { transform: translateY(calc(-79 * var(--px))); }
  50.759% { transform: translateY(calc(-80 * var(--px))); }
  52.200% { transform: translateY(calc(-81 * var(--px))); }
  53.718% { transform: translateY(calc(-82 * var(--px))); }
  55.615% { transform: translateY(calc(-83 * var(--px))); }
  58.422% { transform: translateY(calc(-82 * var(--px))); }
  61.153% { transform: translateY(calc(-81 * var(--px))); }
  63.202% { transform: translateY(calc(-80 * var(--px))); }
  64.947% { transform: translateY(calc(-79 * var(--px))); }
  66.388% { transform: translateY(calc(-78 * var(--px))); }
  67.830% { transform: translateY(calc(-77 * var(--px))); }
  69.575% { transform: translateY(calc(-76 * var(--px))); }
  74.886% { transform: translateY(calc(-77 * var(--px))); }
  76.328% { transform: translateY(calc(-78 * var(--px))); }
  77.466% { transform: translateY(calc(-79 * var(--px))); }
  78.376% { transform: translateY(calc(-80 * var(--px))); }
  79.211% { transform: translateY(calc(-81 * var(--px))); }
  80.046% { transform: translateY(calc(-82 * var(--px))); }
  80.728% { transform: translateY(calc(-83 * var(--px))); }
  81.411% { transform: translateY(calc(-84 * var(--px))); }
  82.094% { transform: translateY(calc(-85 * var(--px))); }
  82.701% { transform: translateY(calc(-86 * var(--px))); }
  83.384% { transform: translateY(calc(-87 * var(--px))); }
  83.991% { transform: translateY(calc(-88 * var(--px))); }
  84.598% { transform: translateY(calc(-89 * var(--px))); }
  85.281% { transform: translateY(calc(-90 * var(--px))); }
  85.964% { transform: translateY(calc(-91 * var(--px))); }
  86.722% { transform: translateY(calc(-92 * var(--px))); }
  87.481% { transform: translateY(calc(-93 * var(--px))); }
  88.240% { transform: translateY(calc(-94 * var(--px))); }
  88.998% { transform: translateY(calc(-95 * var(--px))); }
  89.833% { transform: translateY(calc(-96 * var(--px))); }
  90.592% { transform: translateY(calc(-97 * var(--px))); }
  91.426% { transform: translateY(calc(-98 * var(--px))); }
  92.261% { transform: translateY(calc(-99 * var(--px))); }
  93.323% { transform: translateY(calc(-100 * var(--px))); }
  94.613% { transform: translateY(calc(-101 * var(--px))); }
  100% { transform: translateY(calc(-101 * var(--px))); }
}
/* tall: the bus runs from -210 to 188 (398 steps) */
@keyframes lane-tall {
  0.000% { transform: translateY(calc(-94 * var(--px))); }
  5.025% { transform: translateY(calc(-93 * var(--px))); }
  10.302% { transform: translateY(calc(-92 * var(--px))); }
  14.573% { transform: translateY(calc(-91 * var(--px))); }
  18.090% { transform: translateY(calc(-90 * var(--px))); }
  21.357% { transform: translateY(calc(-89 * var(--px))); }
  24.623% { transform: translateY(calc(-88 * var(--px))); }
  27.889% { transform: translateY(calc(-87 * var(--px))); }
  30.905% { transform: translateY(calc(-86 * var(--px))); }
  34.171% { transform: translateY(calc(-85 * var(--px))); }
  36.181% { transform: translateY(calc(-84 * var(--px))); }
  37.437% { transform: translateY(calc(-83 * var(--px))); }
  38.442% { transform: translateY(calc(-82 * var(--px))); }
  39.447% { transform: translateY(calc(-81 * var(--px))); }
  40.452% { transform: translateY(calc(-80 * var(--px))); }
  41.457% { transform: translateY(calc(-79 * var(--px))); }
  42.462% { transform: translateY(calc(-78 * var(--px))); }
  43.467% { transform: translateY(calc(-77 * var(--px))); }
  44.472% { transform: translateY(calc(-76 * var(--px))); }
  45.477% { transform: translateY(calc(-75 * var(--px))); }
  46.734% { transform: translateY(calc(-74 * var(--px))); }
  48.492% { transform: translateY(calc(-73 * var(--px))); }
  69.598% { transform: translateY(calc(-74 * var(--px))); }
  75.628% { transform: translateY(calc(-75 * var(--px))); }
  79.899% { transform: translateY(calc(-76 * var(--px))); }
  83.166% { transform: translateY(calc(-77 * var(--px))); }
  86.432% { transform: translateY(calc(-78 * var(--px))); }
  89.447% { transform: translateY(calc(-79 * var(--px))); }
  92.462% { transform: translateY(calc(-80 * var(--px))); }
  100% { transform: translateY(calc(-80 * var(--px))); }
}
