/* modules/common/splashScreen.css — the cold-start splash overlay owned by splashScreen.js.

   It paints the SAME background the app itself uses — --bg-color plus --bg-gradient, exactly as
   index.css sets on <body> — rather than a fixed colour. theme-boot.js is a render-blocking classic
   script in <head>, so the theme class is already on <body> before this ever paints; the splash
   therefore comes up in the user's chosen theme, and fading it out reveals an identical background
   underneath instead of a colour change. A hardcoded dark plate would have been a black rectangle
   on the daylight and blossom themes.

   Everything animates in on a stagger — mark, wordmark, tagline, then the progress sweep — so the
   4s hold reads as a considered intro rather than a frozen image. All of it is CSS: no rAF loop, no
   JS-driven animation, nothing that competes with the boot happening behind it. The whole thing is
   cut to a static frame under prefers-reduced-motion (bottom of file).

   Styles live here rather than in index.css so the overlay ships with its module, and are loaded in <head> — a splash that paints after its own stylesheet arrives would
   flash unstyled markup, which is the one thing it exists to prevent. */

.app-splash {
  position: fixed;
  inset: 0;
  z-index: 9000; /* above the app; below #integrity-error-overlay, which must always win */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding: 24px 32px;
  text-align: center;
  /* The trainer's details step is a four-field form, which on a short phone is taller than the
     viewport. Without this the overflow is simply cut off — and what gets cut is the bottom, where
     the save button is. Vertical padding replaces the old `0 32px` for the
     same reason: centred content that overflows needs room at both ends, or the first line goes
     under the top edge. */
  overflow-y: auto;
  background-color: var(--bg-color);
  background-image: var(--bg-gradient);
  background-attachment: fixed;
  background-repeat: no-repeat;
  opacity: 1;
  transition: opacity 320ms ease-out; /* FADE_OUT_MS in splashScreen.js */
}

/* Removed from the layout once faded, so it can never intercept a tap afterwards. */
.app-splash[hidden] {
  display: none;
}

.app-splash.is-dismissing {
  opacity: 0;
  pointer-events: none;
}

/* A soft emerald bloom behind the mark, sized off the mark itself. Drawn as a pseudo-element rather
   than a box-shadow so it can be much larger than the artwork without affecting layout. */
.app-splash::before {
  content: "";
  position: absolute;
  width: min(78vw, 420px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(
    circle,
    var(--primary-light, rgba(16, 185, 129, 0.14)) 0%,
    transparent 68%
  );
  transform: translateY(-8%);
  animation: splash-bloom 3600ms ease-in-out infinite;
  pointer-events: none;
}

/* The one place the FULL clipboard mark is used: at this size the composition actually reads,
   unlike the 26px header slot and the browser tab, which both get the whistle crop instead. */
.app-splash-mark {
  position: relative;
  width: min(46vw, 196px);
  height: auto;
  filter: drop-shadow(0 18px 34px rgba(0, 0, 0, 0.28))
    drop-shadow(0 0 22px var(--primary-light, rgba(16, 185, 129, 0.35)));
  animation: splash-rise 760ms cubic-bezier(0.22, 1, 0.36, 1) both;
}

.app-splash-name {
  position: relative;
  margin: 0;
  font-family: var(--font-header);
  font-size: clamp(30px, 8vw, 38px);
  font-weight: 700;
  letter-spacing: -0.5px;
  line-height: 1.1;
  color: var(--text-main);
  animation: splash-rise 760ms cubic-bezier(0.22, 1, 0.36, 1) 120ms both;
}

/* "PT" in the accent, so the wordmark carries the same emerald as the mark above it. */
.app-splash-name .app-splash-name-accent {
  color: var(--primary);
}

.app-splash-tagline {
  position: relative;
  margin: 0;
  max-width: 30ch;
  font-size: 15px;
  line-height: 1.55;
  color: var(--text-muted);
  animation: splash-rise 760ms cubic-bezier(0.22, 1, 0.36, 1) 240ms both;
}

/* Indeterminate progress: a sweep rather than a spinner, because there is no percentage to report
   and a spinner at this size reads as "stuck" more than "working". */
.app-splash-progress {
  position: relative;
  width: 132px;
  height: 3px;
  margin-top: 10px;
  border-radius: 999px;
  overflow: hidden;
  background: var(--border-color);
  animation: splash-fade-in 600ms ease-out 420ms both;
}

.app-splash-progress::after {
  content: "";
  position: absolute;
  inset: 0;
  width: 44%;
  border-radius: inherit;
  background: var(--primary-gradient, var(--primary));
  animation: splash-sweep 1500ms cubic-bezier(0.65, 0, 0.35, 1) infinite;
}

/* The escape hatch, top-left. 44x44 because it is a real touch target on a phone even though the
   glyph inside it is small — and it sits inside the safe-area inset so it is
   not under a notch or a rounded corner on a device with one. */
.app-splash-dismiss {
  position: absolute;
  top: max(12px, env(safe-area-inset-top));
  left: max(12px, env(safe-area-inset-left));
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--text-muted);
  font-size: 18px;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}

