/* ==========================================================================
   SOTA London - v2 (video bed)
   Standalone stylesheet. index.html and style.css are untouched.

   Palette is sampled from the supplied film rather than invented: the rubble
   grades #171931 -> #2B334D -> #39465F -> #4D586D -> #687183. --void sits one
   step under the film's darkest value so the film reads as lit against the
   page rather than dissolving into it.

   Type roles: the artwork lockup IS the display voice, so no web font tries to
   imitate it. Doto (already self-hosted for v1, ~3KB) is re-cast from titles
   down to micro-labels, echoing the dot-matrix glitch strip inside the
   artwork. Archivo carries body and form copy.
   ========================================================================== */

@font-face {
  font-family: 'Doto';
  src: url('/assets/fonts/doto-round-900.woff2?v=20260702-1') format('woff2');
  font-weight: 900;
  font-style: normal;
  font-display: swap;
}

:root {
  --void:   #0B0C16;
  --basalt: #171931;
  --slate:  #2B334D;
  --steel:  #4D586D;
  --haze:   #687183;
  --mute:   #9BA3B3;
  --bone:   #F2EFE7;
  --white:  #FFFFFF;

  --plate:      rgba(11, 12, 22, 0.62);
  --plate-edge: rgba(242, 239, 231, 0.14);
  --danger:     #FF9E9E;

  --f-body:  'Archivo', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --f-micro: 'Doto', ui-monospace, 'SFMono-Regular', Menlo, monospace;

  --gutter: clamp(20px, 4vw, 64px);
  --stage-pb: clamp(18px, 2.6vw, 34px);   /* stage bottom padding; .foot cancels it to bleed */
  --tooth: 22.5deg;            /* 360 / 16 teeth - one click of the gear */
}

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

html, body { height: 100%; }

body.page {
  margin: 0;
  background: var(--void);
  color: var(--bone);
  font-family: var(--f-body);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

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

/* ==========================================================================
   VIDEO BED
   ========================================================================== */

.bed {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background: var(--void);
}

/* The <video> paints its own current frame over anything behind it, so the
   still can never sit underneath. Video z-index 1, still z-index 2. */
.bed__video {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 42%;
  border: 0;
  pointer-events: none;
}

/* The still is VISIBLE BY DEFAULT and fades out only once playback is
   confirmed running. That way no-JS, reduced-motion, autoplay-refused,
   stalled and Save-Data all land on a correct picture with no extra logic. */
.bed__still {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: block;
  width: 100%;
  height: 100%;
  opacity: 1;
  transition: opacity 600ms ease;
  pointer-events: none;
}
.bed__still img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 42%;
  display: block;
}
.bed__still.is-hidden { opacity: 0; }

/* Crater vignette. Deliberately radial rather than the usual top/bottom
   linear scrim: the film's subject is a lone figure at the centre of a
   crater, so the page darkens outward FROM that point and the figure stays
   readable at every viewport. The second layer lifts contrast under the
   panel without flattening the middle. */
/* NO OVERLAYS ON THE FILM (client, 2026-08-17: "just have the video in the
   background").

   These two layers previously sat over the video: .bed__crater, a dark
   radial+linear scrim, and .bed__grain, a fine noise texture. Both are off.
   The film is shown exactly as the designer graded it - it is also not
   touched in the encode (source and shipped frames both measure 37).

   Kept as rules rather than deleted so the treatment can be restored in one
   line if it is ever wanted; the markup hooks stay in v2.html for the same
   reason. Consequence to be aware of: the film peaks at luminance 210 during
   its strobe bursts, so white type sitting directly on it is unreadable for
   those frames. The signup panel is unaffected - it carries its own plate. */
.bed__crater,
.bed__grain {
  display: none;
}

/* ==========================================================================
   STAGE
   Desktop composition: lockup upper-left, plate lower-right, with the crater
   and its figure left clear through the diagonal between them.
   ========================================================================== */

.stage {
  position: relative;
  z-index: 5;
  min-height: 100svh;
  min-height: 100dvh;
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: clamp(18px, 3vh, 40px);
  padding: clamp(22px, 3.4vw, 52px) var(--gutter) var(--stage-pb);
}

/* No plate behind the lockup either - same instruction, nothing over the film.
   The lockup relies on its own baked glow and the drop-shadow below. */
