/* ==========================================================================
   P6 — shared/components.css
   Rules for partials that render under MORE THAN ONE layout. Every rule below
   is class-scoped, so linking this file into extra layouts is harmless.

   Required links (see the per-section notes for exactly why):
     layouts/retail.html.erb         — site switcher + LinkedIn pixel
     layouts/retail_product.html.erb — site switcher + LinkedIn pixel
     layouts/private_label.html.erb  — site switcher + LinkedIn pixel
     layouts/gateway.html.erb        — LinkedIn pixel
     layouts/un_agency.html.erb      — LinkedIn pixel
     layouts/clueless_book.html.erb  — LinkedIn pixel
     layouts/offers.html.erb         — LinkedIn pixel
     layouts/legal.html.erb          — LinkedIn pixel
   ========================================================================== */

/* ===== Cross-site header switcher (app/views/shared/_site_switcher.html.erb)
   Rendered from retail/partials/_header (layouts: retail, retail_product) and
   private_label/partials/_header (layout: private_label).
   Lifted verbatim from the partial's <style> block.
   ========================================================================= */
/* The cross-site context line (J2, 2026-09-28): the current site on the left,
   the other audience's door on the right, in the gateway's words. */
:root { --site-switcher-h: 2.25rem; }
.site-switcher { height: var(--site-switcher-h); background: #f6f8fb; border-bottom: 1px solid #e5e9f0; color: #475569; font-size: 0.75rem; line-height: 1; }
.site-switcher-inner { max-width: 80rem; height: 100%; margin: 0 auto; padding: 0 1.5rem; display: flex; align-items: center; justify-content: space-between; gap: 1rem; white-space: nowrap; overflow: hidden; }
.site-switcher-here { display: inline-flex; align-items: center; gap: 0.5rem; }
.site-switcher-here strong { color: #0f2a4a; font-weight: 600; }
.site-switcher-dot { width: 0.4375rem; height: 0.4375rem; border-radius: 999px; flex-shrink: 0; }
.site-switcher-dot--consultancies { background: #003366; }
.site-switcher-dot--teams { background: #FF3333; }
.site-switcher-link { color: #64748b; text-decoration: none; }
.site-switcher-target { color: #003366; font-weight: 600; }
.site-switcher-link:hover .site-switcher-target,
.site-switcher-link:focus-visible .site-switcher-target { text-decoration: underline; text-underline-offset: 3px; }

/* The consultancy nav is position: fixed, so on those pages the bar is fixed
   above it, the body makes room, and both slide up once the navbar
   controller marks the nav scrolled (it adds .pl-navbar-scrolled). */
.site-switcher--overlay { position: fixed; top: 0; left: 0; right: 0; z-index: 101; transition: transform 0.25s ease; }
.site-switcher--overlay + .pl-nav { top: var(--site-switcher-h); transition: top 0.25s ease, box-shadow 0.2s; }
.site-switcher--overlay:has(+ .pl-nav.pl-navbar-scrolled) { transform: translateY(-100%); }
.site-switcher--overlay + .pl-nav.pl-navbar-scrolled { top: 0; }
/* Two bar-heights, not one. Every consultancy page's top spacing was tuned
   when the bar sat IN FLOW under the nav (content began one bar down, the
   nav ended at 72px). The nav now ends one bar lower, so content moves down
   by that bar as well: each heading keeps its production gap below the nav.
   One bar-height hid the headings of pages with little top padding (Book a
   Call, Download PDF, Demo, Features Tour). */
body:has(.site-switcher--overlay) { padding-top: calc(var(--site-switcher-h) * 2); }
@media (prefers-reduced-motion: reduce) {
  .site-switcher--overlay, .site-switcher--overlay + .pl-nav { transition: none; }
}

/* On a phone, the door to the other site is what matters. */
@media (max-width: 40rem) {
  .site-switcher-here { display: none; }
  .site-switcher-inner { justify-content: center; }
}


/* ===== LinkedIn <noscript> tracking pixel (app/views/layouts/_linkedin.html.erb)
   The partial is rendered by EIGHT layouts: retail, retail_product,
   private_label, gateway, un_agency, clueless_book, offers, legal.
   The pixel only reaches the DOM for no-JS visitors; if a layout ends up
   without this file linked, the only symptom is a 1x1 image at the foot of the
   page for those visitors.
   ========================================================================= */
.linkedin-noscript-pixel { display: none; }

/* ===== Glossary term card — MOVED (D15, 2026-07-28) =======================
   This note used to say the rule shipped in legacy.css because both call sites
   ran under the `application` layout. Neither statement is true any more: that
   layout is deleted, glossary#index is on `retail_product`, and
   Admin::GlossaryTermsController#preview renders the real public template under
   the same layout. The card is now styled by `.retail-glossary-card` in
   retail/pages.css.
   ========================================================================= */

/* ===== DEAD CODE — no render site in the repo ==============================
   These three partials have no caller. Their inline styles were removed so the
   ratchet baseline can go to zero, and the equivalent rules are parked here.
   Nothing loads them today, and nothing needs to: if any of these partials is
   ever revived, link this file in the reviving layout (or move the rule).
   ========================================================================= */

/* app/views/shared/_browser.html.erb — never rendered; `.screenshot-browser`
   and `.screenshot-browser-controls` have no CSS anywhere in app/assets. */
.screenshot-browser-90 { width: 90%; }

/* app/views/layouts/partials/_cookie_policy.html.erb — never rendered (the
   live banner, if any, is the Termly resource blocker). Bootstrap-era markup. */
.cookie-banner-text { margin-bottom: 5px; }

/* app/views/innate_index/participants/_rating_stats.html.erb — reachable only
   via _friend_ratings, whose sole reference (public_report.html.erb:43) is
   commented out. Would render under the `innate_index` layout. */
.innate-rating-stats-col { padding-left: 50px; }
.innate-rating-stats-col.innate-rating-stats-col-last { padding-left: 60px; padding-right: 0; }


/* ===== Cloudflare Turnstile widget (app/helpers/turnstile_helper.rb) =====
   The helper used to hard-code `style: "margin: 1rem 0;"` on every widget it
   rendered, with one caller passing a tighter override. Both spacings live
   here because the widget renders under every public layout. */
.turnstile-widget { margin: 1rem 0; }
.turnstile-widget--tight { margin: 0.25rem 0; }


/* ===== Layout-less Clueless login (app/views/clueless_book/clueless_login.html.erb)
   That action renders with `layout: false`, so this file is linked from the
   template body rather than a <head>. It is the only page in the app that does
   so — see the comment there. */
.cb-bare-login { padding: 10px; }


/* ===== Body scroll lock while a full-screen overlay is open (audit S14) =====
   Three Stimulus controllers used to set `document.body.style.overflow`
   directly — the same convention violation the inline-style sweep exists to
   remove, just written through the CSSOM instead of an attribute:

     app/javascript/controllers/mobile_menu_panel.js  (retail-mobile-menu +
                                                      un-mobile-menu)
     app/javascript/controllers/offer_modal_controller.js
     app/javascript/private_label/controllers/mobile_menu_controller.js
     app/javascript/private_label/controllers/video_modal_controller.js

   They now toggle this class on <body>. It lives here rather than in a site
   stylesheet because those controllers run under the retail, retail_product,
   un_agency and private_label layouts — every one of which links this file.
   ========================================================================= */
.body-scroll-locked { overflow: hidden; }

/* ── Regional site suggestion banner ──────────────────────────────────────────
   Replaces the GeoIP redirect that used to move European visitors to /en-GB
   without asking (D21, 2026-08-04). Lives here rather than in a site pages.css
   because it renders under every public layout.

   Brand navy so it reads as ours and not as a cookie/consent bar, which
   visitors have learned to dismiss without reading. */
.locale-suggestion {
  background-color: #003366;
  color: #ffffff;
  font-size: 0.95rem;
  line-height: 1.5;
}

.locale-suggestion-inner {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0.75rem 1.5rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.75rem 1.25rem;
}

.locale-suggestion-text {
  margin: 0;
}

.locale-suggestion-actions {
  display: flex;
  align-items: center;
  gap: 1rem;
  white-space: nowrap;
}

.locale-suggestion-go {
  display: inline-block;
  padding: 0.4rem 1rem;
  border-radius: 4px;
  background-color: #ffffff;
  color: #003366;
  font-weight: 600;
  text-decoration: none;
}

.locale-suggestion-go:hover,
.locale-suggestion-go:focus {
  background-color: #e6edf5;
}

/* Underlined, not a bare colour change: on a navy ground a colour-only link is
   not distinguishable enough for low-vision readers. */
.locale-suggestion-dismiss {
  color: #ffffff;
  text-decoration: underline;
}

.locale-suggestion-dismiss:hover,
.locale-suggestion-dismiss:focus {
  color: #e6edf5;
}

@media (max-width: 640px) {
  .locale-suggestion-inner {
    flex-direction: column;
    align-items: flex-start;
    text-align: left;
  }
}

/* ===== Flash messages (shared/_flash_messages.html.erb) =====
   Rendered by every PUBLIC layout above its yield. Lives here rather than in
   one site's pages.css because the partial renders under all of them, and
   every public layout already links this file (including `legal`, which loads
   no Tailwind — so these rules must stand alone, no utility classes). */

.site-flash {
  max-width: 68rem;
  margin: 1rem auto 0;
  padding: 0 1.25rem;
}

.site-flash-message {
  border: 1px solid transparent;
  border-radius: 0.375rem;
  padding: 0.875rem 1.125rem;
  margin-bottom: 0.75rem;
  font-size: 0.9375rem;
  line-height: 1.45;
}

.site-flash-alert {
  background-color: #fef2f2;
  border-color: #fecaca;
  color: #991b1b;
}

.site-flash-notice {
  background-color: #f0fdf4;
  border-color: #bbf7d0;
  color: #166534;
}

/* ===== Image performance (ImageHelper) =====
   Every image_tag now renders inside a <picture> so the browser can pick WebP.
   display:contents removes the wrapper's own box from layout entirely, so the
   <img> stays the flex/grid item it was before — no existing rule has to know
   the wrapper exists, and no layout shifts because of it. */
picture {
  display: contents;
}

/* ImageHelper adds width/height attributes so the browser can reserve the box
   before an image loads. Those attributes are ALSO presentational size hints,
   which is the trap: where a rule sizes only one axis — `.retail-nav-logo img
   { height: 3rem }` is the real example — the width hint stayed at the image's
   intrinsic 391px, got clamped by `max-width: 100%` to whatever the container
   was, and the logo rendered 374x48 instead of 116x48. Stretched.

   `width: auto` at (0,0,1) beats a presentational hint (specificity 0) and
   loses to any class-based rule, so it neutralises the SIZE hint while leaving
   the aspect-ratio the attributes provide — which is the part we actually
   wanted. An image with no CSS sizing at all still lands on its natural size,
   derived from the height hint and that ratio, and still reserves its box.

   Do not "simplify" this away: without it every image sized by a
   height-only (or width-only) rule distorts. */
img {
  width: auto;
}
