/* ============================================================
   OSJ Sharp — components
   ------------------------------------------------------------
   Mobile-first: the base rules are the <=760px layout and
   min-width queries step up. The comps are drawn desktop-first;
   this file inverts that, per README "Responsive behavior".
   ============================================================ */

/* ============================================================
   Buttons
   ============================================================ */
.osj-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  height: var(--control-h-md);
  padding: 0 22px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  font-size: 14.5px;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  text-align: center;
  text-decoration: none;
  transition: background var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out),
              color var(--dur-base) var(--ease-out);
}
.osj-btn:hover { text-decoration: none; }
.osj-btn svg { flex-shrink: 0; }

.osj-btn--primary {
  background: var(--brand);
  color: var(--text-on-brand);
}
.osj-btn--primary:hover,
.osj-btn--primary:focus-visible {
  background: var(--brand-hover);
  color: var(--text-on-brand);
}

/*
 * Secondary. HANDOFF.md "Design-system gap: no on-dark secondary button" —
 * the DS has no inverse outline variant, so the hero's second CTA carried an
 * inline override. Rather than add a `secondary-on-dark` class that authors
 * must remember, the on-dark treatment keys off the same [data-theme="dark"]
 * attribute every ink band already carries. One class, correct on both grounds.
 */
.osj-btn--secondary {
  background: transparent;
  border-color: var(--border-strong);
  color: var(--text-strong);
}
.osj-btn--secondary:hover,
.osj-btn--secondary:focus-visible {
  background: var(--surface-sunken);
  color: var(--text-strong);
}
[data-theme="dark"] .osj-btn--secondary {
  border-color: var(--border-on-dark-loud);
  color: var(--sand-50);
}
[data-theme="dark"] .osj-btn--secondary:hover,
[data-theme="dark"] .osj-btn--secondary:focus-visible {
  background: rgba(214, 155, 54, 0.14);
  border-color: var(--brand);
  color: var(--sand-50);
}

.osj-btn--lg { height: var(--control-h-lg); padding: 0 26px; font-size: 15px; }
.osj-btn--sm { height: var(--control-h-sm); padding: 0 16px; font-size: 13.5px; }
.osj-btn--block { display: flex; width: 100%; }

/* Compact mono "Add" button on cross-sell cards. */
.osj-btn--ghost-mono {
  height: auto;
  padding: 7px 12px;
  border-color: var(--border-strong);
  background: transparent;
  color: var(--text-strong);
  font-family: var(--font-mono);
  font-weight: var(--weight-medium);
  font-size: 11.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.osj-btn--ghost-mono:hover { background: var(--surface-sunken); }

/* ============================================================
   Product card — THE component. Identical on the front page, shop
   archive, related products and cross-sell rows.
   ============================================================ */
.osj-card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  overflow: hidden;
  transition: transform var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out);
}
/*
 * :focus-within gives the keyboard the same affordance as the mouse —
 * the lift, the image swap and Quick view all trigger on focus.
 */
.osj-card:hover,
.osj-card:focus-within {
  transform: translateY(-4px);
  box-shadow: var(--shadow-card-hover);
}

/* On the ink SWU band the card keeps its white body but needs a hairline
   that reads against the dark, not the sand hairline. */
[data-theme="dark"] .osj-card { border-color: rgba(251, 247, 240, 0.1); }

.osj-card__media {
  position: relative;
  display: block;
  aspect-ratio: 4 / 3;
  background: var(--surface-sunken);
  overflow: hidden;
}
.osj-card__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/*
 * Hover crossfade. The swap image is the product's first gallery image;
 * products without one get .osj-card__img--stand-in, which scales the same
 * photo 1.07x so the card still reads as interactive. See the asset gaps in
 * HANDOFF.md — six named products still need a real second shot.
 */
.osj-card__img--swap {
  opacity: 0;
  transition: opacity var(--dur-slow) ease;
}
.osj-card:hover .osj-card__img--swap,
.osj-card:focus-within .osj-card__img--swap { opacity: 1; }
.osj-card__img--stand-in { transform: scale(1.07); }

