/* product.css
   The single product page, ported from the approved design at
   `html site/GPT HTML designs/palesas-ecommerce-pages/product.html` and its
   `css/product.css`, on 4 September 2026.

   ---------------------------------------------------------------------------
   WHAT WAS TAKEN FROM THE DESIGN FILE, AND WHAT WAS NOT
   ---------------------------------------------------------------------------
   Taken, unchanged: every layout decision. The two-column detail grid, the
   64px thumbnail rail beside the main image, the summary stack, the tab strip,
   the two fact cards, the reviews band with its score/bars/cards, and the
   related-products row. All of it, including the breakpoints.

   NOT taken, on Tony's explicit answer (4 Sep 2026):

     1. `"Playfair Display", Georgia, serif` on h1/h2. That is a FOURTH
        typeface on a site that already carries Outfit, DM Sans and Great
        Vibes, and it appears on this page only. Headings use var(--pk-display)
        like every other page.

     2. Pink, square, uppercase buttons. palesas.css states the colour roles
        and shop.css records that this exact substitution was made once and
        corrected: "shop.css had made pink the primary action on eight
        buttons, which with a 4px radius and a 9px uppercase label made the
        Add to cart row read as a different retailer." Berry
        (var(--pk-primary)) is the action colour here, at var(--pk-pill), the
        same as .pk-btn and .add-cart. Pink keeps its real role: decorative
        fills and the rating bars.

     3. Its literal colour values. Every one is a --pk-* token now, so this
        page cannot drift off the palette (CLAUDE.md, "One palette, no foreign
        colours"). Two swaps worth naming: the design's #4d913c in-stock green
        is var(--pk-ok), which is the value this site already measured and
        passes contrast with; and its pink stars are var(--pk-star), which is
        the token that exists for exactly this.

     4. Its Unicode glyph icons (the design uses U+2662, U+25B1, U+2659,
        U+2667, U+2727 and a shopping-cart emoji as icons). to-check.md D17
        already records glyph-as-icon as an open defect on five other pages;
        this page does not add a sixth. Every icon here is a real <use> out of
        assets/img/icons-shell.svg, so none of these rules style a glyph.

     5. Its floating WhatsApp button (.whatsapp). Chaty already renders one
        site-wide; a second would sit on top of it.

     6. Its own .related-card component. The theme already has ONE product
        card (woocommerce/content-product-card.php, styled in shop.css) and a
        second card style for the same object on an adjacent page is exactly
        the drift shop.css's own banner warns about.

   ---------------------------------------------------------------------------
   THE RULE THIS FILE INHERITS FROM shop.css AND ecommerce.css
   ---------------------------------------------------------------------------
   This stylesheet loads AFTER style.css, so any rule here on a SHARED shell
   class (.pk-btn, .pk-wrap, header, footer, nav) silently re-styles that
   component on this page only, while a markup check still calls the page
   clean. There are none, deliberately, and there must never be any. Bare
   element selectors count: one page stylesheet on this project once said
   `nav{display:none}` below 700px and hid the site's primary navigation.

   Everything below is scoped to a class this page introduces, or to
   WooCommerce's own generated class names on a page that is already a
   product (the enqueue in inc/woocommerce-setup.php is is_product()-gated).
   ========================================================================= */


/* ---------------------------------------------------------------------------
   0. THE BREADCRUMB
   ---------------------------------------------------------------------------
   .pk-breadcrumb is defined in ecommerce.css and is centred there, because
   the only place it existed before was under a centred hero. This page has no
   hero (see single-product.php's docblock for why), so it needs the design's
   left-aligned variant. A MODIFIER, not an override of the shared class.
   ------------------------------------------------------------------------ */

.pk-breadcrumb--start{
  justify-content:flex-start;
  padding-top:22px;
  margin-bottom:0;
  font-size:12px;
}


/* ---------------------------------------------------------------------------
   1. THE DETAIL BLOCK: gallery + summary
   ------------------------------------------------------------------------ */

.product-detail{
  display:grid;
  grid-template-columns:minmax(0,1.12fr) minmax(360px,.95fr);
  gap:46px;
  padding-block:38px 42px;
}

