.modal-overlay { position: fixed; inset: 0; background: rgba(15,23,42,.45); backdrop-filter: blur(4px);
      display: none; place-items: center; z-index: 100; padding: clamp(12px, 4vw, 20px); }
    .modal-overlay.open { display: grid; }
    /* Stops the page scrolling behind the dialog while it is open. */
    body.modal-open { overflow: hidden; }
    .modal { width: 100%; max-width: 400px; background: #fff; border: 1px solid var(--color-border);
      border-radius: 18px; padding: clamp(20px, 6vw, 30px); position: relative;
      box-shadow: 0 24px 48px rgba(15,23,42,.18);
      /* dvh declared after vh so it wins where supported. 100vh on a phone
         includes the strip behind the URL bar, so a vh-derived cap lets the
         dialog slide under the browser chrome and hide its own submit button. */
      max-height: calc(100vh - 40px); max-height: calc(100dvh - 24px); overflow-y: auto; }
    .modal h2 { margin: 4px 0 4px; font-size: 22px; color: #0f172a; }
    .modal .muted { margin: 0 0 16px; color: #64748b; font-size: 15px; }
    .modal .label { margin: 14px 0 0; }
    .modal .input { margin-top: 6px; }
    /* Password reveal. Sits inside the field's border so it reads as part of the
       input. .password-field .input outranks the .input rule in components.css
       on specificity, which is how the right padding survives without editing a
       ported file. The focus ring comes from tokens.css, which this page links. */
    .password-field { position: relative; }
    .password-field .input { padding-right: 62px; }
    .password-toggle { position: absolute; top: 50%; right: 5px; transform: translateY(-50%);
      padding: 6px 10px; border: 0; border-radius: 8px; background: none;
      color: var(--color-brand); font: inherit; font-size: 13px; font-weight: 650;
      line-height: 1.4; cursor: pointer; }
    .password-toggle:hover { background: rgba(26, 115, 232, .08); }
    .modal .submit { width: 100%; margin-top: 20px; }
    /* Terms consent. The box aligns to the first line of its label rather than
       centring against a two or three line sentence. accent-color brands the
       native checkbox, so there is no custom control to keep in step. */
    .consent { display: flex; align-items: flex-start; gap: 10px; margin-top: 16px; }
    .consent input[type="checkbox"] {
      flex: 0 0 auto; width: 18px; height: 18px; margin: 1px 0 0;
      accent-color: var(--color-brand); cursor: pointer;
    }
    .consent label { font-size: 13.5px; line-height: 1.5; color: #475569; cursor: pointer; }
    .consent a { color: var(--color-brand); font-weight: 600; }
    .consent a:hover { text-decoration: underline; }
    /* The cross glyph is 24px, well under the ~44px a thumb needs. Padding
       grows the hit area; top and right come in by the same amount so the
       glyph still sits exactly where it did. */
    .modal .close { position: absolute; top: 4px; right: 8px; padding: 8px; background: none; border: 0;
      color: #94a3b8; font-size: 24px; cursor: pointer; line-height: 1; }
    .modal .close:hover { color: #475569; }
    .modal .switch { text-align: center; color: #64748b; font-size: 14px; margin-top: 18px; }
    .modal .switch a { color: var(--color-brand); cursor: pointer; font-weight: 600; }
    .auth-error { background: #fee2e2; border: 1px solid #fecaca; color: #b91c1c; font-size: 14px;
      padding: 10px 12px; border-radius: 10px; margin: 0 0 4px; }
    /* Smallest phones. The dialog is already fluid, so this only trims type
       that was sized for the 400px max-width rather than a 320px viewport. */
    @media (max-width: 400px) {
      .modal h2 { font-size: 20px; }
      .modal .muted { font-size: 14.5px; }
      .consent label { font-size: 13px; }
    }

/* Honeypot: off screen for people, still filled in by naive bots. */
.hp-field { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }
