/** Shopify CDN: Minification failed

Line 837:27 Unexpected bad string token
Line 837:84 Unterminated string token

**/
/* ══════════════════════════════════════════════════════════════════════════
   addon-product.css — the four add-on product pages + their index.
   Staging theme `new products noam` #151293558877 only.  v3, 2026-08-18.

   SELF-CONTAINED BY DESIGN: nothing here touches assets/theme.css, and every
   class is namespaced `apx-`. Generic names (`foot`, `head`, `bar`) in a
   shared stylesheet have bitten us before — a `.foot` class once inherited
   the site footer's black fill on eight cards with zero console errors.

   Tokens only. No raw hex that duplicates an existing :root token.
   RTL: logical properties everywhere (inline-start/end), never left/right.

   ── WHAT v3 IS, AND WHY IT IS A REWRITE AND NOT A PATCH ───────────────────
   The CEO's note on v2 was "not much — he can do far better", with the bottle
   page (`/products/hachiocup`) named as the bar. v2's stylesheet was dressing
   a page that no longer exists: a scroll-snap rail, a dot row, a full-height
   size-guide SECTION, a shade filmstrip, a bottle cross-sell card. All five
   were removed from the markup this round, so half this file was styling
   nothing and the new markup was styling itself.

   THE DESIGN ARGUMENT, in one line: this page is CREAM, and the bottle page
   earns its scroll by alternating cream with one dark, glowing band. So does
   this one now — the benefit band is the page's dark moment, and the rest of
   the page is calm on purpose so that band lands.

   ── THE MOTION BUDGET (read before adding an animation) ───────────────────
   Every animation in this file is `opacity` or `transform` ONLY — no width,
   height, top, filter-on-scroll or box-shadow transition on a scrolling
   element. That is not a style preference: those are the properties the
   compositor can run off the main thread, so the page stays at 60fps on a
   phone while the shade cross-fades. `prefers-reduced-motion` collapses every
   one of them at the bottom of this file.

   THE ONE SIGNATURE MOMENT is the shade change: the big photo dissolves into
   the same corgi in the same pose wearing another colour, and the halo behind
   the frame takes that shade's own measured hex at the same time. It reads as
   the hat changing colour on a live dog. It only works because the four model
   shots are one calibrated set — the craft is in the photos; this file just
   refuses to interrupt them.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── shell ─────────────────────────────────────────────────────────────── */
/* 🔴 THE `--apx-*` GEOMETRY TOKENS MOVED TO `assets/apx-gallery.css` (19.08).
   They were declared here, on `.apx`, which made them unreachable to any
   surface outside an add-on page — and the bottle page now renders the same
   gallery component. They are declared there on `.apx, .apx-gallery` as ONE
   rule, so this page is unchanged (`.apx` still gets them, and `.apx-main`
   below still resolves `--apx-gallery`) and the component is self-sufficient
   wherever it is rendered.
   ⚠️ `apx-gallery.css` is loaded by `snippets/apx-gallery.liquid`, which this
   page renders — so the tokens arrive with the component, not by luck. Do not
   re-declare them here; two declarations is how the hat and the bottle start
   drifting apart. */
.apx__inner {
  width: 100%;
  max-width: 1140px;
  margin-inline: auto;
  padding-inline: 20px;
}
@media (min-width: 760px) { .apx__inner { padding-inline: 32px; } }

/* The eyebrow. Same anatomy as the bottle page's `.section-label` (gold rule +
   gold caps) but not the same class — that one carries `text-transform:
   uppercase`, which does nothing to Hebrew and everything to a stray Latin
   word, and a `margin-bottom` this page's heads set for themselves. */
.apx-kicker {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--color-gold);
}
.apx-kicker::before {
  content: '';
  width: 24px;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
}
.apx-kicker--light { color: var(--color-gold-light); }

/* Section heads are centred on a wide screen and start-aligned on a phone:
   centring a two-line Hebrew heading at 390px costs more in ragged edges than
   it buys in ceremony. Applied per-section below rather than through one
   shared class, because the four heads do not share a wrapper in the markup
   and inventing one to save four media queries would be the wrong trade. */

/* preview strip — used by the INDEX page only. The product pages carry no
   meta-text at all any more (CEO ruling 18.08). */
.apx-note {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 14px;
  margin-block: 28px 0;
  padding: 10px 18px;
  border-radius: var(--r-full);
  background: var(--color-cream-dark);
  border: 1px dashed rgba(43, 52, 40, 0.22);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--color-text-muted);
}
.apx-note__tag {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.06em;
  color: var(--color-gold);
}

/* ── 1 · BUY BAND ──────────────────────────────────────────────────────── */
/* 🐛 `overflow-x: clip` because the shade halo below is inset −7% and at 390px
   that put 5px of it past the viewport: `documentElement.scrollWidth` measured
   395 against an innerWidth of 390. `clip` and NOT `hidden` is the whole point
   — `hidden` would make this section a scroll container and silently kill the
   gallery's `position: sticky` on desktop, which is a far more visible loss
   than the 5px it fixes. `overflow-y` stays visible, which only `clip` allows. */
/* 🔑 TIGHT AT THE TOP ON PURPOSE. The CEO's reason for lifting the palette was
   that changing a colour and seeing the picture move must be ONE glance — so
   every pixel between the photo and the swatches is working against the only
   interaction the page is built around. The kicker above the title went for
   the same reason (and because "תוספת לסל" positioned a product as an
   accessory), and these margins are measured, not chosen: at 390×844 the top
   of the stage and the bottom of the swatch row sit inside one screen with the
   sticky header allowed for. Verified at 360 and 390 before shipping; if a
   line of copy grows, re-measure rather than assume. */
.apx-buyband { padding-block: 12px 52px; overflow-x: clip; }
/* 🔴🔴 `minmax(0, 1fr)` AND NOT `1fr` — 27.08, AND IT IS THE FIX FOR A BUG THE
   CEO HIT ON A REAL IPHONE, NOT A TIDY-UP. Measured on the working theme at
   390px before anything was changed:
       before an upload   .apx-buy 350px · .apx-buyband scrollWidth 390 = client
       after  an upload   .apx-buy **486.56px**, left edge at **−117px**
   He uploaded a photo on the pendant page and the buy column grew 137px off the
   left of the screen, taking the fields, the price and the button with it.
   🔑 THE CAUSE IS THIS LINE AND NOT THE FILENAME. `1fr` is `minmax(auto, 1fr)`,
      and a grid item's automatic minimum size is its MIN-CONTENT — so any single
      unbreakable string inside the column (an iPhone shares photos as
      `9f2c1407-c34b-473e-b4be-0e3bb8de608f.jpeg`, 40 characters with no break
      opportunity) raises the column's min-content above the viewport and the
      track is allowed to follow it. `overflow: hidden` on the text does NOT
      lower min-content; nothing inside the column can.
   ⚠️ THE DESKTOP RULE 10 LINES DOWN WAS ALREADY WRITTEN `minmax(0, …)` FOR BOTH
      COLUMNS. So the trap was known and the mobile half was simply never given
      the same guard — which is why this is one line and why it is the guard the
      whole page family gets: filenames today, a pasted URL or an email address
      tomorrow, on the hat, the ball, the rope and the collar as well.
   🔴 AND IT IS NOT VISIBLE TO `documentElement.scrollWidth`. `.apx-buyband` is
      `overflow-x: clip` (the line above), so the document reports 390 = 390 while
      the column hangs 117px outside it: **no horizontal scrollbar, and the cut
      side unreachable** — exactly what he described. Any overflow gate written
      against the document alone is BLIND to this whole class; it must read
      `scrollWidth > clientWidth` on the clipping containers too. `_qc/qc-pendant`
      §14 does. */
.apx-main {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 18px;
  align-items: start;
}
@media (min-width: 980px) {
  .apx-buyband { padding-block: 28px 84px; }
  .apx-main {
    /* the gallery is capped rather than given half the page: at 700px a single
       square photo reads as a poster and squeezes the column that actually
       sells */
    grid-template-columns: minmax(0, var(--apx-gallery)) minmax(0, 1fr);
    gap: 56px;
  }
  /* the buy column is the long one, so the gallery is what sticks */
  .apx-gallery {
    position: sticky;
    top: calc(var(--nav-h) + 20px);
  }
}

/* ══ GALLERY ═══════════════════════════════════════════════════════════════
   🔴 MOVED OUT, 19.08 → `assets/apx-gallery.css`.
   Every rule for `.apx-gallery / .apx-stage / .apx-slide(s) / .apx-thumb(s)`
   and the `apx-halo` keyframe now lives in that file, verbatim, because the
   BOTTLE product page renders the same component and a component's stylesheet
   cannot live inside one page's 96KB stylesheet. Exactly the seam that had to
   be moved for the FAQ on 18.08 — "design it like X" means USE X's component,
   and a component is markup + behaviour + CSS or it is a lookalike that drifts.
   The stylesheet is loaded by `snippets/apx-gallery.liquid`, which this page
   renders, so nothing here has to link it.
   ⚠️ WHAT STAYED: the `.apx-main` grid and the gallery column's `position:
   sticky`, immediately above. Those are this page's LAYOUT AROUND the gallery,
   not the gallery — the bottle renders it as a standalone section with no buy
   column beside it, and shipping them would have shipped a layout that page
   does not have.
   ⚠️ `.apx[data-apx-kind='hat'] .apx-slide img { object-fit: cover }` and
   `.apx[data-apx-kind='ball'] .apx-stage__count { display: none }` moved too.
   They are `.apx`-scoped, so they simply do not match on the bottle page —
   which is the correct behaviour, not an accident.
   ══════════════════════════════════════════════════════════════════════════ */


/* ── buy column ────────────────────────────────────────────────────────── */
.apx-title {
  font-size: clamp(27px, 5.2vw, 40px);
  font-weight: 900;
  line-height: 1.14;
  color: var(--color-dark-green);
  margin-block: 0 8px;
  letter-spacing: -0.025em;
}
.apx-desc {
  font-size: 15px;
  line-height: 1.6;
  color: var(--color-text-muted);
  margin-block-end: 16px;
  max-width: 46ch;
}

/* ── options ─────────────────────────────────────────────────────────────
   🔑 The pickers sit ABOVE the price this round (CEO, 18.08). His reason is
   exact and worth keeping: the palette has to be high enough that changing a
   colour and the picture moving are ONE glance. Below the price it was
   off-screen from the stage on a phone, so the best interaction on the page
   was invisible. */
.apx-opt { margin-block-end: 20px; }
.apx-opt__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 30px;
  margin-block-end: 11px;
}
.apx-opt__label {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--color-text-light);
}
.apx-opt__value {
  font-size: 14px;
  font-weight: 800;
  color: var(--color-dark-green);
  margin-inline-end: auto;
  margin-inline-start: 8px;
}
/* the shade name is a label for a picture that just dissolved; it changes with
   it instead of snapping, so the two read as one event */
