/* ══════════════════════════════════════════════════════════════════════
   THE PRODUCT PAGE

   Almost nothing. That is the point.

   The buy column now emits the concept's own elements in the concept's own
   order — h1, .pdp-full, .chips, .pdp-price, .pdp-actions, .pricebreak,
   .pdp-assure, .pdp-note — so concept.css:1660-1724 styles all of it directly
   and there is no second copy of those rules to keep in sync.

   What is left here is two things that only exist because the markup changed
   underneath rules that are still in style.css, which this file does not own.
   Both blocks are meant to be DELETED, from here and from style.css, the moment
   that file catches up. Each says which lines.
   ══════════════════════════════════════════════════════════════════════ */

/* ── 1. the flex column, stood down ───────────────────────────────────────
   style.css:632-648 makes .summary a flex column and hands its children an
   `order` each, because WooCommerce's DOM order used to disagree with the
   design's. It no longer does: the pieces are printed in the concept's
   sequence, by callbacks at the priorities that produce it.

   Left in place, that rule would now scramble what it used to fix — every
   element the concept adds (.pdp-full, .pdp-price, .pdp-actions) is unmatched
   by the list and lands on the catch-all `order:60`, i.e. after the price
   break. So the column goes back to being what it is in the concept: normal
   block flow, in source order.

   Delete with style.css:626-648. */
body.single-product div.product div.summary{
  display:block;
}
.pdp-buy .summary > *{
  order:0;
}

/* The form is now one flex row — .pdp-actions (concept.css:1688) — rather than
   two floated children, and it brings its own 22px of top margin. Without this
   the margin is paid twice. Delete with style.css:700-706.

   The rest of this rule is a name collision, not a layout choice.
   concept.css:137 declares `.cart` — the header's cart chip: a 40×40 grid box
   with a 12px radius, `place-items:center` and a wash on hover. The concept has
   exactly one `.cart` on a page and it is that chip. WooCommerce's add-to-cart
   form is `<form class="cart">`, so on a product page the design's header chip
   lands on the buy row: the form became a 40×40 square, and the stepper and the
   "Add to cart" pill hung outside it, the pill squeezed to 89px with its label
   broken over three lines. `display:block` alone was not enough — the width and
   height still applied, and `place-items:center` shrink-wrapped and centred
   .pdp-actions inside whatever was left.

   So the chip's box is undone here and only here. Nothing is restyled: every
   line below is the initial value, handing the row back to concept.css:1688
   and :1714, which then measure it exactly as they do in the concept. */
body.single-product div.product form.cart{
  display:block; margin:0;
  width:auto; height:auto; place-items:normal; border-radius:0; background:none;
}
body.single-product div.product form.cart:hover{
  background:none;
}

/* ── 2. the quantity pill, handed back to concept.css ─────────────────────
   The stepper is `.step` now (concept.css:1699), which is the class the design
   has always used for it. style.css:708-732 draws the same pill a second time
   at higher specificity — a workaround for a `.step` name collision in
   concept.css that no longer exists; the process card was renamed `.hiw`
   precisely to end it, and concept.css:2125 says so.

   Those 25 lines are now dead weight, and the one place they still disagree
   with the design is visible: `border:0` on the input drops the two hairlines
   either side of the number, and the field is 2px narrow for three digits.
   This hands both back. It re-states nothing that concept.css does not already
   say — it only wins the specificity argument until style.css:708-732 is
   deleted, at which point this goes with it. */
body.single-product div.product form.cart div.quantity.step input.qty{
  width:58px;
  font-size:16px;
  border-left:1px solid var(--line);
  border-right:1px solid var(--line);
}

/* ── 3. the price-break footnote ──────────────────────────────────────────
   New element, so concept.css has no rule for it. It sits between .pb-table
   and .pb-bogo inside the same card and takes the table's own measure — the
   18px side padding .pb-table th/td use (concept.css:585) — so the sentence
   lines up with the column it is explaining.

   It exists because the table's rows are quantities under a stepped discount:
   "each" is an average across the order, and a price table that does not say
   so is the same kind of half-truth the old per-band column was. */
.pb-note{
  margin:0; padding:10px 18px 13px;
  border-top:1px solid var(--line);
  font-size:12.5px; line-height:1.5; color:var(--muted);
}