.mark {
  display: flex;
  position: relative;
}

.mark__lockup {
  display: block;
  width: clamp(280px, 42vw, 540px);   /* capped so the 1080px source is never
                                         upscaled past its own resolution */
  height: auto;
  filter: drop-shadow(0 6px 26px rgba(0,0,0,0.55));
}

.signup {
  display: grid;
  align-content: center;
  justify-items: center;
}

/* ==========================================================================
   PANEL - a concrete plate, not a card. Zero radius, hairline bone edge,
   backdrop blur so the film still moves through it.
   ========================================================================== */

.panel {
  width: min(100%, 430px);
  padding: clamp(24px, 2.4vw, 34px) clamp(22px, 2.2vw, 32px) clamp(26px, 2.4vw, 34px);
  background: var(--plate);
  -webkit-backdrop-filter: blur(18px) saturate(115%);
  backdrop-filter: blur(18px) saturate(115%);
  border: 1px solid var(--plate-edge);
  border-radius: 0;
  box-shadow: 0 26px 70px rgba(0,0,0,0.5);
}

/* State machine driven by app.js (card.dataset.formState) */
.panel__otp, .panel__success { display: none; }
.panel[data-form-state="otp"]     .panel__idle    { display: none; }
.panel[data-form-state="otp"]     .panel__otp     { display: block; }
.panel[data-form-state="success"] .panel__idle    { display: none; }
.panel[data-form-state="success"] .panel__success { display: block; }

.panel__eyebrow {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 14px;
  font-family: var(--f-micro);
  font-weight: 900;
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--mute);
}
.panel__eyebrow-hair {
  width: 26px;
  height: 1px;
  background: var(--haze);
  flex: none;
}

/* All panel headings are set in capitals in the markup (client, 2026-08-17).
   Tracking is positive, not the negative value sentence case wants: once every
   letterform is the same height, tight tracking reads as cramped. */
.panel__title {
  margin: 0 0 10px;
  font-family: var(--f-body);
  font-weight: 700;
  font-size: clamp(24px, 2.3vw, 30px);
  line-height: 1.1;
  letter-spacing: 0.005em;
  color: var(--bone);
}

.panel__lede {
  margin: 0 0 22px;
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--mute);
}

.panel__success-note {
  margin: 0 0 12px;
  font-size: 14.5px;
  color: var(--bone);
}

/* ==========================================================================
   ON-SALE BLOCK + COUNTDOWN
   Two static on-sale times plus a clock counting to the presale.

   data-target on .onsale carries the London UTC offset (+01:00, BST on both
   dates - checked, not assumed). Date.parse then resolves the same instant in
   every timezone; a bare "2026-08-26T10:00" would fire an hour out for anyone
   outside the UK and quietly contradict the "all times London" note.

   The clock is display:none until the inline script adds .is-armed, so a JS
   failure leaves the two dates readable rather than a row of "--" placeholders.
   At the presale instant the script swaps .is-armed for .is-live: clock out,
   "presale is live now" in. It does NOT retarget to the general sale
   (client decision 2026-08-18) - the gen-sale time stays listed as text.
   ========================================================================== */

.onsale {
  margin: 0 0 20px;
  padding: 14px 0 2px;
  border-top: 1px solid var(--plate-edge);
  border-bottom: 1px solid var(--plate-edge);
}

.onsale__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin: 0 0 10px;
  font-family: var(--f-micro);
  font-weight: 900;
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--mute);
}
.onsale__tz { color: var(--haze); }

.onsale__rows { margin: 0; }

.onsale__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 5px 0;
}
.onsale__row dt {
  font-size: 13.5px;
  color: var(--mute);
}
.onsale__row dd {
  margin: 0;
  font-size: 13.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--bone);
  white-space: nowrap;
}

/* Clock: hidden until armed by script (see comment above). */
.onsale__clock { display: none; padding: 12px 0 14px; }
.onsale.is-armed .onsale__clock { display: block; }

.onsale__label {
  margin: 0 0 9px;
  text-align: center;
  font-family: var(--f-micro);
  font-weight: 900;
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--mute);
}

/* Fixed 4-column grid + tabular figures: Doto is a dot-matrix face whose digit
   widths differ, so without both the row would twitch once a second. */
