/* Qwipy — header navigation menu (hamburger + dropdown).
   Additive only: does not modify any Framer-generated rule.
   Lives inside the existing (previously empty) Nav Links slot in the
   header navbar, using the site's own tokens, radius, and border scale. */

/* Sticky header on every page. On index.html, Framer's own runtime
   already sets position:sticky as a plain (non-!important) inline style
   once it hydrates — so this rule is redundant-but-harmless there. On
   /privacy/, /terms/ and /contact/, which never hydrate (no React bundle,
   no hydration root), the header keeps the SSR class default
   `position: relative` (from .framer-x20qo5) forever without this.
   !important is required for the same reason as .framer-iu0nmg's z-index
   fix below: an !important rule in an external stylesheet beats a plain
   (non-!important) inline style regardless of selector specificity, so
   this reliably wins on index.html too, not just on the static pages —
   collapsing the hydrated/non-hydrated cases into one code path. */
.framer-x20qo5 {
  position: sticky !important;
  top: 0 !important;
  z-index: 90 !important;
}

/* Glassmorphism navbar. The previous pass kept the site's normal solid
   1px BLACK border, which is exactly what was fighting the glass
   illusion — a bold opaque outline reads as "solid card," not "pane of
   glass," no matter how much blur is behind it. The actual recipe
   (background.opacity ~0.25-0.3, backdrop blur, a soft near-white
   border instead of a dark one, a diffused shadow) is what makes it
   look transparent rather than just tinted.

   Framer doesn't render this element's border with a plain CSS `border`
   property — it reads the custom property `--border-color` (set inline
   on the element) into a `:after` pseudo-element. So the border has to
   be overridden by reassigning that variable, not by setting `border`
   directly (confirmed via getComputedStyle(el, '::after').borderColor
   — a normal `border-color` override here would have no visible effect
   at all). `background-color` is overridden directly since Framer sets
   that with a plain inline style. */
.framer-iu0nmg {
  /* `backdrop-filter` (below) creates its own CSS stacking context on
     this element — which means the dropdown menu's z-index:50 (a
     descendant, several levels down) stopped competing globally and
     started being scoped INSIDE this navbar's local context instead.
     Real symptom: the hero section below the header (a later, unrelated
     DOM sibling) started painting on top of the dropdown, silently
     eating clicks on its second link. Giving the navbar itself an
     explicit z-index here is what actually fixes it — it makes the
     whole glass pill (dropdown included) win the OUTER stacking
     comparison against the hero, not just internally against its own
     children.

     !important: Framer's own hydrated homepage adds a responsive
     "variant" class to an ancestor (only on index.html — the static
     privacy/terms pages never run Framer's React bundle, so they never
     get it) and ships its own rule,
     `.framer-unxdD.framer-v-e2i9s6 .framer-iu0nmg { z-index: 1; }`.
     That's 2-class specificity beating this plain 1-class selector, so
     without !important the homepage silently reverts to z-index:1 while
     privacy/terms correctly get 100 — reopening the exact dropdown-click
     bug this rule was meant to fix, but only on page 1. */
  position: relative;
  z-index: 100 !important;
  --border-color: rgba(255, 255, 255, 0.65) !important;
  background-color: rgba(255, 255, 255, 0.14) !important;
  /* The backdrop-blur trick only reads as "glass" when there's varied
     color behind it to distort — over the homepage's flat green hero
     there's nothing to blur, so it looked like a plain tinted pill there
     even though it was genuinely glass over the gradient hero on
     /privacy/ and /terms/. Two diagonal sheen bands (not one) give the
     glass its own baked-in surface highlight/shadow, so it reads as
     glass on its own regardless of what's behind it. */
  background-image:
    radial-gradient(
      320px circle at var(--qwipy-glow-x, 50%) var(--qwipy-glow-y, -30%),
      rgba(255, 255, 255, 0.75),
      rgba(255, 255, 255, 0) 65%
    ),
    linear-gradient(
      120deg,
      rgba(255, 255, 255, 0.55) 0%,
      rgba(255, 255, 255, 0.04) 24%,
      rgba(255, 255, 255, 0.02) 58%,
      rgba(255, 255, 255, 0.3) 100%
    ),
    linear-gradient(
      205deg,
      rgba(255, 255, 255, 0.3) 0%,
      rgba(255, 255, 255, 0) 32%
    );
  background-repeat: no-repeat, no-repeat, no-repeat;
  /* Heavier blur + saturation + a brightness/contrast lift is what
     actually sells "glass" over "tinted panel" — light passing through
     frosted glass looks brighter and more vivid than what's directly
     behind it, not just soft-focus. */
  -webkit-backdrop-filter: blur(24px) saturate(2.6) brightness(1.1) contrast(1.05);
  backdrop-filter: blur(24px) saturate(2.6) brightness(1.1) contrast(1.05);
  box-shadow:
    0 16px 48px rgba(29, 29, 29, 0.25),
    0 4px 12px rgba(29, 29, 29, 0.1),
    inset 0 2px 1px rgba(255, 255, 255, 0.95),
    inset 0 -1.5px 1px rgba(29, 29, 29, 0.08),
    inset 1.5px 0 1px rgba(255, 255, 255, 0.35),
    inset -1.5px 0 1px rgba(255, 255, 255, 0.2);
  /* Dynamic navbar (see initDynamicNavbar in qwipy-added-sections.js):
     transform is the only property that changes between resting and
     .qwipy-navbar-compact, so it's the only one that needs a transition
     here. Spring easing (4th value >1 overshoots past the target before
     settling back) instead of a flat ease-out — inspired by a reference
     prompt-input component's own expand/collapse "SPRING_TRANSITION"
     (cubic-bezier(0.175, 0.885, 0.32, 1.275)) — so the pill has a small
     bouncy "pop" when it expands back to full size, not just a linear
     resize. Matches that reference's 0.4s duration too, since the
     overshoot needs the extra time to actually read as a bounce rather
     than a twitch. */
  transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}

