/* =============================================================================
   signup.css — the inline notice reader on /signup/.

   ── WHY THIS EXISTS ─────────────────────────────────────────────────────────
   Each consent box asks a person to agree to a notice, and offers a "Read it"
   link next to it. That link used to open /legal/ in a new tab: it sent someone
   away from a half-filled form to make the decision somewhere with no way back,
   and a fair number of them never came back. The notice now opens OVER the
   form, which stays exactly as they left it.

   The link keeps its href and its target, so this is an enhancement rather than
   a replacement — with no JavaScript, or on a browser with no <dialog>, the old
   navigation happens and nothing here is ever seen.

   ── SHARED, NOT COPIED ──────────────────────────────────────────────────────
   The rendered notice body wears .legal-body from styles.css. It is the same
   content as /legal/ prints, so it should be the same typography, defined once.
   The only thing re-stated below is heading SIZE: this dialog's own title
   occupies h2, so a notice's sections sit one level deeper here than the same
   text does on /legal/, and the sizes have to be shifted back up to match.

   Everything else is scoped under .nr so it cannot reach the rest of the page.
   ============================================================================= */

/* A browser that does not know <dialog> renders it as an ordinary inline box —
   i.e. the whole notice reader printed in the middle of the page. This one rule
   is what stops that; the script separately refuses to take the link over
   unless showModal() actually exists. */
.nr:not([open]) { display: none; }

.nr[open] {
  /* Centred, and SIZED TO ITS CONTENT.

     This was a full-height slide-over pinned to the right edge. That is the
     right shape for a long document, and the wrong shape for what these
     notices usually are: a summary and a couple of paragraphs. A 700x990 panel
     holding two sentences reads as a loading failure, not a design.

     So: height is auto and the cap is a max-height. A short notice is a small
     card; a long one grows until it hits the cap and then scrolls inside its
     own body. One rule covers both, with no length check anywhere. */
  position: fixed;
  inset: 0;
  margin: auto;
  width: min(38rem, calc(100% - 2rem));
  /* fit-content, NOT auto. `inset: 0` pins top AND bottom, and an
     absolutely-positioned box with both edges pinned and `height: auto` is
     over-constrained — it stretches to fill and lands on the max-height. That is
     how a 340px notice ended up in a 736px panel. fit-content resolves the
     conflict in favour of the content, and `margin: auto` still centres it. */
  height: fit-content;
  max-width: none;
  max-height: min(82vh, 46rem);
  padding: 0;
  border: 0;
  border-radius: var(--radius-l);
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-lg);
  overflow: hidden; /* the body scrolls; the frame does not */
  /* The DIALOG is the flex column, not just .nr__inner. max-height on a block
     dialog clips its overflow, so with a long notice the body never got a
     constrained height to scroll inside and the footer — holding Close — was
     simply cut off below the fold. Making the capped element the flex container
     is what gives the body something finite to shrink against. */
  display: flex;
  flex-direction: column;
}

/* Literal colour, not a token: ::backdrop does not reliably inherit custom
   properties from the originating element in every engine that ships it. */
.nr::backdrop { background: rgba(11, 34, 57, 0.46); }

.nr[open] { animation: nr-slide-in 320ms var(--ease-out-expo) both; }
.nr[open]::backdrop { animation: nr-fade-in 320ms ease both; }

@keyframes nr-slide-in {
  from { opacity: 0; transform: translateY(0.75rem) scale(0.985); }
  to   { opacity: 1; transform: none; }
}
@keyframes nr-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* ---- Frame ------------------------------------------------------------------ */
.nr__inner {
  display: flex;
  flex-direction: column;
  /* Shrink, never grow: `flex: 1` would stretch a two-line notice back to the
     full cap; `0 1 auto` lets a long one give way so .nr__body can scroll. */
  flex: 0 1 auto;
  min-height: 0;
}

.nr__head {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-s);
  padding: var(--sp-m) var(--sp-m) var(--sp-s);
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}
.nr__heading { min-width: 0; flex: 1; }
.nr__eyebrow {
  font-size: var(--fs-xs);
  font-weight: 550;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-ink);
  margin-bottom: var(--sp-3xs);
}
.nr__eyebrow[hidden] { display: none; }
.nr__title { font-size: var(--fs-h3); text-wrap: pretty; }
/* Version and effective date. Which version somebody read is the whole point of
   recording a consent, so it is stated on the notice, not buried in the API. */
.nr__meta {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  margin-top: var(--sp-3xs);
}
.nr__meta[hidden] { display: none; }

.nr__close {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: 1.5px solid transparent;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  transition: background-color 0.18s ease, border-color 0.18s ease;
}
.nr__close:hover { background: var(--tint); border-color: var(--border); }

/* ---- Body ------------------------------------------------------------------- */
/* Tabbable on purpose: a scroll container that only a mouse can move is
   unusable for anyone driving the page from the keyboard. */
