/*
 * Sque authorization surface — login, consent, and error pages.
 *
 * Served first-party (CSP style-src 'self'); loads no third-party resources and no fonts
 * over the network — the type is a system stack, matching the privacy posture of the page.
 * Aesthetic: minimal and editorial — the form floats on Sque's near-black void, a centered
 * pinwheel mark, a serif display voice, outlined fields with notched floating labels, and a
 * single cream action. No panel, no ornament. Brand tokens are lifted from the Sque frontend.
 */

:root {
  --ink-void: #030303;

  /* Ink — the brand cream, stepped down for hierarchy */
  --cream: #f4efe4;
  --cream-dim: #a49f96;
  --cream-faint: #79746b;
  /* RGB triplets for the two hues used at arbitrary alpha elsewhere in this file (badges, notices,
   * invite panels) — kept in sync with --cream/--green-lit above rather than repeated as separate
   * literals, which is what let those spots go stale when `.theme-light` below needs its own values. */
  --cream-rgb: 244, 239, 228;
  --green-rgb: 125, 154, 138;

  /* Field outlines */
  --field-border: rgba(244, 239, 228, 0.2);
  --field-border-hover: rgba(244, 239, 228, 0.4);

  /* Brand green — lifted so it reads on black; used only for focus + grant marks */
  --green-lit: #7d9a8a;
  --green-glow: rgba(125, 154, 138, 0.28);

  /* Primary action — Sque's cream button on slate ink */
  --button: #f1ede5;
  --button-hover: #fffdf8;
  --button-ink: #202a2b;

  /* Status */
  --danger: #d97a7a;
  --danger-bg: rgba(208, 92, 92, 0.1);
  --danger-line: rgba(208, 92, 92, 0.35);

  /* Success — the brand green, at the same three weights as danger above so the two read as a pair.
   * Derived from --green-lit rather than a new hue: this is the same green as focus rings and grant
   * marks, so an outcome that went well looks like the rest of the product rather than like a
   * borrowed traffic light. */
  --success: #8fae9c;
  --success-bg: rgba(125, 154, 138, 0.12);
  --success-line: rgba(125, 154, 138, 0.38);

  /* Type roles — editorial serif display, neutral sans body, mono for the fine print */
  --font-display:
    "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia,
    ui-serif, serif;
  --font-body:
    -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial,
    sans-serif;
  --font-mono:
    ui-monospace, "SF Mono", SFMono-Regular, "Cascadia Mono", "Roboto Mono",
    Menlo, Consolas, monospace;

  --radius: 10px;
}

/* ── Light theme — the client portal surface ─────────────────────────────
 *
 * Applied via `.theme-light` on <body> (see `_layout.html`'s `body_class` block), set only by
 * portal_login.html and portal_registration.html (which the self-serve /portal/self-registration
 * route also fronts). Everything below re-derives the SAME roles the dark void above defines —
 * void/ink swap, the button inverts, green and danger deepen for contrast on a light ground — so
 * every rule elsewhere in this file that already reads a variable (not a literal) picks this up
 * for free. Nothing in the client flow reads differently from the firm flow by ACCIDENT; this is
 * the one block that makes it read differently on purpose.
 */
.theme-light {
  --ink-void: #faf7f1;
  --cream: #1c1c1a;
  --cream-dim: #6b675f;
  --cream-faint: #9b968c;
  --cream-rgb: 28, 28, 26;

  --field-border: rgba(28, 28, 26, 0.16);
  --field-border-hover: rgba(28, 28, 26, 0.32);

  /* Deepened from the dark theme's --green-lit/--success: the original values were tuned to read on
   * near-black (per the comment above) and wash out to near-invisible on a light ground. */
  --green-lit: #4f7d68;
  --green-glow: rgba(79, 125, 104, 0.18);
  --green-rgb: 79, 125, 104;

  --button: #1c1c1a;
  --button-hover: #000000;
  --button-ink: #faf7f1;

  --danger: #b23b3b;
  --danger-bg: rgba(178, 59, 59, 0.08);
  --danger-line: rgba(178, 59, 59, 0.3);

  --success: #4f7d68;
  --success-bg: rgba(79, 125, 104, 0.1);
  --success-line: rgba(79, 125, 104, 0.3);
}

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

