/* ==========================================================================
   LANE: product detail
   Owner: shinari-opencart-code-35
   --------------------------------------------------------------------------
   Loaded ONLY on product pages, via document->addStyle() from
   modifications/shinari-product-page.ocmod.xml. It is not linked from
   header.twig on purpose - a global <link> would download this on every page
   of the store for a page most visitors never reach, and would put this lane
   inside the header lane's file.

   Load order is stock stylesheet.css -> shinari.css -> (styles loop) -> this.
   So this file layers on top of both and inherits the :root tokens defined in
   shinari.css. It deliberately defines no tokens of its own: per
   docs/FRONTEND-LANES.md §4.3 the token block is shared, and adding a private
   palette here is how two pages start drifting apart.

   Design intent: this is the page where a purchasing officer decides. Part
   number, availability, key dimensions, price and MOQ all have to be legible
   without scrolling or opening a tab. Density over decoration.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Product title
   -------------------------------------------------------------------------- */

.sh-title {
  font-size: 23px;
  font-weight: 600;
  line-height: 1.3;
  margin: 10px 0 14px;
}

/* --------------------------------------------------------------------------
   Identity - part number and brand

   The part number is the primary key a trade buyer works with, so it gets a
   monospaced face: digits and letters line up when comparing two codes, and
   1/l/0/O stay distinguishable. `user-select: all` makes one click select the
   whole code for pasting into a PO or an email.
   -------------------------------------------------------------------------- */

.sh-ident {
  border-top: 1px solid var(--shinari-rule);
  border-bottom: 1px solid var(--shinari-rule);
  padding: 10px 0;
  margin-bottom: 12px;
}

.sh-ident__row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 3px 0;
  font-size: 13px;
}

.sh-ident__label {
  color: var(--shinari-muted);
  flex: 0 0 auto;
  min-width: 92px;
}

.sh-ident__partno {
  font-family: Consolas, "SF Mono", Menlo, "DejaVu Sans Mono", monospace;
  font-size: 15px;
  font-weight: 600;
  color: var(--shinari-ink);
  letter-spacing: .01em;
  -webkit-user-select: all;
  user-select: all;
  word-break: break-all;
}

.sh-ident__brand {
  font-weight: 600;
  color: var(--shinari-primary-dark);
}

/* --------------------------------------------------------------------------
   Availability

   Two states only, and neither invents anything. The TEXT is always the
   merchant's own string from the controller. Colour is the only thing decided
   here: green when quantity > 0, amber when it is a lead time.

   Amber, not red: on this catalogue a zero quantity means Pre-Order or
   "2-3 Days", which is a caveat, not a refusal. No product carries an Out Of
   Stock status, so there is no red state to design for. The danger token stays
   unused on this page rather than being borrowed for emphasis.
   -------------------------------------------------------------------------- */

.sh-avail {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 14px;
  padding: 8px 11px;
  border-radius: var(--shinari-radius);
  border: 1px solid var(--shinari-rule);
  background: var(--shinari-surface-alt);
  color: var(--shinari-ink);
  margin: 0 0 12px;
}

.sh-avail__label { color: var(--shinari-muted); }

.sh-avail--in {
  color: var(--shinari-success);
  background: var(--shinari-success-bg);
  border-color: var(--shinari-success);
}

.sh-avail--lead {
  color: var(--shinari-warning);
  background: var(--shinari-warning-bg);
  border-color: var(--shinari-warning);
}

.sh-avail--in .sh-avail__label,
.sh-avail--lead .sh-avail__label { color: inherit; } /* no opacity: at .8 even the darker success green blends to 3.86:1 on its tint (gap-pass L22) */

/* --------------------------------------------------------------------------
   Key specifications

   A two-column definition list rather than a table: at most three rows on this
   catalogue, so a <table> would be heavier markup for the same result.

   `white-space: pre-line` preserves the newlines a few attribute values carry
   ("-400, 700cc/tube" / "-6 tube/carton" is stored as two lines) without
   needing an nl2br filter, and collapses the surrounding indentation.
   -------------------------------------------------------------------------- */

.sh-keyspec {
  display: grid;
  /* Both tracks need an explicit 0 floor. A bare `1fr` and a bare `auto` are
     floored at min-content, so a long unbroken attribute value pushes the grid
     - and the whole col-sm-4 column - wider than its container. Measured: 5px
     of overflow at the 820px breakpoint before this. */
  grid-template-columns: minmax(70px, auto) minmax(0, 1fr);
  gap: 4px 12px;
  margin: 0 0 14px;
  padding: 10px 11px;
  background: var(--shinari-surface-alt);
  border: 1px solid var(--shinari-rule);
  border-radius: var(--shinari-radius);
  font-size: 13px;
}