.cd {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  max-width: 300px;
  margin-inline: auto;          /* centre the clock within the plate */
}
.cdseg {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 0 4px;
}
.cdseg + .cdseg { border-left: 1px solid var(--plate-edge); }
.cdseg b {
  font-family: var(--f-micro);
  font-weight: 900;
  font-size: clamp(21px, 2.1vw, 26px);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--bone);
}
.cdseg i {
  font-family: var(--f-micro);
  font-weight: 900;
  font-style: normal;
  font-size: 9.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--haze);
}

/* Live state - replaces the clock once the presale instant passes. */
.onsale__live { display: none; margin: 0; padding: 12px 0 14px; }
.onsale.is-live .onsale__clock { display: none; }
.onsale.is-live .onsale__live {
  display: flex;
  align-items: center;
  justify-content: center;      /* matches the centred clock it replaces */
  gap: 9px;
  font-family: var(--f-micro);
  font-weight: 900;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--bone);
}
.onsale__dot {
  width: 7px;
  height: 7px;
  flex: none;
  background: var(--bone);
}


/* ==========================================================================
   FORM
   ========================================================================== */

.form { margin: 0; }

/* display:none, not off-screen positioning - browsers autofill a visually
   hidden but rendered field and the submit is then silently dropped. */
.form__honeypot { display: none !important; }

.form__row { margin: 0 0 15px; }

.form__label {
  display: block;
  margin: 0 0 6px;
  font-family: var(--f-micro);
  font-weight: 900;
  font-size: 10.5px;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--mute);
}

.form__input {
  width: 100%;
  padding: 13px 14px;
  font-family: var(--f-body);
  font-size: 16px;              /* 16px minimum stops iOS zooming on focus */
  line-height: 1.3;
  color: var(--bone);
  background: rgba(23, 25, 49, 0.72);
  border: 1px solid var(--slate);
  border-radius: 0;
  transition: border-color 140ms ease, background-color 140ms ease;
  appearance: none;
}
.form__input::placeholder { color: var(--steel); }
.form__input:hover { border-color: var(--steel); }
.form__input:focus {
  outline: none;
  border-color: var(--bone);
  background: rgba(23, 25, 49, 0.92);
}
.form__input:focus-visible {
  outline: 2px solid var(--bone);
  outline-offset: 2px;
}

.form__input--otp {
  font-family: var(--f-micro);
  font-weight: 900;
  font-size: 24px;
  letter-spacing: 0.34em;
  text-align: center;
}

/* Autofill repaints the field with the UA's own background, which wipes the
   dark treatment. The inset shadow is the only reliable way to hold it. */
.form__input:-webkit-autofill,
.form__input:-webkit-autofill:hover,
.form__input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--bone);
  -webkit-box-shadow: 0 0 0 1000px #1B1E38 inset;
  box-shadow: 0 0 0 1000px #1B1E38 inset;
  caret-color: var(--bone);
  border: 1px solid var(--slate);
}

.form__row--check { margin: 18px 0 20px; }

.form__check {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 11px;
  align-items: start;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--mute);
  cursor: pointer;
}
.form__check input[type="checkbox"] {
  width: 17px;
  height: 17px;
  margin: 2px 0 0;
  accent-color: var(--bone);
  flex: none;
  cursor: pointer;
}
.form__check a { color: var(--bone); text-underline-offset: 2px; }
.form__check a:hover { color: var(--white); }

.form__error {
  margin: 7px 0 0;
  font-size: 12.5px;
  color: var(--danger);
}
.form__error--global { margin-top: 14px; }

.form__resend {
  margin: 14px 0 0;
  font-size: 12.5px;
  color: var(--mute);
  text-align: center;
}
.form__resend-btn {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--bone);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.form__resend-btn:disabled { color: var(--steel); cursor: default; }

/* ==========================================================================
   BUTTONS
   Signature: the saw glyph advances exactly one tooth per interaction.
   ========================================================================== */

.btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  padding: 15px 20px;
  font-family: var(--f-body);
  font-weight: 700;
  font-size: 14px;
  letter-spacing: 0.02em;
  border: 1px solid transparent;
  border-radius: 0;
  cursor: pointer;
  text-decoration: none;
  transition: background-color 160ms ease, color 160ms ease, border-color 160ms ease;
}