/* ---------------------------------------------------------------------------
   ⚠ NEUTRALISING WOOCOMMERCE'S OWN SINGLE-PRODUCT LAYOUT. Read this before
   touching anything in section 1.
   ---------------------------------------------------------------------------
   single-product.php gained a `div.product` wrapper on 4 Sep 2026, and that
   wrapper is load-bearing: without it none of ecommerce.css's cart rules
   matched, which is why the Add to basket button rendered in WooCommerce's
   default purple. But the same wrapper ALSO switches on WooCommerce's own
   two-column single-product layout in woocommerce-layout.css and
   woocommerce.css, which were inert on this page until now. That is the
   trade, and it has to be paid for explicitly rather than discovered later.

   Three of its rules genuinely fight this design, and all three are written
   at `.woocommerce div.product ...` specificity, so an override MUST match or
   exceed it. A rule written as plain `.woocommerce-product-gallery` loses to
   `.woocommerce div.product div.images` and would fail silently:

     1. div.images gets `float:left; width:48%`. The float is already ignored
        (a grid item cannot float) but the width is not.
     2. .flex-control-thumbs li gets `width:25%; float:left`, which turns the
        vertical rail into a horizontal strip.
     3. .flex-control-thumbs li img gets `opacity:.5`, so every thumbnail but
        the active one is half faded. This design signals the active one with
        a ring instead.
   ------------------------------------------------------------------------ */

.woocommerce div.product div.images{
  width:auto;float:none;margin:0;
}
.woocommerce div.product div.images .flex-control-thumbs li{
  width:auto;float:none;margin:0;
}
.woocommerce div.product div.images .flex-control-thumbs li img{
  opacity:1;
}

/* WooCommerce's OWN gallery markup, laid into the design's rail + main-image
   composition rather than replaced.

   Why native: this catalog is entirely VARIABLE products, and WooCommerce's
   variation script swaps the main gallery image when a size that carries its
   own picture is chosen. It also owns the zoom and the PhotoSwipe lightbox,
   both of which are already switched on
   (add_theme_support('wc-product-gallery-zoom'/-lightbox/-slider), see
   inc/woocommerce-setup.php). Hand-rolling the design's <figure> + thumb
   buttons would have thrown all three away for markup that only looks the
   same, which is the same trade single-product.php's own docblock already
   refused once over the variation dropdown.

   So: .woocommerce-product-gallery is the design's .product-gallery,
   .flex-viewport/.woocommerce-product-gallery__image is its
   .product-main-image, and FlexSlider's .flex-control-thumbs is its
   .product-thumbs rail. */
.woocommerce-product-gallery{
  position:relative;
  display:grid;
  grid-template-columns:64px minmax(0,1fr);
  gap:18px;
  align-items:start;
}

/* One image and no rail: the grid collapses to a single column rather than
   leaving a 64px empty gutter. Every one of the 7 live sample products is in
   this state today (none has gallery images yet, to-check.md), so this is the
   normal case, not the edge case. */
.woocommerce-product-gallery--without-images,
.woocommerce-product-gallery.pbt-gallery--single{
  grid-template-columns:minmax(0,1fr);
}

.woocommerce-product-gallery__wrapper,
.woocommerce-product-gallery .flex-viewport{
  grid-column:2;
  margin:0;
  border-radius:var(--pk-r-xl);
  overflow:hidden;
  background:var(--pk-cream);
}
.pbt-gallery--single .woocommerce-product-gallery__wrapper,
.pbt-gallery--single .flex-viewport,
.woocommerce-product-gallery--without-images .woocommerce-product-gallery__wrapper{
  grid-column:1;
}

.woocommerce-product-gallery__image,
.woocommerce-product-gallery__image img{
  display:block;
  width:100%;
}
.woocommerce-product-gallery__image img{
  aspect-ratio:1/1;
  object-fit:cover;
}

/* The thumbnail rail. FlexSlider renders this as a <ol class="flex-control-thumbs">
   AFTER the viewport in source order; grid puts it back on the left without
   moving the markup, which keeps the tab order (image, then thumbnails)
   sensible. */
/* Written at `.woocommerce div.product ...` specificity on purpose. See the
   neutralising block above: WooCommerce's own rules for these exact elements
   are written that way, and a shorter selector loses to them. */
.woocommerce div.product div.images .flex-control-thumbs{
  grid-column:1;
  grid-row:1;
  display:grid;
  align-content:start;
  gap:12px;
  margin:0;
  padding:0;
  overflow:visible;
  list-style:none;
}
.woocommerce div.product div.images .flex-control-thumbs img{
  display:block;
  width:100%;
  height:75px;
  padding:4px;
  border:1px solid var(--pk-border);
  border-radius:var(--pk-r);
  background:var(--pk-surface);
  object-fit:cover;
  cursor:pointer;
  transition:border-color .25s var(--pk-ease),box-shadow .25s var(--pk-ease);
}
/* The selected thumb is a SELECTION state, so it takes the action colour the
   rest of the site uses for "this one", not the design's pink. */