.sh-keyspec dt {
  color: var(--shinari-muted);
  font-weight: 400;
}

.sh-keyspec dd {
  margin: 0;
  font-weight: 600;
  color: var(--shinari-ink);
  white-space: pre-line;
}

/* --------------------------------------------------------------------------
   Price
   -------------------------------------------------------------------------- */

.sh-price {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 14px;
}

.sh-price__now {
  font-size: 27px;
  font-weight: 700;
  line-height: 1.1;
  color: var(--shinari-ink);
}

.sh-price__now--special { color: var(--shinari-primary-dark); }

.sh-price__was {
  font-size: 16px;
  color: var(--shinari-muted);
  text-decoration: line-through;
}

.sh-price__tax,
.sh-price__points {
  font-size: 12px;
  color: var(--shinari-muted);
  flex: 1 0 100%;
}

/* Dormant on this catalogue - 0 product_discount rows. Styled so real
   quantity breaks look right the day they are entered, never populated here. */
.sh-tiers {
  list-style: none;
  padding: 8px 11px;
  margin: 0 0 14px;
  border: 1px solid var(--shinari-rule);
  border-radius: var(--shinari-radius);
  font-size: 13px;
}

.sh-tiers li {
  display: flex;
  justify-content: space-between;
  padding: 2px 0;
}

.sh-tiers__price { font-weight: 600; }

/* --------------------------------------------------------------------------
   Order block - MOQ, quantity, add to cart

   The MOQ line sits above the field because it is a constraint on the number
   about to be typed, not a footnote to it.
   -------------------------------------------------------------------------- */

.sh-order {
  border-top: 1px solid var(--shinari-rule);
  padding-top: 14px;
  margin-top: 4px;
}

.sh-moq {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  font-weight: 600;
  color: var(--shinari-warning);
  background: var(--shinari-warning-bg);
  border: 1px solid var(--shinari-warning);
  border-radius: var(--shinari-radius);
  padding: 7px 10px;
  margin: 0 0 12px;
}

.sh-order__row {
  display: flex;
  /* MUST wrap. This row is not a closed set: the PayPal SDK injects its own
     button container into the form at runtime, as a sibling of the cart
     button, so it becomes a flex item of this row whether we planned for it or
     not. Measured at 820px: PayPal's container is a fixed 200px that cannot
     shrink, which both pushed 130px outside the column AND squeezed the real
     Add to Cart button down to 34px wide. Wrapping is what makes a row safe
     against children that arrive after the CSS was written. */
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 10px;
}

.sh-order__qty { flex: 0 0 96px; }

/* Give PayPal's injected button its own full-width line rather than fighting
   it for horizontal space. Attribute-substring match because the SDK owns
   those class names and may version them. Layout only - nothing here hides or
   disables the button, and the payment flow is untouched. */
.sh-order__row > [class*="paypal"] {
  flex: 1 0 100%;
  min-width: 0;
}

.sh-order__qty label {
  display: block;
  font-size: 12px;
  color: var(--shinari-muted);
  margin-bottom: 4px;
  font-weight: 400;
}

.sh-order__qty .form-control {
  height: 42px;
  font-size: 16px;
  font-weight: 600;
  text-align: center;
  padding: 6px 8px;
}

.sh-order__cart {
  /* 140px basis, not auto: in the tablet band col-sm-4 leaves ~114px beside
     the quantity field, so the button drops to its own full-width line rather
     than being crushed. At desktop width it sits beside the field as intended. */
  flex: 1 1 140px;
  /* Flex items are floored at min-content (min-width: auto), so a btn-lg
     "Add to Cart" refuses to shrink and shoves the buy column out. That is
     invisible at 1440px, where col-sm-4 is ~370px, and invisible on mobile,
     where the column goes full width - it only bites in the tablet band where
     col-sm-4 is ~250px. Measured 162px of overflow at 820px before this line,
     which pushed the PayPal button iframe off-canvas and gave the whole
     document a horizontal scrollbar. white-space: normal lets the label wrap
     once it is allowed to narrow. */
  min-width: 0;
  height: 42px;
  font-size: 15px;
  font-weight: 600;
  white-space: normal;
}

/* --------------------------------------------------------------------------
   Image gallery

   Stock renders the main image and the extras as one undifferentiated <ul> of
   thumbnails, so a product with 8 images shows nine same-sized boxes and the
   main one is not obviously the main one. This gives the first item the full
   width and lays the rest out as a strip beneath it.
   -------------------------------------------------------------------------- */

