/* Subscribe / Follow Us — the hamburger-menu modal.
   ---------------------------------------------------------------------------
   Plan:    .claude/plans/subscribe-follow-modal.md
   Mockup:  .claude/mockups/subscribe-follow-modal/v1.html (variant B)

   Loaded after game.css and result-modals.css on index.html only.

   **Every selector in this file is scoped under `.modal--follow`.** That is not
   tidiness, it is the reason the file is safe to load on the game page: the
   form classes here (`.field`, `.choice`, `.button`, `.form__alert`) are the
   same names css/subscribe.css uses, so the markup can stay identical between
   the page and this modal — but unscoped they would either collide with
   game.css's own `.button` or leak form styling into the board. Keep the scope
   on anything added here.

   Colours are tokens from css/tokens.css, which redefines all of them under
   `body.dark-mode`, so nothing here needs a dark variant. The two brand
   colours that are not ours — Instagram's gradient and Facebook's #1877F2 —
   are fixed values by definition and sit behind white glyphs in both themes.
   =========================================================================== */

/* ========================================
   SHELL

   Borderless header with the title in the display face, the way
   .modal--success does it. The rows carry the message, so there is no lede.
   ======================================== */

.modal--follow .modal__header {
  border-bottom: none;
  padding-bottom: var(--space-xs);
  align-items: flex-start;
}

.modal--follow .modal__title {
  font-family: var(--font-display-modal);
  font-size: 1.45rem;
  font-weight: 700;
  line-height: var(--line-height-tight);
  letter-spacing: var(--letter-spacing-tight);
  font-variation-settings: "opsz" var(--opsz-display);
  /* .modal__title is a flex row for the icon+text titles elsewhere. This one
     is a sentence that wraps, so it needs to be text. */
  display: block;
  padding-top: var(--space-xs);
}

@media (min-width: 480px) {
  .modal--follow .modal__title {
    font-size: 1.55rem;
  }
}

/* The slash divides two ideas; it is not a word. Set back so it separates
   rather than competes. Gray-300 against the card is deliberately low
   contrast — it is decoration, and aria-hidden on the span keeps it out of the
   accessible name, so the contrast minimum does not apply to it. */
.modal--follow .modal__title-slash {
  color: var(--color-gray-300);
  font-weight: var(--font-weight-regular);
  margin: 0 0.12em;
}

.modal--follow .modal__body {
  padding: var(--space-xs) var(--space-md) var(--space-lg);
}

/* ========================================
   SECTIONS

   The body is two labelled groups — the email signup and the socials — each a
   bordered box with an overline label straddling its top border (the
   fieldset-legend look, without a fieldset: role="group" + aria-labelledby in
   index.html carry the semantics). The section owns the frame, so the .rowcard
   boxes inside are flattened; otherwise it reads as a box in a box.
   ======================================== */

.modal--follow .section {
  position: relative;
  border: 1px solid var(--color-gray-200);
  border-radius: var(--border-radius-md);
  /* Top padding clears the overline, which straddles the border above. */
  padding: 22px var(--space-sm) var(--space-sm);
  margin-top: var(--space-lg);
}

.modal--follow .section:first-of-type {
  margin-top: var(--space-xs);
}

.modal--follow .section__label {
  position: absolute;
  top: 0;
  left: 14px;
  transform: translateY(-50%);
  /* var(--color-white) is #121212 in dark mode, so the label reads as cut into
     the border in both themes — same trick the discs use. Swapped for the lift
     colour below while the email form is open. */
  background: var(--color-white);
  padding: 0 var(--space-xs);
  font-family: var(--font-family);
  font-size: var(--font-size-micro);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--color-gray-500);
  /* One line — a wrapped overline breaks the straddle. Both labels are short; if
     the owner lengthens one, check it at 320px. */
  white-space: nowrap;
}

/* The section is the frame now, so the .rowcard inside loses its own box. */
.modal--follow .section .rowcard {
  border: 0;
  padding: 0;
  margin: 0;
}

/* The open-state lift was on the mail card's own border. With that gone, lift the
   whole email section instead — same affordance, one border out. */
