/* ============================================================================
   STORE LAYER - get.benny.tools
   ----------------------------------------------------------------------------
   Additive only. Loads AFTER the base, the comms layer and the theme.

   THE ONE RULE HOLDS: every colour below is a var(--...) from agnt-ds or the
   comms layer. Nothing here forks a token, redefines a palette entry, or
   introduces a literal hex.

   DELIBERATE DEVIATION - elevation.
   agnt-ds is flat by design and ships only --shadow-pop, scoped to floating
   overlays. A storefront is not product chrome: cards are the primary object
   and need to read as liftable. So this layer adds a three-step shadow tier.
   It is declared in BOTH :root and body.light-mode, mirroring exactly how the
   base declares --shadow-pop per scheme, because a mode-dependent value stated
   once at :root freezes to its dark value under body.light-mode.
   Shadows carry no hue, so no contrast pair is affected and the accent rules
   are untouched.

   Sections
     1. Store tokens (elevation, geometry)
     2. Page shell + skip link
     3. Nav (with search docking)
     4. Hero
     5. Toolbar (pills, sort, count)
     6. Product card + procedural art
     7. Product detail page
     8. Checkout success
     9. Footer
    10. Motion + responsive
   ========================================================================== */


/* ============================================================================
   1. STORE TOKENS
   ========================================================================== */
:root {
  /* Elevation - see the deviation note above. Neutral, hueless, per scheme. */
  --lift-1: 0 1px 2px rgba(0,0,0,0.30);
  --lift-2: 0 4px 16px -4px rgba(0,0,0,0.45), 0 1px 3px rgba(0,0,0,0.30);
  --lift-3: 0 12px 40px -12px rgba(0,0,0,0.60);

  /* Geometry only - no colour, so not a palette concern. */
  --store-max: 1120px;
  --store-nav: 56px;
  --r-pill: 20px;
}

body.light-mode {
  --lift-1: 0 1px 2px rgba(0,0,0,0.05);
  --lift-2: 0 4px 16px -4px rgba(0,0,0,0.10), 0 1px 3px rgba(0,0,0,0.06);
  --lift-3: 0 12px 40px -12px rgba(0,0,0,0.18);
}


/* ============================================================================
   2. PAGE SHELL
   ========================================================================== */
* , *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }

:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--r4);
}

.wrap { width: 100%; max-width: var(--store-max); margin: 0 auto; padding: 0 24px; }

.skip {
  position: absolute; left: -9999px; top: 8px; z-index: 100;
  background: var(--text); color: var(--bg);
  padding: 10px 16px; border-radius: var(--r8);
  font-size: 13px; font-weight: 600;
}
.skip:focus { left: 24px; }


/* ============================================================================
   3. NAV
   The hero search docks into the nav once it scrolls away, so search is never
   more than one keystroke out of reach. "/" focuses whichever is visible.
   ========================================================================== */
.store-nav {
  position: sticky; top: 0; z-index: 40;
  height: var(--store-nav);
  display: flex; align-items: center; gap: 16px;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid transparent;
  transition: border-color 0.2s, box-shadow 0.2s;
}
.store-nav.is-stuck { border-bottom-color: var(--line); box-shadow: var(--lift-1); }

.store-nav .wrap { display: flex; align-items: center; gap: 16px; }

.brand { display: inline-flex; align-items: center; gap: 8px; flex: none; }
.brand-mark { width: 18px; height: 24px; color: var(--accent); flex: none; }
.brand-mark svg { width: 100%; height: 100%; }
.brand-name { font-size: 16px; font-weight: 600; letter-spacing: -0.01em; white-space: nowrap; }
.brand-name span { color: var(--accent); }

.nav-links { display: flex; align-items: center; gap: 4px; margin-left: 8px; }
.nav-link {
  padding: 6px 10px; border-radius: var(--r6);
  font-size: 13px; font-weight: 500; color: var(--text2);
  transition: background 0.15s, color 0.15s;
}
.nav-link:hover { background: var(--bg3); color: var(--text); }
.nav-link[aria-current="page"] { color: var(--text); }

.nav-right { margin-left: auto; display: flex; align-items: center; gap: 8px; }

/* the docked search - hidden until the hero field leaves the viewport */
.nav-search {
  flex: 1; max-width: 320px; min-width: 0;
  opacity: 0; transform: translateY(-4px); pointer-events: none;
  transition: opacity 0.2s, transform 0.2s;
}
.store-nav.is-docked .nav-search { opacity: 1; transform: none; pointer-events: auto; }

.menu-btn { display: none; }


/* ============================================================================
   4. HERO
   Extends .web-hero from the comms layer with a search field. Tightened from
   the layer's 112px top padding because the grid should be visible on load -
   a storefront hero that pushes product below the fold is a hero that costs
   money.
   ========================================================================== */