html,
body {
  margin: 0;
  padding: 0;
}

body {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px 24px;
  font-family: var(--font-body);
  color: var(--cream);
  background-color: var(--ink-void);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ── The centered column ──────────────────────────────────────────────── */

.stage {
  width: 100%;
  max-width: 360px;
  animation: rise 0.5s cubic-bezier(0.2, 0.7, 0.2, 1) both;
}

.stage--wide {
  max-width: 428px;
}

/* ── Mark + heading ───────────────────────────────────────────────────── */

.mark {
  display: flex;
  justify-content: center;
  margin-bottom: 26px;
}

.mark svg {
  width: 38px;
  height: 38px;
  color: var(--cream);
}

.eyebrow {
  margin: 0 0 8px;
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--cream-dim);
  text-align: center;
}

.title {
  margin: 0 0 12px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(30px, 6vw, 40px);
  line-height: 1.1;
  letter-spacing: -0.01em;
  text-align: center;
}

.lede {
  margin: 0 auto 34px;
  max-width: 30ch;
  font-family: var(--font-display);
  font-size: 17px;
  line-height: 1.5;
  color: var(--cream-dim);
  text-align: center;
}

.lede strong {
  color: var(--cream);
  font-weight: 600;
}

/* ── Outlined fields with notched floating labels ─────────────────────── */

form {
  margin: 0;
}

.field {
  position: relative;
  margin-bottom: 16px;
}

.field-input {
  width: 100%;
  height: 56px;
  padding: 0 16px;
  font-family: var(--font-body);
  font-size: 16px;
  color: var(--cream);
  background: transparent;
  border: 1px solid var(--field-border);
  border-radius: var(--radius);
  outline: none;
  appearance: none;
  transition:
    border-color 0.15s ease,
    box-shadow 0.15s ease;
}

.field-input:hover {
  border-color: var(--field-border-hover);
}

.field-input:focus {
  border-color: var(--green-lit);
  box-shadow: 0 0 0 1px var(--green-lit);
}

.field-label {
  position: absolute;
  left: 11px;
  top: 28px;
  transform: translateY(-50%);
  padding: 0 5px;
  font-family: var(--font-body);
  font-size: 16px;
  color: var(--cream-faint);
  background: var(--ink-void);
  pointer-events: none;
  transition:
    top 0.15s ease,
    font-size 0.15s ease,
    color 0.15s ease;
}

.field-input:focus + .field-label,
.field-input:not(:placeholder-shown) + .field-label,
/* A <select> has no :placeholder-shown, so it floats on :invalid instead — every select here is
   `required` with an empty disabled first option, so :invalid means exactly "nothing chosen yet"
   and :valid means a real option is selected. Until then the label sits centered like the other
   fields' placeholder, rather than floating up over an otherwise-empty box. */
.field--select .field-input:not(:invalid) + .field-label {
  top: 0;
  font-size: 12px;
  color: var(--cream-dim);
}

.field-input:focus + .field-label {
  color: var(--green-lit);
}

/* Native-select chevron, drawn as a data-URI so no script is needed. Fallback for when the
 * JS-enhanced listbox below never mounts (script blocked, or fails) — the plain <select> stays
 * fully functional and reasonably dressed, matching this file's progressive-enhancement posture. */
.field--select .field-input {
  padding-right: 42px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23a49f96' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 16px center;
  cursor: pointer;
}

/* ── Enhanced select (JS-built listbox) ───────────────────────────────────
 *
 * The chevron rule above is what a `<select>` looks like unstyled-but-dressed: the closed box can be
 * themed, but the OPEN option list is drawn by the OS, not this stylesheet, on every browser that
 * matters here — no amount of CSS reaches it. `enhance-select.js` progressively replaces the closed
 * box with `.select-trigger` (a real <button>, so it inherits `.field-input`'s look for free) and the
 * open list with `.select-panel`/`.select-option` below, which — being ours — CAN be themed. The
 * underlying <select> is kept in the DOM, inert and visually hidden, as the actual form value; if the
 * script never runs, nothing above is touched and the plain select still works.
 */