.apx-opt__value.is-swap { animation: apx-fade-up 0.36s var(--ease-out); }

.apx-swatches { display: flex; flex-wrap: wrap; gap: 6px; }
.apx-swatch {
  position: relative;
  width: 48px;
  height: 48px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  border-radius: 50%;
  cursor: pointer;
}
/* 🐛 THE SELECTED SHADE HAD NO RING ON THE PENDANT PAGE — CEO, 28.08: "לוחצים
   על גוון, רק הכיתוב מתחלף. העיגול עצמו לא מסמן שהוא הנבחר."
   The semantics were never wrong: `addon-product.js` toggles `aria-checked`
   correctly on every group, verified in the browser before a pixel was touched.
   It was a CASCADE ACCIDENT, and the arithmetic is worth writing down because
   the shape will recur. `apx-pendant.css` legitimately enlarges the pendant's
   circles to 34px and restates the hairline:
       .apx-pd-swatches .apx-swatch i        (0,2,1)   ← later stylesheet
       .apx-swatch[aria-checked='true'] i    (0,2,1)   ← this file, loaded first
   IDENTICAL SPECIFICITY, so source order decides — and `apx-pendant.css` is
   linked after `addon-product.css` in `addon-product-page.liquid` (370 vs 519).
   Its plain `box-shadow` therefore erased BOTH the hover ring and the selected
   ring, on the one page whose three shades are 11° of hue apart and need the
   ring most. Nothing was broken; a size override silently took a STATE with it.
   🔑 THE FIX IS STRUCTURAL, NOT A SPECIFICITY WAR, AND IT IS TWO SEPARATE MOVES.
      ① The circle's own EDGE moved into `--sw-hair`, so a page that resizes a
        swatch restates a VARIABLE instead of the whole `box-shadow` property.
      ② The STATE moved off `box-shadow` entirely and onto `outline`, a property
        no size override has any reason to touch.
      Either one alone would have fixed today's bug; together the class of bug is
      gone — there is no longer a single property that carries both "what this
      circle looks like" and "is this the chosen one". ⛔ Never move the ring back
      into `box-shadow`; the next page that resizes a swatch would re-open this.
   ⚙️ THE RING IS THE ONLY THING THAT CHANGES, AND THAT IS DELIBERATE. The shade
      IS the information — a selected state that tints, darkens or borders the
      colour itself would be lying about the metal (the 18.08 "sage blue" rule).
      So the circle keeps its exact hex and grows a ring OUTSIDE it, with an empty
      gap so the ring never touches the shade.
   📐 READ ON ALL THREE METALS, NOT ONE — and on the collar's five fabrics and the
      hat's four, because it is one component. The ring is dark green against the
      SURFACE that surrounds the circle, never against the metal, so its contrast
      cannot depend on which shade is selected: 11.4:1 on the measured #FDF3E6,
      identically for gold, silver and rose. The 3px gap is what buys that
      independence — without it, כסף (a near-white) would meet the ring directly
      and the two would read as one soft edge.
   📐 GEOMETRY, CHECKED AGAINST THE 48px TARGET IT MUST NOT BREAK. Circle 32px
      (34 on the pendant) + 2×(3px offset + 2.5px ring) = 43px (45px), scaled 1.06
      = 45.6px (47.7px) — inside the 48px button, and with `gap: 6px` two
      neighbouring rings stay clear of each other. The touch target is the BUTTON
      and is unchanged at 48px. */
.apx-swatch i {
  display: block;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  --sw-hair: inset 0 0 0 1px rgba(43, 52, 40, 0.14);
  box-shadow: var(--sw-hair);
  /* the ring is declared in every state and only its COLOUR changes, so the
     transition is a fade and never a geometry jump. `outline` takes no layout
     space, so declaring it on the idle swatch costs nothing and moves nothing. */
  outline: 2.5px solid transparent;
  outline-offset: 3px;
  transition: outline-color var(--t-med) var(--ease-out), transform var(--t-med) var(--ease-out);
}
.apx-swatch:hover i { transform: scale(1.06); outline-color: var(--color-gold-light); }
.apx-swatch[aria-checked='true'] i { transform: scale(1.06); outline-color: var(--color-dark-green); }
/* hover must never outrank the answer: a mouse resting on an unselected shade
   may not make the chosen one look less chosen */
.apx-swatch[aria-checked='true']:hover i { outline-color: var(--color-dark-green); }

/* 🔴 IT IS AN `outline`, AND THE FIRST ATTEMPT — A BOX-SHADOW WITH A GAP PAINTED
   IN `--color-bg` — WAS WRONG ON A FACT I HAD ALREADY WRITTEN DOWN AS MEASURED.
   The comment said the surface behind these circles is #F9F5EF. It is not. Read
   off the live working theme, 28.08, on all three pages:
       pendant · collar · hat   →  rgb(253, 243, 230)  =  #FDF3E6
   That is the buy card's own warmer cream, not the page token, so a gap painted
   in `--color-bg` would have drawn a visibly wrong halo around every selected
   swatch — a *new* cosmetic bug shipped inside the fix for a cosmetic bug.
   🔑 THE LESSON IS THE ONE THIS FILE KEEPS PAYING FOR: I typed "measured" into a
      comment BEFORE measuring, because the value was obviously going to be the
      design token. Checking took ninety seconds and the answer was different.
   ⚙️ `outline-offset` LEAVES THE GAP EMPTY instead of painting it, so the real
      background shows through whatever it is. The control can be moved onto white,
      onto the dark band, into a drawer, and the ring stays correct with no second
      value to keep in sync. Background-independence is the reason, not novelty.
   📐 AND IT FOLLOWS THE CIRCLE — measured in both engines this audience uses,
      because an outline that squared off around a 34px disc would look broken:
          Chromium 1234   corner of the outline box = bare #FDF3E6, ring = #2B3428
          WebKit 26.5     identical
      (Chromium computes `outline-width: 2.5px` as 2px; WebKit keeps 2.5. A half
      pixel of difference on a ring, deliberately not chased.)
   ⚠️ Safari below 16.4 does not round an outline to the border radius. There the
      ring reads as a rounded square around the circle — still an unmistakable
      selected state, which is the property that has to survive; only the polish
      degrades. That trade is taken knowingly and is better than a hard-coded
      cream that is wrong on every surface but one. */

/* ♿ KEYBOARD FOCUS IS A SEPARATE SIGNAL AND STAYS SEPARATE. The global
   `.apx :focus-visible` rule at the foot of this file outlines the BUTTON in
   gold at 2px offset, while the ring above lives on the `<i>` inside it. So a
   keyboard user arrowing through the group sees BOTH: gold says "you are here",
   dark green says "this is the one that is chosen". Collapsing them into one
   mark would make the two questions unanswerable at the moment they matter —
   arrowing past a shade you have not committed to. */

/* the size-guide button. Promoted from a text link to a real filled pill
   (CEO: "הכפתור בולט יותר כדי שידעו שיש מדריך") and it opens the modal instead
   of scrolling to a section that no longer exists. 44px tall on purpose — it
   is a control, and controls get 44. */
.apx-guidebtn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 44px;
  padding-inline: 16px;
  border-radius: var(--r-full);
  border: 1px solid rgba(184, 148, 78, 0.42);
  background: var(--color-gold-pale);
  color: #7E6430;
  font-family: var(--font);
  font-size: 13px;
  font-weight: 800;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out), transform var(--t-fast) var(--ease-out);
}
.apx-guidebtn svg { flex: 0 0 auto; }
.apx-guidebtn:hover {
  background: var(--color-gold);
  border-color: var(--color-gold);
  color: var(--color-white);
  transform: translateY(-1px);
}

/* 🐛 THE XL BUG. The CEO reported the fifth size dropping to a second line on
   his phone. `flex: 1 1 0` makes the five chips SHARE the row instead of each
   claiming a minimum width, so no viewport can wrap them — the collision is
   impossible by construction rather than avoided by pixel arithmetic. At 390px
   that is (350 − 4×6) ÷ 5 = 65px a chip, and the widest label inside is 38px. */
.apx-chips { display: flex; gap: 6px; }
.apx-chip {
  flex: 1 1 0;
  min-width: 0;
  min-height: 54px;
  padding: 6px 4px;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  border-radius: 12px;
  border: 1.5px solid var(--color-border);
  background: var(--color-white);
  color: var(--color-dark-green);
  font-family: var(--font);
  cursor: pointer;
  transition: background var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out), transform var(--t-fast) var(--ease-out);
}
.apx-chip__name { font-size: 14px; font-weight: 800; line-height: 1.1; white-space: nowrap; }
.apx-chip__sub {
  font-size: 10px;
  font-weight: 600;
  line-height: 1.1;
  white-space: nowrap;
  color: var(--color-text-light);
}
.apx-chip:hover { border-color: var(--color-gold); transform: translateY(-1px); }
.apx-chip[aria-checked='true'] {
  background: var(--color-dark-green);
  border-color: var(--color-dark-green);
  color: var(--color-white);
  transform: translateY(-1px);
  box-shadow: var(--shadow-sm);
}
.apx-chip[aria-checked='true'] .apx-chip__sub { color: rgba(255, 255, 255, 0.66); }

/* The example breeds were already in the size chart; they just were not where
   the decision gets made. Showing them the instant a size is picked turns
   "XL · 45–55" into "גולדן, לברדור" without opening anything. */
.apx-sizehint {
  margin-block-start: 10px;
  padding-inline-start: 2px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--color-text-muted);
}
.apx-sizehint[hidden] { display: none; }
.apx-sizehint.is-in { animation: apx-fade-up 0.34s var(--ease-out); }

/* ── the ball's model picker (stage 3) ─────────────────────────────────────
   THE BUTTON IS THE BALL. A swatch would have been the obvious reuse of the
   hat's control and it would have been describing the wrong property: these
   three models differ in SHAPE and pattern, not in colour, so a coloured circle
   answers a question nobody asked. Showing each ball at 56px means the control
   and the stage are saying the same thing.
   The three photos are one calibrated set on identically-framed canvases, so
   `object-fit: contain` in equal boxes keeps the thumbnails at TRUE RELATIVE
   SCALE too — the football is visibly the widest here for the same reason it is
   in the band further down the page.
   ⚠️ That sentence was a guess until I measured the painted pixels: 0.926 /
      0.950 / 0.938 px per real mm (football / soccer / volleyball), i.e. within
      2.5%, which is JPEG edge softness and not a scale error. The first
      measurement said the football was 25% oversized and it was my instrument —
      the selected card's cream tint fell inside a "non-white" threshold, so I
      had measured a background. Any claim about scale in this component is
      measured against each image's OWN background, never a global threshold. */
