/*
  The arriving-soon page. One screen, no scroll on a normal viewport.

  Written as plain CSS on purpose: it is one page, so a bundler and a utility
  framework would both be cost with nothing to buy. The admin app's Tailwind
  setup does not reach here and should not — this surface answers to the
  marketing brand, not to Polaris.
*/

:root {
  /* Brand. Lime is the sun and the field it rises from; pink marks what is
     coming; ink grounds both. */
  --cream: #fff9eb;
  --dot: #e9e1c6;
  --dot-on-lime: #c4da00;
  --ink: #0e1626;
  --ink-2: #2a3346;
  --text-2: #5a5344;
  --text-3: #8c8472;
  --lime: #d9f200;
  --pink: #fd438c;

  --display: "Space Grotesk", "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  /* The scene is one number and four ratios off it, so it keeps the proportions
     it was drawn at wherever it lands. `min(vh, vw)` because a laptop is short
     before it is narrow: 1280x800 is the viewport that overflows first. */
  --scene: clamp(150px, min(26vh, 30vw), 280px);
  --sun: calc(var(--scene) * 680 / 280);
  --sun-visible: var(--scene);
  --rider-h: calc(var(--scene) * 240 / 280);
  --rider-w: calc(var(--rider-h) * 234 / 240);
  --city-w: calc(var(--scene) * 620 / 280);

  /* Vertical rhythm. One gap between everything in the pitch and one control
     height, both giving way on a short viewport before the page does — a
     laptop at 1366x768 is the case this exists for. */
  --rhythm: clamp(11px, 2.1vh, 18px);
  --control: clamp(46px, 6.2vh, 54px);

  /* One pedal revolution, and the distance the road covers in the same beat.
     They move together: change one and the other stops agreeing with it. */
  --ride: 1.25s;
  --road: 0.84s;
}

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

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

body {
  margin: 0;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  background: var(--cream);
  background-image: radial-gradient(circle, var(--dot) 1.8px, transparent 1.9px);
  background-size: 22px 22px;
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 23px;
  font-weight: 450;
  -webkit-font-smoothing: antialiased;
  /* Inter and Space Grotesk both arrive over the network. Scaling the fallback
     to their x-height means the swap does not move the page under the reader —
     it is the headline that shifts furthest without this. */
  font-size-adjust: from-font;
}

a {
  color: var(--pink);
  text-decoration: none;
}
a:hover {
  color: var(--ink);
}

img {
  max-width: 100%;
}

.mono {
  font-family: var(--mono);
  font-size: 11px;
  line-height: 16px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* ---------------------------------------------------------------- the band */

/* The brand block the whole pitch sits on. Its own dot field, a shade of the
   lime, so the texture reads as one surface running under both halves. */
.band {
  position: relative;
  background: var(--lime);
  background-image: radial-gradient(circle, var(--dot-on-lime) 2px, transparent 2.1px);
  background-size: 22px 22px;
  padding: 0 24px clamp(16px, 3.4vh, 40px);
}

.rail {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  max-width: 1480px;
  margin: 0 auto;
  padding: clamp(14px, 2.6vh, 34px) 36px;
}

/* A link on both pages, and a target a thumb can hit: the padding takes it to
   44px, the matching negative margin gives the space back, so the mark sits
   exactly where it did and the rail is no taller. */
.lockup {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  margin: -8px -10px;
  border-radius: 10px;
  color: var(--ink);
  text-decoration: none;
}
.lockup:hover .wordmark {
  text-decoration: underline;
  text-decoration-color: var(--pink);
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
}
.lockup:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 0;
}

.mark {
  width: 28px;
  height: 28px;
  flex: none;
  border-radius: 7px;
  background: var(--ink);
  color: var(--lime);
  font-family: var(--display);
  font-size: 15px;
  line-height: 28px;
  font-weight: 700;
  text-align: center;
}

.wordmark {
  font-family: var(--display);
  font-size: 21px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 13px;
  border: 2px solid var(--ink);
  border-radius: 999px;
  background: var(--pink);
  color: var(--cream);
}

.pill-dot {
  width: 7px;
  height: 7px;
  flex: none;
  border-radius: 50%;
  background: var(--cream);
  animation: pulse 1.4s ease-in-out infinite;
}

.pitch {
  max-width: 1100px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Everything in here is spaced by one number, so the rhythm cannot drift
     apart the way four separate margins did. */
  gap: var(--rhythm);
  text-align: center;
}

.eyebrow {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--ink);
}