.woocommerce div.product div.images .flex-control-thumbs img.flex-active{
  border-color:var(--pk-primary);
  box-shadow:0 0 0 2px rgba(110,25,55,.13);
}

/* WooCommerce's own zoom/lightbox trigger, given the design's round button. */
.woocommerce-product-gallery__trigger{
  position:absolute;
  z-index:3;
  right:18px;
  bottom:18px;
  display:grid;
  place-items:center;
  width:42px;
  height:42px;
  border:0;
  border-radius:50%;
  background:var(--pk-surface);
  color:var(--pk-fg);
  box-shadow:var(--pk-shadow-sm);
  font-size:0;
  cursor:pointer;
}
.woocommerce-product-gallery__trigger svg{width:18px;height:18px}


/* --- The summary column ------------------------------------------------- */

.product-summary{padding-top:5px;min-width:0}

.product-category{
  margin:0 0 8px;
  color:var(--pk-gold-ink);
  font-size:12px;
  font-weight:700;
  letter-spacing:.1em;
  text-transform:uppercase;
}
.product-category a{color:inherit;text-decoration:none}
.product-summary h1{
  font-family:var(--pk-display);
  font-size:clamp(31px,3vw,46px);
  font-weight:600;
  line-height:1.1;
}

.product-rating{display:flex;align-items:center;gap:16px;margin-top:15px}
.product-rating .pbt-stars{color:var(--pk-star);font-size:18px;letter-spacing:2px}
.product-rating a{
  font-size:13px;color:var(--pk-muted);
  text-decoration:underline;text-underline-offset:3px;
}
.product-rating a:hover{color:var(--pk-primary)}

/* Scoped to .product-summary: .product-price is shop.css's shared class on
   the ONE product card (content-product-card.php), reused unchanged in the
   "You may also love" row below via .shop-products. An unscoped rule here
   would load after shop.css and silently blow up that card's price to the
   main product's own 28px display size. */
.product-summary .product-price{margin:10px 0;color:var(--pk-primary);font-size:28px;font-weight:700}
.product-summary .product-price .woocommerce-Price-amount{color:inherit}
.product-summary .product-price del{font-size:18px;color:var(--pk-muted);margin-left:8px}

.product-intro{max-width:54ch;margin:0;color:var(--pk-muted);font-size:14px;line-height:1.6}

.product-stock{
  display:flex;align-items:center;gap:7px;
  margin:18px 0;color:var(--pk-ok);font-weight:600;font-size:13px;
}
.product-stock svg{
  width:18px;height:18px;padding:3px;flex:0 0 auto;
  border-radius:50%;background:var(--pk-ok-bg);
}
.product-stock--out{color:var(--pk-muted)}
.product-stock--out svg{background:var(--pk-cream-deep)}

/* The three promises. Real facts only: the design's own three
   ("Secure Payments / Fast Local Delivery / Quality You Can Trust") were not
   ported, because this install force-blocks every payment gateway but Cash on
   Delivery (inc/woocommerce-setup.php) and Palesa's real delivery terms are
   still an open question (to-check.md A4/A5). The three here are her shop
   address, her WhatsApp number and what she stocks, all already published in
   the site footer. */
.product-promises{
  display:grid;grid-template-columns:repeat(3,1fr);gap:13px;
  margin-top:22px;padding-top:18px;border-top:1px solid var(--pk-border);
}
.product-promises div{display:flex;gap:9px;align-items:center;min-width:0}
.product-promises svg{width:20px;height:20px;flex:0 0 auto;color:var(--pk-primary)}
.product-promises span{display:grid;min-width:0;font-size:11px;color:var(--pk-muted);line-height:1.35}
.product-promises strong{font-size:11px;font-weight:600;color:var(--pk-fg)}


/* ---------------------------------------------------------------------------
   2. WOOCOMMERCE'S OWN ADD-TO-CART FORM, inside the design's buy row
   ---------------------------------------------------------------------------
   ecommerce.css already styles `.woocommerce div.product form.cart ...` and
   those rules are the ones that make the size dropdown and the Add to basket
   button look like this site. They matched NOTHING on the live page until
   4 Sep 2026, because single-product.php never emitted a `div.product`
   wrapper, so the button rendered in WooCommerce core's default purple on an
   otherwise fully designed page. The wrapper is now there
   (wc_product_class()), which switches all of that on.

   What is left here is only the bits ecommerce.css does not cover: the
   quantity stepper's boxed look and making the button fill the row.
   ------------------------------------------------------------------------ */