.apx-models {
  display: flex;
  gap: 10px;
}
.apx-model {
  flex: 1 1 0;
  min-width: 0;             /* or a long model name stretches the row */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 10px 6px 8px;
  border: 1.5px solid var(--color-border);
  border-radius: 16px;
  background: var(--color-white);
  cursor: pointer;
  transition: border-color var(--t-fast) var(--ease-out), background var(--t-fast) var(--ease-out), transform var(--t-fast) var(--ease-out);
}
.apx-model__art {
  display: block;
  width: 56px;
  height: 56px;
}
.apx-model__art img { width: 100%; height: 100%; object-fit: contain; }
.apx-model__name {
  font-size: 13px;
  font-weight: 700;
  color: var(--color-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
@media (hover: hover) {
  .apx-model:hover { border-color: var(--color-gold); transform: translateY(-1px); }
}
/* 🔴 THE SELECTED PLATE STAYS WHITE — 19.08, and this is a RULE, not a taste
   call. CEO: "יש ריבוע לבן מסביב לכדור הנבחר, צריך פתרון שלא יפגע בעיצוב."

   THE CAUSE IS THE PHOTO, NOT THE PLATE. These three thumbnails are the
   CALIBRATED canvases and their background is baked-in WHITE — that is the
   whole reason they can be trusted for scale, and the CEO ruled yesterday that
   they stay. So the moment this button was tinted `--color-cream-mid` while
   selected, the photo's own white rectangle appeared inside it. Nothing was
   drawn; a background stopped matching.
      ⇒ A container for an image with a baked-in background must be painted
        that background. Anything else paints the crop's edges for free.
   Two consequences, both intended:
   · the white square cannot come back, because there is no state in which the
     plate is not white
   · the selected signal moved ONTO ELEMENTS THAT CAN CARRY IT — the border
     goes from rgba(43,52,40,.1) to solid #2B3428, doubled by an inset ring, and
     the label goes muted → dark green. It is a STRONGER state than the tint
     was: `--color-cream-mid` on `--color-white` is a 1.06:1 surface difference,
     i.e. the artifact was louder than the state it was announcing.
   📌 This is also what `.apx-scale__frame` already does further down the page
      with the same three photos — white plate always, current item marked by a
      border and a ring. The picker was the one place that disagreed. */
.apx-model[aria-checked='true'] {
  border-color: var(--color-dark-green);
  box-shadow: inset 0 0 0 1px var(--color-dark-green);
}
.apx-model[aria-checked='true'] .apx-model__name { color: var(--color-dark-green); }
/* 🔑 SHORT SCREENS, AND THE HONEST VERSION OF THE REQUIREMENT.
   The rule this page inherits from the hat is "choosing and seeing the picture
   change is ONE GLANCE". I checked it by HEIGHT, not width, because that is
   what the hat taught: at 390×844 and 430×932 the picker clears the fold; at
   360×640 and 412×732 it finishes 105–125px below it.
   Those 125px cannot honestly be bought. `--apx-fit` already shrinks the photo
   on short screens and stops at a 240px floor — below that the product stops
   being visible, and a fitted layout around a useless photo is not a fit. So
   the requirement is measured as what it actually is: with the picker scrolled
   into reach, the photo is still on screen — MEASURED 100% of the stage visible
   at 360×640, 412×732 and 390×844. Both halves of the interaction are together
   in one viewport; they are simply not both at scroll position 0 on a 640px
   phone, and pretending otherwise would mean shrinking the picture to win a
   test.
   What IS worth doing is not spending height we do not need: on a short screen
   the control gets compact rather than the photo getting small. */
@media (max-height: 740px) {
  .apx-model { padding: 7px 5px 6px; gap: 4px; }
  .apx-model__art { width: 42px; height: 42px; }
  .apx-model__name { font-size: 12px; }
}

/* ── supplier claims + measured facts (balls only) ─────────────────────────
   Chips for claims, a grid for facts. Two treatments on purpose: the
   separation is what stops a fifth claim quietly joining the set by being
   typed into the same list. */
.apx-claims { display: grid; gap: 8px; margin-block-end: 20px; }
@media (min-width: 420px) { .apx-claims { grid-template-columns: 1fr 1fr; } }
/* 🔧 A LONE CLAIM TAKES THE ROW — found while rebuilding the spec grid below,
   and it is the SAME fault the CEO pointed at, one component up. "בליעת רעש"
   was removed from `addon-catalog` on 18.08, so from 420px up the surviving
   chip sat in the right-hand half of a two-column grid with an empty half
   beside it: at 768px that is a 348px hole that reads as a claim that failed
   to load. Nothing else moves — at two claims this rule does not apply.
   ⚠️ Not the same thing as the row-span I deleted from `.apx-spec` below. That
      one guessed at a LEFTOVER (any odd count in a fixed 2-col grid) and got it
      wrong at three; this is the single case where the whole set is one item,
      and a set of one is not half a row. */
.apx-claim:only-child { grid-column: 1 / -1; }
.apx-claim {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 14px;
  border-radius: 12px;
  background: var(--color-gold-pale);
  color: #7E6430;
  list-style: none;
}
.apx-claim svg { flex-shrink: 0; margin-block-start: 1px; color: var(--color-gold); }
.apx-claim strong { display: block; font-size: 13.5px; font-weight: 800; line-height: 1.3; }
.apx-claim em {
  display: block;
  margin-block-start: 2px;
  font-size: 11.5px;
  font-style: normal;
  line-height: 1.4;
  opacity: 0.78;
}

/* 🔴 REBUILT 18.08 — CEO: "זה לא נראה טוב… זה נראה גרוע." He was right, and it
   was three faults wearing one coat, so all three are fixed here:

   1. THE LAYOUT WAS SYMMETRIC ONLY AT AN EVEN COUNT. A 2-column grid with a
      `:last-child:nth-child(odd)` row-span was my earlier patch for the same
      complaint, and a patch is what it stayed: at three facts it rendered two
      cards over one full-width card — 2 up, 1 wide — which reads as a missing
      value, not as a layout. At 768px that last card was a 704×65 slab holding
      six characters.
      The fix is not a better special case, it is removing the concept of a
      leftover: `grid-auto-flow: column` gives the row EXACTLY as many equal
      columns as there are facts. Two, three or four, it is symmetric by
      construction and nothing has to know the count.
      ⚠️ THIS IS LOAD-BEARING RIGHT NOW. `weight_g` was blanked in
         `addon-catalog` the same day (the supplier never gave a figure), so
         this grid runs at TWO facts today and returns to three the day a real
         measurement arrives. Verified at 2 · 3 · 4 across 360–1280: equal
         widths, equal heights, zero overflow.

   2. THE CARDS WERE EMPTY RECTANGLES. `--color-cream-mid` (#F2EDE4) on the
      page's #FDF3E6 is a 1.06:1 surface difference — not a card, a smudge —
      and each one wrapped ~60px of text in ~170px of air, so what the eye got
      first was the box and not the fact. Boxes gone. The facts are now divided
      by hairlines and closed by the price's own rule below, which makes the
      band read as one measurement strip and removes three borders from a
      column that already carries claim chips above and trust cards below.
      Deliberately NOT the chips' treatment: claims are the supplier's words,
      these are measurements, and keeping the two treatments apart is what
      stops a fourth claim joining the set by being typed into it.

   3. THE LABELS FAILED CONTRAST, WHICH IS WHY THEY READ AS "WEAK". 11px
      `--color-text-light` (#9BA890) on #FDF3E6 measures **2.28:1** — a WCAG
      failure at any size, not a matter of taste. `--color-olive` measures
      **7.2:1**, and the value in dark green **11.78:1**. The hierarchy is now
      carried by size and weight (11.5/700 vs 15.5/800) instead of by fading
      the label until it disappears. */
.apx-specs {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  margin-block: 2px 20px;
  padding-block-start: 15px;
  border-block-start: 1px solid var(--color-border);
}
.apx-spec { padding-inline: clamp(10px, 3.2vw, 15px); }
/* the outer edges stay flush with the column, so the strip lines up with the
   price and the heading rather than floating inside invisible padding */
.apx-spec:first-child { padding-inline-start: 0; }
.apx-spec:last-child { padding-inline-end: 0; }
.apx-spec + .apx-spec { border-inline-start: 1px solid var(--color-border); }
.apx-spec dt {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--color-olive);
  margin-block-end: 4px;
}
.apx-spec dd {
  margin: 0;
  font-size: 15.5px;
  font-weight: 800;
  line-height: 1.3;
  color: var(--color-dark-green);
}

/* ── 📱 BELOW 560px THE STRIP IS NOT A STRIP — added 26.08 ─────────────────
   🔴 CEO on the collar page: *"בטלפון זה מבולגן, שים לב."* He is describing a
   real geometry failure and not a taste, and it was measured on the render at
   390px before anything was changed:

       strip 350px ÷ 4 cells = 88px each, ~67px of usable text column
         חומר         ניילון ארוג · ריפוד ניאופרן   → 3 lines
         תא למאתר     עד 32 × 8 מ״מ                → 2 lines, FIVE bidi
                                                     fragments: `עד 32 ×` /
                                                     `8 מ״מ`
         פסים בצדדים  מחזירי אור                    → label 2 lines, value 2
         היקף צוואר   30–55 ס״מ                     → `30–55` / `ס״מ`

   Four ragged columns of unequal height, every value broken mid-phrase. The
   worst of it is bidi: a digit run, a neutral and a second digit run is the
   shape that has already rendered 8×9.5 as 9.5×8 on this site, and a 67px
   column forces it to break at exactly the neutral.

   🔑 THE CAUSE IS ONE DECLARATION, NOT THE CONTENT. `grid-auto-flow: column`
      has no lower bound: the component makes N columns at 1440px and N columns
      at 320px, and each new spec row makes every cell narrower on the smallest
      screen. Deleting a row (which was also done) buys one breakpoint and
      leaves the next person the same bug.
   ⚙️ SO ON A PHONE IT BECOMES WHAT A SPEC SHEET IS ON A PHONE: one row per
      fact, label at the start, value at the end, hairline between. Every value
      fits on ONE line at 320 · 375 · 390 · 430 · 560 · 700 (measured, all six).
   📏 AND IT COSTS HEIGHT — SAY SO RATHER THAN CLAIM THE OPPOSITE. Measured at
      390px: the broken strip was **99px**, the stacked list is **121px**, so
      this is **+22px**, not a saving. The first draft of this comment claimed
      the block came out shorter; it does not, and an unmeasured boast in a
      comment is how a false fact gets laundered into the next person's
      reasoning. The trade is 22px of a scrolling column for three values that
      can be read at a glance, and the CEO's "עמוסה" is answered by the CELL
      COUNT (four → three, the bay row removed) rather than by the pixel count.
   ⚖️ IT WAS SCOPED TO THE COLLAR FOR ONE DAY, AS A DELIBERATE COMPROMISE, AND
      🔓 THE PENDANT JOINED IT ON 26.08. The note that stood here said the bug is
      the COMPONENT'S, that the pendant is the worst instance on the site, and
      that the only reason it was held back was that the pendant page was under
      CEO review and changing what he is looking at mid-review is its own kind of
      bug. He then reviewed it and reported this exact strip: *"מבולגן לי מדי מלא
      מילים שווה לסדר."*
      📐 MEASURED ON THE PUSHED THEME AT 390px BEFORE THE CHANGE — three cells of
         **117px**, i.e. ~90px of usable text column:
             חומר    פלדת אל־חלד 304                       → 2 lines
             חריטה   תמונה בצד אחד · שם בצד השני, או גב חלק → **4 lines**
             קוטר    20 מ״מ                                 → 1 line
         Three ragged columns of 1, 2 and 4 lines. The `חריטה` cell is a SENTENCE
         in a value grid — flagged as debt on 26.08 when the `חומר` column was
         filled, and left standing; this is that debt paid.
      ⚙️ TWO HALVES, AND THE LAYOUT IS THE BIG ONE. The value was also shortened
         (`תמונה מלפנים, שם מאחור — או גב חלק`, 38 → 34 chars) but four characters
         do not turn four lines into one in a 117px column — the STACK does. The
         cell is now ~294px wide and every value fits on one line.
      ⚠️ THE COLLAR IS UNTOUCHED BY THIS EDIT. The pendant is ADDED to each
         selector rather than the attribute being dropped, which was the promotion
         path this note originally proposed: dropping it would also silently
         restyle the hat, the ball and the rope, none of which has been measured
         at 700px since. Two named products, two measured products. The rope and
         the ball join the day somebody measures them. */
@media (max-width: 700px) {
  [data-apx-kind="collar"] .apx-specs,
  [data-apx-kind="pendant"] .apx-specs {
    grid-auto-flow: row;
    grid-auto-columns: auto;
    margin-block: 2px 18px;
    padding-block-start: 11px;
  }
  [data-apx-kind="collar"] .apx-spec,
  [data-apx-kind="pendant"] .apx-spec {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    column-gap: 14px;
    padding-inline: 0;
    padding-block: 8px;
  }
  /* the divider turns 90°: it was between columns, it is now between rows */
  [data-apx-kind="collar"] .apx-spec + .apx-spec,
  [data-apx-kind="pendant"] .apx-spec + .apx-spec {
    border-inline-start: none;
    border-block-start: 1px solid var(--color-border);
  }
  [data-apx-kind="collar"] .apx-spec dt,
  [data-apx-kind="pendant"] .apx-spec dt { margin-block-end: 0; font-size: 12.5px; }
  /* `text-align: end` keeps the values on one optical column against the
     price and the button below, so the block still reads as a measurement
     strip rather than as a paragraph of pairs. */
  [data-apx-kind="collar"] .apx-spec dd,
  [data-apx-kind="pendant"] .apx-spec dd { font-size: 15px; text-align: end; }
}

/* ── price, against the button ─────────────────────────────────────────────
   🔑 MOVED DOWN HERE (CEO, 18.08) — the price belongs beside the decision to
   pay it, which is also how the bottle page reads. */
.apx-pricebox {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px 12px;
  padding-block: 18px 16px;
  margin-block-start: 4px;
  border-block-start: 1px solid var(--color-border);
}
.apx-price__num {
  font-size: 36px;
  font-weight: 900;
  line-height: 1;
  color: var(--color-dark-green);
  letter-spacing: -0.03em;
}
.apx-bundle {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 14px;
  border-radius: var(--r-full);
  background: var(--color-gold-pale);
  color: #7E6430;
  font-size: 13px;
  line-height: 1.4;
  max-width: 100%;
}
.apx-bundle svg { flex-shrink: 0; color: var(--color-gold); }
.apx-bundle strong { font-weight: 800; }
/* the planned-bundle variant reads as an intention, never as a live price */
.apx-bundle--planned { background: var(--color-cream-dark); color: var(--color-text-muted); }
.apx-bundle--planned em { font-style: normal; font-weight: 700; color: var(--color-text-light); }

/* ── buy ───────────────────────────────────────────────────────────────────
   Dark green, not gold. The bottle page's `.btn--primary` is gold because it
   sits on cream in a section with no other dark mass; here the button lives
   directly under a dark-green price and above a dark-green benefit band, and a
   gold pill between them fights both. Same brand, one level up the hierarchy. */
.apx-atc {
  width: 100%;
  min-height: 58px;
  padding: 16px 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  border: none;
  border-radius: var(--r-full);
  background: var(--color-dark-green);
  color: var(--color-white);
  font-family: var(--font);
  font-size: 17px;
  font-weight: 800;
  line-height: 1.2;
  cursor: pointer;
  box-shadow: var(--shadow-md);
  transition: background var(--t-med) var(--ease-out), transform var(--t-med) var(--ease-out), box-shadow var(--t-med) var(--ease-out);
  /* deliberately NOT white-space:nowrap — that is the documented cause of the
     site's sub-430px horizontal-overflow bug on long Hebrew CTAs */
}
.apx-atc__cart { flex: 0 0 auto; opacity: 0.85; }
.apx-atc:hover {
  background: var(--color-olive);
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg);
}
.apx-atc:active { transform: translateY(0); }
/* the button states what is missing rather than silently accepting a garment
   with no size — the commonest cause of a return */
