/* ============================================================================
   Avanti Secure Chat — LibreChat auth skin. Direction B, "Split Horizon".
   ----------------------------------------------------------------------------
   Injected into every page by the nginx sub_filter proxy, but only *activates*
   on auth routes (body.avanti-auth, toggled by avanti.js). The app itself stays
   vanilla apart from the chat-icon block at the very bottom of this file.

   Same design system as branding/authentik-theme.css — that page is the one
   users land on, and the hand-off between the two must not change what the
   product looks like. The --ob-* tokens below are copied verbatim from that
   file ON PURPOSE: if one moves, move the other. What is NOT shared is layout
   code, because the two DOMs have nothing in common (authentik is PatternFly
   inside shadow roots and gets its panel out of pseudo-elements; here the DOM
   is ours, so avanti.js injects a real one).

   Targets LibreChat v0.8.7 auth DOM:
     outer  = .relative.flex.min-h-screen.flex-col
     lockup = div:has(> img[src$="logo.svg"])   (hidden; the mark is in the panel)
     card   = .w-authPageWidth
     SSO    = a[data-testid="openid"]
   ============================================================================ */

@font-face {
  font-family: 'Oswald';
  src: url('/branding/fonts/Oswald.woff2') format('woff2');
  font-weight: 200 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Archivo';
  src: url('/branding/fonts/Archivo.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* The pre-Direction-B face, brought back for the panel's bottom line only.
   NOTE: this file has NO smcp feature — verified by measurement, not assumed
   (font-feature-settings:"smcp" renders identical widths to plain). The small
   caps below are therefore SYNTHESISED by the browser. That is fine here and
   only here: Space Grotesk is an even-stroke geometric, so scaled capitals keep
   their weight instead of going thin the way a high-contrast face would. Do not
   go looking for a small-caps build; there isn't one in what we ship. */
@font-face {
  font-family: 'Space Grotesk';
  src: url('/branding/fonts/SpaceGrotesk.woff2') format('woff2');
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}

:root {
  --ob-ink:        #15181d;
  --ob-paper:      #f2f3f5;
  --ob-on-ink:     #eef1f4;
  --ob-teal-ink:   #4ac4b5;   /* teal ON INK only        */
  --ob-teal:       #2ea393;   /* teal on paper           */
  --ob-text:       #181b20;
  --ob-grey:       #67707e;
  --ob-grey-ink:   #9ca6b7;   /* secondary text on ink — see the contrast note */
  --ob-tele:       #565e6a;
  --ob-line:       #d6dae0;

  --ob-split:      44%;
  --ob-pad:        clamp(32px, 4.2vw, 60px);
  --ob-stack:      clamp(38px, 4.4vw, 64px);
  --ob-panel:      400px;
}

/* ---- the ink panel -------------------------------------------------------
   Injected by avanti.js as a direct child of <body>, outside React's tree so a
   re-render cannot drop it. Fixed, so the right column scrolls on its own and
   the ring geometry can bleed off the corner without affecting layout.
   ------------------------------------------------------------------------- */
#avanti-panel {
  display: none;
  position: fixed;
  inset: 0 auto 0 0;
  width: var(--ob-split);
  height: 100dvh;
  box-sizing: border-box;
  padding: clamp(34px, 4vw, 56px) var(--ob-pad);
  background-color: var(--ob-ink);
  overflow: hidden;
  z-index: 0;

  /* ---- the login corner (orbit-earth, 2026-07-30) ------------------------
     A still Earth in the bottom-right, plus the two outer rings. There used to
     be THREE ring gradients here; the innermost (r=280) is now the Earth's own
     limb and drawing it again doubled the stroke, so it is gone and the other
     two are restated below. They stay gradients rather than pixels for the
     original reason: a 1px stroke stays 1px at every DPR.

     Placement is `right bottom` and nothing else — the image is cropped so its
     own corner IS the panel's corner, so there is no offset to drift and it is
     correct at every panel width. Do not "fix" it with calc().

     The EARTH is static; the satellite lights below drift. Yves: "We have earth
     on both pages, it just doesn't rotate. We just have the image, that's fine,
     no JS on either page then." No JavaScript either way.
     ---------------------------------------------------------------------- */
  background-image:
    image-set(url('/branding/dawn-earth-bold@1x.webp') 1x,
              url('/branding/dawn-earth-bold@2x.webp') 2x),
    radial-gradient(circle at calc(100% + 60px) calc(100% + 120px),
      transparent 569px, rgba(74,196,181,.085) 569px, rgba(74,196,181,.085) 570px, transparent 570px);
  background-position: right bottom, 0 0;
  background-size: 472px 412px, auto;
  background-repeat: no-repeat;
}

/* The two satellite lights, one per ring, positioned on the shared orbit centre
   at calc(100% + 60px) / calc(100% + 120px) and thrown out to each ring's radius
   by translateX. The box-shadow stack is the trail: fixed angular offsets behind
   the head, fading out.

   The lights DRIFT; only the Earth is still. Yves was twice told the satellites
   would keep moving and did not object, and the original brief asked for "faint
   lights (satellites) orbiting" — so "it just doesn't rotate" attached to the
   Earth, not to these. No JavaScript is involved either way.

   A ghost lagging D degrees behind the head sits at ( r(cosD-1), -r sinD ) in
   the element's own rotated frame, which is why these fixed box-shadow offsets
   follow the ARC rather than trailing in a straight line.

   THE GLOW (Yves, 2026-07-30: "a glow that fits the theme"). Each head carries
   its original tight pale core glow plus two wider falloffs in --ob-teal-ink
   (#4ac4b5) — the same family as the Earth's limb and the ring strokes, so the
   bloom reads as part of the corner rather than as an effect laid over it. Sat B
   is held back (.17/.08 against A's .24/.11) so it stays the dimmer of the two,
   which is orbit-earth's relationship and not something to flatten.

   The trail ghosts get NO glow, deliberately. The head is the light source and
   the ghosts are afterimages; haloing five of them per satellite smears the arc
   into a streak and loses the beads. Blur only on the head.

   Angles, radii, periods and tints are orbit-earth's, taken verbatim from the
   canvas build Yves approved — do not round them. Keep in step with the same
   block in authentik-theme.css; the two origins must be identical. */
#avanti-panel::before {
  content: '';
  position: absolute;
  right: -61.5px;
  bottom: -121.5px;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: rgba(238,248,246,1);
  box-shadow: 0 0 8px 1px rgba(176,238,228,0.51),
              0 0 17px 3px rgba(74,196,181,0.24),
              0 0 34px 7px rgba(74,196,181,0.11),
              -0.062px -8px 0 0 rgba(176,238,228,0.54),
              -0.249px -15.997px 0 0 rgba(176,238,228,0.315),
              -0.559px -23.991px 0 0 rgba(176,238,228,0.188),
              -0.994px -31.979px 0 0 rgba(176,238,228,0.105),
              -1.553px -39.96px 0 0 rgba(176,238,228,0.054);
  transform: rotate(201deg) translateX(515px);
  animation: ob-sweep-a 31s linear infinite,
             ob-fade-a  31s linear infinite;
  animation-delay: 0s;
  pointer-events: none;
  z-index: 0;
}
@keyframes ob-sweep-a {
  from { transform: rotate(201deg) translateX(515px) }
  to   { transform: rotate(262deg) translateX(515px) }
}
/* The end-of-sweep fade is also what keeps the light off the top of the panel.
   Re-verify by DIFFERENCE if these change — satellites on minus off, per-pixel
   max across phases — because an absolute reading of that region is meaningless
   with the white display stack sitting in it. */