.product-summary form.cart{
  display:flex;flex-wrap:wrap;align-items:center;gap:14px;
  margin-top:22px;
}
.product-summary form.cart .variations{flex:1 1 100%}
.product-summary form.cart .woocommerce-variation{flex:1 1 100%}
.product-summary form.cart .quantity{margin-right:0}
.product-summary form.cart .single_add_to_cart_button{flex:1 1 200px}

/* The design's boxed quantity control, applied to WooCommerce's own
   .quantity input rather than a hand-built stepper. WooCommerce's number
   field carries its own increment arrows, so this is one control, not three
   elements pretending to be one. */
.product-summary form.cart .quantity .qty{
  width:86px;height:46px;
  border:1px solid var(--pk-border);border-radius:var(--pk-r);
  background:var(--pk-surface);color:var(--pk-fg);
  text-align:center;font:inherit;font-size:15px;font-weight:600;
}

.product-summary .stock{margin:0;font-size:12px;color:var(--pk-muted)}

/* The cake order questions (TDZ Orders & Pay), dressed in this site's palette.
   The plugin owns their shape and reads every colour from --tdzp-*; this is the
   one place Palesa's tokens are handed to it. Added 25 Sep 2026, when the form
   was still rendering in raw browser defaults under a fully designed page.

   The Size question is WooCommerce's, not the plugin's, so it is brought into
   line here: same label, same full-width field, so the order form reads as one
   list of questions instead of two different forms stacked. */
.product-summary{
  --tdzp-accent:var(--pk-primary);
  --tdzp-on-accent:var(--pk-primary-fg);
  --tdzp-field:var(--pk-surface);
  --tdzp-panel:var(--pk-surface);
  --tdzp-line:#c4bbb5;
  --tdzp-muted:var(--pk-muted);
  --tdzp-radius:var(--pk-r);
  --tdzp-error:#b3261e;
  --tdzp-shadow:var(--pk-shadow);
}
.product-summary .tdzp-form{flex:1 1 100%;margin:6px 0 6px}
.product-summary .tdzp-form__label,
.woocommerce div.product .product-summary form.cart .variations label{
  margin-bottom:8px;font-size:14px;font-weight:600;letter-spacing:0;
  text-transform:none;color:var(--pk-fg);
}
.woocommerce div.product .product-summary form.cart .variations{margin-bottom:0}
.woocommerce div.product .product-summary form.cart .variations tr{position:relative;margin-bottom:0}
/* WooCommerce's "Clear" link is visibility:hidden until a size is picked, so it
   held a 40px empty band under the Size field. Beside the label it takes no
   height, and sits where a customer looks when they want to undo the choice. */
.woocommerce div.product .product-summary form.cart .reset_variations{
  position:absolute;top:0;right:0;margin:0;line-height:1.35;
}
.woocommerce div.product .product-summary form.cart .variations select,
.product-summary select.tdzp-form__input{
  width:100%;max-width:none;height:auto;min-height:48px;
  padding:.7rem 2.5rem .7rem .95rem;
  border:1px solid var(--tdzp-line);border-radius:var(--pk-r);
  background-color:var(--pk-surface);color:var(--pk-fg);
  font-size:15px;appearance:none;-webkit-appearance:none;
  background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m6 9 6 6 6-6" fill="none" stroke="%236e1937" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>');
  background-repeat:no-repeat;background-position:right 14px center;background-size:18px;
  cursor:pointer;
}
.woocommerce div.product .product-summary form.cart .variations select:focus{
  border-color:var(--pk-primary);box-shadow:0 0 0 3px rgba(110,25,55,.16);
}
.product-summary .tdzp-form__input[type="file"]{background-color:var(--pk-cream)}

/* The running total: the one number the customer is building towards. */
.product-summary .tdzp-total{
  font-family:var(--pk-display);font-size:15px;font-weight:600;color:var(--pk-fg);
}
.product-summary .tdzp-total__value{
  font-size:26px;font-weight:700;letter-spacing:-.01em;color:var(--pk-primary);
}
.product-summary .tdzp-total.is-pending{font-family:var(--pk-sans);font-size:14px;color:var(--pk-muted)}


/* ---------------------------------------------------------------------------
   3. THE TAB STRIP
   ------------------------------------------------------------------------ */