/* `data-apx-atc-state`, not `data-apx-atc` — the buy buttons themselves carry
   `data-apx-atc`, and a root flag sharing a marker's name makes every
   document-level query return the page as one of its own controls. */
/* ⚠️ The STICKY button's copy of this rule is NOT here any more (19.08). It now
   wears `.sticky-cart-bar__btn`, whose gold is `!important`, so its needs-size
   and blocked states live with the other sticky deltas at the foot of this file
   and carry `!important` themselves. Keeping a powerless duplicate here would
   read as coverage that does not exist. */
.apx[data-apx-atc-state='needs-size'] .apx-atc { background: var(--color-olive); }

/* the Shopify product form is a pure wrapper around the buy button — it must
   contribute no box of its own, or the button inherits a margin nobody wrote */
.apx-form { margin: 0; display: block; }

/* ── refused, and saying so ────────────────────────────────────────────────
   A combination the store does not hold, or holds and has sold out. The button
   is NOT `disabled`: a disabled control cannot be focused, announces nothing to
   a screen reader, and hides the very sentence it is refusing with. It stays
   reachable, states the reason on its own face, and declines on click. Muted
   rather than red — this is a fact about stock, not an error the customer made. */
   ⚠️ The sticky button's blocked state is with the sticky deltas at the foot of
   this file, for the `!important` reason noted there — not duplicated here. */
.apx-atc.is-blocked {
  background: var(--color-cream-dark);
  color: var(--color-text-muted);
  box-shadow: none;
  cursor: not-allowed;
}
.apx-atc.is-blocked:hover {
  background: var(--color-cream-dark);
  transform: none;
  box-shadow: none;
}
.apx-atc.is-blocked .apx-atc__cart { display: none; }

.apx-atc__msg {
  margin-block-start: 12px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--color-text-muted);
  text-align: center;
  animation: apx-fade-up 0.34s var(--ease-out);
}
.apx-atc__msg[hidden] { display: none; }

/* ── the trust block ───────────────────────────────────────────────────────
   🔑 IT REUSES THE BOTTLE PAGE'S OWN CLASSES — `.bundle__perk`,
   `.fp-trust-badges`, `.fp-trust-badge` — which live in the global
   `assets/theme.css`. That is deliberate: an imitation drifts the first time
   someone restyles the bottle page, whereas sharing the class means the two
   surfaces cannot diverge. The rules below are the two spacing corrections
   this page's context needs, and nothing else. */
/* 🔴 `.apx-buy .bundle__perk`, NOT `.apx-perk` (19.08). The shipping line is
   `{% render 'bundle-shipping-note' %}` now — the bottle page's own snippet,
   rendered rather than retyped, so the store states one shipping promise in one
   place. That snippet emits `.bundle__perk` and nothing else, and it has to stay
   that way: adding a per-page hook to shared markup is how shared markup quietly
   stops being shared. So the spacing is asked for by CONTEXT (this buy box)
   rather than by a class the snippet would have to carry for us. */
.apx-buy .bundle__perk { margin-block-start: 18px; }
.apx-badges { margin-block-start: 16px; }
.apx-trust__note {
  margin: 10px 0 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--color-text-light);
  text-align: center;
}

/* ══ 2 · THE BENEFIT BAND (hat) ════════════════════════════════════════════
   THE PAGE'S DARK MOMENT, and the reason the rest of it can stay calm. A page
   of unbroken cream has no rhythm; the bottle page earns its scroll with
   exactly this alternation, and the aura behind the cards is the same device
   as `.bowl-demo__aura` there. It also replaces the removed safety block as
   the page's one full-bleed dark section, so the scroll silhouette did not
   change when that block moved into the FAQ.
   Four cards, four approved lines, each describing something you can SEE in
   the photos above — no material, no protection claim, no UV. */
.apx-ben {
  position: relative;
  overflow: hidden;
  padding-block: clamp(46px, 6vw, 78px);
  background: var(--color-dark-green);
  color: var(--color-white);
}
.apx-ben__glow {
  position: absolute;
  inset-block-start: -30%;
  inset-inline-start: 50%;
  width: min(1000px, 130%);
  aspect-ratio: 1;
  transform: translateX(50%);
  background: radial-gradient(circle, rgba(184, 148, 78, 0.20) 0%, rgba(184, 148, 78, 0) 62%);
  pointer-events: none;
}
.apx-ben__head { position: relative; margin-block-end: 26px; }
@media (min-width: 760px) {
  .apx-ben__head { text-align: center; margin-block-end: 40px; }
  .apx-ben__head .apx-kicker { justify-content: center; }
  /* wide enough to let `text-wrap: balance` choose the break; 22ch forced
     `כלב, לא בשביל תמונה` onto its own line and split the sentence at the
     comma, which is the one place it must not break */
  .apx-ben__title { max-width: 30ch; margin-inline: auto; }
}
.apx-ben__title {
  margin-block: 12px 0;
  font-size: clamp(25px, 4vw, 38px);
  font-weight: 800;
  line-height: 1.16;
  letter-spacing: -0.02em;
  color: var(--color-white);
  text-wrap: balance;
}
.apx-ben__grid {
  position: relative;
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
}
@media (min-width: 560px) { .apx-ben__grid { grid-template-columns: 1fr 1fr; gap: 14px; } }
@media (min-width: 1000px) { .apx-ben__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; } }
/* 🔴 BELOW 1000px THE CARD IS A ROW, NOT A CARD. Measured at 390px: four
   stacked vertical cards ran 1,360px — a screen and a half of scroll for four
   short lines, which turns the page's one dark moment into a corridor. Chip
   beside the text drops each to ~110px and the whole band to ~500px, and it is
   also the shape the bottle page's own `.benefit-item` uses. The vertical card
   returns at ≥1000px, where four of them fit one row and the band is 300px. */
