/* Korai Studio — product.html only.
   Shared styles (.wrap, .grid, .card, .btn, .discount-badge, ...) live in
   base.css, loaded before this file — see base.html's {% block extra_css %}. */

.product { display: grid; gap: 2.5rem; padding: 2.5rem 0 4rem; }
@media (min-width: 900px) {
  .product { grid-template-columns: 1.15fr 1fr; gap: 4rem; align-items: start; }
  .product-info { position: sticky; top: 6rem; }
}

/* Product gallery — one .gallery-item per image/video, only `.active`
   shown at a time, switched by the thumbnail strip (korai.js). Falls
   back gracefully with a single item and no thumbs at all when a
   product only has one photo (still the case for all 8 seeded products
   today), which is exactly what rendered before this was built out. */
.gallery-main { position: relative; }
.gallery-item { display: none; }
.gallery-item.active { display: block; }
.gallery-item video { width: 100%; aspect-ratio: 3/4; object-fit: cover; background: var(--cream-deep); }

/* Hover-zoom: overflow:hidden crops the scaled-up image to the frame;
   korai.js tracks the cursor and sets --zoom-x/--zoom-y so the zoomed
   view follows the pointer instead of always zooming from center. Gated
   to real hover devices — on touch, `:hover` never fires anyway, but
   being explicit here avoids the exact class of bug hit earlier this
   session with hover-only content on mobile (see .story-details). */
.gallery-zoom {
  overflow: hidden; aspect-ratio: 3/4; background: var(--cream-deep);
  --zoom-x: 50%; --zoom-y: 50%;
}
.gallery-zoom img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform .2s ease; transform-origin: var(--zoom-x) var(--zoom-y);
  -webkit-user-drag: none; user-select: none; -webkit-touch-callout: none;
}
@media (hover: hover) and (pointer: fine) {
  .gallery-zoom:hover img { transform: scale(2.2); cursor: zoom-in; }
}

.gallery-thumbs { display: flex; gap: .6rem; margin-top: .75rem; flex-wrap: wrap; }
.gallery-thumb {
  position: relative; width: 68px; aspect-ratio: 3/4; padding: 0; border: 1px solid var(--line);
  background: var(--cream-deep); cursor: pointer; overflow: hidden; opacity: .65;
  transition: opacity .15s ease, border-color .15s ease;
}
.gallery-thumb img, .gallery-thumb video { width: 100%; height: 100%; object-fit: cover; display: block; }
.gallery-thumb.active, .gallery-thumb:hover { opacity: 1; border-color: var(--charcoal); }
.gallery-thumb .thumb-play {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,.25); color: #fff; font-size: .55rem;
}

.product-info h1 { font-size: clamp(2rem, 5vw, 2.9rem); }
.stars { color: var(--gold); letter-spacing: .1em; font-size: 1rem; }
/* Social proof, placed above the price on purpose — see product.html.
   A link (not a plain span) straight to #reviews below. */
.rating-summary {
  display: flex; align-items: center; gap: .45rem; margin-top: .6rem;
  color: var(--charcoal); text-decoration: none;
}
.rating-summary:hover .rating-summary-text { text-decoration: underline; }
.rating-summary-text { font-size: .78rem; color: var(--ink-soft); }
.product-price { font-size: 1.05rem; margin: 1rem 0 .5rem; }
.product-price s { color: var(--ink-soft); font-size: .85rem; margin-left: .5rem; }
.free-delivery-note {
  font-size: .8rem; font-weight: 600; color: var(--olive); margin: 0 0 1.75rem;
}
.surcharge-note {
  display: block; font-family: var(--body); font-size: .78rem;
  color: var(--ink-soft); margin-top: .3rem;
}

/* "Size" + the "Size guide" trigger — was riding on .eyebrow (font-size
   .66rem, light --ink-soft), fine for a decorative tag elsewhere on the
   site but too small/low-contrast for what's functionally a form label
   here. Own class instead of touching .eyebrow globally. */
.size-label {
  font-size: .85rem; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--charcoal); margin: 0 0 .5rem;
}
.size-guide-link {
  text-transform: none; letter-spacing: 0; font-weight: 400;
  border: 0; border-bottom: 1px solid var(--olive-deep); margin-left: .6rem;
  background: none; padding: 0; font: inherit; font-size: .8rem;
  color: var(--olive-deep); cursor: pointer;
}
.size-guide-link:hover { color: var(--olive); border-color: var(--olive); }