.product-info{
  margin-bottom:22px;
  border:1px solid var(--pk-border);border-radius:var(--pk-r-xl);
  background:var(--pk-surface);overflow:hidden;
}
.product-tabs{
  display:flex;gap:36px;padding:0 25px;
  border-bottom:1px solid var(--pk-border);
}
.product-tabs button{
  padding:17px 0 14px;border:0;border-bottom:2px solid transparent;
  background:none;color:var(--pk-fg);
  font:600 13px var(--pk-sans);cursor:pointer;
}
.product-tabs button[aria-selected="true"]{
  border-bottom-color:var(--pk-primary);color:var(--pk-primary);
}
.product-tab-panel{padding:20px 25px;font-size:14px;line-height:1.7;color:var(--pk-muted)}
.product-tab-panel > :first-child{margin-top:0}
.product-tab-panel > :last-child{margin-bottom:0}
.product-tab-panel p{margin:0 0 12px}


/* ---------------------------------------------------------------------------
   4. THE TWO FACT CARDS
   ------------------------------------------------------------------------ */

.product-facts{
  display:grid;grid-template-columns:1fr 1fr;gap:18px;
  padding-bottom:56px;
}
.fact-card{
  padding:26px;
  border:1px solid var(--pk-border);border-radius:var(--pk-r-xl);
  background:linear-gradient(135deg,var(--pk-surface) 20%,var(--pk-cream));
}
.fact-card h2{
  margin-bottom:18px;
  color:var(--pk-primary);font-size:25px;font-weight:600;
}

.spec-table{margin:0;border:1px solid var(--pk-border);border-radius:var(--pk-r);overflow:hidden;background:var(--pk-surface)}
.spec-table > div{
  display:grid;grid-template-columns:1fr 1.1fr;
  border-bottom:1px solid var(--pk-border);font-size:13px;
}
.spec-table > div:last-child{border-bottom:0}
.spec-table dt,.spec-table dd{margin:0;padding:12px 14px;min-width:0}
.spec-table dt{border-right:1px solid var(--pk-border);font-weight:700}
.spec-table dd{color:var(--pk-muted)}

.reassurance-item{
  display:flex;gap:17px;align-items:flex-start;
  padding:13px 0;border-bottom:1px solid var(--pk-border);
}
.reassurance-item:last-child{border-bottom:0}
.reassurance-item svg{width:26px;height:26px;flex:0 0 auto;margin-top:2px;color:var(--pk-primary)}
.reassurance-item h3{font-size:14px;font-weight:600}
.reassurance-item p{margin:3px 0 0;color:var(--pk-muted);font-size:12px;line-height:1.55}
.reassurance-item a{color:var(--pk-primary);text-decoration:underline;text-underline-offset:2px}


/* ---------------------------------------------------------------------------
   5. THE REVIEWS BAND
   ---------------------------------------------------------------------------
   Fed by real WooCommerce reviews (woocommerce/single-product-reviews.php).
   The score, the bar widths and the counts are all computed from
   $product->get_rating_counts(); nothing here is a fixed number.
   ------------------------------------------------------------------------ */

.reviews-band{padding:48px 0;background:var(--pk-cream)}
.reviews-heading{
  display:flex;align-items:center;justify-content:space-between;gap:20px;
  margin-bottom:20px;
}
.reviews-heading h2{font-size:31px;font-weight:600}
/* .pk-btn--light has no outline of its own; this page's button needs one to
   read as a button on the cream reviews band. Scoped to this page's own
   class, never to .pk-btn itself. */
.write-review{border:1px solid var(--pk-primary);min-width:0;margin-top:0}

.reviews-grid{display:grid;grid-template-columns:.85fr 1fr 1fr;gap:20px;align-items:stretch}

.review-summary{padding:16px 18px}
.review-score{
  float:left;margin:0 17px 0 0;
  color:var(--pk-primary);font-family:var(--pk-display);
  font-size:62px;font-weight:700;line-height:1;
}
.review-summary > p:not(.review-score){margin:0 0 4px;font-size:12px}
.review-stars,.review-card__top .pbt-stars{color:var(--pk-star);font-size:18px;letter-spacing:2px}

.rating-bars{clear:both;padding-top:18px;display:grid;gap:8px}
.rating-bars div{display:grid;grid-template-columns:36px 1fr 22px;gap:8px;align-items:center;font-size:11px}
.rating-bars i{height:5px;border-radius:4px;background:var(--pk-cream-deep);overflow:hidden}
/* Pink in its real role here: a decorative fill, not an action. */
.rating-bars b{display:block;height:100%;border-radius:inherit;background:var(--pk-pink)}
.rating-bars em{font-style:normal;color:var(--pk-muted)}