.select-trigger {
  display: flex;
  align-items: center;
  width: 100%;
  text-align: left;
  color: var(--cream-faint);
  font: inherit;
}

.select-trigger.select-trigger--filled {
  color: var(--cream);
}

/* Mirrors `.field--select .field-input:not(:invalid) + .field-label` above — a <button> has no
 * :invalid/:valid, so the "has a real value" signal is this class instead, toggled by the script.
 * GENERAL sibling (`~`), not adjacent (`+`): `.select-panel` sits in the DOM between the trigger and
 * the label (inserted after the trigger, so it stays out of the tab/reading order ahead of the
 * label), which an adjacent-sibling selector would never reach past. */
.select-trigger--filled ~ .field-label,
.select-trigger[aria-expanded="true"] ~ .field-label {
  top: 0;
  font-size: 12px;
  color: var(--cream-dim);
}

.select-trigger:focus-visible ~ .field-label,
.select-trigger[aria-expanded="true"] ~ .field-label {
  color: var(--green-lit);
}

.select-panel {
  position: absolute;
  z-index: 20;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  margin: 0;
  padding: 6px;
  max-height: 240px;
  overflow-y: auto;
  list-style: none;
  background: var(--ink-void);
  border: 1px solid var(--field-border-hover);
  border-radius: var(--radius);
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.35);
}

.select-option {
  padding: 10px 12px;
  font-size: 15px;
  color: var(--cream);
  border-radius: calc(var(--radius) - 4px);
  cursor: pointer;
}

.select-option[aria-selected="true"] {
  color: var(--green-lit);
  background: var(--green-glow);
}

.select-option--active {
  background: rgba(var(--cream-rgb), 0.08);
}

/* The underlying <select> once `enhance-select.js` has taken over: still a real form field (value,
 * validity, submission all still flow through it), just no longer the visible or focusable one. */
.sr-only-select {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ── Buttons ──────────────────────────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  width: 100%;
  height: 52px;
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 600;
  border-radius: var(--radius);
  border: 1px solid transparent;
  cursor: pointer;
  transition:
    background 0.15s ease,
    border-color 0.15s ease,
    transform 0.05s ease,
    box-shadow 0.15s ease;
}

.btn:active {
  transform: translateY(1px);
}

.btn:focus-visible {
  box-shadow: 0 0 0 3px var(--green-glow);
  outline: none;
}

.btn--primary {
  background: var(--button);
  color: var(--button-ink);
  margin-top: 8px;
}

.btn--primary:hover {
  background: var(--button-hover);
}

.btn--ghost {
  background: transparent;
  color: var(--cream);
  border-color: var(--field-border);
}

.btn--ghost:hover {
  border-color: var(--field-border-hover);
}

/* A secondary action stacked directly under the primary needs breathing room —
   otherwise the two buttons touch (e.g. Continue / "Email me a sign-in code"). */
.btn--primary + .btn--ghost {
  margin-top: 10px;
}

/* Social sign-in: the same ghost button, with a brand mark and centered label */
.btn--social {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-top: 12px;
}

.social-mark {
  flex: 0 0 auto;
}

/* "or" rule between the password action and the social buttons */
.divider {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 22px 0 6px;
  color: var(--cream-faint);
}

.divider::before,
.divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--field-border);
}

