/* ==========================================================================
   GRV Identity — login & onboarding panel, design "TP 2026"

   Loaded only when the Design tab is set to tp2026. Scoped under
   .grv-modal-tp2026 so the two older designs are untouched.

   Colours are literal tokens, not the theme's --grv-* vars: this panel is
   TalkingPoints chrome and must look identical on every publisher site.
   Token names below map to the design system's colors_and_type.css.
   ========================================================================== */

.grv-tp-overlay,
.grv-modal-tp2026,
.grv-sr-dropdown.grv-sr-dropdown-tp2026 {
  --tp-surface-1: #1C1C1E;   /* panel */
  --tp-surface-2: #2A2A2C;   /* cards */
  --tp-outline: #3A3A3C;     /* hairline */
  --tp-black-panel: #000;    /* app strip, app card */
  --tp-paper: #FFFFFF;       /* primary fill / text */
  --tp-ink: #000;            /* text ON --tp-paper fills */
  --tp-fg-2: #A9A9AD;        /* secondary text */
  --tp-fg-3: #8A8A8E;        /* tertiary text */
  --tp-live: #E53935;        /* accent */
  --tp-notif: #B3121A;       /* darkened live, 4.5:1 with white */
  --tp-bronze: #B87333;
  /* Hover/active washes. Light in dark mode, dark in light mode. */
  --tp-wash-soft: rgba(255, 255, 255, .06);
  --tp-wash-hard: rgba(255, 255, 255, .14);
  --tp-wash-tile: rgba(255, 255, 255, .16);
  --tp-on-notif: rgba(255, 255, 255, .82);
  /* Publisher sites are near-black, so darkening alone is close to invisible:
     62% black over a #111 article reads as the same #111. The scrim leans on
     --tp-scrim-filter below to push the page back, and stays dark enough to
     drop the page's whites well under the panel's. */
  --tp-scrim: rgba(0, 0, 0, .72);
  /* Blur separates the panel from the page on any ground, dark or light, and
     the saturation drop pulls the colour out of what is behind so the panel's
     own reds and whites are the only saturated things on screen. */
  --tp-scrim-filter: blur(6px) saturate(55%);
  /* Field fill. Distinct from the panel so inputs read as inputs; in dark it
     matches the panel, in light it is a touch off-white. */
  --tp-field: #1C1C1E;
  /* The app strip stays dark in both themes, so its foreground is fixed
     rather than theme-following. */
  --tp-on-strip: #FFFFFF;
  --tp-on-strip-2: #8A8A8E;
  --tp-strip-outline: #3A3A3C;
  /* Foreground for the red/dark grounds that do not flip with the theme:
     the notification card, the newsletter toggle knob, the app pill. */
  --tp-on-accent: #FFFFFF;
  /* Deeper and wider than a card's shadow: this panel has to sit clearly in
     front of a scrimmed page rather than just above a light one. The inset
     hairline gives the panel a top-lit edge, which is what separates it from
     the dimmed page when both are near-black. */
  --tp-shadow-panel:
    0 8px 24px rgba(0, 0, 0, .45),
    0 1px 4px rgba(0, 0, 0, .3),
    inset 0 1px 0 rgba(255, 255, 255, .07);
  --tp-shadow-sheet: 0 -2px 12px rgba(0, 0, 0, .32);
  /* A side sheet is lit from the edge it slides over, so its shadow falls
     left rather than up. */
  --tp-shadow-side: -4px 0 18px rgba(0, 0, 0, .35);
  --tp-ease: cubic-bezier(.4, 0, .2, 1);
  /* The side sheet travels its own full width — further than the vertical
     sheets — so it gets a harder decelerate and a little more time than the
     shared curve, which lands too briskly over that distance. Both curves end
     flat at 1: nothing here overshoots. */
  --tp-ease-side: cubic-bezier(.32, .72, 0, 1);
  --tp-dur-side-in: 340ms;
  --tp-dur-side-out: 240ms;
  --tp-fast: 250ms;
}

/* ---------- Light mode ----------

   The panel was specified dark-only, so dark remains the base and light is a
   remap of the same token roles rather than a second design. Surfaces invert,
   the primary fill flips from white-on-black to black-on-white, and the washes
   go from lightening to darkening.

   The accent reds are kept: --tp-notif on white text is the notification
   card's contrast pair in both themes, and --tp-live is brand.

   Only tokens are redefined here — every rule below consumes them, so nothing
   needs a light-mode duplicate. */
@media (prefers-color-scheme: light) {
  .grv-tp-overlay,
  .grv-modal-tp2026,
  .grv-sr-dropdown.grv-sr-dropdown-tp2026 {
    --tp-surface-1: #FFFFFF;   /* panel */
    --tp-surface-2: #F1F1F4;   /* cards */
    --tp-outline: #DDDDE1;     /* hairline */
    /* The app strip and app card stay dark on purpose: they are the
       TalkingPoints band, and inverting them loses that separation. */
    --tp-black-panel: #1C1C1E;
    --tp-paper: #000000;       /* primary fill / text */
    --tp-ink: #FFFFFF;         /* text ON that fill */
    --tp-fg-2: #5F6368;        /* secondary text */
    --tp-fg-3: #74777C;        /* tertiary text, 4.5:1 on white */
    --tp-wash-soft: rgba(0, 0, 0, .05);
    --tp-wash-hard: rgba(0, 0, 0, .11);
    /* A light page already separates from the dark-ish panel by value, so the
       scrim only needs to mute it, not bury it. */
    --tp-scrim: rgba(0, 0, 0, .5);
    --tp-shadow-panel:
      0 8px 24px rgba(20, 20, 30, .16),
      0 1px 4px rgba(20, 20, 30, .10);
    --tp-shadow-sheet: 0 -2px 12px rgba(20, 20, 30, .10);
    --tp-shadow-side: -4px 0 18px rgba(20, 20, 30, .12);
    --tp-field: #F7F7F9;
    /* --tp-on-strip*, --tp-strip-outline and --tp-notif are deliberately not
       remapped: the strip band and the notification card keep their dark
       ground in light mode, so their foregrounds must stay light too. */
  }
}

/* The hidden attribute must beat every display rule in this file — the states
   and the collapsed extras are all toggled through it. */
.grv-modal-tp2026 [hidden] {
  display: none !important;
}

/* ---------- Ad suppression while the panel is open ----------

   Ad units cannot be out-stacked: the interscroller sets
   z-index: 2147483647 !important (theme partial _prop.scss) and GPT's own
   anchor slots run to 999999999, so no z-index on the overlay can win. They
   are hidden for as long as the panel is open instead, which is the pattern
   the base stylesheet already uses for the exit-intent overlay.

   Sticky and anchored units only: in-flow ads scroll away behind the scrim on
   their own, and hiding those would collapse the article layout and lose
   impressions for no reason. Visibility rather than display so a slot that has
   already rendered is not torn down and re-requested on close. */
html.grv-login-modal-open .celtra-ad-host,
html.grv-login-modal-open .celtra-ad-inline-host,
html.grv-login-modal-open .GRVAd.leaderboard-fixed,
html.grv-login-modal-open .GRVAdFix,
html.grv-login-modal-open .grv-sticky,
/* GPT injects its anchor/sticky slots outside the theme's markup. */
html.grv-login-modal-open div[id^="google_ads_iframe"][style*="fixed"],
html.grv-login-modal-open .adhesion,
html.grv-login-modal-open #grv-sticky-footer {
  visibility: hidden !important;
}

/* ---------- Shell ---------- */

.grv-tp-overlay {
  background: var(--tp-scrim);
  /* -webkit- kept for Safari, which still ships the property prefixed. */
  -webkit-backdrop-filter: var(--tp-scrim-filter);
  backdrop-filter: var(--tp-scrim-filter);
  padding: 0;
  /* The scrim fades in with the panel's slide rather than snapping on, so the
     page recedes as the panel arrives. */
  animation: grvTpScrimIn 300ms var(--tp-ease) both;
}

/* Matched to the panel's 200ms close so page and panel land together. The
   closing class goes on the panel, not the overlay (modal/core.js), so the
   scrim keys off the overlay's own closing flag, set on the same tick. */
.grv-tp-overlay[data-grv-closing="1"] {
  animation: grvTpScrimOut 200ms var(--tp-ease) both;
}