.osj-card__badge {
  position: absolute;
  top: 10px;
  left: 10px;
  padding: 4px 8px;
  border-radius: var(--radius-pill);
  background: var(--brand);
  color: var(--ink-900);
  font-family: var(--font-mono);
  font-weight: var(--weight-semibold);
  font-size: 10.5px;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
}
.osj-card__badge { z-index: 2; } /* above the hover scrim */
.osj-card__badge--seller { background: var(--ink-900); color: var(--amber-400); }
.osj-card__badge--sale { background: var(--price-sale); color: var(--sand-50); }

/*
 * Centred on the photo. The comp drew it as a bar pinned to the bottom edge,
 * but centred reads as "act on this image" rather than as a caption, and it
 * stays clear of the badge in the top-left corner.
 *
 * Centring is done with translate(-50%, -50%), so the hover animation has to
 * compose with that offset rather than replace it.
 */
.osj-card__quick {
  position: absolute;
  top: 50%;
  left: 50%;
  padding: 10px 18px;
  border: none;
  border-radius: var(--radius-pill);
  background: rgba(20, 17, 11, 0.9);
  color: var(--sand-50);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-align: center;
  white-space: nowrap;
  /* Base state for pointer devices before the hover query below takes over.
     Touch never sees this button at all — see the no-hover rule further down. */
  opacity: 1;
  transform: translate(-50%, -50%);
  transition: opacity var(--dur-base) ease, transform var(--dur-base) var(--ease-out);
}

.osj-card__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--space-2);
  padding: 16px 16px 18px;
}
.osj-card__eyebrow {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.osj-card__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  font-size: 15px;
  line-height: 1.3;
  color: var(--text-strong);
  text-wrap: pretty;
}
.osj-card__title a { color: inherit; }
.osj-card__title a:hover { color: inherit; text-decoration: underline; }
/* Stretch the title link over the whole card so the hit area is the card,
   without nesting the Quick view button inside an anchor.

   The z-order matters and is not arbitrary. The media used to sit above the
   stretched link, which meant a tap on the photo hit nothing — fine on desktop
   where Quick view covers it, useless on a phone where Quick view is gone. The
   link now sits above the photo, and Quick view sits above the link so a mouse
   can still reach it. */
.osj-card__title a::after { content: ""; position: absolute; inset: 0; z-index: 2; }

/* No z-index on the media. It only needs to be a containing block for the
   image swap and the hover scrim, and giving it one would open a stacking
   context that traps Quick view inside it — where no z-index the button sets
   could lift it above the stretched link. */
.osj-card__media { position: relative; }
.osj-card__quick { z-index: 3; }

/*
 * Touch: there is no hover, so there is no Quick view.
 *
 * A permanently visible pill parked in the middle of every product photo is
 * clutter, and the gesture it competes with — tap the picture, see the product
 * — is the one people already expect. Hiding it hands the whole card, photo
 * included, to the stretched title link, so a tap goes straight to the PDP.
 *
 * Done in CSS rather than by not rendering the button: markup that varies by
 * device cannot be cached, and this site sits behind WP Engine's page cache,
 * which would happily serve one visitor's variant to the next.
 */
@media (max-width: 760px), (hover: none), (pointer: coarse) {
  .osj-card__quick { display: none; }
}

.osj-card__footer {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-top: auto;
  padding-top: 10px;
}
.osj-card__price {
  font-family: var(--font-mono);
  font-weight: var(--weight-semibold);
  font-size: 17px;
  font-variant-numeric: tabular-nums;
  color: var(--price);
}
.osj-card__price del { opacity: 0.5; font-weight: var(--weight-regular); margin-right: 6px; }
.osj-card__price ins { text-decoration: none; color: var(--price-sale); }
.osj-card__meta {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.06em;
  color: var(--text-muted);
  white-space: nowrap;
}

/* Compact card used in the cart cross-sell row. */
.osj-card--compact .osj-card__body { padding: 15px 15px 17px; gap: 7px; }
.osj-card--compact .osj-card__title { font-size: 14.5px; }
.osj-card--compact .osj-card__footer { align-items: center; padding-top: 8px; }
.osj-card--compact .osj-card__price { font-size: 15.5px; }

