/* =====================================================================
   The app look — SDCore's hosted pages, dressed as SDCrew
   =====================================================================

   Two classes, two claims (ENG-2173):

     .auth-app   THE LOOK. Every page on layouts/auth wears it, in any browser
                 on any host — login, registration, verification, consents,
                 the onboarding front door, help, the legal pages and the
                 ticket pages. A guardian moving between the invite email,
                 these pages and the app (or sdcrew-web) sees one product.
     .auth-ios   THE WINDOW. Added only when AuthClientContext resolves that the
                 page is inside the iOS app's own sheet (`client_id=sdcrew-ios`
                 or `?ui=ios`). It carries what only that window needs: the
                 toolbar headroom and pre-scroll, the hidden wordmark.

   Before ENG-2173 the look was gated on the window (and, from ENG-1714, on
   the lkrm.app host via a third class, `auth-brand`), and plain web visitors
   kept auth.css's "Cyber Glacier". auth.css still loads underneath, and
   these rules override it.

   The values are ports, not inventions. They come from SDCrew's own design
   system (Packages/DesignSystem) so the web sheet inside
   ASWebAuthenticationSession is continuous with the app screen that opened it:

     AmbientBackground.swift  -> the gradient + two radial glows below
     GlassSurface.swift       -> the control hairline (iOS 18 fallback value)
     Theme.swift              -> Colors, Spacing, Radius, Typography

   Palette — the LKRM brand direction ("Energetic Blue with Refined Rally",
   SDCrew-iOS #128), converted from the .colorset components and Theme.swift:
     energeticBlueBright #2693F5   the one blue: focus, ticks, tints, the glow
     energeticBlueText   #59ADF7   link text (the bright blue is below AA as
                                   small text on anything lighter than navy)
     rallyOrange         #E95A12   errors, never as small text — on a dot
     purpleGlow          #29298A   top-left ambient glow (an indigo, despite
                                   the name)
     brandBone           #F4F1EA   primary button ground (brandBonePressed
                                   #D8D4CC pressed); brandInk #091A31 its label
     gradient #081128 -> #0A1D38 (LKRM Navy) -> #133153

   Theme still ships ONE accent (`mint` is documented as unused on purpose),
   so everything interactive here is the blue. No cyan anywhere.
   ===================================================================== */

.auth-app {
  /* --- Theme.Colors --- */
  --energetic-blue: #2693f5;
  --energetic-blue-rgb: 38, 147, 245;
  --energetic-blue-text: #59adf7;
  --rally-orange: #e95a12;
  --rally-orange-rgb: 233, 90, 18;
  --purple-glow: 41, 41, 138;
  --gradient-top: #081128;
  --gradient-mid-a: #0a1d38;
  --gradient-mid-b: #133153;

  /* --- glassSurface(), iOS 18 fallback: the .quaternary hairline --- */
  --glass-border: rgba(255, 255, 255, 0.18);

  /* --- Theme.Spacing --- */
  --space-xs: 6px;
  --space-sm: 10px;
  --space-md: 14px;
  --space-lg: 20px;
  --space-xl: 28px;
  /* Beyond Theme.Spacing's scale — the gap under the greeting, which has no
     token counterpart in the app because no app screen pairs these two things. */
  --space-xxl: 40px;
  /* The top bar of whatever window holds the page. A browser tab draws none
     over the page, so the look defaults to zero; the app's sheet sets it
     below. */
  --chrome-top: 0px;

  /* --- Theme.Radius --- */
  --radius-control: 12px;

  /* --- text --- */
  --text-primary: #ffffff;
  --text-secondary: rgba(255, 255, 255, 0.6);

  /* --- application.css's shared OAuth-block tokens ---
     Their fallbacks are cyan (rgba(0, 242, 255, …)). Overriding the tokens
     rather than the rules means a selector we never touched still cannot
     render neon. */
  --oauth-divider-color: rgba(255, 255, 255, 0.14);
  --oauth-divider-bg: transparent;
  --oauth-btn-border: var(--glass-border);
  --oauth-btn-hover-border: rgba(255, 255, 255, 0.32);
  --oauth-btn-bg: rgba(255, 255, 255, 0.06);
  --oauth-btn-color: #ffffff;
  --oauth-link-color: var(--energetic-blue-text);

  /* Primary CTA — BrandPrimaryButtonStyle: bone ground, navy ink */
  --btn-primary-bg: #f4f1ea;
  --btn-primary-bg-pressed: #d8d4cc;
  --btn-primary-fg: #091a31;
}

/* ===== The sheet's toolbars =====
   The bar tint follows the BODY's declared background-color — not html's, not
   theme-color, and not any rendered pixel. Established by a controlled
   experiment across on-device screenshots: html's background changed
   #261d34 -> #44192f with body untouched and the bar did not move a byte
   (#172032 both times), while every bar shift in earlier builds tracked body's
   value (mid-a -> #101824, #080d16 -> #0f1119, mid-b -> #172032 ~= mid-b).
   And it is the declared style, not a sample: body's top edge RENDERS plum,
   yet the bar came out navy while body declared navy. (This matches WebKit
   deriving its chrome/under-page tint from the page's background-color.)

   So body declares, below, the colour the bar should LOOK — and html here
   takes over body's old job. Its background is what paints the overscroll
   region past the page's end, so it matches the gradient's final stop
   (--gradient-mid-b; a literal because custom properties inherit downward and
   html sits above where the tokens are defined). If :has is ever unsupported
   the rule is ignored and body's toolbar colour propagates instead — the bar stays
   right, and only the bottom rubber-band mismatches. */
