/* ============================================================================
   TOM SHAW — Portal Component Layer
   Requires ts-tokens.css. No hardcoded colours below this line.
   Framework-agnostic: plain CSS, no Bootstrap dependency.
   ========================================================================= */

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

/**
 * Form controls do not inherit the typeface from their parent — the user agent
 * gives <button>, <input>, <select> and <textarea> its own default. Without
 * this reset, half of this system's controls rendered in Arial while the text
 * beside them rendered in the brand stack: .ts-segment__item, .ts-avatar and
 * .ts-nav__cta were all Arial on 15 Aug 2026, sitting next to .ts-btn and
 * .ts-crumbs in Neo Sans/Helvetica. Measured on list-view.html.
 *
 * Family only. Size and weight stay with the component, so this cannot change
 * a control's scale.
 */
button, input, select, textarea, optgroup { font-family: inherit; }

html, body { height: 100%; }

body {
  margin: 0;
  font-family: var(--ts-font);
  font-size: var(--ts-fs-body);
  line-height: var(--ts-lh-body);
  color: var(--ts-ink);
  background: var(--ts-white);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { font-weight: var(--ts-fw-bold); margin: 0; }
h1 { font-size: var(--ts-fs-h1); line-height: var(--ts-lh-h1); }
h2 { font-size: var(--ts-fs-h2); line-height: var(--ts-lh-h2); }
h3 { font-size: var(--ts-fs-h3); line-height: var(--ts-lh-h3); }
p  { margin: 0 0 calc(var(--ts-space) * 2); }

/* Universal focus treatment. Never remove this without a replacement. */
:focus-visible {
  outline: var(--ts-focus-width) solid var(--ts-focus-light);
  outline-offset: var(--ts-focus-offset);
  border-radius: 2px;
}
.ts-panel--brand :focus-visible { outline-color: var(--ts-focus-dark); }

/**
 * `left: 0` is load-bearing, not tidiness.
 *
 * These elements are absolutely positioned and almost never have a positioned
 * ancestor, so their containing block is the initial one — which means a
 * scroll container between them and the page does NOT clip them. A
 * visually-hidden <label> sitting inside a horizontally scrolled board column
 * therefore keeps its static x position and extends the PAGE's scrollable
 * width to wherever that column happens to start.
 *
 * Measured 15 Aug 2026 at a 320px viewport: board.html scrolled sideways 649px
 * and document-edit.html 483px, both caused entirely by 1px labels. Anchoring
 * to the containing block's left edge removes the escape without changing what
 * any assistive technology reads — announcement order comes from the DOM.
 */
.ts-visually-hidden {
  position: absolute; left: 0; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ============================================================================
   1. AUTH SHELL — 50/50 split. Form on light, brand glow on black.
   ========================================================================= */

.ts-auth {
  display: grid;
  grid-template-columns: 1fr 1fr;
  min-height: 100vh;
  min-height: 100dvh;
}

.ts-auth__form-side {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(var(--ts-space) * 8) calc(var(--ts-space) * 5);
  background: var(--ts-white);
}

.ts-auth__form {
  width: 100%;
  max-width: 420px;   /* keeps body copy inside the 45–75 character rule */
}

/* --- Brand panel: black ground, one corner-anchored glow, mesh texture --- */
.ts-panel--brand {
  position: relative;
  overflow: hidden;
  background: var(--ts-black);
  color: var(--ts-white);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: calc(var(--ts-space) * 8);
}
.ts-panel--brand::before {           /* the glow — one per surface, never fills */
  content: "";
  position: absolute; inset: 0;
  background: var(--ts-glow-panel);
  pointer-events: none;
}
.ts-panel__mesh {                     /* constellation texture, atmosphere only */
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  opacity: .5;
  pointer-events: none;
}
.ts-panel__content { position: relative; z-index: 2; max-width: 30ch; }

.ts-panel__eyebrow {
  font-family: var(--ts-mono);
  font-size: var(--ts-fs-label);
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--ts-violet-300);        /* 8.0:1 on black — safe at small size */
  margin-bottom: calc(var(--ts-space) * 2);
}
.ts-panel__headline {
  font-size: var(--ts-fs-h1);
  line-height: var(--ts-lh-h1);
  font-weight: var(--ts-fw-bold);
  margin-bottom: calc(var(--ts-space) * 2);
}
.ts-panel__sub {
  font-size: var(--ts-fs-small);
  line-height: var(--ts-lh-small);
  color: var(--ts-on-dark-2);
  margin: 0;
}
.ts-panel__foot {
  position: relative; z-index: 2;
  margin-top: calc(var(--ts-space) * 6);
  padding-top: calc(var(--ts-space) * 3);
  border-top: 1px solid var(--ts-hairline-dark);
  font-family: var(--ts-mono);
  font-size: 11px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ts-on-dark-3);
  display: flex;
  gap: calc(var(--ts-space) * 3);
  flex-wrap: wrap;
}