@keyframes ob-fade-a {
  0%, 100% { opacity: 0 }
  11%, 89% { opacity: 1 }
}

#avanti-panel::after {
  content: '';
  position: absolute;
  right: -61.5px;
  bottom: -121.5px;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: rgba(238,248,246,1);
  box-shadow: 0 0 8px 1px rgba(206,222,236,0.51),
              0 0 17px 3px rgba(74,196,181,0.17),
              0 0 34px 7px rgba(74,196,181,0.08),
              -0.056px -8px 0 0 rgba(206,222,236,0.54),
              -0.225px -15.998px 0 0 rgba(206,222,236,0.315),
              -0.505px -23.993px 0 0 rgba(206,222,236,0.188),
              -0.898px -31.983px 0 0 rgba(206,222,236,0.105),
              -1.403px -39.967px 0 0 rgba(206,222,236,0.054);
  transform: rotate(196deg) translateX(570px);
  animation: ob-sweep-b 44s linear infinite,
             ob-fade-b  44s linear infinite;
  animation-delay: -12.5s;
  pointer-events: none;
  z-index: 0;
}
@keyframes ob-sweep-b {
  from { transform: rotate(196deg) translateX(570px) }
  to   { transform: rotate(264deg) translateX(570px) }
}
/* The end-of-sweep fade is also what keeps the light off the top of the panel.
   Re-verify by DIFFERENCE if these change — satellites on minus off, per-pixel
   max across phases — because an absolute reading of that region is meaningless
   with the white display stack sitting in it. */
