:root {
  --surface: #FAFAF7;
  --on-surface: #1C1C1A;
  --on-surface-muted: #5F5F5C;
  --font-display: 'Outfit', system-ui, sans-serif;
  --font-body: 'Nunito Sans', system-ui, sans-serif;
}

*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; height: 100%; }

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  background: var(--surface);
  color: var(--on-surface);
  font-family: var(--font-body);
  font-variation-settings: 'YTLC' 500, 'wdth' 100;
  -webkit-font-smoothing: antialiased;
  text-wrap: pretty;
}

.ss-hero {
  margin: auto;
  width: 100%;
  max-width: 480px;
  padding: 64px 24px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  text-align: center;
}

.ss-mark { display: block; }

.ss-title {
  margin: 0;
  font: 800 clamp(34px, 7vw, 48px) / 1.15 var(--font-display);
  letter-spacing: -1px;
}

.ss-lead {
  margin: 0;
  font: 400 18px / 28px var(--font-body);
  color: var(--on-surface-muted);
}

.ss-soon {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  background: rgba(168, 216, 245, 0.22);
  color: var(--on-surface);
  padding: 12px 22px;
  border-radius: 999px;
  font: 600 16px/1 var(--font-body);
}
.ss-soon-icon { width: 20px; height: 20px; flex: none; color: #1F6F9F; }

/* The hero keeps `margin: auto`, so it stays optically centred and soaks up
   the free space; this footer is left sitting on the bottom edge. */
.ss-foot {
  flex: none;
  padding: 24px;
  text-align: center;
}

/* A quiet text link, not a second pill — the hero already has one, and the
   changelog is a side road off it rather than a rival to the main message.
   #1F6F9F is the same accent the badge's icon and the changelog footer's
   links use. */
.ss-link {
  display: inline-flex;
  align-items: center;
  border-radius: 4px;
  color: #1F6F9F;
  font: 600 15px/1 var(--font-body);
  text-decoration: none;
}
.ss-link:hover { text-decoration: underline; }
.ss-link:focus-visible {
  outline: 2px solid #2397DD;
  outline-offset: 4px;
  box-shadow: 0 0 0 4px rgb(28 28 26 / 0.45);
}

.ss-signup {
  width: 100%;
  max-width: 400px;
  margin-top: 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}

.ss-hp {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.ss-chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
}

.ss-chip {
  appearance: none;
  cursor: pointer;
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  border: none;
  border-radius: 12px;
  padding: 10px 16px;
  background: #E6E2D9;
  color: var(--on-surface);
  font: 600 14px/1 var(--font-body);
  transition: background 140ms cubic-bezier(0.3, 0.9, 0.4, 1),
    transform 200ms cubic-bezier(0.3, 0.9, 0.4, 1);
}
.ss-chip:hover { background: #DAD6CD; }
.ss-chip:active { transform: scale(0.97); }
.ss-chip:disabled { opacity: 0.45; cursor: default; }

/* Click-origin fill — grows from the point that was clicked rather than
   flooding the whole chip. --rx/--ry/--ripple-r are set in JS from the
   click position and the distance to the chip's furthest corner, so the
   circle is always exactly big enough to cover the chip regardless of
   where it was clicked. Matches Porthole's shared/ripple.css technique. */
.ss-chip::before {
  content: '';
  position: absolute;
  left: var(--rx, 50%);
  top: var(--ry, 50%);
  width: calc(2 * var(--ripple-r, 60px));
  height: calc(2 * var(--ripple-r, 60px));
  border-radius: 50%;
  background: #A8D8F5;
  transform: translate(-50%, -50%) scale(0);
  transition: transform 200ms cubic-bezier(0.3, 0.9, 0.4, 1);
  z-index: -1;
}
.ss-chip[aria-pressed="true"]::before { transform: translate(-50%, -50%) scale(1); }

.ss-chip-icon { width: 18px; height: 18px; flex: none; }

.ss-field-group {
  position: relative;
  display: flex;
  gap: 8px;
  width: 100%;
}
.ss-field-group input[type="email"] {
  flex: 1;
  min-width: 0;
  height: 48px;
  padding: 0 20px;
  border: 1.5px solid rgb(28 28 26 / 0.16);
  border-radius: 999px;
  background: var(--surface);
  color: var(--on-surface);
  font: 400 15px/1 var(--font-body);
}
.ss-field-group input[type="email"]::placeholder { color: var(--on-surface-muted); }
.ss-field-group input[type="email"]:focus-visible {
  outline: 2px solid #2397DD;
  outline-offset: 2px;
  box-shadow: 0 0 0 4px rgb(28 28 26 / 0.45);
}
body.ss-mouse-user .ss-field-group input[type="email"]:focus-visible {
  outline: none;
  box-shadow: none;
}
@media (max-width: 480px) {
  .ss-field-group { flex-direction: column; }
  /* flex:1's flex-basis:0 applies to the main axis, which is now vertical —
     left alone it overrides height:48px and collapses the field down to its
     min-content height instead. */
  .ss-field-group input[type="email"] { flex: none; }
  /* .ss-btn-wrap stretches to the full row width here (the column's cross
     axis is horizontal), but its content-sized button doesn't follow along
     on its own — tell it to match, so it's still full-width like the field
     above it instead of shrinking back to its content size. */
  .ss-btn-wrap { width: 100%; }
  .ss-btn-wrap .ss-btn { width: 100%; }
}

.ss-btn {
  appearance: none;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  border: none;
  height: 48px;
  padding: 0 24px;
  border-radius: 999px;
  background: #A8D8F5;
  color: var(--on-surface);
  font: 600 16px/1 var(--font-body);
  white-space: nowrap;
  transition: opacity 140ms cubic-bezier(0.3, 0.9, 0.4, 1),
    transform 200ms cubic-bezier(0.3, 0.9, 0.4, 1);
}
.ss-btn:hover { opacity: 0.9; }
.ss-btn:active { opacity: 0.82; transform: scale(0.97); }
.ss-btn:focus-visible {
  outline: 2px solid #2397DD;
  outline-offset: 2px;
  box-shadow: 0 0 0 4px rgb(28 28 26 / 0.45);
}
.ss-btn:disabled {
  opacity: 0.45;
  cursor: default;
}
/* No pointer-events:none here — the native disabled attribute already
   blocks clicks/keyboard activation on its own, and suppressing pointer
   events would also suppress :hover, which the tooltip below relies on. */

.ss-btn-wrap {
  position: relative;
  flex: none;
}

.ss-btn-tooltip {
  /* left:0 + right:0 (rather than a fixed width) sizes this to exactly
     match .ss-btn-wrap's own width — i.e. the button's width — so it can
     never spill wider than its anchor and overlap the email field next to
     it, at any viewport width. Sitting below the button rather than above
     it sidesteps that field entirely, whether it's beside the button (row
     layout) or stacked right above it (column layout, an 8px gap away). */
  position: absolute;
  left: 0;
  right: 0;
  top: calc(100% + 10px);
  background: var(--on-surface);
  color: var(--surface);
  padding: 10px 14px;
  border-radius: 10px;
  font: 500 13px/1.5 var(--font-body);
  text-align: center;
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity 140ms cubic-bezier(0.3, 0.9, 0.4, 1),
    transform 140ms cubic-bezier(0.3, 0.9, 0.4, 1);
  pointer-events: none;
  z-index: 2;
}
.ss-btn-tooltip::after {
  content: '';
  position: absolute;
  bottom: 100%;
  right: 50%;
  margin-right: -6px;
  width: 0;
  height: 0;
  border: 6px solid transparent;
  border-bottom-color: var(--on-surface);
  border-top: 0;
}
.ss-btn:disabled:hover ~ .ss-btn-tooltip {
  opacity: 1;
  transform: translateY(0);
}
/* Once confirmed, the button is disabled for an unrelated reason
   (submission already happened) — the "why is this disabled" tooltip no
   longer applies. */
.ss-field-group.ss-confirming .ss-btn-tooltip { display: none; }

.ss-note {
  margin: 0;
  font: 500 13px/18px var(--font-body);
  color: var(--on-surface-muted);
}

/* Confirmation: the email field fades in place while the submit button
   grows leftward to fill the row — its right edge stays put (it was already
   flush with the field-group's right edge in the normal flex layout) and
   only the left edge sweeps out, rather than the whole row re-flowing.
   --ss-btn-start-width is set from the button's own measured width right
   before this class is added, so the grow starts from its true rendered
   size instead of a guessed one. */
.ss-field-group.ss-confirming { height: 48px; }
.ss-field-group.ss-confirming input[type="email"] {
  position: absolute;
  inset: 0;
  animation: ssFieldFadeOut 300ms cubic-bezier(0.3, 0.9, 0.4, 1) forwards;
}
@keyframes ssFieldFadeOut {
  to { opacity: 0; }
}
/* .ss-btn-wrap is only a positioning context for the tooltip in the normal
   state — drop that here so .ss-btn's position:absolute below resolves
   against .ss-field-group instead, letting it grow across the full row
   rather than being boxed into the wrap's own (button-sized) width. */
.ss-field-group.ss-confirming .ss-btn-wrap { position: static; }
.ss-field-group.ss-confirming .ss-btn {
  position: absolute;
  top: 0;
  right: 0;
  width: var(--ss-btn-start-width, 152px);
  animation: ssBtnGrow 420ms cubic-bezier(0.3, 0.9, 0.4, 1) 260ms forwards;
}
@keyframes ssBtnGrow {
  to { width: 100%; }
}
.ss-field-group.ss-confirming .ss-btn:disabled { opacity: 1; }

.ss-btn-label {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 0 24px;
  font: 600 16px/1 var(--font-body);
  color: var(--on-surface);
  white-space: nowrap;
}
.ss-btn-label-old {
  animation: ssLabelExit 240ms cubic-bezier(0.3, 0.9, 0.4, 1) 520ms forwards;
}
.ss-btn-label-new {
  transform: translateY(100%);
  opacity: 0;
  animation: ssLabelEnter 260ms cubic-bezier(0.3, 0.9, 0.4, 1) 680ms forwards;
}
@keyframes ssLabelExit {
  to { transform: translateY(-100%); opacity: 0; }
}
@keyframes ssLabelEnter {
  to { transform: translateY(0); opacity: 1; }
}

.ss-confirm-badge {
  position: relative;
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--on-surface);
  display: flex;
  align-items: center;
  justify-content: center;
  transform: scale(0.5);
  opacity: 0;
  animation: ssBadgePop 320ms cubic-bezier(0.3, 0.9, 0.4, 1) 760ms forwards;
}
.ss-confirm-check {
  width: 12px;
  height: 12px;
  fill: none;
  stroke: #A8D8F5;
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 20;
  stroke-dashoffset: 20;
  animation: ssCheckDraw 300ms ease-out 980ms forwards;
}
@keyframes ssBadgePop {
  to { transform: scale(1); opacity: 1; }
}
@keyframes ssCheckDraw {
  to { stroke-dashoffset: 0; }
}

.ss-error {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  background: rgb(250 108 140 / 0.20);
  border: 1px solid rgb(227 87 121 / 0.26);
  color: var(--on-surface);
  padding: 12px 18px;
  border-radius: 12px;
  font: 600 14px/20px var(--font-body);
}
.ss-error svg { flex: none; width: 20px; height: 20px; color: #7D1C0D; }

/* Not an error and nothing was just accomplished — a plain informational
   notice you land on, not a celebratory or alarming one, so it gets its own
   calm, neutral treatment rather than reusing .ss-error's coral or the
   confirmation's animated button-grow. */
.ss-closed {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  background: rgba(168, 216, 245, 0.18);
  border: 1px solid rgba(35, 151, 221, 0.3);
  color: var(--on-surface);
  padding: 14px 20px;
  border-radius: 12px;
  font: 600 15px/22px var(--font-body);
}
.ss-closed svg { flex: none; width: 20px; height: 20px; color: #1F6F9F; }

/* --- Landing page: the ocean ----------------------------------------------
 *
 * Two paintings, swapped rather than one reused. They cannot be the same file:
 * the gutter rule below needs an aspect at or under ~1.6 for its cover floor
 * not to override it, and a phone wants ~0.5. Forcing the landscape art onto a
 * phone puts the boat at 13% of screen width and behind the card.
 *
 * Scoped to .ss-landing throughout, because this stylesheet is shared with the
 * changelog, which reuses .ss-btn and would otherwise inherit the plate.
 */
.ss-landing {
  /* Shows for one paint before the artwork lands, and behind it if a very tall
     window ever outruns the crop. It is the painting's own mean colour. */
  background-color: #1C6A7B;

  /* `vh` is the viewport with the browser's URL bar retracted, so on a phone
     the page is laid out about a bar's height taller than the fold you can
     actually see, and the footer's changelog link lands under the bar. Worse,
     that overflow makes the page scrollable, and scrolling is what retracts
     the bar: .ss-sea then grows with it and the painting re-crops under your
     thumb. `svh` is the fold with the bar showing, so the link stays on
     screen, there is nothing to scroll, and the bar never moves.
     `height: auto` is here to undo the `height: 100%` on html/body above,
     which would otherwise pin the body back to the taller viewport and leave
     min-height with nothing to do. */
  height: auto;
  min-height: 100vh;
  min-height: 100svh;
}

.ss-sea {
  position: fixed;
  /* Deliberately the *large* viewport rather than `inset: 0`, which tracks the
     live one: where a URL bar does still retract (landscape phones, where the
     hero is taller than the window), an edge-to-edge fixed box is resized
     mid-scroll and `cover` re-crops the painting as you go. Pinned to the tall
     viewport it is cropped by a constant amount instead — the boat stays put,
     and no bare strip of #1C6A7B is ever exposed along the bottom. */
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  height: 100lvh;
  z-index: 0;
  background-color: #1C6A7B;
  background-image: url('assets/ocean.webp');
  background-repeat: no-repeat;
  /* The boat sits at 75.59% across the painting, and the gutter between the
     card and the window edge is centred on 75vw + 129px (129 being a quarter
     of the 516px card). Rendering the artwork at (that centre / 0.7559) wide
     therefore lands the boat exactly in the gutter — and at that width the
     artwork's left edge falls flush with the viewport's, which is why the
     position below is simply `left`.
     The max() is a floor at `cover`: go under it and the artwork stops filling
     the window, leaving flat colour down the side. */
  background-size: max(calc(99.22vw + 170.7px), 100vw, 133.33vh) auto;
  background-position: left center;
}

/* Phones get the portrait painting, which has the boat drawn low so it lands
   in the gutter beneath the card with no positioning at all — there is no
   vertical travel to correct with on a portrait viewport anyway.
   Landscape windows under 900px stay on the rule above; the portrait art would
   put the boat below the fold. */
@media (max-width: 899px) and (orientation: portrait) {
  .ss-sea {
    background-image: url('assets/ocean-portrait.webp');
    background-size: cover;
    background-position: center;
  }
}

/* The hero becomes a plate. Cream text on open water measures 3.0–3.4:1 in the
   lighter patches, and the scrim strong enough to fix that (0.63) flattens the
   painting — so the copy keeps the page's own palette on its own ground, and
   the artwork is left exactly as painted. */
.ss-landing .ss-hero {
  position: relative;
  z-index: 1;
  max-width: min(516px, 85vw);
  padding: 40px clamp(20px, 5vw, 32px) 36px;
  border-radius: 26px;
  background: var(--surface);
  box-shadow: 0 22px 60px rgb(4 22 27 / 0.30);
}

/* The footer sits on the water, where the #1F6F9F link is unreadable. */
.ss-landing .ss-foot {
  position: relative;
  z-index: 1;
}
.ss-landing .ss-link { color: #EDE8CC; }
.ss-landing .ss-link:focus-visible { outline-color: #EDE8CC; }