@media (min-width: 761px) {
  /* Pointer devices get the reveal; the hover query keeps it off hybrid touch. */
  @media (hover: hover) and (pointer: fine) {
    /* The 6px rise is added to the centring offset, not substituted for it. */
    .osj-card__quick { opacity: 0; transform: translate(-50%, calc(-50% + 6px)); }
    .osj-card:hover .osj-card__quick,
    .osj-card:focus-within .osj-card__quick { opacity: 1; transform: translate(-50%, -50%); }

    /* A slight darkening so the pill reads against busy product photography. */
    .osj-card__media::after {
      content: "";
      position: absolute;
      inset: 0;
      background: rgba(14, 11, 7, 0.18);
      opacity: 0;
      pointer-events: none;
      transition: opacity var(--dur-base) ease;
    }
    .osj-card:hover .osj-card__media::after,
    .osj-card:focus-within .osj-card__media::after { opacity: 1; }
  }
}

/* ============================================================
   Category tile — a card with a 5:4 stage and no price row.
   ============================================================ */
.osj-tile {
  display: flex;
  flex-direction: column;
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: transform var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out);
}
.osj-tile:hover, .osj-tile:focus-within {
  transform: translateY(-4px);
  box-shadow: var(--shadow-card-hover);
  text-decoration: none;
}
.osj-tile__media { position: relative; aspect-ratio: 5 / 4; background: var(--surface-sunken); overflow: hidden; }
.osj-tile__media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.osj-tile__body { display: flex; flex: 1; flex-direction: column; gap: 10px; padding: 22px 22px 26px; }
.osj-tile__title {
  margin: 0;
  font-size: 1.15rem;
  font-weight: var(--weight-black);
  letter-spacing: var(--tracking-snug);
  text-transform: uppercase;
  color: var(--text-strong);
}
.osj-tile__body p { font-size: 14.5px; line-height: 1.55; color: var(--text-body); }
.osj-tile__cta {
  margin-top: auto;
  padding-top: 10px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-link);
}

/* ============================================================
   Review card
   ============================================================ */
.osj-review {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin: 0;
  padding: var(--space-5);
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
}
.osj-review p { font-size: 15.5px; line-height: 1.6; color: var(--ink-600); }
.osj-review__cite {
  margin-top: auto;
  padding-top: 6px;
  border-top: 1px solid var(--border-hairline);
  font-family: var(--font-mono);
  font-size: 11.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  font-style: normal;
}

.osj-stars { display: flex; gap: 2px; color: var(--brand); }
.osj-stars svg { fill: currentColor; }

/* ============================================================
   Chips, checkboxes, forms
   ============================================================ */
.osj-chip {
  display: inline-block;
  padding: 6px 11px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-pill);
  background: var(--surface-card);
  color: var(--text-body);
  font-size: 12.5px;
  line-height: 1.4;
  transition: background var(--dur-base) var(--ease-out), color var(--dur-base) var(--ease-out);
}
.osj-chip:hover { text-decoration: none; border-color: var(--border-strong); color: var(--text-strong); }
.osj-chip--active,
.osj-chip[aria-pressed="true"] {
  background: var(--ink-900);
  border-color: var(--ink-900);
  color: var(--sand-50);
}
.osj-chip--active:hover { color: var(--sand-50); }

.osj-field {
  width: 100%;
  padding: 14px;
  border: 1px solid var(--border-field);
  border-radius: var(--radius-sm);
  background: var(--surface-card);
  font-family: var(--font-body);
  font-size: 15px;
  color: var(--text-strong);
}
.osj-field::placeholder { color: var(--text-muted); }
.osj-field--mono { font-family: var(--font-mono); }
.osj-field--sm { padding: 11px 13px; font-size: 14px; }

.osj-check {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 14.5px;
  color: var(--text-body);
  cursor: pointer;
}
.osj-check input[type="checkbox"],
.osj-check input[type="radio"] {
  width: 16px;
  height: 16px;
  accent-color: var(--brand);
  flex-shrink: 0;
}