@keyframes ob-fade-b {
  0%, 100% { opacity: 0 }
  11%, 89% { opacity: 0.78 }
}

/* Parked where the composition reads, recomputed for the new radii — the angles
   from the 380/480 build put a light at 59% of the panel height once thrown out
   to 515/570, which is up against the descriptor. */
@media (prefers-reduced-motion: reduce) {
  #avanti-panel::before {
    animation: none;
    transform: rotate(225deg) translateX(515px);
    opacity: 1;
  }
  #avanti-panel::after {
    animation: none;
    transform: rotate(205deg) translateX(570px);
    opacity: 0.78;
  }
}


body.avanti-auth #avanti-panel {
  display: flex;
  flex-direction: column;
  /* Brand row, then the implementation line, then the display stack — NOT the
     bottom-anchored order the panel had before the Bold earth.

     The line used to sit at the panel's foot. With Bold that position is gone:
     the planet's lit area now covers the whole lower right, and the text
     measured 1.12:1 there. Moving it up does not rescue it either — measured at
     60/120/180/240px of lift it never cleared 2.3:1, and directly under the
     descriptor it still fails up to 1440px wide. The only region that is ink at
     EVERY viewport is the top, where it measures 7.25:1.

     Narrowing was the other option and it is worse: 20ch clears at 1440+ but
     fails at 1280 and 1330-1390, because the planet is a fixed 472px while the
     panel is 44% of viewport — so the safe width is viewport-dependent and there
     is no single value. Re-measure with tools/probe-panel-contrast.js if this
     ever moves again. */
  justify-content: flex-start;
}

body.avanti-auth #avanti-panel .ap-tele { order: 1; margin-top: 18px; }

/* the stack keeps its optical centring between the top group and the foot */
/* Top-aligned, NOT centred. Centring the stack pushed the descriptor down onto
   the planet — measured 3.89:1 — so the whole group sits high and the planet
   gets the lower half of the panel to itself. */
body.avanti-auth #avanti-panel .ap-stack { order: 2; margin-top: clamp(28px, 6vh, 64px); }

#avanti-panel .ap-brand {
  display: flex;
  align-items: center;
  gap: 14px;
}

#avanti-panel .ap-brand img {
  width: 40px;
  height: 40px;
}

#avanti-panel .ap-brand b {
  color: var(--ob-on-ink);
  font: 600 13px/1 'Archivo', system-ui, sans-serif;
  letter-spacing: .26em;
  text-transform: uppercase;
}

#avanti-panel .ap-stack {
  color: var(--ob-on-ink);
  font: 500 var(--ob-stack)/1.12 'Oswald', 'Archivo', system-ui, sans-serif;
  letter-spacing: .02em;
  text-transform: uppercase;
}

/* the full stop is a real element here — authentik has to do this with a
   gradient clipped to text because it only gets pseudo-elements */
#avanti-panel .ap-stack i {
  font-style: normal;
  color: var(--ob-teal-ink);
}

#avanti-panel .ap-stack small {
  display: block;
  max-width: 34ch;
  margin-top: 22px;
  color: var(--ob-grey-ink);
  font: 400 17px/1.6 'Archivo', system-ui, sans-serif;
  letter-spacing: 0;
  text-transform: none;
  /* even the lines rather than letting the last one run longest — without it
     this block broke after the em dash and left it dangling at a line end */
  text-wrap: balance;
}