.eyebrow img {
  height: 22px;
  width: auto;
  display: block;
  /* The bag in the wordmark stands taller than the word beside it, so the
     image's midpoint is not the word's: centred on the line, "shopify" sits
     about two pixels below the capitals. Measured from the asset — the
     word's x-height runs rows 25–51 of 64, centred on 38, not 32. */
  translate: 0 -2px;
}

h1 {
  margin: 0;
  font-family: var(--display);
  /* A rem in the middle term so a raised base font size still moves it; pure
     vw would pin the headline to the viewport and ignore the reader. Max is
     2.07x the min, inside the 2.5x that keeps zoom usable. */
  font-size: clamp(1.875rem, 1rem + 3.2vw, 3.875rem);
  line-height: 1.1;
  font-weight: 700;
  letter-spacing: -0.03em;
  text-wrap: balance;
}

/* Held at two lines so the form sits in the same place whether or not the
   confirmation has replaced it. */
.lede {
  margin: 0;
  max-width: 680px;
  font-size: 17px;
  line-height: 26px;
  color: var(--ink-2);
  text-wrap: pretty;
}

/* --------------------------------------------------------------- the form */

.signup {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  column-gap: 12px;
  /* No row gap: the only thing on a second row is usually Turnstile, rendering
     nothing at all. The messages below carry their own spacing instead. */
  row-gap: 0;
}

.field {
  width: 300px;
  height: var(--control);
  padding: 0 18px;
  border: 2px solid var(--ink);
  border-radius: 10px;
  background: #ffffff;
  color: var(--ink);
  font-family: inherit;
  font-size: 15px;
}
.field::placeholder {
  color: var(--text-3);
}
.field:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 2px;
}

/* `:user-invalid`, not `:invalid` — the address is only wrong once the visitor
   has left the field or tried to send it. Typing `a@` is not an error yet. */
.field:user-invalid {
  border-color: var(--pink);
}

.btn {
  height: var(--control);
  padding: 0 28px;
  border: 2px solid var(--ink);
  border-radius: 10px;
  background: var(--ink);
  color: var(--lime);
  font-family: var(--display);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.08em;
  cursor: pointer;
  /* `translate` rather than `transform`, so nothing here has to know about
     any other transform the element might pick up later. */
  transition:
    background-color 140ms ease,
    translate 90ms ease;
}
.btn:hover {
  background: var(--ink-2);
}
/* A button is pressed, not inflated. Lifting it and growing a shadow read as
   the thing swelling under the cursor, which is what felt wrong. */
.btn:active {
  translate: 0 1px;
}
.btn:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 3px;
}
.btn:disabled {
  opacity: 0.55;
  cursor: progress;
  translate: none;
}

/* Turnstile lives inside the form so it leaves with it, and takes a line of its
   own so a real challenge has somewhere to go. In interaction-only mode that
   line is zero pixels tall, which is why the row gap above is zero. */
.turnstile {
  flex-basis: 100%;
}

/* Both messages sit on their own line under the row. `flex-basis: 100%` is
   what puts them there; being a sibling of the input is what lets the input's
   own state reveal one of them without a class. */
.field-error,
.form-error {
  flex-basis: 100%;
  margin: 12px 0 0;
  font-size: 14px;
  line-height: 20px;
  color: var(--ink);
  text-align: center;
}

.field-error {
  display: none;
}
.field:user-invalid ~ .field-error {
  display: block;
}

/* A dot rather than a warning glyph: the pink already carries the alarm, and
   the shape survives a screenshot in greyscale. */
.field-error::before,
.form-error::before {
  content: "";
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-right: 8px;
  border-radius: 50%;
  background: var(--pink);
  vertical-align: 1px;
}

/* The confirmation takes the row the form occupied — same height, same place,
   so nothing else on the page moves when it arrives. */
/* Matching the form's height is what keeps the page still when one replaces
   the other, so it is the same variable rather than the same number. */
.done {
  min-height: var(--control);
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 26px;
  border-radius: 10px;
  background: var(--ink);
  color: var(--cream);
  text-align: left;
}

.done-tick {
  width: 24px;
  height: 24px;
  flex: none;
  border-radius: 50%;
  background: var(--lime);
  display: flex;
  align-items: center;
  justify-content: center;
}