@keyframes grvTpScrimIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes grvTpScrimOut {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* Confetti burst on the welcome state (utils/confetti.js).

   Lives in the overlay rather than the panel: .grv-modal-tp2026 sets
   overflow: hidden, so a canvas inside it would be clipped at the very edge
   the pieces are supposed to fly off. Fixed to the viewport so the pieces can
   travel above the panel and out past its sides.

   Above the panel in the stacking order — pieces launched from the top edge
   should pass in front of it as they fall back — but pointer-events: none, so
   it never intercepts a tap meant for the panel or the scrim behind it. */
.grv-tp-overlay > .grv-tp-confetti {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 3;
  pointer-events: none;
}

.grv-modal.grv-modal-tp2026 {
  width: 420px;
  max-width: 100%;
  background: var(--tp-surface-1);
  color: var(--tp-paper);
  /* Square top edge meets the header the panel hangs from. */
  border-radius: 0 0 20px 20px;
  box-shadow: var(--tp-shadow-panel);
  overflow: hidden;
  font-family: "DM Sans", system-ui, sans-serif;
  line-height: 1.45;
  border: 0;
}

/* The anchored-positioning rules in the base stylesheet square off all four
   corners; this design keeps the bottom two rounded. */
.grv-modal-overlay.grv-modal-anchored > .grv-modal.grv-modal-tp2026 {
  width: min(420px, calc(100vw - 24px));
  border-radius: 0 0 20px 20px;
  box-shadow: var(--tp-shadow-panel);
}

/* Clips the travelling content to the panel's own box, so the slide reads as
   emerging from behind the header instead of flying over it. Generous room
   left below for the shadow. */
.grv-modal-overlay.grv-modal-anchored > .grv-modal.grv-modal-tp2026 {
  clip-path: inset(0 -100px -100px -100px);
}

/* The wrapper is what slides; the panel stays put and does the clipping. */
.grv-modal-tp2026 > .grv-tp-slide {
  display: block;
  animation: grvTpPanelDown 300ms var(--tp-ease) both;
}

.grv-modal-tp2026.grv-tp-closing > .grv-tp-slide {
  animation: grvTpPanelUpOutInner 200ms var(--tp-ease) both;
}

/* The account menu is the same anchored surface, and already clips via its
   own overflow: hidden, so only the inner slide is needed. */
.grv-sr-dropdown.grv-sr-dropdown-tp2026 > .grv-sr-dropdown-inner {
  animation: grvTpPanelDown 300ms var(--tp-ease) both;
}

.grv-sr-dropdown.grv-sr-dropdown-tp2026.grv-tp-closing > .grv-sr-dropdown-inner {
  animation: grvTpPanelUpOutInner 200ms var(--tp-ease) both;
}

.grv-modal-tp2026 .grv-tp-steps {
  /* The strip and legal footer sit outside the scroll area so the strip stays
     pinned to the panel's bottom edge. */
  max-height: calc(100vh - 160px);
  max-height: calc(100dvh - 160px);
  overflow-y: auto;
  /* Once this is the thing scrolling, the scroll must stop here rather than
     handing off to the article behind the sheet. */
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

.grv-modal-tp2026 .grv-tp-view {
  padding: 28px 24px 20px;
  margin: 0;
  border: 0;
  background: none;
  box-sizing: border-box;
}

/* State changes fade in with a 6px rise — no scale, no bounce. */
.grv-modal-tp2026 .grv-tp-view:not([hidden]) {
  animation: grvTpFade var(--tp-fast) var(--tp-ease);
}

@keyframes grvTpFade {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* Bottom sheets rise from the screen edge they are pinned to. Translate only —
   the design rules out scale and bounce — over the 300ms default with the
   standard easing, which decelerates into place. */
@keyframes grvTpSheetUp {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}

/* Opacity only: used for state changes inside a sheet, where the sheet's own
   travel already supplies the movement. */
@keyframes grvTpSheetFade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* The desktop panel drops out from behind the header bar it hangs from — the
   mirror of the sheet rising from the bottom edge. It travels its own full
   height, so the content has to be masked while it is still above its resting
   position: clip-path hides everything above the panel's top edge, and the
   inner wrapper is what actually moves. Translate only, no scale. */
@keyframes grvTpPanelDown {
  from { transform: translateY(-100%); }
  to   { transform: translateY(0); }
}

@keyframes grvTpPanelUpOutInner {
  from { transform: translateY(0); }
  to   { transform: translateY(-100%); }
}

/* Exits are the reverse of the entrances, and shorter — a dismissal should
   not make the reader wait. JS adds .grv-tp-closing, then hides the overlay
   once the animation ends. */
@keyframes grvTpSheetDownOut {
  from { transform: translateY(0); }
  to   { transform: translateY(100%); }
}

/* The account panel is a side sheet, not a bottom one: it slides in from the
   right edge it is pinned to and leaves the same way. Translate only, matching
   the rest of the design's motion rules. */
@keyframes grvTpSideIn {
  from { transform: translateX(100%); }
  to   { transform: translateX(0); }
}

@keyframes grvTpSideOut {
  from { transform: translateX(0); }
  to   { transform: translateX(100%); }
}

@media (prefers-reduced-motion: reduce) {
  .grv-modal-tp2026 .grv-tp-view:not([hidden]),
  .grv-modal-tp2026 .grv-tp-more-panel:not([hidden]) {
    animation: none;
  }
  .grv-modal-tp2026 .grv-tp-toggle { transition: none; }
}

/* The panel carries an explicit close in every state. Escape and the trigger
   also dismiss it, but neither is dependable on its own: the header trigger
   scrolls out of view, the mobile sheet has no trigger on screen at all, and a
   backdrop tap is not an affordance anyone can see. */
.grv-modal-tp2026 .grv-tp-close,
.grv-sr-dropdown-tp2026 .grv-tp-close {
  position: absolute;
  top: 8px;
  left: 10px;
  z-index: 2;
  /* Rounded-square tile, following the notification bell's (40px / 12px
     radius) but smaller, so it reads as the same family without competing
     with the bell. The wash is the panel's own, as the view's back control
     used before this button absorbed that job. */
  background: var(--tp-wash-soft);
  border: 0;
  border-radius: 10px;
  color: var(--tp-fg-3);
  font-size: 22px;
  line-height: 1;
  /* 32px tile inside a 44px hit area: the visible square stays small while the
     touch target keeps its minimum. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  margin: 0;
  cursor: pointer;
  transition: background 120ms var(--tp-ease);
}

/* The back chevron is an SVG, and publisher themes set their own fill on
   descendant paths — usually the theme's near-black ink, which would ignore the
   button's own colour and its hover. Pinned to currentColor for the same reason
   the bell glyph and the QR modules are. The wrapper case covers the anchored
   subtree, where the theme's rules are scoped to the lit trigger. */
.grv-modal-tp2026 .grv-tp-close svg,
.grv-modal-tp2026 .grv-tp-close svg path,
.grv-sr-dropdown-tp2026 .grv-tp-close svg,
.grv-sr-dropdown-tp2026 .grv-tp-close svg path,
.grv-anchor-wrap-active .grv-tp-close svg,
.grv-anchor-wrap-active .grv-tp-close svg path {
  fill: currentColor !important;
}

/* Sized here rather than trusting the SVG's width/height attributes, so the
   chevron reads at the same optical weight as the × it replaces — that glyph
   is text at 22px, whose drawn mark is a good deal smaller than its font size.
   Publisher themes also set dimensions on descendant SVGs, which the
   attributes alone would lose to. */
.grv-modal-tp2026 .grv-tp-close svg,
.grv-sr-dropdown-tp2026 .grv-tp-close svg,
.grv-anchor-wrap-active .grv-tp-close svg {
  width: 15px;
  height: 15px;
  display: block;
}

.grv-modal-tp2026 .grv-tp-close:focus-visible,
.grv-sr-dropdown-tp2026 .grv-tp-close:focus-visible {
  outline: 2px solid var(--tp-paper);
  outline-offset: -4px;
}

/* The close sits in the same band as the centred title, so the title reserves
   the width of the button on BOTH sides — padding one side only would shift
   the centred text off the panel's axis. */
.grv-modal-tp2026 .grv-tp-view > .grv-tp-title:first-child,
.grv-modal-tp2026 .grv-tp-signin .grv-tp-title {
  padding-left: 34px;
  padding-right: 34px;
}

.grv-modal-tp2026 .grv-tp-close:hover,
.grv-sr-dropdown-tp2026 .grv-tp-close:hover {
  background: var(--tp-wash-hard);
  color: var(--tp-paper);
}

.grv-modal-tp2026 .grv-tp-ico {
  /* The base modal stylesheet sets font-family: "DM Sans" !important on
     .grv-modal *, which would beat this and print the ligature name as text. */
  font-family: "Material Symbols Outlined" !important;
  font-weight: 400;
  font-style: normal;
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  white-space: nowrap;
  word-wrap: normal;
  direction: ltr;
  -webkit-font-feature-settings: "liga";
  -webkit-font-smoothing: antialiased;
  flex: none;
}

/* Not every publisher theme ships .screen-reader-text, so the panel carries
   its own. */
.grv-modal-tp2026 .grv-tp-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- Type ---------- */

.grv-modal-tp2026 .grv-tp-title {
  font: 700 22px/1.25 "DM Sans", system-ui, sans-serif;
  text-align: center;
  color: var(--tp-paper);
  margin: 0;
  padding: 0;
}

.grv-modal-tp2026 .grv-tp-sub {
  font: 400 14px/1.45 "DM Sans", system-ui, sans-serif;
  color: var(--tp-fg-2);
  text-align: center;
  margin: 6px 0 0;
}

/* ---------- Buttons ---------- */

.grv-modal-tp2026 .grv-tp-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  height: 52px;
  box-sizing: border-box;
  border: 0;
  border-radius: 100px;
  font: 700 16px/1 "DM Sans", system-ui, sans-serif;
  cursor: pointer;
  text-decoration: none;
  transition: opacity var(--tp-fast) var(--tp-ease),
              background-color var(--tp-fast) var(--tp-ease);
}

.grv-modal-tp2026 .grv-tp-btn-primary {
  background: var(--tp-paper);
  color: var(--tp-ink);
}

.grv-modal-tp2026 .grv-tp-btn-primary:hover { opacity: .92; color: var(--tp-ink); }
.grv-modal-tp2026 .grv-tp-btn-primary:active { opacity: .82; }

.grv-modal-tp2026 .grv-tp-btn-outline {
  background: transparent;
  border: 1px solid var(--tp-outline);
  color: var(--tp-paper);
  font-weight: 600;
}

.grv-modal-tp2026 .grv-tp-btn-outline:hover {
  background: var(--tp-wash-soft);
  color: var(--tp-paper);
}

.grv-modal-tp2026 .grv-tp-btn-outline:active { background: var(--tp-wash-hard); }

.grv-modal-tp2026 .grv-tp-btn-sm {
  height: 48px;
  font-size: 15px;
}

.grv-modal-tp2026 .grv-tp-text-btn {
  background: none;
  border: 0;
  color: var(--tp-fg-2);
  font: 600 13px/1 "DM Sans", system-ui, sans-serif;
  cursor: pointer;
  padding: 6px;
  text-decoration: none;
}

.grv-modal-tp2026 .grv-tp-text-btn:hover { color: var(--tp-paper); }

/* The provider pairs swipe horizontally rather than stacking, so the panel
   holds one height whichever pair is showing. The viewport clips; the track
   slides. */
.grv-modal-tp2026 .grv-tp-providers {
  margin-top: 18px;
  overflow: hidden;
  /* Clipping a rounded pill would shave its edge, so the viewport is padded
     and the padding pulled back with a negative margin. */
  padding: 2px;
  margin-left: -2px;
  margin-right: -2px;
}

.grv-modal-tp2026 .grv-tp-provider-track {
  display: flex;
  align-items: flex-start;
  transition: transform 300ms var(--tp-ease);
}

.grv-modal-tp2026 .grv-tp-providers[data-grv-slide="1"] .grv-tp-provider-track {
  transform: translateX(-100%);
}

.grv-modal-tp2026 .grv-tp-provider-slide {
  flex: 0 0 100%;
  display: flex;
  flex-direction: column;
  gap: 10px;
  /* Both slides sit in flow, so the taller one sets the height and neither
     pair jumps as the track moves. */
  min-width: 0;
}

/* The off-screen pair must not be reachable or announced. */
.grv-modal-tp2026 .grv-tp-provider-slide[aria-hidden="true"] {
  visibility: hidden;
}

.grv-modal-tp2026 .grv-tp-providers[data-grv-slide="1"] .grv-tp-provider-slide[data-grv-provider-slide="1"] {
  visibility: visible;
}

.grv-modal-tp2026 .grv-tp-providers[data-grv-slide="1"] .grv-tp-provider-slide[data-grv-provider-slide="0"] {
  visibility: hidden;
}

@media (prefers-reduced-motion: reduce) {
  .grv-modal-tp2026 .grv-tp-provider-track { transition: none; }
}

.grv-modal-tp2026 .grv-tp-provider-mark { flex: none; }

/* ---------- Provider button label: two forms, one markup ----------

   Default (wide) is unchanged from the original design: the mark leads and the
   full sentence follows — "[G] Continue with Google". The narrow form is set
   up in the width media query further down. Both verbs are in the DOM; each
   rule below shows one and hides the other, so no width ever shows both. */

.grv-modal-tp2026 .grv-tp-btn {
  /* Containing block for the visually-hidden brand name at narrow widths: its
     -1px margin would otherwise open a stray scroll area on whatever ancestor
     happens to be positioned. */
  position: relative;
}

/* The short verb is the narrow-only one, so it starts hidden. */
.grv-modal-tp2026 .grv-tp-verb-narrow { display: none; }

/* The verb and the brand words are two elements but one phrase, so they must
   sit a single word-space apart — not the button's 10px flex gap, which is the
   spacing meant for the mark. Wrapping them in their own inline-level box
   takes them out of the flex layout: inside it they lay out as normal text and
   the whitespace between the tags does the spacing, exactly as it would if the
   label were one string. */
.grv-modal-tp2026 .grv-tp-btn-label {
  display: inline;
  min-width: 0;
}

/* ---------- Held comment draft ---------- */

.grv-modal-tp2026 .grv-tp-draft {
  margin-top: 18px;
  padding: 12px 14px;
  background: var(--tp-surface-2);
  border-radius: 12px;
  border-left: 3px solid var(--tp-live);
}

.grv-modal-tp2026 .grv-tp-draft-eyebrow {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  font: 500 11px/1.25 "DM Sans", system-ui, sans-serif;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--tp-fg-2);
}

.grv-modal-tp2026 .grv-tp-draft-body {
  margin: 6px 0 0;
  font: 400 15px/1.45 "DM Sans", system-ui, sans-serif;
  color: var(--tp-paper);
  /* A long draft must not push the buttons off-panel. */
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
}

.grv-modal-tp2026 .grv-tp-draft-meta {
  margin: 8px 0 0;
  font: 500 12px/1.3 "DM Sans", system-ui, sans-serif;
  color: var(--tp-fg-3);
}

.grv-modal-tp2026 .grv-tp-draft-meta .grv-tp-story { color: var(--tp-live); }

/* ---------- Divider ---------- */

.grv-modal-tp2026 .grv-tp-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 18px 0 14px;
}

.grv-modal-tp2026 .grv-tp-divider::before,
.grv-modal-tp2026 .grv-tp-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--tp-outline);
}

.grv-modal-tp2026 .grv-tp-divider span {
  font: 500 11px/1 "DM Sans", system-ui, sans-serif;
  letter-spacing: .06em;
  color: var(--tp-fg-3);
}

/* ---------- Email row ---------- */

.grv-modal-tp2026 .grv-tp-email-form { margin: 0; }

.grv-modal-tp2026 .grv-tp-email-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.grv-modal-tp2026 .grv-tp-email-row input[type="email"] {
  flex: 1;
  min-width: 0;
  height: 52px;
  box-sizing: border-box;
  margin: 0;
  padding: 0 16px;
  border-radius: 100px;
  border: 1px solid var(--tp-outline);
  background: var(--tp-field);
  color: var(--tp-paper);
  font: 400 16px/1 "DM Sans", system-ui, sans-serif;
  outline: none;
  box-shadow: none;
}