.sizes { display: flex; flex-wrap: wrap; gap: .5rem; margin: .75rem 0 1.5rem; }
.size-opt input { position: absolute; opacity: 0; }
.size-opt label {
  display: grid; place-items: center;
  min-width: 3.1rem; padding: .65rem .8rem;
  border: 1px solid var(--line);
  font-size: .76rem; letter-spacing: .08em; white-space: nowrap;
  cursor: pointer;
  transition: border-color .15s, background .15s;
}
.size-opt input:checked + label { border-color: var(--charcoal); background: var(--charcoal); color: var(--cream); }
.size-opt input:focus-visible + label { outline: 2px solid var(--olive); outline-offset: 2px; }
.size-opt input:disabled + label {
  color: var(--ink-soft); cursor: not-allowed;
  background: repeating-linear-gradient(-45deg, transparent 0 5px, var(--line) 5px 6px);
}

.custom-measurements { margin: -.5rem 0 1.5rem; padding: 1rem; background: var(--cream-deep); }
.custom-fields { display: grid; grid-template-columns: repeat(3, 1fr); gap: .75rem; }
.custom-fields label { display: block; font-size: .68rem; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-soft); }
.custom-fields input {
  display: block; width: 100%; margin-top: .35rem; padding: .5rem .6rem;
  font-family: var(--body); font-size: .9rem; border: 1px solid var(--line); background: var(--cream);
}
@media (max-width: 480px) { .custom-fields { grid-template-columns: 1fr; } }

.trust-badges {
  list-style: none; margin: 1.75rem 0 0; padding: 1.5rem 0 0; border-top: 1px solid var(--line);
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; text-align: center;
}
.trust-badges li { display: flex; flex-direction: column; align-items: center; gap: .5rem; }
.trust-badges svg { width: 26px; height: 26px; color: var(--charcoal); }
.trust-badges span {
  font-size: .66rem; letter-spacing: .04em; color: var(--ink-soft); line-height: 1.3;
}
@media (max-width: 420px) { .trust-badges { grid-template-columns: repeat(2, 1fr); row-gap: 1.25rem; } }

/* Size guide — native <dialog> rather than a hand-rolled overlay div:
   free backdrop, free Escape-to-close, free focus trapping, and
   .showModal()/.close() in korai.js is the entire JS footprint. Body is
   the scrollable part (max-height + overflow-y), header stays pinned, so
   a long guide never pushes the close button off-screen. */
.size-guide-dialog {
  padding: 0; border: 0; border-radius: 4px; max-width: 480px; width: calc(100% - 2.5rem);
  max-height: 80vh; box-shadow: 0 24px 60px rgba(0,0,0,.25);
}
.size-guide-dialog::backdrop { background: rgba(35,20,15,.55); }
.size-guide-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 1.1rem 1.4rem; border-bottom: 1px solid var(--line); background: var(--cream);
  position: sticky; top: 0;
}
.size-guide-head h2 { font-size: 1.2rem; margin: 0; }
.size-guide-close {
  background: none; border: 0; font-size: 1.5rem; line-height: 1; cursor: pointer;
  color: var(--ink-soft); padding: .2rem .4rem;
}
.size-guide-close:hover { color: var(--charcoal); }
.size-guide-body { padding: 1.4rem; overflow-y: auto; max-height: calc(80vh - 4rem); }

/* Fabric band — 3-panel strip (photo / olive / rose), same "wide
   editorial band" idea as a reference layout the user shared, but not a
   copy of it: original icons (drawn here, not the reference's), the
   photo panel uses this product's *own* cover image rather than a
   stock/generic texture shot, and the copy correctly says Cotton — this
   catalog has no linen pieces. Stays within .wrap's own content width
   like every other section on this page (Reviews, Style Suggestions,
   Community Styles) rather than bleeding to the true viewport edge —
   the `100vw` full-bleed trick was tried first and caused a real
   horizontal-scroll bug (100vw doesn't account for the scrollbar's own
   width, so it overflows the actual visible viewport by however wide
   the scrollbar is). Not worth that fragility for a decorative band. */
