/* Correct the Robot — the way in.
 *
 * The landing page is shown when the URL names no pack, in place of the round.
 * It sits on the same painting the game does, so it is a scrim and a column of
 * cards rather than a screen of its own: the robot is the first thing a child
 * should see either way, and arriving at the menu should look like arriving at
 * the game with nothing started yet.
 *
 * It follows game.css's two layouts and its unit exactly — `--u` capped by
 * height in portrait so a wide-but-short window gets a phone-shaped column
 * rather than a poster, and plain `1cqw` once the painting fills the frame.
 * Kept in its own file because game.css is already the longest thing here and
 * none of this is on screen once a round starts.
 */

/* The round's UI and the menu are never both up. Hiding the whole column is
   one rule here rather than a `hidden` on each of its ten sections — and it
   means a section the game shows on its own schedule cannot leak onto the
   menu. */
.app[data-state="LANDING"] .ui { display: none; }

.landing {
  /* The same cap game.css explains at length: on a phone the two agree, on
     anything squatter the height wins. */
  --u: min(1cqw, .462cqh);

  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Centred in the band *below* the lit window rather than pinned to either
     edge of it: the menu is four things and a title, which is too little to
     fill a phone, and pushed to the bottom it left a hand's width of empty
     night under the robot's chin. The padding is the window's own height. */
  justify-content: center;
  gap: calc(2.6 * var(--u));
  padding: 36cqh calc(5 * var(--u)) calc(4cqh + env(safe-area-inset-bottom, 0px));
  overflow-y: auto;
  text-align: center;

  /* Clear at the top so the robot's head is still the first thing seen, and
     opaque by the bottom where the type is. Sampled from --dusk-deep, so the
     scrim is the painting's own night rather than a grey wash over it. */
  background: linear-gradient(180deg,
    #101B4200 0%, #101B4259 30%, #101B42D6 58%, #101B42F5 100%);
}
/* `display: flex` beats the hidden attribute's UA rule, so it has to be said
   again. Without this the menu is on screen underneath every round. */
.landing[hidden] { display: none; }

/* — the title — */

/* Every line of type here sits on a painting rather than on a card, so they
   all carry the same shadow — enough to hold a white letter over the robot's
   white head, which is the worst case on the page. */
.landing-title,
.landing-title-en,
.landing-tagline,
.landing-heading,
.landing-heading-en {
  text-shadow: 0 calc(.4 * var(--u)) calc(1.8 * var(--u)) #0A143BE6;
}

.landing-title {
  margin: 0;
  font-family: var(--kai);
  font-weight: 700;
  font-size: calc(9 * var(--u));
  line-height: 1.15;
  color: var(--paper);
}
.landing-title-en {
  display: block;
  margin-top: .45em;
  font-family: var(--hei);
  font-weight: 400;
  font-size: calc(3 * var(--u));
  letter-spacing: .12em;
  color: #FFFFFFDB;
}

.landing-tagline {
  margin: 0;
  max-width: calc(78 * var(--u));
  font-family: var(--hei);
  font-size: calc(3.1 * var(--u));
  line-height: 1.65;
  color: #FFFFFFD9;
}

/* — the two top buttons — */

.landing-primary {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: stretch;
  gap: calc(2.6 * var(--u));
  width: 100%;
}
.landing-primary[hidden] { display: none; }

.try-row { display: contents; }

/* — one button per pack, and the intro — */

.menu-link {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: calc(.4 * var(--u));
  padding: calc(2.6 * var(--u)) calc(3.4 * var(--u));
  min-width: calc(30 * var(--u));
  border: calc(.24 * var(--u)) solid var(--paper-edge);
  border-radius: calc(3.6 * var(--u));
  background: #FFFFFFF2;
  color: var(--ink);
  box-shadow: 0 calc(1.4 * var(--u)) calc(3 * var(--u)) calc(-1.4 * var(--u)) #0A143BCC;
  text-decoration: none;
  cursor: pointer;
  font: inherit;
  transition: transform .14s;
}
.menu-link:hover { transform: translateY(-4%); }
/* Quieter, not fainter. Dropping the opacity of the whole card let the
   painting through it and read as a rendering fault rather than as a button
   that is not ready — and it took the label with it, which is the one part
   that has to stay readable for the state to mean anything. */
.menu-link:disabled {
  cursor: default;
  background: var(--paper-2);
  color: var(--ink-soft);
  box-shadow: none;
}
.menu-link:disabled .menu-label-en { color: var(--ink-soft); }
.menu-link:disabled:hover { transform: none; }

.menu-label {
  font-family: var(--kai);
  font-weight: 700;
  font-size: calc(5 * var(--u));
  line-height: 1.2;
}
.menu-label-en {
  font-family: var(--hei);
  font-weight: 400;
  font-size: calc(2.3 * var(--u));
  letter-spacing: .06em;
  color: var(--ink-soft);
}
/* The one button that starts something. Coral, like the microphone — the two
   places in this game where pressing means "begin". */
.try-link {
  background: linear-gradient(180deg, #FF8093, var(--coral));
  border-color: transparent;
  color: #fff;
  box-shadow: 0 calc(1.6 * var(--u)) calc(3.2 * var(--u)) calc(-1.4 * var(--u)) #7A1228;
}
.try-link .menu-label-en { color: #FFFFFFD9; }

.intro-link .button-icon {
  width: calc(4.4 * var(--u));
  height: calc(4.4 * var(--u));
  color: var(--ink-soft);
}

/* Shown for exactly as long as the button is disabled — landing.js keeps the
   two in step, so the button never looks ready while doing nothing. */
.landing-soon {
  margin-top: calc(.6 * var(--u));
  padding: calc(.5 * var(--u)) calc(1.6 * var(--u));
  border-radius: 999px;
  background: var(--paper-2);
  color: var(--ink-warm);
  font-family: var(--hei);
  font-size: calc(2.1 * var(--u));
}
.landing-soon[hidden] { display: none; }

/* — the ladder — */

.landing-courses {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(1.8 * var(--u));
  width: 100%;
}
.landing-courses[hidden] { display: none; }

.landing-heading {
  margin: 0;
  font-family: var(--kai);
  font-weight: 700;
  font-size: calc(4.2 * var(--u));
  color: var(--paper);
}
.landing-heading-en {
  display: block;
  font-family: var(--hei);
  font-weight: 400;
  font-size: calc(2.3 * var(--u));
  letter-spacing: .06em;
  color: #FFFFFFCC;
}

/* Three across at every width, like the chips: the ladder is one choice made
   by comparing three things, and a column would make it three decisions. */
.course-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: calc(2 * var(--u));
  width: 100%;
  max-width: calc(96 * var(--u));
}
.course-link {
  min-width: 0;
  padding: calc(2.4 * var(--u)) calc(1.6 * var(--u));
}

/* ══ LANDSCAPE — the painting fills the frame ══════════════════════════
 *
 * The same two conditions game.css uses, for the same reasons: below 820px a
 * three-up row of cards stops being readable, and below 5:4 the letterboxing
 * stops reading as sky and desk.
 */
@media (min-width: 820px) and (min-aspect-ratio: 5 / 4) {

  .landing {
    --u: 1cqw;
    /* Matched to the scene's box rather than to the viewport, so the column
       sits on the painting and not on the letterbox above it. */
    inset: auto;
    left: 0;
    top: 50%;
    width: 100cqw;
    height: auto;
    aspect-ratio: 1672 / 941;
    transform: translateY(-50%);
    /* Anchored to the bottom rather than centred, so the robot's face clears
       the column. Centring put the title across its screen, which is the one
       thing on this page a child recognises before they can read any of it. */
    justify-content: flex-end;
    padding: 4% 8% 3.6%;
    gap: 1.8%;
    background: linear-gradient(180deg,
      #101B4200 0%, #101B421F 22%, #101B4299 44%, #101B42E6 68%, #101B42F5 100%);
  }

  .landing-title { font-size: calc(5 * var(--u)); }
  .landing-title-en { font-size: calc(1.7 * var(--u)); }
  .landing-tagline {
    max-width: calc(58 * var(--u));
    font-size: calc(2 * var(--u));
  }

  .menu-link {
    min-width: calc(18 * var(--u));
    padding: calc(1.4 * var(--u)) calc(2.2 * var(--u));
    border-radius: calc(1.8 * var(--u));
    gap: calc(.2 * var(--u));
  }
  .menu-label { font-size: calc(2.6 * var(--u)); }
  .menu-label-en { font-size: calc(1.3 * var(--u)); }
  .intro-link .button-icon {
    width: calc(2.4 * var(--u));
    height: calc(2.4 * var(--u));
  }
  .landing-soon { font-size: calc(1.2 * var(--u)); padding: .2% .8%; }

  .landing-heading { font-size: calc(2.2 * var(--u)); }
  .landing-heading-en { font-size: calc(1.3 * var(--u)); }
  .course-grid { max-width: calc(58 * var(--u)); gap: calc(1.4 * var(--u)); }
  .course-link { padding: calc(1.4 * var(--u)) calc(1.2 * var(--u)); }
}

@media (prefers-reduced-motion: reduce) {
  .menu-link { transition: none; }
  .menu-link:hover { transform: none; }
}

/* ══ THE INTRO ═════════════════════════════════════════════════════════
 *
 * Five narrated pictures, played by the vendored component in
 * public/intro/. It draws the picture, its text and the camera move and
 * nothing else, so everything below is either the room it plays in or the
 * one button it hands off to.
 *
 * Darker than the menu and without the painting behind it: this is the one
 * screen of the game that is not set in the robot's room, and a story reads
 * better in a dark theatre than over a lit one.
 */

.app[data-state="INTRO"] .ui,
.app[data-state="INTRO"] .scene { display: none; }

.intro {
  --u: min(1cqw, .462cqh);

  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: calc(3 * var(--u));
  padding: calc(4 * var(--u));
  overflow-y: auto;
  background: var(--dusk-deep);
}
.intro[hidden] { display: none; }

/* The component sizes itself to its mount's width and holds its own 4:3, so
   the height is capped from here by capping the width. The room left over is
   for the button, which must never need scrolling to reach. */
.intro-stage {
  width: 100%;
  max-width: calc(74cqh * 4 / 3);
}

/* Restyled from here rather than forked — the component exposes all of it as
   custom properties. The screen text is 黑体, like every other line in this
   game that is not the sentence being read. */
.intro .ia-player {
  --ia-ink: var(--ink);
  --ia-paper: var(--dusk-deep);
  --ia-scrim: #FFFCF4EE;
  --ia-radius: calc(3.4 * var(--u));
  --ia-text-size: calc(3.4 * var(--u));
  font-family: var(--hei);
  box-shadow: 0 calc(2 * var(--u)) calc(5 * var(--u)) calc(-2 * var(--u)) #000A;
}

.intro-actions { display: flex; justify-content: center; }
.intro-actions[hidden] { display: none; }

#intro-start { min-width: calc(40 * var(--u)); }

@media (min-width: 820px) and (min-aspect-ratio: 5 / 4) {
  .intro {
    --u: 1cqw;
    gap: 2%;
    padding: 3%;
  }
  .intro-stage { max-width: calc(76cqh * 4 / 3); }
  .intro .ia-player { --ia-text-size: calc(2 * var(--u)); }
  #intro-start { min-width: calc(22 * var(--u)); }
}

/* Two buttons at the end of the story: on, and back. The way on keeps the
   coral it has everywhere else in this game; the way back is quieter, because
   it is the choice a child makes by not making the other one. */
.intro-actions { gap: calc(2.4 * var(--u)); }

.home-link {
  background: #FFFFFFE6;
  color: var(--ink-soft);
  min-width: calc(30 * var(--u));
}

@media (min-width: 820px) and (min-aspect-ratio: 5 / 4) {
  .intro-actions { gap: calc(1.6 * var(--u)); }
  .home-link { min-width: calc(16 * var(--u)); }
}

/* — the disclaimer — */

/* The foot of the front page, and deliberately the quietest type on it. It is
   the only English-only text in the game: the legal notice is for an adult,
   and a child choosing 简单 is not reading it. So it is sized and coloured to
   be findable and not to compete — a menu whose loudest neighbour is a licence
   would be a strange way in.
   An ordinary item in the column, *not* pushed down with `margin-top: auto`:
   the landing centres its content in the band below the lit window, and an
   auto margin would eat all the free space that centring distributes, pinning
   the menu to the top of the screen to put the footer at the bottom. It sits
   under the courses with a little more air than the gap gives it. */
.landing-footer {
  padding-top: calc(3 * var(--u));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(.6 * var(--u));
  font-family: var(--hei);
  /* The same shadow every other line on this painting carries. */
  text-shadow: 0 calc(.4 * var(--u)) calc(1.8 * var(--u)) #0A143BE6;
}

.landing-copyright {
  margin: 0;
  font-size: calc(2.1 * var(--u));
  line-height: 1.5;
  color: #FFFFFF8C;
}

/* A button, because it opens an overlay rather than going anywhere — but it
   has to read as the link it behaves like, so it is underlined and carries no
   chrome of its own. */
.landing-terms {
  padding: calc(.8 * var(--u)) calc(1.6 * var(--u));
  border: 0;
  background: none;
  font: inherit;
  font-size: calc(2.1 * var(--u));
  color: #FFFFFFB8;
  text-decoration: underline;
  text-underline-offset: calc(.5 * var(--u));
  cursor: pointer;
}
.landing-terms:hover,
.landing-terms:focus-visible { color: var(--paper); }

/* — what it opens — */

/* Full-bleed, above everything, and its own scrim rather than the landing's:
   this covers the page, and the menu underneath should read as put away
   rather than as merely dimmed a second time. */
.terms {
  --u: min(1cqw, .462cqh);

  position: absolute;
  inset: 0;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(5 * var(--u));
  background: #060C26D9;
}
.terms[hidden] { display: none; }

/* Paper, like every card in this game — the licence is the one place the game
   speaks as a company, and it should look like a sheet handed over rather
   than like part of the painting. */
.terms-panel {
  display: flex;
  flex-direction: column;
  gap: calc(2.4 * var(--u));
  width: min(calc(88 * var(--u)), 100%);
  /* Tall enough that the licence fits without scrolling on a laptop. At
     86cqh it overflowed by 21px — an amount small enough to read as a
     clipped line rather than as a scrollable region, which is the worst of
     both. It still scrolls when the window is genuinely short. */
  max-height: 92cqh;
  padding: calc(4 * var(--u));
  border-radius: calc(3.6 * var(--u));
  background: var(--paper);
  color: var(--ink-warm);
  box-shadow: 0 calc(2 * var(--u)) calc(6 * var(--u)) calc(-2 * var(--u)) #000A;
}

.terms-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: calc(2 * var(--u));
}

.terms-title {
  margin: 0;
  font-family: var(--hei);
  font-size: calc(4.2 * var(--u));
  font-weight: 700;
  color: var(--ink);
}

.terms-close {
  flex: none;
  display: grid;
  place-items: center;
  width: calc(6 * var(--u));
  height: calc(6 * var(--u));
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--ink-soft);
  cursor: pointer;
}
.terms-close:hover,
.terms-close:focus-visible { background: var(--paper-2); color: var(--ink); }
.terms-close .button-icon { width: calc(3.2 * var(--u)); height: calc(3.2 * var(--u)); }

/* The only scrolling region on the screen: the panel is capped at the window
   and the legal text is as long as it is. */
.terms-body {
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: calc(1.8 * var(--u));
  font-family: var(--hei);
  font-size: calc(2.9 * var(--u));
  line-height: 1.7;
}
.terms-body p { margin: 0; }
.terms-body strong { color: var(--ink); }
.terms-note { color: var(--ink-soft); }

/* Full width, and the last thing in the panel: on a phone the × is a reach
   away at the top, and this is the button a thumb finds. */
.terms-dismiss {
  justify-content: center;
  background: var(--paper-2);
  color: var(--ink);
  font-family: var(--hei);
  font-size: calc(3.4 * var(--u));
}

@media (min-width: 820px) and (min-aspect-ratio: 5 / 4) {
  .terms {
    --u: 1cqw;
    padding: 4%;
  }
  /* Narrower than it looks like it should be. The panel is a column of
     prose, and a line of it wants ~70 characters however wide the window
     gets — at 56u the licence ran the full width of a laptop and read like a
     contract. */
  .terms-panel {
    width: min(calc(44 * var(--u)), 100%);
    gap: calc(1.6 * var(--u));
    padding: calc(2.4 * var(--u));
    border-radius: calc(1.8 * var(--u));
  }
  .terms-head { gap: calc(1.2 * var(--u)); }
  .terms-close { width: calc(3.2 * var(--u)); height: calc(3.2 * var(--u)); }
  .terms-close .button-icon { width: calc(1.8 * var(--u)); height: calc(1.8 * var(--u)); }
  .terms-body { gap: calc(1 * var(--u)); }

  .landing-footer { padding-top: calc(1.4 * var(--u)); }
  .landing-copyright,
  .landing-terms { font-size: calc(1.05 * var(--u)); }
  .terms-title { font-size: calc(1.9 * var(--u)); }
  .terms-body { font-size: calc(1.15 * var(--u)); }
  .terms-dismiss { font-size: calc(1.4 * var(--u)); }
}

/* — the iOS warning — */

/* A bottom banner on the menu, up for two seconds and gone.
 *
 * `pointer-events: none` is the load-bearing line, not the z-index. The menu's
 * bottom row sits under this on a short screen, and a warning that ate the
 * press that starts the game would be a worse bug than the one it is warning
 * about. Nothing here is interactive, so nothing here needs to take a press.
 *
 * Fixed to the viewport rather than absolute in .app, so it stays on the
 * bottom edge of a phone whose address bar is mid-collapse — .app is sized to
 * the container and moves while that happens.
 */
.ios-notice {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 5;
  pointer-events: none;

  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(.4 * var(--u, 3.9px));
  padding: calc(2 * var(--u, 3.9px)) calc(4 * var(--u, 3.9px));
  /* Clear of the home indicator, which on an iPhone is exactly where this
     lands and is the one gesture target the OS will not let a page cover. */
  padding-bottom: calc(2 * var(--u, 3.9px) + env(safe-area-inset-bottom, 0px));

  text-align: center;
  font-family: var(--hei);
  color: var(--paper);
  /* Its own dark bar rather than the landing's scrim: this has to stay
     readable over whatever it happens to be covering, and on a round it would
     be over the painting. */
  background: linear-gradient(0deg, #2A1030F5, #2A1030D9);
  border-top: 1px solid #FFFFFF26;

  animation: ios-notice-in .22s ease-out;
}
.ios-notice[hidden] { display: none; }

/* Up from the edge it is pinned to, which is the only direction that does not
   look like something arriving over the buttons. */
@keyframes ios-notice-in {
  from { opacity: 0; transform: translateY(35%); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .ios-notice { animation: none; }
}

.ios-notice-zh,
.ios-notice-en {
  margin: 0;
  line-height: 1.45;
  max-width: calc(92 * var(--u, 3.9px));
}
/* Bigger than it first looked right at. The banner is up for two seconds and
   carries a warning, so it has to be read at a glance rather than merely be
   present: the first draft set the Chinese at 2.7u, below the landing's own
   tagline, and the English at 2u, which measured 7.8px on a phone. */
.ios-notice-zh { font-size: calc(3.6 * var(--u, 3.9px)); }
.ios-notice-en {
  font-size: calc(2.6 * var(--u, 3.9px));
  color: #FFFFFFC7;
}

/* The unit is the landing's, but this sits outside .landing — it is shown over
   whatever is on screen — so it has to be given one of its own. */
.ios-notice { --u: min(1cqw, .462cqh); }

@media (min-width: 820px) and (min-aspect-ratio: 5 / 4) {
  .ios-notice {
    --u: 1cqw;
    padding: calc(1.2 * var(--u)) calc(3 * var(--u));
    padding-bottom: calc(1.2 * var(--u) + env(safe-area-inset-bottom, 0px));
  }
  .ios-notice-zh { font-size: calc(1.5 * var(--u)); }
  .ios-notice-en { font-size: calc(1.1 * var(--u)); }
  .ios-notice-zh,
  .ios-notice-en { max-width: calc(70 * var(--u)); }
}