.apx-bencard {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  column-gap: 14px;
  padding: 16px 16px 17px;
  border-radius: 20px;
  background: rgba(255, 255, 255, 0.045);
  border: 1px solid rgba(255, 255, 255, 0.09);
  /* `opacity` is named because this element also carries `.reveal` — see the
     note near the bottom of this file */
  transition: opacity var(--t-slow) var(--ease-out), transform var(--t-med) var(--ease-out), border-color var(--t-med) var(--ease-out), background var(--t-med) var(--ease-out);
}
.apx-bencard:hover {
  transform: translateY(-4px);
  background: rgba(255, 255, 255, 0.07);
  border-color: rgba(212, 172, 106, 0.45);
}
.apx-bencard__chip {
  grid-row: 1 / span 2;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: rgba(212, 172, 106, 0.15);
  border: 1px solid rgba(212, 172, 106, 0.24);
  font-size: 22px;
  line-height: 1;
  transition: transform var(--t-med) var(--ease-out);
}
.apx-bencard:hover .apx-bencard__chip { transform: scale(1.08) rotate(-4deg); }
.apx-bencard__title {
  grid-column: 2;
  align-self: center;
  min-height: 44px;
  display: flex;
  align-items: center;
  font-size: 16.5px;
  font-weight: 800;
  line-height: 1.25;
  color: var(--color-white);
}
.apx-bencard__body {
  grid-column: 2;
  margin-block-start: 4px;
  font-size: 13.5px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.66);
  text-wrap: pretty;
}
@media (min-width: 1000px) {
  .apx-bencard { display: block; padding: 24px 22px 26px; }
  .apx-bencard__title { display: block; min-height: 0; margin-block: 16px 7px; font-size: 17px; }
  .apx-bencard__body { margin-block-start: 0; }
}

/* ══ 2b · THE FEED ═════════════════════════════════════════════════════════
   🔴 MOVED OUT, 19.08 → `assets/apx-feed.css`. The BOTTLE product page renders
   the same lifestyle band now — the CEO asked for its rhythm by name — and a
   component two pages render cannot live inside one page's stylesheet. Loaded
   by `snippets/apx-feed.liquid`, which both surfaces render.
   The three reduced-motion rules for `.apx-feed__*` went with it, because this
   file's `@media (prefers-reduced-motion)` block is scoped to `.apx` and would
   have stopped at the edge of these pages.
   ══════════════════════════════════════════════════════════════════════════ */

/* ══ 3 · SCALE (balls only) ════════════════════════════════════════════════
   Every frame is the same width and every photo is calibrated to the same
   120mm canvas, so equal frames ARE equal scale. The dimension line is drawn
   at real_mm/120 of the frame from `--apx-mm`, so it lands on the product's
   real edges instead of being positioned by eye. */
.apx-scale {
  padding-block: 56px;
  background: var(--color-cream-mid);
  border-block: 1px solid var(--color-border);
}
.apx-scale__head { max-width: 52ch; margin-block-end: 34px; }
.apx-scale__title {
  font-size: clamp(23px, 3.4vw, 32px);
  font-weight: 800;
  line-height: 1.2;
  color: var(--color-dark-green);
  margin-block: 10px 0;
}
.apx-scale__lede {
  margin-block-start: 12px;
  font-size: 15px;
  line-height: 1.7;
  color: var(--color-text-muted);
}
.apx-scale__row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(10px, 3vw, 34px);
  align-items: end;
}
.apx-scale__item { margin: 0; }
.apx-scale__frame {
  position: relative;
  aspect-ratio: 1 / 1;
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--r-md);
  transition: box-shadow var(--t-med) var(--ease-out);
}
.apx-scale__item.is-current .apx-scale__frame {
  border-color: var(--color-gold);
  box-shadow: var(--shadow-sm), 0 0 0 3px var(--color-gold-pale);
}
.apx-scale__frame img { width: 100%; height: 100%; object-fit: contain; border-radius: var(--r-md); }
.apx-scale__dim {
  position: absolute;
  inset-block-end: 9px;
  inset-inline-start: 50%;
  transform: translateX(50%);
  width: calc(var(--apx-mm) / 120 * 100%);
  height: 1px;
  background: var(--color-gold);
}
.apx-scale__dim::before,
.apx-scale__dim::after {
  content: '';
  position: absolute;
  inset-block-start: -4px;
  width: 1px;
  height: 9px;
  background: var(--color-gold);
}
.apx-scale__dim::before { inset-inline-start: 0; }
.apx-scale__dim::after { inset-inline-end: 0; }
.apx-scale__item figcaption {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-block-start: 12px;
  text-align: center;
}
.apx-scale__name {
  font-size: clamp(12px, 2.4vw, 14px);
  font-weight: 700;
  line-height: 1.3;
  color: var(--color-dark-green);
}
.apx-scale__mm { font-size: clamp(11px, 2.2vw, 12.5px); font-weight: 600; color: var(--color-gold); }
.apx-scale__foot {
  margin-block-start: 28px;
  padding-block-start: 20px;
  border-block-start: 1px solid var(--color-border);
  font-size: 14px;
  line-height: 1.65;
  color: var(--color-text-muted);
}

/* the hand shot: apart from the three calibrated frames, because it is the
   intuition and they are the proof. Capped narrow and centred so it reads as a
   companion to the row rather than a fourth item IN it — a fourth frame would
   invite exactly the comparison it is not calibrated for. */
.apx-scale__hand {
  margin: 22px auto 0;
  max-width: 340px;
  text-align: center;
}
.apx-scale__hand img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 20px;
  box-shadow: var(--shadow-sm);
}
.apx-scale__hand figcaption {
  margin-block-start: 8px;
  font-size: 12.5px;
  color: var(--color-text-muted);
}

/* ── 3a · THE FIT CARD (balls) — "is that size right for MY dog?" ──────────
   Added 20.08. It is the second half of the section's own question, so it sits
   inside this band and not in the safety block: at this point in the page the
   customer is already thinking about size, and an answer arriving where the
   question is asked is help. The safety band gets ONE line naming the mechanism.
   🔴 SO IT IS DESIGNED AS CONTENT, NOT AS A DISCLAIMER — and every choice here
   is that decision: the calm white card the rest of the page uses for good news,
   the same radius as the hand shot, no shield, no red, no icon at all. A warning
   dressed as a warning in the middle of a buy page is exactly what drives a
   hesitant buyer away, and the CEO asked for the opposite.
   ⛔ GOLD IS DECORATION HERE, NEVER TEXT. `--color-gold` on white measures
      2.84:1 — a WCAG failure at this size, and the same trap that made the spec
      grid's labels unreadable on 18.08. The gold carries the markers; every word
      is dark-green or muted body colour.
   ⛔ AND NO 1.06:1 PANELS. The obvious grouping for the ten-second check is a
      cream sub-panel; `--color-cream-mid` on `--color-white` is ~1.09:1, i.e. an
      invisible box that only adds edges. Hairlines divide this card instead. */
.apx-fit {
  max-width: 720px;
  margin: 26px auto 0;
  padding: clamp(18px, 4vw, 26px);
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
}
.apx-fit__rule {
  font-size: clamp(14px, 2.9vw, 15.5px);
  line-height: 1.7;
  color: var(--color-text-muted);
}
/* the approved copy opens with a bolded rule, and that sentence IS this card's
   heading — promoting it to a block is how it gets heading weight without
   anybody inventing a heading to put above approved copy */
.apx-fit__rule strong {
  display: block;
  margin-block-end: 7px;
  /* balance, not pretty: this is a two-clause statement that becomes two lines on
     a phone, and `balance` is the one that refuses to leave "לפה הכלב." alone on
     line 2. It only applies because the strong is a BLOCK here (see above) */
  text-wrap: balance;
  font-size: clamp(16px, 3.4vw, 18.5px);
  font-weight: 800;
  line-height: 1.35;
  color: var(--color-dark-green);
}
.apx-fit__lead {
  margin-block-start: 18px;
  padding-block-start: 18px;
  border-block-start: 1px solid var(--color-border);
  font-size: clamp(14.5px, 3vw, 15.5px);
  color: var(--color-dark-green);
}
.apx-fit__lead strong { font-weight: 700; }
.apx-fit__list {
  margin-block-start: 10px;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 9px;
}
.apx-fit__list li {
  position: relative;
  padding-inline-start: 22px;
  font-size: clamp(14px, 2.9vw, 15.5px);
  line-height: 1.65;
  color: var(--color-text-muted);
}
/* a neutral dot, deliberately not a checkmark: both rows describe a MISFIT, and
   a tick beside "it is too small for him" reads as approval of the hazard */
.apx-fit__list li::before {
  content: '';
  position: absolute;
  inset-inline-start: 5px;
  inset-block-start: .62em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--color-gold);
}
.apx-fit__tip {
  margin-block-start: 12px;
  font-size: clamp(13.5px, 2.8vw, 14.5px);
  line-height: 1.65;
  color: var(--color-text-muted);
  /* measured at 390px: the chase line ended with "באוויר." alone on its own row.
     A one-word last line reads as a mistake in a card whose whole job is to look
     considered — and this is the line a customer must actually act on */
  text-wrap: pretty;
}
/* the hairline belongs to the FIRST tip, and it is asked for by its position
   after the list rather than by :first-of-type — `.apx-fit__rule` is also a <p>,
   so :first-of-type would put the rule under a line that separates nothing */
.apx-fit__list + .apx-fit__tip {
  margin-block-start: 18px;
  padding-block-start: 18px;
  border-block-start: 1px solid var(--color-border);
}
.apx-fit__tip strong { font-weight: 700; color: var(--color-dark-green); }

/* ══ 3b · WHAT ₪49 BUYS (balls) ════════════════════════════════════════════
   🔁 THIS BAND WAS BUILT AROUND A 453px FILE AND NO LONGER IS. The only play
   photo we owned was small, so the layout's job was to stop it being stretched:
   a narrow column and a hard cap at its own pixel width. A real 1000px
   photograph arrived on 18.08, the cap moved with it, and the two columns are
   now equal — the picture can carry its half of the band.
   🔑 The rule that survives the change: the cap is always the FILE's true width,
   never a design number. An upscaled photo would undo in one glance the
   credibility the calibrated product shots earn two sections away.
   Cream, not dark — the dark band belongs to the safety section below, and two
   dark bands in a row would flatten the page's rhythm exactly where it should
   be warmest. */