.grv-modal-tp2026 .grv-tp-email-row input[type="email"]::placeholder { color: var(--tp-fg-3); }

.grv-modal-tp2026 .grv-tp-email-row input[type="email"]:focus,
.grv-modal-tp2026 .grv-tp-code-input:focus {
  border-color: var(--tp-paper);
}

.grv-modal-tp2026 .grv-tp-email-send {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  /* Disabled until the address contains "@". */
  background: var(--tp-surface-2);
  color: var(--tp-fg-3);
  cursor: not-allowed;
  transition: background-color var(--tp-fast) var(--tp-ease),
              color var(--tp-fast) var(--tp-ease);
}

.grv-modal-tp2026 .grv-tp-email-send.is-ready {
  background: var(--tp-paper);
  color: var(--tp-ink);
  cursor: pointer;
}

.grv-modal-tp2026 .grv-tp-email-send.is-ready:hover { opacity: .92; }

.grv-modal-tp2026 .grv-tp-help {
  margin: 8px 0 0;
  padding-left: 4px;
  font: 400 12px/1.4 "DM Sans", system-ui, sans-serif;
  color: var(--tp-fg-3);
}

.grv-modal-tp2026 .grv-tp-status {
  margin: 8px 0 0;
  padding-left: 4px;
  font: 400 12px/1.4 "DM Sans", system-ui, sans-serif;
  color: var(--tp-fg-2);
}

.grv-modal-tp2026 .grv-tp-status.is-error { color: var(--tp-live); }

/* ---------- Collapsed extras ---------- */

.grv-modal-tp2026 .grv-tp-more-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  width: 100%;
  /* Sits between the provider buttons and the OR divider, so it takes a
     tighter top margin than it did below the email row. */
  margin-top: 12px;
  padding: 8px;
  background: none;
  border: 0;
  color: var(--tp-fg-2);
  font: 600 13px/1 "DM Sans", system-ui, sans-serif;
  cursor: pointer;
}

.grv-modal-tp2026 .grv-tp-more-toggle:hover { color: var(--tp-paper); }

/* One chevron at a time: it trails the label going forward and leads coming
   back, so it always points the way the track will travel. */
.grv-modal-tp2026 .grv-tp-more-chevron {
  display: flex;
  align-items: center;
}

.grv-modal-tp2026 .grv-tp-more-chevron-back { display: none; }

.grv-modal-tp2026 .grv-tp-more-toggle[aria-expanded="true"] .grv-tp-more-chevron-back {
  display: flex;
}

.grv-modal-tp2026 .grv-tp-more-toggle[aria-expanded="true"] .grv-tp-more-chevron-fwd {
  display: none;
}

.grv-modal-tp2026 .grv-tp-more-panel {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* display:flex above would otherwise defeat the hidden attribute, leaving the
   collapsed extras open on load. */
.grv-modal-tp2026 .grv-tp-more-panel[hidden] {
  display: none;
}

.grv-modal-tp2026 .grv-tp-more-panel:not([hidden]) {
  animation: grvTpFade var(--tp-fast) var(--tp-ease);
}

.grv-modal-tp2026 .grv-tp-more-panel .grv-tp-text-btn {
  color: var(--tp-fg-3);
  font-weight: 500;
}

/* ---------- 2. Check your email ---------- */

.grv-modal-tp2026 .grv-tp-code { padding-bottom: 22px; }

.grv-modal-tp2026 .grv-tp-back {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 0 0 14px;
  background: none;
  border: 0;
  color: var(--tp-fg-2);
  font: 600 13px/1 "DM Sans", system-ui, sans-serif;
  cursor: pointer;
}

.grv-modal-tp2026 .grv-tp-back:hover { color: var(--tp-paper); }

.grv-modal-tp2026 .grv-tp-code-badge {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  margin: 0 auto;
  border-radius: 50%;
  background: var(--tp-surface-2);
  color: var(--tp-paper);
}

.grv-modal-tp2026 .grv-tp-code .grv-tp-title { margin-top: 14px; }

.grv-modal-tp2026 .grv-tp-code-input {
  width: 100%;
  box-sizing: border-box;
  height: 60px;
  margin-top: 20px;
  padding: 0;
  border-radius: 12px;
  border: 1px solid var(--tp-outline);
  background: var(--tp-field);
  color: var(--tp-paper);
  font: 700 26px/1 "DM Sans", system-ui, sans-serif;
  text-align: center;
  letter-spacing: .5em;
  /* Offsets the trailing letter-space so the digits read as centred. */
  text-indent: .5em;
  outline: none;
  box-shadow: none;
}

.grv-modal-tp2026 .grv-tp-code-input::placeholder {
  color: var(--tp-fg-3);
  font-weight: 400;
}

.grv-modal-tp2026 .grv-tp-verify {
  margin-top: 14px;
  /* Inert until six digits are in. */
  background: var(--tp-surface-2);
  color: var(--tp-fg-3);
  cursor: default;
}

.grv-modal-tp2026 .grv-tp-verify.is-ready {
  background: var(--tp-paper);
  color: var(--tp-ink);
  cursor: pointer;
}

.grv-modal-tp2026 .grv-tp-verify.is-ready:hover { opacity: .92; }
.grv-modal-tp2026 .grv-tp-verify.is-busy { cursor: progress; }

.grv-modal-tp2026 .grv-tp-code-error {
  margin: 8px 0 0;
  font: 400 12px/1.4 "DM Sans", system-ui, sans-serif;
  color: var(--tp-live);
  text-align: center;
}

.grv-modal-tp2026 .grv-tp-code-note {
  margin: 10px 0 0;
  font: 400 10px/1.4 "DM Sans", system-ui, sans-serif;
  color: var(--tp-fg-3);
  text-align: right;
}

/* Marker on the title, tied to the footnote below. Raised and lightened so it
   reads as a reference rather than part of the word. */
.grv-modal-tp2026 .grv-tp-title-note {
  color: var(--tp-fg-3);
  font-weight: 400;
  vertical-align: super;
  font-size: .6em;
  margin-left: 1px;
}

.grv-modal-tp2026 .grv-tp-code-actions {
  display: flex;
  justify-content: center;
  gap: 18px;
  margin-top: 14px;
}

.grv-modal-tp2026 .grv-tp-text-btn[disabled] {
  color: var(--tp-fg-3);
  cursor: default;
}

/* ---------- 3. Welcome ---------- */

.grv-modal-tp2026 .grv-tp-welcome { padding-bottom: 22px; }

/* Welcome state: the two ways on, stacked with an OR between them — the same
   divider and rhythm the sign-in state uses between its social buttons and the
   email form. Stacked rather than side by side because that divider is a
   horizontal rule with a centred label; it only reads between rows. Neither
   control is the default: the reader is choosing site or app. */
.grv-modal-tp2026 .grv-tp-finish-row {
  display: flex;
  flex-direction: column;
  /* The lead line above supplies the gap to the card before it. */
  margin-top: 12px;
}

.grv-modal-tp2026 .grv-tp-finish-row > .grv-tp-btn {
  width: 100%;
  margin: 0;
}

/* Sits above the two routes and introduces them. Card-title scale, since it
   heads the choice rather than annotating it. */
.grv-modal-tp2026 .grv-tp-finish-lead {
  margin: 18px 0 0;
  font: 700 15px/1.3 "DM Sans", system-ui, sans-serif;
  color: var(--tp-paper);
  text-align: center;
}

/* Animated gradient border on the app route, marking it as the recommended
   one without a second filled button competing with "Continue on site".

   Drawn on a ::before rather than as a real border: a border cannot take a
   gradient. The pseudo-element covers the button, is painted with the gradient,
   then masked so only a 1.5px ring survives — two mask layers, one filling the
   whole box and one the inset interior, composited with `exclude` so the
   interior is punched out. Nothing is blurred and nothing sits outside the
   button's own box, so it cannot bleed onto neighbouring rows.

   Everything animates on background-position, which is composited — no layout
   or paint work per frame. */
.grv-modal-tp2026 .grv-tp-finish-row > .grv-tp-app-open {
  position: relative;
  /* The real border becomes the ring's own space: transparent, so the gradient
     below shows through it rather than sitting under a grey line. */
  border-color: transparent;
}

.grv-modal-tp2026 .grv-tp-finish-row > .grv-tp-app-open::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  /* Matches the 1px border this replaces, so the ring lands exactly where the
     grey line did. */
  padding: 1px;
  background-image: linear-gradient(
    92deg,
    #FF6B4A,
    #FFB03A,
    #E0479A,
    #7B5BFF,
    #35C4E8,
    #7B5BFF,
    #E0479A,
    #FFB03A,
    #FF6B4A
  );
  background-size: 300% 100%;
  background-position: 0% 0;
  /* Ring only: fill the box, then subtract the content area. -webkit-* first
     for Safari, which still needs the prefixed pair. */
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
  animation: grvTpBorderFlow 9s linear infinite;
}

@keyframes grvTpBorderFlow {
  /* The gradient is mirrored end to end, so travelling one full width lands
     back on the starting colours — the loop has no visible seam. */
  to { background-position: 300% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .grv-modal-tp2026 .grv-tp-finish-row > .grv-tp-app-open::before {
    animation: none;
  }
}

/* Caption under the app route. Matches the legal footer's scale — it explains
   the button above it rather than competing with either label. */
.grv-modal-tp2026 .grv-tp-finish-note {
  margin: 18px 0 0;
  font: 400 12px/1.4 "DM Sans", system-ui, sans-serif;
  color: var(--tp-fg-3);
  text-align: center;
}

.grv-modal-tp2026 .grv-tp-finish-note strong { font-weight: 700; }

.grv-modal-tp2026 .grv-tp-finish-note strong { font-weight: 700; }

/* The divider carries the spacing between the two, so the row adds none. */
.grv-modal-tp2026 .grv-tp-finish-row > .grv-tp-divider {
  margin: 14px 0;
}

/* The app route is an <a>, which the button rules do not centre on their own. */
.grv-modal-tp2026 .grv-tp-finish-row > a.grv-tp-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
}

.grv-modal-tp2026 .grv-tp-avatar {
  flex: none;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  object-fit: cover;
  /* Placeholder ground for a provider avatar that is missing or still loading. */
  background: var(--tp-surface-2);
}

.grv-modal-tp2026 .grv-tp-welcome-who { min-width: 0; }

.grv-modal-tp2026 .grv-tp-welcome-title {
  margin: 0;
  padding: 0;
  font: 700 22px/1.25 "DM Sans", system-ui, sans-serif;
  color: var(--tp-paper);
  text-align: center;
}

.grv-modal-tp2026 .grv-tp-welcome-sub {
  margin: 3px 0 0;
  font: 400 13px/1.4 "DM Sans", system-ui, sans-serif;
  color: var(--tp-fg-2);
}

/* No shadows on cards inside the panel — dark-mode rule. */
.grv-modal-tp2026 .grv-tp-card {
  margin-top: 14px;
  padding: 14px;
  background: var(--tp-surface-2);
  border-radius: 12px;
  box-shadow: none;
}

.grv-modal-tp2026 .grv-tp-nl {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 20px;
}

.grv-modal-tp2026 .grv-tp-nl-copy { flex: 1; min-width: 0; }

.grv-modal-tp2026 .grv-tp-card-title {
  margin: 0;
  font: 600 15px/1.35 "DM Sans", system-ui, sans-serif;
  color: var(--tp-paper);
}

.grv-modal-tp2026 .grv-tp-card-body {
  margin: 2px 0 0;
  font: 400 13px/1.4 "DM Sans", system-ui, sans-serif;
  color: var(--tp-fg-2);
}

.grv-modal-tp2026 .grv-tp-toggle {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  width: 52px;
  height: 30px;
  padding: 3px;
  box-sizing: border-box;
  border: 0;
  border-radius: 100px;
  background: var(--tp-outline);
  cursor: pointer;
  transition: background-color var(--tp-fast) var(--tp-ease);
}

.grv-modal-tp2026 .grv-tp-toggle[aria-checked="true"] {
  background: var(--tp-live);
  /* The knob is moved by justify-content, not a transform. */
  justify-content: flex-end;
}

.grv-modal-tp2026 .grv-tp-toggle-knob {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  /* On the accent track in both themes, so it stays white rather than
     flipping to black with --tp-paper. */
  background: var(--tp-on-accent);
}