/* ══════════════════════════════════════════════════════════════════════
   VARIABLE PRODUCTS — the attribute selectors

   18 of production's 39 published products are variable and the concept has no
   drawing of one, because the page generator flattened every variable product
   to its first price before the design was made. So this is the one part of
   the product page with no picture to copy, and the rule it is built to is the
   same one the review band is built to: invent no VALUES. Every colour,
   radius, measure, easing and shadow below is a concept.css token, and the
   shapes are the ones the design already uses — `.chip` (concept.css:1804) for
   the option, `.f input:focus` (concept.css:655) for the focus ring, `.f label`
   (:648) for the field label, `.pill` for the state.

   ── What the markup is, and what may not be assumed about it ──────────

   It is WooCommerce's, untouched. inc/product.php explains at length why not
   one core template is overridden; the consequence HERE is that the selectors
   arrive inside `<table class="variations">` with `<th class="label">` and
   `<td class="value">`, and that table has to be laid out as a field stack
   without being replaced. `display:block` on the table parts does exactly
   that, and it keeps every selector a plugin might use.

   The second consequence is that these rules must survive core rewriting the
   <option> list on every change: nothing below styles an <option>, and every
   chip state is expressed with a class the script sets from the select.
   ══════════════════════════════════════════════════════════════════════ */

/* The table, stood down to a field stack. `role="presentation"` is already on
   it in core's markup, so nothing is being taken away from a screen reader
   that it was not already told to ignore. */
.variations,
.variations tbody,
.variations tr,
.variations th,
.variations td{
  display:block; width:auto; padding:0; border:0; text-align:left;
}
.variations{margin:22px 0 0; border-collapse:collapse}
.variations tr + tr{margin-top:16px}

/* concept.css:648, the field label — the same weight, size and colour the cart
   and the checkout give theirs, so a question on the product page reads as the
   same kind of question. */
.variations th.label label{
  display:block; margin:0 0 8px;
  font-size:13.5px; font-weight:600; color:var(--ink);
}

/* ── the native select, dressed ───────────────────────────────────────────
   Shown when the chips could not be built — an attribute with more than twelve
   options, a browser without DOMDocument's output, or a script that failed. It
   is the fallback and it still has to look like this store, so it borrows the
   concept's input exactly (concept.css:651-656) and adds the one thing a
   <select> needs that a text input does not: a chevron, drawn rather than left
   to the platform's grey triangle. */
.rosen-var__native select{
  appearance:none; -webkit-appearance:none;
  width:100%; max-width:100%;
  font:400 15.5px/1.4 inherit; color:var(--ink);
  border:1px solid var(--line); border-radius:11px;
  padding:12px 40px 12px 14px; background:#fff;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23667079' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 13px center; background-size:17px;
  transition:border-color .18s var(--ease), box-shadow .18s var(--ease);
}
.rosen-var__native select:focus{
  outline:none; border-color:var(--green-ink); box-shadow:0 0 0 4px rgba(140,192,84,.22);
}

/* Once the chips are live the select is still the control core drives, so it
   stays in the DOM with its name and its value — it is only taken off the
   screen. Never `display:none`: core's reset focuses this element, and a
   display:none element cannot take focus, so the "Clear" link would swallow
   the keyboard. Core's own screen-reader-text recipe, to the letter. */
.rosen-var--chipped .rosen-var__native{
  position:absolute; width:1px; height:1px; margin:-1px; padding:0;
  overflow:hidden; clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0;
}

/* ── the chips ────────────────────────────────────────────────────────────
   `.chip` is the concept's own token for "one small fact about this product",
   and an option is exactly that until it is chosen. The interactive states are
   the ones the design already spends: the green wash and green ink of
   `.btn--ghost:hover`, and the 4px focus halo of a focused field. */
.rosen-var__chips{display:flex; flex-wrap:wrap; gap:8px}

.rosen-opt{
  appearance:none; cursor:pointer;
  padding:9px 15px; min-height:40px;
  border:1px solid var(--line); border-radius:11px; background:#fff;
  font:500 14px/1.3 inherit; color:var(--body);
  transition:border-color .18s var(--ease), background .18s var(--ease),
             color .18s var(--ease), box-shadow .18s var(--ease);
}
.rosen-opt:hover{border-color:var(--green-ink); color:var(--ink)}
.rosen-opt:focus-visible{
  outline:none; border-color:var(--green-ink); box-shadow:0 0 0 4px rgba(140,192,84,.22);
}