/* The implementation line. Was a tracked-caps telemetry row — "EU · SWEDEN
   CENTRAL / SSO · AVANTI ID / AES-256" — which Yves rejected: "blubber… sounds
   too american" (2026-07-30). The tracked caps were most of the problem; that
   treatment IS the acronym-badge register, so this is sentence case and states
   who operates the thing and where instead of naming ciphers.

   --ob-grey-ink, and the number that matters is NOT the one against flat ink.
   The old note here said 4.71:1 on the ink ground and treated that as settled.
   It was a knife-edge: AA needs the backdrop under 0.0118 luminance and ink is
   0.0090, so the entire margin was 0.0028. Anything that lit this corner broke
   it — and something already had. Measured on the live page before the fix, the
   bottom line was 3.32:1, because the Earth's outer glow clipped the very last
   pixel of its box.

   So the colour is #9ca6b7 since 2026-07-30, chosen against the WORST BACKDROP
   PIXEL rather than against ink: 5.10:1 over that pixel, 7.25:1 on flat ink, and
   it still clears AA up to a backdrop of 0.0450 — about a third lighter than
   today's worst — which is the headroom the Bold earth is going to spend.
   ~#949ca9 was suggested and rejected: it measures 4.51:1, another knife-edge,
   which is the bug rather than the fix.

   HOW TO CHECK IT, and this is the acceptance criterion for this line:
       node tools/probe-panel-contrast.js            (chat)
       node tools/probe-panel-contrast.js --auth     (authentik)
   Contrast over a moving backdrop is a MINIMUM OVER TIME, not a number — two
   satellites sweep behind this text on 31s and 44s cycles, so the worst frame
   can be seconds from whichever still you screenshot. The probe hides the text,
   samples the true backdrop across the rotation, and reports the worst pixel.
   Keep both origins in step.

   Set in Space Grotesk small caps since 2026-07-30 ("the old font with
   Kapitälchen"). The text stays SENTENCE CASE in the DOM on purpose — feeding
   it uppercase would flatten the initial cap into the small caps and lose the
   cap/small-cap contrast that makes it Kapitälchen rather than tracked caps.
   Slightly larger and a touch tracked, because synthesised small caps sit on
   the x-height and read smaller than their nominal size. */
#avanti-panel .ap-tele {
  max-width: 46ch;
  color: var(--ob-grey-ink);
  /* 500, not 400: synthesised small caps come out lighter than true ones, so
     the weight is bumped to compensate. Matched to authentik-theme.css — this
     is the one piece of prose in the panel and the two origins must look
     identical. If you change any of the three numbers here (500 / 14px /
     .04em), change them there too. */
  font: 500 14px/1.6 'Space Grotesk', 'Archivo', system-ui, sans-serif;
  font-variant-caps: small-caps;
  letter-spacing: .04em;
  text-transform: none;
  /* small caps made this line long enough to orphan "infrastructure." onto a
     line of its own; balance splits it evenly instead */
  text-wrap: balance;
}

/* ---- page shell ----------------------------------------------------------
   The outer column becomes the paper side; the fixed panel is reserved with
   padding rather than a grid column, so LibreChat's own flex layout inside it
   keeps working untouched.
   ------------------------------------------------------------------------- */
/* The paper goes on <body>, NOT on this element. Its background paints across
   the full width including the padding that reserves the panel, and it is a
   positioned sibling that stacks above #avanti-panel — so painting paper here
   covers the panel completely. */
body.avanti-auth { background: var(--ob-paper) !important; }

body.avanti-auth .relative.flex.min-h-screen.flex-col {
  position: relative;
  z-index: 1;
  box-sizing: border-box;
  justify-content: center !important;
  padding-left: var(--ob-split);
  background: transparent !important;
  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
}

body.avanti-auth main.flex.flex-grow {
  flex-grow: 0 !important;
  /* LibreChat centres the card with a flex ROW here; the phone brand row is a
     sibling of the card, so as a row it lands beside the form instead of above */
  flex-direction: column !important;
  align-items: center !important;
  width: 100%;
  padding: 0 40px;
}

/* the mark lives in the panel now */
body.avanti-auth div:has(> img[src$="logo.svg"]),
body.avanti-auth .mt-6.h-10.w-full {
  display: none !important;
}

/* ---- the form column ----------------------------------------------------- */
body.avanti-auth .w-authPageWidth {
  box-sizing: border-box;
  width: 100%;
  max-width: var(--ob-panel) !important;
  margin: 0 !important;
  background: none !important;
  border: 0 !important;
  border-radius: 0 !important;
  padding: 0 !important;
  box-shadow: none !important;
  overflow: visible !important;
  color: var(--ob-text);
}

