/*
 * AlchemyStripe admin overrides — injected into the Alchemy admin layout
 * via Alchemy.admin_stylesheets in engine.rb.
 */

/*
 * Make the left menu a flex column so #main_navi can fill the remaining height
 * and its overflow-y: auto (already set by Alchemy) actually activates when
 * there are more module entries than fit in the viewport.
 */
#left_menu {
  display: flex;
  flex-direction: column;
  height: 100%;       /* ensure a bounded height so flex children can scroll */
  overflow: hidden;   /* clip anything that escapes the flex container */
}

#main_navi {
  flex: 1 1 auto;
  min-height: 0; /* required: flex children won't shrink below content size without this */
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
}

/*
 * Shop admin: dashboard, events and reports.
 *
 * These pages used to be inline styles, which a host could not adjust and a
 * strict CSP would refuse. Colours are custom properties on .shop-admin so one
 * rule re-skins them; the storefront's --alchemy-stripe-* palette is deliberately
 * not used here (the admin does not load it).
 */
.shop-admin {
  --shop-admin-card-bg: #fff;
  --shop-admin-border: #e0e0e0;
  --shop-admin-text: #333;
  --shop-admin-muted: #666;
  --shop-admin-ok: #2e7d32;
  --shop-admin-warn: #b26a00;
  --shop-admin-bad: #c62828;
  --shop-admin-info: #1565c0;
  --shop-admin-accent: #2e7d32;
}

.shop-admin__grid {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin-bottom: 2rem;
}

.shop-stat {
  flex: 1 1 160px;
  min-width: 140px;
  background: var(--shop-admin-card-bg);
  border: 1px solid var(--shop-admin-border);
  border-radius: 4px;
  padding: 1.25rem 1.5rem;
}

.shop-stat__value { font-size: 2rem; font-weight: 600; color: var(--shop-admin-text); }
.shop-stat__value--ok { color: var(--shop-admin-ok); }
.shop-stat__value--warn { color: var(--shop-admin-warn); }
.shop-stat__value--bad { color: var(--shop-admin-bad); }
.shop-stat__value--info { color: var(--shop-admin-info); }
.shop-stat__label { font-size: 0.85rem; color: var(--shop-admin-muted); margin-top: 0.25rem; }
.shop-stat__note { font-size: 0.8rem; color: var(--shop-admin-muted); margin-top: 0.25rem; }
.shop-stat__link { margin-top: 0.75rem; font-size: 0.8rem; }

.shop-panel {
  background: var(--shop-admin-card-bg);
  border: 1px solid var(--shop-admin-border);
  border-radius: 4px;
  padding: 1rem 1.25rem;
  margin-bottom: 1.5rem;
}
.shop-panel > h2, .shop-panel > h3 { margin-top: 0; }

.shop-actions { display: flex; gap: 0.75rem; flex-wrap: wrap; }
.shop-muted { color: var(--shop-admin-muted); font-size: 0.85rem; }

/* A <progress>, not a div with an inline width: no style attribute to refuse. */
.shop-meter {
  display: block;
  width: 100%;
  height: 0.6rem;
  margin: 0.4rem 0;
  -webkit-appearance: none;
     -moz-appearance: none;
          appearance: none;
  border: 0;
  border-radius: 3px;
  background: var(--shop-admin-border);
  overflow: hidden;
}
.shop-meter::-webkit-progress-bar { background: var(--shop-admin-border); }
.shop-meter::-webkit-progress-value { background: var(--shop-admin-accent); }
.shop-meter::-moz-progress-bar { background: var(--shop-admin-accent); }