.thumbnails {
  list-style: none;
  padding: 0;
  margin: 0 0 18px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;

  /* The cap that stops the hero image swallowing the page.

     The image column is col-sm-8, and since .container went full width that is
     ~1017px at 1920. With `width: 100%` on the image below, a 228x228 generated
     thumbnail was being stretched to 965x965 - four times its own size, so the
     product photo was both enormous AND soft. Measured, not guessed: 965 at
     1920, 885 at 1440, 338 at 390.

     Capping the gallery rather than the image keeps the extra-images strip
     aligned under the main shot instead of running the full column width.

     460 pairs with the generated thumbnail being 500px square (Extensions >
     Themes > Default > Image dimensions). Displaying a 500px file at ~438
     inside the padding is a slight downscale, which is sharp; the old pairing
     was a 4x upscale. If that setting is ever lowered, lower this with it. */
  max-width: 460px;
}

.thumbnails > li:first-child {
  flex: 0 0 100%;
}

.thumbnails > li:first-child .thumbnail {
  margin: 0;
  padding: 10px;
  border-color: var(--shinari-rule);
  border-radius: var(--shinari-radius);
}

.thumbnails > li:first-child img {
  width: 100%;
  height: auto;
}

.thumbnails > li.image-additional {
  flex: 0 0 auto;
}

.thumbnails > li.image-additional .thumbnail {
  margin: 0;
  padding: 4px;
  border-color: var(--shinari-rule);
  border-radius: var(--shinari-radius);
  transition: border-color .15s ease;
}

.thumbnails > li.image-additional .thumbnail:hover,
.thumbnails > li.image-additional .thumbnail:focus {
  border-color: var(--shinari-primary);
}

/* --------------------------------------------------------------------------
   Tabs
   -------------------------------------------------------------------------- */

#product-product .nav-tabs {
  border-bottom: 2px solid var(--shinari-rule);
}

#product-product .nav-tabs > li > a {
  font-size: 14px;
  font-weight: 600;
  color: var(--shinari-muted);
  border: 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -2px;
  border-radius: 0;
  padding: 9px 15px;
}

#product-product .nav-tabs > li > a:hover {
  background: transparent;
  color: var(--shinari-ink);
}

#product-product .nav-tabs > li.active > a,
#product-product .nav-tabs > li.active > a:hover,
#product-product .nav-tabs > li.active > a:focus {
  color: var(--shinari-primary-dark);
  background: transparent;
  border: 0;
  border-bottom: 2px solid var(--shinari-primary);
}

#product-product .tab-content {
  padding: 16px 0;
  font-size: 14px;
}

/* The full specification table in the tab. Same data as .sh-keyspec above;
   this stays as the canonical complete view. */
#tab-specification .table {
  font-size: 13px;
  margin-bottom: 0;
}

#tab-specification .table td { padding: 7px 10px; }

#tab-specification thead td {
  background: var(--shinari-surface-alt);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .04em;
}

/* --------------------------------------------------------------------------
   Related products
   -------------------------------------------------------------------------- */

#product-product > .container > h3,
#product-product h3.sh-related-title {
  font-size: 17px;
  font-weight: 600;
  padding-bottom: 10px;
  border-bottom: 2px solid var(--shinari-rule);
  margin: 30px 0 18px;
}

/* --------------------------------------------------------------------------
   Responsive

   Below 768px Bootstrap 3 stacks the columns, which puts the whole buy panel
   under the gallery. Tighten the type so the decision data still fits on one
   phone screen once stacked.
   -------------------------------------------------------------------------- */

@media (max-width: 767px) {
  .sh-title       { font-size: 20px; }
  .sh-price__now  { font-size: 24px; }

  .sh-ident__row  { font-size: 13px; }
  .sh-ident__label { min-width: 80px; }

  /* Full-width cart button on a phone; the qty field keeps a thumb-sized
     target rather than shrinking with it. */
  .sh-order__row  { flex-wrap: wrap; }
  .sh-order__qty  { flex: 0 0 90px; }
  .sh-order__cart { flex: 1 1 100%; }

  /* 44px, not the 42px used at desktop. The button is full width here so the
     horizontal target is never the problem - the height is, and 42 is two
     short of the touch floor. The matching rule for .sh-order__rfq lives with
     that rule at the foot of this file, NOT here: a media query adds no
     specificity, and .sh-order__rfq is defined after this block, so a 44px
     declaration here loses to its own 42px base rule on source order.
     Measured, after doing exactly that. */
  .sh-order__cart { height: 44px; }

  .thumbnails > li.image-additional .thumbnail img { width: 56px; height: auto; }
}