.grv-modal-tp2026 .grv-tp-app {
  padding: 16px;
  background: var(--tp-black-panel);
  border: 1px solid var(--tp-strip-outline);
  color: var(--tp-on-strip);
}

.grv-modal-tp2026 .grv-tp-app .grv-tp-card-body {
  margin-top: 3px;
  color: var(--tp-on-strip-2);
}

/* The official store badges carry their own dark artwork, so their outline and
   hover wash stay light-on-dark too. */
.grv-modal-tp2026 .grv-tp-store-badges .grv-v3-buttons a {
  border-color: var(--tp-strip-outline);
}

.grv-modal-tp2026 .grv-tp-store-badges .grv-v3-buttons a:hover {
  background: rgba(255, 255, 255, .06);
}

.grv-modal-tp2026 .grv-tp-store-badges {
  display: flex;
  flex-direction: column;
  gap: 9px;
  flex: 1;
  min-width: 0;
}

/* render_store_buttons() ships the official Apple/Google artwork with its own
   .grv-v3-buttons sizing (160px fixed, centred row). Inside this card the two
   badges stack and fill the column beside the QR instead. */
.grv-modal-tp2026 .grv-tp-store-badges .grv-v3-buttons {
  display: flex;
  flex-direction: column;
  gap: 9px;
  width: 100%;
}

.grv-modal-tp2026 .grv-tp-store-badges .grv-v3-buttons a {
  width: 100%;
  height: 42px;
  /* The supplied badges carry their own black ground and rounded corners. */
  background: transparent;
  border: 1px solid var(--tp-outline);
  border-radius: 8px;
  padding: 0 10px;
  transition: background-color var(--tp-fast) var(--tp-ease);
}

.grv-modal-tp2026 .grv-tp-store-badges .grv-v3-buttons a:hover {
  background: var(--tp-wash-soft);
}

.grv-modal-tp2026 .grv-tp-store-badges .grv-v3-buttons a svg {
  width: auto;
  height: 100%;
  max-width: 100%;
}



/* ---------- QR ---------- */

/* The base stylesheet paints every descendant SVG of an anchored trigger with
   `fill: ... !important` so the header icon stays lit above the scrim. The
   account menu lives inside that subtree, which repainted the QR's black
   modules white — a blank box. A QR has to keep its own colours to scan, so
   they are forced back. Matches .grv-anchor-active's specificity + the
   attribute selector, and !important beats !important on equal specificity by
   coming later in the cascade. */
.grv-anchor-active .grv-tp-qr svg,
.grv-anchor-active .grv-tp-qr svg *,
.grv-anchor-wrap-active .grv-tp-qr svg,
.grv-anchor-wrap-active .grv-tp-qr svg *,
.grv-tp-qr[data-grv-qr] svg,
.grv-tp-qr[data-grv-qr] svg * {
  opacity: 1 !important;
}

/* Module colour comes from the SVG's own fill attributes, so the override is
   scoped to the two shapes the generator emits. */
.grv-anchor-active .grv-tp-qr svg rect,
.grv-anchor-wrap-active .grv-tp-qr svg rect,
.grv-tp-qr[data-grv-qr] svg rect {
  fill: #fff !important;
}

.grv-anchor-active .grv-tp-qr svg path,
.grv-anchor-wrap-active .grv-tp-qr svg path,
.grv-tp-qr[data-grv-qr] svg path {
  fill: #000 !important;
}

.grv-modal-tp2026 .grv-tp-qr,
.grv-sr-dropdown-tp2026 .grv-tp-qr {
  flex: none;
  background: var(--tp-paper);
  box-sizing: border-box;
}

.grv-modal-tp2026 .grv-tp-qr-lg,
.grv-sr-dropdown-tp2026 .grv-tp-qr-lg {
  width: 92px;
  height: 92px;
  padding: 6px;
  border-radius: 8px;
}

.grv-modal-tp2026 .grv-tp-qr-sm,
.grv-modal-tp2026 .grv-tp-qr-sm,
.grv-sr-dropdown-tp2026 .grv-tp-qr-sm {
  width: 46px;
  height: 46px;
  padding: 3px;
  border-radius: 6px;
}

.grv-modal-tp2026 .grv-tp-qr canvas,
.grv-sr-dropdown-tp2026 .grv-tp-qr canvas,
.grv-modal-tp2026 .grv-tp-qr img,
.grv-sr-dropdown-tp2026 .grv-tp-qr img,
.grv-modal-tp2026 .grv-tp-qr svg,
.grv-sr-dropdown-tp2026 .grv-tp-qr svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* ---------- 4. Signed in ---------- */

.grv-modal-tp2026 .grv-tp-menu { padding: 22px 20px 18px; }

.grv-modal-tp2026 .grv-tp-menu-head {
  display: flex;
  align-items: center;
  gap: 12px;
}

.grv-modal-tp2026 .grv-tp-menu-avatar-wrap {
  position: relative;
  flex: none;
}

.grv-modal-tp2026 .grv-tp-avatar-lg {
  width: 52px;
  height: 52px;
}

.grv-modal-tp2026 .grv-tp-tier-badge {
  position: absolute;
  right: -4px;
  bottom: -4px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  /* Tier colour is overridden by JS from the user's actual tier. */
  background: var(--tp-bronze);
  border: 2px solid var(--tp-surface-1);
  color: var(--tp-paper);
}

.grv-modal-tp2026 .grv-tp-menu-who { min-width: 0; }

.grv-modal-tp2026 .grv-tp-menu-name {
  margin: 0;
  font: 700 20px/1.2 "DM Sans", system-ui, sans-serif;
  color: var(--tp-paper);
}

.grv-modal-tp2026 .grv-tp-menu-meta {
  margin: 2px 0 0;
  font: 400 14px/1.3 "DM Sans", system-ui, sans-serif;
  color: var(--tp-fg-2);
}

.grv-modal-tp2026 .grv-tp-notif {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  box-sizing: border-box;
  margin-top: 16px;
  padding: 12px 14px;
  background: var(--tp-notif);
  border: 0;
  border-radius: 12px;
  color: var(--tp-paper);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition: opacity var(--tp-fast) var(--tp-ease);
}

.grv-modal-tp2026 .grv-tp-notif:hover { opacity: .92; color: var(--tp-paper); }

.grv-modal-tp2026 .grv-tp-notif-tile {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 12px;
  background: var(--tp-wash-tile);
}

.grv-modal-tp2026 .grv-tp-notif-copy {
  flex: 1;
  min-width: 0;
}

.grv-modal-tp2026 .grv-tp-notif,
.grv-modal-tp2026 .grv-tp-notif-title,
.grv-modal-tp2026 .grv-tp-notif-tile {
  color: var(--tp-on-accent);
}

.grv-modal-tp2026 .grv-tp-notif-title {
  display: block;
  font: 700 15px/1.3 "DM Sans", system-ui, sans-serif;
}

.grv-modal-tp2026 .grv-tp-notif-sub {
  display: block;
  font: 400 13px/1.35 "DM Sans", system-ui, sans-serif;
  color: var(--tp-on-notif);
}

.grv-modal-tp2026 .grv-tp-rows {
  display: flex;
  flex-direction: column;
  margin-top: 8px;
}

.grv-modal-tp2026 .grv-tp-row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  box-sizing: border-box;
  padding: 13px 14px;
  background: transparent;
  border: 0;
  border-radius: 12px;
  color: var(--tp-paper);
  font: 600 15px/1 "DM Sans", system-ui, sans-serif;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--tp-fast) var(--tp-ease);
}

.grv-modal-tp2026 .grv-tp-row .grv-tp-ico { color: var(--tp-fg-2); }

.grv-modal-tp2026 .grv-tp-row:hover {
  background: var(--tp-wash-soft);
  color: var(--tp-paper);
}

/* Deliberately de-emphasised — it was too prominent before. */
.grv-modal-tp2026 .grv-tp-row-muted { color: var(--tp-fg-2); }
.grv-modal-tp2026 .grv-tp-row-muted:hover { color: var(--tp-fg-2); }

/* ---------- Persistent app strip ---------- */

.grv-modal-tp2026 .grv-tp-strip,
.grv-sr-dropdown-tp2026 .grv-tp-strip {
  padding: 14px 24px;
  background: var(--tp-black-panel);
  border-top: 1px solid var(--tp-strip-outline);
  /* Dark band in both themes, so it sets its own colour context: descendants
     using currentColor (the TP mark) inherit light, not the theme's ink. */
  color: var(--tp-on-strip);
}

.grv-modal-tp2026 .grv-tp-strip-desktop,
.grv-sr-dropdown-tp2026 .grv-tp-strip-desktop {
  display: flex;
  align-items: center;
  gap: 12px;
}

.grv-modal-tp2026 .grv-tp-strip-mobile,
.grv-sr-dropdown-tp2026 .grv-tp-strip-mobile { display: none; }

.grv-modal-tp2026 .grv-tp-strip-copy,
.grv-sr-dropdown-tp2026 .grv-tp-strip-copy {
  flex: 1;
  min-width: 0;
}

.grv-modal-tp2026 .grv-tp-wordmark,
.grv-sr-dropdown-tp2026 .grv-tp-wordmark {
  display: flex;
  align-items: center;
  gap: 7px;
  font: 400 14px/1.2 "DM Sans", system-ui, sans-serif;
  color: var(--tp-on-strip);
}

.grv-modal-tp2026 .grv-tp-wordmark b,
.grv-sr-dropdown-tp2026 .grv-tp-wordmark b { font-weight: 700; }
.grv-modal-tp2026 .grv-tp-mark,
.grv-sr-dropdown-tp2026 .grv-tp-mark { flex: none; }

.grv-modal-tp2026 .grv-tp-strip-caption,
.grv-sr-dropdown-tp2026 .grv-tp-strip-caption {
  display: block;
  margin-top: 3px;
  font: 400 12px/1.35 "DM Sans", system-ui, sans-serif;
  color: var(--tp-on-strip-2);
}

/* The caption is the call to action in this band, not a subtitle under one, so
   it takes pure white rather than the strip's muted secondary. Literal #FFF,
   not --tp-on-strip: the strip stays black in both themes, so there is nothing
   for a token to flip to here. */
.grv-modal-tp2026 .grv-tp-strip-mobile .grv-tp-strip-caption,
.grv-sr-dropdown-tp2026 .grv-tp-strip-mobile .grv-tp-strip-caption {
  color: #FFFFFF;
}

/* "Talking" carries the wordmark's bold half, as it does in the app's own
   banner. The base caption rule sets `font:` shorthand, which resets weight on
   the parent — so this has to restate it rather than rely on <strong>'s
   default, and it is scoped tightly enough to survive a theme's own strong
   styling. */
.grv-modal-tp2026 .grv-tp-strip-caption strong,
.grv-sr-dropdown-tp2026 .grv-tp-strip-caption strong,
.grv-modal-tp2026 .grv-tp-strip-mobile .grv-tp-strip-caption strong,
.grv-sr-dropdown-tp2026 .grv-tp-strip-mobile .grv-tp-strip-caption strong {
  font-weight: 700;
  color: inherit;
}


.grv-modal-tp2026 .grv-tp-strip-cta,
.grv-sr-dropdown-tp2026 .grv-tp-strip-cta {
  flex: none;
  background: transparent;
  border: 1px solid var(--tp-strip-outline);
  border-radius: 100px;
  padding: 9px 14px;
  color: var(--tp-on-strip);
  font: 700 12px/1 "DM Sans", system-ui, sans-serif;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--tp-fast) var(--tp-ease);
}

.grv-modal-tp2026 .grv-tp-strip-cta:hover,
.grv-sr-dropdown-tp2026 .grv-tp-strip-cta:hover {
  background: rgba(255, 255, 255, .06);
  color: var(--tp-on-strip);
}

/* ---------- Store badge (per-device) ----------

   On a phone the "Get it" pill becomes the store's own badge — App Store on
   iOS, Google Play on Android — since both require their supplied artwork on a
   link into their store. utils/store-link.js does the swap and adds the class;
   desktop keeps the text pill, as there is no single right store there.

   The badge carries its own frame and wordmark, so the pill drops the border,
   padding and background it needed as a text button and becomes a plain box
   around the artwork. */

/* The two badges sit as one unit at the end of the strip row, so the caption
   flexes against the pair rather than each badge in turn. */