/* Required, not redundant: `display: flex` above outranks the UA stylesheet's `[hidden]` rule, so
   without this the hidden attribute silently does nothing and the button stays on screen. */
.app-splash-dismiss[hidden] {
  display: none;
}

.app-splash-dismiss:hover,
.app-splash-dismiss:focus-visible {
  background: var(--card-bg);
  color: var(--text-main);
  outline: none;
}

/* Language choice — the first thing shown when nobody has picked one, ahead of the hold and ahead
   of any onboarding. Shares .app-splash-action with the onboarding buttons so the two steps look
   like one flow rather than two designs. */
.app-splash-language,
.app-splash-step {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 12px;
  width: min(100%, 320px);
  margin-top: 8px;
  animation: splash-rise 520ms cubic-bezier(0.22, 1, 0.36, 1) both;
}

.app-splash-language[hidden],
.app-splash-step[hidden] {
  display: none;
}

.app-splash-language-prompt,
.app-splash-step-prompt {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-muted);
}

/* Sized for a registry, not for two. Two languages sit side by side; twenty-four wrap into a grid
   that scrolls inside the splash instead of pushing the prompt off the top of a phone. The minimum
   column is wide enough for the longest language name written in itself, and the maximum height
   leaves the prompt above it on screen at all times. */
.app-splash-language-options,
.app-splash-step-options {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 10px;
  max-height: 46vh;
  overflow-y: auto;
}

/* Each still gets a full-height target, whatever the column count is. */
.app-splash-language-options .app-splash-action,
.app-splash-step-options .app-splash-action {
  width: 100%;
}

/* The theme the trainer tapped: the one they see around them, marked in words for a screen reader
   (aria-pressed) and by the border for the eye. */
.app-splash-action[aria-pressed="true"] {
  border-color: var(--primary);
  box-shadow: 0 0 0 2px var(--primary);
}

.app-splash-action:disabled {
  opacity: 0.5;
  cursor: default;
}

/* Onboarding entry point — replaces the progress sweep once the hold is over and the trainer has
   no data yet. Buttons are full-width and 48px tall: this is the first thing a new user touches,
   often one-handed on a phone, so the targets are sized for a thumb rather than for the text. */
.app-splash-onboarding {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
  width: min(100%, 320px);
  margin-top: 8px;
  animation: splash-rise 520ms cubic-bezier(0.22, 1, 0.36, 1) both;
}

.app-splash-onboarding[hidden] {
  display: none;
}

/* The `?demo=chapters` screen hides the two other offers. Required for the same reason as the X's
   rule: `.app-splash-action`'s `display: flex` outranks the UA stylesheet's `[hidden]` rule. */
.app-splash-onboarding .app-splash-action[hidden],
.app-splash-onboarding .app-splash-skip[hidden] {
  display: none;
}

/* The trainer's details, a first-run step of its own. Left-aligned against the splash's centred
   text: a form whose labels are centred is a form nobody can scan. */
.app-splash-details {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
  width: 100%;
  text-align: left;
}

.app-splash-details-lede {
  margin: 0;
  font-size: 13px;
  color: var(--text-muted);
}