.done b {
  font-family: var(--display);
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.done span {
  font-size: 15px;
  color: rgba(255, 249, 235, 0.82);
}

[hidden] {
  display: none !important;
}

/* -------------------------------------------------------------- the claims */

.claims {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  column-gap: 14px;
  row-gap: 8px;
  margin: clamp(10px, 2vh, 22px) auto 0;
  padding: 0 24px;
  color: var(--text-2);
}

.claims i {
  width: 6px;
  height: 6px;
  flex: none;
  background: var(--lime);
}

/* --------------------------------------------------------------- the scene */

/* The sun is a disc whose centre sits below the horizon, so the horizon cuts
   it. `overflow: hidden` here is what does the cutting. */
.scene {
  position: relative;
  flex: 1 0 auto;
  min-height: var(--sun-visible);
  margin-top: clamp(10px, 2.6vh, 32px);
  overflow: hidden;
}

.sun {
  position: absolute;
  left: 50%;
  bottom: calc(var(--sun-visible) - var(--sun));
  width: var(--sun);
  height: var(--sun);
  transform: translateX(-50%);
  border-radius: 50%;
  background: var(--lime);
}

.city {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: var(--city-w);
  height: auto;
  transform: translateX(-50%);
  display: block;
}

.rider {
  position: absolute;
  left: 50%;
  bottom: -5px;
  width: var(--rider-w);
  height: var(--rider-h);
  transform: translateX(-50%);
  background: url("drafts/rider-sheet.png") 0 0 / calc(var(--rider-w) * 6) calc(var(--rider-h) * 3) no-repeat;
  image-rendering: pixelated;
  animation: ride var(--ride) step-end infinite;
}

.horizon {
  height: 3px;
  background: var(--ink);
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 16%, #000 84%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 16%, #000 84%, transparent 100%);
}

/* Marks running the other way, so he is travelling rather than pedalling on
   the spot. */
.road {
  height: 3px;
  margin-top: 10px;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 20%, #000 80%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 20%, #000 80%, transparent 100%);
}

.road div {
  display: flex;
  gap: 46px;
  width: max-content;
  animation: road var(--road) linear infinite;
}

.road i {
  width: 34px;
  height: 3px;
  flex: none;
  background: var(--ink);
  opacity: 0.22;
}

footer {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: clamp(10px, 2.2vh, 26px) 24px clamp(12px, 2.6vh, 30px);
  color: var(--text-3);
}
footer i {
  width: 4px;
  height: 4px;
  flex: none;
  background: var(--text-3);
  opacity: 0.6;
}
footer a {
  color: var(--text-3);
}
footer a:hover {
  color: var(--ink);
}

/* ------------------------------------------------------------- a document */

/* A page that is words: the privacy notice. No band, no scene — the rail on
   the cream ground, a measure of prose, the footer. Sized to read, not to fit
   a screen, so nothing here is clamped to the viewport height. */
.doc {
  min-height: 100svh;
}
.doc .rail {
  padding: clamp(18px, 3vh, 34px) 36px;
}
.prose {
  max-width: 640px;
  margin: 0 auto;
  padding: clamp(16px, 4vh, 40px) 24px clamp(32px, 6vh, 64px);
  flex: 1 0 auto;
  font-size: 16px;
  line-height: 26px;
  color: var(--ink-2);
}
.prose h1 {
  margin: 0;
  font-family: var(--display);
  font-size: clamp(1.75rem, 1rem + 2.2vw, 2.625rem);
  line-height: 1.1;
  font-weight: 700;
  letter-spacing: -0.03em;
  color: var(--ink);
}
.prose .date {
  margin: 10px 0 28px;
  color: var(--text-3);
}
.prose h2 {
  margin: 34px 0 8px;
  font-family: var(--display);
  font-size: 20px;
  line-height: 26px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.prose p {
  margin: 0 0 14px;
  text-wrap: pretty;
}
.prose ul {
  margin: 0 0 14px;
  padding-left: 22px;
}
.prose li {
  margin-bottom: 6px;
}
.prose b {
  font-weight: 650;
  color: var(--ink);
}
/* In running text a link is underlined, or it is indistinguishable from
   emphasis. The pink is the same one the rest of the site uses for attention. */
.prose a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--pink);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}
.prose a:hover {
  color: var(--pink);
}


/* ------------------------------------------------------------- the phone */