/* LibreChat's own heading is replaced, not restyled: "Welcome back" is not in
   this design and the eyebrow/title/sub block below says it better. The :not()
   is load-bearing — the replacement is also an <h1> and also a direct child of
   the card, so without it this rule hides the new title too. */
body.avanti-auth .w-authPageWidth > h1:not(#avanti-title) {
  display: none !important;
}

/* The teal tracked-caps eyebrow that used to sit here is gone (2026-07-30): the
   panel's brand row now carries the product name on every breakpoint, so the
   eyebrow repeated it — two lines apart on phones. Title is the first child of
   the card now, hence no top margin. */
#avanti-title {
  margin: 0 0 6px !important;
  color: var(--ob-text);
  font: 600 30px/1.2 'Archivo', system-ui, sans-serif;
  letter-spacing: -.01em;
}

#avanti-sub {
  margin: 0 0 34px !important;
  color: var(--ob-grey);
  font: 400 15px/1.5 'Archivo', system-ui, sans-serif;
}

/* ---- fields (register / forgot-password / reset-password) ---------------- */
body.avanti-auth form {
  display: grid;
  gap: 20px;
}

/* LibreChat floats its labels over the field's top border rather than stacking
   them above it. Left alone on paper they render as black chips, because the
   chip that punches through the border is `bg-surface-primary` — dark-theme
   surface. Repainting it white gives the notched-outline form of the design's
   label instead of fighting LibreChat's mechanism. */
body.avanti-auth label {
  background: #fff !important;
  padding: 0 5px !important;
  color: var(--ob-grey) !important;
  font: 600 12px/1 'Archivo', system-ui, sans-serif !important;
  letter-spacing: .14em;
  text-transform: uppercase;
}

body.avanti-auth input:not([type=checkbox]):not([type=radio]) {
  box-sizing: border-box;
  width: 100%;
  height: 48px !important;
  padding: 0 16px !important;
  background-color: #fff !important;
  border: 1px solid var(--ob-line) !important;
  border-radius: 6px !important;
  color: var(--ob-text) !important;
  font: 400 15.5px/48px 'Archivo', system-ui, sans-serif !important;
  box-shadow: none !important;
  -webkit-text-fill-color: var(--ob-text);
  transition: border-color .15s ease, box-shadow .15s ease;
}

body.avanti-auth input:focus {
  border-color: var(--ob-teal) !important;
  box-shadow: 0 0 0 3px rgba(46, 163, 147, .18) !important;
  outline: none !important;
}

/* -webkit-text-fill-color as well as color, and it is not belt-and-braces: the
   field sets -webkit-text-fill-color for the autofill rules below, that property
   INHERITS into ::placeholder, and it outranks `color` for the actual paint. So
   `color` alone computed correctly and never painted — every placeholder on this
   origin had been rendering in --ob-text, reading like an entered value rather
   than a hint. Measured before the fix: phColor rgb(154,161,171) but phFill
   rgb(24,27,32). Same bug and same fix on the auth side (flow-design found it
   there); if you touch one, touch both. */
body.avanti-auth input::placeholder {
  color: #9aa1ab !important;
  -webkit-text-fill-color: #9aa1ab !important;
}

body.avanti-auth input:-webkit-autofill,
body.avanti-auth input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--ob-text);
  -webkit-box-shadow: 0 0 0 1000px #fff inset;
  caret-color: var(--ob-text);
}

/* ---- links (before the button rules: the SSO control is an <a>) ---------- */
body.avanti-auth a {
  color: var(--ob-teal) !important;
  font-weight: 500;
}

body.avanti-auth a:hover { text-decoration: underline; }

/* ---- primary actions ----------------------------------------------------- */
body.avanti-auth a[data-testid="openid"],
body.avanti-auth button[type="submit"] {
  display: flex !important;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: 100% !important;
  height: 48px !important;
  margin-top: 8px;
  padding: 0 16px !important;
  background: var(--ob-teal) !important;
  border: 0 !important;
  border-radius: 6px !important;
  color: #0d1417 !important;
  font: 600 15px/1 'Archivo', system-ui, sans-serif !important;
  letter-spacing: .02em;
  text-decoration: none !important;
  box-shadow: none !important;
  transition: filter .15s ease, transform .15s ease;
}