.store-hero { text-align: center; padding: 72px 24px 48px; }
.store-hero .hero-kicker {
  font-size: 12px; font-weight: 600; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--accent2);
}
/* The headline is 63 characters, so the measure has to widen or it stacks into
   five lines. Fluid size keeps it to two or three at every width. */
.store-hero .hero-title {
  font-size: clamp(30px, 4.4vw, 50px);
  font-weight: 600; line-height: 1.08; letter-spacing: -0.02em;
  margin: 12px auto 0; max-width: 26ch;
  text-wrap: balance;
}
/* The parenthetical is an aside in the sentence, so it gets its own line,
   its own smaller scale and an italic. Scales fluidly like the headline so it
   never collapses below the subheading it sits above. */
.hero-aside {
  display: block;
  margin-top: 10px;
  font-size: clamp(18px, 2.4vw, 24px);
  font-style: italic;
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--text2);
}
.store-hero .hero-sub {
  font-size: 18px; color: var(--text2); line-height: 1.5;
  margin: 16px auto 0; max-width: 60ch;
  text-wrap: pretty;
}

/* Search field. The base .input is sized for dense product chrome at 13px;
   a hero field is a primary target and needs to read as one. */
.field { position: relative; max-width: 480px; margin: 32px auto 0; }
.field-icon {
  position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; color: var(--text2); pointer-events: none;
}
.field input {
  width: 100%;
  background: var(--bg2);
  border: 1px solid var(--line2);
  border-radius: var(--r-pill);
  padding: 12px 40px 12px 40px;
  font-size: 15px; color: var(--text);
  font-family: inherit;
  transition: border-color 0.15s, background 0.15s;
}
.field input::placeholder { color: var(--text2); }
.field input:focus { outline: none; border-color: var(--accent); background: var(--bg3); }
.field-clear {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  width: 26px; height: 26px; display: none;
  align-items: center; justify-content: center;
  background: transparent; border: none; border-radius: 50%;
  color: var(--text2); cursor: pointer;
}
.field-clear:hover { background: var(--bg4); color: var(--text); }
.field.has-value .field-clear { display: inline-flex; }

.field-hint { margin: 12px 0 0; font-size: 12px; color: var(--text2); }
.field-hint kbd {
  font-family: var(--font-mono); font-size: 11px;
  background: var(--bg3); border: 1px solid var(--line2);
  border-radius: var(--r4); padding: 1px 5px; color: var(--text2);
}

/* nav-docked variant of the same field */
.nav-search .field { margin: 0; max-width: none; }
.nav-search .field input { padding: 7px 32px 7px 32px; font-size: 13px; border-radius: var(--r6); }
.nav-search .field-icon { left: 10px; width: 14px; height: 14px; }


/* ============================================================================
   5. TOOLBAR
   ========================================================================== */
.toolbar-row {
  display: flex; align-items: center; gap: 16px;
  flex-wrap: wrap;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 24px;
}
.toolbar-row .filter-pills { flex: 1; min-width: 0; }
.toolbar-end { display: flex; align-items: center; gap: 10px; margin-left: auto; }

.result-count { font-size: 12px; color: var(--text2); white-space: nowrap; }
.result-count b { color: var(--text2); font-weight: 600; }

.sort-select {
  background: var(--bg3);
  border: 1px solid var(--line2);
  border-radius: var(--r6);
  padding: 6px 10px;
  font-size: 12px; font-weight: 600; color: var(--text2);
  font-family: inherit;
  cursor: pointer;
}
.sort-select:focus { outline: none; border-color: var(--accent); }


/* ============================================================================
   6. PRODUCT CARD
   ========================================================================== */
.p-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 16px;
}