/* Scroll-velocity-driven compact state — shrinks the whole pill (and,
   since .qwipy-nav-inline/.qwipy-nav-menu are anchored with
   position:absolute inside it, everything in it) a modest amount while
   the page is scrolling fast, easing back to full size the moment
   scrolling slows or stops. transform-origin pins the top edge so the
   pill visually "condenses" downward from where it's already sitting
   (under the sticky header) rather than shifting vertically. */
.qwipy-navbar-compact {
  transform: scale(0.86);
  transform-origin: 50% 0%;
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  /* Browsers without backdrop-filter support (rare) get a slightly more
     opaque fill instead of a transparent one with no blur, so text
     underneath never shows through unblurred. */
  .framer-iu0nmg {
    background-color: rgba(255, 255, 255, 0.92) !important;
  }
}

/* The header's "Nav Links" slot is a Framer component that switches
   between several responsive "variant" classes after hydration — at
   some breakpoints those variants turn it into a huge absolutely
   positioned overlay panel with `pointer-events: none` (a leftover
   mobile-menu-panel design from the source template, unused since this
   export's actual menu-button component was removed). Even when reset to
   a plain flex item, its layout box still competes in the Navbar's
   `justify-content: space-between` row alongside two other leftover
   "Social Icons" containers that are empty but still take up width — so
   its horizontal position swings unpredictably per breakpoint/variant
   (it landed next to the logo on one page, floating mid-navbar on
   another). Zero out its box entirely so it takes no space in that row,
   and position our menu directly against the Navbar itself instead. */
nav[data-framer-name="Nav Links"] {
  position: static !important;
  width: 0 !important;
  height: 0 !important;
  min-width: 0 !important;
  min-height: 0 !important;
  padding: 0 !important;
  margin: 0 !important;
  overflow: visible !important;
  pointer-events: auto !important;
  /* Same component also ships an inline `opacity:0; transform:translateX(-50%)`
     appear-animation initial state that nothing ever triggers — force it
     visible and untransformed. qwipy-added-sections.js also clears the
     inline style directly, since inline opacity/transform can be re-applied
     after this stylesheet loads. */
  opacity: 1 !important;
  transform: none !important;
  /* CRITICAL: this element's inline style also carries `will-change:
     transform`. Per spec, `will-change: transform` makes an element a
     containing block for absolutely-positioned descendants EVEN THOUGH
     it's `position: static` — so `.qwipy-nav-menu` below was anchoring
     to this (deliberately zero-sized) box instead of skipping past it to
     the Navbar, which is why the button kept landing wherever this zero-
     width box happened to sit in the flex row instead of at the corner.
     Clearing will-change lets position:absolute correctly skip up to the
     Navbar (position: relative) as intended. */
  will-change: auto !important;
}

/* Right-hand nav cluster — desktop text links (or, below 809px, the
   hamburger) plus the CTA button, grouped as ONE flex row anchored to the
   pill's right edge, with a real `gap` between them. This replaced an
   earlier version where .qwipy-nav-inline/.qwipy-nav-menu/.qwipy-nav-cta
   were each independently `position: absolute` with a hand-measured
   pixel offset guessing the other elements' rendered widths — on a
   narrower mobile pill that guess was badly wrong, leaving the hamburger
   stranded near the logo with a large dead gap before the CTA. A flex
   container can't drift like that: the gap is always exactly `gap`,
   regardless of how wide the CTA's text or the link row actually render.
   Anchored at the same 30px inset the Navbar pill uses for its own
   padding, so the cluster always sits flush at the top-right corner. */