/* Quantity stepper — used on the PDP, the cart drawer and quick view. */
.osj-qty {
  display: flex;
  align-items: center;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  background: var(--surface-card);
  overflow: hidden;
}
.osj-qty__btn {
  width: 42px;
  height: 52px;
  border: none;
  background: none;
  color: var(--text-body);
  font-size: 18px;
  line-height: 1;
}
.osj-qty__btn:hover { background: var(--surface-sunken); }
.osj-qty__btn:disabled { opacity: 0.35; cursor: not-allowed; }
.osj-qty input {
  width: 40px;
  padding: 0;
  border: none;
  background: none;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 15px;
  font-weight: var(--weight-semibold);
  -moz-appearance: textfield;
  appearance: textfield;
}
.osj-qty input::-webkit-outer-spin-button,
.osj-qty input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.osj-qty--sm .osj-qty__btn { width: 36px; height: 40px; font-size: 17px; }
.osj-qty--sm input { width: 34px; font-size: 14px; }

/* ============================================================
   Icon + text feature row (hero trust row, logistics strip, buy box)
   ============================================================ */
.osj-feature { display: flex; gap: 14px; align-items: flex-start; }
.osj-feature svg { flex-shrink: 0; margin-top: 2px; color: var(--text-link); }
.osj-feature__title {
  margin-bottom: 4px;
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  font-size: 15px;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: var(--text-strong);
}
.osj-feature p { font-size: 14px; line-height: 1.55; color: var(--text-body); }
[data-theme="dark"] .osj-feature svg { color: var(--amber-400); }

/* ============================================================
   Pagination
   ============================================================ */
.osj-pagination { display: flex; align-items: center; justify-content: center; gap: var(--space-2); margin-top: var(--space-8); flex-wrap: wrap; }
.osj-pagination .page-numbers {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 38px;
  height: 38px;
  padding: 0 10px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  background: var(--surface-card);
  color: var(--text-body);
  font-family: var(--font-mono);
  font-size: 14px;
}
.osj-pagination .page-numbers:hover { text-decoration: none; border-color: var(--border-strong); color: var(--text-strong); }
.osj-pagination .page-numbers.current {
  background: var(--ink-900);
  border-color: var(--ink-900);
  color: var(--sand-50);
}
.osj-pagination .page-numbers.dots { border-color: transparent; background: none; }

/* ============================================================
   Notices — Woo messages restyled to the palette
   ============================================================ */
.woocommerce-message,
.woocommerce-info,
.woocommerce-error {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin: 0 0 var(--space-5);
  padding: 14px 18px;
  border: 1px solid var(--border-subtle);
  border-left: 3px solid var(--brand);
  border-radius: var(--radius-sm);
  background: var(--surface-card);
  font-size: 14.5px;
  color: var(--text-body);
  list-style: none;
}
.woocommerce-error { border-left-color: var(--danger); }
.woocommerce-message { border-left-color: var(--success); }
.woocommerce-message .button,
.woocommerce-info .button { margin-left: auto; }

/* ============================================================
   Ink page header — shop archive, search, blog archives
   ============================================================ */
.osj-page-head {
  background: var(--gradient-ink-header);
  color: var(--sand-50);
  padding-block: 40px 44px;
}
.osj-page-head__title {
  margin: 14px 0 0;
  font-size: clamp(2rem, 5vw, 3rem);
  font-weight: var(--weight-ultra);
  line-height: 1;
  letter-spacing: -0.028em;
  text-transform: uppercase;
  color: var(--sand-50);
}
.osj-page-head__lede {
  max-width: 54ch;
  margin-top: 14px;
  font-size: 1.05rem;
  line-height: 1.6;
  color: var(--sand-200);
}
@media (min-width: 1001px) { .osj-page-head { padding-block: 48px 52px; } }

.osj-page-title {
  margin-bottom: var(--space-6);
  font-size: clamp(1.9rem, 4vw, 2.6rem);
  font-weight: var(--weight-ultra);
  line-height: 1.05;
  letter-spacing: -0.026em;
  text-transform: uppercase;
}

/* ============================================================
   Prose — editor output inside our type system
   ============================================================ */
.osj-prose { font-size: 15.5px; line-height: 1.65; color: var(--text-body); }
.osj-prose > * + * { margin-top: var(--space-4); }
.osj-prose h2 { font-size: 1.5rem; font-weight: var(--weight-ultra); text-transform: uppercase; letter-spacing: -0.02em; }
.osj-prose h3 { font-size: 1.2rem; font-weight: var(--weight-black); }
.osj-prose ul, .osj-prose ol { padding-left: 1.25em; }
.osj-prose li + li { margin-top: var(--space-2); }
.osj-prose img { border-radius: var(--radius-md); }
.osj-prose a { text-decoration: underline; text-underline-offset: 2px; }