.btn--primary {
  color: var(--void);
  background: var(--bone);
  border-color: var(--bone);
}
.btn--primary:hover { background: var(--white); border-color: var(--white); }
.btn--primary:disabled { background: var(--steel); border-color: var(--steel); color: var(--basalt); cursor: default; }

.btn:focus-visible {
  outline: 2px solid var(--bone);
  outline-offset: 3px;
}

.btn__gear {
  width: 15px;
  height: 15px;
  flex: none;
  fill: currentColor;
  transition: transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.btn:hover  .btn__gear { transform: rotate(var(--tooth)); }
.btn:active .btn__gear { transform: rotate(calc(var(--tooth) * 2)); }

.btn--whatsapp {
  margin-top: 18px;
  color: var(--bone);
  background: transparent;
  border-color: var(--plate-edge);
}
.btn--whatsapp:hover { border-color: var(--bone); background: rgba(242,239,231,0.06); }
.btn__icon { width: 17px; height: 17px; flex: none; }

/* ==========================================================================
   FOOTER
   ========================================================================== */

/* The footer used to be bare text straight over the film, so it read as part
   of the video rather than as a footer (client, 2026-08-18). It now carries the
   same plate language as .panel - same fill, same hairline, same sharp corners -
   but bled out to the viewport edges so it anchors as a bar. The negative inline
   margins cancel the stage gutter and --stage-pb cancels the stage bottom
   padding; the media queries below therefore set width:auto, not 100%, or the
   declared width plus the negative margins would overflow horizontally. */
.foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  font-size: 11.5px;
  color: var(--haze);
  justify-self: stretch;
  margin-left: calc(var(--gutter) * -1);
  margin-right: calc(var(--gutter) * -1);
  margin-bottom: calc(var(--stage-pb) * -1);
  padding: clamp(11px, 1.1vw, 15px) var(--gutter);
  background: var(--plate);
  -webkit-backdrop-filter: blur(18px) saturate(115%);
  backdrop-filter: blur(18px) saturate(115%);
  border-top: 1px solid var(--plate-edge);
}
.foot__left { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.foot__legal { margin: 0; }
.foot__legal a { color: var(--mute); text-underline-offset: 2px; }
.foot__legal a:hover { color: var(--bone); }
.site-built a {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--haze);
  text-decoration: none;
}
.site-built a:hover { color: var(--bone); }
.site-built img { opacity: 0.65; }

/* ==========================================================================
   intl-tel-input - dark treatment over the vendor stylesheet
   ========================================================================== */

.iti { width: 100%; display: block; }
.iti__tel-input, .iti input[type="tel"] { width: 100%; }

.iti--separate-dial-code .iti__selected-country {
  background: rgba(23, 25, 49, 0.9);
  border-right: 1px solid var(--slate);
}
.iti__selected-dial-code { color: var(--mute); font-size: 15px; }

.iti__dropdown-content {
  background: #12142A;
  border: 1px solid var(--slate);
  border-radius: 0;
  color: var(--bone);
}
.iti__search-input {
  background: #0E1024;
  color: var(--bone);
  border: 0;
  border-bottom: 1px solid var(--slate);
  padding: 11px 12px;
  font-family: var(--f-body);
  font-size: 15px;
}
.iti__search-input::placeholder { color: var(--steel); }
.iti__country { color: var(--bone); padding: 9px 12px; }
.iti__country.iti__highlight,
.iti__country:hover { background: var(--slate); }
.iti__dial-code { color: var(--mute); }
.iti__country-name { color: var(--bone); }

/* ==========================================================================
   RESPONSIVE
   ========================================================================== */

/* Desktop: one centred column - lockup with the form directly beneath it,
   the pair centred on the page. Same composition as tablet and phone, so the
   page reads identically at every width rather than rearranging itself.

   The two 1fr spacer rows are what centre the pair vertically; the footer
   keeps its own full-width row at the bottom. */
@media (min-width: 980px) {
  .stage {
    grid-template-columns: 1fr;
    grid-template-rows: 1fr auto auto 1fr auto;
    grid-template-areas:
      "."
      "mark"
      "panel"
      "."
      "foot";
    justify-items: center;
    row-gap: 0;
    padding-bottom: clamp(24px, 2.6vw, 40px);
  }

  .mark {
    grid-area: mark;
    justify-content: center;
    margin-bottom: clamp(4px, 0.6vw, 10px);
  }
  .signup { grid-area: panel; align-content: start; justify-items: center; }
  .foot   { grid-area: foot; width: auto; margin-top: clamp(20px, 2.4vw, 34px); }

  /* 520px keeps the 1080px lockup source comfortably above 2x. */
  .panel        { width: min(100%, 460px); }
  .mark__lockup { width: min(100%, 520px); }
}