.p-card {
  content-visibility: auto;
  contain-intrinsic-size: auto 420px;
  display: flex; flex-direction: column;
  background: var(--bg2);
  border: 1px solid var(--line);
  border-radius: var(--r12);
  box-shadow: var(--lift-1);
  position: relative;
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.p-card:hover, .p-card:focus-within {
  z-index: 2;
  transform: translateY(-2px);
  box-shadow: var(--lift-2);
  border-color: var(--line2);
}
.p-card:focus-within { border-color: var(--accent); }

/* Procedural art. Stands in until a product has real screenshots in R2.
   Hues come from --series-1..8, the design system's validated categorical
   ramp, set per card via --art. Never an invented colour. */
.p-art {
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--bg3);
  overflow: hidden;
  border-radius: var(--r12) var(--r12) 0 0;
}
.p-art::before {
  content: ""; position: absolute; inset: 0;
  background:
    radial-gradient(120% 120% at 18% 12%, var(--art) 0%, transparent 62%),
    linear-gradient(150deg, var(--bg4) 0%, var(--bg3) 100%);
}
.p-art::after {
  content: ""; position: absolute; inset: 0;
  opacity: 0.5;
  background-image: var(--art-pattern, none);
  background-size: var(--art-size, 24px 24px);
}
.p-art img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* pattern variants - geometry only */
.p-art[data-pattern="grid"]   { --art-pattern: linear-gradient(var(--line2) 1px, transparent 1px), linear-gradient(90deg, var(--line2) 1px, transparent 1px); --art-size: 26px 26px; }
.p-art[data-pattern="lines"]  { --art-pattern: repeating-linear-gradient(115deg, var(--line2) 0 1px, transparent 1px 12px); }
.p-art[data-pattern="diag"]   { --art-pattern: repeating-linear-gradient(45deg, var(--line2) 0 1px, transparent 1px 16px); }
.p-art[data-pattern="waves"]  { --art-pattern: repeating-radial-gradient(circle at 20% 110%, transparent 0 18px, var(--line2) 18px 19px); }
.p-art[data-pattern="rings"]  { --art-pattern: repeating-radial-gradient(circle at 78% 22%, transparent 0 14px, var(--line2) 14px 15px); }
.p-art[data-pattern="blocks"] { --art-pattern: linear-gradient(90deg, var(--line2) 2px, transparent 2px); --art-size: 14px 14px; }
.p-art[data-pattern="layers"] { --art-pattern: repeating-linear-gradient(0deg, transparent 0 15px, var(--line2) 15px 16px); }
.p-art[data-pattern="nodes"]  { --art-pattern: radial-gradient(circle, var(--line2) 1.5px, transparent 1.5px); --art-size: 20px 20px; }
.p-art[data-pattern="arc"]    { --art-pattern: repeating-conic-gradient(from 200deg at 12% 88%, transparent 0 6deg, var(--line2) 6deg 7deg); }

.p-art-badge {
  position: absolute; top: 10px; left: 10px; z-index: 1;
}

.p-body { display: flex; flex-direction: column; gap: 8px; padding: 16px; flex: 1; }

.p-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.p-name {
  font-size: 15px; font-weight: 600; color: var(--text);
  letter-spacing: -0.01em; margin: 0; flex: 1; min-width: 0;
}
.p-name a::after { content: ""; position: absolute; inset: 0; } /* whole-card target */

.p-price {
  font-family: var(--font-mono);
  font-size: 14px; font-weight: 600; color: var(--text);
  white-space: nowrap; flex: none;
}
/* --green is a status colour, guaranteed for fills and icons, not for small
   text: it measures 3.05:1 on --bg2 in light mode. Deriving a darker green
   would fork the palette, which the system forbids, so "Free" carries its
   meaning as a word and takes the strongest text step instead. */
.p-price.is-free { color: var(--text); }
/* A price that is a phrase rather than a number is not a number: it drops the
   mono face and the numeric weight so it cannot crowd the product name. */
.p-price.is-label {
  font-family: var(--font-ui); font-size: 12px; font-weight: 500;
  color: var(--text2); flex-basis: 100%; order: 3; margin-top: -4px;
}
.p-price small { font-size: 11px; color: var(--text2); font-weight: 400; }

.p-desc {
  font-size: 13px; color: var(--text2); line-height: 1.5; margin: 0;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}

.p-foot {
  display: flex; align-items: center; gap: 8px;
  margin-top: auto; padding-top: 12px;
  border-top: 1px solid var(--line);
}


.p-card-link { position: relative; display: contents; }

.empty-wrap { grid-column: 1 / -1; }


/* ============================================================================
   7. PRODUCT DETAIL
   ========================================================================== */
.p-detail { padding: 32px 0 64px; }

.p-detail-head { margin-bottom: 24px; }
.p-detail-title {
  font-size: 40px; font-weight: 600; letter-spacing: -0.02em;
  line-height: 1.1; margin: 8px 0 0;
}
.p-detail-blurb {
  font-size: 17px; color: var(--text2); line-height: 1.55;
  margin: 12px 0 0; max-width: 62ch;
}
.p-detail-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 16px; }

.p-layout { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 32px; align-items: start; }

.p-main { min-width: 0; display: flex; flex-direction: column; gap: 32px; }

.p-hero-art {
  aspect-ratio: 16 / 8;
  border-radius: var(--r12);
  border: 1px solid var(--line);
  box-shadow: var(--lift-2);
}

.p-section-title {
  font-size: 20px; font-weight: 600; letter-spacing: -0.01em;
  margin: 0 0 12px;
}