/* --- Wordmark ----------------------------------------------------------- */
/**
 * The block carries IDENTITY only — never placement. Positioning lives in
 * --auth, because a wordmark in normal flow (a section nav, a document header,
 * a gallery) is the common case and must not have to undo an absolute position
 * it never asked for.
 */
.ts-wordmark {
  font-weight: var(--ts-fw-black);
  font-size: var(--ts-fs-h3);
  letter-spacing: .16em;
  text-transform: uppercase;
}
/* Pinned to the corner of the auth brand panel / full-page stage. */
.ts-wordmark--auth {
  position: absolute;
  top: calc(var(--ts-space) * 6);
  left: calc(var(--ts-space) * 8);
  z-index: 2;
}
.ts-wordmark--light { color: var(--ts-white); }
.ts-wordmark__mark { color: var(--ts-violet-300); }

/**
 * The supplied wordmark asset — use this in preference to the type-set fallback
 * above wherever the real logo can be loaded.
 *
 * Extracted from TOMSHAW-Brand-Guidelines-v1.1. Per the guidelines, ONLY two
 * colour variants exist: white (reverse, for dark grounds) and black (positive,
 * for light). The wordmark is never rendered in violet, deep purple, grey or
 * cyan — so there is deliberately no token-driven colour hook here, and no
 * filter/tint rule to reach for.
 *
 *   <img class="ts-wordmark ts-wordmark--asset"
 *        src="assets/tomshaw-wordmark-white.png" alt="TOM SHAW">
 *
 * Native 1568×224 (7:1). Height is fixed and width follows, so the aspect ratio
 * cannot drift.
 */
.ts-wordmark--asset {
  height: 22px;
  width: auto;
  letter-spacing: normal;
  display: block;
}

/* --- Form-side heading -------------------------------------------------- */
.ts-auth__title { font-size: var(--ts-fs-h2); line-height: var(--ts-lh-h2); }
.ts-auth__title strong { color: var(--ts-purple); font-weight: var(--ts-fw-bold); }
/* Progress rail for a multi-step auth flow (invitation, reset). The form
   column stays 420px — a flow that does not fit becomes more steps, not a
   wider column. */
.ts-auth__steps { margin-bottom: calc(var(--ts-space) * 4); }

/* The access notice that closes every auth screen. This lives here, not in
   ts-standalone.css, because login.html loads only ts-tokens + ts-portal —
   it could never see a rule defined further down the import order. Until
   14 Aug 2026 login.html set this with style="margin-top:24px" on a page
   whose own CSP has no unsafe-inline, so the spacing silently did nothing. */
.ts-auth__note {
  margin: calc(var(--ts-space) * 4) 0 0;
  font-size: var(--ts-fs-label);
  line-height: 1.5;
  color: var(--ts-grey-text);
}

.ts-auth__lede {
  font-size: var(--ts-fs-small);
  line-height: var(--ts-lh-small);
  color: var(--ts-grey-text);
  margin: var(--ts-space) 0 calc(var(--ts-space) * 5);
}

/* ============================================================================
   2. FIELDS — underlined, floating label. The signature of the reference.
   ========================================================================= */

.ts-field {
  position: relative;
  padding-top: calc(var(--ts-space) * 3);
  margin-bottom: calc(var(--ts-space) * 3);
}