.nr__body {
  /* 0 1 auto, not 1: it may SHRINK and scroll when the notice is long, but it
     must not GROW to fill space a short notice does not need. */
  flex: 0 1 auto;
  min-height: 0;
  overflow-y: auto;
  /* Stops a flick at the end of the notice from scrolling the page underneath. */
  overscroll-behavior: contain;
  padding: var(--sp-m);
}
.nr__summary {
  font-size: var(--fs-body);
  color: var(--text-secondary);
  margin-bottom: var(--sp-m);
  text-wrap: pretty;
}
.nr__summary[hidden] { display: none; }
/* Two classes plus the pseudo-class, so this out-ranks the `.nr .legal-body h3`
   rule below. Written as `.nr__doc > :first-child` it lost to it, and a notice
   that opens on a heading — most of them do — got a 32px band of nothing
   between the title rule and its first word. */
.nr .legal-body > :first-child { margin-top: 0; }
/* The dialog's own title is the h2, so the notice's own sections start at h3 —
   one level deeper than the same text gets on /legal/. Level and appearance are
   separate decisions: the outline shifts down, the SIZES stay where /legal/ puts
   them, so a section still looks like a section. */
.nr .legal-body h3 { font-size: var(--fs-h3); margin: var(--sp-l) 0 var(--sp-2xs); }
.nr .legal-body h4 { font-size: var(--fs-body); margin: var(--sp-m) 0 var(--sp-3xs); }
.nr .legal-body h5,
.nr .legal-body h6 { font-size: var(--fs-sm); margin: var(--sp-m) 0 var(--sp-3xs); }

/* ---- States ------------------------------------------------------------------
   Loading, network failure and not-yet-published each say something different.
   A blank panel is the one outcome that is never acceptable: it is
   indistinguishable from the reader being broken. */
.nr__state { margin-bottom: var(--sp-m); }
.nr__state[hidden] { display: none; }
.nr__state-msg { color: var(--text-secondary); text-wrap: pretty; }
.nr__state--error .nr__state-msg { color: var(--danger); }
.nr__state-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2xs);
  margin-top: var(--sp-s);
}
.nr__state-actions[hidden] { display: none; }
.nr__state-actions .btn { font-size: var(--fs-xs); padding: 0.55rem 1rem; }
.nr__state-actions [hidden] { display: none; }

.nr__skeleton { margin-top: var(--sp-m); }
.nr__skeleton[hidden] { display: none; }
.nr__skeleton span {
  display: block;
  height: 0.75rem;
  border-radius: var(--radius-pill);
  background: var(--tint);
  margin-bottom: var(--sp-xs);
  animation: nr-pulse 1.4s ease-in-out infinite;
}
.nr__skeleton span:nth-child(2) { width: 92%; animation-delay: 120ms; }
.nr__skeleton span:nth-child(3) { width: 78%; animation-delay: 240ms; }
.nr__skeleton span:nth-child(4) { width: 88%; animation-delay: 360ms; }
.nr__skeleton span:nth-child(5) { width: 54%; animation-delay: 480ms; }

@keyframes nr-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.45; }
}

/* ---- Foot -------------------------------------------------------------------- */
.nr__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2xs);
  padding: var(--sp-s) var(--sp-m);
  border-top: 1px solid var(--border);
  background: var(--bg);
}

/* ---- Small screens ------------------------------------------------------------
   Same card, tighter gutters, and it arrives from the bottom — which is where a
   sheet belongs on a phone. */
@media (max-width: 40rem) {
  /* No width override here any more. `width: 100%` was right for the old
     full-height sheet; on a content-sized rounded card it puts the corners hard
     against the screen edges. The base rule's calc(100% - 2rem) keeps a 16px
     gutter, so the card still reads as a card on a phone. */
  .nr[open] { animation-name: nr-rise-in; }
  .nr__head, .nr__body { padding-left: var(--sp-s); padding-right: var(--sp-s); }
  .nr__foot { padding-left: var(--sp-s); padding-right: var(--sp-s); }
  /* Stacked, not side by side. Sharing the row equally makes "Open the full
     page" wrap to two lines while "Close" stays on one, so the two buttons end
     up different heights — which reads as a mistake rather than a pair.

     `column`, never `column-reverse`: reversing paints the pair in the opposite
     order to the DOM, so the eye reads Close-then-link while Tab visits
     link-then-Close. Stacking in source order also puts the primary action
     nearest the thumb. */
  .nr__foot { flex-direction: column; align-items: stretch; }
  .nr__foot .btn { flex: none; width: 100%; }
}
@keyframes nr-rise-in {
  from { opacity: 0; transform: translateY(2rem); }
  to   { opacity: 1; transform: none; }
}

/* styles.css already flattens every animation under this preference; the
   skeleton is the one that would otherwise keep pulsing forever, so it is
   stopped explicitly rather than run 1400 times faster. */
@media (prefers-reduced-motion: reduce) {
  .nr__skeleton span { animation: none !important; }
}