.p-includes { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.p-includes li {
  display: flex; gap: 10px; align-items: flex-start;
  font-size: 14px; color: var(--text2); line-height: 1.5;
}
.p-includes .tick { flex: none; width: 16px; height: 16px; color: var(--green); margin-top: 3px; }
.p-includes code {
  font-family: var(--font-mono); font-size: 12px;
  background: var(--bg3); border-radius: var(--r4); padding: 1px 5px; color: var(--text);
}

/* buy panel - sticky, the only thing on the page with a job */
.p-buy {
  position: sticky; top: calc(var(--store-nav) + 24px);
  background: var(--bg2);
  border: 1px solid var(--line);
  border-radius: var(--r12);
  padding: 24px;
  box-shadow: var(--lift-2);
  display: flex; flex-direction: column; gap: 16px;
}
.p-buy-price { display: flex; align-items: baseline; gap: 6px; }
.p-buy-cur {
  font-family: var(--font-mono); font-size: 15px; font-weight: 600;
  color: var(--text2); letter-spacing: 0;
}
.p-buy-amount { font-family: var(--font-mono); font-size: 32px; font-weight: 600; letter-spacing: -0.02em; }
.p-buy-amount.is-free { color: var(--text); }
.p-buy-note { font-size: 12px; color: var(--text2); }

.btn-buy {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  width: 100%;
  background: var(--accent-interactive);
  color: var(--on-accent-interactive);
  border: none; border-radius: var(--r8);
  padding: 12px 16px;
  font-size: 14px; font-weight: 600; font-family: inherit;
  cursor: pointer;
  transition: opacity 0.15s;
}
.btn-buy:hover { opacity: 0.88; }
.btn-buy svg { width: 16px; height: 16px; }

.p-spec { display: flex; flex-direction: column; gap: 0; }
.p-spec-row {
  display: flex; flex-direction: column; gap: 2px;
  padding: 10px 0;
  border-top: 1px solid var(--line);
}
.p-spec-row dt {
  font-size: 10px; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.07em; color: var(--text2);
}
.p-spec-row dd { margin: 0; color: var(--text); font-size: 13px; line-height: 1.45; }
.p-spec-row dd.mono { font-family: var(--font-mono); font-size: 12px; }

/* A <ul> carries a 40px UA indent and a 1em block margin. Every other row in
   this panel is flush to the padding edge, so both have to go. */
.p-trust {
  display: flex; flex-direction: column; gap: 8px;
  margin: 4px 0 0; padding: 0; list-style: none;
}
.p-trust li {
  display: flex; gap: 8px; align-items: center;
  font-size: 12px; color: var(--text2); list-style: none;
}
.p-trust svg { width: 14px; height: 14px; flex: none; color: var(--text2); }

/* changelog */
.p-log { display: flex; flex-direction: column; }
.p-log-row {
  display: grid; grid-template-columns: 80px 88px 1fr; gap: 16px;
  padding: 12px 0; border-top: 1px solid var(--line);
  font-size: 13px; align-items: baseline;
}
.p-log-row:first-child { border-top: none; }
.p-log-ver { font-family: var(--font-mono); font-size: 12px; color: var(--accent2); }
.p-log-at { font-family: var(--font-mono); font-size: 12px; color: var(--text2); }
.p-log-notes { color: var(--text2); line-height: 1.5; }

/* faq */
.p-faq { display: flex; flex-direction: column; }
.p-faq details { border-top: 1px solid var(--line); }
.p-faq details:first-child { border-top: none; }
.p-faq summary {
  cursor: pointer; list-style: none;
  padding: 14px 0; font-size: 14px; font-weight: 600; color: var(--text);
  display: flex; align-items: center; gap: 10px;
}
.p-faq summary::-webkit-details-marker { display: none; }
.p-faq summary .chev { width: 14px; height: 14px; color: var(--text2); margin-left: auto; transition: transform 0.2s; flex: none; }
.p-faq details[open] summary .chev { transform: rotate(180deg); }
.p-faq p { margin: 0 0 14px; font-size: 14px; color: var(--text2); line-height: 1.6; max-width: 68ch; }

/* related */
.p-related { border-top: 1px solid var(--line); padding-top: 32px; }


/* ============================================================================
   8. CHECKOUT SUCCESS
   ========================================================================== */
.done { max-width: 560px; margin: 0 auto; padding: 80px 24px; text-align: center; }
.done-glyph {
  width: 56px; height: 56px; margin: 0 auto 24px;
  border-radius: 50%; display: grid; place-items: center;
  background: var(--green-bg); color: var(--green);
}
.done-glyph svg { width: 28px; height: 28px; }
.done h1 { font-size: 30px; font-weight: 600; letter-spacing: -0.02em; margin: 0; }
.done p { font-size: 15px; color: var(--text2); line-height: 1.6; margin: 12px 0 0; }
.done-actions { display: flex; gap: 10px; justify-content: center; margin-top: 32px; flex-wrap: wrap; }
.done-steps {
  margin-top: 48px; text-align: left;
  background: var(--bg2); border: 1px solid var(--line);
  border-radius: var(--r12); padding: 24px;
  box-shadow: var(--lift-1);
}
.done-steps ol { margin: 12px 0 0; padding-left: 20px; }
.done-steps li { font-size: 14px; color: var(--text2); line-height: 1.6; margin: 8px 0; }


/* ============================================================================
   9. FOOTER
   ========================================================================== */
.store-footer {
  border-top: 1px solid var(--line);
  margin-top: 64px;
  padding: 48px 0 32px;
}
.footer-row { display: flex; gap: 32px; flex-wrap: wrap; align-items: flex-start; }
.footer-brand { flex: 1; min-width: 220px; }
.footer-tag { font-size: 13px; color: var(--text2); margin: 8px 0 0; max-width: 40ch; line-height: 1.5; }
.footer-col { display: flex; flex-direction: column; gap: 8px; min-width: 140px; }
.footer-col h3 { font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.07em; color: var(--text2); margin: 0 0 2px; }
.footer-col a { font-size: 13px; color: var(--text2); }
.footer-col a:hover { color: var(--text); }
.footer-base {
  margin-top: 32px; padding-top: 24px; border-top: 1px solid var(--line);
  display: flex; gap: 16px; flex-wrap: wrap; align-items: center;
  font-size: 12px; color: var(--text2);
}
.footer-base .mor { margin-left: auto; }


/* ============================================================================
   10. MOTION + RESPONSIVE
   ========================================================================== */
.reveal { opacity: 0; transform: translateY(8px); animation: rise 0.5s ease forwards; }
@keyframes rise { to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .p-card:hover { transform: none; }
}

@media (max-width: 900px) {
  .p-layout { grid-template-columns: 1fr; }
  .p-buy { position: static; }
}

@media (max-width: 720px) {
  .nav-links { display: none; }
  .nav-search { display: none; }
  .menu-btn { display: inline-flex; }
  /* already in the mobile panel - two of the same control is one too many */
  .nav-right .btn-secondary { display: none; }
  .store-hero { padding: 48px 24px 32px; }
  .store-hero .hero-sub { font-size: 16px; }
  .hero-aside { margin-top: 6px; }
  .p-detail-title { font-size: 30px; }
  .toolbar-end { width: 100%; margin-left: 0; }
  .p-log-row { grid-template-columns: 1fr; gap: 4px; }
}

/* mobile menu panel */
.mobile-panel {
  display: none;
  border-bottom: 1px solid var(--line);
  background: var(--bg2);
  padding: 8px 0 16px;
}
.mobile-panel.is-open { display: block; }
.mobile-panel .wrap { display: flex; flex-direction: column; gap: 2px; }
.mobile-panel a { padding: 10px 8px; border-radius: var(--r6); font-size: 15px; color: var(--text2); }
.mobile-panel a:hover { background: var(--bg3); color: var(--text); }
@media (min-width: 721px) { .mobile-panel { display: none !important; } }


/* ============================================================================
   11. CONTRAST CORRECTIONS
   ----------------------------------------------------------------------------
   Measured, not eyeballed. --text3 is 2.87:1 against --bg, which is correct
   for an ornamental mark and wrong for anything a person reads, so this layer
   uses --text2 (6.81:1) throughout instead. Two component-level corrections
   remain, both because a base component is being used on a surface it was not
   designed for.
   ========================================================================== */

/* .badge-default assumes it sits on the page. On card art it sits on a
   gradient, where --bg4 on --text3 is unreadable. Give it its own opaque
   ground and a real text step. */
.p-art-badge .badge {
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  color: var(--text);
  border: 1px solid var(--line2);
}

/* .empty-state paints its copy --text3. Same reasoning. */
.empty-state, .empty-state p { color: var(--text2); }

/* Inline SVG needs an explicit size where the base sized an icon font. */
.icon-btn svg { width: 18px; height: 18px; }
.btn-secondary svg, .btn-buy svg { width: 16px; height: 16px; }

/* .badge-default is --text3 on --bg4: 1.9:1, unreadable. Every place this
   layer uses it, it carries a category name a buyer needs to read. */
.p-detail-meta .badge-default,
.p-card .badge-default { color: var(--text2); }

/* .crumbs paints its trail --text3 (2.83:1). The "/" separators are
   ornamental and may stay, but the trailing segment is the page's own name
   and is read. */
.crumbs > span:not(:empty) { color: var(--text2); }
.crumbs > span + span { color: var(--text2); }

/* .btn-secondary is sized for dense product chrome (12px / 6px). Where it
   sits beside .btn-buy as a peer action it has to match it, or the pair reads
   as primary-plus-afterthought rather than two choices. */
.done-actions { align-items: stretch; }
.done-actions .btn-secondary {
  padding: 12px 16px;
  font-size: 14px;
  border-radius: var(--r8);
}


/* ============================================================================
   12. DOWNLOAD COUNT + REPOSITORY SYNC MARK
   ========================================================================== */

/* Downloads, top-right of the card art, mirroring the category badge opposite.
   Shows the carried-over seed only; live purchase counts are not published. */
.p-art-downloads {
  position: absolute; top: 10px; right: 10px; z-index: 1;
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 8px 3px 6px;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  border: 1px solid var(--line2);
  color: var(--text);
  font-family: var(--font-mono); font-size: 11px; font-weight: 600;
  white-space: nowrap;
}
.p-art-downloads svg { width: 12px; height: 12px; }

/* Repository sync mark. A git-branch glyph rather than the Git project's own
   logo: the official mark is trademarked, ships in a fixed orange that fights
   the palette, and would be the only element on the page not drawn in the
   system's stroke language. */
.p-repo {
  /* The card's whole-surface link overlay (.p-name a::after) covers the foot,
     so without this the mark can never be hovered, focused or read. */
  position: relative; z-index: 3;
  margin-left: auto;
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--text2);
  cursor: help;
  border-radius: var(--r4);
  transition: color 0.15s;
}
.p-repo svg { width: 15px; height: 15px; }
.p-repo:hover, .p-repo:focus { color: var(--accent2); }
.p-repo.is-inline { margin-left: 0; font-size: 13px; }