.grv-modal-tp2026 .grv-tp-store-links,
.grv-sr-dropdown-tp2026 .grv-tp-store-links {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.grv-modal-tp2026 .grv-tp-store-link,
.grv-sr-dropdown-tp2026 .grv-tp-store-link {
  display: inline-flex;
  align-items: center;
  /* The badge artwork carries its own frame, so the link is just a box around
     it — no border, padding or background of its own. */
  border: 0;
  padding: 0;
  background: none;
  border-radius: 0;
  text-decoration: none;
  transition: opacity var(--tp-fast) var(--tp-ease);
}

.grv-modal-tp2026 .grv-tp-store-link:hover,
.grv-sr-dropdown-tp2026 .grv-tp-store-link:hover {
  background: none;
  /* The artwork is fixed, so the hover reads on opacity instead of a wash. */
  opacity: .85;
}

/* Both badges share one viewBox, so a single height sets both and the strip's
   caption keeps the same width whichever device is looking at it. */
.grv-modal-tp2026 .grv-tp-store-badge,
.grv-sr-dropdown-tp2026 .grv-tp-store-badge {
  display: block;
  /* Two badges plus the mark and caption share one narrow row, so they sit a
     little under the 34px a lone badge took. Still a 32px tap target. */
  height: 32px;
  width: auto;
}

/* Exactly the QR's problem, and the same fix. The base stylesheet paints every
   descendant SVG of an anchored trigger with `fill: ... !important` to keep the
   header icon lit above the scrim; the account menu lives inside that subtree,
   so the badge came out as a solid block — the white square the QR used to be.
   Brand artwork has to keep its own colours, so they are forced back.

   Selectors mirror the QR block above: the anchored-subtree cases plus the
   plain one, so they match its specificity, and !important beats !important at
   equal specificity by coming later in the cascade.

   Path order matches the SVG markup in utils/store-link.js — keep the two in
   step if that artwork is ever replaced. */

/* Apple: outer hairline, black body, then the white glyph (two paths). */
.grv-anchor-active .grv-tp-store-badge--apple path:nth-of-type(1),
.grv-anchor-wrap-active .grv-tp-store-badge--apple path:nth-of-type(1),
.grv-tp-store-badge--apple path:nth-of-type(1) { fill: #A6A6A6 !important; }

.grv-anchor-active .grv-tp-store-badge--apple path:nth-of-type(2),
.grv-anchor-wrap-active .grv-tp-store-badge--apple path:nth-of-type(2),
.grv-tp-store-badge--apple path:nth-of-type(2) { fill: #000000 !important; }

.grv-anchor-active .grv-tp-store-badge--apple path:nth-of-type(n+3),
.grv-anchor-wrap-active .grv-tp-store-badge--apple path:nth-of-type(n+3),
.grv-tp-store-badge--apple path:nth-of-type(n+3) { fill: #FFFFFF !important; }

/* Google Play: hairline, black body, then the four brand quadrants. */
.grv-anchor-active .grv-tp-store-badge--google path:nth-of-type(1),
.grv-anchor-wrap-active .grv-tp-store-badge--google path:nth-of-type(1),
.grv-tp-store-badge--google path:nth-of-type(1) { fill: #A6A6A6 !important; }

.grv-anchor-active .grv-tp-store-badge--google path:nth-of-type(2),
.grv-anchor-wrap-active .grv-tp-store-badge--google path:nth-of-type(2),
.grv-tp-store-badge--google path:nth-of-type(2) { fill: #000000 !important; }

.grv-anchor-active .grv-tp-store-badge--google path:nth-of-type(3),
.grv-anchor-wrap-active .grv-tp-store-badge--google path:nth-of-type(3),
.grv-tp-store-badge--google path:nth-of-type(3) { fill: #EA4335 !important; }

.grv-anchor-active .grv-tp-store-badge--google path:nth-of-type(4),
.grv-anchor-wrap-active .grv-tp-store-badge--google path:nth-of-type(4),
.grv-tp-store-badge--google path:nth-of-type(4) { fill: #FBBC04 !important; }

.grv-anchor-active .grv-tp-store-badge--google path:nth-of-type(5),
.grv-anchor-wrap-active .grv-tp-store-badge--google path:nth-of-type(5),
.grv-tp-store-badge--google path:nth-of-type(5) { fill: #4285F4 !important; }

.grv-anchor-active .grv-tp-store-badge--google path:nth-of-type(6),
.grv-anchor-wrap-active .grv-tp-store-badge--google path:nth-of-type(6),
.grv-tp-store-badge--google path:nth-of-type(6) { fill: #34A853 !important; }

/* The base also dims and repaints whole SVGs in that subtree, not just paths. */
.grv-anchor-active .grv-tp-store-badge,
.grv-anchor-active .grv-tp-store-badge *,
.grv-anchor-wrap-active .grv-tp-store-badge,
.grv-anchor-wrap-active .grv-tp-store-badge * {
  opacity: 1 !important;
}

@media (prefers-reduced-motion: reduce) {
  .grv-modal-tp2026 .grv-tp-store-link,
  .grv-sr-dropdown-tp2026 .grv-tp-store-link {
    transition: none;
  }
}

/* ---------- Legal footer (sign-in state only) ---------- */

.grv-modal-tp2026 .grv-tp-legal {
  margin: 0;
  /* The app strip follows this line and closes the panel, so the bottom value
     is the gap to that band, not the panel's final spacing. */
  padding: 12px 24px 14px;
  font: 400 11px/1.5 "DM Sans", system-ui, sans-serif;
  color: var(--tp-fg-3);
  text-align: center;
}

.grv-modal-tp2026 .grv-tp-legal a {
  color: var(--tp-fg-2);
  text-decoration: underline;
}

/* The footer belongs to state 1; JS sets data-grv-step on .grv-tp-steps. */
.grv-modal-tp2026 .grv-tp-steps:not([data-grv-step="1"]) ~ .grv-tp-legal {
  display: none;
}

/* ---------- Focus ---------- */

/* Visible ring on every control. Inputs take a white border instead, matching
   the design's own focus treatment. */
.grv-modal-tp2026 :focus-visible {
  outline: 2px solid var(--tp-paper);
  outline-offset: 2px;
}

.grv-modal-tp2026 input:focus-visible {
  outline: none;
  border-color: var(--tp-paper);
}

/* ---------- Opening style: centred floating modal ----------

   The alternative to hanging the panel off the header trigger: the same panel,
   floated in the middle of the viewport. Chosen per site on the Design tab
   (tp2026_opening = centred), which renders .grv-tp-centred onto the overlay;
   modal/core.js reads the same class and skips anchoring entirely, so no
   left/top is ever written here and no backing block is painted on the header.

   Desktop only. Everything below sits above the mobile block that follows, so
   the bottom-sheet rules still win under 991px and the two openings are
   identical on a phone.

   Only .grv-modal-anchored is absent in this mode, so the overlay falls back to
   the base stylesheet's own flex centring — these rules only need to undo the
   parts of the anchored look that assumed a header to butt against. */

/* All four corners round: nothing to sit flush with out in the middle. */
.grv-tp-overlay.grv-tp-centred > .grv-modal.grv-modal-tp2026 {
  border-radius: 20px;
  /* The anchored panel clips its slide to read as emerging from the header.
     A centred panel emerges from nowhere, so the clip would only cut the
     shadow off. */
  clip-path: none;
  box-shadow: var(--tp-shadow-sheet);
}

/* The anchored entrance slides the inner wrapper down from behind the header.
   Centred, there is nothing above to slide out of, so the panel itself scales
   up from slightly small while the scrim fades — and the wrapper holds still. */
.grv-tp-overlay.grv-tp-centred > .grv-modal.grv-modal-tp2026 {
  animation: grvTpCentredIn 300ms var(--tp-ease) both;
}

.grv-tp-overlay.grv-tp-centred > .grv-modal.grv-modal-tp2026 > .grv-tp-slide,
.grv-tp-overlay.grv-tp-centred > .grv-modal.grv-modal-tp2026.grv-tp-closing > .grv-tp-slide {
  animation: none;
}

.grv-tp-overlay.grv-tp-centred > .grv-modal.grv-modal-tp2026.grv-tp-closing {
  animation: grvTpCentredOut 200ms var(--tp-ease) both;
}

@keyframes grvTpCentredIn {
  from { opacity: 0; transform: scale(0.96); }
  to   { opacity: 1; transform: scale(1); }
}

@keyframes grvTpCentredOut {
  from { opacity: 1; transform: scale(1); }
  to   { opacity: 0; transform: scale(0.96); }
}

@media (prefers-reduced-motion: reduce) {
  .grv-tp-overlay.grv-tp-centred > .grv-modal.grv-modal-tp2026,
  .grv-tp-overlay.grv-tp-centred > .grv-modal.grv-modal-tp2026.grv-tp-closing {
    animation: none;
  }
}

/* ---------- Mobile: bottom sheet ---------- */

@media (max-width: 991px) {
  .grv-tp-overlay,
  .grv-modal-overlay.grv-modal-anchored.grv-tp-overlay {
    display: flex;
    align-items: flex-end;
    justify-content: center;
    padding: 0;
  }

  /* JS-written anchor offsets are irrelevant to a bottom sheet. */
  .grv-modal-overlay.grv-modal-anchored.grv-tp-overlay > .grv-modal.grv-modal-tp2026 {
    position: static;
    inset: auto;
    left: auto;
    top: auto;
    /* Rounded at the top where the sheet meets the page, square along the
       bottom where it meets the screen edge. Repeated here because the
       unmediated anchored rule above outranks the sheet rule below. */
    border-radius: 20px 20px 0 0;
    /* Capped at the panel width and centred: full width only once the screen
       is narrower than the cap, which is the 400px-and-below case. */
    width: 100%;
    max-width: 420px;
    box-shadow: var(--tp-shadow-sheet);
    animation: grvTpSheetUp 300ms var(--tp-ease) both;
    /* No self-clipping on a sheet: it slides as a whole, and the clip would
       cut the shadow along its top edge. */
    clip-path: none;
  }

  /* The sheet itself slides on mobile, so the inner wrapper stays still and
     just carries the flex column that pins the strip to the bottom edge. */
  .grv-modal-tp2026 > .grv-tp-slide,
  .grv-modal-tp2026.grv-tp-closing > .grv-tp-slide {
    animation: none;
    display: flex;
    flex-direction: column;
    min-height: 0;
    flex: 1;
  }

  /* Sheets slide back down to the edge they came from. */
  .grv-modal-overlay.grv-modal-anchored.grv-tp-overlay > .grv-modal.grv-modal-tp2026.grv-tp-closing,
  .grv-modal-overlay.grv-modal-anchored > .grv-modal.grv-modal-tp2026.grv-tp-closing,
  .grv-modal.grv-modal-tp2026.grv-tp-closing {
    animation: grvTpSheetDownOut 200ms var(--tp-ease) both;
  }

  .grv-modal.grv-modal-tp2026 {
    width: 100%;
    max-width: 420px;
    border-radius: 20px 20px 0 0;
    box-shadow: var(--tp-shadow-sheet);
    animation: grvTpSheetUp 300ms var(--tp-ease) both;
    /* The strip stays pinned to the sheet's bottom edge while the content
       above it scrolls. */
    display: flex;
    flex-direction: column;
    /* dvh, not vh: vh is the tallest-possible viewport, so with the browser's
       address bar showing, 92vh is more than the screen actually offers and
       the sheet is cut off at the bottom with no way to reach the rest.
       dvh tracks the space really available. vh stays as the fallback for
       engines without dvh. */
    max-height: 92vh;
    max-height: 92dvh;
  }

  .grv-modal-tp2026 .grv-tp-steps {
    flex: 1;
    min-height: 0;
    max-height: none;
  }

  /* The sheet itself is moving, so the per-state rise would double up. The
     states cross-fade in place instead. */
  .grv-modal-tp2026 .grv-tp-view:not([hidden]) {
    animation: grvTpSheetFade 250ms var(--tp-ease);
  }

  .grv-modal-tp2026 .grv-tp-view { padding: 28px 20px 22px; }
  .grv-modal-tp2026 .grv-tp-menu { padding: 22px 20px 18px; }

  .grv-modal-tp2026 .grv-tp-strip,
.grv-sr-dropdown-tp2026 .grv-tp-strip {
    padding: 14px 20px;
    /* Clear of the home-indicator area on iOS. */
    padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  }

  .grv-modal-tp2026 .grv-tp-strip-desktop,
.grv-sr-dropdown-tp2026 .grv-tp-strip-desktop { display: none; }

  .grv-modal-tp2026 .grv-tp-strip-mobile,
.grv-sr-dropdown-tp2026 .grv-tp-strip-mobile {
    display: flex;
    align-items: center;
    gap: 10px;
  }

  .grv-modal-tp2026 .grv-tp-strip-mobile .grv-tp-strip-caption,
.grv-sr-dropdown-tp2026 .grv-tp-strip-mobile .grv-tp-strip-caption {
    flex: 1;
    min-width: 0;
    margin-top: 0;
    font: 500 13px/1.35 "DM Sans", system-ui, sans-serif;
    /* Pure white: this caption is the band's call to action, not a subtitle.
       Set here rather than only in the shared rule above, since this block is
       the later one at equal specificity and so decides the colour. */
    color: #FFFFFF;
  }

  /* The `font:` shorthand above resets weight for everything inside, so the
     wordmark's bold half restates it here — same later-rule-wins reason as
     the colour. */
  .grv-modal-tp2026 .grv-tp-strip-mobile .grv-tp-strip-caption strong,
.grv-sr-dropdown-tp2026 .grv-tp-strip-mobile .grv-tp-strip-caption strong {
    font-weight: 700;
    color: #FFFFFF;
  }


  .grv-modal-tp2026 .grv-tp-legal { padding: 12px 20px 18px; }
}

/* ==========================================================================
   Account menu (design state 4)

   The signed-in menu is rendered by ui/notification-bell.js as .grv-sr-*
   markup, not by the modal — it predates this design and carries the
   notification polling, tier data and mark-all-read behaviour. Rather than
   duplicate that in the panel, it is restyled here to the new design and the
   panel's own state 4 stays a fallback.

   Scoped to .grv-sr-dropdown-tp2026, which the component only emits when
   designVersion is tp2026, so the older designs keep their existing menu.
   ========================================================================== */

/* The account menu's own close, matching the sign-in panel's. It sits outside
   .grv-sr-dropdown-inner in the markup, because that element is rebuilt on
   every view switch and would take the button with it. */
/* The close is the same component as the login panel's — same markup, same
   classes, styled by the shared .grv-tp-close rule above. Only the stacking
   index differs, since this panel has the app strip in its own context. */
.grv-sr-dropdown-tp2026 .grv-sr-close {
  z-index: 3;
}


.grv-sr-dropdown.grv-sr-dropdown-tp2026 {
  /* Tokens come from the shared declaration at the top of this file, which the
     light-mode block also targets — redeclaring them here would sit later in
     the cascade at equal specificity and lock the menu to dark. */
  /* The base sets color: rgb(var(--grv-text)) here — the publisher theme's
     text colour. On a light-mode site that is near-black, which anything
     using currentColor then inherits. Pinned to the panel's own token. */
  color: var(--tp-paper);
  width: 420px;
  max-width: calc(100vw - 24px);
  background: var(--tp-surface-1);
  /* Square top edge meets the header, as the login panel does. */
  border-radius: 0 0 20px 20px;
  /* No shadow: `none` rather than omitting the property, since the base
     stylesheet sets one on .grv-sr-dropdown that would otherwise apply. */
  box-shadow: none;
  /* Replaces the base's grv-sr-pop-in, which scales from the top-right. The
     slide itself runs on .grv-sr-dropdown-inner so this box can clip it. */
  animation: none;
  transform-origin: top center;
  border: 0;
  overflow: hidden;
  font-family: "DM Sans", system-ui, sans-serif;
}

.grv-sr-dropdown-tp2026 .grv-sr-dropdown-inner {
  padding: 22px 20px 18px;
  background: transparent;
}

/* ---------- Identity ---------- */

.grv-sr-dropdown-tp2026 .grv-sr-id-name {
  font: 700 20px/1.2 "DM Sans", system-ui, sans-serif;
  color: var(--tp-paper);
}

.grv-sr-dropdown-tp2026 .grv-sr-id-handle {
  font: 400 14px/1.3 "DM Sans", system-ui, sans-serif;
  color: var(--tp-fg-2);
  margin-top: 2px;
}

/* The avatar is requested at 52px by the component in this design, so only
   the row gap needs setting here. */
.grv-sr-dropdown-tp2026 .grv-sr-identity { gap: 12px; }

/* Tier badge, bottom-right of the avatar. The tier artwork is already a
   coloured hexagon, so it stands alone — no disc behind it. The base rule
   makes this a 28px white circle with a drop shadow and a grid centre, all of
   which has to be cleared, or the badge reads as a sticker on a white dot. */
.grv-sr-dropdown-tp2026 .grv-sr-tier-badge-dot {
  width: 26px;
  height: 26px;
  bottom: -5px;
  right: -5px;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
  box-sizing: border-box;
  overflow: visible;
  /* place-items from the base rule stays fine; the child fills the box. */
}

.grv-sr-dropdown-tp2026 .grv-sr-tier-badge-dot img,
.grv-sr-dropdown-tp2026 .grv-sr-tier-badge-dot svg {
  width: 26px !important;
  height: 26px !important;
  object-fit: contain;
  display: block;
}

/* Emoji/text fallback when the tier has no artwork: it has no shape of its
   own, so it keeps a disc to sit on. */
.grv-sr-dropdown-tp2026 .grv-sr-tier-badge-dot[data-glyph] {
  width: 22px;
  height: 22px;
  bottom: -4px;
  right: -4px;
  border-radius: 50%;
  border: 2px solid var(--tp-surface-1);
  background: var(--tp-bronze);
}

.grv-sr-dropdown-tp2026 .grv-sr-tier-badge-dot[data-tier="silver"][data-glyph] { background: #AFB1B6; }
.grv-sr-dropdown-tp2026 .grv-sr-tier-badge-dot[data-tier="gold"][data-glyph] { background: #F2B100; }

.grv-sr-dropdown-tp2026 .grv-sr-tier-badge-dot span {
  font-size: 11px !important;
  line-height: 1;
}

/* ---------- Notification card ---------- */

/* The base rule paints a red gradient (#C8102E -> #A30D26) and lifts the card
   on hover with a transform. The design wants the flat darkened live red, and
   forbids transform on press, so both are replaced rather than tweaked.
   background-image is cleared explicitly: the shorthand alone would leave the
   gradient in place under some cascade orders. */
.grv-sr-dropdown-tp2026 .grv-sr-notif-cta {
  background: var(--tp-notif);
  background-image: none;
  border: 0;
  border-radius: 12px;
  padding: 12px 14px;
  gap: 12px;
  /* Red ground in both themes, so the foreground must not follow the theme. */
  color: var(--tp-on-accent);
  transition: opacity 250ms var(--tp-ease);
}

.grv-sr-dropdown-tp2026 .grv-sr-notif-cta:hover {
  opacity: .92;
  transform: none;
  box-shadow: none;
}

.grv-sr-dropdown-tp2026 .grv-sr-notif-cta-tile {
  width: 40px;
  height: 40px;
  border-radius: 12px;
  /* Always a light wash: it sits on the red card, not on the panel. */
  background: rgba(255, 255, 255, .16);
  flex: none;
  color: var(--tp-on-accent);
}

/* The bell glyph and the chevron both sit on the red card, so both stay white
   in either theme.
 *
 * !important because publisher themes set their own fill on descendant SVG
 * paths — usually rgb(var(--grv-text)), which is near-black on a light-mode
 * site — and this card's ground is red in both themes, so following the
 * theme's ink turns the bell black on red. Same reason the QR modules and the
 * store badges are pinned; the base stylesheet does it too (.grv-sr-action).
 * The wrapper cases cover the anchored subtree, where the theme's rules are
 * scoped to the lit trigger. */
.grv-sr-dropdown-tp2026 .grv-sr-notif-cta svg,
.grv-sr-dropdown-tp2026 .grv-sr-notif-cta svg *,
.grv-anchor-active .grv-sr-notif-cta svg,
.grv-anchor-active .grv-sr-notif-cta svg *,
.grv-anchor-wrap-active .grv-sr-notif-cta svg,
.grv-anchor-wrap-active .grv-sr-notif-cta svg * {
  fill: var(--tp-on-accent, #FFFFFF) !important;
  opacity: 1 !important;
}

/* The tile's own colour feeds anything using currentColor inside it. */
.grv-sr-dropdown-tp2026 .grv-sr-notif-cta-tile,
.grv-anchor-wrap-active .grv-sr-notif-cta-tile {
  color: var(--tp-on-accent, #FFFFFF);
}

.grv-sr-dropdown-tp2026 .grv-tp-strip {
  /* The strip is the menu's last band and must reach the panel edges, so it
     cancels the dropdown's own inner padding: pulled out to both sides and
     down over the bottom padding. The top margin is the gap to the divider. */
  margin: 18px -20px -22px;
  /* Its bottom edge is the panel's own, so the hairline goes on top, where it
     separates the band from the actions above it. */
  border-top: 1px solid var(--tp-strip-outline);
  border-bottom: 0;
}

.grv-sr-dropdown-tp2026 .grv-sr-notif-cta-primary {
  font: 700 15px/1.3 "DM Sans", system-ui, sans-serif;
  color: var(--tp-on-accent);
}

.grv-sr-dropdown-tp2026 .grv-sr-notif-cta-secondary {
  font: 400 13px/1.35 "DM Sans", system-ui, sans-serif;
  color: var(--tp-on-notif);
}

/* ---------- Rows ---------- */

/* The design stacks these full width; the older menu put Profile and Log Out
   side by side on one row. */
.grv-sr-dropdown-tp2026 .grv-sr-actions {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  gap: 0;
  padding: 8px 0 0;
}

.grv-sr-dropdown-tp2026 .grv-sr-action,
.grv-sr-dropdown-tp2026 .grv-sr-action:link,
.grv-sr-dropdown-tp2026 .grv-sr-action:visited {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  flex: 0 0 auto;
  box-sizing: border-box;
  padding: 13px 14px;
  border-radius: 12px;
  font: 600 15px/1 "DM Sans", system-ui, sans-serif;
  letter-spacing: normal;
  color: var(--tp-paper) !important;
  transition: background-color 250ms var(--tp-ease);
}

.grv-sr-dropdown-tp2026 .grv-sr-action:hover {
  background: var(--tp-wash-soft);
  opacity: 1;
}

.grv-sr-dropdown-tp2026 .grv-sr-action svg { width: 21px; height: 21px; }

.grv-sr-dropdown-tp2026 .grv-sr-action svg path {
  fill: var(--tp-fg-2) !important;
}

/* "Profile" becomes "Profile & interests" in this design. */
.grv-sr-dropdown-tp2026 .grv-sr-action-label { display: none; }
.grv-sr-dropdown-tp2026 .grv-sr-action-label-long { display: inline; }
.grv-sr-dropdown-tp2026 .grv-sr-action--logout .grv-sr-action-label {
  display: inline;
}

/* Deliberately de-emphasised — it was too prominent before. */
.grv-sr-dropdown-tp2026 .grv-sr-action--logout,
.grv-sr-dropdown-tp2026 .grv-sr-action--logout:link,
.grv-sr-dropdown-tp2026 .grv-sr-action--logout:visited {
  font: 600 15px/1 "DM Sans", system-ui, sans-serif;
  gap: 12px;
  color: var(--tp-fg-2) !important;
}

.grv-sr-dropdown-tp2026 .grv-sr-action--logout svg { width: 21px; height: 21px; }

/* ---------- Footer ---------- */

/* The design ends the menu on the app strip, so the old rule/brandbar pair is
   replaced by the strip's black band. */
.grv-sr-dropdown-tp2026 .grv-sr-divider { display: none; }



/* ---------- Anchor block: joining trigger to panel ----------

   The base paints the block behind the avatar with the publisher theme's body
   colour (rgb(var(--grv-body))), which on this design leaves a black rectangle
   sitting against the panel's #1C1C1E rather than continuing it. Repainted with
   the panel's own surface token so the avatar and the panel below it read as one
   container. Scoped to the wrapper that holds a tp2026 menu so the older
   designs keep the theme-coloured block.
   ========================================================================== */

/* The block must match whatever the panel leads with directly beneath it. The
   app strip moved to the panel's bottom edge, so what the block now meets is
   the panel's own surface — white in light mode, #1C1C1E in dark. Following
   --tp-surface-1 keeps the join in both themes; it was pinned to the strip's
   black while that band led the panel.

   Two selectors, one rule. The signed-in menu is a descendant of the wrapper,
   so :has() finds it. The signed-out login panel is not — it is mounted in
   #grv-modal-overlay, outside this wrapper entirely — so nothing in CSS can
   see it from here and modal/core.js sets .grv-anchor-wrap-tp2026 on the
   wrapper instead. Without that second selector the login panel's block kept
   the base stylesheet's rgb(var(--grv-body)), the publisher theme's body
   colour, which sits a shade off --tp-surface-1 and reads as a seam. */
.grv-anchor-wrap-active:has(.grv-sr-dropdown-tp2026)::before,
.grv-anchor-wrap-active.grv-anchor-wrap-tp2026::before {
  background: var(--tp-surface-1, #FFFFFF);
}

/* The panel's own token, restated because the wrapper sits outside the panel
   and so inherits none of the panel's tokens. Both themes are given, since the
   surface is no longer one colour in both. */
.grv-anchor-wrap-active:has(.grv-sr-dropdown-tp2026),
.grv-anchor-wrap-active.grv-anchor-wrap-tp2026 {
  --tp-surface-1: #FFFFFF;
}

@media (prefers-color-scheme: dark) {
  .grv-anchor-wrap-active:has(.grv-sr-dropdown-tp2026),
  .grv-anchor-wrap-active.grv-anchor-wrap-tp2026 {
    --tp-surface-1: #1C1C1E;
  }
}

/* The avatar sits on that panel-coloured block once the menu is open, so the
   glyph follows the panel's own foreground rather than the strip's. */
.grv-anchor-wrap-active:has(.grv-sr-dropdown-tp2026) .grv-sr-avatar-btn {
  color: var(--tp-paper, #000000);
}

/* The avatar sits immediately after the header's social icons, and the shared
   10px wrapper padding leaves it almost touching them. Widened on the leading
   side only: the trailing side is the screen edge, and padding there would
   pull the avatar in off it.

   Set unconditionally rather than under :has() — that would only match while
   the panel is mounted, so the avatar would visibly shift sideways as it
   opened. On the wrapper rather than as a margin, so the block behind the
   avatar grows with it: the geometry helper measures the wrapper, and a margin
   would put the gap outside the block and leave the seam where it was. */
.header-user .grv-sr-wrapper {
  padding-left: 20px;
}

/* ---------- Notifications view (in-panel) ----------

   Clicking the notifications card swaps this in over the menu rather than
   opening a second panel, so the reader stays in the surface they opened. The
   base stylesheet styles these rows for a light card; every colour is remapped
   to the panel's own tokens here, and the rows run edge to edge, which means
   cancelling the inner wrapper's horizontal padding for this view only.
   ========================================================================== */

.grv-sr-dropdown-tp2026 .grv-sr-view--notifs {
  /* Rows bleed to the panel edges; the inner wrapper's padding is pulled back
     out rather than removed, so the main view keeps it. */
  margin: -22px -20px -18px;
}

.grv-sr-dropdown-tp2026 .grv-sr-notif-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 16px 16px 14px;
  border-bottom: 1px solid var(--tp-outline);
  /* Stays put while the list below it scrolls. */
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--tp-surface-1);
}

.grv-sr-dropdown-tp2026 .grv-sr-notif-title {
  flex: 1;
  font: 700 17px/1.2 "DM Sans", system-ui, sans-serif;
  color: var(--tp-paper);
}

.grv-sr-dropdown-tp2026 .grv-sr-notif-markall {
  color: var(--tp-fg-2);
  font-family: "DM Sans", system-ui, sans-serif;
}

.grv-sr-dropdown-tp2026 .grv-sr-notif-markall:hover {
  background: var(--tp-wash-soft);
  color: var(--tp-paper);
}

.grv-sr-dropdown-tp2026 .grv-sr-notif-deleteall {
  color: var(--tp-fg-3);
}

.grv-sr-dropdown-tp2026 .grv-sr-notif-deleteall:hover {
  background: rgba(229, 57, 53, .12);
  color: var(--tp-live);
}

.grv-sr-dropdown-tp2026 .grv-sr-notif-deleteall svg,
.grv-sr-dropdown-tp2026 .grv-sr-notif-deleteall svg path,
.grv-sr-dropdown-tp2026 .grv-sr-notif-delete svg,
.grv-sr-dropdown-tp2026 .grv-sr-notif-delete svg path {
  fill: currentColor;
}

/* On the desktop panel the list is capped so the menu stays a dropdown; the
   mobile block below lets it run the sheet's full height instead. */
.grv-sr-dropdown-tp2026 .grv-sr-notif-list {
  max-height: min(420px, 60vh);
}

.grv-sr-dropdown-tp2026 .grv-sr-notif-row-wrap,
.grv-sr-dropdown-tp2026 .grv-sr-notif-row {
  border-bottom-color: var(--tp-outline);
}

.grv-sr-dropdown-tp2026 .grv-sr-notif-row:hover {
  background: var(--tp-wash-soft);
}

/* The base tints unread rows blue. The design's accent is the live red, and
   the tint has to read on a near-black panel rather than white. */
.grv-sr-dropdown-tp2026 .grv-sr-notif-row--unread {
  background: rgba(229, 57, 53, .09);
}

.grv-sr-dropdown-tp2026 .grv-sr-notif-row--unread:hover {
  background: rgba(229, 57, 53, .15);
}

.grv-sr-dropdown-tp2026 .grv-sr-notif-unread-dot {
  background: var(--tp-live);
}

.grv-sr-dropdown-tp2026 .grv-sr-notif-icon-tile {
  background: var(--tp-surface-2);
}

/* Per-kind colour is written inline on the tile, so only the glyph is told to
   follow it — the base's fill rules would otherwise flatten it. */
.grv-sr-dropdown-tp2026 .grv-sr-notif-icon-tile svg,
.grv-sr-dropdown-tp2026 .grv-sr-notif-icon-tile svg path {
  fill: currentColor;
}

.grv-sr-dropdown-tp2026 .grv-sr-notif-msg {
  color: var(--tp-paper);
  font-family: "DM Sans", system-ui, sans-serif;
}

.grv-sr-dropdown-tp2026 .grv-sr-notif-text { color: var(--tp-fg-2); }
.grv-sr-dropdown-tp2026 .grv-sr-notif-time { color: var(--tp-fg-3); }

.grv-sr-dropdown-tp2026 .grv-sr-notif-empty { color: var(--tp-fg-2); }

.grv-sr-dropdown-tp2026 .grv-sr-notif-empty-title {
  color: var(--tp-paper);
  font-family: "DM Sans", system-ui, sans-serif;
}

.grv-sr-dropdown-tp2026 .grv-sr-notif-empty-sub { color: var(--tp-fg-2); }

.grv-sr-dropdown-tp2026 .grv-sr-notif-empty svg,
.grv-sr-dropdown-tp2026 .grv-sr-notif-empty svg path {
  fill: var(--tp-paper);
}

.grv-sr-dropdown-tp2026 .grv-sr-notif-retry {
  border-color: var(--tp-outline);
  color: var(--tp-paper);
  font-family: "DM Sans", system-ui, sans-serif;
}

.grv-sr-dropdown-tp2026 .grv-sr-notif-retry:hover {
  background: var(--tp-wash-soft);
}

/* The base's shimmer is built from light greys, invisible on a dark panel. */
.grv-sr-dropdown-tp2026 .grv-sr-skeleton-tile,
.grv-sr-dropdown-tp2026 .grv-sr-skeleton-bar {
  background: linear-gradient(90deg,
    var(--tp-surface-2) 25%, var(--tp-wash-hard) 50%, var(--tp-surface-2) 75%);
  background-size: 200% 100%;
}

/* Touch devices never hover, so the per-row delete would stay invisible. */
@media (hover: none) {
  .grv-sr-dropdown-tp2026 .grv-sr-notif-delete { opacity: 1; }
}

/* Both views put content in the close button's corner — the notifications
   header its mark-all/delete-all actions, the app strip its store badges — so
   each reserves the button's width on its trailing edge.

   Placed after every unmediated `padding` shorthand for these two elements:
   the shorthands reset padding-right, so an earlier longhand would be wiped.
   The mobile blocks below re-shorthand the strip and are handled there. */
/* The panel's nav button is absolutely positioned at the top-left, so the
   header's own content starts clear of it. Was padding-right, from when this
   view carried its own back button on the left and the close sat right. */
.grv-sr-dropdown-tp2026 .grv-sr-notif-header { padding-left: 52px; }
.grv-sr-dropdown-tp2026 .grv-sr-view--main .grv-tp-strip { padding-right: 52px; }

/* ---------- Account menu: full-height side panel ----------

   The alternative to the avatar dropdown, chosen per site on the Design tab
   (tp2026_menu_style = side). notification-bell.js puts .grv-sr-dropdown--side
   on the panel and skips anchoring entirely, so nothing writes left/top here
   and no block is drawn behind the avatar.

   Unlike the mobile sheet below — which hangs from the header's bottom edge —
   this runs the full height of the viewport and covers the header's right end.
   That is the point of it, so the avatar underneath is deliberately not joined
   to the panel.

   Desktop only: the mobile block that follows re-pins the panel under the
   header, so below 991px both settings give the same side sheet as today.

   The unmediated `.grv-sr-dropdown.grv-sr-dropdown-tp2026` rule above sets
   width/border-radius/overflow for the dropdown; each is restated rather than
   assumed, since this selector adds only one class of specificity over it. */

/* Wrapped in min-width rather than left unmediated: these selectors carry
   one more class than the mobile sheet rules below, so without this they
   would outrank them at every width — a media query adds no specificity of
   its own. Above the breakpoint only; below it the sheet rules stand alone. */
@media (min-width: 992px) {

  .grv-sr-dropdown.grv-sr-dropdown-tp2026.grv-sr-dropdown--side {
    /* CRITICAL: no transform or animation on this element. A transform makes it
       the containing block for its own `position: fixed`, which then resolves
       against the header trigger it is nested in rather than the viewport, and
       the panel snaps back to the top-right corner. The slide runs on the inner
       element instead — the same trap the mobile sheet documents below. */
    animation: none;
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: auto;
    width: 100%;
    max-width: 420px;
    margin: 0;
    /* Flush to the top, right and bottom viewport edges; only the leading edge
       sits over the page, so only its corners round. */
    border-radius: 20px 0 0 20px;
    box-shadow: var(--tp-shadow-side);
    max-height: none;
    /* Above the site header, which this panel covers rather than hangs from.
       The login overlay's own z-index is unaffected — the two never coexist. */
    z-index: 10000;
    /* `clip` rather than `hidden`: hidden makes this a scroll container, and a
       scroll container pans to reach an out-of-flow child — so the panel would
       slide sideways chasing the inner element's translateX(100%) start and snap
       back on landing. clip never scrolls. Scrolling moves to the inner element. */
    overflow: clip;
    display: flex;
    flex-direction: column;
  }

  /* The inner element carries the travel, so the panel's fixed positioning
     survives — and the scrolling, so the panel itself stays a clip box. */
  .grv-sr-dropdown.grv-sr-dropdown-tp2026.grv-sr-dropdown--side > .grv-sr-dropdown-inner {
    animation: grvTpSideIn var(--tp-dur-side-in) var(--tp-ease-side) both;
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    /* clip across, for the same reason as the panel: the strip's negative side
       margins reach outside this box and a scrollable axis would pan to them. */
    overflow-x: clip;
    overscroll-behavior: contain;
    /* A transformed scroll container repaints entirely each frame; promoting it
       keeps the slide on the compositor. */
    will-change: transform;
  }

  .grv-sr-dropdown.grv-sr-dropdown-tp2026.grv-sr-dropdown--side.grv-tp-closing > .grv-sr-dropdown-inner {
    animation: grvTpSideOut var(--tp-dur-side-out) var(--tp-ease-side) both;
  }

  .grv-sr-dropdown.grv-sr-dropdown-tp2026.grv-sr-dropdown--side.grv-tp-closing {
    animation: none;
  }

  /* Both views fill the panel, so the app strip lands on the bottom edge rather
     than floating under the last row. */
  .grv-sr-dropdown-tp2026.grv-sr-dropdown--side .grv-sr-view {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
  }

  /* Notifications fill the panel: header pinned, list taking the rest and
     scrolling on its own so the header cannot slide away. */
  .grv-sr-dropdown-tp2026.grv-sr-dropdown--side .grv-sr-view--notifs {
    overflow: hidden;
  }

  .grv-sr-dropdown-tp2026.grv-sr-dropdown--side .grv-sr-notif-list {
    flex: 1;
    min-height: 0;
    max-height: none;
  }

  /* Two nested scrollers would let the sticky header drift, so the wrapper does
     not scroll behind the list. */
  .grv-sr-dropdown-tp2026.grv-sr-dropdown--side > .grv-sr-dropdown-inner:has(.grv-sr-view--notifs) {
    overflow-y: hidden;
  }

  /* The strip leads the bottom edge here, and must not stretch or a long menu
     would grow it. */
  .grv-sr-dropdown-tp2026.grv-sr-dropdown--side .grv-tp-strip {
    flex: none;
  }

  @media (prefers-reduced-motion: reduce) {
    .grv-sr-dropdown.grv-sr-dropdown-tp2026.grv-sr-dropdown--side > .grv-sr-dropdown-inner,
    .grv-sr-dropdown.grv-sr-dropdown-tp2026.grv-sr-dropdown--side.grv-tp-closing > .grv-sr-dropdown-inner {
      animation: none;
    }
  }

}

/* ---------- Mobile: side sheet ---------- */

@media (max-width: 991px) {
  .grv-sr-dropdown.grv-sr-dropdown-tp2026 {
    /* Side sheet pinned to the right edge, hanging from under the header the
       avatar sits in — so the panel reads as coming out of the trigger rather
       than up off the bottom of the screen. The base hangs this off the avatar
       (position: absolute; top: 100%; right: 0), which is what needs undoing.
       CRITICAL: no transform/animation on this element. A transform makes it
       the containing block for its own `position: fixed`, which then resolves
       against the header trigger instead of the viewport — the sheet snaps
       back to the top-right corner. The slide runs on the inner element. */
    animation: none;
    position: fixed;
    /* JS writes the top offset from the header's own geometry; the fallback
       covers the case where the anchor geometry helper is unavailable. */
    top: var(--grv-sr-sheet-top, 0px);
    right: 0;
    bottom: 0;
    left: auto;
    width: 100%;
    max-width: 420px;
    margin: 0;
    /* Square along the top, where the panel meets the header, and along the
       right and bottom, where it meets the screen edges. Only the leading
       corner is rounded — the one edge that sits over the page. */
    border-radius: 0 0 0 20px;
    box-shadow: none;
    max-height: none;
    /* `clip` rather than `hidden`: hidden makes this a scroll container on
       both axes even when only one is scrollable, and a scroll container is
       scrolled to reach an out-of-flow child — so the sheet would pan sideways
       to follow the inner element's translateX(100%) start position and then
       snap back as it landed. clip never scrolls, so the travel is purely the
       transform. The scrolling moves to the inner element below. */
    overflow: clip;
    /* Full-height sheet: the flex column lets the inner element fill it. */
    display: flex;
    flex-direction: column;
  }

  /* The inner element carries the travel, so the sheet's fixed positioning
     stays intact — and now the scrolling too, so the sheet itself can stay a
     non-scrolling clip box. */
  .grv-sr-dropdown.grv-sr-dropdown-tp2026 > .grv-sr-dropdown-inner {
    animation: grvTpSideIn var(--tp-dur-side-in) var(--tp-ease-side) both;
    /* Fills the sheet so its own column can lay the views out. */
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    /* clip on the horizontal axis for the same reason as the sheet: the
       strip's negative side margins reach outside this box, and a scrollable
       axis would let the container pan to them. */
    overflow-y: auto;
    overflow-x: clip;
    overscroll-behavior: contain;
    /* A transformed scroll container repaints its whole surface every frame;
       promoting it keeps the slide on the compositor. */
    will-change: transform;
  }

  .grv-sr-dropdown.grv-sr-dropdown-tp2026.grv-tp-closing > .grv-sr-dropdown-inner {
    animation: grvTpSideOut var(--tp-dur-side-out) var(--tp-ease-side) both;
  }

  .grv-sr-dropdown.grv-sr-dropdown-tp2026.grv-tp-closing {
    animation: none;
  }

  /* Both views fill the sheet's height, so the strip lands on the bottom edge
     rather than floating under the last row. */
  .grv-sr-dropdown-tp2026 .grv-sr-view {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
  }

  /* The notifications view fills the sheet: its header pins to the top, the
     list takes the rest and scrolls on its own rather than inside the sheet,
     so the header cannot slide away. */
  .grv-sr-dropdown-tp2026 .grv-sr-view--notifs {
    overflow: hidden;
  }

  .grv-sr-dropdown-tp2026 .grv-sr-notif-list {
    flex: 1;
    min-height: 0;
    max-height: none;
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }

  /* The notification list scrolls on its own, so the wrapper around it must
     not scroll behind it — two nested scrollers would let the sticky header
     drift out of view. */
  .grv-sr-dropdown-tp2026 > .grv-sr-dropdown-inner:has(.grv-sr-view--notifs) {
    overflow-y: hidden;
  }

  /* The band closes the sheet, so it carries the home-indicator padding again
     — and it must not stretch, or a long menu would grow it. */
  .grv-sr-dropdown-tp2026 .grv-tp-strip {
    padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px));
    flex: none;
  }

}

/* ==========================================================================
   Short viewports — the sheet compresses instead of clipping

   Every vertical dimension in the sign-in stack is a fixed pixel value: view
   padding, 52px provider buttons, the 18px/14px divider margins, the app strip
   and the legal footer. Together they need more height than a landscape phone
   or a small split-screen window has, and since nothing in the stack yields,
   the content simply overran the sheet's bottom edge.

   These blocks tighten the stack in two steps as height runs out, so the panel
   adapts to the screen rather than staying at its comfortable size and being
   cut off. Sizes shrink in proportion — the tap targets stay at or above 44px
   until the last step, where the panel is scrolling anyway.

   Height-based, not width-based: a short landscape phone and a short desktop
   window have the same problem, and neither is described by a width.
   ========================================================================== */

@media (max-width: 991px) and (max-height: 700px) {
  /* Reclaims ~30px from the paddings at each end of the sheet. */
  .grv-modal-tp2026 .grv-tp-view { padding: 18px 20px 14px; }
  .grv-modal-tp2026 .grv-tp-menu { padding: 14px 20px 12px; }
  .grv-modal-tp2026 .grv-tp-legal { padding: 8px 20px 12px; }

  /* The title block loses its most generous leading first — type stays at the
     same size, so nothing gets harder to read. */
  .grv-modal-tp2026 .grv-tp-title { font-size: 20px; }
  .grv-modal-tp2026 .grv-tp-sub { margin-top: 4px; }

  /* ~14px out of the gaps around the provider pair and the divider. */
  .grv-modal-tp2026 .grv-tp-providers { margin-top: 12px; }
  .grv-modal-tp2026 .grv-tp-provider-slide { gap: 8px; }
  .grv-modal-tp2026 .grv-tp-divider { margin: 12px 0 10px; }
  .grv-modal-tp2026 .grv-tp-more-toggle { margin-top: 8px; padding: 6px; }

  /* Still a 48px target — comfortably tappable. */
  .grv-modal-tp2026 .grv-tp-btn { height: 48px; }

  /* The app band is the least essential row on a cramped screen, so it gives
     up its padding before anything above it does. */
  .grv-modal-tp2026 .grv-tp-strip,
  .grv-sr-dropdown-tp2026 .grv-tp-strip {
    padding: 10px 20px;
    padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
  }
}

@media (max-width: 991px) and (max-height: 560px) {
  /* Below this the sheet takes the whole viewport: leaving a gap above it
     would cost height the content needs, and there is no page left to see
     behind it anyway. Square top corners follow from meeting the top edge. */
  .grv-modal.grv-modal-tp2026,
  .grv-modal-overlay.grv-modal-anchored.grv-tp-overlay > .grv-modal.grv-modal-tp2026 {
    max-height: 100vh;
    max-height: 100dvh;
    border-radius: 0;
  }

  .grv-modal-tp2026 .grv-tp-view { padding: 14px 18px 10px; }
  .grv-modal-tp2026 .grv-tp-title { font-size: 18px; }

  /* The subline and the email hint are the two lines the panel can lose
     without losing a function — the buttons and the field say what they do. */
  .grv-modal-tp2026 .grv-tp-sub,
  .grv-modal-tp2026 .grv-tp-help { display: none; }

  .grv-modal-tp2026 .grv-tp-providers { margin-top: 10px; }
  .grv-modal-tp2026 .grv-tp-divider { margin: 10px 0 8px; }
  .grv-modal-tp2026 .grv-tp-btn { height: 44px; font-size: 15px; }

  /* The app band is a cross-sell, not a step in signing in; at this height the
     rows that are keep its space. Its pill is duplicated by the app banner. */
  .grv-modal-tp2026 .grv-tp-strip { display: none; }

  /* Last to go, since it is the panel's only legal notice — it survives as one
     tighter line rather than being dropped. */
  .grv-modal-tp2026 .grv-tp-legal { padding: 6px 18px 8px; font-size: 10px; }
}

/* ==========================================================================
   Narrow screens — the short button label

   Below this width the provider buttons drop the brand words and lead with the
   verb, so "Continue with Facebook" becomes "Sign in [f]" and the mark carries
   the naming. The full label still fits here on paper; the swap is about the
   button not reading as a cramped sentence once the panel is at its narrowest,
   and it buys back the room the longer brands (Facebook, Google) would take.

   Width-only, deliberately: this is about the button's horizontal space, so it
   applies whatever the height is — and it stacks with the short-height tiers
   above, which handle the vertical squeeze independently.
   ========================================================================== */

@media (max-width: 400px) {
  /* Verb swap: the short form appears, the long one goes. */
  .grv-modal-tp2026 .grv-tp-verb-wide { display: none; }
  .grv-modal-tp2026 .grv-tp-verb-narrow { display: inline; }

  /* The brand words leave the visible label but stay in the accessible name,
     so the button still announces "Sign in with Google" while showing
     "Sign in [G]". Same technique as .grv-tp-sr-only, applied conditionally. */
  .grv-modal-tp2026 .grv-tp-provider-name {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }

  /* The mark moves to the end, so the row reads "Sign in [G]" rather than
     "[G] Sign in". order works on the flex children the button already has —
     no second copy of the SVG, and the DOM order that assistive tech reads
     ("Sign in" then "with Google") is untouched. */
  .grv-modal-tp2026 .grv-tp-provider-mark { order: 2; }

  /* Two short parts sit closer than an icon beside a full sentence did. */
  .grv-modal-tp2026 .grv-tp-btn { gap: 8px; }
}

/* ==========================================================================
   Reduced motion — sheet travel

   Placed last on purpose: these cancel the sheet animations declared in the
   mobile blocks above, and at equal specificity the later rule wins.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .grv-modal.grv-modal-tp2026,
  .grv-modal-overlay.grv-modal-anchored > .grv-modal.grv-modal-tp2026,
  .grv-modal-overlay.grv-modal-anchored.grv-tp-overlay > .grv-modal.grv-modal-tp2026,
  .grv-sr-dropdown.grv-sr-dropdown-tp2026,
  .grv-modal.grv-modal-tp2026.grv-tp-closing,
  .grv-sr-dropdown.grv-sr-dropdown-tp2026.grv-tp-closing,
  /* The side sheet's travel runs on the inner wrapper, so cancelling it on the
     sheet alone would leave the panel sliding. */
  .grv-sr-dropdown.grv-sr-dropdown-tp2026 > .grv-sr-dropdown-inner,
  .grv-sr-dropdown.grv-sr-dropdown-tp2026.grv-tp-closing > .grv-sr-dropdown-inner,
  /* The scrim's fades use fill mode "both", so cancelling them without also
     restoring opacity would leave the scrim stuck at its 0 keyframe and the
     page undimmed. */
  .grv-tp-overlay,
  .grv-tp-overlay[data-grv-closing="1"] {
    animation: none !important;
  }

  .grv-tp-overlay {
    opacity: 1;
  }
}