.modal--follow .section .rowcard--mail.is-open {
  background: transparent;
}

.modal--follow .section--mail:has(.rowcard--mail.is-open) {
  border-color: var(--color-primary);
  background: var(--color-gray-50);
}

.modal--follow .section--mail:has(.rowcard--mail.is-open) .section__label {
  background: var(--color-gray-50);
}

/* Two socials in one box, parted by a hairline rather than two boxes. */
.modal--follow .section--social .rowcard--social + .rowcard--social {
  margin-top: var(--space-sm);
  padding-top: var(--space-md);
  border-top: 1px solid var(--color-gray-100);
}

/* ========================================
   ROWS
   ======================================== */

.modal--follow .rowcard {
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
  padding: var(--space-md);
  margin-bottom: var(--space-xs);
  border: 1px solid var(--color-gray-200);
  border-radius: var(--border-radius-md);
}

.modal--follow .rowcard:last-child {
  margin-bottom: 0;
}

/* The mail row is a column so the form can expand beneath it inside the same
   card; the social rows are a single centred line. */
.modal--follow .rowcard--mail {
  flex-direction: column;
  align-items: stretch;
  gap: 0;
}

/* Neutral lift rather than a teal wash. The selected region choice below is
   teal-50, and a teal-50 chip on a teal-50 card is invisible — which is the one
   state in this form that has to be readable at a glance. */
.modal--follow .rowcard--mail.is-open {
  border-color: var(--color-primary);
  background: var(--color-gray-50);
}

.modal--follow .rowcard--social {
  align-items: center;
}

/* The collapsed mail row: disc, name, button. Its own element rather than the
   card itself, because the card also holds the form and the receipt and this
   part is hidden once the signup is accepted. */
.modal--follow .rowcard__top {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.modal--follow .rowcard__top[hidden] {
  display: none;
}

.modal--follow .rowcard__icon {
  flex: 0 0 auto;
  display: grid;
  place-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  /* var(--color-white), which is #121212 in dark mode, not literal white. The
     teal underneath is the brighter #14B8A6 there, and a white glyph on it is
     2.3:1. Same trick, and the same reason, as the page's submit button — do
     not "fix" this by hardcoding #fff. The two brand discs below do want
     literal white, because their backgrounds do not change with the theme. */
  color: var(--color-white);
}

.modal--follow .rowcard__icon svg {
  width: 22px;
  height: 22px;
}

.modal--follow .rowcard__icon--mail {
  background: var(--color-primary);
}

/* Instagram's and Facebook's own colours, kept as they are: these two discs
   are decorative — aria-hidden, with the platform name in text beside them —
   so brand fidelity wins over the contrast the pills have to meet. */
.modal--follow .rowcard__icon--ig,
.modal--follow .rowcard__icon--fb {
  color: #FFFFFF;
}

.modal--follow .rowcard__icon--ig {
  background: linear-gradient(45deg, #F58529, #DD2A7B, #8134AF);
}

.modal--follow .rowcard__icon--fb {
  background: #1877F2;
}

.modal--follow .rowcard__main {
  flex: 1 1 auto;
  min-width: 0;
}

.modal--follow .rowcard__name {
  margin: 0;
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-bold);
  line-height: var(--line-height-tight);
  color: var(--color-gray-900);
}

.modal--follow .rowcard__handle {
  margin: 2px 0 0;
  font-family: var(--font-mono-display-modal);
  font-size: var(--font-size-micro);
  /* gray-500 rather than the mockup's gray-400: 400 is 2.6:1 on white, which
     fails AA for text this size. 500 clears 4.6:1 in light and 8.4:1 in dark. */
  color: var(--color-gray-500);
  overflow-wrap: anywhere;
}

/* ========================================
   ROW BUTTONS

   Pills. The mail one is a <button>, the two social ones are <a href> — same
   pill, so both need the anchor reset as well as the button reset.
   ======================================== */

.modal--follow .rowcard__btn {
  flex: 0 0 auto;
  align-self: center;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Below the 44px touch minimum on its own, so the padding does the work:
     36px of pill inside a 44px row whose whole height is the tap target on the
     social rows. Kept at 36 because a 44px pill next to a 44px icon disc reads
     as two competing circles. */
  min-height: 36px;
  padding: var(--space-xs) var(--space-md);
  border: 0;
  border-radius: 999px;
  font-family: var(--font-family);
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-semibold);
  line-height: 1;
  /* See .rowcard__icon — theme-flipping white, so the label stays readable on
     the brighter dark-mode teal. Overridden to literal white on the two brand
     pills below, whose backgrounds do not flip. */
  color: var(--color-white);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--duration-fast) var(--ease-standard),
              filter var(--duration-fast) var(--ease-standard),
              transform var(--duration-instant) var(--ease-standard);
}