.apx-play {
  padding-block: clamp(34px, 5vw, 56px);
  background: var(--color-cream-mid);
}
.apx-play__inner {
  display: grid;
  gap: 22px;
  align-items: center;
}
@media (min-width: 820px) {
  .apx-play__inner { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 44px; }
}
.apx-play__shot { margin: 0; }
.apx-play__shot img {
  display: block;
  width: 100%;
  /* 🔁 was 453px, the old corgi file's true width. The photograph here is 1000px
     and the cap moves with it — a constraint written for one file has to be
     re-derived when the file changes, or it silently keeps shrinking a photo
     that no longer needs protecting. */
  max-width: 1000px;
  height: auto;
  border-radius: 20px;
  box-shadow: var(--shadow-sm);
}
.apx-play__shot figcaption {
  margin-block-start: 8px;
  font-size: 12.5px;
  color: var(--color-text-muted);
}
.apx-play__title {
  margin-block: 10px 0;
  font-size: clamp(24px, 3.4vw, 34px);
  font-weight: 800;
  line-height: 1.18;
  letter-spacing: -0.02em;
  color: var(--color-dark-green);
  text-wrap: balance;
}
.apx-play__lede {
  margin-block-start: 12px;
  font-size: 16px;
  line-height: 1.65;
  color: var(--color-text);
  text-wrap: pretty;
}
.apx-play__note {
  margin-block-start: 14px;
  font-size: 14.5px;
  line-height: 1.7;
  color: var(--color-text-muted);
  text-wrap: pretty;
}
.apx-play__note strong { color: var(--color-dark-green); }

/* ══ 4 · SAFETY (balls only) ═══════════════════════════════════════════════
   Deliberately not fine print. A brand that talks about trust prints these at
   full size, on its own band, with confidence. The HAT no longer shows this
   band — the same five lines render as one FAQ answer, from the same data. */
.apx-safety {
  padding-block: clamp(34px, 5vw, 52px);
  background: var(--color-dark-green);
  color: var(--color-white);
}
.apx-safety__inner { display: grid; gap: 26px; }
@media (min-width: 860px) {
  .apx-safety__inner { grid-template-columns: minmax(0, 0.7fr) minmax(0, 1.3fr); gap: 48px; }
}
.apx-safety__aside svg { color: var(--color-gold-light); }
.apx-safety__title {
  margin-block: 12px 8px;
  font-size: clamp(21px, 3vw, 28px);
  font-weight: 800;
  line-height: 1.2;
  color: var(--color-white);
}
.apx-safety__sub { font-size: 13.5px; line-height: 1.6; color: rgba(255, 255, 255, 0.62); }
.apx-safety__list { display: grid; gap: 1px; align-content: start; }
.apx-safety__list li {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding-block: 13px;
  list-style: none;
  font-size: 15px;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.93);
  border-block-end: 1px solid rgba(255, 255, 255, 0.11);
}
.apx-safety__list li:last-child { border-block-end: none; }
.apx-safety__list li::before {
  content: '';
  flex-shrink: 0;
  width: 7px;
  height: 7px;
  margin-block-start: 8px;
  border-radius: 50%;
  background: var(--color-gold-light);
}

/* ══ 5 · SHIPPING & RETURNS ════════════════════════════════════════════════
   Same white-card-on-cream language as the bottle page's own bands. Seven
   facts, every one copied from the shipping knowledge base or the store's
   published policy pages — and the two delivery figures are never summed. */
.apx-logi { padding-block: clamp(44px, 5vw, 68px); background: var(--color-bg); }
.apx-logi__head { margin-block-end: 24px; }
@media (min-width: 760px) {
  .apx-logi__head { text-align: center; }
  .apx-logi__head .apx-kicker { justify-content: center; }
}
.apx-logi__title {
  margin: 12px 0 0;
  font-size: clamp(24px, 3.6vw, 34px);
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.18;
  color: var(--color-dark-green);
}
.apx-logi__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
  margin: 0;
}
@media (min-width: 760px) { .apx-logi__grid { grid-template-columns: 1fr 1fr; gap: 12px; } }
.apx-logi__item {
  margin: 0;
  padding: 18px 20px;
  background: var(--color-white);
  border: 1px solid rgba(43, 52, 40, 0.08);
  border-radius: var(--r-md);
  box-shadow: 0 4px 20px rgba(43, 52, 40, 0.02);
  transition: transform var(--t-med) var(--ease-out), box-shadow var(--t-med) var(--ease-out), border-color var(--t-med) var(--ease-out);
}
.apx-logi__item:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 28px rgba(43, 52, 40, 0.06);
  border-color: rgba(184, 148, 78, 0.35);
}
.apx-logi__item dt {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 14.5px;
  font-weight: 800;
  color: var(--color-dark-green);
}
.apx-logi__item dt svg { color: var(--color-gold); flex: 0 0 auto; }
.apx-logi__item dd {
  margin: 7px 0 0;
  padding-inline-start: 26px;
  font-size: 13.5px;
  line-height: 1.65;
  color: var(--color-text-muted);
  text-wrap: pretty;
}
/* The hygiene rule decides the purchase on a worn item — it is not fine print.
   It is also the 7th of 7 cells, so in a two-column grid it would leave a hole
   beside it that reads as a missing card. Spanning it is both the honest
   emphasis and the tidy row. */
.apx-logi__item--hygiene { background: var(--color-gold-pale); border-color: rgba(184, 148, 78, 0.3); }
.apx-logi__item--hygiene dt svg { color: var(--color-olive); }
.apx-logi__item--hygiene dd { color: #7E6430; }
@media (min-width: 760px) { .apx-logi__item--hygiene { grid-column: 1 / -1; } }
/* 🐛 A ROW OF PILLS, NOT A SENTENCE OF LINKS. Measured at 390px: three
   underlined links in one paragraph, each padded to a 44px tap target, wrapped
   into a ladder — the underlines detached from their words and one link sat
   alone on a line looking like a heading. Padding a link to 44px inside running
   text is the technique that fails first when the text wraps; a pill carries
   its own box, so wrapping puts a whole target on the next line instead of
   half of one. */
.apx-logi__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 20px 0 0;
  font-size: 12.5px;
  color: var(--color-text-light);
}
.apx-logi__foot a {
  display: inline-flex;
  align-items: center;
  /* 44, not 40. The QC gate failed this at all six widths and it was right to:
     these are the page's only route to the full policy text, and my own ruling
     (18.08) is that a CONTROL gets 44×44 while inline navigational text gets
     24×24. Turning the links into pills made them controls — the standard came
     with the shape. */
  min-height: 44px;
  padding-inline: 15px;
  border-radius: var(--r-full);
  border: 1px solid var(--color-border);
  background: var(--color-white);
  color: var(--color-olive);
  font-weight: 700;
  text-decoration: none;
  transition: color var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out);
}
.apx-logi__foot-label { color: var(--color-text-light); }
.apx-logi__foot a:hover, .apx-logi__foot a:focus-visible {
  color: var(--color-gold);
  border-color: var(--color-gold);
}

/* ══ 6 · FAQ ═══════════════════════════════════════════════════════════════
   Rebuilt in the bottle page's FAQ language (CEO: "מעוצב יפה כמו בבקבוק"):
   white cards on cream, a gold-pale chip, a chevron in a circle that fills and
   turns. The difference from that one is the element: native `<details>`, so
   it opens with ZERO JavaScript and works before hydration and inside a
   `no-js` render. That is why the body animates in rather than sliding a
   max-height — a height transition needs JS to own the open state, and the
   trade (an instant height, a 0.32s fade-and-rise of the text) buys a
   component with no script at all. */
.apx-faq {
  padding-block: clamp(46px, 5.5vw, 76px);
  background: var(--color-cream-mid);
  border-block-start: 1px solid var(--color-border);
}
.apx-faq__head { margin-block-end: 26px; }
@media (min-width: 760px) {
  .apx-faq__head { text-align: center; margin-block-end: 38px; }
  .apx-faq__head .apx-kicker { justify-content: center; }
}
.apx-faq__title {
  margin-block: 12px 0;
  font-size: clamp(24px, 3.6vw, 34px);
  font-weight: 800;
  line-height: 1.18;
  letter-spacing: -0.02em;
  color: var(--color-dark-green);
}
.apx-faq__list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 800px;
  margin-inline: auto;
}
.apx-qa {
  background: var(--color-white);
  border: 1px solid rgba(43, 52, 40, 0.08);
  border-radius: var(--r-md);
  box-shadow: 0 4px 20px rgba(43, 52, 40, 0.02);
  /* `opacity` — this element also carries `.reveal` */
  transition: opacity var(--t-slow) var(--ease-out), transform var(--t-med) var(--ease-out), box-shadow var(--t-med) var(--ease-out), border-color var(--t-med) var(--ease-out);
}
.apx-qa:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 28px rgba(43, 52, 40, 0.06);
  border-color: rgba(184, 148, 78, 0.35);
}
.apx-qa[open] {
  border-color: var(--color-gold);
  box-shadow: 0 16px 36px rgba(43, 52, 40, 0.08);
}
.apx-qa summary {
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 16px 18px;
  min-height: 56px;
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.apx-qa summary::-webkit-details-marker { display: none; }
.apx-qa__chip {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: rgba(184, 148, 78, 0.09);
  font-size: 17px;
  line-height: 1;
  transition: background var(--t-med) var(--ease-out), transform var(--t-med) var(--ease-out);
}
.apx-qa[open] .apx-qa__chip { background: var(--color-gold-pale); transform: scale(1.06); }
.apx-qa__q {
  flex: 1 1 auto;
  font-size: 15.5px;
  font-weight: 700;
  line-height: 1.4;
  color: var(--color-dark-green);
  text-wrap: pretty;
}
/* the chevron, drawn — not an icon file and not a glyph, so it inherits the
   gold and rotates on the compositor */
.apx-qa__i {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: rgba(184, 148, 78, 0.08);
  transition: background var(--t-med) var(--ease-out), transform var(--t-med) var(--ease-out);
}
/* 🐛 PHYSICAL BORDERS HERE, AND IT IS NOT A SLIP — measured 18.08 at 390px.
   Built from `border-inline-end` + `border-block-end` (this file's own rule is
   logical properties everywhere) the corner lands on the LEFT under RTL, and
   the chevron pointed sideways instead of down: a "collapse me" affordance
   reading as "go back".
   A chevron that means DOWN is a direction-NEUTRAL glyph and must not mirror
   with the writing direction — like a play triangle or a spinner. The rule for
   the next one: mirror what follows the reading order, pin what points at the
   page itself. */
.apx-qa__i::after {
  content: '';
  width: 8px;
  height: 8px;
  border-bottom: 2px solid var(--color-gold);
  border-right: 2px solid var(--color-gold);
  transform: rotate(45deg) translate(-1.5px, -1.5px);
  transition: border-color var(--t-med) var(--ease-out);
}
.apx-qa[open] .apx-qa__i { background: var(--color-gold); transform: rotate(180deg); }
.apx-qa[open] .apx-qa__i::after { border-color: var(--color-white); }
.apx-qa__body {
  margin: 0 18px;
  padding-block: 16px 20px;
  border-block-start: 1px solid rgba(43, 52, 40, 0.08);
  font-size: 14.5px;
  line-height: 1.8;
  color: var(--color-text-muted);
  animation: apx-fade-up 0.32s var(--ease-out);
}
.apx-qa__body strong { color: var(--color-dark-green); font-weight: 700; }
.apx-qa__body a { color: var(--color-gold); font-weight: 700; }
/* The seven logistics facts, now living inside two answers instead of a
   section of their own. A definition list, not cards: in an accordion the
   container IS the card, and a card inside a card is how an answer starts
   looking like the section it was meant to replace. */
.apx-qa__facts { display: grid; gap: 14px; margin-block-end: 6px; }
.apx-qa__facts dt { font-size: 13.5px; font-weight: 800; color: var(--color-dark-green); }
.apx-qa__facts dd {
  margin: 3px 0 0;
  font-size: 14px;
  line-height: 1.7;
  color: var(--color-text-muted);
  text-wrap: pretty;
}
/* the hygiene rule decides the purchase on a worn garment — it is not fine
   print, and in a plain list it would read as the seventh of seven */
.apx-qa__fact--key { padding: 12px 14px; border-radius: 12px; background: var(--color-gold-pale); }
.apx-qa__fact--key dd { color: #7E6430; }
.apx-qa__body .apx-inlink + .apx-inlink { margin-inline-start: 18px; }

/* 🔴 THE FIVE WARNINGS, AS AN ANSWER RATHER THAN A BAND (CEO ruling 18.08).
   Rendered at full readable size, not as fine print — what changed is where
   they sit, not whether they are published. */
.apx-qa__warn { display: grid; gap: 9px; margin-block-start: 4px; }
.apx-qa__warn li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 13px;
  border-radius: 10px;
  background: var(--color-cream-mid);
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--color-olive);
  list-style: none;
}
.apx-qa__warn li::before {
  content: '';
  flex: 0 0 auto;
  width: 6px;
  height: 6px;
  margin-block-start: 7px;
  border-radius: 50%;
  background: var(--color-gold);
}