.p-tip {
  position: absolute; bottom: calc(100% + 10px); right: -6px; z-index: 60;
  width: 236px;
  background: var(--bg4);
  color: var(--text);
  border: 1px solid var(--line2);
  border-radius: var(--r8);
  box-shadow: var(--shadow-pop);
  padding: 10px 12px;
  font-family: var(--font-ui);
  font-size: 12px; font-weight: 400; line-height: 1.45;
  text-align: left;
  opacity: 0; transform: translateY(4px); pointer-events: none;
  transition: opacity 0.15s ease, transform 0.15s ease;
}
.p-tip::after {
  content: ""; position: absolute; top: 100%; right: 12px;
  border: 6px solid transparent; border-top-color: var(--line2);
}
/* :focus as well as :focus-visible - on a touch device a tap is the only way
   to reach this, and it produces focus without focus-visible. */
.p-repo:hover .p-tip,
.p-repo:focus .p-tip { opacity: 1; transform: none; }

/* On the product page the mark sits in a right-aligned spec cell, so the
   tooltip anchors to the panel edge rather than off it. */
.p-spec .p-tip { right: 0; }

@media (max-width: 720px) {
  .p-tip { width: 200px; right: -4px; }
}


/* ============================================================================
   13. PRODUCT GALLERY
   ----------------------------------------------------------------------------
   Shown only when a product has two or more images. Images are stacked and
   cross-faded rather than translated in a track: there is no swipe gesture to
   honour here, and opacity is a compositor-only property, so switching costs
   no layout and no repaint.
   ========================================================================== */