.review-card{
  padding:21px;
  border:1px solid var(--pk-border);border-radius:var(--pk-r-xl);
  background:var(--pk-surface);
}
.review-card__top{display:flex;justify-content:space-between;align-items:center;gap:8px}
.review-card__top small{color:var(--pk-ok);font-size:10px;font-weight:600}
.review-card h3{margin-top:10px;font-size:14px;font-weight:600}
.review-card p{margin:10px 0 0;color:var(--pk-muted);font-size:12px;line-height:1.65}
.review-card time{display:block;margin-top:17px;color:var(--pk-muted);font-size:11px}

/* Empty state: a product with no reviews yet says so, rather than showing a
   0.0 score and five empty bars. */
.reviews-empty{
  grid-column:1/-1;
  padding:34px;text-align:center;
  border:1px dashed var(--pk-border);border-radius:var(--pk-r-xl);
  background:var(--pk-surface);color:var(--pk-muted);font-size:14px;
}

/* WooCommerce's own review form, inside the design's dialog. */
/* ⚠ CENTRING IS DECLARED HERE, NOT INHERITED. Tony, 4 Sep 2026: the popup
   opened hard against the TOP-LEFT corner of the window instead of the middle.
   A modal <dialog> is centred by the user agent's own
   `dialog:modal{ position:fixed; inset:0; margin:auto }`, which means the
   centring is three separate declarations any one of which a stylesheet can
   knock out without touching the other two. Relying on that is how it broke.
   All three are stated explicitly below, so this popup lands in the middle
   whatever else is on the page.

   max-height + overflow are not optional either: with a rating, a name, a
   review box and a button, this form is taller than a phone in landscape,
   and a <dialog> that overflows the viewport cannot be scrolled to. */
.review-dialog{
  position:fixed;
  inset:0;
  margin:auto;
  width:min(520px,calc(100% - 30px));
  max-height:min(88vh,760px);
  overflow:auto;
  border:0;border-radius:var(--pk-r-xl);padding:0;
  background:var(--pk-surface);
  box-shadow:var(--pk-shadow);
}

/* The no-JavaScript fallback. single-product-reviews.php prints the dialog
   WITH `open` so the form is reachable before the script runs; a non-modal
   dialog must not be fixed to the middle of the viewport, or it covers the
   page it is sitting in. Only the modal state gets the overlay treatment. */
.review-dialog:not(:modal){
  position:static;
  max-height:none;
  margin-inline:auto;
  margin-block:24px;
  border:1px solid var(--pk-border);
  box-shadow:var(--pk-shadow-sm);
}
.review-dialog::backdrop{background:rgba(60,13,28,.53)}
.review-dialog .review-dialog__in{padding:32px;position:relative}
.review-dialog h2,.review-dialog h3{font-size:28px;font-weight:600;margin-bottom:6px}
.review-dialog #reply-title{font-size:28px}
.review-dialog p{color:var(--pk-muted);font-size:13px}
/* Every field label is its own line ... except the cookie-consent one, which
   is a checkbox and its text. Without the :not() below, `display:block` drops
   the words onto the line UNDER the tick box and the pair stops reading as one
   control. Caught on screen 4 Sep 2026, not by any measurement. */
.review-dialog label:not(.comment-form-cookies-consent label){display:block;margin:14px 0 6px;font-size:12px;font-weight:600;color:var(--pk-fg)}
.review-dialog .comment-form-cookies-consent{
  display:flex;align-items:center;gap:9px;margin:18px 0 0;
}
.review-dialog .comment-form-cookies-consent input[type=checkbox]{
  width:18px;height:18px;flex:0 0 auto;margin:0;accent-color:var(--pk-primary);
}
.review-dialog .comment-form-cookies-consent label{
  margin:0;font-size:12px;font-weight:400;color:var(--pk-muted);line-height:1.4;
}
.review-dialog :is(input[type=text],input[type=email],select,textarea){
  width:100%;border:1px solid var(--pk-border);border-radius:var(--pk-r);
  padding:11px;font:inherit;font-size:14px;background:var(--pk-surface);color:var(--pk-fg);
}
.review-dialog .comment-form-rating .stars{margin:0}
.dialog-close{
  position:absolute;top:12px;right:14px;
  display:grid;place-items:center;width:34px;height:34px;
  border:0;border-radius:50%;background:var(--pk-cream);color:var(--pk-muted);cursor:pointer;
}
.dialog-close svg{width:16px;height:16px}


/* ---------------------------------------------------------------------------
   6. RELATED PRODUCTS
   ---------------------------------------------------------------------------
   The heading only. The cards themselves are the site's ONE product card
   (content-product-card.php + shop.css), deliberately not the design's
   second .related-card style.
   ------------------------------------------------------------------------ */