.fabric-band {
  display: grid; grid-template-columns: 1fr 1fr 1fr;
  margin-top: 3rem; border-radius: 4px; overflow: hidden;
}
.fabric-panel { position: relative; min-height: 280px; padding: 2rem; display: flex; flex-direction: column; }
.fabric-panel-photo { padding: 0; }
.fabric-panel-photo img { width: 100%; height: 100%; object-fit: cover; display: block; -webkit-user-drag: none; }
.fabric-panel-label {
  position: absolute; left: 1.5rem; bottom: 1.5rem; color: #fff;
  font-family: var(--display); font-size: 1.5rem; font-weight: 600;
  text-shadow: 0 2px 12px rgba(0,0,0,.45);
}
.fabric-panel-olive { background: var(--olive); color: var(--cream); justify-content: flex-end; }
.fabric-panel-rose { background: var(--rose); color: var(--charcoal); justify-content: flex-end; }
.fabric-icon {
  width: 64px; height: 64px; margin-bottom: 1.5rem;
  border-radius: 50%; object-fit: cover;
  border: 2px solid rgba(255,255,255,.65);
}
.fabric-panel h3 { font-size: 1.1rem; margin-bottom: .6rem; }
.fabric-panel-olive h3 { color: var(--cream); }
.fabric-panel p { font-size: .86rem; line-height: 1.55; max-width: 30ch; }
.fabric-panel-olive p { color: rgba(255,243,219,.85); }
.fabric-panel-rose p { color: var(--charcoal); }
@media (max-width: 800px) {
  .fabric-band { grid-template-columns: 1fr; }
  .fabric-panel { min-height: 220px; }
}

/* Community reviews — .section-head (base.css) is the same header style
   already used for "The first collection" etc. --gold exists as a
   palette variable but was otherwise unused site-wide (see base.css's
   palette comment) — stars are a fitting, honest use for it rather than
   reaching for --olive and making it do double duty. */
.reviews { padding: 3rem 0; border-top: 1px solid var(--line); margin-top: 3rem; }
.reviews-avg { font-size: .8rem; color: var(--ink-soft); white-space: nowrap; }
.reviews-empty { color: var(--ink-soft); font-size: .88rem; margin-bottom: 2rem; }

.review-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 2rem; margin-bottom: 2.5rem; }
.review-stars { color: var(--gold); letter-spacing: .1em; font-size: 1rem; margin-bottom: .5rem; }
.review-body { font-size: .88rem; line-height: 1.6; margin-bottom: .75rem; color: var(--charcoal); }
.review-media { display: flex; gap: .5rem; flex-wrap: wrap; margin-bottom: .75rem; }
.review-media img, .review-media video {
  width: 72px; height: 72px; object-fit: cover; border-radius: 4px;
  background: var(--cream-deep); -webkit-user-drag: none;
}
.review-meta { font-size: .7rem; color: var(--ink-soft); text-transform: uppercase; letter-spacing: .06em; }

.review-form-guest { background: var(--cream-deep); padding: 1.75rem; text-align: center; max-width: 520px; }
.review-form-guest p { margin-bottom: 1rem; color: var(--ink-soft); font-size: .88rem; }

.review-form { max-width: 520px; }
.review-form h3 { margin-bottom: 1.2rem; font-size: 1.15rem; }
.review-form label {
  display: block; font-size: .7rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-soft); margin-bottom: 1.1rem;
}
.review-form label small { text-transform: none; letter-spacing: 0; }
.review-form select, .review-form textarea, .review-form input[type="file"] {
  display: block; width: 100%; margin-top: .45rem; padding: .6rem .7rem;
  font-family: var(--body); font-size: .88rem; border: 1px solid var(--line); background: var(--cream);
}
.review-form textarea { resize: vertical; }

.style-suggestions { padding: 3rem 0; border-top: 1px solid var(--line); }
/* minmax(..., 1fr) with only 1-2 cards stretches each column to fill the
   whole row — a card could end up ~590px wide in a 1180px container.
   Capping the track at 200px (not 1fr) keeps cards a normal thumbnail
   size regardless of count; justify-content lets leftover width become
   margin instead of forcing cards wider. */
