/* ============================================================
   SHINE BRISTOL
   Document, Bristol - Saturday 13 February 2027, 14:00-23:00
   Sign-up. Client: Louder Entertainment & Document.

   DIRECTION
   The artwork is one photograph: an empty warehouse under blue strip
   light, a hot pink orb hung in the middle of it with SHINE across it,
   and the lineup laid on the wet floor's reflection. The client sent it
   as LAYERS (plate with the wordmark baked in, then date, lineup, venue
   and logos on transparency), so the poster is rebuilt as the page.

   Adam asked for the left side "not too boxy". So it has no edge at all:
   the plate is masked away on all four sides into a blurred, sunk copy
   of the same warehouse that fills the viewport, and reads as depth of
   field rather than a frame. The ONE hard edge on the page is the
   sign-up panel, framed in the neon tube the poster uses for its date
   and its KEY4050 block.

   COLOUR - literal pixels from the supplied layers:
     #000513  plate floor        (Shine-Doc-4x5-New.png, bottom band)
     #050A22  warehouse navy     (floor mid-tones, darkened for the panel)
     #F22AF3  neon tube          (Lineup.png / date.png box outline, median)
     #C000A6  orb magenta        (orb median #DB00BC, deepened for white text)
     #FFFFFF  poster type
   Neon on the panel: 5.97:1. White on the orb button: 5.54:1.

   TYPE - the poster's SHINE and date face is a wide geometric grotesk,
   present on the page only as image layers. Archivo Expanded echoes that
   width for the live chrome; Archivo for reading copy. Both OFL.

   MOTION - one moment only: the warehouse lights come up. The plate
   rises from near-dark, then the type layers arrive. Nothing else
   animates on load. Off under prefers-reduced-motion.
   ============================================================ */

@font-face {
  font-family: "Archivo Exp";
  src: url("/assets/fonts/ArchivoExp.woff2?v=20261013-1") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Archivo";
  src: url("/assets/fonts/Archivo-var.woff2?v=20261013-1") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  --void:      #000513;   /* plate floor - page ground */
  --panel:     #050A22;   /* panel base */
  --field:     #0A1230;   /* form field - OPAQUE on purpose, see autofill note */
  --neon:      #F22AF3;   /* poster box outline - frame, titles, structure */
  --neon-soft: #FF7BF8;   /* lifted neon for small links (8.8:1 on panel) */
  --orb:       #C000A6;   /* button fill */
  --paper:     #FFFFFF;   /* poster type */
  --ash:       #A9B4D6;   /* reading copy (9.49:1 on panel) */
  --ash-dim:   #7F8BB0;   /* secondary copy + placeholders (5.45:1 on field) */
  --danger:    #FF8A9A;
  --whatsapp:  #25D366;
  --line:      rgba(242,42,243,.34);

  --display: "Archivo Exp", "Helvetica Neue", Arial, sans-serif;
  --body:    "Archivo", "Helvetica Neue", Arial, sans-serif;

  --maxw: 1240px;
  --gutter: clamp(16px, 4vw, 40px);
}

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

/* The `hidden` attribute is only display:none in the UA stylesheet, the weakest
   layer of the cascade, so ANY author rule that sets display beats it and the
   element stays on screen with no error anywhere. Keep this rule ABOVE
   everything that sets display, and never remove it. */
[hidden] { display: none !important; }

/* Ground colour lives on html, NOT body: an opaque body background paints over
   the z-index:-2 .bg layer and the artwork disappears. */
html {
  -webkit-text-size-adjust: 100%;
  background: var(--void);
}

body {
  margin: 0;
  min-height: 100vh;
  min-height: 100svh;
  font-family: var(--body);
  color: var(--ash);
  background: transparent;
  display: flex;
  flex-direction: column;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;             /* the full-bleed phone poster must never scroll sideways */
}

/* ---------- the ground: the same warehouse, blurred and sunk ----------
   Two plates, chosen by aspect ratio (width-only breakpoints leave a gap at
   squarish windows). Both are pre-blurred offline, so they are tiny and need
   no CSS filter. The desktop one keeps the orb's glow ~30% across, behind the
   poster column, and has the orb painted OUT of its mirrored side tiles so no
   second orb appears behind the panel. The <link rel=preload> conditions in
   the head mirror these two rules exactly. */