.modal--follow .rowcard__btn:active {
  transform: scale(0.97);
}

.modal--follow .rowcard__btn:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 3px;
}

.modal--follow .rowcard__btn[hidden] {
  display: none;
}

.modal--follow .rowcard__btn--mail {
  background: var(--color-primary);
}

.modal--follow .rowcard__btn--mail:hover {
  background: var(--color-primary-hover);
}

/* These two carry white text on a brand colour, which is where the platforms'
   own palettes stop being usable:

     - White on Instagram's orange (#F58529) is 2.5:1, and on its pink
       (#DD2A7B) 4.5:1 on the nose. The gradient is pulled back to the two dark
       end-stops, #C13584 → #8134AF, which run 5.1:1 to 6.9:1 and still read as
       Instagram.
     - White on Facebook's #1877F2 is 4.2:1 — the well-known near-miss. #166BDC
       is the same blue a shade down, at 5.0:1.

   The discs above keep the untouched brand colours; only the pills, which
   carry actual words, are adjusted. Hover darkens rather than brightens, for
   the same reason. */
.modal--follow .rowcard__btn--ig,
.modal--follow .rowcard__btn--fb {
  color: #FFFFFF;
}

.modal--follow .rowcard__btn--ig:hover,
.modal--follow .rowcard__btn--fb:hover {
  filter: brightness(0.92);
}