.style-list {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 200px));
  justify-content: start; gap: 1.5rem;
}
.style-card { margin: 0; }
.style-card img, .style-card video {
  /* height: auto is load-bearing here, not decorative — the <img> has an
     HTML height="" attribute (the real photo's pixel height), which is a
     genuine style (low-priority, but still wins when nothing else sets
     height). With no CSS height at all, that attribute silently
     determined the box's height and `aspect-ratio` had nothing left to
     compute, so a tall source photo rendered at its full native height
     instead of the intended 3:4 crop. */
  width: 100%; height: auto; aspect-ratio: 3/4; object-fit: cover; border-radius: 4px;
  background: var(--cream-deep); -webkit-user-drag: none;
}
.style-card figcaption { margin-top: .6rem; font-size: .82rem; color: var(--ink-soft); line-height: 1.4; }
.style-card-text {
  aspect-ratio: 3/4; display: flex; align-items: center; justify-content: center;
  padding: 1rem; background: var(--cream-deep); border-radius: 4px;
}
.style-card-text figcaption { margin: 0; text-align: center; font-size: .85rem; }

/* "How our community styles it" — same scroll-snap + horizontal-track
   shape as the homepage's .stories-track (see korai.js's storiesNext for
   the auto-advance this pairs with), independent class names so either
   can change without touching the other. */
.community-styles { padding-top: 2.5rem; }
.community-track {
  display: flex; gap: 1rem; overflow-x: auto; scroll-snap-type: x mandatory;
  padding-bottom: .5rem; -webkit-overflow-scrolling: touch; scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) { .community-track { scroll-behavior: auto; } }
.community-card {
  position: relative; flex: 0 0 auto; width: clamp(130px, 16vw, 180px);
  aspect-ratio: 9/16; border-radius: 8px; overflow: hidden;
  scroll-snap-align: start; background: var(--cream-deep);
}
.community-card img, .community-card video { width: 100%; height: 100%; object-fit: cover; display: block; -webkit-user-drag: none; }

.detail { border-top: 1px solid var(--line); }
.detail summary {
  padding: 1rem 0; cursor: pointer; list-style: none;
  font-size: .72rem; letter-spacing: .14em; text-transform: uppercase;
  display: flex; justify-content: space-between;
}
.detail summary::-webkit-details-marker { display: none; }
.detail summary::after { content: "+"; }
.detail[open] summary::after { content: "\2212"; }
.detail p { margin: 0 0 1.25rem; color: var(--ink-soft); font-size: .86rem; }

/* Floating "picture-in-picture" styling video on the product page —
   bottom-right by default, freely draggable (korai.js), closeable for
   the rest of this page view (no persistence — a refresh/revisit brings
   it back, per how this was scoped). `touch-action: none` stops the
   browser's own scroll/zoom gestures from fighting the drag on touch.
   Chunked delivery for the video itself needs no special handling here
   — it's a plain <video src>, and browsers request those via HTTP Range
   automatically; the server already answers with 206 Partial Content. */
.pip-video {
  position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 200;
  width: 130px; aspect-ratio: 9/16; border-radius: 14px; overflow: hidden;
  box-shadow: 0 12px 32px rgba(0,0,0,.28); background: var(--cream-deep);
  cursor: grab; touch-action: none; -webkit-user-select: none; user-select: none;
}
.pip-video.dragging { cursor: grabbing; }
.pip-video video { width: 100%; height: 100%; object-fit: cover; display: block; pointer-events: none; }
.pip-video-close, .pip-video-mute {
  position: absolute; border: 0; border-radius: 50%; background: rgba(0,0,0,.55); color: #fff;
  cursor: pointer; display: flex; align-items: center; justify-content: center; line-height: 1;
}
.pip-video-close { top: .35rem; right: .35rem; width: 22px; height: 22px; font-size: .95rem; }
.pip-video-mute { bottom: .35rem; right: .35rem; width: 24px; height: 24px; font-size: .72rem; }
.pip-video-close:hover, .pip-video-mute:hover { background: rgba(0,0,0,.75); }
@media (max-width: 480px) { .pip-video { width: 100px; right: .85rem; bottom: .85rem; } }