.ts-field__label {
  position: absolute;
  left: 0;
  top: calc(var(--ts-space) * 3 + 10px);
  font-size: var(--ts-fs-body);
  color: var(--ts-grey-mid);
  pointer-events: none;
  transition: all var(--ts-dur-base) var(--ts-ease);
}
/* Label lifts on focus or when the field holds a value */
.ts-field__input:focus + .ts-field__label,
.ts-field__input:not(:placeholder-shown) + .ts-field__label,
.ts-field.is-filled .ts-field__label {
  top: 0;
  font-size: var(--ts-fs-label);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ts-purple);
  font-weight: var(--ts-fw-bold);
}

.ts-field__input {
  width: 100%;
  height: 44px;
  padding: 0 calc(var(--ts-space) * 5) 0 0;
  font-family: inherit;
  font-size: var(--ts-fs-body);
  color: var(--ts-ink);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--ts-border-control);   /* 4.29:1 — 1.4.11 */
  border-radius: var(--ts-radius-input);
  transition: border-color var(--ts-dur-base) var(--ts-ease),
              box-shadow var(--ts-dur-base) var(--ts-ease);
}
.ts-field__input::placeholder { color: transparent; }  /* placeholder drives the float only */
.ts-field__input:hover  { border-bottom-color: var(--ts-ink); }
.ts-field__input:focus  {
  outline: none;                                        /* replaced, not removed */
  border-bottom-color: var(--ts-violet);
  box-shadow: 0 1px 0 0 var(--ts-violet);               /* 2px underline on focus */
}
.ts-field__input:disabled {
  color: var(--ts-grey-mid);
  border-bottom-style: dashed;
  cursor: not-allowed;
}
.ts-field__input:-webkit-autofill {
  -webkit-text-fill-color: var(--ts-ink);
  box-shadow: 0 0 0 1000px var(--ts-white) inset, 0 1px 0 0 var(--ts-grey-light);
}

/* Error state — colour is never the only signal; text and aria carry it too */
.ts-field.is-invalid .ts-field__input { border-bottom-color: var(--ts-error); box-shadow: 0 1px 0 0 var(--ts-error); }
.ts-field.is-invalid .ts-field__label { color: var(--ts-error-ink); }
.ts-field__hint {
  display: block;
  margin-top: var(--ts-space);
  font-size: var(--ts-fs-label);
  line-height: 1.45;
  color: var(--ts-grey-text);
}
.ts-field__hint--error { color: var(--ts-error-ink); font-weight: var(--ts-fw-bold); }

/* Reveal-password / trailing field action */
.ts-field__action {
  position: absolute; right: 0; bottom: 6px;
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  padding: 0;
  background: none; border: 0;
  color: var(--ts-grey-mid);
  cursor: pointer;
  border-radius: var(--ts-radius-chip);
}
.ts-field__action:hover { color: var(--ts-purple); }
.ts-field__action svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.6; vector-effect: non-scaling-stroke; }

/* ============================================================================
   3. CHECKBOX — 20px, 4px radius, violet when checked
   ========================================================================= */

.ts-check {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: calc(var(--ts-space) * 1.5);
  padding-left: calc(var(--ts-space) * 4);   /* 20px box + the 12px gap above */
  font-size: var(--ts-fs-small);
  color: var(--ts-grey-text);
  cursor: pointer;
  user-select: none;
}
.ts-check input { position: absolute; opacity: 0; width: 20px; height: 20px; margin: 0; cursor: pointer; }
/**
 * WCAG 1.4.11 is about the CONTROL's boundary, not its label. The unchecked
 * box was a --ts-grey-card fill (#EEE) inside a --ts-grey-light border (#E5E5E5)
 * on white: 1.1:1 and 1.3:1 respectively, so an empty checkbox was effectively
 * invisible — on the sign-in page, in every table select column, and in every
 * modal. White ground with a --ts-grey-mid edge is 4.29:1 and the box reads as
 * an empty control rather than a smudge. Same reasoning as .ts-btn--on-dark.
 */
