/* Shared styling for the auth surfaces: /sign-in/ and /account/.
 *
 * Deliberately NOT in styles.css. That file is loaded by all 16 hand-written
 * pages, and none of them need any of this — putting it there would add weight
 * to every page on the site to style two.
 *
 * Everything below is built from the tokens styles.css already defines, so both
 * light and dark themes come for free and stay in step with the rest of the
 * site — with one documented exception, immediately below.
 */

/* ⚠️ An error colour that is legible AS TEXT, which --status-error is not in
   dark mode. --status-error is #C97A4A there, which lands at 4.33:1 on
   --bg-surface — fine for a border or a fill, short of the 4.5:1 that 15px text
   needs. This is a real gap in the palette rather than a local preference, and
   it was already affecting .auth-status.bad before the delete button existed.
   Scoped here rather than added to styles.css, which all 16 pages load, until
   something outside the auth surfaces needs it too.

   Light is unchanged from the token; only dark is lightened. 6.9:1 on
   --bg-surface, 7.8:1 on --bg-page. */
:root { --status-error-text: #8A4B2C; }
:root[data-theme="dark"] { --status-error-text: #E7A57C; }

/* ⚠️ And the foreground to use ON that fill, which cannot be white in both
   themes. --status-error DARKENS to #8A4B2C in light and LIGHTENS to #C97A4A in
   dark, so a fixed white label is 6.7:1 on one and 3.3:1 on the other. The
   readable pairing inverts with the theme. */
:root { --status-error-on: var(--white); }
:root[data-theme="dark"] { --status-error-on: #0E1917; }

/* ⚠️ `hidden` MUST WIN over any display rule in this file.
 *
 * The browser hides `[hidden]` with a UA rule of the lowest possible weight, so
 * ANY author `display` declaration silently defeats it — `.auth-actions` sets
 * `display: flex`, and every element carrying both stayed on screen with its
 * `hidden` attribute set and honoured by every script that read it.
 *
 * Live on production 2026-08-19: the resend button appeared on the password-
 * reset panel, where it is deliberately not offered, permanently disabled
 * because the cooldown that enables it is only started when the button is
 * meant to exist. It looked like a broken button rather than one that should
 * not have been there.
 *
 * ⚠️ It also defeated the tests. Every check asserted `element.hidden === true`
 * — the PROPERTY, which was correct throughout. Nothing asked the browser what
 * it was actually painting. Assert on `getComputedStyle().display` for anything
 * whose visibility matters.
 *
 * `!important` on purpose, and the one place in this file it is warranted:
 * `hidden` means "this must not render", and no layout rule may outrank it.
 * Scoped to the auth surfaces, so it cannot reach the other 14 pages.
 *
 * The two `[hidden]` rules further down are now redundant. Kept because they
 * document intent at the element they belong to. */
[class*="auth-"][hidden],
.auth-wrap [hidden] { display: none !important; }

.auth-wrap {
  max-width: 480px;
  margin: 0 auto;
  padding: var(--space-7) var(--space-4) var(--space-9);
}

/* The account page only. Sign-in is a single column of fields and reads better
   narrow; the account page has a row of three actions that wraps at 480px, and
   a wrapped button row makes the third look like it belongs to something else.
   Widening one page beats shrinking the buttons on both. */
.auth-wrap-wide { max-width: 660px; }

.auth-panel {
  background: var(--bg-surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  box-shadow: var(--shadow-sm);
  margin-bottom: var(--space-5);
}
.auth-panel[hidden] { display: none; }

/* For a panel that asks for a decision rather than being part of the flow — the
   heavier left border marks it out. Unused right now: the one-time progress
   import was the only caller and was dropped. Kept because Phase 7's banner
   editor and Phase 9's dashboards will both want it, and it is one line. */
.auth-panel-highlight { border-color: var(--fg-brand); border-left-width: 4px; }

.auth-panel h1 { font-size: 1.5rem; color: var(--fg-heading); margin-bottom: var(--space-2); }
.auth-panel h2 { font-size: 1.2rem; color: var(--fg-heading); margin-bottom: var(--space-2); }
.auth-lede { color: var(--fg-1); margin-bottom: var(--space-5); }
.auth-muted { color: var(--fg-2); font-size: var(--fs-label); margin-bottom: var(--space-4); }

.auth-panel label {
  display: block;
  font-size: var(--fs-label);
  font-weight: 600;
  color: var(--fg-heading);
  margin-bottom: var(--space-1);
}

/* ⚠️ THE LABELS ARE HIDDEN, NOT REMOVED, AND THAT DISTINCTION IS THE WHOLE
 * POINT. The visible label text moved into the placeholder on 2026-08-27 so the
 * fields read as the design intends — but a placeholder is NOT an accessible
 * name. Screen readers treat it inconsistently, some ignore it entirely, and it
 * disappears the moment anyone types, taking the only description of the field
 * with it. Deleting the <label> would leave three unnamed inputs on the one form
 * that asks for a password.
 *
 * So each label stays in the DOM, still tied to its input by `for`, and is
 * removed from view only. Do not "tidy" these away.
 *
 * `clip-path` rather than `display:none` or `visibility:hidden`, both of which
 * take the element out of the accessibility tree along with the layout. */
.auth-label-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* A field with an icon sitting inside it. The wrapper owns the bottom margin,
 * the same arrangement `.auth-pw` already uses, so `.auth-hint`'s -12px tuck
 * keeps landing where it did rather than depending on margin collapsing through
 * a new element. */
.auth-field { position: relative; margin-bottom: var(--space-4); }

/* ⚠️ `.auth-panel` IS IN THESE SELECTORS ON PURPOSE, AND REMOVING IT BREAKS
 * THEM SILENTLY. `.auth-panel input` below sets `padding: 10px 12px` — a
 * SHORTHAND, so it rewrites padding-left too — and at (0,1,1) it ties with a
 * bare `.auth-field input`. A tie is settled by source order, so the icon
 * padding lost and the placeholder ran underneath the icon. Valid CSS, correct
 * class names, and only a measurement finds it: the first version of this
 * shipped that way and was caught by comparing the icon's right edge against
 * the input's padding, not by looking.
 *
 * (0,2,1) wins outright and keeps winning if these rules are ever moved. */
.auth-panel .auth-field input { margin-bottom: 0; padding-left: 2.5rem; }
.auth-panel .auth-field .auth-pw input { padding-left: 2.5rem; }

.auth-field-icon {
  position: absolute;
  left: 12px;
  top: 11px;
  width: 16px;
  height: 16px;
  color: var(--fg-2);
  pointer-events: none;   /* the icon must never eat a click meant for the field */
  z-index: 1;
}
.auth-field-icon svg { width: 100%; height: 100%; display: block; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

/* The icon is decorative — the label already names the field — so it is
 * aria-hidden in the markup and gets no colour of its own in dark mode beyond
 * the token, which does flip. */

.auth-panel input {
  display: block;
  width: 100%;
  padding: 10px 12px;
  margin-bottom: var(--space-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--bg-page);
  color: var(--fg-1);
  font-family: inherit;
  font-size: 15px;
}
.auth-panel input:focus-visible { outline: 2px solid var(--fg-brand); outline-offset: 1px; }

/* ⚠️ PLACEHOLDERS CARRY HELP TEXT HERE, SO A CLIPPED ONE IS A SENTENCE THAT
 * STOPS MID-WORD. Every placeholder on these forms was measured against the
 * field's inner width at a 375px viewport and shortened until it fits — three
 * of them did not, and only measuring found it. Below about 340px the longest
 * still run out of room, and this makes that read as truncation rather than as
 * the end of the sentence.
 *
 * ⚠️ If you lengthen a placeholder, measure it. The password fields have
 * ~227px of usable width at 375px, not the ~283px the others get, because the
 * Show button takes 4.25rem of padding-right. */
.auth-panel input::placeholder { text-overflow: ellipsis; }

/* Reveal control for password fields.
 *
 * A typo in a password field is invisible, and on sign-up it costs a whole email
 * round trip to discover. The wrapper owns the bottom margin rather than the
 * input, so the negative margin on .auth-hint below keeps landing where it did
 * instead of depending on margin collapsing through the new element. */
.auth-pw { position: relative; margin-bottom: var(--space-4); }
.auth-field .auth-pw { margin-bottom: 0; }
.auth-pw input { margin-bottom: 0; padding-right: 4.25rem; }

.auth-pw-toggle {
  position: absolute;
  top: 9px;
  right: 6px;
  padding: 3px 6px;
  background: none;
  border: none;
  border-radius: var(--radius-sm, 4px);
  color: var(--fg-brand);
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.auth-pw-toggle:hover { text-decoration: underline; }
.auth-pw-toggle:focus-visible { outline: 2px solid var(--fg-brand); outline-offset: 1px; }

.auth-hint { font-size: 12px; color: var(--fg-2); margin: -12px 0 var(--space-4); }

/* ── Consent checkbox ──────────────────────────────────────────────────────
   Used by the sign-up form on /sign-in/ and the preference toggle on
   /account/. One implementation because the two must look and behave
   identically: someone who ticks a box at sign-up and later finds a different
   looking control on their account has to work out whether it is the same
   setting.

   ⚠️ THE CHECKBOX ITSELF IS NOT RESTYLED, and that is deliberate. A custom
   control here would mean owning the indeterminate state, the focus ring, the
   forced-colors rendering and the platform's own "checked" affordance — on the
   one control in the site whose entire job is to be an unambiguous record of
   what someone chose. `accent-color` tints the native control and leaves all of
   that alone. */
/* Sits after a field whose hint already carries `margin-bottom: space-4`, and
   needs visible separation from it rather than none — this is a different
   question being asked, not more detail about the password. */
.auth-check { margin: var(--space-5) 0 var(--space-4); }

/* ⚠️ `.auth-panel` IS PART OF THIS SELECTOR AND MUST STAY.
   `.auth-panel label { display: block }` above is (0,1,1); a bare
   `.auth-check-label` is (0,1,0) and LOSES, which stacks the box above its
   text instead of beside it. Shipped that way once. The same rule is also why
   font-weight and margin-bottom are reset here rather than left to inherit:
   it sets 600 and a bottom margin for field labels, and a checkbox label is
   not a field label. */
.auth-panel .auth-check-label {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  cursor: pointer;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.5;
  color: var(--fg-1);
  margin-bottom: 0;
}

.auth-check-label input[type="checkbox"] {
  accent-color: var(--fg-brand);
  width: 16px;
  height: 16px;
  margin: 2px 0 0;
  flex-shrink: 0;
  cursor: pointer;
}

/* The hint carries the negative margin that pulls other hints under their
   field. Here the label directly above is not a field, so it would pull the
   text into the label. */
.auth-check-hint { margin: var(--space-2) 0 0 calc(16px + var(--space-3)); }

.auth-check[hidden] { display: none; }

/* ⚠️ THE ONE HINT THAT SITS BELOW A BUTTON, NOT BELOW A FIELD.
   `.auth-hint` carries `margin-top: -12px` to tuck a hint under the input it
   describes. Applied to the terms-and-privacy line, which follows the submit
   button, that -12px pulled the text UP ONTO the button and rendered the two
   on top of each other. Reset explicitly rather than by dropping .auth-hint,
   because everything else about the hint — size, colour — is still wanted. */
#auth-legal { margin: var(--space-4) 0 0; }

/* A standalone note rather than a hint about the field above it. Cancels
   `.auth-hint`'s tuck-under margin, which otherwise pulls it against whatever
   precedes it and makes two separate statements read as one wrapped one. */
.auth-note { margin: var(--space-5) 0 0; }

/* The "what does an account get me?" line, directly under the lede. The lede
   carries the panel's bottom margin, so this only needs to close the gap to it
   and then restore separation before the first field. */
.auth-why-line { margin: calc(var(--space-4) * -1) 0 var(--space-5); font-size: var(--fs-label); }
.auth-why-line[hidden] { display: none; }

/* ── The short version of terms and privacy ────────────────────────────────
   A <details>, so it costs nothing when closed and needs no script.

   ⚠️ `.auth-panel summary` would inherit nothing useful, but `.auth-panel
   label`'s display:block problem is the reminder here: check specificity
   against the panel's element selectors before assuming a class wins. */
.auth-summary {
  margin-top: var(--space-4);
  border: 1px solid var(--line);
  border-radius: 6px;
  overflow: hidden;
}
.auth-summary[hidden] { display: none; }

.auth-summary summary {
  list-style: none;
  cursor: pointer;
  padding: var(--space-3) var(--space-4);
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-label);
  font-weight: 600;
  color: var(--fg-heading);
}
.auth-summary summary::-webkit-details-marker { display: none; }
.auth-summary summary:hover { background: var(--bg-sunken); }
.auth-summary summary:focus-visible { outline: 2px solid var(--fg-brand); outline-offset: -2px; }

.auth-chevron {
  width: 14px; height: 14px; flex-shrink: 0;
  stroke: var(--fg-brand); stroke-width: 2.5; fill: none;
  stroke-linecap: round; stroke-linejoin: round;
  transition: transform .18s;
}
.auth-summary[open] .auth-chevron { transform: rotate(90deg); }

.auth-summary ul {
  margin: 0;
  padding: 0 var(--space-4) var(--space-3) calc(var(--space-4) + 18px);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.auth-summary li { font-size: 13px; line-height: 1.55; color: var(--fg-1); }
.auth-summary li::marker { color: var(--fg-brand); }

.auth-summary-foot {
  margin: 0;
  padding: 0 var(--space-4) var(--space-3);
  font-size: 12px;
  color: var(--fg-2);
}

@media (prefers-reduced-motion: reduce) {
  .auth-chevron { transition: none; }
}

/* ── Document modal ────────────────────────────────────────────────────────
   A native <dialog>. `showModal()` supplies the focus trap, the inert
   background, Escape-to-close and focus return; the CSS here only has to make
   it look like the site. */
.auth-doc-modal {
  width: min(760px, calc(100vw - 2rem));
  height: min(80vh, 900px);
  max-width: none;
  max-height: none;
  padding: 0;
  border: none;
  border-radius: 10px;
  background: var(--bg-surface);
  color: var(--fg-1);
  box-shadow: 0 24px 64px rgba(0, 0, 0, .28);
  overflow: hidden;
}
.auth-doc-modal::backdrop { background: rgba(12, 20, 18, .55); }

/* Grid rather than flex, so the iframe takes exactly the space the header
   leaves. With flex the iframe's default intrinsic height fights `1fr` and it
   overflows the dialog by the height of the header. */
.auth-doc-modal { display: none; }
.auth-doc-modal[open] { display: grid; grid-template-rows: auto 1fr; }

.auth-doc-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--line);
  background: var(--bg-sunken);
}
.auth-doc-head h2 { font-size: var(--fs-label); font-weight: 600; color: var(--fg-heading); margin: 0; }

.auth-doc-actions { display: flex; align-items: center; gap: var(--space-4); }
.auth-doc-actions a { font-size: 12px; color: var(--fg-brand); }
.auth-doc-actions button {
  background: none;
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 4px 10px;
  font: inherit;
  font-size: 12px;
  color: var(--fg-1);
  cursor: pointer;
}
.auth-doc-actions button:hover { background: var(--bg-surface); }
.auth-doc-actions button:focus-visible,
.auth-doc-actions a:focus-visible { outline: 2px solid var(--fg-brand); outline-offset: 2px; }

.auth-doc-modal iframe { width: 100%; height: 100%; border: 0; display: block; }
.auth-blocked { color: var(--status-error-text); margin-top: var(--space-2); }
.auth-blocked a { color: var(--status-error-text); }

/* No reserved height. Turnstile's Managed mode resolves with no visible widget
   for most visitors, and `appearance: interaction-only` keeps it that way — so
   this box is 0px tall until a challenge is genuinely required. */
.auth-turnstile:not(:empty) { margin-bottom: var(--space-4); }

.auth-panel button[type="submit"], .auth-btn {
  display: inline-block;
  padding: 10px 18px;
  border: none;
  border-radius: var(--radius-md);
  background: var(--bg-brand);
  color: var(--fg-on-brand);
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
}
.auth-panel button[type="submit"] { width: 100%; }
.auth-panel button[type="submit"]:hover, .auth-btn:hover { filter: brightness(1.08); }
.auth-panel button[type="submit"]:disabled,
.auth-btn:disabled { opacity: .55; cursor: not-allowed; filter: none; }

.auth-btn-quiet {
  background: transparent;
  color: var(--fg-brand);
  border: 1px solid var(--line-strong);
}

.auth-btn-danger { background: var(--status-error); color: var(--status-error-on); }

/* Outlined rather than solid, and the distinction is doing work. This button
   opens a panel; it deletes nothing. Solid red is reserved for the button that
   actually does it, so the colour escalates as the consequence does — red text,
   then red border, then red fill. Two solid red buttons on one screen would
   make the irreversible one no louder than the disclosure that reveals it. */
.auth-btn-danger-quiet {
  background: transparent;
  color: var(--status-error-text);
  border: 1px solid var(--status-error);
}
.auth-btn-danger-quiet:hover {
  background: var(--status-error);
  color: var(--status-error-on);
  filter: none;
}

/* The one irreversible thing on the site. The border is the only signal that
   survives a page skimmed rather than read. */
.auth-panel-danger { border-color: var(--status-error); border-left-width: 4px; }
.auth-panel-danger h2 { color: var(--status-error-text); }

.auth-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; margin-top: var(--space-4); }

.auth-status { margin-top: var(--space-3); font-size: var(--fs-label); min-height: 1.2em; }
.auth-status.bad { color: var(--status-error-text); font-weight: 600; }
.auth-status.good { color: var(--status-ok); font-weight: 600; }

.auth-switch {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line-soft);
}

.auth-link {
  background: none;
  border: none;
  padding: 0;
  color: var(--fg-brand);
  font-family: inherit;
  font-size: var(--fs-label);
  text-align: left;
  cursor: pointer;
  text-decoration: underline;
}
.auth-link[hidden] { display: none; }

.auth-field-row { display: flex; justify-content: space-between; gap: var(--space-3); align-items: baseline; }
.auth-field-row dt { font-size: var(--fs-label); color: var(--fg-2); }
.auth-field-row dd { font-size: var(--fs-label); color: var(--fg-1); word-break: break-all; }
