/* ==========================================================================
   Air Market - v3 / the Stack.  Pairs with assets/js/v3/stack.js.
   REDESIGN.md §3 (the pre-flight manifest) and §11.2 (it finally has a home).

   Three surfaces, in the order a reader meets them:
     1. .v3-stack-btn      the counted affordance in [data-v3-slot="stack"],
                           tier 2, where a shopper looks for the cart;
     2. .v3-stack-chip     the "+ Stack" chip injected onto every card;
     3. .v3-stack-dialog   the drawer - an Air_DS Dialog re-anchored to the
                           right edge, which is where every commerce cart
                           opens from. UI.openDialog brings Escape, the focus
                           trap and focus restoration; only the geometry is
                           ours, so components.css is not touched.

   EVERY rule is scoped under :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) - v0, v1 and v2 must render
   byte-identically. v4 keeps the Stack exactly as v3 renders it (V4-SPEC.md
   §2.3), so it shares v3's gate rather than a private clause. The dialog
   overlay is appended to <body>, which is still inside :root, so the same
   scope reaches it.

   HOUSE RULES: no hardcoded hex - tokens.css custom properties and the v3
   aliases from store-v3-core.css only; cursors via var(--cur-*), never a bare
   keyword; Manrope throughout with font-variant-numeric: tabular-nums on the
   quantities (no JetBrains Mono - the drawer's check dates read exactly as the
   card's "Checked Jul 2026" does, and that is not mono either); teal
   (--v3-verif) means verified and nothing else, so the count badge is ink and
   only the shield is teal.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. THE COUNTED AFFORDANCE (tier 2).

   A pill, not a square icon button: the count has to sit beside the word, and
   a labelled control is the one thing every reference storefront agrees on.
   The badge is --color-action-primary (ink on inverse text) rather than teal,
   because the badge is a quantity and teal is a claim about verification. The
   teal shield next to it IS that claim, and appears only when every listing in
   the Stack has a check date.
   -------------------------------------------------------------------------- */

:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-8);
  height: 32px;
  padding: 0 var(--space-12);
  border: 1px solid var(--v3-line);
  border-radius: var(--radius-full);
  background: var(--v3-paper);
  color: var(--v3-ink);
  font-family: var(--font-main);
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  cursor: var(--cur-pointer);
  transition: background var(--v3-ease), border-color var(--v3-ease);
}

:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-btn .ic { --ic-size: 18px; }
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-btn:hover { background: var(--color-action-secondary); }
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-btn:focus-visible { outline: none; border-color: var(--color-border-focus); }
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-btn[aria-expanded="true"] {
  background: var(--color-action-secondary);
  border-color: var(--color-border-default);
}
/* Holding something is a state worth seeing before you read the number. */
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-btn.has-items { border-color: var(--color-border-default); }

:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-btn-n {
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-full);
  background: var(--color-action-primary);
  color: var(--color-text-inverse);
  font-size: 11.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-btn-ok {
  display: inline-flex;
  align-items: center;
  color: var(--v3-verif);
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-btn-ok .ic { --ic-size: 15px; }
/* display:grid / inline-flex above would beat the UA's [hidden] rule. */
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-btn-n[hidden],
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-btn-ok[hidden] { display: none; }

/* Tier 2 loses 8px when condensed; the label is the first thing that can go
   without costing the control its meaning (icon + count still read). */
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar.is-condensed .v3-stack-btn { height: 30px; }

/* --------------------------------------------------------------------------
   2. THE CARD CHIP.

   REDESIGN §4 asks for "+ Stack on hover". Hover-only would strand a keyboard
   user and hide the state of a card already in the Stack, so it is revealed by
   :hover, by :focus, whenever the card IS in the Stack, and permanently on
   touch devices where :hover never fires.

   Bottom-right of .a-shot is the only free corner: .a-kind is top-left,
   .a-rank top-right and .a-mark bottom-left.
   -------------------------------------------------------------------------- */

:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .a-card .v3-stack-chip {
  position: absolute;
  right: 8px;
  bottom: 8px;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 24px;
  padding: 0 8px;
  border: 1px solid var(--v3-line);
  border-radius: var(--radius-full);
  background: var(--v3-paper);
  color: var(--v3-ink);
  font-family: var(--font-main);
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1;
  cursor: var(--cur-pointer);
  opacity: 0;
  transform: translateY(2px);
  transition: opacity .12s ease, transform .12s ease, background .12s ease, color .12s ease;
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .a-card .v3-stack-chip .ic { --ic-size: 14px; }

:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .a-card:hover .v3-stack-chip,
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .a-card .v3-stack-chip:focus,
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .a-card .v3-stack-chip[aria-pressed="true"] {
  opacity: 1;
  transform: none;
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .a-card .v3-stack-chip:hover { background: var(--color-action-secondary); }
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .a-card .v3-stack-chip:focus-visible { outline: none; border-color: var(--color-border-focus); }

/* In the Stack = ink, not teal. Teal is reserved for "checked by AIR". */
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .a-card .v3-stack-chip[aria-pressed="true"] {
  background: var(--color-action-primary);
  border-color: var(--color-action-primary);
  color: var(--color-text-inverse);
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .a-card .v3-stack-chip[aria-pressed="true"]:hover {
  background: var(--color-action-primary-hover);
}

@media (hover: none) {
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .a-card .v3-stack-chip { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------------------
   2b. THE SAME CONTROL, ON THE DETAIL HERO.

   app.js's ctaBlock() renders `.cta-ghost.cta-stack` next to Save and Share
   (stackBtn()), and stack.js paints it with the chip's own glyph, label and
   aria-pressed. So the shape is Share's, not the chip's - it sits in a row of
   secondary pills and has to read as one of them - and the only thing this
   sheet has to add is the pressed state, which .cta-ghost has no concept of.

   Ink fill, exactly as the chip does it, and for the same reason: teal stays
   reserved for "checked by AIR", and a control that means the same thing in
   two places should not change colour between them. It is the same 32px pill
   as Share either way, so the fill never grows the row.
   -------------------------------------------------------------------------- */

:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .cta-secondary .cta-stack[aria-pressed="true"] {
  background: var(--color-action-primary);
  border-color: var(--color-action-primary);
  color: var(--color-text-inverse);
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .cta-secondary .cta-stack[aria-pressed="true"]:hover {
  background: var(--color-action-primary-hover);
  border-color: var(--color-action-primary-hover);
}

/* THE THIRD PILL HAS TO PAY FOR ITS OWN ROW.

   .cta-secondary is `display:flex` with no wrap (store-v5.css:222), which was
   free when the row held two pills. Measured at 320px: the hero column is
   260px wide and Save (87) + In Stack (92) + Share (92) + two 8px gaps is 287,
   so Share hung 27px past the column and into the gutter. Worse, a nowrap flex
   row is also the widest thing in .cta-block, so it set that column's width
   and pushed "Open in Claude" out of the primary button with it - a third pill
   broke a CTA two rows above itself.

   Two rules, in the order the layout should give way:

   1. WHERE THE ROW CANNOT HOLD THREE LABELS, THIS LABEL GOES - NOT THE
      BUTTON. Icon only is 44px instead of 107, which brings the row from 302
      to 239. The label is the cheapest thing here to lose: the button keeps
      the full height of its row (44px on a phone, which is v9's tap floor -
      only the width changes), the ink fill still says it is in the
      Stack, and paintChip writes a full aria-label ("Add Meeting Notes Pro to
      your Stack"), so a screen reader hears MORE than the two words that came
      off. Save and Share keep theirs - they were fitting before this button
      existed and it is not their row to pay for.

      Below 375px only, where .cta-col is the full viewport
      (store-v5.css:335) and 302 does not fit 315 minus the gutters. 374 is
      store-v11-detail.css section 1b's line and for the same reason: 375 is
      the narrowest width in common use, and at 375 all three labels do fit,
      by 13px.

   1b. THE TABLET COLUMN IS 2px SHORT, SO IT PAYS IN PADDING INSTEAD.
      store-v5.css:329 narrows .cta-col to 300px between 721 and 1023, and
      302 into 300 wrapped Share onto a second line under a half-empty first
      one. Two pixels is not worth a label: 16px of side padding becomes 12px
      on all three pills, which is 24px back and 22px of slack, and at 14px
      text nobody can see which one they are looking at. It applies only to a
      row that HAS the third pill, so the two-pill rows this file never
      touched keep store-v5.css's own metrics.

   2. AND THE ROW WRAPS ANYWAY. Belt and braces for what CSS cannot measure:
      a longer name, a large text-size preference, a translated "In Stack".
      Wrapping drops a pill to a second line, which is untidy; overhanging the
      column is broken. `:has(.cta-stack)` keeps both rules off the two-pill
      row this file did not change - v0-v5 detail pages that render no Stack
      button lay out exactly as they did. */
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .cta-secondary:has(.cta-stack) {
  flex-wrap: wrap;
}
@media (max-width: 374px) {
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .cta-secondary .cta-stack {
    width: 44px;
    padding: 0;
    justify-content: center;
  }
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .cta-secondary .cta-stack > span {
    display: none;
  }
}
@media (min-width: 721px) and (max-width: 1023px) {
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .cta-secondary:has(.cta-stack) .save-btn,
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .cta-secondary:has(.cta-stack) .cta-ghost {
    padding: 0 var(--space-12);
  }
}

/* --------------------------------------------------------------------------
   3. THE DRAWER.

   .dialog-overlay is `display:grid; place-items:center; padding:24px` in
   components.css. Re-anchoring it to the right edge and letting the dialog
   fill the height is the whole conversion - every other Dialog on the site is
   untouched because these rules require the .v3-stack-dialog class that
   stack.js adds to its own overlay.
   -------------------------------------------------------------------------- */

:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .dialog-overlay.v3-stack-dialog {
  padding: 0;
  place-items: stretch end;
}

:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-dialog .dialog {
  width: min(460px, 100%);
  height: 100%;
  border-radius: 0;
  border-width: 0 0 0 1px;
  padding: var(--space-24);
  gap: var(--space-16);
}

/* The dialog is a flex column; the panel takes the remaining height so the
   manifest scrolls and the install button never leaves the viewport. */
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-panel {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  gap: var(--space-16);
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  gap: var(--space-20);
}

/* -------- the listings ---------------------------------------------------- */

:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-12);
  padding: var(--space-12) 0;
  border-bottom: 1px solid var(--v3-line);
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-item:first-child { padding-top: 0; }
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-item-main { flex: 1; min-width: 0; }

:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-open-item {
  display: block;
  max-width: 100%;
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--font-main);
  font-size: 14px;
  font-weight: 700;
  line-height: 1.25;
  text-align: left;
  color: var(--v3-ink);
  cursor: var(--cur-pointer);
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-open-item:hover { text-decoration: underline; }
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-open-item:focus-visible {
  outline: 2px solid var(--color-border-focus);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}

:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-item-meta {
  margin-top: 2px;
  font-size: 12px;
  color: var(--text-3);
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-item-check {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-top: 6px;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: var(--v3-verif);
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-item-check .ic { --ic-size: 13px; }

:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-rm {
  flex: none;
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-3);
  cursor: var(--cur-pointer);
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-rm .ic { --ic-size: 15px; }
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-rm:hover {
  background: var(--color-action-secondary-hover);
  color: var(--color-text-primary);
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-rm:focus-visible { outline: none; border-color: var(--color-border-focus); }

/* -------- the combined ledger --------------------------------------------
   The one thing in the store nobody else ships, so it gets a surface of its
   own rather than being another paragraph. Sunken field + hairline is the same
   treatment the detail page gives its trust cards.
   -------------------------------------------------------------------------- */

:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-sec {
  background: var(--v3-field);
  border: 1px solid var(--v3-line);
  border-radius: var(--radius-lg);
  padding: var(--space-16);
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-h {
  margin: 0 0 var(--space-12);
  font-family: var(--font-main);
  font-size: 13px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--v3-ink);
}

:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-ledger {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-12);
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-lrow {
  display: grid;
  grid-template-columns: 106px minmax(0, 1fr);
  gap: var(--space-12);
  align-items: baseline;
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-lrow dt {
  font-size: 12px;
  color: var(--text-3);
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-lrow dd { margin: 0; min-width: 0; }
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-lval {
  display: block;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.35;
  color: var(--v3-ink);
  font-variant-numeric: tabular-nums;
}
/* The precise scope, one layer down from the plain sentence (REDESIGN §10). */
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-lscope {
  display: block;
  margin-top: 2px;
  font-size: 11.5px;
  line-height: 1.4;
  color: var(--text-3);
}

:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-note {
  margin: var(--space-12) 0 0;
  font-size: 12px;
  line-height: 1.45;
  color: var(--text-2);
}

:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-sum {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: var(--space-12) 0 0;
  padding-top: var(--space-12);
  border-top: 1px solid var(--v3-line);
  font-size: 12px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--v3-verif);
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-sum .ic { --ic-size: 14px; }

/* -------- actions --------------------------------------------------------- */

:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  padding-top: var(--space-16);
  border-top: 1px solid var(--v3-line);
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-actions .btn { width: 100%; }

/* -------- empty state -----------------------------------------------------
   The least familiar control in the store, so this does real work: what a
   Stack is, what you get out of it, and the exact affordance to look for.
   -------------------------------------------------------------------------- */

:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-empty {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-12);
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-empty-lead {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  color: var(--v3-ink);
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-empty p {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--text-2);
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-inline {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 1px 7px;
  border: 1px solid var(--v3-line);
  border-radius: var(--radius-full);
  background: var(--v3-paper);
  color: var(--v3-ink);
  font-size: 11.5px;
  font-weight: 600;
  vertical-align: baseline;
}
:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-inline .ic { --ic-size: 13px; }

/* --------------------------------------------------------------------------
   4. Motion.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-stack-btn,
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .a-card .v3-stack-chip { transition: none; }
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .a-card .v3-stack-chip { transform: none; }
}