.p-gallery { display: flex; flex-direction: column; gap: 12px; }

.p-gallery-stage {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--r12);
  border: 1px solid var(--line);
  background: var(--bg2);
  overflow: hidden;
  box-shadow: var(--lift-2);
}

.p-gallery-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.22s ease;
}
.p-gallery-img.is-active { opacity: 1; }

.p-gallery-nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 36px; height: 36px;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  border: 1px solid var(--line2);
  border-radius: 50%;
  color: var(--text);
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s, background 0.15s;
}
.p-gallery-nav svg { width: 18px; height: 18px; }
.p-gallery-nav.prev { left: 12px; }
.p-gallery-nav.next { right: 12px; }
.p-gallery-nav.next svg { transform: rotate(180deg); }
.p-gallery-nav:hover { background: var(--bg4); }

/* Arrows appear on pointer intent, but are always present for a keyboard:
   a control that only exists on hover does not exist for a keyboard user. */
.p-gallery-stage:hover .p-gallery-nav,
.p-gallery-nav:focus-visible { opacity: 1; }

.p-gallery-count {
  position: absolute; bottom: 12px; right: 12px;
  padding: 3px 9px;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  border: 1px solid var(--line2);
  color: var(--text2);
  font-family: var(--font-mono); font-size: 11px;
}
.p-gallery-count b { color: var(--text); font-weight: 600; }

.p-gallery-caption {
  margin: 0; font-size: 13px; color: var(--text2); line-height: 1.5;
}

.p-gallery-thumbs {
  display: flex; gap: 8px;
  overflow-x: auto;
  scrollbar-width: thin;
  padding-bottom: 4px;
}
.p-gallery-thumb {
  flex: 0 0 104px;
  aspect-ratio: 16 / 9;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--r8);
  background: var(--bg2);
  overflow: hidden;
  cursor: pointer;
  opacity: 0.55;
  transition: opacity 0.15s, border-color 0.15s;
}
.p-gallery-thumb img { width: 100%; height: 100%; object-fit: cover; }
.p-gallery-thumb:hover { opacity: 0.85; }
.p-gallery-thumb.is-active { opacity: 1; border-color: var(--accent); }

.p-single {
  margin: 0;
  aspect-ratio: 16 / 9;
  border-radius: var(--r12);
  border: 1px solid var(--line);
  overflow: hidden;
  box-shadow: var(--lift-2);
}
.p-single img { width: 100%; height: 100%; object-fit: cover; }