.ts-check__box {
  position: absolute; left: 0; top: 50%;
  transform: translateY(-50%);
  width: 20px; height: 20px;
  background: var(--ts-white);
  border: 1px solid var(--ts-border-control);   /* 4.29:1 on white */
  border-radius: var(--ts-radius-chip);
  transition: background var(--ts-dur-fast) var(--ts-ease),
              border-color var(--ts-dur-fast) var(--ts-ease);
}
.ts-check__box::after {
  content: "";
  position: absolute; left: 6px; top: 2px;
  width: 5px; height: 10px;
  border: solid var(--ts-white);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg) scale(0);
  transition: transform var(--ts-dur-fast) var(--ts-ease);
}
.ts-check:hover input:not(:disabled) ~ .ts-check__box { border-color: var(--ts-violet); }
.ts-check input:checked ~ .ts-check__box { background: var(--ts-violet); border-color: var(--ts-violet); }
.ts-check input:checked ~ .ts-check__box::after { transform: rotate(45deg) scale(1); }
.ts-check input:focus-visible ~ .ts-check__box {
  outline: var(--ts-focus-width) solid var(--ts-focus-light);
  outline-offset: var(--ts-focus-offset);
}
.ts-check input:disabled ~ .ts-check__box { opacity: .5; }

/* ============================================================================
   4. BUTTONS — pill geometry, Neo Sans Black
   ========================================================================= */

.ts-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: calc(var(--ts-space) * 1.5);
  height: var(--ts-control-h);
  padding: 0 calc(var(--ts-space) * 4);
  font-family: var(--ts-font);
  font-size: var(--ts-fs-btn);
  /**
   * Rectangle, not pill; 700, not 800. Changed 15 Aug 2026.
   *
   * The pill at --ts-radius-pill (100px) with an 800 weight made the primary
   * action the loudest object on every screen — it competed with the filter
   * pills, the segment pills and the active rail tile, so a toolbar read as
   * four things shouting rather than one action and three states. An 8px
   * radius is the same --ts-radius-ui the rail tiles, nav rows and inputs
   * already use, so the button now belongs to the same family as the controls
   * beside it.
   *
   * This supersedes the "Pill, deep purple" row in the README's Colorlib
   * divergence table, which has been updated rather than left to disagree.
   * The deep-purple FILL is unchanged — that is the part that clears AAA.
   */
  font-weight: var(--ts-fw-bold);
  letter-spacing: .01em;
  text-decoration: none;
  border: 2px solid transparent;
  border-radius: var(--ts-radius-ui);
  cursor: pointer;
  transition: background var(--ts-dur-fast) var(--ts-ease),
              color var(--ts-dur-fast) var(--ts-ease),
              border-color var(--ts-dur-fast) var(--ts-ease);
}
.ts-btn--block { display: flex; width: 100%; }

/**
 * An icon inside a button. This had no rule at all until 15 Aug 2026 — the one
 * place it appeared, list-view.html sized it with style="width:15px;height:15px"
 * on a page whose CSP has no unsafe-inline, so it rendered at zero. A button
 * icon is sized by the button, not by an extra stylesheet or an inline style.
 * 2.2 because a plus mark or chevron reads thin at 1.7 (01 §5).
 */
