/*
 * OpenEssayMe maintenance page: "Words Take Flight".
 *
 * The static layout comes first. It is the finished page, and it is what
 * visitors see with no JavaScript, with reduced motion, and on screens too
 * short for the film. The scroll film layers on top only when js/flight.js
 * adds the "film" class to <html>, and only inside the media query below.
 *
 * Contrast (WCAG): every text color here passes AA on the navy page, and
 * also on the lightest background any text can sit on (navy plus the faint
 * page glow and a ruled line). The full table is in maintenance/CLAUDE.md.
 */

/* Plus Jakarta Sans, variable weight, Latin subset. SIL Open Font License 1.1,
   see /fonts/OFL.txt. Self-hosted: no font CDN. */
@font-face {
  font-family: "Plus Jakarta Sans";
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url("/fonts/plus-jakarta-sans-latin-wght-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* Brand colors, sampled from the logo (memory/design-kit.md). */
  --navy: #0D263E;   /* page */
  --sky: #45ADEF;    /* quill blue: brand word, link */
  --pale: #D9E7F5;   /* pale highlight: body copy, focus ring */
  --white: #FFFFFF;  /* pages of the quill: headline */
  --muted: #B4C7DA;  /* quiet text: footer */

  --pad-x: clamp(1.25rem, 5vw, 3rem);
  --margin-line: clamp(0.75rem, 4vw, 3.5rem);
  --rule: 2rem;
}

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

html {
  background-color: var(--navy);
  color-scheme: dark;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  font-family: "Plus Jakarta Sans", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  font-weight: 400;
  line-height: 1.5;
  color: var(--white);
  background-color: var(--navy);
  /* A sheet of writing paper: a soft glow from the top, a margin line,
     and faint ruled lines. All three stay faint so text contrast holds. */
  background-image:
    radial-gradient(120% 65% at 50% 0%, rgba(69, 173, 239, 0.06), rgba(69, 173, 239, 0) 70%),
    linear-gradient(90deg,
      transparent calc(var(--margin-line) - 1px),
      rgba(69, 173, 239, 0.12) calc(var(--margin-line) - 1px),
      rgba(69, 173, 239, 0.12) var(--margin-line),
      transparent var(--margin-line)),
    repeating-linear-gradient(180deg,
      transparent 0,
      transparent calc(var(--rule) - 1px),
      rgba(217, 231, 245, 0.05) calc(var(--rule) - 1px),
      rgba(217, 231, 245, 0.05) var(--rule));
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

::selection {
  background: var(--sky);
  color: var(--navy);
}

/* ---------- Layout (static, finished state) ---------- */

.track {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
}

.stage {
  flex: 1 0 auto;
  position: relative;
  display: grid;
  place-items: center;
  padding:
    clamp(2.5rem, 8vh, 5rem)
    max(var(--pad-x), env(safe-area-inset-right))
    clamp(2rem, 6vh, 4rem)
    max(var(--pad-x), env(safe-area-inset-left));
}

.content {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 46rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

/* The logo keeps the mark's exact aspect ratio (viewBox 627 by 405), so the
   script can map logo coordinates to the screen with one scale factor. The
   width also shrinks on short screens so the whole page fits in one view. */
.mark-wrap {
  position: relative;
  width: clamp(9.5rem, 54vw, 23.75rem);
  width: min(clamp(9.5rem, 54vw, 23.75rem), 49.5vh);
  width: min(clamp(9.5rem, 54vw, 23.75rem), 49.5svh);
  aspect-ratio: 627 / 405;
  margin: 0 0 clamp(1.5rem, 4.5vh, 2.75rem);
}

.mark {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

/* Soft light behind the quill. It fades to nothing at the logo box edges,
   so it never sits behind any text. */
.glow {
  position: absolute;
  inset: -18% -32% 0;
  z-index: -1;
  background: radial-gradient(closest-side, rgba(69, 173, 239, 0.2), rgba(69, 173, 239, 0.08) 55%, rgba(69, 173, 239, 0) 100%);
  pointer-events: none;
}

.headline {
  position: relative;
  z-index: 2;
  margin: 0;
  max-width: 100%;
  font-size: clamp(2rem, 1.1rem + 3.4vw, 4rem);
  font-weight: 800;
  line-height: 1.06;
  letter-spacing: -0.028em;
  text-wrap: balance;
  overflow-wrap: break-word;
}

.brand-word,
.headline .ch.is-brand {
  color: var(--sky);
}

.lede {
  margin: clamp(1rem, 2.6vh, 1.5rem) 0 0;
  max-width: 34ch;
  font-size: clamp(1.0625rem, 0.98rem + 0.4vw, 1.3125rem);
  font-weight: 450;
  line-height: 1.55;
  color: var(--pale);
  text-wrap: balance;
}

.contact {
  margin: clamp(1.1rem, 3vh, 1.75rem) 0 0;
  font-size: clamp(1rem, 0.96rem + 0.2vw, 1.0625rem);
  line-height: 1.5;
  color: var(--pale);
}

.contact a {
  color: var(--sky);
  font-weight: 650;
  text-decoration-line: underline;
  text-decoration-thickness: 0.08em;
  text-underline-offset: 0.24em;
  border-radius: 0.25rem;
  overflow-wrap: anywhere;
  transition: color 0.2s ease, text-decoration-thickness 0.2s ease;
}

.contact a:hover {
  color: var(--white);
  text-decoration-thickness: 0.12em;
}

/* Visible keyboard focus: pale ring, 12.2 to 1 against the navy page. */
a:focus-visible {
  outline: 3px solid var(--pale);
  outline-offset: 4px;
}

.site-footer {
  padding:
    1.5rem
    max(var(--pad-x), env(safe-area-inset-right))
    max(1.5rem, env(safe-area-inset-bottom))
    max(var(--pad-x), env(safe-area-inset-left));
  text-align: center;
  font-size: 0.875rem;
  color: var(--muted);
}

.site-footer p {
  margin: 0;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Film-only pieces are made by the script; hidden unless the film runs. */
.caret,
.nib {
  display: none;
}

/* Measures the small viewport height for the script (never visible). */
.vh-probe {
  position: fixed;
  top: 0;
  left: 0;
  width: 0;
  height: 100vh;
  height: 100svh;
  visibility: hidden;
  pointer-events: none;
}

/* ---------- The scroll film ----------
   Only for screens, only when the visitor allows motion, only when the
   screen is tall enough. js/flight.js uses this exact query too. */
@media screen and (prefers-reduced-motion: no-preference) and (min-height: 480px) {
  /* The track gives the film 200 small-viewport heights of scrolling while
     the stage stays pinned. Small viewport units (svh) keep it steady when a
     phone's address bar hides or shows. */
  .film .track {
    display: block;
    flex: none;
    height: 300vh;
    height: calc(100svh + 200svh);
  }

  .film .stage {
    position: sticky;
    top: 0;
    height: 100vh;
    height: 100svh;
    overflow: hidden;
  }

  /* Hold the content back until the script has set the opening frame.
     Failsafe: show it after 4 seconds even if the script never finishes. */
  .film:not(.is-ready) .content {
    visibility: hidden;
    animation: oe-failsafe-show 0s linear 4s forwards;
  }

  @keyframes oe-failsafe-show {
    to {
      visibility: visible;
    }
  }

  .film .headline .letters {
    -webkit-user-select: none;
    user-select: none;
  }

  .film .headline .word {
    display: inline-block;
    white-space: nowrap;
  }

  .film .headline .ch {
    display: inline-block;
    /* The letter's visual middle; js/flight.js uses the same point (ANCHOR_Y). */
    transform-origin: 50% 60%;
    will-change: transform, opacity;
  }

  /* The supporting lines settle in once the headline is typed. */
  .film .lede,
  .film .contact {
    opacity: 0;
    transform: translate3d(0, 0.75rem, 0);
    transition:
      opacity 1s cubic-bezier(0.2, 0.7, 0.2, 1),
      transform 1s cubic-bezier(0.2, 0.7, 0.2, 1);
  }

  .film.is-typed .lede,
  .film.is-typed .contact {
    opacity: 1;
    transform: none;
  }

  .film.is-typed .contact {
    transition-delay: 0.18s;
  }

  /* Failsafe: if typing never finishes (the script stalls), bring the
     supporting lines in after 4 seconds anyway. It only applies while
     .is-typed is missing, so it never fights the normal reveal: when
     .is-typed arrives first, this animation is removed before it starts;
     when it arrives later, the lines are already at full opacity and the
     .is-typed rule holds the same values. */
  .film:not(.is-typed) .lede,
  .film:not(.is-typed) .contact {
    animation: oe-failsafe-reveal 0.8s cubic-bezier(0.2, 0.7, 0.2, 1) 4s forwards;
  }

  @keyframes oe-failsafe-reveal {
    to {
      opacity: 1;
      transform: none;
    }
  }

  /* The blueprint: dashed outlines of the quill, waiting to be inked. */
  .film .sketch {
    opacity: 0;
    transition: opacity 1.4s ease;
  }

  .film.is-ready .sketch {
    opacity: 0.7;
  }

  /* Typing caret. Its font size is set by the script to match the headline. */
  .film .caret {
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 3;
    width: 0.07em;
    height: 0.84em;
    border-radius: 1px;
    background: var(--sky);
    opacity: 0;
    pointer-events: none;
  }

  /* Blinks four times once typing ends, then stays still. */
  .film .caret.is-blinking {
    animation: oe-caret-blink 1.05s steps(1, end) 4;
  }

  @keyframes oe-caret-blink {
    50% {
      visibility: hidden;
    }
  }

  /* The nib: a point of ink that leads the headline as the quill rewrites it. */
  .film .nib {
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 3;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--sky);
    box-shadow: 0 0 0 3px rgba(69, 173, 239, 0.18), 0 0 14px 3px rgba(69, 173, 239, 0.55);
    opacity: 0;
    pointer-events: none;
    will-change: transform, opacity;
  }
}
