/* src/web/static/css/discord_cta.css */
.discord-cta-floating {
  --discord-cta-inline-gutter: 1rem;
  position: fixed;
  /* Constrain both physical edges. A lone `right` anchor lets this fixed,
     max-content row participate in document overflow on the narrowest layout;
     the paired insets and viewport cap keep the whole row inside the visual
     width while the auto start margin preserves its right alignment. Each
     inset independently clears a landscape notch. */
  left: max(var(--discord-cta-inline-gutter), env(safe-area-inset-left, 0px));
  right: max(var(--discord-cta-inline-gutter), env(safe-area-inset-right, 0px));
  width: max-content;
  max-width: calc(100vw - max(var(--discord-cta-inline-gutter), env(safe-area-inset-left, 0px)) - max(var(--discord-cta-inline-gutter), env(safe-area-inset-right, 0px)));
  margin-left: auto;
  margin-right: 0;
  /* Unstacked (no chat widget on the page): a lone floating element, so max().
     The --stacked variant below reads --cc-fab-slot-*, which carries the
     allowance in theme.css so the whole stack shifts together. */
  bottom: max(1rem, var(--safe-bottom));
  z-index: 1030;
  display: flex;
  align-items: center;
  gap: .25rem;
}

.discord-cta-floating__link {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  box-shadow: var(--shadow-md);
  transition:
    transform var(--motion-instant) var(--ease-standard),
    filter var(--motion-fast) var(--ease-standard),
    box-shadow var(--motion-fast) var(--ease-standard);
}

.discord-cta-floating__link:hover,
.discord-cta-floating__link:focus-visible {
  transform: translateY(-1px);
  filter: brightness(1.04);
  box-shadow: var(--shadow-lg);
}

.discord-cta-floating__close {
  display: inline-grid;
  place-items: center;
  flex: 0 0 44px;
  min-width: 44px;
  min-height: 44px;
  /* A backplate, not a bare glyph (GH-167). Floating, this control is drawn
     over whatever the page has under it -- cover art, mostly -- and a
     transparent button with a grey `bi-x` measured effectively invisible
     there: a pill a reader could not find a way to close. The chip carries its
     own surface so the glyph is read against a token the ink ramp is solved
     for, whatever lies beneath. */
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  box-shadow: var(--shadow-sm);
  color: var(--text-muted);
  line-height: 1;
  padding: 0;
  cursor: pointer;
}

.discord-cta-floating__close:hover {
  color: var(--text);
}

/* Top of the bottom-right floating stack (theme.css). `--stacked` is emitted by
   the template when the reading-log chat FAB will render, so this CTA must clear
   whatever is below it: slot 2 normally, slot 3 on a page that also floats its
   own FAB in slot 2.

   The doubled class is load-bearing, not style: it was added when a phone block
   re-declared `bottom` on the bare class at equal specificity and silently
   un-stacked the CTA. That block is gone (phones render the CTA in flow, see
   the end of this sheet), but two classes keep this rule outranking any
   single-class `bottom` a later edit adds. */
/* ONE unconditional rule, and it must stay that way: this sheet may not set
   `bottom` inside a media query. The chat FAB below it moves at the slot
   scale's own breakpoint, so a breakpoint written HERE can disagree with that
   one and leave a band of widths where the FAB has moved and this has not --
   which is the overlap `--stacked` exists to prevent, and is pinned by
   test_stacked_cta_and_chat_fab_cannot_disagree_about_the_breakpoint.

   `--cc-cta-slot` is how a page that parks its own FAB below this one asks for
   more room, WITHOUT this sheet having to know that page exists or where its
   breakpoints are. The edition page sets it (edition_page.css): slot 3 while
   its dice FAB renders, and back to slot 2 on the widths where that FAB is
   hidden -- one decision, made by the sheet that owns the FAB, in the same
   rule that hides it, so the two cannot drift apart. Anything unaware of the
   variable falls through to slot 2, directly above the chat FAB. */
.discord-cta-floating.discord-cta-floating--stacked {
  bottom: var(--cc-cta-slot, var(--cc-fab-slot-2));
}

/* Page clearance (GH-530). A fixed element takes no space in the flow, so the
   end of every page -- the last card's price line, the last table row's
   wishlist heart, the footer's Privacy/Terms links -- sat under this CTA with
   no way to scroll clear. The allowance goes on <body>, AFTER the footer, so
   everything in the flow can scroll above the CTA; on <main> alone the footer
   (and its own Discord invite link) would stay covered.

   Each value is the CTA's `bottom` in that state plus the row's height: the
   dismiss control's 44px hit area governs the row (the link's min-height is
   2.25rem), plus 1rem of breathing room -- 3.75rem. Each clearance is pinned
   to the exact `bottom` it mirrors (tests/web/test_discord_cta_floating.py),
   so moving the CTA without moving its clearance is red.

   `:has()` matches PRESENCE, not visibility, which is why _discord_cta.html
   dismisses with x-if (the node leaves the DOM, and this rule stops matching)
   rather than x-show.

   Only where the CTA floats: at phone widths it is in the flow (below) and
   takes its own room, so a reservation there would be a hole. The boundary is
   the exact complement of the phone block's. */
@media (min-width: 640.1px) {
  body:has(.discord-cta-floating) {
    padding-bottom: calc(max(1rem, var(--safe-bottom)) + 3.75rem);
  }

  body:has(.discord-cta-floating.discord-cta-floating--stacked) {
    padding-bottom: calc(var(--cc-cta-slot, var(--cc-fab-slot-2)) + 3.75rem);
  }
}

@media (prefers-reduced-motion: reduce) {
  .discord-cta-floating__link {
    transition: none;
  }
  .discord-cta-floating__link:hover,
  .discord-cta-floating__link:focus-visible {
    transform: none;
  }
}

/* Phones: an in-flow card before the footer, not a floating pill (GH-167).

   No resting offset clears a phone. Floating, the CTA sat across the edition
   sticky action bar's Want/Own/Read buttons, the lower covers of every card
   grid, reading-order rows and the synopsis column as the reader scrolled --
   and the page clearance above only frees the END of the page, never the
   middle. On a 320-430px viewport a ~210px pill is half the width of the
   content it floats over, so the only placement that obscures nothing is one
   that takes its own room. `_discord_cta.html` is included just before the
   footer for exactly this.

   640px is the floating stack's own boundary: the slot scale (theme.css), the
   chat FAB's relocation and the edition sticky bar all move there, so below it
   this CTA leaves the stack entirely rather than negotiating a slot in it.
   `bottom` is deliberately not touched -- a static box ignores it, and the
   stacked rule above must stay the only `bottom` this sheet declares under a
   doubled class. Dismissal is unchanged: the same x-if removes the card. */
@media (max-width: 640px) {
  .discord-cta-floating {
    position: static;
    /* An explicit 100%, not auto: <body> is a flex column, where auto
       margins shrink-wrap an auto-width item instead of filling the line. */
    width: 100%;
    max-width: 32rem;
    box-sizing: border-box;
    margin: 2rem auto 0;
    padding-left: max(.75rem, env(safe-area-inset-left, 0px));
    padding-right: max(.75rem, env(safe-area-inset-right, 0px));
  }

  .discord-cta-floating__link {
    flex: 1 1 auto;
    justify-content: center;
    box-shadow: none;
  }

  /* In the flow the chip sits on the page background, not over cover art, so
     the floating shadow would only read as a stray layer. */
  .discord-cta-floating__close {
    box-shadow: none;
  }
}