body.avanti-auth a[data-testid="openid"]:hover,
body.avanti-auth button[type="submit"]:hover {
  filter: brightness(1.06);
  text-decoration: none !important;
}

body.avanti-auth a[data-testid="openid"]:active,
body.avanti-auth button[type="submit"]:active { transform: translateY(1px); }

body.avanti-auth a[data-testid="openid"]:focus-visible,
body.avanti-auth button[type="submit"]:focus-visible {
  outline: 2px solid var(--ob-teal);
  outline-offset: 3px;
}

/* the OpenID keyhole is a third-party mark that exists nowhere in this design,
   and `space-x-3` puts the gap on the <p>, so hiding the icon alone would leave
   the label optically off-centre */
body.avanti-auth a[data-testid="openid"] > svg { display: none !important; }
body.avanti-auth a[data-testid="openid"] > p { margin: 0 !important; }

/* /register and /forgot-password put SSO under their own submit button, where
   two identical teal bars read as two primary actions */
body.avanti-auth .w-authPageWidth:has(button[type="submit"]) a[data-testid="openid"] {
  background: transparent !important;
  border: 1px solid var(--ob-teal) !important;
  color: var(--ob-teal) !important;
}

body.avanti-auth .w-authPageWidth:has(button[type="submit"]) a[data-testid="openid"]:hover {
  background: rgba(46, 163, 147, .08) !important;
  filter: none;
}

/* ---- chrome + messages ---------------------------------------------------
   LibreChat pins its theme toggle bottom-LEFT of the outer column, which is now
   the ink panel — a --ob-grey glyph on #15181d. Move it to the paper side
   rather than recolour it, so one rule covers both themes of the control. */
body.avanti-auth .absolute.bottom-0.left-0 {
  left: auto !important;
  right: 0 !important;
}

body.avanti-auth .absolute.bottom-0.left-0 button { color: var(--ob-grey) !important; }

body.avanti-auth [role="alert"],
body.avanti-auth .text-red-500,
body.avanti-auth .text-red-600 { color: #c0392b !important; }

body.avanti-auth .text-gray-700,
body.avanti-auth .text-gray-600,
body.avanti-auth .text-black,
body.avanti-auth .text-white { color: var(--ob-grey) !important; }

/* ---- 760–1100px: keep the split, shrink the stack ------------------------ */
@media (max-width: 1100px) {
  :root {
    --ob-split: 40%;
    --ob-stack: clamp(30px, 3.6vw, 42px);
    --ob-panel: 340px;
  }

  #avanti-panel .ap-stack small { font-size: 15px; }
  #avanti-panel .ap-tele { font-size: 10.5px; letter-spacing: .16em; }
  body.avanti-auth main.flex.flex-grow { padding: 0 24px; }
}

/* ---- below 760px: no panel ----------------------------------------------
   A 40% ink column on a phone leaves the form about 200px wide. The panel is
   dropped and its brand row is reproduced above the form in ink-on-paper.
   ------------------------------------------------------------------------- */
@media (max-width: 759px) {
  body.avanti-auth #avanti-panel { display: none !important; }

  /* display:none on the panel does NOT reliably stop the background image being
     fetched, so drop it explicitly — otherwise phones pay for a 14/36 KB Earth
     they never see. content:none kills the two satellite lights with it. */
  #avanti-panel { background-image: none; }
  #avanti-panel::before,
  #avanti-panel::after { content: none; }

  body.avanti-auth .relative.flex.min-h-screen.flex-col {
    padding-left: 0;
    padding-top: 30px;
    justify-content: flex-start !important;
  }

  body.avanti-auth main.flex.flex-grow { padding: 0 22px; }

  #avanti-mobile-brand {
    display: flex !important;
    align-items: center;
    gap: 12px;
    width: 100%;
    max-width: var(--ob-panel);
    margin: 0 auto 30px;
  }

  /* both mark PNGs are white-on-transparent, so on paper the mark is painted as
     a mask filled with the ink colour rather than drawn as an image — the same
     reason the in-app icon does it (DEPLOY.md §8). Monogram, not the
     ring-and-wordmark: six letters cannot resolve at 32px. */
  #avanti-mobile-brand i {
    width: 32px;
    height: 32px;
    background: var(--ob-text);
    -webkit-mask: url('/branding/avanti-mark-a-512.png') center / contain no-repeat;
            mask: url('/branding/avanti-mark-a-512.png') center / contain no-repeat;
  }

  /* The phone wordmark spells the product out in full (26 characters), which at
     the panel's 13px/.26em ran past 360px and wrapped — orphaning "CHAT" onto a
     second line with the monogram centred against the pair. A wordmark must not
     wrap, so it is set tighter here than in the panel. nowrap is deliberate: if
     a longer name is ever introduced it should visibly run out of room rather
     than quietly fold in half. */
  #avanti-mobile-brand b {
    color: var(--ob-text);
    font: 600 11.5px/1 'Archivo', system-ui, sans-serif;
    letter-spacing: .16em;
    text-transform: uppercase;
    white-space: nowrap;
  }

  #avanti-title { font-size: 26px; }

  /* /register stacks five fields under the brand row and overflowed an 844px
     phone by 25px; the design's desktop rhythm is what does not fit, so it
     compresses here rather than the fields shrinking below 48px touch height */
  #avanti-mobile-brand { margin-bottom: 20px; }
  #avanti-sub { margin-bottom: 24px !important; }
  body.avanti-auth form { gap: 16px; }
}