/* ══ 7 · THE REST OF THE STORE ═════════════════════════════════════════════
   🔴 THE BAND'S CSS IS NOT HERE ANY MORE — `assets/cross-sell.css` (19.08).
   107 lines of `.apx-more*` and `.apx-card*` were removed from this file, not
   deleted: the CEO asked for the same band on the BOTTLE page, and the bottle
   page has no business loading this ~99KB stylesheet (gallery, swatches, size
   guide, lightbox) to get one grid of cards. The rules moved out whole and were
   renamed `.xsell*`, because `.apx-` means "the add-on product page" and a
   shared component wearing one page's prefix is how it drifts back apart.
   The band is `{% render 'cross-sell-band' %}`, which loads its own stylesheet.
   ⛔ Do not restyle it from this file. If the add-on pages need the band to look
      different from the bottle page's, that is a modifier on the shared
      component — the divergence this extraction exists to prevent. */

/* ══ THE SIZE GUIDE, AS A MODAL ════════════════════════════════════════════
   CEO ruling 18.08: the old full-page section "just took up space". A native
   `<dialog>` costs the page ZERO height and has to fit ONE SCREEN —
   "תמציתי בהרבה. בלי חפירות."

   🔴 THE TABLE STAYS A TABLE AT 390px, and that is the size decision here.
   The old section collapsed it into stacked cards on a phone; five cards is
   ~440px of scroll and turns a one-screen answer into a document. Three narrow
   columns at 12px fit 310px of inner width (34 + 74 + the rest) and the whole
   modal lands at ~590px on a 390×844 phone — measured, not estimated. It also
   retires the anonymous-table-box bug class the card layout carried.
   ⚠️ Esc, the backdrop, the focus trap and the inertness of the page behind
      are the platform's, not ours. The JS adds only click-outside and the
      body scroll lock. */
.apx-sgm {
  inset: 0;
  margin: auto;
  width: min(560px, calc(100vw - 28px));
  max-width: none;
  max-height: calc(100dvh - 28px);
  padding: 0;
  border: none;
  background: transparent;
  overflow: visible;
  color: var(--color-text);
}
.apx-sgm::backdrop {
  background: rgba(24, 31, 22, 0.55);
  backdrop-filter: blur(3px);
  animation: apx-fade 0.28s var(--ease-out);
}
.apx-sgm__inner {
  max-height: calc(100dvh - 28px);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 18px;
  border-radius: var(--r-lg);
  background: var(--color-bg);
  box-shadow: var(--shadow-lg);
  animation: apx-modal-in 0.34s var(--ease-out);
  text-align: start;
}
@media (min-width: 560px) { .apx-sgm__inner { padding: 24px 26px 26px; } }
@keyframes apx-modal-in {
  from { opacity: 0; transform: translateY(14px) scale(0.975); }
  to   { opacity: 1; transform: none; }
}

.apx-sgm__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-block-end: 14px;
}
.apx-sgm__title {
  font-size: 20px;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--color-dark-green);
}
.apx-sgm__x {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  margin-inline-end: -8px;
  display: grid;
  place-items: center;
  border: none;
  border-radius: 50%;
  background: var(--color-cream-dark);
  color: var(--color-olive);
  cursor: pointer;
  transition: background var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out);
}
.apx-sgm__x:hover { background: var(--color-dark-green); color: var(--color-white); }

/* how to measure — the drawing and the sentence say the same thing, side by
   side, because a definition the customer cannot picture is not a definition */
.apx-sgm__how {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 14px;
  border-radius: var(--r-md);
  background: var(--color-white);
  border: 1px solid var(--color-border);
}
.apx-sgm__fig {
  flex: 0 0 auto;
  width: 78px;
  height: 78px;
  object-fit: contain;
  /* no plate and no radius on purpose: the PNG is keyed to transparency so the
     card's own colour shows through */
}
.apx-sgm__howto {
  font-size: 14px;
  font-weight: 600;
  line-height: 1.6;
  color: var(--color-dark-green);
  text-wrap: pretty;
}

/* the finder — the only thing in this modal that answers a customer rather
   than informing them: it applies the boundary rule so the overlap in the
   chart never lands on them */
.apx-sgm__finder { margin-block-start: 14px; }
.apx-sgm__flabel {
  display: block;
  margin-block-end: 8px;
  font-size: 13px;
  font-weight: 700;
  color: var(--color-dark-green);
}
.apx-sgm__frow { display: flex; gap: 8px; }
.apx-sgm__field { position: relative; flex: 1 1 auto; min-width: 0; }
.apx-sg__input {
  width: 100%;
  min-height: 48px;
  padding-inline: 14px 52px;
  border: 1.5px solid var(--color-border);
  border-radius: 12px;
  background: var(--color-white);
  font-family: var(--font);
  font-size: 16px; /* 16px minimum or iOS zooms the whole page on focus */
  font-weight: 700;
  color: var(--color-dark-green);
}
.apx-sg__input:focus {
  outline: none;
  border-color: var(--color-gold);
  box-shadow: 0 0 0 3px var(--color-gold-pale);
}
.apx-sg__input::-webkit-outer-spin-button,
.apx-sg__input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.apx-sg__input { -moz-appearance: textfield; appearance: textfield; }
.apx-sgm__unit {
  position: absolute;
  inset-inline-end: 14px;
  inset-block-start: 50%;
  transform: translateY(-50%);
  font-size: 13px;
  font-weight: 600;
  color: var(--color-text-light);
  pointer-events: none;
}
.apx-sgm__go {
  flex: 0 0 auto;
  min-height: 48px;
  padding-inline: 22px;
  border: none;
  border-radius: 12px;
  background: var(--color-dark-green);
  color: var(--color-white);
  font-family: var(--font);
  font-size: 14.5px;
  font-weight: 800;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease-out);
}
.apx-sgm__go:hover { background: var(--color-olive); }
.apx-sgm__answer {
  margin-block-start: 9px;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--color-text-muted);
  min-height: 1.55em;
}
.apx-sgm__answer strong { color: var(--color-dark-green); font-weight: 800; }
.apx-sgm__answer--out { color: #8A5A2B; }

.apx-sg__table {
  width: 100%;
  margin-block-start: 16px;
  border-collapse: collapse;
  font-size: 12.5px;
}
@media (min-width: 560px) { .apx-sg__table { font-size: 13.5px; } }
.apx-sg__table th, .apx-sg__table td {
  padding: 9px 6px;
  text-align: start;
  vertical-align: middle;
}
.apx-sg__table thead th {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--color-text-light);
  border-block-end: 1.5px solid var(--color-gold-pale);
  white-space: nowrap;
}
.apx-sg__table tbody tr {
  border-block-end: 1px solid var(--color-border);
  transition: background var(--t-fast) var(--ease-out);
}
.apx-sg__table tbody tr:last-child { border-block-end: none; }
.apx-sg__size {
  width: 1%;
  white-space: nowrap;
  font-size: 14px;
  font-weight: 900;
  color: var(--color-dark-green);
}
.apx-sg__num { white-space: nowrap; font-weight: 700; color: var(--color-dark-green); }
.apx-sg__breeds { color: var(--color-text-muted); line-height: 1.45; }
.apx-sg__table tbody tr.is-match {
  background: var(--color-gold-pale);
  outline: 1.5px solid var(--color-gold);
}
.apx-sg__table tbody tr.is-match .apx-sg__breeds { color: #7E6430; }

/* the one rule that removes the ambiguity, published where the ambiguity is
   visible rather than in a footnote nobody reads */
.apx-sg__rule {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  margin-block-start: 14px;
  padding: 12px 14px;
  border-radius: 12px;
  background: var(--color-white);
  border: 1px solid var(--color-border);
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--color-text-muted);
}
.apx-sg__rule svg { flex-shrink: 0; margin-block-start: 2px; color: var(--color-gold); }
.apx-sg__rule strong { color: var(--color-dark-green); font-weight: 700; }

/* ── the sticky buy bar — DELTAS ONLY ──────────────────────────────────────
   🔴 THE COMPONENT ITSELF IS `.sticky-cart-bar` IN `assets/theme.css` (19.08).
   This file used to hold a whole second sticky: `.apx-sticky` + `__info` +
   `__name` + `__meta` + `__btn`, one full-width strip at every width, and
   `display:none` above 980px. It looked close enough to the bottle's and was
   not the same thing, which is exactly how it went wrong in both directions at
   once — the CEO opened the hat on a phone and saw the treatment the bottle
   reserves for a DESKTOP, and a desktop visitor to the hat saw no sticky at all.
   Deleting those 48 lines is the fix. The bottle's component already
   distinguishes the two: full bar ≥769px, compact floating sticker ≤768px
   (`.sticky-cart-bar__product { display:none }`, transparent wrapper, the
   button alone, 16px off the bottom). Sharing it means the add-on pages inherit
   that distinction and every future change to it, instead of re-deciding it.
   ⛔ Do not re-add a `.apx-sticky` layout rule. If this bar needs to look
      different from the bottle's, that is a change to the bottle's bar, or a
      real modifier on the shared component — not a private copy.

   What stays here is the four things that are genuinely this page's: */