@media (max-width: 860px) {
  .band {
    padding: 0 20px 30px;
  }
  .rail {
    padding: 22px 0;
  }
  .mark {
    width: 24px;
    height: 24px;
    border-radius: 6px;
    font-size: 13px;
    line-height: 24px;
  }
  .wordmark {
    font-size: 17px;
  }
  .pill {
    padding: 4px 10px;
  }
  .eyebrow img {
    height: 18px;
  }

  .lede {
    font-size: 15px;
    line-height: 23px;
  }

  .signup {
    width: 100%;
    flex-direction: column;
    /* Still zero, for the same reason as the desktop rule: the stack spaces
       itself below, so Turnstile's empty line stays free. */
    row-gap: 0;
  }
  .field,
  .btn {
    width: 100%;
  }
  .btn {
    margin-top: 10px;
  }

  .done {
    min-height: calc(var(--control) * 2 + 10px);
    padding: 16px 18px;
    align-items: flex-start;
    gap: 10px;
  }
  .done-tick {
    width: 22px;
    height: 22px;
    margin-top: 1px;
  }
  /* The tick keeps its own column; the words wrap in the one beside it. */
  .done-text {
    min-width: 0;
  }
  .done b {
    font-size: 15px;
  }
  .done span {
    font-size: 14px;
    line-height: 22px;
  }

}

/* A short viewport — an older phone, or a desktop window dragged down. Every
   fluid value gives a little more so the rider stays above the fold; the
   footer is what goes under it, which is the right thing to lose. */
@media (max-height: 780px) {
  :root {
    --scene: clamp(112px, min(24vh, 30vw), 280px);
    --rhythm: clamp(9px, 1.8vh, 18px);
    --control: clamp(44px, 6vh, 54px);
  }
  .lede {
    font-size: 15px;
    line-height: 22px;
  }
}

/* Shorter still — a 4.7-inch phone, or a very squat window. On a narrow screen
   it is the width term that binds the scene, so that is what gives here. */
@media (max-height: 700px) {
  :root {
    --scene: clamp(88px, min(22vh, 24vw), 280px);
    --rhythm: clamp(8px, 1.6vh, 18px);
  }
  .lede {
    font-size: 14px;
    line-height: 20px;
  }
}

/* The rider is the page. Slowing him is honest where stopping him is not —
   and a `transitionend` nothing is waiting on still fires. */
@media (prefers-reduced-motion: reduce) {
  .rider {
    animation-duration: 4s;
  }
  .road div {
    animation-duration: 4s;
  }
  .pill-dot {
    animation-duration: 6s;
  }
  .btn {
    transition: none;
  }
}

@keyframes pulse {
  0%,
  100% {
    opacity: 0.4;
  }
  50% {
    opacity: 1;
  }
}

@keyframes road {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-80px);
  }
}

/* One pedal revolution: 18 cells, 6 across by 3 down. */
@keyframes ride {
  0.0000% { background-position: calc(var(--rider-w) * -0) calc(var(--rider-h) * -0); }
  5.5556% { background-position: calc(var(--rider-w) * -1) calc(var(--rider-h) * -0); }
  11.1111% { background-position: calc(var(--rider-w) * -2) calc(var(--rider-h) * -0); }
  16.6667% { background-position: calc(var(--rider-w) * -3) calc(var(--rider-h) * -0); }
  22.2222% { background-position: calc(var(--rider-w) * -4) calc(var(--rider-h) * -0); }
  27.7778% { background-position: calc(var(--rider-w) * -5) calc(var(--rider-h) * -0); }
  33.3333% { background-position: calc(var(--rider-w) * -0) calc(var(--rider-h) * -1); }
  38.8889% { background-position: calc(var(--rider-w) * -1) calc(var(--rider-h) * -1); }
  44.4444% { background-position: calc(var(--rider-w) * -2) calc(var(--rider-h) * -1); }
  50.0000% { background-position: calc(var(--rider-w) * -3) calc(var(--rider-h) * -1); }
  55.5556% { background-position: calc(var(--rider-w) * -4) calc(var(--rider-h) * -1); }
  61.1111% { background-position: calc(var(--rider-w) * -5) calc(var(--rider-h) * -1); }
  66.6667% { background-position: calc(var(--rider-w) * -0) calc(var(--rider-h) * -2); }
  72.2222% { background-position: calc(var(--rider-w) * -1) calc(var(--rider-h) * -2); }
  77.7778% { background-position: calc(var(--rider-w) * -2) calc(var(--rider-h) * -2); }
  83.3333% { background-position: calc(var(--rider-w) * -3) calc(var(--rider-h) * -2); }
  88.8889% { background-position: calc(var(--rider-w) * -4) calc(var(--rider-h) * -2); }
  94.4444% { background-position: calc(var(--rider-w) * -5) calc(var(--rider-h) * -2); }}

/* Reachable by a screen reader, absent for everyone else. The email field's
   placeholder is not a label. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