/* Chosen. Two signals, not one: the wash AND the weight — 8% of the men who
   place these orders cannot separate the green from the grey, and a border
   colour on its own is the thing they would be asked to read. */
.rosen-opt.is-on{
  border-color:var(--green-ink); background:var(--green-soft);
  color:var(--green-ink); font-weight:700;
}

/* Not reachable from the current selection. Core deletes the <option>; the
   chip is kept and struck through, because a row that loses buttons as you
   choose jumps under the pointer and hides the fact that the combination was
   ever offered. Struck through rather than merely faded, so the state is
   legible without colour. */
.rosen-opt.is-off{
  cursor:not-allowed; color:var(--muted); background:var(--wash);
  border-color:var(--line); text-decoration:line-through;
  text-decoration-thickness:1px; text-underline-offset:2px;
}
.rosen-opt.is-off:hover{border-color:var(--line)}

/* ── "Clear" ──────────────────────────────────────────────────────────────
   Core keeps this hidden with an inline `visibility` until something is
   chosen, and fades it in itself — so this may set anything EXCEPT display,
   which would override nothing and leave the link permanently gone. */
.variations .reset_variations{
  display:inline-block; margin-top:10px;
  font-size:13.5px; font-weight:600; color:var(--green-ink);
  text-decoration:none;
}
.variations .reset_variations:hover{text-decoration:underline; text-underline-offset:3px}
.variations .reset_variations:focus-visible{
  outline:2px solid var(--green-ink); outline-offset:3px; border-radius:4px;
}

/* ── the panel core fills in ──────────────────────────────────────────────
   `.single_variation` is emptied and rewritten from a wp.template on every
   change, so nothing here may depend on what is inside it. */
.single_variation_wrap{margin-top:18px}

.woocommerce-variation.single_variation:empty{display:none}

.woocommerce-variation-description{
  margin:0 0 10px; font-size:14.5px; line-height:1.6; color:var(--body);
}
.woocommerce-variation-description p{margin:0}

/* The variation's price is lifted to `.pdp-price` where the design puts the
   price, so the figure does not appear twice on one column. Scoped to
   `.rosen-var-ready`, which the script adds only once it is running: if it
   never runs, this rule never matches and core's own price panel is the one
   the customer reads. */
.rosen-var-ready .woocommerce-variation-price{display:none}

/* The availability line. `.stock` is WooCommerce's class and carries either
   `in-stock`, `out-of-stock` or `available-on-backorder`; all three are drawn
   as the concept's pill so the answer reads the same as the one at the top of
   the column. */