html:has(> body.auth-app) {
  /* The SAME stack as .auth-app.auth-body below, in literals because the tokens
     are defined on body and custom properties only inherit downward. Both
     copies are background-attachment: fixed, i.e. anchored to the viewport, so
     they render as the same pixels wherever each paints — html's copy exists to
     cover the canvas PAST the body's end (the sheet's scrollable area runs
     taller than 100vh, and a flat colour there showed as a gap where the
     gradient stopped). Change one copy, change both. */
  background:
    radial-gradient(circle 420px at 0% 0%, rgba(41, 41, 138, 0.35), transparent),
    radial-gradient(circle 460px at 100% 100%, rgba(38, 147, 245, 0.30), transparent),
    linear-gradient(180deg, #081128 0%, #0a1d38 50%, #133153 100%);
  background-attachment: fixed;
  /* Rubber-band past the canvas still shows this flat colour: the gradient's
     final stop, so the edge stays seamless. */
  background-color: #133153;
}

/* ===== Ground: AmbientBackground, as CSS =====
   A ZStack of a vertical LinearGradient with two RadialGradients over it. CSS
   paints background layers first-listed-on-top, so the glows are listed before
   the linear gradient — the reverse of the Swift ZStack's source order.
   `background-attachment: fixed` keeps the glows anchored to the viewport
   rather than scrolling with a tall form, which is what ignoresSafeArea() plus
   a full-bleed ZStack does on the app side. */
.auth-app.auth-body {
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;
  color: var(--text-primary);
  background:
    radial-gradient(circle 420px at 0% 0%, rgba(var(--purple-glow), 0.35), transparent),
    radial-gradient(circle 460px at 100% 100%, rgba(var(--energetic-blue-rgb), 0.30), transparent),
    linear-gradient(180deg, var(--gradient-top) 0%, var(--gradient-mid-a) 50%, var(--gradient-mid-b) 100%);
  background-attachment: fixed;
  /* This is the sheet's TOOLBAR colour, not a visible fill: the bars tint from
     body's declared background-color (see the html rule above for the
     measurements), and the opaque fixed gradient covers the body box, so this
     value never paints anywhere on the page itself. #0d1435 is the page top as
     it actually renders — gradient-top under the indigo glow — so bar and page
     meet without a seam. The pre-LKRM value (#211b2d) was sampled from an
     on-device screenshot; this one is a headless-Chrome render sampled at the
     point where that render matched the device value (x≈260 of 393, at the
     page top), so re-check it against a device capture. The overscroll
     region past the page's end is html's job now, not this value's: an earlier
     build used mid-a here while it still propagated to the canvas, and shipped
     a hard horizontal edge at the bottom; a near-black "fade" tail was also
     tried and dimmed the whole screen, bars included, since the fixed
     gradient's stops span the viewport. */
  background-color: #0d1435;
  /* One toolbar-height taller than the viewport, so the pre-scroll below has
     exactly --chrome-top of room: at scrollY = --chrome-top the content sits
     where an unscrolled page would put it, while the gradient's top slides
     under the translucent bar — real pixels behind the chrome, not a tint
     imitating them. The gradient is fixed, so the extra height paints no
     visible difference of its own. */
  min-height: calc(100vh + var(--chrome-top));
  /* Safe areas: viewport-fit=cover makes these non-zero on a notched device,
     so the card never sits under the home indicator or the status bar. */
  /* ASWebAuthenticationSession's toolbar is translucent and the webview runs
     underneath it, so the gradient already paints behind the chrome — it is the
     content that needs pushing clear, not the background that needs raising.
     --chrome-top is the sheet's bar; there is no env() for it, since as far as
     the page is concerned nothing is overlapping it. */
  padding:
    calc(max(var(--space-lg), env(safe-area-inset-top)) + var(--chrome-top))
    env(safe-area-inset-right)
    max(var(--space-lg), env(safe-area-inset-bottom))
    env(safe-area-inset-left);
  box-sizing: border-box;
  /* The sheet is a single focused task; rubber-banding a login form reads as a
     broken web page inside what the user experiences as an app screen. */
  overscroll-behavior-y: none;
  -webkit-text-size-adjust: 100%;
}

/* ===== Inside the app's sign-in sheet =====
   ASWebAuthenticationSession's toolbar is translucent and the web view runs
   under it, so the page stands one bar taller and pads its content clear (see
   the body rule above). The bar is guessed at ~52pt and confirmed by eye — a
   page cannot measure browser chrome. Two classes so it beats .auth-app's
   zero. */
.auth-app.auth-ios {
  --chrome-top: 52px;
  /* In a browser the content stays centred vertically (auth.css's
     align-items: center on a 100vh flex body). In the sheet that centring put
     a large gap between ASWebAuthenticationSession's header and the heading,
     with no margin on .auth-title to remove, so the content starts at the top
     and the body's padding is the only gap left. */
  align-items: flex-start;
}

/* ===== When the host draws the bar =====
   Everything above assumes ASWebAuthenticationSession's model: the web view runs
   UNDER a translucent toolbar, so the page pads its content clear of it, stands
   one bar taller than the viewport, and pre-scrolls by that much to slide real
   gradient pixels behind the chrome.

   SDCrew's support sheet is not that. It presents the web view inside a SwiftUI
   `NavigationStack`, which lays it out BELOW an opaque nav bar — nothing
   overlaps, so all three offsets apply where there is no toolbar to compensate
   for. Rendered in the app that showed as a dead band under the "Help" title
   with the content pushed down behind it.

   All three read one token, so setting it to zero stands them all down: the
   `padding`/`min-height` calcs add 0, and the layout's pre-scroll script tests
   `chrome > 0` and does nothing. Nothing else consumes --chrome-top — checked,
   and worth re-checking before adding a fourth consumer that this cannot reach.

   Three classes so it beats the sheet's `.auth-app.auth-ios` above. */
.auth-app.auth-ios.auth-hosted-chrome {
  --chrome-top: 0px;
}

/* ===== When the host draws the title, too =====
   The app's sheet carries "Need Help?" in its navigation bar, so the page's own
   `<h1>` is the same words a second time, twenty pixels lower.

   **Hidden visually, not removed.** `display: none` would take the page's only
   `<h1>` out of the accessibility tree with it, and a screen-reader user
   navigating by heading would find the document headingless — the app's bar
   title is in the *app's* tree, not this document's. The clip-rect idiom keeps
   the heading readable to assistive tech and paints nothing.

   Separate from `auth-hosted-chrome` on purpose: "the host draws the bar" and
   "the host also names the page" are different claims, and a future hosted page
   may well want its own visible heading. */
/* With the heading hidden and the bar directly above, the container's own
   --space-xl gutter left the first line sitting well clear of the title bar for
   no reason — there is no heading for it to breathe under any more. The side and
   bottom gutters stay: they are what keeps the fields off the screen edge. */
.auth-app.auth-ios.auth-hosted-title .auth-container {
  padding-top: var(--space-xs);
}

.auth-app.auth-ios.auth-hosted-title .auth-title {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ===== The questions, collapsed =====
   `<details>`/`<summary>` in the app only — see help/new.html.erb for why the web
   keeps its heading-and-list markup instead.

   `display: block` on the summary is what lets the marker be replaced: with the
   default `display: list-item` the UA draws its own triangle from
   `::marker`/`::-webkit-details-marker`, which cannot be positioned. Drawing it
   as a rotating ::after instead puts it on the trailing edge, matching the
   per-item chevrons already in the list below rather than pointing at the label
   from the left. */
.auth-app details.help-self-service > summary {
  display: block;
  position: relative;
  cursor: pointer;
  padding: var(--space-sm) 0;
  padding-right: var(--space-xl);
  min-height: 44px; /* the HIG tap target — it is the control for the whole block */
  box-sizing: border-box;
  list-style: none;
}

.auth-app details.help-self-service > summary::-webkit-details-marker {
  display: none;
}

.auth-app details.help-self-service > summary::after {
  content: "\25B6"; /* the same glyph the per-item questions use */
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  font-size: 0.75rem;
  color: rgba(255, 255, 255, 0.4);
}

/* Open state points the triangle down. No transition: this file has none by
   design (see Motion, below), and the disclosure is a state change, not a move. */
.auth-app details.help-self-service[open] > summary::after {
  transform: translateY(-50%) rotate(90deg);
}

.auth-app .auth-container {
  max-width: 420px;
  /* The card used to supply the inner gutter; with it gone the container is the
     only thing keeping the fields off the screen edge. */
  padding: var(--space-xl);
}

/* ===== No card =====
   The content sits directly on the ambient gradient. Decided on the render:
   once the wordmark went, the glass card was a box drawn around the only thing
   on the screen — and SDCrew makes exactly this call for a single-decision form
   (ApprovalRequestView, 2026-08-27), where cards read as an inbox of one item
   rather than as the decision they are. The glass rule is about *lists of
   content rows*; a login has none.

   The fields and buttons keep their own fills, so they still read as controls
   against the gradient. .auth-card is emptied rather than removed from the
   layout because that markup is shared with every other product on this
   layout — the element still draws its card for them.

   The --oauth-divider-bg override stays: auth.css sets it on .auth-card itself,
   and a custom property resolves from the NEAREST ancestor that defines it, so
   without this the "or" keeps a #060b18 box behind it even with no card. */
.auth-app .auth-card {
  background: none;
  border: none;
  border-radius: 0;
  padding: 0;
  box-shadow: none;
  --oauth-divider-bg: transparent;
}

/* ===== Wordmark =====
   In a browser nothing else names the brand, so the wordmark stays — in the
   palette's white, not auth.css's cyan glow. */
.auth-app .auth-logo {
  margin-bottom: var(--space-lg);
}

.auth-app .logo-text {
  color: var(--text-primary);
  text-shadow: none;
}

/* Inside the app's sheet it is hidden, not reworded. ASWebAuthenticationSession
   draws its own header naming the host, and the user arrived here by tapping
   "Get started" in SDCrew one screen ago — a second brand line under that
   header only pushed the form down. Hiding it in CSS rather than branching the
   ERB keeps one copy of the markup. */
.auth-app.auth-ios .auth-logo {
  display: none;
}

/* ===== Type =====
   Sized in rem off the root so the OS text-size setting scales the page the way
   Dynamic Type scales the app. */
.auth-app .auth-title {
  /* Theme.Typography.titleSmall — .title3, semibold */
  font-size: 1.25rem;
  font-weight: 600;
  /* xxl rather than xl: the heading is a greeting, not a field label, and wants
     visible separation from the form it introduces. */
  margin: 0 0 var(--space-xxl);
}

/* In a browser the wordmark already stands above the heading, so the heading
   takes the smaller gap. Measured, not guessed: with the app's 40px here, a
   browser's registration page with Apple enabled pushed the consent notice
   10px below the fold at 390x844 (legal_consent_spec). */
.auth-app:not(.auth-ios) .auth-title {
  margin-bottom: var(--space-xl);
}

.auth-app .section-title {
  /* auth.css colours the text and the rule #00f2ff, with no token to override. */
  color: var(--text-primary);
  font-weight: 600;
  border-bottom-color: var(--glass-border);
}

/* ===== Form ===== */
.auth-app .auth-form {
  gap: var(--space-md);
}

.auth-app .form-group {
  gap: var(--space-xs);
}

.auth-app .form-group label {
  color: var(--text-primary);
  font-size: 0.9375rem;
  font-weight: 500;
}

/* :not([type="checkbox"]) is load-bearing: the consent and information-
   validation screens nest their .form-checkbox inside a .form-group, and this
   selector is more specific than .auth-app .form-checkbox below — without the
   exclusion the checkbox renders as a 44px filled, rounded text-field box.
   Radios excluded on the same reasoning, though no auth view uses one today. */
/* `textarea` is here for the help page's Description field and nothing else —
   no auth view has one, which is why the original list did not mention it. Left
   off, it kept auth.css's Cyber Glacier fill inside an otherwise themed form.
   `min-height` is overridden below: 44px is a tap-target floor for a one-line
   control and far too short for a message box. */
.auth-app .form-group input:not([type="checkbox"]):not([type="radio"]),
.auth-app .form-group textarea,
.auth-app .form-group select {
  background: rgba(255, 255, 255, 0.06);
  border: 0.5px solid var(--glass-border);
  border-radius: var(--radius-control);
  color: var(--text-primary);
  /* 16px exactly: iOS Safari zooms the viewport on focus for anything smaller,
     which inside ASWebAuthenticationSession looks like the sheet lurching. */
  font-size: 16px;
  font-family: inherit;
  min-height: 44px; /* the HIG minimum tap target */
  padding: var(--space-sm) var(--space-md);
  box-sizing: border-box;
}

/* The message box wants room to be a message box, and a resize handle in a sheet
   with no address bar is a control the guardian cannot undo — the `rows="5"` in
   the view sets the height, so vertical dragging only lets them break it. */
.auth-app .form-group textarea {
  min-height: 120px;
  resize: none;
  line-height: 1.5;
}

.auth-app .form-group input:not([type="checkbox"]):not([type="radio"]):focus,
.auth-app .form-group textarea:focus,
.auth-app .form-group select:focus {
  outline: none;
  border-color: var(--energetic-blue);
  box-shadow: 0 0 0 3px rgba(var(--energetic-blue-rgb), 0.22);
}

.auth-app .form-group input::placeholder,
.auth-app .form-group textarea::placeholder {
  color: rgba(255, 255, 255, 0.35);
}

/* Rally Orange is the error colour now (it replaced the app's live red), and
   it never colours small text — 3.73:1 on the gradient's foot. So the words are
   text-primary and the orange rides on a leading dot, as the app's StatusLine
   draws an error. */
.auth-app .field-error {
  color: var(--text-primary);
  font-size: 0.8125rem;
}

.auth-app .field-error::before {
  content: "";
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-right: var(--space-xs);
  border-radius: 50%;
  background: var(--rally-orange);
  vertical-align: 0.1em;
}

.auth-app .form-checkbox {
  accent-color: var(--energetic-blue);
  min-width: 22px;
  min-height: 22px;
}

.auth-app .form-checkbox-label {
  color: var(--text-secondary);
}

/* ===== Buttons ===== */
/* Bone with navy ink, not the blue accent — it is SDCrew's own primary CTA
   (BrandPrimaryButtonStyle, "Get started" on Welcome), so the button the user
   taps here is the same button they tapped one screen ago. Blue on a solid fill
   would also need navy ink (bone on it is 2.83:1), and the brand keeps blue for
   links and focus rings. */
.auth-app .btn-auth {
  background: var(--btn-primary-bg);
  border: none;
  border-radius: var(--radius-control);
  color: var(--btn-primary-fg); /* dark, for contrast on a light button */
  font-family: inherit;
  /* Theme.Typography.brandCallToAction — 16 Bold. The app sets it in Sansation;
     the web keeps the system face, as every SDCrew screen but Welcome does. */
  font-size: 1rem;
  font-weight: 700;
  /* BrandPrimaryButtonStyle's height, which is taller than the HIG's 44 floor */
  min-height: 52px;
  padding: var(--space-sm) var(--space-lg);
  text-shadow: none;
  box-shadow: none;
  -webkit-tap-highlight-color: transparent;
}

.auth-app .btn-auth:hover {
  background: var(--btn-primary-bg);
  box-shadow: none; /* auth.css glows the button cyan on hover */
}

/* The app's pressed state: a step darker AND one pixel lower — the shift is
   what is felt at the edges when the thumb covers the fill. */
.auth-app .btn-auth:active {
  background: var(--btn-primary-bg-pressed);
  transform: translateY(1px);
}

.auth-app .btn-auth:disabled {
  background: rgba(244, 241, 234, 0.16);
  color: #6e7d90; /* brandTextMuted — WCAG exempts an inactive control */
  opacity: 1;
}

.auth-app .btn-auth-secondary {
  background: transparent;
  border: 0.5px solid var(--glass-border);
  color: var(--text-primary);
}

.auth-app .btn-auth-secondary:hover,
.auth-app .btn-auth-secondary:active {
  background: rgba(255, 255, 255, 0.08);
}

/* ===== OAuth providers ===== */
.auth-app .oauth-divider {
  color: var(--text-secondary);
}

.auth-app .btn-oauth {
  border-radius: var(--radius-control);
  font-family: inherit;
  /* The primary button's weight and height, so the stack reads as one set */
  font-size: 1rem;
  font-weight: 700;
  min-height: 52px;
  -webkit-tap-highlight-color: transparent;
}

/* Apple's mark gets the system treatment; Google keeps its brand colours, which
   its own guidelines require. */
.auth-app .btn-apple {
  background: #ffffff;
  border: none;
  color: #000000;
}

.auth-app .btn-apple svg path {
  fill: #000000;
}

.auth-app .btn-google {
  background: rgba(255, 255, 255, 0.06);
  border: 0.5px solid var(--glass-border);
  color: var(--text-primary);
}

/* ===== Links ===== */
.auth-app .auth-links {
  color: var(--text-secondary);
}

.auth-app .auth-links a,
.auth-app .faq-answer-inner a {
  color: var(--energetic-blue-text);
  text-decoration: none;
  /* Links sit in a "Forgot password? | Create account" row; padding gets them to
     a real tap target without moving the baseline. */
  display: inline-block;
  padding: var(--space-xs) 0;
}

.auth-app .auth-links a:active {
  opacity: 0.6;
}

/* ===== Flash ===== */
.auth-app .flash {
  border-radius: var(--radius-control);
  font-size: 0.9375rem;
}

/* The washes carry the colour and the words stay text-primary — the small-text
   rule again. A notice takes the blue: the palette has no green (`mint` is
   unused on purpose), and Rally Orange is reserved for what went wrong. */
.auth-app .flash-notice {
  background: rgba(var(--energetic-blue-rgb), 0.14);
  border: 0.5px solid rgba(var(--energetic-blue-rgb), 0.5);
  color: var(--text-primary);
}

.auth-app .flash-alert {
  background: rgba(var(--rally-orange-rgb), 0.14);
  border: 0.5px solid rgba(var(--rally-orange-rgb), 0.5);
  color: var(--text-primary);
}

/* ===== Supporting surfaces (consent, invite, verification copy) =====
   Not `.sms-terms-section`: SMS terms is a document, and its sections sit on
   the gradient like the legal documents' do — a box per section read as a
   stack of cards, with the text flush against each edge. */
.auth-app .invite-info {
  background: rgba(255, 255, 255, 0.05);
  border: 0.5px solid var(--glass-border);
  border-radius: var(--radius-control);
  color: var(--text-secondary);
}

/* A solid pill with navy ink (5.28:1), as the app draws a blue status pill —
   blue type on a blue wash is the small-text case the brand rules out. */
.auth-app .role-badge {
  background: var(--energetic-blue);
  border: none;
  color: var(--btn-primary-fg);
  text-shadow: none;
}

/* ===== Remaining Cyber Glacier neon =====
   auth.css writes #00f2ff literally in these rules — no token to override — so
   each is restated here. Three surfaces beyond the auth pages themselves reach
   the theme: SMS terms, linked from information_validations#edit and
   profiles#edit; `/help`, which the app's support sheet opens directly; and the
   legal documents, which its legal sheet does.

   **The help rules below are a restoration, not a new invention.** They shipped
   in PR #737's first commit and were removed — correctly at the time — when
   `auth_ui_theme_class` became nil for controllers not including
   AuthClientContext, which HelpController did not. It does now, so the rules
   come back with it. Anything here that is genuinely new says so.

   The legal rules are genuinely new (PR #819). Those documents arrived after
   #737, with PR #751, and brought two more cyan literals with them. */
.auth-app .btn-auth:hover {
  box-shadow: none; /* auth.css glows the button cyan on hover */
}

.auth-app .section-title {
  border-bottom-color: var(--glass-border);
}

.auth-app .help-section-title,
.auth-app .sms-terms-heading {
  color: var(--text-primary);
}

/* The question is text and takes the link blue; the chevron is a glyph and
   keeps the bright one. */
.auth-app .faq-question:hover {
  color: var(--energetic-blue-text);
}

.auth-app .faq-item--open .faq-chevron {
  color: var(--energetic-blue);
}

/* "Still need help? Send us a message below" is gone wherever the look applies
   — in the app, where the questions collapse behind a disclosure, and since
   ENG-2173 in a browser too, where the form follows the list directly.

   Hidden rather than rebuilt because of how auth.css draws it: the two rules
   are positioned absolutely at a fixed `width: 25%`, which assumes the text
   never reaches the outer quarter of the container. That held at auth.css's
   560px and fails at this look's 420px, where the rules strike straight
   through the sentence. If it ever comes back, that is where to look. */
.auth-app .help-divider {
  display: none;
}

/* New: auth.css tints the whole video block `rgba(0, 242, 255, 0.3)`. It is
   inert until `config_for(:help)[:video_url]` is set, so nothing renders it
   today — restated anyway, because the day it is configured is not the day
   anyone will remember this file. */
.auth-app .help-video {
  color: var(--text-secondary);
}

.auth-app .sms-terms a,
.auth-app .consent-link {
  color: var(--energetic-blue-text);
}

/* ===== Legal documents (/terms, /privacy) =====
   SDCrew's Account hub opens these in an in-app sheet, and the hosted login
   page links them from its consent notice, so a guardian reaches them from two
   directions and both should look like the app.

   Most of the work is already done by the shared rules above: .auth-app drops
   the card chrome and paints the ambient gradient (and .auth-ios hides the
   wordmark in the app's sheet), so only what auth.css hard-codes for these
   documents is left. That is exactly two colours and one type scale.

   Headings go to text-primary rather than the accent, following
   .sms-terms-heading directly above — the precedent this file already set for a
   long-form document. Accent-coloured section headings every few paragraphs
   read as links in a wall of prose, which is the specific reason a document
   differs from a form here. */
.auth-app .legal-heading,
.auth-app .legal-subheading {
  color: var(--text-primary);
}

/* Sizes, not just colours, and the two are connected: auth.css leans on cyan to
   pick section headings out of the prose, and it sets .legal-heading at the
   same 0.9375rem this file gives the body below. Recolouring the headings
   without resizing them therefore left "1. Agreement to Terms" the same size as
   the paragraph under it, separated by weight alone — caught on a render, not in
   the diff, where both rules look individually correct.
   So the heading takes the step back up. 1.0625rem/1rem against a 0.9375rem body
   is the same proportion auth.css had at its smaller scale. */
.auth-app .legal-heading {
  font-size: 1.0625rem;
}

.auth-app .legal-subheading {
  font-size: 1rem;
}

/* The only accent in the documents, matching .sms-terms a above. These are real
   links — Terms §16 points at the Privacy Policy, and both carry contact
   mailto:s — so they are the one thing here that should look tappable. */
.auth-app .legal-document a {
  color: var(--energetic-blue-text);
}

/* The two special paragraphs, and they need the extra `.legal-document` to work
   at all.
   `.legal-subtitle` and `.legal-meta` are <p> elements INSIDE `.legal-document`,
   so the prose rule below — `.auth-app .legal-document p`, at 0-2-1 — outranks a
   two-class selector naming them directly. Written that way they were dead
   declarations: the subtitle rendered as ordinary prose and the effective-date
   line lost its smaller size, both at exactly the body's colour. Measured before
   fixing, not reasoned about.
   Worth knowing: this is NOT a regression from theming. auth.css has the same
   collision — its own `.legal-document p` (0-1-1) beats its own `.legal-subtitle`
   (0-1-0) — so before ENG-2173 put these pages in the app look, the browser
   rendered subtitle, metadata and prose all at 13px. Fixed here rather than in
   auth.css, which stays the base layer this stylesheet overrides (#737);
   correcting it there is a separate change with its own review surface. */
.auth-app .legal-document .legal-subtitle {
  color: var(--text-primary);
  font-size: 1rem;
}

.auth-app .legal-document .legal-meta {
  color: var(--text-secondary);
  /* Smaller than the prose but not auth.css's 0.75rem: against a 0.9375rem body
     that reads as fine print rather than as a date line. 0.875rem keeps the step
     down at the same proportion the web page intended. */
  font-size: 0.875rem;
}

/* auth.css sets the body of these documents at 0.8125rem (13px), sized for a
   desktop card. Everything else on the themed page was resized to the app's
   scale — .form-group label is 0.9375rem — and 13px is small for the longest
   sustained reading anywhere in this product, on a phone, by a guardian who may
   be doing it because they are being asked to agree to it.
   0.9375rem matches the label scale; the 1.6 line-height auth.css already sets
   carries over and is right for prose. In rem, like the rest of the Type
   section, so the OS text-size setting scales it the way Dynamic Type scales
   the app. */
.auth-app .legal-document p,
.auth-app .legal-document ul,
.auth-app .legal-document ol {
  color: var(--text-secondary);
  font-size: 0.9375rem;
}

/* ===== Copy, dividers and quiet buttons (ENG-2173) =====
   Classes the auth views have always used and neither stylesheet ever styled,
   so they rendered as browser defaults: the verification page's message and
   "Didn't receive it?", and the sign-out `button_to` that application.css
   paints in its own admin blue. Secondary text for copy, white for what it
   names, link blue for anything tappable. */
/* Centred under the centred .auth-title: it is the page's subtitle. */
.auth-app .auth-message {
  color: var(--text-secondary);
  font-size: 0.9375rem;
  line-height: 1.5;
  text-align: center;
  margin: 0 0 var(--space-lg);
}

/* Except where it introduces a list, which reads down the left edge. */
.auth-app .validation-summary .auth-message {
  text-align: left;
}

.auth-app .auth-message p {
  margin: 0 0 var(--space-sm);
}

.auth-app .auth-message p:last-child {
  margin-bottom: 0;
}

.auth-app .auth-message strong {
  color: var(--text-primary);
  font-weight: 600;
}

.auth-app .auth-divider {
  color: var(--text-secondary);
  font-size: 0.8125rem;
  text-align: center;
  margin: var(--space-xl) 0 var(--space-sm);
}

.auth-app .auth-change-email {
  margin-top: var(--space-lg);
}

.auth-app .auth-change-email > summary {
  color: var(--energetic-blue-text);
  cursor: pointer;
  font-size: 0.9375rem;
  text-align: center;
  padding: var(--space-xs) 0;
  margin-bottom: var(--space-sm);
}

.auth-app .btn-link {
  background: none;
  border: none;
  color: var(--energetic-blue-text);
  cursor: pointer;
  font: inherit;
  padding: var(--space-xs) 0;
}

.auth-app .btn-link:hover {
  text-decoration: none;
}

.auth-app .btn-link:active {
  opacity: 0.6;
}

/* `button_to` wraps its button in a form; inline keeps it in the link row. */
.auth-app .auth-links form {
  display: inline;
}

/* A link dressed as a button (the funnel's "Go to sign in", "Add a player").
   auth.css styles .btn-auth for <button>/<input>, which centre their label on
   their own; an <a> needs it said. */
.auth-app a.btn-auth {
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  text-align: center;
  text-decoration: none;
}

/* ===== The onboarding front door (/onboard/:token) =====
   What the link covers sits in one glass panel — the disclosure is a single
   thing to read before deciding, not a list of decisions — and the consent
   and duplicate choices below it are plain rows of checkbox + sentence. */
.auth-app .onboarding-confirm,
.auth-app .onboarding-terminal {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.auth-app .onboarding-confirm .auth-title,
.auth-app .onboarding-terminal .auth-title {
  margin-bottom: var(--space-lg);
}

.auth-app .onboarding-confirm .auth-message,
.auth-app .onboarding-terminal .auth-message {
  margin-bottom: var(--space-sm);
}

.auth-app .transfer-manifest,
.auth-app .onboarding-resume-banner {
  background: rgba(255, 255, 255, 0.05);
  border: 0.5px solid var(--glass-border);
  border-radius: var(--radius-control);
  color: var(--text-secondary);
  font-size: 0.9375rem;
  line-height: 1.45;
  padding: var(--space-md) var(--space-lg);
  margin-bottom: var(--space-lg);
}

.auth-app .transfer-manifest h2,
.auth-app .onboarding-resume-banner h2 {
  color: var(--text-primary);
  font-size: 1rem;
  font-weight: 600;
  margin: 0 0 var(--space-sm);
}

.auth-app .transfer-manifest p {
  margin: var(--space-sm) 0 2px;
}

.auth-app .transfer-manifest ul,
.auth-app .onboarding-resume-banner ul {
  margin: 0;
  padding-left: 1.25rem;
}

.auth-app .transfer-manifest li {
  color: var(--text-primary);
  margin: 2px 0;
}

.auth-app .onboarding-resume-banner a {
  color: var(--energetic-blue-text);
  text-decoration: none;
  display: inline-block;
  padding: var(--space-xs) 0;
}

.auth-app .consent-players,
.auth-app .duplicate-candidate {
  border: none;
  margin: 0 0 var(--space-sm);
  padding: 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.auth-app .consent-players legend,
.auth-app .duplicate-candidate legend {
  color: var(--text-primary);
  font-size: 0.9375rem;
  font-weight: 600;
  padding: 0;
  margin-bottom: var(--space-md);
}

.auth-app .duplicate-candidate p {
  color: var(--text-secondary);
  font-size: 0.9375rem;
  margin: 0;
}

.auth-app .consent-player,
.auth-app .duplicate-candidate-choice {
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
}

.auth-app .consent-player .form-checkbox-label,
.auth-app .duplicate-candidate-choice .form-checkbox-label {
  color: var(--text-primary);
  font-size: 0.9375rem;
  line-height: 1.4;
}

/* ===== The post-login dashboard (/auth/dashboard) =====
   Moved out of an inline <style> that drew the cards in cyan. A tappable row
   in the app is a glass surface with a white title; the blue stays for links. */
.auth-app .dashboard-links {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.auth-app .dashboard-card {
  background: rgba(255, 255, 255, 0.06);
  border: 0.5px solid var(--glass-border);
  border-radius: var(--radius-control);
}

.auth-app .dashboard-card:active {
  background: rgba(255, 255, 255, 0.1);
}

.auth-app .dashboard-link {
  display: block;
  padding: var(--space-md) var(--space-lg);
  text-decoration: none;
  color: inherit;
  -webkit-tap-highlight-color: transparent;
}

.auth-app .dashboard-link h3 {
  margin: 0 0 2px;
  color: var(--text-primary);
  font-size: 1.0625rem;
  font-weight: 600;
}

.auth-app .dashboard-link p {
  margin: 0;
  color: var(--text-secondary);
  font-size: 0.875rem;
}

/* ===== Tickets (/tickets, /events/:id/tickets, the ticket-taker lookup) =====
   Events and orders are glass rows like the dashboard's; the QR keeps its
   white ground because scanners need the contrast. */
.auth-app .ticket-subtitle {
  color: var(--text-secondary);
  font-size: 0.9375rem;
  text-align: center;
  margin: calc(-1 * var(--space-lg)) 0 var(--space-xl);
}

.auth-app .ticket-muted {
  color: var(--text-secondary);
  font-size: 0.875rem;
  margin: 0;
}

.auth-app .ticket-empty {
  color: var(--text-secondary);
  text-align: center;
  padding: var(--space-lg) 0;
}

.auth-app .ticket-empty-headline {
  color: var(--text-primary);
  font-size: 1.0625rem;
  font-weight: 600;
}

.auth-app .ticket-event-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.auth-app .ticket-event-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding: var(--space-md) var(--space-lg);
  background: rgba(255, 255, 255, 0.06);
  border: 0.5px solid var(--glass-border);
  border-radius: var(--radius-control);
  color: inherit;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}

.auth-app .ticket-event-card:hover,
.auth-app .ticket-event-card:active {
  background: rgba(255, 255, 255, 0.1);
}

.auth-app .ticket-event-name {
  color: var(--text-primary);
  font-size: 1.0625rem;
  font-weight: 600;
}

.auth-app .ticket-event-dates {
  color: var(--text-secondary);
  font-size: 0.8125rem;
  margin-top: 2px;
}

.auth-app .ticket-event-cta {
  color: var(--energetic-blue-text);
  font-size: 0.875rem;
  font-weight: 600;
  white-space: nowrap;
}

.auth-app .ticket-section {
  margin-bottom: var(--space-xl);
}

.auth-app .ticket-section-heading {
  color: var(--text-secondary);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  margin: 0 0 var(--space-sm);
}

.auth-app .ticket-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-md) 0;
  border-bottom: 0.5px solid var(--glass-border);
}

.auth-app .ticket-row:last-child {
  border-bottom: none;
}

.auth-app .ticket-info {
  flex: 1;
}

.auth-app .ticket-name {
  color: var(--text-primary);
  font-size: 0.9375rem;
  font-weight: 600;
}

.auth-app .ticket-price {
  color: var(--text-secondary);
  font-size: 0.8125rem;
  margin-top: 2px;
}

.auth-app .ticket-qty-control {
  display: flex;
  align-items: center;
}

.auth-app .qty-btn {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 0.5px solid var(--glass-border);
  background: rgba(255, 255, 255, 0.06);
  color: var(--text-primary);
  font-size: 1.125rem;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

.auth-app .qty-btn:active {
  background: rgba(255, 255, 255, 0.14);
  transform: scale(0.92);
}

.auth-app .qty-display {
  width: 48px;
  text-align: center;
  color: var(--text-secondary);
  font-size: 1.125rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.auth-app .qty-display.has-value {
  color: var(--text-primary);
}

.auth-app .ticket-total {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-md) 0;
  margin-top: 4px;
  border-top: 0.5px solid var(--glass-border);
}

.auth-app .ticket-total-label {
  color: var(--text-secondary);
  font-size: 0.9375rem;
  font-weight: 600;
}

.auth-app .ticket-total-amount {
  color: var(--text-primary);
  font-size: 1.125rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* sd_create.css draws this badge; it does not load on this layout. */
.auth-app .status-badge {
  background: rgba(var(--rally-orange-rgb), 0.14);
  border: 0.5px solid rgba(var(--rally-orange-rgb), 0.5);
  border-radius: 999px;
  color: var(--text-primary);
  font-size: 0.8125rem;
  font-weight: 600;
  padding: 4px 10px;
  white-space: nowrap;
}

.auth-app .ticket-opt-in {
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
  margin-top: var(--space-md);
}

.auth-app .ticket-opt-in .form-checkbox-label {
  color: var(--text-secondary);
  font-size: 0.875rem;
  line-height: 1.4;
}

.auth-app .ticket-confirmation {
  color: var(--text-secondary);
  text-align: center;
  margin-bottom: var(--space-xl);
}

.auth-app .ticket-confirmation .auth-title {
  color: var(--text-primary);
  margin-bottom: var(--space-md);
}

.auth-app .ticket-confirmation p {
  margin: 0 0 var(--space-xs);
}

.auth-app .ticket-confirmation strong {
  color: var(--text-primary);
}

.auth-app .ticket-qr {
  display: block;
  width: fit-content;
  margin: 0 auto var(--space-xl);
  padding: var(--space-sm);
  background: #ffffff;
  border-radius: var(--radius-control);
}

.auth-app .ticket-qr svg {
  display: block;
}

.auth-app .ticket-qr--small {
  margin: 0;
  padding: var(--space-xs);
  border-radius: 8px;
}

.auth-app .ticket-summary {
  margin-bottom: var(--space-lg);
}

.auth-app .ticket-summary table {
  width: 100%;
  border-collapse: collapse;
}

.auth-app .ticket-summary td {
  color: var(--text-primary);
  font-size: 0.9375rem;
  padding: var(--space-xs) 0;
}

.auth-app .ticket-summary td:last-child {
  text-align: right;
  font-weight: 600;
}

.auth-app .ticket-lookup-results {
  margin-top: var(--space-xl);
}

.auth-app .ticket-lookup-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.auth-app .ticket-lookup-order {
  background: rgba(255, 255, 255, 0.06);
  border: 0.5px solid var(--glass-border);
  border-radius: var(--radius-control);
  padding: var(--space-md);
}

/* Wraps on a phone: the QR keeps its scannable size and the details drop
   below it, rather than squeezing into a one-word-wide column beside it. */
.auth-app .ticket-lookup-order-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--space-md);
}

.auth-app .ticket-lookup-order-qr {
  flex: 0 0 auto;
}

.auth-app .ticket-lookup-order-body {
  flex: 1 1 160px;
  min-width: 0;
}

.auth-app .ticket-lookup-order-team {
  color: var(--text-primary);
  font-size: 1rem;
  font-weight: 600;
}

.auth-app .ticket-lookup-order-coach {
  color: var(--text-primary);
  font-size: 0.875rem;
  opacity: 0.85;
}

.auth-app .ticket-lookup-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  margin-top: var(--space-sm);
}

.auth-app .ticket-lookup-actions form {
  display: inline;
}

/* A row-level action, not the page's CTA: hugs its label instead of filling
   the width, at the HIG's 44 floor rather than the CTA's 52. */
.auth-app .btn-auth.btn-auth-compact {
  width: auto;
  min-height: 44px;
  margin-top: 0;
  padding: var(--space-xs) var(--space-md);
  font-size: 0.875rem;
  font-weight: 600;
}

/* ===== Motion: none =====
   auth.css puts `transition: all 0.3s ease` on inputs, selects and .btn-auth.
   With `all`, every property that resolves late animates — and auth_ios.css is a
   SECOND stylesheet applied after auth.css, so each override it makes is a
   property change the transition dutifully animates. That is what read as the
   fields "animating in" on load: not an entrance animation (there are no
   keyframes anywhere in this stack) but the theme itself being applied, visibly,
   over 300ms.

   So the fix is not to shorten it — it is to have no transitions at all. A login
   is a static screen; nothing here benefits from motion, and `!important` is
   warranted because the rules being overridden (.form-group input, .btn-auth)
   are more specific than any blanket selector scoped to .auth-app could be
   without listing every one of them. */
.auth-app,
.auth-app *,
.auth-app *::before,
.auth-app *::after {
  transition: none !important;
  animation: none !important;
}