.shop-chart { display: block; max-width: 100%; }
.shop-chart--bars { width: 100%; height: auto; }
.shop-chart__line { stroke: var(--shop-admin-accent, #2e7d32); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.shop-chart__bar { fill: var(--shop-admin-accent, #2e7d32); }
.shop-chart__bar:hover { opacity: 0.75; }
.shop-chart__axis { font-size: 10px; fill: var(--shop-admin-muted, #666); }

.shop-tier-form { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: flex-end; }
.shop-tier-form label { display: flex; flex-direction: column; font-size: 0.85rem; }
.order-filter-date { display: inline-flex; align-items: center; gap: 0.35rem; margin-left: 0.5rem; }
.shop-admin__grid--flush { margin-bottom: 0; }

.refund-extras { display: flex; flex-wrap: wrap; gap: 0.75rem 1.5rem; align-items: center; margin-bottom: 0.75rem; }
.order-breakdown--refunded td { color: var(--shop-admin-bad, #c62828); }

/*
 * Shop admin tables, toolbars and forms. These were inline styles on the order and
 * product screens; classes here so a host can adjust them and a strict CSP is happy.
 */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* A wide table scrolls inside its own box instead of pushing the page sideways. */
.shop-table-scroll { overflow-x: auto; max-width: 100%; }
.shop-table--spaced { margin-bottom: 2rem; }
.shop-nowrap { white-space: nowrap; }
.shop-toolbar { margin-bottom: 0.75rem; }
.shop-toolbar--wrap { flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1rem; }
.shop-inline, form.shop-inline { display: inline; }
.shop-inline-form { display: inline-flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
.shop-input-sm { padding: 4px 8px; font-size: 0.9rem; }
.shop-input-sm--wide { min-width: 200px; }
.shop-note-form { display: flex; gap: 0.5rem; align-items: flex-start; margin-bottom: 1rem; }
.shop-note-form__body { flex: 1; }
.search_field .search_field_clear.shop-clear--visible { visibility: visible; }
.search_field .search_field_clear.shop-clear--hidden { visibility: hidden; }
.shop-drag-handle { cursor: grab; }
.shop-move-up:focus-visible, .shop-move-down:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
/*
 * AlchemyStripe — Materialize CSS overrides and shop-specific styles.
 *
 * Requires Materialize 1.0.0 (loaded via shared/_shop_chrome, or add it to your
 * host app's layout). The storefront's icons are inline SVG and need no icon font;
 * the Material Icons font is only for a host's own `<i class="material-icons">`
 * markup (see config.load_material_icons_font):
 *
 *   <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
 *   <link href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css" rel="stylesheet">
 *   <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
 *


 */

/* ─── Theming ─────────────────────────────────────────────────────────────────
 *
 * The shop's accent colour. The defaults below are Materialize's teal, which is
 * what every view hardcoded before — so a host that sets nothing renders exactly
 * as it did. Teal is a framework default rather than anyone's brand, and getting
 * rid of it used to mean copying whole engine partials into the host app, which
 * silently opts that host out of every later fix to those files.
 *
 * The status families (success / info / warning) are the same idea one step on:
 * order-status pills, the checkout-cancelled page and the low-stock indicators
 * used to hardcode `teal lighten-5` / `blue lighten-5` / `orange lighten-5`, so
 * a host that re-pointed --alchemy-stripe-accent away from teal still got teal
 * "complete" pills next to its branded buttons. The defaults below reproduce the
 * old Materialize values exactly.
 *
 * Re-point them instead, anywhere that loads after this stylesheet:
 *
 *   :root {
 *     --alchemy-stripe-accent:          #6a4c93;
 *     --alchemy-stripe-accent-dark:     #4a3468;
 *     --alchemy-stripe-accent-muted:    #b3a1cc;
 *     --alchemy-stripe-accent-soft:     #ede7f3;
 *     --alchemy-stripe-accent-contrast: #fff;
 *     --alchemy-stripe-danger:          #c62828;
 *     --alchemy-stripe-danger-soft:     #f6dede;
 *     --alchemy-stripe-success:         #2e7d32;
 *     --alchemy-stripe-success-soft:    #e6efe6;
 *     --alchemy-stripe-info:            #4a3468;
 *     --alchemy-stripe-info-soft:       #ede7f3;
 *     --alchemy-stripe-warning:         #b26a00;
 *     --alchemy-stripe-warning-soft:    #f7ecd9;
 *   }
 *
 * Theme through these properties, not by overriding the .shop-accent-* /
 * .shop-status--* classes. Those declarations are !important on purpose:
 * Materialize's own colour helpers are !important too, and .btn / .chip carry a
 * background-color at the same specificity, so a plain override loses depending
 * on stylesheet order — a rule that matches and still loses is this codebase's
 * most-repeated silent failure.
 */

:root {
  --alchemy-stripe-accent:          #00897b; /* teal darken-1 */
  --alchemy-stripe-accent-dark:     #00796b; /* teal darken-2 */
  --alchemy-stripe-accent-muted:    #4db6ac; /* teal lighten-3 */
  --alchemy-stripe-accent-soft:     #e0f2f1; /* teal lighten-5 */
  --alchemy-stripe-accent-contrast: #fff;
  --alchemy-stripe-danger:          #e53935; /* red darken-1 */
  --alchemy-stripe-danger-soft:     #ffebee; /* red lighten-5 */
  --alchemy-stripe-success:         #00695c; /* teal text darken-2 */
  --alchemy-stripe-success-soft:    #e0f2f1; /* teal lighten-5 */
  --alchemy-stripe-info:            #1565c0; /* blue text darken-2 */
  --alchemy-stripe-info-soft:       #e3f2fd; /* blue lighten-5 */
  --alchemy-stripe-warning:         #ef6c00; /* orange text darken-3 */
  --alchemy-stripe-warning-soft:    #fff3e0; /* orange lighten-5 */
}

.shop-accent-text {
  color: var(--alchemy-stripe-accent) !important;
}

.shop-accent-text-strong {
  color: var(--alchemy-stripe-accent-dark) !important;
}

.shop-accent-bg {
  background-color: var(--alchemy-stripe-accent) !important;
  color: var(--alchemy-stripe-accent-contrast) !important;
}

/* The "already completed" step in the order timeline, and other de-emphasised
   accent fills — readable against the accent without competing with it. */
.shop-accent-bg-muted {
  background-color: var(--alchemy-stripe-accent-muted) !important;
  color: var(--alchemy-stripe-accent-dark) !important;
}

/* Tinted panel backgrounds: the in-stock chip, the complete order-status pill. */
.shop-accent-bg-soft {
  background-color: var(--alchemy-stripe-accent-soft) !important;
  color: var(--alchemy-stripe-accent-dark) !important;
}

.shop-danger-bg {
  background-color: var(--alchemy-stripe-danger) !important;
  color: #fff !important;
}

.shop-danger-text {
  color: var(--alchemy-stripe-danger) !important;
}

/* Soft danger panel — the cart's JS-toggled #error-message stock band.
   Parallels .shop-accent-bg-soft / .shop-status--danger: tinted fill, readable
   danger text, over the custom properties instead of Materialize's
   `red lighten-5 red-text text-darken-2`. */
.shop-danger-bg-soft {
  background-color: var(--alchemy-stripe-danger-soft) !important;
  color: var(--alchemy-stripe-danger) !important;
}

/* Filled warning fill — the checkout-cancelled card header (parallels
   .shop-accent-bg on the success card) and any "attention, not an error" band. */
.shop-warning-bg {
  background-color: var(--alchemy-stripe-warning) !important;
  color: #fff !important;
}

.shop-warning-text {
  color: var(--alchemy-stripe-warning) !important;
}

/* ─── Status pills ────────────────────────────────────────────────────────────
   One class per order-status family, replacing the raw `teal lighten-5
   teal-text text-darken-2` etc. that order_status_class used to emit. The three
   consumer views (orders index/show, members index) each carried their own
   inline padding/radius — folded in here so a status pill looks the same
   wherever it appears. */

.shop-status {
  display: inline-block;
  padding: 0.2rem 0.75rem;
  border-radius: 12px;
  font-size: 0.85rem;
  font-weight: 500;
  line-height: 1.4;
}

.shop-status--success {
  background-color: var(--alchemy-stripe-success-soft) !important;
  color: var(--alchemy-stripe-success) !important;
}

.shop-status--info {
  background-color: var(--alchemy-stripe-info-soft) !important;
  color: var(--alchemy-stripe-info) !important;
}

.shop-status--warning {
  background-color: var(--alchemy-stripe-warning-soft) !important;
  color: var(--alchemy-stripe-warning) !important;
}

.shop-status--danger {
  background-color: var(--alchemy-stripe-danger-soft) !important;
  color: var(--alchemy-stripe-danger) !important;
}

.shop-status--neutral {
  background-color: #f5f5f5 !important;
  color: #757575 !important;
}

/* ─── Icons ───────────────────────────────────────────────────────────────────
   The storefront's icons are inline <svg class="shop-icon"> (the `shop_icon`
   helper, glyphs in AlchemyStripe::ShopIcons), not Material Icons ligatures. The
   SVG has to reproduce the box a ligature <i class="material-icons"> had, because
   Materialize sizes and positions icons through that class and these rules used to
   do it for free:

   - A glyph is one em square, so width/height are 1em and the *font-size* is what
     varies by context, exactly as with the ligature. The contexts below are copies
     of Materialize 1.0.0's `i`-keyed rules (it never matches an <svg>), in the same
     order, so the same one wins.
   - Inside a line taller than the glyph (a button's 36px line-height, a nav's 56px)
     the ligature box was the whole line tall with the glyph centred in it and its
     baseline half the leading above the bottom. `1lh` is that line-height, so the
     vertical margin and the vertical-align below put an em-high SVG in the same
     place. Where line-height is 1 (the default) both are 0.
   - `currentColor` keeps `grey-text`, `white-text`, `shop-accent-text` (!important,
     over --alchemy-stripe-accent) and a host's own colours working unchanged. */

.shop-icon {
  width: 1em;
  height: 1em;
  font-size: 24px;
  line-height: 1;
  display: inline-block;
  fill: currentColor;
  flex-shrink: 0;
  margin-top: calc((1lh - 1em) / 2);
  margin-bottom: calc((1lh - 1em) / 2);
  vertical-align: calc((1em - 1lh) / 2);
}

.shop-icon.left {
  float: left;
  margin-right: 15px;
}

.shop-icon.right {
  float: right;
  margin-left: 15px;
}

/* Sizes: Materialize's i.tiny / i.small / i.medium / i.large. */
.shop-icon--tiny   { font-size: 1rem; }
.shop-icon--small  { font-size: 2rem; }
.shop-icon--medium { font-size: 4rem; }
.shop-icon--large  { font-size: 6rem; }

/* Buttons: 1.3rem, and the line box is the button's own line-height. */
.btn .shop-icon,
.btn-large .shop-icon,
.btn-small .shop-icon,
.btn-floating .shop-icon,
.btn-flat .shop-icon {
  font-size: 1.3rem;
  line-height: inherit;
}

.btn-large .shop-icon { font-size: 1.6rem; }
.btn-small .shop-icon { font-size: 1.2rem; }

/* Pagination arrows. */
.pagination li .shop-icon {
  font-size: 2rem;
}

/* A search field's leading icon. `.input-field .prefix` also matches the <svg>
   and gives it Materialize's 3rem width, which would centre the glyph in a wide box
   instead of leaving it flush left like the ligature, so the width is put back. */
.input-field .shop-icon.prefix {
  position: absolute;
  top: 0.5rem;
  width: 1em;
  font-size: 2rem;
}

/* Materialize's navbar makes every icon a block 56px tall. */
nav .shop-icon {
  display: block;
  font-size: 24px;
  line-height: 56px;
}

/* ...and 64px tall from 601px up, as Materialize's own media query has it. */
@media only screen and (min-width: 601px) {
  nav .nav-wrapper .shop-icon {
    line-height: 64px;
  }
}

nav .brand-logo .shop-icon {
  float: left;
  margin-right: 15px;
}

nav ul a.btn > .shop-icon,
nav ul a.btn-large > .shop-icon,
nav ul a.btn-small > .shop-icon,
nav ul a.btn-flat > .shop-icon,
nav ul a.btn-floating > .shop-icon {
  line-height: inherit;
}

.card .card-content .card-title .shop-icon {
  line-height: 32px;
}

/* ─── Navbar ──────────────────────────────────────────────────────────────── */

.shop-nav .brand-logo.shop-nav__brand {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding-left: 0.5rem;
  font-size: 1.3rem;
  font-weight: 500;
  letter-spacing: 0.01em;
}

.shop-nav .brand-logo.shop-nav__brand i,
.shop-nav .brand-logo.shop-nav__brand .shop-icon {
  font-size: 1.4rem;
}

.shop-nav__cart {
  position: relative;
  display: flex;
  align-items: center;
}

@keyframes cart-added-pulse {
  0%   { transform: scale(1); }
  30%  { transform: scale(1.35); }
  55%  { transform: scale(0.95); }
  75%  { transform: scale(1.2); }
  100% { transform: scale(1); }
}

.shop-nav__cart.cart-added-pulse {
  animation: cart-added-pulse 0.55s ease-in-out;
}

.shop-nav__cart-badge {
  position: absolute;
  top: 8px;
  right: -4px;
  background: #fff;
  color: var(--alchemy-stripe-accent);
  border-radius: 50%;
  width: 18px;
  height: 18px;
  font-size: 0.65rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}

/* ─── Flash messages ──────────────────────────────────────────────────────── */

.alchemy-stripe-flash {
  display: flex;
  align-items: center;
  margin: 0;
  padding: 0.75rem 1rem;
  border-radius: 4px;
}

/* ─── Shop layout ─────────────────────────────────────────────────────────── */

.shop-header {
  padding: 1.5rem 0 0;
}

.shop-title {
  margin: 0;
  font-weight: 400;
  letter-spacing: -0.01em;
}

.shop-search-field {
  margin-top: 0.25rem;
}

.shop-filters-row {
  margin-top: 0.5rem;
  margin-bottom: 0;
}

.shop-filter-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding: 0.5rem 0 0.75rem;
}

.shop-filter-chip {
  cursor: pointer;
  -webkit-user-select: none;
     -moz-user-select: none;
          user-select: none;
  transition: background-color 0.15s, color 0.15s;
  display: inline-flex;
  align-items: center;
  gap: 0.2rem;
}

.shop-filter-chip.active {
  background-color: var(--alchemy-stripe-accent);
  color: var(--alchemy-stripe-accent-contrast);
}

.shop-meta-row {
  padding: 0.5rem 0 0.75rem;
}

.browser-default.shop-sort {
  display: inline-block;
  width: auto;
  border: 1px solid #9e9e9e;
  border-radius: 4px;
  padding: 0.3rem 0.5rem;
  font-size: 0.875rem;
  background: #fff;
  color: #424242;
}

/* ─── Product card ─────────────────────────────────────────────────────────── */

/* Equal-height cards: stretch columns to match the tallest in the row */
#product-grid {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
}

#product-grid .product-card-col {
  display: flex;
}

#product-grid .col {
  margin-bottom: 0; /* row gap handled by card margin */
}

/* Both hide rules below MUST keep the `#product-grid` prefix. `#product-grid
   .product-card-col { display: flex }` above carries an id (specificity
   0,1,1,0), and no number of classes can outrank an id — a rule written as
   `.product-card-col:has(...)` matches the element and still loses, so the card
   stays visible with no error anywhere. That is how the sold-out hide silently
   did nothing before; see LESSONS.md 2026-08-06. */

/* Hide unsynced placeholder cards entirely */
#product-grid .product-card-col:has(.product-card__unconfigured) {
  display: none;
}

/* Hide sold-out cards entirely — the shop lists in-stock products only, unless
   an admin opts in via the shop_header `show_sold_out` toggle (which sets
   data-show-sold-out="true" on the wrapper). The pagination filter in
   _shop.html.erb reads the same attribute and must stay in step with this rule.
   NOTE: keyed on __sold-out-badge specifically, not __stock-badge — the
   low-stock badge on products/index.html.erb must never match here. */
.alchemy-stripe-shop:not([data-show-sold-out="true"]) #product-grid .product-card-col:has(.product-card__sold-out-badge) {
  display: none;
}

.product-card {
  display: flex;
  flex-direction: column;
  width: 100%;
  margin-bottom: 1.5rem;
}

.product-card .card-image {
  position: relative;
  overflow: hidden;
}

.product-card .card-image img.product-card__img {
  height: 200px;
  width: 100%;
  -o-object-fit: cover;
     object-fit: cover;
  display: block;
}

.product-card .product-card__no-image {
  height: 200px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.product-card .product-card__no-image .shop-icon {
  font-size: 3.5rem;
}

.product-card__sold-out-badge,
.product-card__stock-badge {
  position: absolute;
  top: 0.6rem;
  right: 0.6rem;
}

.product-card .card-content {
  flex: 1;
  padding: 1rem 1rem 0.5rem;
}

.product-card .card-title.product-card__name {
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.35;
  margin-bottom: 0.4rem;
  white-space: normal;
}

.product-card .card-title.product-card__name a:hover {
  text-decoration: underline;
}

.product-card .product-price {
  font-size: 1.05rem;
  font-weight: 700;
  margin: 0 0 0.5rem;
}

.product-card .product-description {
  font-size: 0.875rem;
  line-height: 1.5;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin: 0;
}

.product-card .product-stock-warning {
  font-size: 0.8rem;
  font-weight: 600;
  margin: 0.4rem 0 0;
  display: flex;
  align-items: center;
  gap: 0.2rem;
}

.product-card .card-action {
  padding: 0.75rem 1rem;
  border-top: 1px solid rgba(0, 0, 0, 0.06);
  margin-top: auto;
}

.product-card__image-link {
  display: block;
  line-height: 0; /* removes inline gap below image */
}

.product-card__form {
  margin: 0;
}

.product-card__actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;

  input[type="number"] {
    width: 10% !important;
  }
}

.product-card__qty-input {
  width: 3.5rem;
  height: auto !important;
  border: 1px solid #9e9e9e !important;
  border-radius: 3px !important;
  padding: 0.2rem 0.4rem !important;
  text-align: center;
  font-size: 0.9rem;
  box-sizing: border-box;
  margin: 0 !important;
  flex-shrink: 0;
}

.product-card__add-btn {
  flex: 1;
  text-align: center;
}

/* ─── Product page (show / element) ──────────────────────────────────────── */

.product-page__breadcrumb {
  background: transparent !important;
  box-shadow: none !important;
  padding: 0;
  margin-bottom: 0.5rem;
}

.product-page__breadcrumb .nav-wrapper {
  padding: 0;
}

.product-page__breadcrumb .breadcrumb::before {
  color: #9e9e9e;
}

.product-page__image-col {
  margin-bottom: 1.5rem;
}

.product-page__image-wrapper img.product-page__img {
  width: 100%;
  max-height: 480px;
  -o-object-fit: cover;
     object-fit: cover;
  border-radius: 4px;
}

.product-page__no-image {
  min-height: 300px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
}

/* Shown instead of .product-page__image-wrapper when a product has 2+
   uploaded photos (app/views/alchemy/elements/_product_page.html.erb). */
.product-page__carousel {
  position: relative;
}

.product-page__carousel-track {
  position: relative;
  width: 100%;
  max-height: 480px;
  overflow: hidden;
  border-radius: 4px;
}

.product-page__carousel-slide {
  display: none;
}

.product-page__carousel-slide.is-active {
  display: block;
}

.product-page__carousel-slide img.product-page__img {
  max-height: 480px;
}

.product-page__carousel-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 40px;
  height: 40px;
  border: none;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.5);
  color: #fff;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}