.bg {
  position: fixed;
  inset: 0;
  z-index: -2;
  background-color: var(--void);
  background-image: url("/assets/images/bg-mobile.webp?v=20261013-1");
  background-position: center top;
  background-size: cover;
}
@media (min-aspect-ratio: 7/10) {
  .bg { background-image: url("/assets/images/bg-desktop.webp?v=20261013-1"); background-position: center center; }
}

/* ============================================================
   STAGE
   ============================================================ */
.stage {
  flex: 1 0 auto;
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;                 /* auto at EVERY breakpoint - never plain 0 */
  padding: clamp(22px, 4vw, 48px) var(--gutter);
  display: flex;
  align-items: center;
}
.stage__inner {
  width: 100%;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(22px, 4vw, 54px);
  align-items: center;
}

/* ---------- the poster ----------
   Coordinate space is the plate's own 1920px width, extended to 2330 tall so
   the venue line sits below a slightly enlarged lineup (72% wide vs the
   artwork's 61.5%: at page sizes the small "PLUS ADDITIONAL SOLO SETS" line
   needs it). Layer positions came from diffing full_artwork.png against the
   plate. Lefts/widths are % of width, tops are % of the 2330 height, so every
   layer stays locked to the plate at any size. */
.bill { justify-self: center; width: 100%; }
.poster {
  position: relative;
  width: 100%;
  aspect-ratio: 1920 / 2330;
  margin: 0 auto;
  overflow: hidden;
}
.poster__h1 { margin: 0; font-weight: inherit; }
.poster__plate {
  position: absolute;
  top: 0; left: 0;
  display: block;
  width: 100%;
  height: auto;                   /* 4:5 - runs 3% past the box, into the faded floor */
  /* No box: fade all four edges into the blurred ground. Two gradients
     intersected, so the corners fade from both directions. The wordmark spans
     20-80% across, clear of the 14% side fades. */
  -webkit-mask-image:
    linear-gradient(to right, transparent 0, #000 14%, #000 86%, transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 8%, #000 60%, transparent 94%);
  -webkit-mask-composite: source-in;
          mask-image:
    linear-gradient(to right, transparent 0, #000 14%, #000 86%, transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 8%, #000 60%, transparent 94%);
          mask-composite: intersect;
  animation: lights-up 1.9s cubic-bezier(.3,.6,.25,1) both;
}
.poster__layer {
  position: absolute;
  display: block;
  height: auto;
  animation: layer-in .9s .95s ease-out both;
}
.poster__date   { left: 23%; top: 1.29%;  width: 54%; }
.poster__lineup { left: 14%; top: 57.73%; width: 72%; }
.poster__venue  { left: 22%; top: 97.08%; width: 56%; }

/* Promoter logos: own grid item. Under the poster on wide screens, below the
   sign-up panel on phones. */
.logos { justify-self: center; width: min(100%, 520px); }
.logos__img { display: block; width: 90%; height: auto; margin: 0 auto; opacity: .9; }   /* the artwork insets its logo row ~5% each side */

/* The one load moment: the warehouse lights come up, then the type lands. */
@keyframes lights-up {
  0%   { opacity: 0; filter: brightness(.25) saturate(.5); }
  25%  { opacity: 1; }
  100% { opacity: 1; filter: none; }
}
@keyframes layer-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* ============================================================
   SIGNUP PANEL
   ============================================================ */
.signup {
  justify-self: center;
  width: 100%;
  max-width: 470px;
  /* Load-bearing. The logos come AFTER this section in the DOM (they sit below
     the panel on phones) and carry opacity < 1, which gives them their own
     stacking layer - painted over the whole panel, country dropdown included,
     whose z-index only counts inside the panel's isolated stack. Lifting the
     section above its later siblings keeps the dropdown on top. */
  position: relative;
  z-index: 2;
}
.panel {
  width: 100%;
  position: relative;
  isolation: isolate;             /* keeps the country dropdown above the panel chrome */
  background: rgba(5,10,34,.86);
  /* The neon tube: the poster boxes its date and its KEY4050 block in a
     magenta outline with a soft bloom. The panel is the only boxed thing on
     the page, so it gets that same frame and nothing else does. */
  border: 2px solid var(--neon);
  border-radius: 0;
  box-shadow:
    0 0 0 1px rgba(242,42,243,.22),
    0 0 26px rgba(242,42,243,.38),
    inset 0 0 22px rgba(242,42,243,.12),
    0 40px 90px -30px rgba(0,0,0,.95);
  overflow: visible;
  -webkit-backdrop-filter: blur(10px);
          backdrop-filter: blur(10px);
}
.panel__inner { position: relative; z-index: 1; padding: clamp(24px, 3.2vw, 36px); }

/* --- state machine (app.js flips data-form-state on .panel) --- */
.panel__state { display: none; }
.panel[data-form-state="idle"]    .panel__idle    { display: block; }
.panel[data-form-state="otp"]     .panel__otp     { display: block; }
.panel[data-form-state="success"] .panel__success { display: block; }

/* ============================================================
   STEP SPINE
   Three cells driven by the same data-form-state the form logic
   sets. Numbered because it genuinely is a sequence: form -> SMS
   code -> email confirm, and OTP drop-off is worst when people
   cannot see how much is left.
   ============================================================ */
.spine {
  display: flex;
  list-style: none;
  margin: 0 0 22px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 2px;
  overflow: hidden;
  background: rgba(0,5,19,.6);
}
.spine__step {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 9px 6px 8px;
  font-family: var(--display);
  font-weight: 600;
  font-size: 10.5px;
  letter-spacing: .06em;
  color: var(--ash-dim);
  transition: color .25s ease, background-color .25s ease;
}
.spine__step + .spine__step { border-left: 1px solid var(--line); }
.spine__n {
  flex: 0 0 auto;
  width: 16px; height: 16px;
  display: grid;
  place-items: center;
  border: 1px solid currentColor;
  border-radius: 50%;
  font-size: 9px;
  line-height: 1;
  letter-spacing: 0;
}
.spine__t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* current step */
.panel[data-form-state="idle"]    .spine__step:nth-child(1),
.panel[data-form-state="otp"]     .spine__step:nth-child(2),
.panel[data-form-state="success"] .spine__step:nth-child(3) {
  color: var(--paper);
  background: rgba(242,42,243,.16);
}
.panel[data-form-state="idle"]    .spine__step:nth-child(1) .spine__n,
.panel[data-form-state="otp"]     .spine__step:nth-child(2) .spine__n,
.panel[data-form-state="success"] .spine__step:nth-child(3) .spine__n {
  background: var(--orb);
  border-color: var(--neon);
  color: var(--paper);
}
/* completed steps: the numeral is replaced by a tick */
.panel[data-form-state="otp"]     .spine__step:nth-child(1),
.panel[data-form-state="success"] .spine__step:nth-child(1),
.panel[data-form-state="success"] .spine__step:nth-child(2) {
  color: var(--neon-soft);
}
.panel[data-form-state="otp"]     .spine__step:nth-child(1) .spine__n,
.panel[data-form-state="success"] .spine__step:nth-child(1) .spine__n,
.panel[data-form-state="success"] .spine__step:nth-child(2) .spine__n {
  font-size: 0;                    /* hide the digit, keep the ring */
  border-color: var(--neon-soft);
  background: rgba(242,42,243,.16);
}
.panel[data-form-state="otp"]     .spine__step:nth-child(1) .spine__n::after,
.panel[data-form-state="success"] .spine__step:nth-child(1) .spine__n::after,
.panel[data-form-state="success"] .spine__step:nth-child(2) .spine__n::after {
  content: "";
  width: 7px; height: 4px;
  border: 1.5px solid var(--neon-soft);
  border-top: 0;
  border-right: 0;
  transform: rotate(-45deg) translate(1px, -1px);
}
@media (max-width: 400px) {
  .spine__step { font-size: 9.5px; letter-spacing: .02em; gap: 5px; padding: 8px 3px 7px; }
  .spine__n { width: 14px; height: 14px; }
}

/* ============================================================
   PANEL COPY
   ============================================================ */
.panel__title {
  margin: 0 0 12px;
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(1.32rem, 3.4vw, 1.62rem);
  line-height: 1.08;
  letter-spacing: 0;
  text-transform: uppercase;
  color: var(--paper);
  text-wrap: balance;
}
/* House pattern across the fleet: the second line carries the accent. The
   neon is 5.97:1 on the panel, so it clears body-text AA too. A faint bloom
   ties it to the frame. Reset from italic: the face has no true italic. */
.panel__title em { font-style: normal; color: var(--neon); text-shadow: 0 0 18px rgba(242,42,243,.45); }
.panel__lede {
  margin: 0 0 10px;
  font-size: 15px;
  color: var(--ash);
  max-width: 46ch;
}
.panel__lede--last { margin-bottom: 20px; }
.panel__lede strong { color: var(--paper); font-weight: 600; }
.panel__success-note { margin: 0 0 12px; color: var(--neon-soft); font-size: 14.5px; }

/* ============================================================
   FORM
   ============================================================ */
.form__row { margin: 0 0 13px; }
/* labels stay for screen readers; placeholders carry the meaning visually */
.form__label {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.form__input {
  width: 100%;
  padding: 13px 14px;
  font-family: var(--body);
  font-size: 16px;                /* 16px minimum or iOS zooms the page on focus */
  color: var(--paper);
  background: var(--field);       /* OPAQUE - see the autofill rule below */
  border: 1px solid rgba(242,42,243,.32);
  border-radius: 2px;
  transition: border-color .18s ease, box-shadow .18s ease;
}
.form__input::placeholder { color: var(--ash-dim); opacity: 1; }
.form__input:focus {
  outline: none;
  border-color: var(--neon);
  box-shadow: 0 0 0 3px rgba(242,42,243,.22);
}
.form__input--otp {
  font-family: var(--display);
  font-weight: 600;
  font-size: 22px;
  letter-spacing: .42em;
  text-align: center;
  text-indent: .42em;             /* re-centres text that tracking pushes left */
}

/* Chrome paints autofilled inputs pale lavender and IGNORES background /
   background-color on :-webkit-autofill - a huge inset box-shadow is the only
   reliable override. A box-shadow cannot be semi-transparent, so the colour
   must be the FLATTENED OPAQUE equivalent of the field's rendered stack. That
   is trivially --field here because the field was made opaque on purpose; keep
   it opaque, or this value has to be recomputed by compositing field over
   panel over the background plate. */
.form__input:-webkit-autofill,
.form__input:-webkit-autofill:hover,
.form__input:-webkit-autofill:focus,
.form__input:-webkit-autofill:active {
  -webkit-box-shadow: 0 0 0 1000px var(--field) inset !important;
          box-shadow: 0 0 0 1000px var(--field) inset !important;
  -webkit-text-fill-color: var(--paper) !important;
  caret-color: var(--paper);
  transition: background-color 9999s ease-in-out 0s;   /* stops the lavender flashing in */
}

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

/* Honeypot. display:none ONLY - off-screen positioning stays an autofill
   target in Chrome/Edge, which fills it and silently blocks real signups. */
.hp-row { display: none !important; }

.form__row--check { margin: 17px 0 19px; }
.form__check {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ash);
  cursor: pointer;
}
.form__check input {
  flex: 0 0 auto;
  width: 18px; height: 18px;
  margin: 1px 0 0;
  accent-color: var(--neon);
  cursor: pointer;
}
.form__check a { color: var(--paper); text-decoration: underline; text-underline-offset: 2px; }
.form__check a:hover { color: var(--neon-soft); }

.form__resend {
  margin: 14px 0 0;
  font-size: 13px;
  color: var(--ash-dim);
  text-align: center;
}
.form__resend-btn {
  font: inherit;
  color: var(--neon-soft);
  background: none;
  border: 0;
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.form__resend-btn:disabled { color: var(--ash-dim); cursor: default; text-decoration: none; }

/* ---------- buttons ---------- */
.btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  padding: 15px 18px;
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(.8rem, 2.9vw, .92rem);
  letter-spacing: clamp(.03em, .3vw, .07em);
  text-transform: uppercase;
  white-space: nowrap;            /* shrink the type before it can ever wrap */
  text-align: center;
  text-decoration: none;          /* <a class="btn"> needs this, <button> doesn't */
  border: 0;
  border-radius: 0;
  cursor: pointer;
  transition: transform .12s ease, filter .18s ease;
}
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .6; cursor: default; }
/* White on the orb magenta: 5.54:1. (The brighter neon is only 3.28:1 under
   white, so it frames the button's glow, not its fill.) */
.btn--primary {
  color: var(--paper);
  background: var(--orb);
  box-shadow: 0 0 0 1px rgba(242,42,243,.5), 0 14px 34px -12px rgba(242,42,243,.75);
}
.btn--primary:hover:not(:disabled) { filter: brightness(1.1); }
.btn--whatsapp {
  color: #06301A;                 /* 7.33:1 on the WhatsApp green */
  background: var(--whatsapp);
  margin-top: 4px;
}
.btn--whatsapp:hover { filter: brightness(1.06); }
.btn__icon { flex: 0 0 auto; width: 19px; height: 19px; }

.wa-hook { margin: 18px 0 12px; font-size: 13.5px; color: var(--ash); }
.wa-hook strong { color: var(--paper); font-weight: 600; }

/* ============================================================
   intl-tel-input v25
   ============================================================ */
.iti { width: 100%; display: block; }
/* app.js re-measures and sets padding-left inline; this is the sane default
   before it runs. Anything >= 80px breaks 320-390px viewports. */
.iti input.form__input { padding-left: calc(52px + 0.7rem); }
.iti__country-container { z-index: 3; }
.iti--inline-dropdown .iti__dropdown-content { z-index: 50; }
/* Until the vendor stylesheet loads, the hidden state does not exist and the
   whole list plus search box flashes open on first paint. */
.iti__dropdown-content.iti__hide { display: none !important; }

.iti__dropdown-content {
  background: var(--panel);
  border: 1px solid rgba(242,42,243,.55);
  border-radius: 3px;
  box-shadow: 0 26px 56px -18px rgba(0,0,0,.94);
  overflow: hidden;
}
.iti__search-input {
  /* Keep the vendor's horizontal padding reservation: 30px left clears the
     absolutely positioned search icon, 28px right clears the clear button. A
     padding SHORTHAND that drops them puts the magnifier on top of the
     placeholder text. Set the vertical values, keep the horizontal ones. */
  padding: 10px 30px 10px 32px;
  width: 100%;
  font-family: var(--body);
  font-size: 14px;
  color: var(--paper);
  background: var(--field);
  border: 0;
  border-bottom: 1px solid rgba(242,42,243,.4);
}
.iti__search-input::placeholder { color: #7F8BB0; }
.iti__search-input:focus { outline: none; background: #111A40; }
.iti__country-list { background: var(--panel); max-height: 240px; }
.iti__country {
  padding: 9px 12px;
  font-family: var(--body);
  font-size: 14px;
  color: var(--paper);
}
.iti__country:hover,
.iti__country.iti__highlight { background: rgba(242,42,243,.16); }
.iti__dial-code { color: var(--ash-dim); }
.iti__country-name { color: var(--paper); }
.iti__selected-country { border-radius: 2px 0 0 2px; }
.iti__selected-country:hover,
.iti__selected-country-primary:hover { background: rgba(242,42,243,.14); }
.iti__selected-dial-code { color: var(--paper); font-family: var(--body); font-size: 15px; }
.iti__arrow { border-top-color: var(--ash-dim); }
.iti__arrow--up { border-bottom-color: var(--ash-dim); }

/* ============================================================
   FOOTER - standard fleet band (basslayerz pattern), credit in a pill
   ============================================================ */
.foot {
  flex-shrink: 0;
  width: 100%;
  margin: 0;
  padding: 18px clamp(16px, 4vw, 40px) 26px;
  background: rgba(0,5,19,.78);
  border-top: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
}
.foot__left { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.foot__legal { margin: 0; font-size: 12px; color: var(--ash-dim); letter-spacing: .02em; }
.foot__legal a { color: var(--ash-dim); text-decoration: none; }
.foot__legal a:hover { color: var(--neon-soft); }
.site-built { margin: 0; }
.site-built a {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 11px;
  border: 1px solid rgba(255,255,255,.16);
  border-radius: 999px;
  font-size: 12px;
  color: var(--ash-dim);
  text-decoration: none;
  transition: border-color .18s ease, color .18s ease;
}
.site-built a:hover { color: var(--paper); border-color: var(--neon); }
/* the credit logo is teal-on-dark artwork: dark band, so a bare opacity only */
.site-built img { display: block; opacity: .9; }

/* ============================================================
   RESPONSIVE
   Aspect-ratio thresholds, not width alone.
   Grid areas carry the logo move: phones stack bill / signup / logos;
   wide screens put the logos under the poster in the left column and the
   panel spans both rows.
   ============================================================ */
.stage__inner { grid-template-areas: "bill" "signup" "logos"; }
.bill   { grid-area: bill; }
.signup { grid-area: signup; }
.logos  { grid-area: logos; }

/* --- stacked (phones, portrait tablets) ---
   Phones: the poster runs edge to edge, like the artwork on a feed. The
   side fades mean it never shows a hard edge against the screen. */
@media (max-width: 599px) {
  .stage { padding-top: 6px; }
  .bill {
    width: calc(100% + 2 * var(--gutter));
    margin-inline: calc(-1 * var(--gutter));
    max-width: none;
  }
  /* the panel then tucks up under the venue line */
  .stage__inner { gap: 20px; }
  .logos { width: 100%; margin-top: 2px; }
}
@media (min-width: 600px) {
  .poster { width: min(100%, 560px); }
}

/* --- two column: poster left, panel right ---
   The poster's WIDTH is derived from the viewport height, so it can never
   outgrow its column on a tall window (the kotr.tours bug, gotcha 38) and
   never pushes the page past one screen on a short one. 0.824 = 1920/2330.
   The 220px allowance covers stage padding, the logos row and the footer. */
@media (min-width: 900px) and (min-aspect-ratio: 7/10) {
  .stage__inner {
    grid-template-columns: minmax(0, 1fr) minmax(0, 470px);
    grid-template-rows: auto auto;
    grid-template-areas: "bill signup" "logos signup";
    column-gap: clamp(36px, 5vw, 88px);
    row-gap: 18px;
    align-items: center;
  }
  .bill   { align-self: end; }
  .poster { width: min(100%, 600px, calc((100svh - 220px) * 0.824)); }
  .logos  { align-self: start; width: min(100%, 600px, calc((100svh - 220px) * 0.824)); }
  .signup { justify-self: end; }
}

@media (max-width: 899px) {
  .foot { justify-content: center; text-align: center; }
  .foot__left { justify-content: center; }
}

/* Phones: stack the footer rather than relying on the flex row wrapping.
   Around 390px the copyright + "built by" row is JUST wide enough to fill the
   band, so centring it reads as left-aligned. Force a centred column. */
@media (max-width: 480px) {
  .foot {
    flex-direction: column;
    align-items: center;
    gap: 10px;
    padding-bottom: 22px;
  }
  .foot__left {
    flex-direction: column;
    align-items: center;
    gap: 10px;
  }
}

/* --- landscape phones are a first-class layout ---
   A rotated phone (844x390, 932x430, 667x375) satisfies the wide conditions
   while having almost no height. Compact side-by-side: poster top-left at
   (nearly) full screen height, logos under it, panel right; tighter type.
   Placed AFTER the desktop block so it wins at equal specificity. The page
   scrolls here, and that is fine: the panel is taller than the screen. */
@media (min-width: 640px) and (min-aspect-ratio: 7/10) and (max-height: 520px) {
  .stage { padding: 12px clamp(12px, 3vw, 26px); align-items: flex-start; }
  .stage__inner {
    grid-template-columns: minmax(0, .85fr) minmax(0, 1fr);
    grid-template-rows: auto 1fr;
    grid-template-areas: "bill signup" "logos signup";
    column-gap: 22px;
    row-gap: 12px;
    align-items: start;
  }
  .bill   { align-self: start; min-height: 0; }
  .poster { width: min(100%, calc((100svh - 24px) * 0.824)); }
  .logos  { width: min(100%, calc((100svh - 24px) * 0.824)); }
  .signup { justify-self: stretch; max-width: none; min-height: 0; }
  .panel__inner { padding: 16px 18px; }
  .spine { margin-bottom: 13px; }
  .panel__title { font-size: 1.15rem; margin-bottom: 8px; }
  .panel__lede { font-size: 13px; margin-bottom: 7px; }
  .panel__lede--last { margin-bottom: 12px; }
  .form__row { margin-bottom: 10px; }
  .form__input { padding: 10px 13px; }
  .form__row--check { margin: 12px 0 13px; }
  .btn { padding: 12px 16px; }
  .iti__country-list { max-height: 150px; }
}

/* --- small phones --- */
@media (max-width: 400px) {
  .panel__inner { padding: 22px 18px; }
}

/* ============================================================
   ACCESSIBILITY
   ============================================================ */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
:focus-visible {
  outline: 2px solid var(--paper);
  outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}