/* ============================================================
   Native select, styled
   ============================================================ */
.osj-select { position: relative; display: inline-flex; align-items: center; }
.osj-select select {
  appearance: none;
  -webkit-appearance: none;
  padding: 9px 34px 9px 13px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  background: var(--surface-card);
  color: var(--text-strong);
  font-size: var(--text-sm);
  line-height: 1.3;
}
.osj-select svg {
  position: absolute;
  right: 11px;
  color: var(--text-muted);
  pointer-events: none;
}

/* ============================================================
   Modal — quick view
   ============================================================ */
.osj-modal {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  visibility: hidden;
}
.osj-modal[data-open="true"] { visibility: visible; }
.osj-modal__scrim {
  position: absolute;
  inset: 0;
  border: 0;
  padding: 0;
  background: var(--scrim);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease-out);
}
.osj-modal__panel {
  position: relative;
  width: min(880px, 100%);
  max-height: 88vh;
  /* The panel itself must not scroll — only the copy column inside it does,
     so the image and the Add to cart button stay put. */
  overflow: hidden;
  background: var(--surface-page);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  box-shadow: var(--shadow-lg);
  transform: translateY(24px);
  opacity: 0;
  transition: transform var(--dur-slow) var(--ease-out), opacity var(--dur-slow) var(--ease-out);
}
.osj-modal[data-open="true"] .osj-modal__scrim { opacity: 1; }
.osj-modal[data-open="true"] .osj-modal__panel { transform: translateY(0); opacity: 1; }
.osj-modal__close {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 2;
  display: flex;
  padding: 8px;
  border: none;
  border-radius: var(--radius-sm);
  background: var(--surface-card);
  color: var(--text-body);
  box-shadow: var(--shadow-sm);
}
.osj-modal__close:hover { color: var(--text-strong); }

@media (min-width: 761px) {
  .osj-modal { align-items: center; padding: var(--space-5); }
  .osj-modal__panel { border-radius: var(--radius-lg); }
}

/*
 * align-items: start, not the default stretch. Stretching made the media cell
 * as tall as the copy column — a 440x330 photo rendered into a 1030px box and
 * object-fit: cover cropped it into a zoomed strip.
 */
.osj-quick { display: grid; grid-template-columns: minmax(0, 1fr); align-items: start; }
.osj-quick__media { background: var(--surface-sunken); aspect-ratio: 4 / 3; overflow: hidden; }
.osj-quick__media img { width: 100%; height: 100%; object-fit: cover; }
.osj-quick__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-5);
  min-height: 0;
}

/*
 * Product short descriptions here are full spec dumps — hundreds of words.
 * A quick view is a summary, so clamp it and let the product page carry the
 * detail. Without this the panel blows past its max height and the buy button
 * lands below the fold.
 */
.osj-quick__excerpt {
  display: -webkit-box;
  -webkit-line-clamp: 5;
  line-clamp: 5;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.osj-quick__title {
  font-size: 1.5rem;
  font-weight: var(--weight-ultra);
  line-height: 1.1;
  letter-spacing: -0.024em;
  text-wrap: pretty;
}
.osj-quick__rating { display: flex; align-items: center; gap: 10px; font-family: var(--font-mono); font-size: 12.5px; color: var(--text-body); }
.osj-quick__price { font-family: var(--font-mono); font-weight: var(--weight-semibold); font-size: 1.5rem; font-variant-numeric: tabular-nums; color: var(--price); }
.osj-quick__lead {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-link);
}
.osj-quick__excerpt { font-size: 15px; line-height: 1.6; color: var(--text-body); }
/*
 * Pinned to the bottom of the copy column. However long the description runs,
 * the buy button is always on screen — the whole point of a quick view.
 */
.osj-quick__actions {
  position: sticky;
  bottom: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: auto;
  padding-top: var(--space-3);
  background: var(--surface-page);
}
.osj-quick__actions .osj-btn { width: 100%; }
.osj-card__quick.is-loading { opacity: 0.6; }