.ts-btn svg {
  flex: 0 0 auto;
  width: 15px; height: 15px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  vector-effect: non-scaling-stroke;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.ts-btn--primary {
  background: var(--ts-purple);     /* white on #4F1398 = 11.2:1, AAA */
  border-color: var(--ts-purple);
  color: var(--ts-white);
}
.ts-btn--primary:hover  { background: var(--ts-violet-700); border-color: var(--ts-violet-700); }
.ts-btn--primary:active { background: var(--ts-violet-900); border-color: var(--ts-violet-900); }

/**
 * Primary action sitting on a BLACK surface — promo band, hero band, stage.
 *
 * Deep purple is 1.88:1 against black. The white LABEL is fine at 11.2:1, but
 * WCAG 1.4.11 is about the control's own boundary: the fill is what tells you
 * a button is there, and at 1.88:1 the shape is invisible. The button reads as
 * floating text until you hover it.
 *
 * The fill stays --ts-purple so the primary action is the same colour on both
 * surfaces; a violet-300 border supplies the boundary at 8.14:1. Both brand
 * colours keep their documented roles — purple is the fill, violet the accent.
 *
 *   <button class="ts-btn ts-btn--primary ts-btn--on-dark">
 */
.ts-btn--primary.ts-btn--on-dark { border-color: var(--ts-violet-300); }
.ts-btn--primary.ts-btn--on-dark:hover,
.ts-btn--primary.ts-btn--on-dark:active { border-color: var(--ts-violet-200); }
/* Focus on black is cyan, per the focus spec — the violet ring would sit too
   close to the violet border to read as a separate state. */
.ts-btn--on-dark:focus-visible { outline-color: var(--ts-focus-dark); }

/**
 * 1px, not the 2px the block declares. A secondary action drawn with the same
 * edge weight as the primary's fill competes with it: on settings.html the
 * "Documentation" ghost was the loudest object above the fold, louder than the
 * h1 it sat beside. Halving the edge keeps the deep-purple identity (11.2:1,
 * well clear of the 3:1 that 1.4.11 asks of a control boundary) and returns
 * the emphasis to the primary. Height is fixed, so nothing shifts.
 */
.ts-btn--ghost {
  background: transparent;
  border-width: 1px;
  border-color: var(--ts-purple);
  color: var(--ts-purple);
}
.ts-btn--ghost:hover { background: var(--ts-purple); color: var(--ts-white); }

.ts-btn--ghost-dark {                /* on black surfaces */
  background: transparent;
  border-width: 1px;
  border-color: var(--ts-white);     /* 21:1 — far past the 3:1 boundary floor */
  color: var(--ts-white);
}
.ts-btn--ghost-dark:hover { background: var(--ts-white); color: var(--ts-black); }

.ts-btn:disabled, .ts-btn[aria-disabled="true"] {
  opacity: .45;
  cursor: not-allowed;
  pointer-events: none;
}
.ts-btn.is-busy { pointer-events: none; }
.ts-btn.is-busy .ts-btn__label { opacity: .6; }
.ts-btn__spinner {
  width: 18px; height: 18px;
  border: 2px solid var(--ts-hairline-dark-2);
  border-top-color: var(--ts-white);
  border-radius: 50%;
  animation: ts-spin 700ms linear infinite;
}
@keyframes ts-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .ts-btn__spinner { animation-duration: 2s; } }

/* ============================================================================
   5. LINKS, DIVIDER, SSO
   ========================================================================= */

.ts-link {
  color: var(--ts-violet);          /* 5.0:1 on white — AA */
  text-decoration: underline;
  text-underline-offset: 3px;
  font-size: var(--ts-fs-small);
}
.ts-link:hover { color: var(--ts-purple); }

.ts-form-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: calc(var(--ts-space) * 2);
  margin: calc(var(--ts-space) * 3) 0 calc(var(--ts-space) * 4);
}

.ts-divider {
  display: flex;
  align-items: center;
  gap: calc(var(--ts-space) * 2);
  margin: calc(var(--ts-space) * 4) 0 calc(var(--ts-space) * 3);
  font-family: var(--ts-mono);
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ts-grey-mid);
}
.ts-divider::before, .ts-divider::after {
  content: ""; flex: 1; height: 1px; background: var(--ts-grey-light);
}
/* This block sets display, so [hidden] has to be restated — an author
   declaration beats the UA sheet's `[hidden] { display: none }`. Same rule as
   .ts-announce, .ts-bulkbar and .ts-nav__scrim.
   Without it, /login renders "or continue with" and a dead SSO button whenever
   AUTH_MODE=local, because the server's `hidden` attribute has no effect. */
.ts-divider[hidden] { display: none; }

/* Enterprise SSO — identity providers, not consumer social networks */
.ts-sso { display: grid; gap: calc(var(--ts-space) * 1.5); }
.ts-sso[hidden] { display: none; }   /* see .ts-divider[hidden] above */
.ts-sso__btn {
  display: flex;
  align-items: center;
  gap: calc(var(--ts-space) * 1.5);
  /* --ts-control-h, not 48px. These sit directly under .ts-btn in the same
     column doing the same job — signing you in — and a 6px step between two
     stacked buttons reads as a mistake, not as hierarchy. Weight is what
     separates them: filled primary, outlined secondary. */
  height: var(--ts-control-h);
  padding: 0 calc(var(--ts-space) * 2.5);
  font-size: var(--ts-fs-small);
  font-weight: var(--ts-fw-bold);
  color: var(--ts-ink);
  text-decoration: none;
  background: var(--ts-white);
  border: 1px solid var(--ts-border-control);   /* 4.29:1 — 1.4.11 */
  /**
   * Rectangle, following .ts-btn. The primary action stopped being a pill on
   * 15 Aug 2026 and these did not, which left the sign-in page with one 8px
   * rectangle sitting directly above two 100px pills doing the same job. One
   * radius family per screen; --ts-radius-ui is the one the buttons, inputs,
   * rail tiles and nav rows already share.
   */
  border-radius: var(--ts-radius-ui);
  transition: border-color var(--ts-dur-fast) var(--ts-ease),
              background var(--ts-dur-fast) var(--ts-ease);
}
.ts-sso__btn:hover { border-color: var(--ts-violet); background: var(--ts-violet-50); }
.ts-sso__btn svg { width: 18px; height: 18px; flex: 0 0 18px; }