.modal--follow .rowcard__btn--ig {
  background: linear-gradient(45deg, #C13584, #8134AF);
}

.modal--follow .rowcard__btn--fb {
  background: #166BDC;
}

/* ========================================
   THE EXPANDED FORM

   Same class names and same measurements as css/subscribe.css, so the two
   signup forms stay one design. Anything changed here should be changed there.
   ======================================== */

.modal--follow .rowcard__form {
  margin-top: var(--space-md);
  padding-top: var(--space-md);
  border-top: 1px solid var(--color-gray-200);
}

.modal--follow .rowcard__form[hidden],
.modal--follow .rowcard__sent[hidden] {
  display: none;
}

.modal--follow .form {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

/* The `hidden` attribute sets `display: none` from the browser's own
   stylesheet, which any class selector here outranks. Every block that both
   sets `display` and gets hidden from JavaScript needs this, or the element
   stays on screen with `hidden` quietly set on it — which also leaves it
   announced to a screen reader while looking dismissed. */
.modal--follow .field--check[hidden],
.modal--follow .field--choice[hidden],
.modal--follow .form__alert[hidden],
.modal--follow .field__error[hidden] {
  display: none;
}

.modal--follow .field {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  /* fieldset carries browser defaults that have to go before it can share the
     flex column with the plain divs. */
  margin: 0;
  padding: 0;
  border: 0;
  min-width: 0;
}

.modal--follow .field__label {
  display: block;
  padding: 0;
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--letter-spacing-wide);
  color: var(--color-gray-700);
}

.modal--follow .field__hint {
  margin: 0;
  font-size: var(--font-size-caption);
  line-height: var(--line-height-relaxed);
  color: var(--color-gray-500);
}

.modal--follow .field__input {
  width: 100%;
  min-height: var(--touch-target-min);
  padding: var(--space-sm) var(--space-md);
  background: var(--color-white);
  color: var(--color-gray-900);
  border: 1px solid var(--color-gray-300);
  border-radius: var(--border-radius-md);
  /* 1rem. Anything under 16px makes iOS Safari zoom the whole page on focus,
     and a modal that is suddenly wider than the viewport cannot be closed
     without pinching back out. */
  font-size: var(--font-size-body);
  font-family: var(--font-family);
  transition: border-color var(--duration-fast) var(--ease-standard),
              box-shadow var(--duration-fast) var(--ease-standard);
}

.modal--follow .field__input:hover {
  border-color: var(--color-gray-400);
}

.modal--follow .field__input:focus {
  border-color: var(--color-primary);
  outline: none;
  box-shadow: var(--glow-primary-soft);
}

.modal--follow .field__input[aria-invalid='true'] {
  border-color: var(--color-error);
}

.modal--follow .field__input[aria-invalid='true']:focus {
  box-shadow: 0 0 0 4px var(--color-error-bg);
}

.modal--follow .field__error {
  margin: 0;
  font-size: var(--font-size-caption);
  line-height: var(--line-height-relaxed);
  font-weight: var(--font-weight-medium);
  color: var(--color-error);
}

/* ========================================
   REGION CHOICES

   Three radios drawn as cards. The native input is kept in the flow and
   restyled with `appearance: none` rather than hidden behind a fake control —
   it stays a real radio for the keyboard, for screen readers and for forced
   colours, and the checked dot needs no `:has()` support to appear.
   ======================================== */

/* Block, not flex, and deliberately so. A <legend> is laid out by the browser
   in its own box rather than as an ordinary child, and how it behaves inside a
   flex fieldset differs between engines. Margins on the children cost nothing
   and have no such disagreement. */
.modal--follow .field--choice {
  display: block;
}

.modal--follow .field--choice .field__hint {
  margin-top: var(--space-xs);
}

.modal--follow .choices {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin-top: var(--space-sm);
}

.modal--follow .field--choice .field__error {
  margin-top: var(--space-xs);
}

.modal--follow .choice {
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
  min-height: var(--touch-target-min);
  padding: var(--space-sm) var(--space-md);
  background: var(--color-white);
  border: 1px solid var(--color-gray-200);
  border-radius: var(--border-radius-md);
  cursor: pointer;
  transition: border-color var(--duration-fast) var(--ease-standard),
              background-color var(--duration-fast) var(--ease-standard);
}

.modal--follow .choice:hover {
  border-color: var(--color-gray-400);
}

.modal--follow .choice__input {
  appearance: none;
  -webkit-appearance: none;
  flex: 0 0 auto;
  /* Optical alignment with the first line of the title rather than the top of
     the box, which is what makes the two-line options look right. */
  margin: 0.3rem 0 0;
  width: 1.125rem;
  height: 1.125rem;
  border: 2px solid var(--color-gray-400);
  border-radius: 50%;
  background: var(--color-white);
  cursor: pointer;
  transition: border-color var(--duration-fast) var(--ease-standard),
              box-shadow var(--duration-fast) var(--ease-standard);
}

.modal--follow .choice__input:checked {
  border-color: var(--color-primary);
  /* The dot is an inset ring rather than a pseudo-element so it survives
     forced-colors mode, where backgrounds are replaced but borders are not. */
  box-shadow: inset 0 0 0 3px var(--color-white),
              inset 0 0 0 1rem var(--color-primary);
}

.modal--follow .choice__input:focus-visible {
  /* The card takes the ring instead — see below. Two rings, one inside the
     other, is what happens without this. */
  outline: none;
}

.modal--follow .choice__body {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
}

/* Same sizes as the page's own choices. The modal is 500px, the page card is
   480px — there is no reason for the same three options to be smaller here,
   and the EMEA detail line is a correctness requirement rather than a
   flourish, so it does not get shrunk to fit. */
.modal--follow .choice__title {
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-medium);
  line-height: var(--line-height-relaxed);
  color: var(--color-gray-900);
}

.modal--follow .choice__detail {
  font-size: var(--font-size-caption);
  line-height: var(--line-height-relaxed);
  color: var(--color-gray-500);
}