/* padding-block only: this element also carries .pk-wrap, which sets
   padding-inline for the page's side gutters. The old `padding:55px 0 62px`
   shorthand zeroed left/right back out on the same element, so the related
   row's cards ran edge to edge on phone while every other section kept its
   20px gutter. See to-check.md / decisions.md for the report this fixes. */
.pk-related{padding-block:55px 62px}
.pk-related > h2{
  text-align:center;margin-bottom:25px;
  font-size:25px;font-weight:600;
}
/* .shop-products is shop.css's own grid and is reused here, so the related
   row and the shop grid are literally the same component. Two things it needs
   for this context, both scoped under .pk-related so shop.css's own page is
   untouched: no panel padding or white ground (it is not inside the shop's
   bordered layout box here), and four across instead of three, which is the
   count the approved design shows.

   The three widths are restated at every breakpoint on purpose. shop.css sets
   3-up at <=1023 and 2-up at <=767; this stylesheet loads AFTER it, so a
   single unconditional 4-up rule here would win inside those media queries
   too and the row would stay 4 across on a phone. */
.pk-related .shop-products{
  padding:0;background:none;
  grid-template-columns:repeat(4,minmax(0,1fr));gap:17px;
}
@media(max-width:1023px){ .pk-related .shop-products{grid-template-columns:repeat(3,minmax(0,1fr))} }
@media(max-width:767px){ .pk-related .shop-products{grid-template-columns:repeat(2,minmax(0,1fr));gap:11px} }


/* ---------------------------------------------------------------------------
   7. BREAKPOINTS. The design's own two, unchanged.
   ------------------------------------------------------------------------ */

@media (max-width:900px){
  .product-detail{grid-template-columns:1fr;gap:28px}
  .reviews-grid{grid-template-columns:1fr 1fr}
  .review-summary{grid-column:1/-1}
}

@media (max-width:620px){
  .product-detail{padding-block:24px 30px}
  /* The rail moves under the image and becomes a 4-up strip. */
  .woocommerce-product-gallery{grid-template-columns:1fr}
  .woocommerce-product-gallery__wrapper,
  .woocommerce-product-gallery .flex-viewport{grid-column:1;grid-row:1}
  .woocommerce div.product div.images .flex-control-thumbs{
    grid-column:1;grid-row:2;
    grid-template-columns:repeat(4,1fr);
  }
  .woocommerce div.product div.images .flex-control-thumbs img{height:62px}
  .product-summary h1{font-size:31px}
  .product-promises{grid-template-columns:1fr}
  .product-tabs{gap:18px;padding-inline:15px;overflow-x:auto;-webkit-overflow-scrolling:touch}
  .product-tabs button{white-space:nowrap;font-size:12px}
  .product-tab-panel{padding-inline:16px}
  .product-facts,.reviews-grid{grid-template-columns:1fr}
  .fact-card{padding:20px}
  .reviews-band{padding-block:35px}
  .reviews-heading{align-items:flex-start;flex-direction:column;gap:12px}
  .reviews-heading h2{font-size:27px}
  .product-summary form.cart .single_add_to_cart_button{flex:1 1 100%}
  /* A 1fr/1.1fr spec table at 320px leaves 8 characters for the value. */
  .spec-table > div{grid-template-columns:1fr}
  .spec-table dt{border-right:0;border-bottom:1px solid var(--pk-border);padding-bottom:8px}
  .spec-table dd{padding-top:8px}
}

/* Motion follows the site, not this file: .reveal is defined in style.css and
   the two sections that use it here carry the same reveal--up the rest of the
   site uses. Nothing is re-timed locally. */

/* WooCommerce's own review form controls, which the first pass missed: only
   text inputs, selects and textareas were styled, so the submit button
   rendered as a grey browser default in the middle of a designed dialog. */
.review-dialog .form-submit{margin:20px 0 0}

/* ⚠ THE SELECTOR BELOW IS DELIBERATELY ID-HEAVY. Do not "tidy" it.
   WooCommerce styles this exact button with
     .woocommerce:where(body:not(...)) #respond input#submit
   Its :where() contributes nothing, but #respond and #submit are two IDs, so
   it computes to (2,1,1). The obvious rule here, `.review-dialog
   input[type=submit]`, is (0,2,1) and LOSES: measured live on 4 Sep 2026 the
   button was still WooCommerce's default rgb(233,230,237) grey at 3px radius
   inside a fully designed dialog. Matching (2,1,1) and loading later is what
   wins it, without !important. Third time this project has been bitten by a
   WooCommerce selector outranking a component class: see [[l-219]]. */