.divider span {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.actions {
  display: flex;
  gap: 12px;
  margin-top: 20px;
}

.actions .btn {
  margin-top: 0;
}

.actions .btn--ghost {
  flex: 0 0 34%;
}

.actions .btn--primary {
  flex: 1;
}

/* ── Implied terms/privacy notice ─────────────────────────────────────── */

.consent-text {
  margin: 20px 2px 20px;
  font-size: 13px;
  line-height: 1.45;
  text-align: center;
  color: var(--cream-dim);
}

.consent-text a {
  color: var(--cream);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.consent-text a:hover {
  color: var(--button-hover);
}

/* ── Alerts ───────────────────────────────────────────────────────────── */

.alert {
  background: var(--danger-bg);
  border: 1px solid var(--danger-line);
  color: var(--danger);
  border-radius: var(--radius);
  padding: 11px 13px;
  font-size: 13.5px;
  line-height: 1.4;
  margin-bottom: 20px;
  text-align: center;
}

/* `.alert` is red because something went wrong. A request that reached the firm is the opposite —
 * it is the journey completing — and red would tell somebody their request had failed at the exact
 * moment it succeeded. Same box, same spacing, green ink. */
.alert--success {
  background: var(--success-bg);
  border-color: var(--success-line);
  color: var(--success);
}

/* ── Fine print (consent account + links, error code) ─────────────────── */

.account {
  margin: 24px 0 0;
  font-size: 13px;
  color: var(--cream-dim);
  text-align: center;
}

.account .who {
  color: var(--cream);
}

/* "Create an account" / inline actions — brand cream, not the UA default blue. */
.account a {
  color: var(--cream);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.account a:hover {
  color: var(--button-hover);
}

.links {
  margin: 12px 0 0;
  font-size: 13px;
  text-align: center;
  color: var(--cream-faint);
}

.links a {
  color: var(--cream-dim);
  text-decoration: none;
}

.links a:hover {
  color: var(--cream);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.links-sep {
  margin: 0 6px;
  color: var(--cream-faint);
}

.error-code {
  display: inline-block;
  margin-top: 16px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--cream-dim);
}

.centered {
  text-align: center;
}

/* ── Motion ───────────────────────────────────────────────────────────── */

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .stage {
    animation: none;
  }

  * {
    transition-duration: 0.01ms !important;
  }
}

.scopes {
  margin: 1.5rem 0 1.75rem;
}

.scopes-label {
  margin: 0 0 0.75rem;
  font-size: 0.8125rem;
  letter-spacing: 0.02em;
  color: var(--cream-dim);
}

.scope-chips {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.scope-chip {
  display: inline-flex;
  align-items: center;
  padding: 0.3125rem 0.6875rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  line-height: 1;
  color: var(--cream);
  background: rgba(var(--cream-rgb), 0.06);
  border: 1px solid var(--field-border);
  border-radius: 999px;
}

/* ── Per-field validation ─────────────────────────────────────────────────
 *
 * The demo-request form asks for six things, so a single banner at the top cannot say WHICH one is
 * wrong — the reader has to re-scan every field to find out. These put the message under the field it
 * belongs to and tint that field's outline, so the eye lands on the problem rather than hunting.
 *
 * Keyed off `aria-invalid` rather than a bespoke class: the attribute the screen reader needs is the
 * same fact the styling needs, so there is one thing to set and it cannot be set inconsistently.
 */

.field-input[aria-invalid="true"] {
  border-color: var(--danger-line);
}

.field-input[aria-invalid="true"]:focus {
  border-color: var(--danger);
  box-shadow: 0 0 0 1px var(--danger);
}

.field-input[aria-invalid="true"]:focus + .field-label,
.field-input[aria-invalid="true"]:not(:placeholder-shown) + .field-label {
  color: var(--danger);
}

/* Sits in the 16px gap `.field` already reserves, so showing one shifts nothing below it. */
.field-error {
  display: block;
  margin: 6px 0 0 1px;
  font-family: var(--font-body);
  font-size: 12.5px;
  line-height: 1.35;
  color: var(--danger);
}

/* ── Multi-line input ─────────────────────────────────────────────────── */

.field-input--area {
  height: auto;
  min-height: 92px;
  padding: 18px 16px 12px;
  line-height: 1.45;
  resize: vertical;
}

/* A textarea is never empty-looking enough for :placeholder-shown to float the label reliably at
 * rest, so its label starts raised and stays there. */
.field--area .field-label {
  top: 0;
  font-size: 12px;
  color: var(--cream-dim);
}

/* ── Two-up field row (name + firm, size + role) ──────────────────────── */

.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 12px;
}

@media (max-width: 460px) {
  .field-row {
    grid-template-columns: 1fr;
  }
}

/* ── Confirmation (post-submit booking step) ──────────────────────────── */

.confirm {
  margin: 0 0 22px;
  padding: 13px 15px;
  font-size: 13.5px;
  line-height: 1.45;
  color: var(--cream);
  background: rgba(var(--green-rgb), 0.1);
  border: 1px solid var(--green-glow);
  border-radius: var(--radius);
  text-align: center;
}

/* ── Neutral notice ───────────────────────────────────────────────────────
 *
 * Distinct from `.alert`, which is red because something went wrong. "Sque is available by
 * invitation" is not a fault — it is the product working as designed — and rendering it in danger
 * colours tells a prospective customer they have made a mistake on their first contact with us.
 * Cream-dim on a faint wash: informational, not alarming.
 */

.notice {
  /* Symmetric, and both halves are load-bearing. The TOP margin is because `.btn--primary` carries
   * none of its own below it, so a notice after a button used to touch it. The BOTTOM margin is
   * because on the request-access page a notice is followed by a form, which had the same problem in
   * the other direction — a top-only margin fixed one page by breaking the other. */
  margin: 22px 0;
  padding: 12px 14px;
  font-size: 13.5px;
  line-height: 1.45;
  color: var(--cream-dim);
  background: rgba(var(--cream-rgb), 0.04);
  border: 1px solid var(--field-border);
  border-radius: var(--radius);
  text-align: center;
}

.notice strong {
  color: var(--cream);
  font-weight: 600;
}

/* ── Form section heading ─────────────────────────────────────────────────
 *
 * The demo form asks six things; without a break it reads as one undifferentiated column. This
 * separates "who you are" from "what you need" so the eye has a resting point.
 */

.form-legend {
  margin: 26px 0 14px;
  font-family: var(--font-body);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cream-faint);
}

.form-legend:first-of-type {
  margin-top: 0;
}

/* ── Wider lede ───────────────────────────────────────────────────────────
 *
 * `.lede` is capped at 30ch, which is right for the one-line prompts on login and registration but
 * breaks a two-sentence message onto three lines. A modifier rather than a change to `.lede` itself,
 * because that class is shared by every page here and widening it would re-wrap all of them.
 *
 * Only useful together with `stage--wide`: the stage is 360px by default, so a wider max-width on the
 * paragraph has nothing to expand into.
 */

.lede--wide {
  max-width: 46ch;
}

/* ── Checkbox (marketing consent) ─────────────────────────────────────────
 *
 * Restyled to the outlined-field language rather than left native: `accent-color` on a system box
 * paints a filled square in the browser's blue-ish default, which on this near-black page is the one
 * element that looks borrowed from another site.
 *
 * `appearance: none` changes the PAINT only. It is still an `<input type="checkbox">` inside a
 * `<label>`, so it stays keyboard-reachable, is announced correctly, and toggles on label click —
 * none of which survives the usual trick of hiding the input behind a styled pseudo-element.
 *
 * The tick is a data URI for the same reason the select chevron is: no script, no extra element, and
 * nothing to load.
 */

.checkbox {
  display: grid;
  grid-template-columns: 18px 1fr;
  gap: 11px;
  /* `start`, not `center`: the label runs to two or three lines at this width, and centring would put
   * the box beside the middle line instead of the first. */
  align-items: start;
  margin: 2px 0 26px;
  cursor: pointer;
}

.checkbox-input {
  appearance: none;
  width: 18px;
  height: 18px;
  /* 2px down so the box sits on the text's cap height rather than its line box. */
  margin: 2px 0 0;
  background: transparent;
  border: 1px solid var(--field-border);
  border-radius: 5px;
  cursor: pointer;
  transition:
    border-color 0.15s ease,
    background-color 0.15s ease;
}

.checkbox:hover .checkbox-input {
  border-color: var(--field-border-hover);
}

.checkbox-input:checked {
  background-color: var(--green-lit);
  border-color: var(--green-lit);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='9' viewBox='0 0 11 9'%3E%3Cpath d='M1 4.6l2.9 2.9L10 1.4' fill='none' stroke='%23030303' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
}

.checkbox-input:focus-visible {
  outline: none;
  border-color: var(--green-lit);
  box-shadow: 0 0 0 1px var(--green-lit);
}

/* 13px and cream-dim to sit with `.consent-text` directly below it — the two are the same kind of
 * fine print, and a half-pixel difference between them reads as a mistake. */
.checkbox-text {
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.45;
  color: var(--cream-dim);
}

/* ── Booking dialog ───────────────────────────────────────────────────────
 *
 * A native `<dialog>`, not a div pretending to be one: it gets focus trapping, ESC-to-close, inert
 * background content and the top-layer stacking from the platform. Reimplementing those correctly is
 * a library's worth of work, and reimplementing them incorrectly is the usual outcome.
 *
 * The scheduler is FRAMED, never scripted in. Only `frame-src` is widened for its origin, so it
 * cannot reach this page's DOM, cookies or session — which the vendor's own embed script would have
 * needed. That matters more here than on a marketing site: this origin also serves login.
 */

.modal {
  width: min(92vw, 468px);
  max-width: none;
  height: min(88vh, 720px);
  padding: 0;
  overflow: hidden;
  color: var(--cream);
  background: var(--ink-void);
  border: 1px solid var(--field-border);
  border-radius: var(--radius);
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.6);
}

