@font-face {
  font-family: "Archivo";
  src: url("/fonts/archivo.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-stretch: 62.5% 125%;
  font-display: swap;
}

@font-face {
  font-family: "Plex Mono";
  src: url("/fonts/plexmono.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: "Plex Mono";
  src: url("/fonts/plexmono-med.woff2") format("woff2");
  font-weight: 500;
  font-display: swap;
}

:root {
  --paper: #f2f1ec;
  --paper-raised: #fbfaf7;
  --ink: #141c2a;
  --ink-2: #4a5568;
  --ink-3: #646f80;
  --hair: #d9d6cc;
  --hair-strong: #c7c3b6;
  --control-border: #8d8776;
  --navy: #101a28;
  --navy-2: #182437;
  --navy-hair: #2a3850;
  --green: #0c7c53;
  --green-bright: #17a56f;
  --green-dim: #0a5c3f;
  --green-pale: #e7f2ec;
  --red: #a33a3a;
  --red-pale: #f7eaea;
  --red-dim: #7f2525;
  --amber: #c48a2a;
  --sans: "Archivo", system-ui, sans-serif;
  --mono: "Plex Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;
}

* { box-sizing: border-box; }

[hidden] { display: none !important; }

html {
  min-height: 100%;
  background: var(--paper);
}

/* A column the footer always sits at the bottom of: main grows to fill
   whatever room a short page leaves rather than the body growing past the
   viewport by the footer's own height, which is what a bare min-height on
   .page (measured against the viewport, not the space actually left) did. */
body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

/* min-width: 0 overrides a flex item's default auto, which otherwise sizes
   main to its widest descendant's content (the invitations table) instead of
   the viewport — the exact horizontal overflow this flex column exists to
   prevent. */
main {
  flex: 1 0 auto;
  min-width: 0;
}

button,
input { font: inherit; }

a { color: inherit; }

code {
  font-family: var(--mono);
  font-size: .92em;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

::selection {
  background: var(--green);
  color: #fff;
}

/*
 * Shell: a thin navy identity bar, an optional administration nav, the page
 * itself, and a footer. Every page shares this skeleton; only the middle
 * changes shape between a centred auth panel and a full-width console.
 */
.site-header {
  position: relative;
  z-index: 2;
  height: 56px;
  color: #fff;
  background: var(--navy);
  border-bottom: 1px solid var(--navy-hair);
}

.site-header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  max-width: 72rem;
  height: 56px;
  margin: 0 auto;
  padding: 0 clamp(18px, 4vw, 40px);
}

.wordmark {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  text-decoration: none;
  font-family: var(--mono);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: .08em;
}

.wordmark-open { color: #fff; }

.wordmark-blt {
  margin-left: .22em;
  color: var(--green-bright);
}

.wordmark img {
  display: block;
  width: auto;
  height: 28px;
}

.header-account {
  display: flex;
  align-items: center;
  gap: 1.1rem;
  min-width: 0;
  font-size: 14px;
}

/* overflow: hidden gives this flex item an automatic minimum size of 0
   instead of its content's full width, so the address truncates instead of
   pushing the header wider than the viewport on a narrow phone. */
.header-account-email {
  overflow: hidden;
  color: #b9c4d6;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The sign-out form, not the button inside it, is the flex item here — it
   needs the same protection from shrinking so "Sign out" never wraps. */
.header-account .inline-form { flex: 0 0 auto; }

.header-account .link-button { color: #fff; }

.header-account .link-button:hover { color: #b9c4d6; }

.console-nav {
  position: relative;
  z-index: 1;
  background: var(--paper-raised);
  border-bottom: 1px solid var(--hair-strong);
}

.console-nav-inner {
  display: flex;
  gap: 28px;
  width: 100%;
  max-width: 72rem;
  margin: 0 auto;
  padding: 0 clamp(18px, 4vw, 40px);
  /* A last resort below 600px, not the everyday behaviour: the narrower gap
     and padding there normally fit all three links, so this only scrolls if
     something still doesn't — and scrolls inside the nav, never the page. */
  overflow-x: auto;
}

.console-nav a {
  display: block;
  padding: 14px 0 12px;
  color: var(--ink-2);
  font-size: 14.5px;
  text-decoration: none;
  border-bottom: 2px solid transparent;
}

.console-nav a:hover { color: var(--ink); }

.console-nav a[aria-current="page"] {
  color: var(--ink);
  font-weight: 600;
  border-bottom-color: var(--green);
}

.page {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  /* align-content: start keeps the panel and the optional paragraph below it
     packed together at the top — without it the auto row tracks stretch to
     fill the container, and "start" on the items alone just moves each one
     to the top of its own, now much taller, row. */
  align-content: start;
  place-items: start center;
  padding: clamp(32px, 7vh, 72px) 24px 48px;
  background:
    linear-gradient(135deg, rgb(16 26 40 / 4%) 25%, transparent 25%) 0 0 / 28px 28px,
    var(--paper);
}

/* The console has no panel to centre: the body handles its own width and
   centring, so the page itself steps out of the way entirely. */
.page.console {
  display: block;
  padding: 0;
}

.auth-stage {
  display: grid;
  grid-template-columns: minmax(18rem, .9fr) minmax(22rem, 1.1fr);
  width: min(100%, 58rem);
  min-height: 34rem;
  background: var(--paper-raised);
  border: 1px solid var(--hair-strong);
  box-shadow: 0 24px 70px rgb(16 26 40 / 10%);
}

.auth-identity {
  display: flex;
  flex-direction: column;
  padding: clamp(32px, 5vw, 56px);
  overflow: hidden;
  color: #fff;
  background:
    radial-gradient(circle at 10% 85%, rgb(23 165 111 / 26%), transparent 34%),
    linear-gradient(145deg, var(--navy-2), var(--navy));
  border-right: 1px solid var(--navy-hair);
}

.identity-kicker,
.identity-status {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .12em;
}

.identity-kicker { color: var(--green-bright); }

.identity-title {
  margin: auto 0 18px;
  font-size: clamp(32px, 4vw, 48px);
  font-weight: 620;
  letter-spacing: -.035em;
  line-height: 1.02;
}

.identity-copy {
  max-width: 23rem;
  margin: 0;
  color: #b9c4d6;
  font-size: 14px;
  line-height: 1.6;
}

.identity-status {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-top: 44px;
  color: #b9c4d6;
  letter-spacing: .06em;
}

.identity-status i {
  width: 7px;
  height: 7px;
  background: var(--green-bright);
  border-radius: 50%;
  box-shadow: 0 0 0 4px rgb(23 165 111 / 13%);
}

.auth-workspace {
  display: grid;
  align-content: center;
  min-width: 0;
  padding: clamp(28px, 5vw, 56px);
}

.panel {
  width: 100%;
  max-width: 26rem;
  margin: 0 auto;
  background: var(--paper-raised);
  border: 0;
  padding: 0;
}

.below {
  margin-top: 20px;
  text-align: center;
  font-size: 14px;
  color: var(--ink-2);
}

.below a {
  color: var(--green-dim);
  font-weight: 600;
}

.below a:hover { color: var(--ink); }

.console-body {
  width: 100%;
  max-width: 66rem;
  margin: 0 auto;
  padding: 32px clamp(18px, 4vw, 40px) 48px;
}

h1 {
  margin: 0;
  color: var(--ink);
  font-size: 26px;
  font-weight: 640;
  letter-spacing: -.01em;
  line-height: 1.15;
  text-wrap: balance;
}

.intro {
  margin: 8px 0 0;
  color: var(--ink-2);
  font-size: 15.5px;
  line-height: 1.55;
}

form {
  display: grid;
  gap: 16px;
  margin-top: 26px;
}

label {
  display: grid;
  gap: 6px;
  color: var(--ink-2);
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 500;
}

.field-hint {
  margin: -6px 0 0;
  color: var(--ink-2);
  font-size: 13px;
  line-height: 1.5;
}

/* appearance: interaction-only renders nothing until Cloudflare decides a
   challenge is needed, so the container has no reserved height of its own;
   ":empty" drops it from the form's grid entirely so its gap does not double
   up against the button above and below it. Once a challenge does render,
   the flexible widget size fills the form's width on its own. */
.turnstile:empty { display: none; }

.notice {
  margin: 20px 0 0;
  padding: .7rem .8rem;
  color: #7f2525;
  background: var(--red-pale);
  border-left: 3px solid var(--red);
  font-size: .88rem;
}

/* Inputs must not inherit the label's type: entered text stays 16px Archivo
   regardless of the label above it, so iOS Safari never zooms on focus. */
input {
  width: 100%;
  min-height: 44px;
  padding: 0 12px;
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  background: #fff;
  border: 1px solid var(--control-border);
  border-radius: 0;
  outline: none;
  transition: border-color .15s, box-shadow .15s;
}

input:hover { border-color: var(--ink-2); }

input[aria-invalid="true"] {
  border-color: var(--red);
  box-shadow: inset 0 0 0 1px var(--red);
}

input:focus-visible {
  border-color: var(--green);
  box-shadow: 0 0 0 3px rgb(12 124 83 / 14%);
}

/* #6f7a8b rather than the browser default: 4.9:1 against the white field,
   readable without reading as entered text. */
input::placeholder { color: #6f7a8b; }

button {
  min-height: 44px;
  padding: 0 20px;
  color: #fff;
  background: var(--green);
  border: 1px solid var(--green);
  border-radius: 0;
  cursor: pointer;
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: .04em;
  text-transform: none;
  transition: background-color .15s, border-color .15s, transform .15s;
}

button::after {
  margin-left: .55rem;
  content: "→";
}

button:hover:not(:disabled) {
  background: #0a6947;
  border-color: #0a6947;
}

button:active:not(:disabled) { transform: translateY(1px); }

button:focus-visible,
a:focus-visible {
  outline: 2px solid var(--green-bright);
  outline-offset: 3px;
}

button:disabled {
  cursor: wait;
  opacity: .68;
}

.secondary {
  color: var(--ink);
  background: transparent;
  border-color: var(--control-border);
}

.secondary:hover:not(:disabled) {
  color: var(--ink);
  background: var(--paper);
  border-color: var(--ink);
}

.secondary::after { content: ""; }

/* An anchor that has to read as the primary action of the page. */
.button-link {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0 20px;
  color: #fff;
  background: var(--green);
  border: 1px solid var(--green);
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: .04em;
  text-align: center;
  text-decoration: none;
  text-transform: none;
  transition: background-color .15s, border-color .15s;
}

.button-link::after {
  margin-left: .55rem;
  content: "→";
}

.button-link:hover {
  color: #fff;
  background: #0a6947;
  border-color: #0a6947;
}

.actions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: .75rem;
  margin-top: 1.75rem;
}

.actions-single { grid-template-columns: 1fr; }

.page-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: .55rem 1.2rem;
  margin-top: 1.4rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--hair);
  font-size: 14px;
}

.page-links a,
.link-button {
  color: var(--green-dim, #0a5c3f);
  font-weight: 600;
  text-underline-offset: .2rem;
  text-decoration-thickness: 1px;
}

.page-links a:hover,
.link-button:hover { color: var(--ink); }

/* Lets the sign-out form sit among header or link-row content like any
   other inline entry. */
.inline-form {
  display: inline;
  margin: 0;
}

/* A button that has to sit among text links without shouting. */
.link-button {
  min-height: 0;
  padding: 0;
  background: none;
  border: 0;
  font-family: inherit;
  font-size: inherit;
  letter-spacing: normal;
  text-decoration: underline;
  text-transform: none;
  transition: color .15s;
}

.link-button::after { content: ""; }

.link-button:hover:not(:disabled) {
  background: none;
  border: 0;
}

.status {
  min-height: 0;
  margin: 1rem 0 0;
  padding: 0;
  color: var(--green);
  font-size: 14px;
}

.status:empty { margin: 0; }

.status:not(:empty) {
  padding: .7rem .8rem;
  background: var(--green-pale);
  border-left: 3px solid var(--green);
}

.status.error {
  color: #7f2525;
  background: var(--red-pale);
  border-left-color: var(--red);
}

.status.error:focus-visible {
  outline: 2px solid var(--red);
  outline-offset: 2px;
}

.error-message { margin: 0; }

/* Recovery actions offered alongside an error message. */
.error-actions {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem 1rem;
  margin-top: .5rem;
}

.error-actions a,
.error-actions .link-button {
  color: #7f2525;
  font-weight: 600;
  text-underline-offset: .2rem;
}

.error-actions a:hover,
.error-actions .link-button:hover { color: var(--ink); }

.site-footer { border-top: 1px solid var(--hair); }

.site-footer-inner {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: .5rem 1.5rem;
  width: 100%;
  max-width: 72rem;
  margin: 0 auto;
  padding: 16px clamp(18px, 4vw, 40px);
}

.footer-line {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .06em;
  color: var(--ink-3);
}

/*
 * Administration: invitations. The console has no card of its own — the
 * table is the one surface with a border — so the invite form rides beside
 * the heading instead of opening a section of its own.
 */
.title-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 20px 32px;
}

.console-summary {
  margin: 6px 0 0;
  color: var(--ink-2);
  font-size: 14.5px;
}

.summary-alert {
  color: var(--red-dim);
  font-weight: 600;
}

/* One field beside its button, not a stacked form: this row sits next to
   the heading, so it needs a fixed width rather than the auth forms' full
   width of a 26rem panel. */
.invite-form {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  margin-top: 0;
}

.invite-form label {
  width: 22rem;
  max-width: 100%;
}

.table-card {
  margin-top: 2rem;
  background: var(--paper-raised);
  border: 1px solid var(--hair-strong);
}

/* Hidden until the table is narrow enough to need it: the scroll container
   has no visible edge of its own to hint that it scrolls sideways. */
.scroll-hint {
  display: none;
  margin: 0;
  padding: 10px 14px 0;
  color: var(--ink-3);
  font-family: var(--mono);
  font-size: 11px;
}

/* position: relative makes this the containing block for its own
   position: absolute descendants (the visually-hidden caption and column
   label). Without it they position against the viewport using their static
   position inside the table's full, unscrolled width — which is exactly
   what was widening the whole page at narrow widths, not just the table's
   own (correctly contained) horizontal scroll. */
.table-scroll {
  position: relative;
  overflow-x: auto;
}

.table-scroll:focus-visible {
  outline: 2px solid var(--green-bright);
  outline-offset: -2px;
}

.data-table {
  width: 100%;
  min-width: 52rem;
  border-collapse: collapse;
  font-size: 14px;
}

.data-table th,
.data-table td {
  padding: 12px 10px;
  text-align: left;
  vertical-align: middle;
}

.data-table thead th {
  color: var(--ink-3);
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 600;
  border-bottom: 1px solid var(--hair-strong);
  white-space: nowrap;
}

/* Proportions rather than content-driven widths: an address column that
   collapses turns every recipient into three broken lines. The actions
   column is left to shrink to its own content instead of taking a share. */
.data-table th:nth-child(1) { width: 22%; min-width: 15rem; }
.data-table th:nth-child(2) { width: 15%; }
.data-table th:nth-child(3) { width: 20%; min-width: 13.5rem; }
.data-table th:nth-child(4) { width: 11%; }
.data-table th:nth-child(5) { width: 16%; }

/* width: 1% rather than a percentage share: the actions column should
   shrink to whatever "Resend verification" needs, not claim a fixed slice
   that either clips it or leaves it swimming in extra space. */
.data-table th:nth-child(6) { width: 1%; }

.data-table tbody tr + tr { border-top: 1px solid var(--hair); }

.data-table td.cell-actions {
  padding-left: 6px;
  white-space: nowrap;
  text-align: right;
}

/* Sans, not mono: the address a person recognises should read like text,
   not like an identifier. */
.cell-primary {
  color: var(--ink);
  font-family: var(--sans);
  font-size: 14px;
  overflow-wrap: anywhere;
}

.cell-date { white-space: nowrap; }

/* The inviter is a person, not an identifier, so it gets the same sans
   treatment at a slightly quieter colour. */
.cell-secondary {
  color: var(--ink-2);
  font-size: 13.5px;
  overflow-wrap: anywhere;
}

.cell-note {
  display: block;
  margin-top: 2px;
  color: var(--ink-3);
  font-size: 11.5px;
  line-height: 1.4;
}

/* Desktop only: above 900px the columns are wide enough (see the rebalanced
   percentages above) for a note like "email not confirmed" to sit on one
   line. Below 900px the table already scrolls sideways at content width, so
   the note is left free to wrap rather than stretching that column further. */
@media (min-width: 901px) {
  .cell-note { white-space: nowrap; }
}

.cell-empty { color: var(--ink-3); }

.data-table td.table-message {
  padding: 26px 14px;
  color: var(--ink-2);
  text-align: center;
}

.table-message .compact { margin-top: .85rem; }

.table-footnote {
  margin: 0;
  padding: 12px 14px;
  color: var(--ink-3);
  font-size: 13px;
  border-top: 1px solid var(--hair);
}

/* Every state names itself in the text, so colour only reinforces it — a
   dot rather than a filled chip, because "Expired" in red-dim bold already
   carries the one state that needs an administrator's attention. */
.state {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  font-size: 14px;
}

.state::before {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  background: var(--ink-3);
  border-radius: 50%;
  content: "";
}

.state-open::before {
  background: transparent;
  border: 1.5px solid var(--green);
}

.state-active::before { background: var(--green); }

.state-expired {
  color: var(--red-dim);
  font-weight: 600;
}

.state-expired::before { background: var(--red); }

.state-unverified::before { background: var(--amber); }

.state-accepted::before { background: var(--ink-3); }

/* The actions cell is sized to content (width: 1% on the th/td above), so
   this never needs to wrap — nowrap keeps "Resend verification" on one line
   and "Resend" / "Revoke" side by side instead of stacked with a large gap. */
.row-actions {
  display: flex;
  flex-wrap: nowrap;
  gap: 1rem;
  justify-content: flex-end;
  white-space: nowrap;
}

/* A text link rather than a filled button: the row already has a primary
   action (the invite form above it), and a column of coloured buttons would
   compete with it. */
.row-action {
  min-height: 0;
  padding: 0;
  color: var(--green-dim);
  background: none;
  border: 0;
  font-family: var(--sans);
  font-size: 13.5px;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: .18em;
}

.row-action::after { content: ""; }

/* Background and border have to be repeated here, not just color: the
   generic button:hover:not(:disabled) rule elsewhere in this sheet outranks
   a bare .row-action on specificity and would otherwise refill this text
   link with its solid green hover state. */
.row-action:hover:not(:disabled) {
  color: var(--ink);
  background: none;
  border: 0;
}

.row-action-danger { color: var(--red-dim); }

.row-action-danger:hover:not(:disabled) {
  color: var(--ink);
  background: none;
  border: 0;
}

/* The confirmation opens as its own row so the question can name the
   recipient in full instead of being folded into the actions column. */
.confirm-row td {
  background: var(--red-pale);
  box-shadow: inset 3px 0 var(--red);
}

.confirm-prompt {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem .75rem;
  align-items: center;
  justify-content: flex-end;
}

.confirm-question {
  margin-right: auto;
  color: var(--red-dim);
  font-size: 13px;
  font-weight: 600;
}

/* The one destructive action that stays a filled button: naming it "Confirm
   revoke" only helps once the question above it is impossible to miss. */
.confirm-danger {
  min-height: 34px;
  padding: 6px 14px;
  color: #fff;
  background: var(--red-dim);
  border: 1px solid var(--red-dim);
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .03em;
}

.confirm-danger::after { content: ""; }

.confirm-danger:hover:not(:disabled) {
  background: var(--red);
  border-color: var(--red);
}

.compact {
  min-height: 34px;
  padding: 7px 12px;
  font-size: 11px;
}

.compact::after { content: ""; }

/*
 * Administration: email previews. Tabs switch between the three templates —
 * one rendered preview at a time — instead of three stacked cards.
 */
.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  margin-top: 1.75rem;
  border-bottom: 1px solid var(--hair-strong);
}

.tabs button {
  min-height: 0;
  padding: 10px 0 12px;
  color: var(--ink-2);
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  font-family: var(--sans);
  font-size: 14.5px;
  font-weight: 500;
  letter-spacing: normal;
  text-transform: none;
}

.tabs button::after { content: ""; }

/* Background and the non-bottom border sides have to be repeated here: the
   generic button:hover:not(:disabled) rule elsewhere in this sheet ties
   .tabs button on specificity, and — being declared first — would otherwise
   lose the tiebreak and refill this tab with its solid green hover state. */
.tabs button:hover,
.tabs button:focus-visible {
  background: none;
  border-color: transparent;
}

.tabs button:hover { color: var(--ink); }

.tabs button[aria-selected="true"] {
  color: var(--ink);
  font-weight: 600;
  border-bottom-color: var(--green);
}

/* Without JavaScript every panel renders in sequence, so each one needs its
   own separation; script then leaves exactly one panel showing at a time. */
.preview-panel {
  margin-top: 1.75rem;
  padding-top: 1.75rem;
  border-top: 1px solid var(--hair);
}

.preview-panel:first-of-type {
  padding-top: 0;
  border-top: 0;
}

/* Only visible without JavaScript, where it is a template's only name (the
   tab strip is still hidden); once script enhances the tabs it takes the
   .visually-hidden treatment instead, so the tab label is not repeated. */
.preview-panel h2 {
  margin: 0;
  color: var(--ink);
  font-size: 17px;
  font-weight: 640;
}

.preview-meta {
  display: grid;
  gap: 10px;
  max-width: 40rem;
  margin: 18px 0 22px;
}

.preview-subject {
  margin: 0;
  color: var(--ink);
  font-size: 13.5px;
  line-height: 1.5;
}

.preview-field {
  display: block;
  color: var(--ink-3);
  font-size: 11px;
  font-weight: 500;
}

.preview-note {
  margin: 0;
  color: var(--ink-3);
  font-size: 13px;
  line-height: 1.5;
}

.preview-actions { margin: 4px 0; }

/* The label carries an email address, which the mono treatment used
   elsewhere would make considerably harder to read. */
.preview-actions button {
  font-size: 11.5px;
  letter-spacing: .02em;
}

.preview-text { font-size: 13px; }

.preview-text summary {
  color: var(--green-dim);
  cursor: pointer;
  font-weight: 600;
}

.preview-text summary:focus-visible {
  outline: 2px solid var(--green-bright);
  outline-offset: 2px;
}

.preview-text pre {
  margin: 10px 0 0;
  padding: 12px;
  max-height: 18rem;
  overflow: auto;
  color: var(--ink-2);
  background: var(--paper-raised);
  border: 1px solid var(--hair);
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.6;
  white-space: pre-wrap;
  word-break: break-word;
}

.preview-frame {
  display: block;
  width: 100%;
  height: 640px;
  background: #fff;
  border: 1px solid var(--hair-strong);
}

/*
 * Below this width the table sizes to its content and scrolls sideways
 * inside its own container instead of breaking every address over three
 * lines, and the panel's top padding tightens now that there is no keyboard
 * competing with it yet.
 */
@media (max-width: 900px) {
  .page { padding-top: 28px; }

  .auth-stage { grid-template-columns: minmax(0, 1fr); }

  .auth-identity {
    display: block;
    padding: 22px clamp(22px, 6vw, 40px);
    border-right: 0;
    border-bottom: 1px solid var(--navy-hair);
  }

  .identity-title,
  .identity-copy,
  .identity-status { display: none; }

  .scroll-hint { display: block; }

  .data-table { min-width: 0; }

  .data-table th:nth-child(1),
  .data-table th:nth-child(2),
  .data-table th:nth-child(3),
  .data-table th:nth-child(4),
  .data-table th:nth-child(5) { width: auto; }

  .cell-primary,
  .cell-secondary {
    overflow-wrap: normal;
    white-space: nowrap;
  }
}

@media (max-width: 700px) {
  .title-row { align-items: flex-start; }

  .invite-form {
    flex-direction: column;
    align-items: stretch;
  }

  .invite-form label { width: 100%; }
}

@media (max-width: 600px) {
  .page { padding: 0; }

  .auth-stage {
    min-height: 100%;
    border-width: 0 0 1px;
    box-shadow: none;
  }

  .auth-workspace { padding: 30px 22px 36px; }

  .actions { grid-template-columns: 1fr; }

  /* Stacked links need real hit areas: 8px of padding on top of the text's
     own height comfortably clears the 32px minimum. */
  .page-links {
    flex-direction: column;
    gap: 0;
  }

  .page-links a { padding: 8px 0; }

  .site-footer-inner { flex-direction: column; }

  /* Three labels at the desktop gap and padding no longer fit a 320px
     viewport; overflow-x on the inner container above is what makes this
     survive if it still doesn't, but tightening these first keeps the
     common case from ever needing to scroll. */
  .console-nav-inner {
    gap: 18px;
    padding: 0 14px;
  }
}

/* Below phone width the truncated address only crowds the wordmark, and the
   account page names it anyway. Keep it in the accessibility tree so a
   screen reader still announces who is signed in; only the pixels go. */
@media (max-width: 420px) {
  .header-account-email {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    scroll-behavior: auto !important;
    transition-duration: .01ms !important;
  }
}