/* ============================================================================
   6. ALERTS — form-level messaging (auth failure, lockout, session expiry)
   ========================================================================= */

/**
 * [hidden] MUST be restated on any block that sets `display`. The hidden
 * attribute works through a user-agent rule, and an author declaration beats
 * the UA origin regardless of specificity — so `display: flex` here silently
 * defeats `hidden` and the alert renders. This is not theoretical: the generic
 * auth-failure alert on the sign-in page shipped visible, and announced, before
 * anyone had typed a credential.
 */
.ts-alert[hidden] { display: none; }

.ts-alert {
  display: flex;
  gap: calc(var(--ts-space) * 1.5);
  padding: calc(var(--ts-space) * 1.5) calc(var(--ts-space) * 2);
  margin-bottom: calc(var(--ts-space) * 3);
  font-size: var(--ts-fs-small);
  line-height: var(--ts-lh-small);
  border-left: 3px solid var(--ts-grey-light);
  /* --ts-radius-ui, matching .ts-notice. The two banner blocks were 4px and
     8px, and app-shell.html puts a 4px alert directly above a 12px table —
     three radii in 200px of vertical space. --ts-radius-chip is for a chip. */
  border-radius: var(--ts-radius-ui);
  background: var(--ts-grey-card);
  color: var(--ts-ink);
}
.ts-alert__icon { flex: 0 0 18px; width: 18px; height: 18px; margin-top: 3px; }
.ts-alert--error   { border-left-color: var(--ts-error);   background: var(--ts-error-50); color: var(--ts-error-ink); }
.ts-alert--warning { border-left-color: var(--ts-warning); background: var(--ts-warning-50); }
.ts-alert--success { border-left-color: var(--ts-success); background: var(--ts-success-50); }
.ts-alert--info    { border-left-color: var(--ts-purple);  background: var(--ts-violet-50); }

/* Classification / environment banner — required on gov & mil deployments */
.ts-classification {
  width: 100%;
  padding: 6px calc(var(--ts-space) * 2);
  font-family: var(--ts-mono);
  font-size: 11px;
  letter-spacing: .18em;
  text-transform: uppercase;
  text-align: center;
  color: var(--ts-white);
  background: var(--ts-surface-3);
}
.ts-classification--official { background: var(--ts-purple); }
.ts-classification--protected{ background: var(--ts-error); }

/* ============================================================================
   7. RESPONSIVE — brand panel collapses to a banner; form never shrinks below
   ========================================================================= */

@media (max-width: 991px) {
  .ts-auth { grid-template-columns: 1fr; }
  .ts-panel--brand {
    order: -1;
    min-height: 240px;
    padding: calc(var(--ts-space) * 10) calc(var(--ts-space) * 4) calc(var(--ts-space) * 4);
  }
  .ts-panel__headline { font-size: var(--ts-fs-h2); }
  .ts-panel__foot { display: none; }
  .ts-wordmark { top: calc(var(--ts-space) * 3); left: calc(var(--ts-space) * 4); }
  .ts-auth__form-side { padding: calc(var(--ts-space) * 6) calc(var(--ts-space) * 4); }
}

@media (max-width: 420px) {
  .ts-auth__title { font-size: var(--ts-fs-h3); }
  .ts-form-meta { flex-direction: column; align-items: flex-start; }
}

@media (prefers-contrast: more) {
  .ts-field__input { border-bottom-width: 2px; border-bottom-color: var(--ts-ink); }
  .ts-sso__btn    { border-color: var(--ts-ink); }
}