/* 1 · the bar is not in the DOM's flow until the observer has something to
      observe. `.sticky-cart-bar` declares no `display`, so the UA's
      `[hidden]{display:none}` already wins — this is stated rather than relied
      on, because the day someone adds `display:flex` to the shared component,
      every add-on page would ship a bar stuck across the fold. */
.apx-sticky[hidden] { display: none !important; }

/* 2 · the hat cannot be bought without a size, so its sticky button carries the
      same refusal states as the main one. `!important` is not decoration here:
      `.sticky-cart-bar__btn` sets its gold with `!important` (it predates this),
      so a plain declaration would lose to it and the button would stay gold
      while claiming to be blocked. */
.apx[data-apx-atc-state='needs-size'] .sticky-cart-bar__btn {
  background: var(--color-olive) !important;
}
.sticky-cart-bar__btn.is-blocked,
.sticky-cart-bar__btn.is-blocked:hover {
  background: var(--color-cream-dark) !important;
  color: var(--color-text-muted) !important;
  box-shadow: none;
  transform: none;
  cursor: not-allowed;
}

/* 3 · the meta line. The bottle's `__note` is a fixed sentence; here it is the
      live selection (colour · size), so it must not push the price around or
      widen the bar when a long Hebrew colour name is chosen. */
.sticky-cart-bar__note[data-apx-sticky-meta] {
  max-width: 22ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 4 · the add-on thumbnails are square manufacturer photos on a light canvas,
      not the bottle's cut-out, so they want `cover` inside the same 50px box —
      `contain` leaves a visible letterbox on a 4:3-ish frame. */
.apx-sticky .sticky-cart-bar__img { object-fit: cover; }

/* ── lightbox ──────────────────────────────────────────────────────────────
   🔴 MOVED OUT, 19.08 → `assets/apx-gallery.css`, together with the markup,
   which moved from a sibling of `.apx` into the gallery snippet itself. It is
   `position: fixed`, so nesting it inside the component costs nothing and
   stops it being a second thing somebody has to remember to copy. */


/* ── breadcrumb ────────────────────────────────────────────────────────── */
.apx-crumb { margin-block-start: 18px; }
.apx-crumb ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 12.5px;
  color: var(--color-text-light);
}
.apx-crumb li { display: inline-flex; align-items: center; gap: 6px; }
/* the separator points the way reading travels — leftwards in RTL */
.apx-crumb li + li::before {
  content: '';
  width: 5px;
  height: 5px;
  border-block-start: 1.4px solid currentColor;
  border-inline-start: 1.4px solid currentColor;
  transform: rotate(-45deg);
  opacity: 0.55;
}
/* 44px tall (the axis a thumb misses on) and 34px wide. Not padded out to 44
   wide on purpose: at this font size the neighbouring trail items would
   overlap, and a tap that lands on the wrong destination is worse than a
   narrow one. 34 clears WCAG 2.5.8's 24px comfortably. */
.apx-crumb a {
  color: var(--color-text-muted);
  text-decoration: none;
  padding-block: 13px;
  padding-inline: 8px;
  margin-block: -13px;
  margin-inline: -8px;
  transition: color var(--t-fast) var(--ease-out);
}
.apx-crumb a:hover, .apx-crumb a:focus-visible { color: var(--color-gold); }
.apx-crumb [aria-current] { color: var(--color-dark-green); font-weight: 700; }

/* ── inline controls that live inside a line of copy ──────────────────────
   padding-block grows the HIT BOX to 44px and the negative margin gives the
   space back, so the target gets bigger without the line moving. This lives ON
   the base rule on purpose: an earlier `padding-block` rule was silently
   overridden by a later `padding: 0` at the same specificity. */
.apx-linkbtn {
  padding: 12px 0;
  margin-block: -12px;
  background: none;
  border: none;
  font: inherit;
  font-weight: 700;
  color: var(--color-gold);
  cursor: pointer;
  border-block-end: 1px solid currentColor;
}
.apx-inlink {
  display: inline-block;
  padding-block: 12px;
  margin-block: -12px;
  color: var(--color-olive);
  font-weight: 700;
}

/* ── index page (the four previews, one list) ──────────────────────────────
   ⚠️ OPEN FINDING (19.08), NOT A DECISION — THIS WHOLE BLOCK IS DEAD.
   Its only markup was `templates/index.addons.liquid` + `snippets/addon-index.liquid`,
   and both were DELETED on 19.08 (they were an unlinked internal list that still
   claimed "ארבעה מוצרים"). The two `.apx-card__*` overrides below are now worse
   than dead: `.apx-card` itself left this file the same day when the cross-sell
   band became `assets/cross-sell.css`, so they qualify a base rule that no
   longer exists anywhere in the theme.
   It is left in place rather than swept, because deleting CSS is not what the
   CEO asked for this round and a dead rule harms nobody. Recorded here so it is
   removed on purpose next time, not rediscovered as a mystery. */
.apx-index { padding-block: 34px 72px; }
.apx-index__head { max-width: 54ch; margin-block: 26px 30px; }
.apx-index__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(10px, 2.4vw, 20px);
}
@media (min-width: 760px) { .apx-index__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.apx-index__grid .apx-card__name { font-size: clamp(13px, 2.6vw, 15px); }
.apx-index__grid .apx-card__price { font-size: 14px; }
.apx-card__bundle {
  display: block;
  margin-block-start: 2px;
  font-size: 11.5px;
  font-style: normal;
  font-weight: 600;
  color: var(--color-text-light);
}
.apx-index .apx-title { margin-block-start: 12px; }
.apx-index__note {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  margin-block-start: 26px;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--color-text-light);
}

/* ── the picker the buy button just pointed at ─────────────────────────── */
@keyframes apx-flash {
  0%, 100% { box-shadow: 0 0 0 0 rgba(184, 148, 78, 0); }
  30%      { box-shadow: 0 0 0 5px rgba(184, 148, 78, 0.32); }
}
.apx-flash { border-radius: 12px; animation: apx-flash 1.3s var(--ease-out); }

/* ══ LIVING WITH THE GLOBAL `.reveal` SYSTEM ═══════════════════════════════
   The scroll-in on the benefit cards, the FAQ rows and the sibling cards is
   the theme's own `.reveal` + `.delay-N` (observer in `assets/theme.js`) —
   reused rather than re-implemented, because a second scroll observer on the
   same page is two systems that can disagree about what "visible" means.
   Reuse costs two corrections, and both are the same shape: `.reveal`'s rules
   keep matching AFTER the element has arrived.

   1. `.reveal` declares its own `transition`, and my component rules declare
      theirs at equal specificity — the later file wins, so `opacity` fell out
      of the list and the cards would POP in instead of fading. Every component
      below that also carries `.reveal` therefore names `opacity` itself.
   2. 🐛 `.delay-4` is a `transition-delay`, and it is still applied an hour
      later — so hovering the fourth benefit card waited 400ms before anything
      moved. It reads as a dead card, not as a slow one. Zeroing the delay on
      `:hover` (0,3,0 beats 0,2,0) keeps the staggered entrance and gives the
      hover back its instant response. */
.apx .reveal.visible:hover { transition-delay: 0s; }

/* ── shared keyframes ──────────────────────────────────────────────────── */
@keyframes apx-fade-up {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
@keyframes apx-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* ── motion hygiene ────────────────────────────────────────────────────────
   Every animation above is opacity or transform, so collapsing them here is
   complete — there is no layout animation hiding somewhere that survives. */
@media (prefers-reduced-motion: reduce) {
  .apx *, .apx *::before, .apx *::after,
  .apx-sgm, .apx-sgm *, .apx-sgm::backdrop {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
  /* 🔴 `.apx-slide` and `.apx-lb__img` moved to `apx-gallery.css` (19.08). The
     component now declares its OWN reduced-motion contract, because this block
     is scoped to `.apx` and would have stopped at the edge of this page — a
     gallery on the bottle page would have kept animating for a visitor who
     asked the operating system for no motion. A motion contract belongs to the
     component, not to the page that happens to host it. */
  /* 🔴 the `.apx-feed__*` reduced-motion rules moved to `apx-feed.css` with the
     component, for the same reason the gallery's did: this block is scoped to
     `.apx` and a component rendered elsewhere would have kept animating. */
}

/* ── focus visibility — keyboard users must always see where they are ──── */
.apx :focus-visible,
.apx-sgm :focus-visible {
  outline: 2px solid var(--color-gold);
  outline-offset: 2px;
  border-radius: 10px;
}

/* The site header is sticky, so anything the browser scrolls to on its own —
   an anchor target, or a control reached with Tab — lands UNDERNEATH it
   unless it reserves the room. */
.apx [id],
.apx a, .apx button, .apx summary, .apx input {
  scroll-margin-block-start: calc(var(--nav-h) + 20px);
}
/* and the mobile buy bar owns the bottom edge, so nothing scrolls under it */
@media (max-width: 979px) {
  .apx a, .apx button, .apx summary, .apx input { scroll-margin-block-end: 92px; }
}

/* ══ THE WALKING DOG PASSES BEHIND THE BUY BUTTON ════════════════════════
   Added 23.08, after it was seen sitting on top of "הקלידו את השם" at 1485px.

   🔴 THE DOG IS NOT DELETED FROM THIS PAGE, AND THAT IS A RULING, NOT A
   PREFERENCE. CEO, 19.08: "הכלב המטייל לא מופיע בעמודים החדשים" — he asked for
   it back after a previous fix removed it, and the note in `theme.liquid`
   records why the removal was wrong: the popup and the pet are not one problem
   in two hats. The popup is a modal over the one decision these pages exist
   for. The pet is 90×80px of the brand's own signature.
   ⛔ So "turn the pet off on the pendant page" is not available. What was
      actually reported is narrower and entirely fixable: an OCCLUDED CTA. The
      theme's own note already states the principle — "an occluded CTA is a bug,
      not an interruption" — and the existing fix for the same collision was to
      make the dog step over the sticky bar, not to delete the dog.

   So it keeps walking and it loses its right to paint over the purchase. At
   `z-index: 99999` it was above everything on the page; here it drops below the
   buy column and the sticky bar, and above nothing else that matters. The dog
   walks BEHIND the button, which is what it does on the rest of the store
   anyway — there is simply nothing pinned there to collide with.
   ⚙️ `:has()` scopes this to the add-on pages by the presence of the add-on
      root, so it cannot reach the bottle page or the home page. Falling back to
      today's behaviour in a browser without `:has()` is the correct failure
      direction: a visible dog, never a missing one.
   ⚠️ The buy column has to be given a stacking position for this to mean
      anything — an unpositioned element cannot sit above a positioned one, no
      matter what its z-index says. That is why `.apx-buy` is raised here too. */
body:has(.apx[data-apx-root]) .hachio-pet-container { z-index: 3; }
body:has(.apx[data-apx-root]) .apx-buy,
body:has(.apx[data-apx-root]) .apx-buyband,
body:has(.apx[data-apx-root]) .apx-pd-studio { position: relative; z-index: 4; }