.app-splash-action {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  min-height: 48px;
  padding: 0 18px;
  border: 1px solid var(--card-border);
  border-radius: 14px;
  background: var(--card-bg);
  color: var(--text-main);
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  transition: transform 0.12s ease, border-color 0.12s ease;
}

.app-splash-action:hover:not(:disabled),
.app-splash-action:focus-visible:not(:disabled) {
  transform: translateY(-1px);
  border-color: var(--primary);
  outline: none;
}

.app-splash-action-primary {
  border-color: transparent;
  background: var(--primary-gradient, var(--primary));
  color: var(--on-primary, #fff);
}

.app-splash-skip {
  padding: 10px 8px; /* a real target, not just the 14px of text */
  border: 0;
  background: none;
  color: var(--text-muted);
  font-family: inherit;
  font-size: 14px;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.app-splash-skip:hover,
.app-splash-skip:focus-visible {
  color: var(--text-main);
  outline: none;
}

/* While onboarding, the tagline is the pitch rather than a loading caption, so it gets room. */
.app-splash.is-onboarding .app-splash-tagline {
  margin-bottom: 4px;
}

@keyframes splash-rise {
  from {
    opacity: 0;
    transform: translateY(14px) scale(0.96);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

@keyframes splash-fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes splash-bloom {
  0%,
  100% {
    opacity: 0.75;
    transform: translateY(-8%) scale(1);
  }
  50% {
    opacity: 1;
    transform: translateY(-8%) scale(1.06);
  }
}

@keyframes splash-sweep {
  from {
    transform: translateX(-115%);
  }
  to {
    transform: translateX(330%);
  }
}

/* Reduced motion: the hold still happens and the layout is identical, but nothing moves — no
   entrance stagger, no bloom pulse, no sweep, and the dismissal cuts instead of fading. */
@media (prefers-reduced-motion: reduce) {
  .app-splash,
  .app-splash::before,
  .app-splash-mark,
  .app-splash-name,
  .app-splash-tagline,
  .app-splash-progress,
  .app-splash-progress::after,
  .app-splash-language,
  .app-splash-step,
  .app-splash-onboarding {
    animation: none;
    transition: none;
  }
}

/* The guided walkthrough's table of contents, under the button that starts it from the beginning
   (asked for 2026-09-21). Open, never folded: it says what the four to six minutes contain, and a
   fold would be an offer nobody can see.

   The rows are full-width 44px targets for the same reason the buttons above them are 48px: this is
   a phone, held in one hand. The numbers come from the <ol>, so the list says how long the story is
   without a word about it. The list scrolls inside the splash rather than pushing the three choices
   above it off the top. */
.app-splash-chapters {
  width: 100%;
  text-align: left;
}

.app-splash-chapters[hidden] {
  display: none;
}

.app-splash-chapters-heading {
  margin: 0;
  padding: 10px 4px 4px;
  color: var(--text-muted);
  font-size: 14px;
  font-weight: 600;
}

.app-splash-chapter-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 0;
  padding: 0 0 0 22px;
  max-height: 38vh;
  overflow-y: auto;
}

.app-splash-chapter {
  width: 100%;
  min-height: 44px;
  padding: 0 10px;
  border: 1px solid transparent;
  border-radius: 10px;
  background: none;
  color: var(--text-main);
  font-family: inherit;
  font-size: 14px;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease;
}

.app-splash-chapter:hover,
.app-splash-chapter:focus-visible {
  border-color: var(--card-border);
  background: var(--card-bg);
}

/* A short phone has no room for the full mark above a four-field form. On 320x680 — the smallest
   screen the app supports — the details step's save button used to end 42px below the bottom edge
   with only its top border line showing, and the page itself does not scroll (the splash does), so
   the screen looked finished without its only button. Height comes back from the decoration and the
   rhythm; every word stays. */
@media (max-height: 720px) {
  .app-splash {
    gap: 10px;
    padding: 16px 20px;
  }

  .app-splash-mark {
    width: min(28vw, 104px);
  }

  .app-splash-name {
    font-size: clamp(22px, 6vw, 28px);
  }

  .app-splash-language,
  .app-splash-step {
    gap: 8px;
    margin-top: 0;
  }

  .app-splash-details {
    gap: 8px;
  }
}