.review-dialog #respond input#submit,
.review-dialog input[type=submit],
.review-dialog .submit{
  min-height:46px;padding:0 26px;
  border:0;border-radius:var(--pk-pill);
  background:var(--pk-primary);color:var(--pk-primary-fg);
  font:500 14px var(--pk-sans);cursor:pointer;
}

/* The rating control. WooCommerce renders EITHER a <select name="rating">
   (which is what this template asks for and what works without JavaScript) or,
   once its own review script upgrades it, a row of <a> stars in
   `p.stars`. Both are styled, because which one the visitor sees depends on a
   script that may not have run yet. */
.review-dialog .comment-form-rating{margin-top:14px}
.review-dialog p.stars{display:flex;gap:4px;margin:0}
.review-dialog p.stars a{
  color:var(--pk-star);font-size:22px;line-height:1;text-decoration:none;
}
.review-dialog .comment-form-comment textarea{min-height:120px;resize:vertical}
.review-dialog .comment-notes,
.review-dialog .must-log-in{margin:0 0 4px}
.review-dialog .required{color:var(--pk-pink-ink)}

/* ---------------------------------------------------------------------------
   THE POPUP'S MOTION AND PRESS STATES. Added 4 September 2026 after running
   `emil-design-eng` over it at Tony's request.
   ---------------------------------------------------------------------------
   Four things that review names, applied with THIS PROJECT'S tokens rather
   than the skill's own curves: decisions.md records a deliberate per-project
   motion exception (the reference's cubic-bezier(0.16, 1, 0.3, 1), which is
   var(--pk-ease)), so importing a second easing vocabulary here would break
   the thing that exception exists to protect.

     1. The dialog appeared instantly. "Nothing in the real world appears from
        nothing." It now arrives at scale(.96) + opacity, which is above the
        scale(0) the review rejects outright.
     2. A modal KEEPS transform-origin:center. The origin-aware rule is for
        popovers anchored to a trigger; a modal is not anchored to anything.
     3. Exit is faster than enter (140ms against 200ms): slow where the
        visitor is deciding, fast where the system is responding.
     4. Every pressable thing gets a press state, and every hover is gated
        behind a real pointer so a tap on a phone does not trigger it.

   Durations sit inside the review's 200-500ms band for modals and its
   100-160ms band for a button press.
   ------------------------------------------------------------------------ */

.review-dialog[open]{
  animation:pbt-dialog-in 200ms var(--pk-ease) both;
}
.review-dialog[open]::backdrop{
  animation:pbt-backdrop-in 200ms var(--pk-ease) both;
}
@keyframes pbt-dialog-in{
  from{opacity:0;transform:scale(.96)}
  to{opacity:1;transform:none}
}
@keyframes pbt-backdrop-in{
  from{opacity:0}
  to{opacity:1}
}

/* The press states. Scale is subtle on purpose: 0.97 reads as "the interface
   heard you", 0.9 reads as a toy. */
.review-dialog input[type=submit],
.write-review,
.dialog-close{
  transition:transform 140ms var(--pk-ease),opacity 140ms var(--pk-ease),
             background-color 140ms var(--pk-ease),color 140ms var(--pk-ease);
}
.review-dialog #respond input#submit:active,
.review-dialog input[type=submit]:active,
.write-review:active{transform:scale(.97)}
.dialog-close:active{transform:scale(.92)}

/* Hover only where there is a real pointer. On a touch device :hover sticks
   after a tap, so an ungated hover rule leaves the button visibly "stuck". */
@media (hover:hover) and (pointer:fine){
  .dialog-close:hover{background:var(--pk-cream-deep);color:var(--pk-fg)}
  .write-review:hover{background:var(--pk-primary);color:var(--pk-primary-fg)}
}

/* Reduced motion: fewer and gentler, not none. The opacity fade still tells
   the visitor something arrived; only the movement goes. */
@media (prefers-reduced-motion:reduce){
  .review-dialog[open]{animation-name:pbt-backdrop-in}
  .review-dialog input[type=submit]:active,
  .write-review:active,
  .dialog-close:active{transform:none}
}

/* ⚠ One price, not two. Once a size is chosen WooCommerce prints that size's
   bare price (R950) directly under the order form's running total (R1030 with
   the filling), which reads as two different prices for one cake. The running
   total already follows the chosen size (tdzp.js, show_variation), so where
   it exists the bare one goes. :has() scopes this to forms that carry a total;
   a product with no order form keeps WooCommerce's price as before. 25 Sep 2026. */
.woocommerce div.product .product-summary form.cart:has(.tdzp-total) .woocommerce-variation-price{display:none}