@media (max-width: 720px) {
  .p-gallery-nav { opacity: 1; }        /* no hover on touch */
  .p-gallery-thumb { flex-basis: 84px; }
}


/* ============================================================================
   14. ICON ART
   ----------------------------------------------------------------------------
   For products whose real artwork is a mark rather than a screenshot. The
   colours arrive as inline custom properties from the product record, so no
   literal hex enters this stylesheet and the token rule is untouched.

   Sizing is proportional so one definition serves every surface: 22% of the
   frame is ~76px on a browse card, ~158px on the product hero. The bounds stop
   it disappearing on a narrow phone card or ballooning on the hero.
   ========================================================================== */
.p-art.is-icon { background: var(--art-bg); }
.p-art.is-icon::before,
.p-art.is-icon::after { display: none; }

.p-art-icon {
  position: absolute; inset: 0;
  display: grid; place-items: center;
}
.p-art-icon svg {
  width: var(--art-w, 22%);
  min-width: 54px;
  max-width: 160px;
  height: auto;
  aspect-ratio: var(--art-ratio, 110 / 85);
  color: var(--art-fg);
}


/* ============================================================================
   15. SUBCATEGORY PILLS
   ----------------------------------------------------------------------------
   The second tier appears only once a parent is chosen, so the default view
   shows four categories rather than fifteen pills of mixed rank.
   ========================================================================== */
.sub-pills {
  margin: -8px 0 24px;
  padding-left: 14px;
  border-left: 2px solid var(--line2);
}
.sub-pills .filter-pill { font-weight: 500; }
.sub-pills[hidden] { display: none; }

/* Repository link in the post-purchase steps. */
.done-steps a { color: var(--accent2); text-decoration: underline; text-underline-offset: 2px; }
.done-steps a:hover { color: var(--accent); }


/* ============================================================================
   16. PROSE PAGES (docs, about)
   ----------------------------------------------------------------------------
   A single measured column. No sidebar: these pages are short enough that a
   table of contents would be more chrome than content.
   ========================================================================== */
.prose { padding: 56px 0 80px; }
/* The column starts at the same left edge as the logo and everything else -
   the header and footer are never altered. A measured column under a full
   width header leaves space to its right, so that space carries the section
   nav rather than sitting empty.

   The measure is tuned by measurement, not by the ch unit alone: ch is the
   width of "0", narrower than the average letter, so 78ch rendered 91
   characters a line. 62ch lands at 71, inside the comfortable 60-75. */
.prose-layout {
  display: grid;
  grid-template-columns: 62ch minmax(0, 1fr);
  gap: 48px;
  align-items: start;
}

.prose-nav { position: sticky; top: calc(var(--store-nav) + 32px); }
.prose-nav h2 {
  font-size: 10px; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.07em; color: var(--text2); margin: 0 0 10px;
}
.prose-nav ol { list-style: none; margin: 0; padding: 0; }
.prose-nav li { margin: 0; }
.prose-nav a {
  display: block; padding: 5px 0 5px 12px;
  border-left: 2px solid var(--line);
  font-size: 13px; line-height: 1.4; color: var(--text2);
  text-decoration: none;
  transition: color 0.15s, border-color 0.15s;
}
.prose-nav a:hover { color: var(--text); border-left-color: var(--line2); }
.prose-nav a.is-current { color: var(--accent2); border-left-color: var(--accent); }

@media (max-width: 1000px) {
  .prose-layout { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .prose-nav { display: none; }
}

/* Same measure as the body column, so the heading and lede line up with the
   text rather than running the full page width. */
.prose-head { margin-bottom: 40px; max-width: 62ch; }
/* .hero-kicker is scoped to .store-hero, so it renders unstyled anywhere else. */
.prose-head .hero-kicker {
  font-size: 12px; font-weight: 600; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--accent2); margin: 0;
}
.prose-head h1 {
  font-size: clamp(30px, 4vw, 44px); font-weight: 600;
  letter-spacing: -0.02em; line-height: 1.1; margin: 8px 0 0;
}
.prose-head .lede {
  font-size: 18px; color: var(--text2); line-height: 1.55;
  margin: 14px 0 0;
}
.prose-body { max-width: none; }
.prose-body h2 {
  font-size: 22px; font-weight: 600; letter-spacing: -0.01em;
  margin: 44px 0 12px; padding-top: 20px; border-top: 1px solid var(--line);
}
.prose-body h2:first-child { margin-top: 0; padding-top: 0; border-top: none; }
.prose-body h3 { font-size: 16px; font-weight: 600; margin: 26px 0 8px; }
.prose-body p { font-size: 15px; line-height: 1.65; color: var(--text2); margin: 0 0 14px; }
.prose-body strong { color: var(--text); font-weight: 600; }
.prose-body a { color: var(--accent2); text-decoration: underline; text-underline-offset: 2px; }
.prose-body a:hover { color: var(--accent); }
.prose-body ul, .prose-body ol { margin: 0 0 16px; padding-left: 22px; }
.prose-body li { font-size: 15px; line-height: 1.65; color: var(--text2); margin: 6px 0; }
.prose-body code {
  font-family: var(--font-mono); font-size: 13px;
  background: var(--bg3); border-radius: var(--r4); padding: 2px 6px; color: var(--text);
}
.prose-body table { width: 100%; border-collapse: collapse; margin: 0 0 20px; font-size: 14px; }
.prose-body th {
  text-align: left; font-size: 10px; text-transform: uppercase; letter-spacing: 0.07em;
  color: var(--text2); padding: 8px; border-bottom: 1px solid var(--line2);
}
.prose-body td { padding: 10px 8px; border-bottom: 1px solid var(--line); color: var(--text2); vertical-align: top; }