.product-page__carousel-arrow--prev {
  left: 0.5rem;
}

.product-page__carousel-arrow--next {
  right: 0.5rem;
}

.product-page__carousel-dots {
  display: flex;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

.product-page__carousel-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: none;
  background: #ccc;
  cursor: pointer;
  padding: 0;
}

.product-page__carousel-dot.is-active {
  background: var(--alchemy-stripe-accent, #26a69a);
}

.product-page__details {
  padding-left: 1.5rem;
}

@media (max-width: 600px) {
  .product-page__details {
    padding-left: 0;
  }
}

.product-page__name {
  font-weight: 500;
  margin: 0 0 0.5rem;
  line-height: 1.25;
}

.product-page__details .product-price {
  font-size: 1.6rem;
  font-weight: 700;
  margin: 0 0 0.75rem;
}

.product-stock-chip {
  margin-bottom: 0.25rem;
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
}

.product-page__description {
  color: #555;
  line-height: 1.7;
  margin: 0.75rem 0 1.25rem;
}

.product-page__qty-row {
  margin-bottom: 0.5rem;
}

.product-page__qty-label {
  font-weight: 500;
  color: #424242;
  font-size: 0.9rem;
}

.product-page__qty-input {
  width: 5.5rem;
  height: auto !important;
  border: 1px solid #9e9e9e !important;
  border-radius: 4px !important;
  padding: 0.45rem 0.6rem !important;
  font-size: 1rem;
  text-align: center;
  box-sizing: border-box;
  margin: 0 !important;
}

/* ─── Cart ────────────────────────────────────────────────────────────────── */

/* Screen titles are h1s (one per page) but keep the h4/h5 sizes the screens were
   designed with; Materialize would otherwise set an h1 at 4.2rem. */
h1.shop-title {
  font-size: 2.28rem;
  line-height: 110%;
  font-weight: 400;
  margin: 1.52rem 0 1.168rem;
}

h1.shop-title--sm {
  font-size: 1.64rem;
  margin: 1.0933rem 0 0.656rem;
}

.alchemy-stripe-cart h4,
.alchemy-stripe-cart h1.shop-title {
  display: flex;
  align-items: center;
  margin-bottom: 1.25rem;
}

.cart-table th {
  color: #757575;
  font-weight: 600;
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

/* ─── Cart availability notice ────────────────────────────────────────────────
   Shown whenever the cart holds more than is in stock. Deliberately persistent
   rather than a toast: it is still true on the next render, because the cart
   page no longer rewrites itself. */

.cart-availability-notice {
  border-left: 4px solid var(--alchemy-stripe-danger);
  background-color: #fff8f7;
}

.cart-availability-notice__title {
  margin: 0 0 0.5rem;
  font-weight: 600;
  color: var(--alchemy-stripe-danger);
}

.cart-availability-notice__title .shop-icon {
  vertical-align: middle;
  margin-right: 0.25rem;
}

.cart-availability-notice__list {
  margin: 0 0 0.5rem;
  padding-left: 1.25rem;
  list-style: disc;
}

.cart-availability-notice__hint {
  margin: 0;
  font-size: 0.85rem;
}

/* ─── Cart quantity stepper ───────────────────────────────────────────────────
   Three separate forms sit side by side (− , the number input, +) so quantity
   editing needs no JavaScript. `button_to` and `form_with` each emit a block
   <form>, hence the inline-flex reset — without it the three stack vertically
   and the row grows to three lines. */

.cart-qty {
  display: inline-flex;
  align-items: center;
  gap: 0.15rem;
}

.cart-qty__form {
  display: inline-flex;
  margin: 0;
}

.cart-qty__step {
  padding: 0 0.5rem;
  min-width: 2rem;
  color: var(--alchemy-stripe-accent);
}

.cart-qty__step[disabled] {
  color: #bdbdbd;
  cursor: not-allowed;
}

.cart-qty__step .shop-icon {
  font-size: 1.1rem;
  line-height: 1;
}

/* `browser-default` opts the input out of Materialize's underlined text-field
   styling, which strips the border and stretches it to the full column. */
.cart-qty__input {
  width: 3.25rem;
  height: 2rem;
  padding: 0 0.25rem;
  text-align: center;
  border: 1px solid #e0e0e0;
  border-radius: 3px;
  font-size: 0.95rem;
}

.cart-line-option {
  font-size: 0.85rem;
}

.product-option-field {
  margin-bottom: 0.75rem;
}

.product-option-field label {
  display: block;
  font-size: 0.85rem;
  margin-bottom: 0.25rem;
}

.product-option-field select,
.product-option-field input[type="text"] {
  display: block;
  width: 100%;
}

.cart-qty__note {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.72rem;
}

.cart-summary-card {
  margin-top: 0.5rem;
}

.cart-total-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 0.5rem 0 0;
  font-size: 1.05rem;
  border-top: 1px solid #e0e0e0;
  margin-top: 0.5rem;
}

.full-width-btn {
  width: 100%;
  text-align: center;
}

/* ─── Checkout success ────────────────────────────────────────────────────── */

.checkout-success-card .order-summary-table td {
  padding: 0.5rem 0;
  border-bottom: 1px solid #f5f5f5;
}

/* ─── Host-app global style resets ───────────────────────────────────────── */
/*
 * Some host apps set `p, h1, h2, h3 { text-align: center }` globally.
 * Shop and cart layouts are left-aligned — reset here so the engine is
 * self-contained regardless of what the host app does.
 */
.alchemy-stripe-shop p,
.alchemy-stripe-shop h1,
.alchemy-stripe-shop h2,
.alchemy-stripe-shop h3,
.alchemy-stripe-shop h4,
.alchemy-stripe-cart p,
.alchemy-stripe-cart h1,
.alchemy-stripe-cart h2,
.alchemy-stripe-cart h3,
.alchemy-stripe-cart h4,
.alchemy-stripe-product p,
.alchemy-stripe-product h1,
.alchemy-stripe-product h2,
.alchemy-stripe-product h3,
.alchemy-stripe-product h4,
.alchemy-stripe-product-page p,
.alchemy-stripe-product-page h1,
.alchemy-stripe-product-page h2,
.alchemy-stripe-product-page h3,
.alchemy-stripe-product-page h4 {
  text-align: left;
}

/* ─── Accessibility and member-page helpers ─────────────────────────────────── */

.shop-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Filter chips are real buttons / links now: reset the button chrome and show focus. */
button.shop-filter-chip {
  border: 0;
  font: inherit;
  line-height: 32px;
}
.shop-filter-chip:focus-visible {
  outline: 2px solid var(--alchemy-stripe-accent-dark);
  outline-offset: 2px;
}

/* Materialize's default grey-text (#9e9e9e) is about 2.7:1 on white; these read at 4.5:1+. */
.shop-text-muted { color: #595959; }
.shop-text-strong { color: #212121; }

/* "Account pending activation": dark amber on the soft warning fill, over 4.5:1. */
.shop-notice-warning {
  background-color: var(--alchemy-stripe-warning-soft);
  color: var(--alchemy-stripe-warning-strong, #7a3e00);
}

.shop-order-list { border: none; }
.shop-order-list__item { padding: 0.5rem 0; border-bottom: 1px solid #f0f0f0; }
.shop-order-list__row { display: flex; justify-content: space-between; align-items: center; }
.shop-success-processing { margin-top: 1rem; }