/* --------------------------------------------------------------------------
   Request Quote (WhatsApp)
   Added 2026-09-25 by shinari-opencart-code-c1, product-detail lane.

   Its own full-width line, never beside Add to Cart. The buy row is already
   tight - at desktop col-sm-4 is about 370px and the quantity field takes 96
   of it, and the tablet band is worse at roughly 250px - so a third item on
   that line would crush both buttons. The existing comments on .sh-order__cart
   record 162px of measured overflow from exactly that mistake. flex 1 0 100%
   is the same treatment the PayPal container already gets, and for the same
   reason.

   It also reads correctly: Add to Cart is the primary path and keeps the
   primary colour; Request Quote is the alternative for a buyer who wants a
   price on 500 rather than 5.

   THE TWO COLOUR LITERALS BELOW ARE DELIBERATE AND MUST NOT BECOME TOKENS.

   docs/FRONTEND-LANES.md section 4 is right that literals outside :root are
   what stop a re-skin being a one-block change - but these are WhatsApp's
   brand colours, identifying a third-party channel. They should NOT follow the
   Shinari palette when it changes, which is precisely what making them tokens
   would cause.

   #075E54 is WhatsApp's dark teal, measured 7.8:1 on white, so it is safe as
   text and as an icon at this weight. WhatsApp's familiar bright green
   (#25D366) is 1.9:1 on white and fails AA at BOTH polarities - white on it
   and it on white are the same number - so it appears nowhere here. That is
   the same trap already documented for --shinari-accent in shinari.css.
   -------------------------------------------------------------------------- */

.sh-order__rfq {
  flex: 1 0 100%;
  min-width: 0;
  height: 42px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-size: 15px;
  font-weight: 600;
  white-space: normal;
  color: #075E54;
  background: var(--shinari-surface);
  border: 1px solid var(--shinari-rule);
  border-radius: var(--shinari-radius);
}

.sh-order__rfq:hover,
.sh-order__rfq:focus {
  color: #075E54;
  background: #EAF6F2;
  border-color: #075E54;
  text-decoration: none;
}

.sh-order__rfq:focus-visible {
  outline: 2px solid #075E54;
  outline-offset: 2px;
}

.sh-order__rfq .fa-whatsapp {
  font-size: 19px;
}

@media (max-width: 767px) {
  /* Must sit after .sh-order__rfq's base rule, not in the mobile block higher
     up the file - see the note there. */
  .sh-order__rfq {
    height: 44px;
  }
}

/* --------------------------------------------------------------------------
   Description content pasted from suppliers' websites
   Owner: shinari-opencart-code-c4 - site test finding, 2026-09-28

   Descriptions are merchant HTML, and some of it is copied straight off a
   supplier's page with that page's inline styles attached. Product 259 (FESTO
   MFH-5-1/8) carries a list with `style="... width: 1465px"`, which made the
   whole product page 146px wider than a 1366 window and 1,090px wider than a
   phone.

   Nothing in a description may be wider than the description itself:
   max-width beats an inline width, so a hard-coded width shrinks to fit while
   everything narrower is left alone. Long unbroken strings (part numbers, URLs)
   wrap instead of pushing out. A table that genuinely needs its columns keeps
   them and scrolls sideways inside the tab, not the page.
   -------------------------------------------------------------------------- */
#tab-description {
  overflow-wrap: anywhere;
}

#tab-description * {
  max-width: 100%;
}

#tab-description img {
  height: auto;
}

#tab-description table {
  display: block;
  overflow-x: auto;
}

/* --------------------------------------------------------------------------
   Description text size
   Owner: ifast-backend-57 - the user's request, 2026-10-15

   Every description reads at 18px - "font size 18 for all". The tab used to
   inherit 14px, and 33 of 111 descriptions bring their own inline sizes from
   the supplier page they were copied from, 9px to 18px (product 241, Zap-Ox,
   was 12px), so one product read at a different size from the next.

   An inline style can only be beaten by !important, so every element inside
   the tab is told to take the tab's size and line height. Headings come down
   to 18px too (one description, product 249, uses <h1>, at an inline 15px).
   Superscript and subscript keep their smaller size: m² and ® mean
   something only while they are small.

   18px is written in the site's own units, like every other size here, so on
   desktop screens 1440px and wider the page scale in shinari.css shows it at
   about 15px, in proportion with the rest of the page.
   -------------------------------------------------------------------------- */
#tab-description {
  font-size: 18px;
  line-height: 1.6;
}

#tab-description *:not(sub):not(sup) {
  font-size: inherit !important;
  line-height: inherit !important;
}

/* --------------------------------------------------------------------------
   Live price note
   Owner: ifast-backend-57 - the user's request, 2026-10-16

   The price follows the chosen options (modifications/shinari-live-price,
   the script at the foot of product.twig). When the options add to it, this
   line says so, so the larger figure does not read as a different product.
   -------------------------------------------------------------------------- */
.sh-price__note {
  display: block;
  margin-top: 2px;
  font-size: 13px;
  color: var(--shinari-muted);
}

.sh-price__note[hidden] {
  display: none;
}