.qwipy-nav-cluster {
  position: absolute;
  top: 50%;
  right: 30px;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  gap: 20px;
  z-index: 50;
}

@media (max-width: 809px) {
  .qwipy-nav-cluster {
    gap: 14px;
  }
}

/* Desktop/tablet nav: Privacy, Terms and Contact as plain always-visible
   text links. Hidden below 809px in favor of the hamburger (see the
   media query below) — matches the breakpoint qwipy-legal.css already
   uses. No longer independently positioned — see .qwipy-nav-cluster
   above, its flex parent. */
.qwipy-nav-inline {
  display: flex;
  align-items: center;
  gap: 28px;
}

.qwipy-nav-inline-link {
  display: inline-block;
  /* Real padding + no-wrap, not just glyph bounds: the footer-link bug
     (a wrapped inline <a> whose own bounding-box center falls in the gap
     between its line-fragments and isn't part of the element for hit-
     testing) is prevented here proactively rather than fixed reactively —
     these can never wrap onto two lines in the first place. */
  padding: 10px 2px;
  white-space: nowrap;
  text-decoration: none;
  color: var(--token-2f7038ac-bbf8-44f5-ae13-dbd500870086, #1d1d1d);
  opacity: 0.75;
  transition: opacity 0.15s ease;
}

.qwipy-nav-inline-link:hover {
  opacity: 1;
}

/* Hamburger + dropdown. Hidden by default now that .qwipy-nav-inline
   covers desktop/tablet — shown again only below 809px, see the media
   query below. `position: relative` (rather than the old absolute pin to
   the pill's own corner) so .qwipy-nav-dropdown below anchors under the
   hamburger icon itself, wherever the flex cluster actually places it. */
.qwipy-nav-menu {
  position: relative;
  display: none;
  align-items: center;
  /* The Navbar also has two empty leftover "Social Icons" containers
     (siblings of Nav Links, painted after it in DOM order) that overlap
     this same corner at some header variants and — despite being
     invisible — still intercept real pointer clicks before they reach
     the button underneath (confirmed via elementFromPoint: a real click
     hit .framer-el0tsv, not the button, even though the button was
     visually on top). A real z-index stacking context here guarantees
     the menu always wins the hit-test regardless of DOM order. */
  z-index: 50;
}

@media (max-width: 809px) {
  .qwipy-nav-inline {
    display: none;
  }

  .qwipy-nav-menu {
    display: flex;
  }
}

/* Primary CTA — the one button on the site that actually gets someone to
   install the extension, so it's in the nav cluster (the one spot
   visible at every scroll position, on every page) rather than living
   only mid-page or in the footer. Filled/high-contrast on purpose: every
   other nav item is a plain text link, this is the one thing that should
   read as a button, not a link. No longer independently positioned — see
   .qwipy-nav-cluster above, its flex parent. */
.qwipy-nav-cta {
  display: inline-flex;
  align-items: center;
  white-space: nowrap;
  padding: 10px 18px;
  border-radius: 10px;
  background: var(--token-2f7038ac-bbf8-44f5-ae13-dbd500870086, #1d1d1d);
  color: var(--token-9fc38d7a-a3f6-4f38-9b69-d5eb5696eb0d, #fff);
  font-family: "Uncut Sans Variable Variable", "Uncut Sans Variable Variable Placeholder", sans-serif;
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  transition: transform 0.2s cubic-bezier(0.16, 0.8, 0.3, 1), box-shadow 0.2s ease;
}

.qwipy-nav-cta:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 16px rgba(29, 29, 29, 0.25);
}

.qwipy-nav-cta:active {
  transform: translateY(0) scale(0.97);
}

/* Hidden below 809px, not just shrunk — Chrome extensions can't be
   installed from a mobile browser at all (no extensions API there), so
   the button has nowhere useful to send a mobile visitor. Showing it
   anyway was also what caused the cramped/overlapping mobile nav in the
   first place (an earlier fix shrank it to fit instead of asking whether
   it belonged there). The hamburger's own menu still has every other nav
   link, so nothing is lost — just the one action mobile genuinely can't
   do. */
@media (max-width: 809px) {
  .qwipy-nav-cta {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .qwipy-nav-cta {
    transition: none;
  }
}

.qwipy-nav-toggle {
  width: 40px;
  height: 40px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
}

.qwipy-nav-toggle span {
  display: block;
  width: 20px;
  height: 2px;
  border-radius: 2px;
  background: var(--token-2f7038ac-bbf8-44f5-ae13-dbd500870086, #1d1d1d);
  transition: transform 0.2s ease, opacity 0.2s ease;
}

.qwipy-nav-toggle[aria-expanded="true"] span:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}

.qwipy-nav-toggle[aria-expanded="true"] span:nth-child(2) {
  opacity: 0;
}

.qwipy-nav-toggle[aria-expanded="true"] span:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

/* Frosted-glass dropdown — third pass. The first two both floated as a
   small, separately-sized box off the pill's corner (wrong width, wrong
   opacity, and a scale-from-corner animation that read as "a card fading
   in nearby" rather than "the bar opening downward"). This version fixes
   the actual complaint: `left`/`width`/`top` are set inline by
   openDropdown() in qwipy-added-sections.js to the navbar pill's own
   measured position every time it opens, so the dropdown is ALWAYS
   exactly as wide as the bar it hangs from — not a guessed min-width.
   Deliberately `position: absolute`, not `fixed` — this page has an
   ancestor wrapper with its own `transform: translateX(-50%)` (the
   outer layout-centering container), which per spec makes THAT the
   containing block for any `position: fixed` descendant instead of the
   viewport, so a naive fixed + raw getBoundingClientRect() offset lands
   in the wrong place (confirmed: the dropdown rendered ~275px off from
   where it measured). openDropdown() computes the offset relative to
   the dropdown's own actual offsetParent instead, which is correct
   regardless of that transform. Flatter top corners than bottom (14px
   vs the pill's own
   20px) read as "continuing out of the bar's bottom edge" at the small
   gap between them, rather than a fully independent rounded rectangle.
   A simpler, single-layer glass tint (no layered sheen gradients — the
   previous attempt's blotchy look) plus a top-anchored scaleY "unfurl"
   instead of a corner scale is what actually makes the open/close read
   as smooth and attached rather than a disconnected fade. Plain CSS
   transition + setTimeout to trigger it, not a WAAPI/rAF completion
   callback — this codebase's established pattern, see the FAQ
   accordion's comments for why. */
.qwipy-nav-dropdown {
  position: absolute;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 14px;
  /* Was rgba(255,255,255,0.24) — a real glass tint, but on a sticky
     header it stays open while the page scrolls underneath (the JS
     scroll-close handler helps, but touch-scroll on real mobile devices
     can outrun/skip it), and at 24% opacity whatever scrolled into view
     behind it — body copy, card text, images — read at nearly the same
     contrast as the link labels themselves, making both unreadable
     together. Near-opaque instead of relying on blur alone to hide
     what's behind: it still keeps the border/shadow/blur for a soft
     edge, but the fill itself can no longer be seen through. */
  background-color: rgba(255, 255, 255, 0.94);
  -webkit-backdrop-filter: blur(28px) saturate(1.8) brightness(1.05);
  backdrop-filter: blur(28px) saturate(1.8) brightness(1.05);
  border: 1px solid rgba(255, 255, 255, 0.55);
  border-radius: 20px;
  border-top-left-radius: 14px;
  border-top-right-radius: 14px;
  box-shadow:
    0 20px 44px -14px rgba(13, 13, 13, 0.32),
    0 4px 12px rgba(29, 29, 29, 0.08);
  z-index: 20;
  transform-origin: top center;
  opacity: 0;
  transform: scaleY(0.85) translateY(-4px);
  transition: opacity 0.2s ease, transform 0.28s cubic-bezier(0.16, 0.8, 0.3, 1);
}

.qwipy-nav-dropdown[hidden] {
  display: none;
}

.qwipy-nav-dropdown--open {
  opacity: 1;
  transform: scaleY(1) translateY(0);
}

.qwipy-nav-link {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 13px 14px;
  border-radius: 13px;
  text-decoration: none;
  color: var(--token-2f7038ac-bbf8-44f5-ae13-dbd500870086, #1d1d1d);
  transition: background-color 0.15s ease, transform 0.15s cubic-bezier(0.16, 0.8, 0.3, 1);
}

.qwipy-nav-link:hover {
  background: rgba(255, 255, 255, 0.4);
  transform: translateX(2px);
}

.qwipy-nav-link-icon {
  flex-shrink: 0;
  opacity: 0.55;
}

@media (prefers-reduced-motion: reduce) {
  .qwipy-nav-dropdown {
    transition: none;
  }

  .qwipy-nav-link {
    transition: none;
  }
}

/* App icon next to the "Qwipy" wordmark in the navbar — icons/icon-mark.png,
   the same black-circle-white-Q mark used for the Chrome extension icon
   itself (transparent background, so it works on the glass navbar too).
   The wordmark link (Framer's own element) becomes the flex row.

   Framer's own rule for this exact element — .framer-ZjuXR.framer-1yti7pd
   — sets `flex-flow: column` with `gap: 70px` and `align-items:
   flex-start` (sized for a single text child, stacked vertically with
   huge spacing meant for something else entirely). At 2 classes of
   specificity it beats a plain single-class rule, so the icon was
   stacking above the wordmark with a 70px gap between them instead of
   sitting beside it. !important forces this to actually be a tidy
   horizontal row. */
.qwipy-logo-link {
  display: flex !important;
  flex-direction: row !important;
  align-items: center !important;
  gap: 9px !important;
  height: auto !important;
}

/* On the homepage's narrower footer column, "Privacy Policy" and "Terms
   of Service" wrap onto two lines. A plain inline <a> that wraps breaks
   into one line-box per line for hit-testing purposes — the point at the
   geometric center of its own getBoundingClientRect() (the natural place
   a user clicks) falls in the gap BETWEEN those two line-boxes, which
   belongs to nothing, so the click falls through to the parent <p> and
   does nothing. display:block makes it one box whose whole area
   (including the inter-line gap) is genuinely part of the element. */
.qwipy-footer-link {
  display: block;
}

/* Framer's own mobile variant for this row (`.framer-E69eo.framer-v-jxzfdc
   .framer-1enn96l`, in the page's inline stylesheet) shrinks the gap between
   links for mobile but leaves `flex-flow: row` inherited from the desktop
   rule — four links packed into one ~320px-wide row leaves each link only
   ~55-65px, nowhere near enough for "How it works" or "Terms of Service" to
   fit on one line, so they wrap word-by-word instead of phrase-by-phrase.
   Stacking them below 809px (this file's existing mobile breakpoint) gives
   each link the full row width instead. !important: that Framer rule isn't
   itself !important, so this plain single-class rule already wins on
   cascade order regardless of its lower selector specificity — but written
   explicitly so a future edit to this rule can't silently lose that fight. */
@media (max-width: 809px) {
  .framer-1enn96l {
    flex-direction: column !important;
    align-items: flex-start !important;
    gap: 14px !important;
  }
}

.qwipy-logo-mark {
  height: 24px;
  width: 24px;
  flex-shrink: 0;
  object-fit: contain;
  /* icon-128.png is a filled square tile (green bg, black circle, white
     Q) — the real extension icon, not just the circle mark cropped out
     on its own. Its content fills the frame edge-to-edge with even
     weight on all sides, so centering it against the wordmark via the
     flex row's own align-items:center needs no extra offset. A soft
     border-radius keeps its square corners from reading as a flat
     sticker inside the pill's own much-rounder 20px corners. */
  border-radius: 7px;
}

/* Custom interactive cursor — the iPadOS/Magic-Keyboard-trackpad style:
   a single soft round "puck" that follows the real cursor with a light
   lag, then fully morphs — width, height, corner radius, and position —
   to match whatever link/button/card it's over, snapping onto the
   element's own shape like a soft highlight rather than just growing a
   fixed-size ring around a point. All of that morphing (position, size,
   radius) is driven every frame from JS (see initCustomCursor in the
   inline script at the end of the page) via exponential smoothing, NOT
   a CSS transition — a target rect can change the instant the visitor
   moves to a different-sized element, and a mid-flight CSS transition
   restarting on every such change reads as a stutter; a continuous JS
   lerp just retargets smoothly instead. Only opacity/background here are
   plain CSS transitions, since those really are simple two-state toggles.
   The OS cursor is left alone (no `cursor: none`) — this is a decorative
   layer on top of it, not a replacement. Fine-pointer devices only. */
.qwipy-cursor-puck {
  position: fixed;
  top: 0;
  left: 0;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: rgba(29, 29, 29, 0.12);
  /* !important: this is the one property that must never lose a
     specificity fight with anything, on this page or a future one —
     if this ever became clickable, it would sit on top of every link
     on the site at z-index 9999 and silently eat every click. */
  pointer-events: none !important;
  z-index: 9999;
  opacity: 0;
  transition: opacity 0.25s ease, background-color 0.2s ease;
}

.qwipy-cursor-puck--active {
  background: rgba(29, 29, 29, 0.08);
}

.qwipy-cursor-visible {
  opacity: 1;
}

@media (hover: none), (pointer: coarse) {
  .qwipy-cursor-puck {
    display: none;
  }
}