/* Progressive enhancement only — the checked dot above already carries the
   state. This just extends it to the whole row so the selection is legible at
   a glance rather than at a squint. */
.modal--follow .choice:has(.choice__input:checked) {
  border-color: var(--color-primary);
  background: var(--color-teal-50);
}

.modal--follow .choice:has(.choice__input:focus-visible) {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.modal--follow .field--choice[data-invalid='true'] .choice {
  border-color: var(--color-error-border);
}

/* ========================================
   BOT CHECK

   Cloudflare draws this in an iframe of its own, so there is very little to
   style. The reserved height is set from JavaScript, not here — which size the
   widget gets depends on how much room the card actually has, and only the
   script knows that. See mountCheck() in js/subscribe-core.js.

   The 300px floor on Cloudflare's flexible widget is wider than this card's
   content box on a small phone, so this is the one element allowed to overflow
   rather than shrink.
   ======================================== */

.modal--follow .field--check {
  overflow-x: auto;
}

/* ========================================
   SUBMIT, ALERTS AND FINE PRINT

   The submit button is game.css's own `.button.button--primary`; only the
   things that button does not set are added here.
   ======================================== */

.modal--follow .form .button {
  width: 100%;
}

.modal--follow .form .button[disabled],
.modal--follow .form .button[aria-busy='true'] {
  opacity: 0.65;
  cursor: default;
  transform: none;
}

.modal--follow .form__alert {
  padding: var(--space-sm) var(--space-md);
  background: var(--color-error-bg);
  border: 1px solid var(--color-error-border);
  border-radius: var(--border-radius-md);
  font-size: var(--font-size-caption);
  line-height: var(--line-height-relaxed);
  color: var(--color-gray-900);
}

/* It takes focus when a submit fails so the problem is announced, but it is
   not a control and should not draw a ring on a mouse click. */
.modal--follow .form__alert:focus {
  outline: none;
}

.modal--follow .form__fineprint {
  margin: 0;
  font-size: var(--font-size-caption);
  line-height: var(--line-height-relaxed);
  color: var(--color-gray-500);
}

.modal--follow .form__fineprint a {
  color: var(--color-gray-600);
}

/* ========================================
   DONE STATE
   ======================================== */

.modal--follow .rowcard__sent {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding: var(--space-xs) 0;
}

.modal--follow .done__title {
  margin: 0;
  font-family: var(--font-display-modal);
  font-size: 1.15rem;
  font-weight: 700;
  line-height: var(--line-height-tight);
  letter-spacing: var(--letter-spacing-tight);
  color: var(--color-gray-900);
}

/* It takes focus on success so the outcome is announced, but it is not a
   control and should not advertise itself with a ring on a mouse click. */
.modal--follow .done__title:focus {
  outline: none;
}

.modal--follow .done__body {
  margin: 0;
  font-size: var(--font-size-body);
  line-height: var(--line-height-loose);
  color: var(--color-gray-600);
}

/* ========================================
   REDUCED MOTION
   ======================================== */

@media (prefers-reduced-motion: reduce) {
  .modal--follow .field__input,
  .modal--follow .choice,
  .modal--follow .choice__input,
  .modal--follow .rowcard__btn {
    transition: none;
  }

  .modal--follow .rowcard__btn:active {
    transform: none;
  }
}

/* ========================================
   FORCED COLOURS

   Windows high contrast replaces backgrounds and colours but keeps borders and
   outlines, so anything carried by colour alone — including the three brand
   discs and the pills, whose fills are dropped entirely — needs a border to
   fall back to.
   ======================================== */

@media (forced-colors: active) {
  .modal--follow .rowcard,
  .modal--follow .choice,
  .modal--follow .field__input,
  .modal--follow .form__alert,
  .modal--follow .rowcard__icon {
    border: 1px solid CanvasText;
  }

  .modal--follow .rowcard__btn {
    border: 1px solid ButtonText;
    color: ButtonText;
  }

  .modal--follow .choice:has(.choice__input:checked) {
    border-width: 3px;
  }
}