.modal::backdrop {
  background: rgba(3, 3, 3, 0.72);
}

.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 13px 10px 13px 16px;
  border-bottom: 1px solid var(--field-border);
}

.modal-title {
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--cream-dim);
}

.modal-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  font-size: 20px;
  line-height: 1;
  color: var(--cream-dim);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 8px;
  cursor: pointer;
  transition:
    color 0.15s ease,
    border-color 0.15s ease;
}

.modal-close:hover {
  color: var(--cream);
  border-color: var(--field-border);
}

.modal-close:focus-visible {
  outline: none;
  color: var(--cream);
  border-color: var(--green-lit);
  box-shadow: 0 0 0 1px var(--green-lit);
}

/* `calc` rather than `flex: 1` because the dialog is `overflow: hidden` and a percentage height on an
 * iframe inside a flex column collapses to zero in Safari. */
.modal-frame {
  display: block;
  width: 100%;
  height: calc(100% - 59px);
  border: 0;
  background: var(--ink-void);
}

/* ── Invitation offer (post-login hand-off) ───────────────────────────────
 *
 * One card per invitation, each its own form. Cards rather than a list because the page can carry
 * more than one and each has its own submit button — a shared list with buttons in it reads as a
 * single choice, which is exactly what this is not.
 *
 * NOT `.notice` and NOT `.alert`. Being invited is neither informational fine print nor a fault: it
 * is the offer the page exists to make, so it takes the green accent the product uses for a
 * favourable state, one step calmer than `.confirm` (which reports something already done).
 */

.invite {
  margin: 0 0 14px;
  padding: 15px 16px;
  background: rgba(var(--green-rgb), 0.07);
  border: 1px solid var(--green-glow);
  border-radius: var(--radius);
  text-align: left;
}

/* The button is the card's own primary action, so it loses the top margin `.btn--primary` carries for
 * standalone use — otherwise every card gains a gap the border makes obvious. */
.invite .btn--primary {
  margin-top: 14px;
}

.invite-body {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.invite-firm {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  color: var(--cream);
  /* A firm's name is user-supplied and unbounded; wrap rather than overflow the card. */
  overflow-wrap: anywhere;
}

.invite-meta {
  margin: 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--cream-dim);
  overflow-wrap: anywhere;
}

.invite-expiry {
  margin: 0;
  font-size: 12.5px;
  color: var(--cream-dim);
}