@media (min-width: 761px) {
  .osj-quick { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: stretch; }
  /*
   * Two columns: let the photo fill its side rather than leaving dead space
   * beneath it. Safe now that the clamped excerpt bounds the copy column —
   * that bound is what stops the stretch running away as it did before.
   */
  .osj-quick__media { aspect-ratio: auto; height: 100%; }
  .osj-quick__body {
    padding: var(--space-7);
    /* Only this column scrolls, and only if the copy is long. */
    max-height: 88vh;
    overflow-y: auto;
  }
  .osj-quick__actions { flex-direction: row; }
  .osj-quick__actions .osj-btn { width: auto; flex: 1; }
}

/* Bottom sheet on phones: cap it so it never fills the screen, and keep the
   copy column the only scrolling part. */
@media (max-width: 760px) {
  .osj-modal__panel { max-height: 86vh; }
  .osj-quick__body { max-height: 46vh; overflow-y: auto; }
  .osj-quick__actions { padding-bottom: env(safe-area-inset-bottom); }
}

/* ============================================================
   Specification table
   ============================================================ */
.osj-spec {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 12px 28px;
  margin: 0;
  font-size: 14.5px;
}
.osj-spec dt {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.osj-spec dd { margin: 0; color: var(--text-strong); }
.osj-spec__mono { font-family: var(--font-mono); }

/* ============================================================
   Sheets — side drawer and bottom sheet
   MOBILE-VARIANT-HANDOFF §1.3
   ============================================================ */
.osj-sheet {
  position: fixed;
  inset: 0;
  z-index: var(--z-drawer);
  display: flex;
  visibility: hidden;
}
.osj-sheet[data-open="true"] { visibility: visible; }
.osj-sheet__scrim {
  position: absolute;
  inset: 0;
  border: 0;
  padding: 0;
  background: var(--scrim);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease-out);
}
.osj-sheet[data-open="true"] .osj-sheet__scrim { opacity: 1; }

.osj-sheet__panel {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--surface-page);
  transition: transform var(--dur-slow) var(--ease-out);
}

/* Side drawer — filters. */
.osj-sheet--side { justify-content: flex-end; }
.osj-sheet--side .osj-sheet__panel {
  width: 360px;
  max-width: 90%;
  height: 100%;
  border-left: 1px solid var(--border-subtle);
  transform: translateX(100%);
}
.osj-sheet--side[data-open="true"] .osj-sheet__panel { transform: translateX(0); }

/* Bottom sheet — sort. */
.osj-sheet--bottom { align-items: flex-end; justify-content: center; }
.osj-sheet--bottom .osj-sheet__panel {
  width: 100%;
  max-width: 520px;
  padding: 8px 0 16px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  transform: translateY(100%);
}
.osj-sheet--bottom[data-open="true"] .osj-sheet__panel { transform: translateY(0); }

.osj-sheet__handle {
  display: block;
  width: 40px;
  height: 4px;
  margin: 8px auto 6px;
  border-radius: var(--radius-pill);
  background: var(--border-field);
}

.osj-sheet__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: 16px 20px;
  border-bottom: 1px solid var(--border-subtle);
}
.osj-sheet__title {
  font-family: var(--font-display);
  font-weight: var(--weight-black);
  font-size: 15px;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--text-strong);
}
.osj-sheet__title--bottom { padding: 10px 20px 12px; }
.osj-sheet__close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  border: none;
  background: none;
  color: var(--text-body);
}
.osj-sheet__close:hover { color: var(--text-strong); }

.osj-sheet__body { flex: 1; overflow-y: auto; padding: 22px 20px; }

.osj-sheet__foot {
  display: flex;
  gap: 10px;
  padding: 16px 20px 22px;
  border-top: 1px solid var(--border-subtle);
  background: var(--surface-page);
  /* Clears the iPhone home indicator when the sheet is full height. */
  padding-bottom: calc(22px + env(safe-area-inset-bottom));
}
.osj-sheet__clear { flex: 0 0 auto; min-height: 50px; }
.osj-sheet__apply { flex: 1; min-height: 50px; }

.osj-sheet__options { list-style: none; }
.osj-sheet__option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-height: 54px;
  padding: 0 20px;
  color: var(--text-strong);
  font-size: 16px;
}
.osj-sheet__option:hover { background: var(--surface-sunken); text-decoration: none; }
.osj-sheet__option.is-selected { font-weight: var(--weight-semibold); color: var(--text-link); }