.woocommerce-variation-availability .stock,
.single_variation_wrap p.stock{
  margin:0 0 12px; display:inline-flex; align-items:center; gap:7px;
  font:600 12px/1 inherit; letter-spacing:.05em; text-transform:uppercase;
}
.woocommerce-variation-availability .stock::before,
.single_variation_wrap p.stock::before{
  content:""; width:6px; height:6px; border-radius:50%; background:currentColor;
}
.woocommerce-variation-availability .in-stock{color:#245c7e}
.woocommerce-variation-availability .out-of-stock,
.single_variation_wrap p.out-of-stock{color:#9a2f2f}
.woocommerce-variation-availability .available-on-backorder{color:#8a5a00}

/* The hint, and the button it explains. It is removed from the flow the moment
   core reveals the variation panel above it — `.single_variation` only stops
   being empty when there is a real variation to describe. */
.rosen-var__hint{
  margin:0 0 12px; font-size:13.5px; line-height:1.5; color:var(--muted);
}
.rosen-var-ready .single_variation_wrap:has(.woocommerce-variation:not(:empty)) .rosen-var__hint{
  display:none;
}

/* ── a button that cannot be pressed yet ──────────────────────────────────
   Core adds `disabled` as a CLASS and deliberately leaves the button
   submittable, because it wants the click so it can explain the refusal in an
   alert. So the appearance is the whole of the signal before the press, and
   the script mirrors it into aria-disabled for anyone who is not looking.

   style.css:764-767 already answers this with `opacity:.55`, which was the
   right call when nothing on this store could ever be disabled — and is not
   enough now that 18 of 39 products open in that state. A 55% green pill on
   white still reads as the live primary action; it is the first thing the eye
   lands on and the one thing that will not work. Spent, not faded.

   The selector matches style.css's own specificity exactly and wins on order
   (product.css is enqueued with `rosen` as a dependency, so it loads after),
   and it is narrowed to `form.variations_form` — the simple-product button is
   never disabled and is left entirely alone. */
body.single-product div.product form.variations_form button.single_add_to_cart_button.disabled,
body.single-product div.product form.variations_form button.single_add_to_cart_button.disabled:hover{
  opacity:1;
  background:var(--wash); color:var(--muted);
  border:1px solid var(--line); box-shadow:none;
  cursor:not-allowed; transform:none;
}

/* Core's own "sorry, that combination is unavailable" notice, which it inserts
   after `.single_variation`. It arrives with `.woocommerce-info`, a class this
   theme has no rule for outside the cart. */
.wc-no-matching-variations{
  margin:12px 0 0; padding:12px 14px;
  border:1px solid var(--line); border-left:3px solid #9a2f2f; border-radius:11px;
  background:var(--wash); font-size:14px; line-height:1.5; color:var(--body);
}

/* ── the price-break table, per variation ─────────────────────────────────
   A variable product carries one table per distinct variation price and shows
   the one that belongs to the chosen combination. `[hidden]` is set in the
   markup and toggled by the script; the explicit rule is here because
   `.pb-prices` is a block and `display` from elsewhere in the cascade would
   otherwise beat the attribute. */
.pb-prices[hidden],
.pb-choose[hidden]{display:none}

.pb-choose{
  margin:0; padding:12px 18px 14px;
  border-top:1px solid var(--line);
  font-size:13px; line-height:1.5; color:var(--muted);
}

@media (max-width:520px){
  .rosen-opt{padding:8px 13px; font-size:13.5px}
}

/* ══════════════════════════════════════════════════════════════════════
   THE GALLERY (parity C1)

   33 of the 39 published products carry more than one photograph. This is the
   DEMO's way of showing them — on production the stage is core's gallery and
   none of these rules match anything.

   The stage keeps the concept's shape exactly (concept.css:1663-1670: the 22px
   radius, the 1:1 box, the drop shadow, the blurred ellipse under the part).
   All that changes is that the box is now two rows instead of one, and the
   photograph gives up the height the rail takes.
   ══════════════════════════════════════════════════════════════════════ */

/* The stage is a fixed 1:1 box with 52px of padding (concept.css:1663), sized
   for one photograph and nothing else. A rail inside it takes ~80px out of the
   part, which at 390px leaves the component smaller than its own thumbnails.
   So the stage gives the padding back — and only when there IS a rail, which
   is what `:has()` is for. Nothing changes on a single-image product, on a
   product using core's gallery, or in the concept. */
.pdp-media:has(.pdp-thumbs){padding:34px 34px 22px}

@media (max-width:640px){
  .pdp-media:has(.pdp-thumbs){padding:20px 18px 16px}
}

.pdp-gallery{
  display:grid; grid-template-rows:1fr auto; gap:18px;
  width:100%; height:100%; min-height:0;
  position:relative; z-index:1;
}

/* min-height:0 on the row, or a grid item with an image in it refuses to
   shrink below the image's intrinsic height and the rail is pushed out of the
   box. `object-fit:contain` keeps a part that is wider than it is tall from
   being cropped — these are photographs of machined components and the
   proportions are the point. */
.pdp-gallery .pdp-shot{
  min-height:0; max-height:100%; width:100%; object-fit:contain;
  justify-self:center; align-self:center;
}

.pdp-thumbs{
  display:flex; gap:10px; flex-wrap:wrap; justify-content:center;
}

.pdp-thumb{
  appearance:none; cursor:pointer; padding:6px;
  width:62px; height:62px;
  display:grid; place-items:center;
  border:1px solid rgba(17,21,23,.10); border-radius:13px;
  background:rgba(255,255,255,.62);
  transition:border-color .2s var(--ease), background .2s var(--ease), transform .2s var(--ease);
}
.pdp-thumb:hover{background:#fff; transform:translateY(-2px)}
.pdp-thumb:focus-visible{
  outline:none; border-color:var(--green-ink); box-shadow:0 0 0 4px rgba(140,192,84,.28);
}
.pdp-thumb.is-on{border-color:var(--green-ink); background:#fff}

/* concept.css:1670 gives every image on this stage `max-height:100%`,
   `width:auto` and a 26px drop shadow — right for the part on the stage, wrong
   for a 62px thumbnail, where the shadow is larger than the picture. */
.pdp-thumb img{
  max-width:100%; max-height:100%; width:auto; height:auto;
  filter:none; object-fit:contain;
}

@media (max-width:900px){
  .pdp-thumb{width:54px; height:54px}
  .pdp-gallery{gap:14px}
}
@media (max-width:640px){
  .pdp-thumb{width:46px; height:46px; padding:4px; border-radius:11px}
  .pdp-gallery{gap:10px}
}

/* ══════════════════════════════════════════════════════════════════════
   VIDEO — the fifth one

   Four of the five products with footage carry an .mp4 the store hosts and are
   drawn by concept.css:626-634. The fifth, the OsseoPress Kit, is a YouTube
   embed, and concept.css declares `.vid-frame iframe` for it (:630) but has no
   rule for the wrapper itself — the concept's page carries the box inline. So
   this is the wrapper, and only the wrapper.
   ══════════════════════════════════════════════════════════════════════ */

.vid-frame{
  position:relative; aspect-ratio:16/9; width:100%;
  border-radius:16px; overflow:hidden; background:#11151799;
}

/* ══════════════════════════════════════════════════════════════════════
   REVIEWS

   The concept has no review band — production has never rendered one, so
   there was nothing for the design to draw. This is therefore the only block
   in this file that invents anything, and it invents no VALUES: every colour,
   radius, line and measure below is a concept.css token, and the band reuses
   .section / .section-head / .eyebrow exactly as the compatibility band and
   the "goes with this" grid above it do. It reads as one page because it is
   made of the same parts.

   Nothing here is conditional on there being reviews. The section is not
   rendered at all when there are none (P5), so there is no empty state to
   style — and there is deliberately no "be the first to review" rule waiting
   to be switched on.
   ══════════════════════════════════════════════════════════════════════ */

.reviews{
  list-style:none; margin:0; padding:0;
  display:grid; gap:14px;
  grid-template-columns:repeat(auto-fit,minmax(300px,1fr));
}

.review{
  margin:0; padding:18px 20px;
  border:1px solid var(--line); border-radius:var(--radius);
  background:#fff;
}

/* The masked name, the verified mark and the date on one row, wrapping to two
   on a narrow card rather than truncating — a date that reads "4 Mar…" is a
   date nobody can check. */
.review-head{
  display:flex; flex-wrap:wrap; align-items:baseline; gap:8px 10px;
  margin-bottom:8px;
}

/* Monospaced, because the name is a MASK and not a name: the asterisks are
   what the row is telling you, and they only line up in a fixed pitch. */
.review-who{
  font:600 14px/1.3 ui-monospace,SFMono-Regular,Menlo,monospace;
  color:var(--ink); letter-spacing:.01em;
}

.review-owner{
  padding:2px 8px; border-radius:999px;
  background:var(--green-soft); color:var(--green-ink);
  font-size:11px; font-weight:600; letter-spacing:.01em;
}

.review-when{margin-left:auto; font-size:12.5px; color:var(--muted); white-space:nowrap}

.review p{margin:8px 0 0; font-size:14.5px; line-height:1.6; color:var(--body)}
.review p:first-of-type{margin-top:6px}

/* The stars. --gold is declared here rather than borrowed because concept.css
   has no rating token; it is the same warmth as the anodising gold swatch the
   spec table already uses (#c8a02e), lifted one step so it holds against
   white at 15px. */
.stars{display:inline-flex; gap:2px; vertical-align:-2px; color:#d9d9de}
.stars .star--on{color:#d8a520}

/* In the section head the average sits inline with the sentence next to it. */
.reviews-band .section-head .stars{margin-right:4px}

@media (max-width:640px){
  .review{padding:16px 16px}
  .review-when{margin-left:0; width:100%}
}

/* Product documents retain the legacy signed-in section without empty cards. */
.pdp-document-list {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.pdp-document-list .btn {
  max-width: 100%;
  white-space: normal;
  overflow-wrap: anywhere;
}
.pdp-document-list li {
  min-width: 0;
  max-width: 100%;
}