/* Tablet / narrow desktop: centred single column. */
@media (min-width: 760px) and (max-width: 979.98px) {
  .mark { justify-content: center; }
  .signup { justify-items: center; align-content: center; }
  .mark__lockup { width: min(74vw, 520px); }
}

/* Phone portrait: same centred column as desktop.
   This used to carry padding-top: 30svh, pushing the content down by 30% of
   the viewport so it cleared the crater in the mobile film. That belonged to
   the original stacked composition; with the layout centred it was simply a
   large empty band at the top. */
@media (max-width: 759.98px) {
  .stage {
    grid-template-rows: 1fr auto auto 1fr auto;
    grid-template-areas:
      "."
      "mark"
      "panel"
      "."
      "foot";
    justify-items: center;
    gap: 0;
  }
  .mark {
    grid-area: mark;
    justify-content: center;
    margin-bottom: clamp(8px, 2vw, 16px);
  }
  .mark__lockup { width: min(86vw, 420px); }
  .signup { grid-area: panel; align-content: start; width: 100%; }
  .foot   { grid-area: foot; width: auto; margin-top: 18px; }
  .panel { width: 100%; }
  .bed__video, .bed__still img { object-position: 50% 28%; }
  /* Same lightening as the desktop scrim above. The mobile film is the darker
     of the two (mean 78/255), so it needs the overlay even less. */
  .bed__crater {
    background:
      radial-gradient(120% 60% at 50% 26%,
        rgba(11,12,22,0.00) 0%,
        rgba(11,12,22,0.08) 50%,
        rgba(11,12,22,0.34) 84%,
        rgba(11,12,22,0.52) 100%),
      linear-gradient(180deg,
        rgba(11,12,22,0.34) 0%,
        rgba(11,12,22,0.00) 20%,
        rgba(11,12,22,0.26) 54%,
        rgba(11,12,22,0.62) 100%);
  }
}

/* Phone LANDSCAPE - a first-class layout, not an afterthought. The viewport is
   too short to stack, so lockup and plate sit side by side and the vertical
   rhythm compresses. */
@media (max-height: 520px) and (orientation: landscape) {
  .stage {
    grid-template-rows: 1fr auto;
    grid-template-columns: 1fr minmax(300px, 380px);
    grid-template-areas:
      "mark panel"
      "foot foot";
    align-items: center;
    gap: 16px clamp(18px, 3vw, 40px);
    padding: 16px var(--gutter) 12px;
  }
  .mark   { grid-area: mark; align-items: center; }
  .signup { grid-area: panel; align-content: center; justify-items: stretch; }
  .foot   { grid-area: foot; }

  .mark__lockup { width: min(42vw, 400px); }
  .panel {
    width: 100%;
    max-height: calc(100svh - 62px);
    overflow-y: auto;
    padding: 16px 18px 18px;
  }
  .panel__title { font-size: 20px; margin-bottom: 6px; }
  .panel__lede  { margin-bottom: 14px; font-size: 13px; }
  .panel__eyebrow { margin-bottom: 10px; }
  .onsale { margin-bottom: 14px; padding-top: 10px; }
  .onsale__head { margin-bottom: 7px; }
  .onsale__row { padding: 3px 0; }
  .onsale__clock, .onsale.is-live .onsale__live { padding: 9px 0 10px; }
  .cdseg b { font-size: 19px; }
  .form__row { margin-bottom: 11px; }
  .form__row--check { margin: 13px 0 14px; }
  .btn { padding: 12px 18px; }
  .bed__video, .bed__still img { object-position: 50% 40%; }
}

/* ==========================================================================
   MOTION / CONTRAST PREFERENCES
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  /* v2-bg.js does not load the film at all under reduced motion; the still is
     already the default layer, so nothing further is needed here. */
}

@media (prefers-contrast: more) {
  .panel { background: rgba(11, 12, 22, 0.92); border-color: rgba(242,239,231,0.34); }
  .panel__lede, .form__label, .form__check { color: #C7CDD8; }
}