#avanti-mobile-brand { display: none; }

/* ---------------------------------------------------------------------------
   Chat icons, theme-adaptive via mask + currentColor.

   LibreChat renders each model spec's iconURL as an <img>, which cannot inherit
   currentColor. So the <img>'s own bitmap is hidden (object-fit:none + an
   offscreen position keeps the layout box) and the mark is painted as
   background-color: currentColor clipped by a mask - the original feather-slot
   treatment (6058b9e) applied to a new target. currentColor follows the APP
   theme toggle, which can disagree with the OS theme; that is why the fallback
   inside avanti-icon.svg (which can only see prefers-color-scheme) is a fixed
   grey rather than adaptive.

   Which mark where: everything small gets the OpenAI blossom (Yves: the 'A'
   next to a model name says nothing - these ARE OpenAI models; the picker
   button, menu rows and message avatars all sit beside a model name). Only the
   ~48px landing greeting (.icon-xl wrapper) carries the Avanti ring-and-wordmark,
   at 94% - sized up on request, still clearing the rounded-full crop.

   Hardening, after a black-on-dark report that never reproduced in Chromium:
   substring [src*=] match instead of a prefix (immune to a component emitting
   an absolute URL), and longhand mask properties instead of the shorthand
   (Safari has history with slash-size in -webkit-mask shorthand; longhands
   parse everywhere).
   ------------------------------------------------------------------------- */
img[src*="/branding/avanti-icon.svg"] {
  object-fit: none;
  object-position: -9999px -9999px;
  background-color: currentColor;
  -webkit-mask-image: url('/branding/openai-mark.svg');
  -webkit-mask-position: center;
  -webkit-mask-size: 100%;
  -webkit-mask-repeat: no-repeat;
  mask-image: url('/branding/openai-mark.svg');
  mask-position: center;
  mask-size: 100%;
  mask-repeat: no-repeat;
}
.icon-xl img[src*="/branding/avanti-icon.svg"] {
  -webkit-mask-image: url('/branding/avanti-mark-512.png');
  -webkit-mask-size: 94%;
  mask-image: url('/branding/avanti-mark-512.png');
  mask-size: 94%;
}

/* currentColor must not depend on inheritance. Landing.tsx passes its
   theme-aware `text-black dark:text-white` into ConvoIcon's className, but
   ConvoIcon does not forward className to the image branch - so the landing
   <img> inherits no colour from anywhere and lands on the UA default: BLACK,
   in dark mode too. (Every other icon slot happens to sit under an element
   with a real text colour, which is why only the greeting broke.) Pin the
   colour on the img itself, keyed to LibreChat's html.dark/html.light class -
   the same values its text-primary resolves to on each theme. */
img[src*="/branding/avanti-icon.svg"] { color: #212121; }
html.dark img[src*="/branding/avanti-icon.svg"] { color: #ececec; }