.note {
  background: var(--bg2); border: 1px solid var(--line);
  border-left: 2px solid var(--accent);
  border-radius: var(--r8); padding: 16px 18px; margin: 0 0 20px;
}
.note p:last-child { margin-bottom: 0; }
.note .note-title { font-size: 13px; font-weight: 600; color: var(--text); margin-bottom: 6px; }

/* An unanswered question, visible to whoever reads the page next. */
.todo {
  background: var(--amber-bg); border: 1px solid var(--amber);
  border-radius: var(--r8); padding: 16px 18px; margin: 0 0 20px;
}
/* --amber is a status colour: 3.1:1 on its own tint in light mode, which is
   fine for a border and not for a 13px label. The border and background carry
   the signal; the text takes the readable step. */
.todo .note-title { color: var(--text); }
.todo p { color: var(--text); }

/* A product with no checkout link cannot be bought. Saying so is better than a
   button that looks live and goes somewhere misleading. */
.btn-buy.is-disabled {
  background: var(--bg4); color: var(--text2);
  border: 1px solid var(--line2); cursor: not-allowed; opacity: 1;
}
.btn-buy.is-disabled:hover { opacity: 1; }


/* ============================================================================
   17. BREADCRUMB
   ----------------------------------------------------------------------------
   .crumbs is a flex row with no wrap, so on a narrow screen every crumb
   shrinks and wraps inside itself: "All products" over two lines, a long
   product name over three, separators stranded beside them. A 19px row becomes
   56px of ragged column.

   Crumbs wrap between each other, never within one.
   ========================================================================== */
.crumbs { flex-wrap: wrap; row-gap: 2px; }
.crumbs > * { flex-shrink: 0; white-space: nowrap; }

@media (max-width: 720px) {
  /* The trailing crumb is the page title, which the h1 states immediately
     below it. Breadcrumbs are for going up, so on a narrow screen the path
     keeps the levels you can actually navigate to and drops the one you are
     already on. */
  .crumbs .crumb-current,
  .crumbs .crumb-current + span,
  .crumbs > span:has(+ .crumb-current) { display: none; }
}

/* A failed checkout says what happened, in the panel, where the button was. */
.checkout-error {
  margin: 0; padding: 10px 12px;
  background: var(--red-bg); border: 1px solid var(--red);
  border-radius: var(--r8);
  font-size: 12px; line-height: 1.45; color: var(--text);
}


/* ============================================================================
   18. CONSENT BANNER
   ----------------------------------------------------------------------------
   A bar, not a modal. A dialog that traps focus and blocks the page to ask
   about analytics costs more than the analytics is worth.
   ========================================================================== */
.consent {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 80;
  background: var(--bg2);
  border-top: 1px solid var(--line2);
  box-shadow: var(--shadow-pop);
  padding: 16px 24px;
}
.consent-inner {
  max-width: var(--store-max); margin: 0 auto;
  display: flex; align-items: center; gap: 24px; flex-wrap: wrap;
}
.consent-text {
  margin: 0; flex: 1; min-width: 260px;
  font-size: 13px; line-height: 1.5; color: var(--text2);
}
.consent-text a { color: var(--accent2); text-decoration: underline; text-underline-offset: 2px; }
.consent-text a:hover { color: var(--accent); }
.consent-actions { display: flex; gap: 8px; margin-left: auto; }

.btn-consent {
  background: var(--accent-interactive); color: var(--on-accent-interactive);
  border: none; border-radius: var(--r6);
  padding: 8px 18px; font: inherit; font-size: 13px; font-weight: 600;
  cursor: pointer; transition: opacity 0.15s;
}
.btn-consent:hover { opacity: 0.88; }
.consent .btn-secondary { padding: 8px 16px; font-size: 13px; }

@media (max-width: 620px) {
  .consent-actions { margin-left: 0; width: 100%; }
  .consent-actions button { flex: 1; }
}

.prose-updated {
  margin: 12px 0 0; font-size: 12px; color: var(--text2);
  font-family: var(--font-mono);
}
