/* ══════════════════════════════════════════════════════════════════════
   Customer reviews.

   No design system of its own: every colour, radius, space and font here
   resolves to an --opsim-* token from assets/css/base.css, and the two
   buttons (.write-review-toggle, .submit-review-btn) are already part of
   base.css's shared button contract, so this file gives them no styling.

   Star colour follows the brand cascade documented in CLAUDE.md — it is
   var(--opsim-brand), never a hex — so a Customizer brand change carries
   through the stars along with everything else.
   ══════════════════════════════════════════════════════════════════════ */

/*
 * The star tokens, the read-only star glyphs and the rating line under the
 * product title live in template-parts/product/single-product.css: that line
 * is above the fold, and this stylesheet is loaded non-blocking (it styles the
 * section below the product). See defer_style() in inc/performance/assets.php.
 */

.opsim-reviews {
    width: 100%;
    padding: 50px 0;
    /* The anchor target. Without it, jumping from the rating link under the
       product title parks the section heading underneath the sticky header. */
    scroll-margin-top: calc(100px + var(--opsim-admin-bar, 0px));
}

.opsim-reviews-inner {
    width: 100%;
    max-width: var(--opsim-container);
    margin: 0 auto;
    padding: 0 16px;
    /* Belt and braces against a long unbreakable review word widening the
       page — the same min-width:0 rule the product columns carry. */
    min-width: 0;
}

.opsim-reviews-title {
    margin: 0 0 28px;
    font-size: 1.125rem;
    font-weight: 700;
    line-height: 1.3;
    text-transform: uppercase;
    text-align: center;
    color: var(--opsim-text-primary);
}


/* ── Summary ───────────────────────────────────────────────────────────
   Three columns on desktop, stacked on mobile. Single-column is the
   BASE and the grid is opt-in at 768px up, so the narrow layout can
   never depend on a rule being unset later.                            */

.opsim-reviews-summary {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 28px;
    align-items: center;
    padding: 0 0 28px;
    border-bottom: 1px solid var(--opsim-border, #e5e7eb);
}

.opsim-reviews-average {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.opsim-reviews-average-row {
    display: flex;
    align-items: center;
    column-gap: 10px;
    row-gap: 4px;
    flex-wrap: wrap;
    justify-content: center;
    min-width: 0;
}

.opsim-reviews-average-value {
    font-size: 0.875rem;
    font-weight: 700;
    line-height: 1.4;
    color: var(--opsim-text-primary);
    white-space: nowrap;
}

.opsim-reviews-average-count {
    margin: 0;
    font-size: 0.8125rem;
    color: var(--opsim-text-secondary);
}

/* ── Rating distribution ─────────────────────────────────────────────── */

.opsim-reviews-distribution {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
}

.opsim-reviews-distribution-row {
    display: grid;
    /* The count column is fixed so every bar starts and ends on the same
       two vertical lines regardless of whether a row reads 0 or 1 204. */
    grid-template-columns: auto minmax(0, 1fr) 2.5ch;
    align-items: center;
    gap: 10px;
}

.opsim-reviews-distribution-stars {
    display: inline-flex;
    line-height: 0;
}

.opsim-reviews-distribution-track {
    display: block;
    height: 8px;
    min-width: 0;
    background: var(--opsim-bg-secondary, #f3f4f6);
    border-radius: var(--opsim-radius);
    overflow: hidden;
}

.opsim-reviews-distribution-fill {
    display: block;
    height: 100%;
    background: var(--opsim-star-color);
    border-radius: inherit;
}

.opsim-reviews-distribution-count {
    font-size: 0.75rem;
    font-variant-numeric: tabular-nums;
    color: var(--opsim-text-secondary);
    text-align: right;
}

/* ── Call to action ───────────────────────────────────────────────────── */

.opsim-reviews-cta {
    display: flex;
    justify-content: center;
    min-width: 0;
}

.opsim-reviews-cta .write-review-toggle {
    width: 100%;
    max-width: 260px;
}

.opsim-reviews-closed,
.opsim-reviews-empty {
    margin: 0;
    font-size: 0.8125rem;
    color: var(--opsim-text-secondary);
    text-align: center;
}

.opsim-reviews-empty {
    padding: 28px 0 0;
}

/* ── Review list ──────────────────────────────────────────────────────── */

.opsim-reviews-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.opsim-review {
    padding: 22px 0;
    border-bottom: 1px solid var(--opsim-border, #e5e7eb);
    min-width: 0;
}

.opsim-review-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    /* Column and row gaps differ on purpose: 12px reads well between the
       name block and the date side by side, but is too airy once the date
       wraps underneath. */
    column-gap: 12px;
    row-gap: 4px;
    flex-wrap: wrap;
    margin-bottom: 8px;
}

.opsim-review-head-main {
    display: flex;
    align-items: baseline;
    column-gap: 10px;
    row-gap: 2px;
    flex-wrap: wrap;
    /* Takes the room the date does not. min-width: 0 is kept deliberately:
       this block is still a flex child that must be allowed to shrink below
       its content rather than push the date out of the row — it applies to
       whatever it holds, now and later. */
    flex: 1 1 auto;
    min-width: 0;
}

/*
 * Deliberately NOT a filled chip.
 *
 * This was `background: var(--opsim-brand)` with white text. On a store whose
 * brand colour is pure black — which this one is — that rendered as a hard
 * black badge, which fought the star rating beside it for the eye at every
 * viewport width. The information is unchanged; only its weight is.
 *
 * It carried a leading middot to hold it off the author name. The name is no
 * longer displayed, so the separator has gone with it — the row's 10px
 * column-gap is now the only thing between it and the stars, which is enough.
 */
.opsim-review-verified {
    display: inline-flex;
    align-items: center;
    font-size: 0.6875rem;
    font-weight: 600;
    line-height: 1.4;
    color: var(--opsim-text-secondary);
    background: none;
    border: 0;
    padding: 0;
    text-transform: none;
    letter-spacing: normal;
    white-space: nowrap;
}

.opsim-review-date {
    font-size: 0.75rem;
    line-height: 1.4;
    color: var(--opsim-text-secondary);
    white-space: nowrap;
    /* Pinned to the far end while it shares the row, and never compressed —
       a date that wraps or ellipsises is worse than one that drops to its
       own line, which is what the narrow breakpoint below does. */
    flex: none;
    margin-left: auto;
}

.opsim-review-title {
    margin: 0 0 6px;
    font-size: 0.875rem;
    font-weight: 700;
    line-height: 1.35;
    color: var(--opsim-text-primary);
    overflow-wrap: anywhere;
}

.opsim-review-body {
    font-size: 0.875rem;
    line-height: 1.6;
    color: var(--opsim-text-secondary);
    min-width: 0;
    overflow-wrap: anywhere;
}

.opsim-review-body p {
    margin: 0 0 8px;
}

.opsim-review-body p:last-child {
    margin-bottom: 0;
}

/* A review the visitor can see only because it is their own, still held. */
.opsim-review.is-pending {
    opacity: 0.75;
}

.opsim-review-pending {
    margin: 8px 0 0;
    font-size: 0.75rem;
    font-style: italic;
    color: var(--opsim-text-secondary);
}

/* ── Write-review form ────────────────────────────────────────────────── */

.opsim-review-form-panel {
    padding: 24px 0 4px;
    border-bottom: 1px solid var(--opsim-border, #e5e7eb);
}

.opsim-review-form-title {
    margin: 0 0 16px;
    font-size: 0.9375rem;
    font-weight: 700;
    text-align: center;
    color: var(--opsim-text-primary);
}

.opsim-review-form-notice {
    margin: 0;
    font-size: 0.8125rem;
    text-align: center;
    color: var(--opsim-text-secondary);
}

.opsim-review-form {
    display: flex;
    flex-direction: column;
    gap: 14px;
    max-width: 640px;
    margin: 0 auto;
}

/* ── Form fields ──────────────────────────────────────────────────────
   Every selector in this block is scoped to .opsim-reviews on purpose.
   `.opsim-field-label` is ALSO the admin panel's class
   (inc/admin/support/admin-support.css), and `.opsim-field-error-message` is the
   COD form's. Neither stylesheet meets this one today — one is admin-only,
   and the cod-form.css overlap is only a shared prefix — but an unscoped
   `.opsim-field` here would be one rename away from restyling the checkout
   form sitting on this very page. The scope costs a selector and removes
   the whole class of problem.                                            */

.opsim-reviews .opsim-field {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin: 0;
    min-width: 0;
}

.opsim-rating-field {
    border: 0;
    padding: 0;
    align-items: center;
}

.opsim-reviews .opsim-field-label {
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: var(--opsim-text-primary);
    padding: 0;
}

.opsim-reviews .opsim-field .required {
    color: var(--opsim-brand);
}

.opsim-reviews .opsim-field-input {
    width: 100%;
    font: inherit;
    font-size: 0.875rem;
    padding: 10px 12px;
    color: var(--opsim-text-primary);
    background: var(--opsim-bg-primary, #fff);
    border: 1px solid var(--opsim-border, #e5e7eb);
    border-radius: var(--opsim-radius);
    outline: none;
}

/* The privacy opt-in checkbox: a control beside its label, not a stacked
   label-over-input like the text fields. */
.opsim-reviews .opsim-field-inline {
    flex-direction: row;
    align-items: center;
    gap: 8px;
}

.opsim-reviews .opsim-field-inline label {
    font-size: 0.75rem;
    line-height: 1.4;
    color: var(--opsim-text-secondary);
}

.opsim-reviews .opsim-field-inline input[type="checkbox"] {
    flex: none;
    width: 16px;
    height: 16px;
    margin: 0;
    accent-color: var(--opsim-brand);
}

.opsim-reviews textarea.opsim-field-input {
    min-height: 120px;
    resize: vertical;
}

.opsim-reviews .opsim-field-input:focus {
    border-color: var(--opsim-brand);
}

/* ── Interactive star rating ──────────────────────────────────────────
   Native radio inputs, no JavaScript.

   The radios are printed 5 → 1 and this row is reversed, so the customer
   reads 1 → 5 left to right while the DOM keeps the higher rating first.
   That ordering is the whole mechanism: `:checked ~ .opsim-rating-star`
   reaches exactly the stars at and below the chosen one. Print them 1 → 5
   instead and the fill inverts.

   The input is not display:none — a hidden form control is unreachable by
   keyboard and invisible to assistive tech. It is clipped instead, so the
   radio group keeps native arrow-key navigation and its focus ring is
   drawn on the label via :focus-visible.                                */

.opsim-rating-input {
    /* Containing block for the clipped radios below, so they resolve against
       this row rather than the initial containing block. */
    position: relative;
    display: flex;
    flex-direction: row-reverse;
    justify-content: center;
    gap: 4px;
}

.opsim-rating-radio {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    border: 0;
    white-space: nowrap;
}

.opsim-rating-star {
    --opsim-star-size: 26px;
    display: inline-flex;
    padding: 2px;
    cursor: pointer;
    color: var(--opsim-star-empty);
    border-radius: var(--opsim-radius);
}

.opsim-rating-radio:checked ~ .opsim-rating-star {
    color: var(--opsim-star-color);
}

/* Pointer feedback. A colour swap only — no transition, no transform. */
.opsim-rating-input:hover .opsim-rating-star {
    color: var(--opsim-star-empty);
}

.opsim-rating-star:hover,
.opsim-rating-star:hover ~ .opsim-rating-star {
    color: var(--opsim-star-color);
}

.opsim-rating-radio:focus-visible ~ .opsim-rating-star {
    outline: 2px solid var(--opsim-brand);
    outline-offset: 2px;
}

/* ── Optional photo / video ───────────────────────────────────────────
   A visually-hidden file input with the drop area as its <label>. The
   input is clipped rather than display:none so it keeps native keyboard
   focus and native screen-reader semantics — the label is only the
   appearance. Clicking the label activates the input for free; no
   JavaScript is involved in making the control work.                    */

.opsim-reviews .opsim-media-field {
    align-items: center;
}

.opsim-reviews .opsim-media-input {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    border: 0;
    white-space: nowrap;
}

.opsim-reviews .opsim-media-drop {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    /* Square-ish and capped, so the zone never becomes a full-width band on
       desktop; `width: 100%` up to the cap keeps it comfortable on a phone. */
    width: 100%;
    max-width: 260px;
    min-height: 150px;
    padding: 18px 16px;
    text-align: center;
    cursor: pointer;
    color: var(--opsim-text-secondary);
    background: var(--opsim-bg-primary, #fff);
    border: 1px solid var(--opsim-border, #e5e7eb);
    border-radius: var(--opsim-radius);
}

/* Focus lands on the clipped input, so the ring has to be drawn on the
   label the customer can actually see. */
.opsim-reviews .opsim-media-input:focus-visible + .opsim-media-drop {
    outline: 2px solid var(--opsim-brand);
    outline-offset: 2px;
}

.opsim-reviews .opsim-upload-icon {
    width: 34px;
    height: 34px;
    display: block;
}

.opsim-reviews .opsim-media-hint {
    font-size: 0.75rem;
    font-weight: 600;
    line-height: 1.4;
}

.opsim-reviews .opsim-media-filename[data-state="error"] {
    color: var(--opsim-brand);
    font-weight: 700;
}

.opsim-reviews .opsim-media-filename {
    font-size: 0.6875rem;
    line-height: 1.4;
    color: var(--opsim-text-secondary);
    /* A chosen filename can be long and unbreakable; it wraps inside the
       zone rather than widening it. */
    overflow-wrap: anywhere;
    max-width: 100%;
}

/* ── Media attached to a published review ─────────────────────────────
   A grid of fixed-ratio cells, not a block image.

   The point is that ONE photo is one compact thumbnail. Rendered as a
   plain block, a single upload filled the review column and roughly
   doubled the height of every review carrying one, which pushed the
   next review off the screen.

   `auto-fill` + minmax means the same two declarations cover every
   width: cells stay at least their minimum, share the leftover space,
   and the grid wraps to as many columns as fit. The max-width is what
   keeps a lone thumbnail small — without it a single cell would stretch
   to the full column and the whole problem comes back.                 */

.opsim-reviews .opsim-review-media {
    list-style: none;
    margin: 10px 0 0;
    padding: 0;
    display: grid;
    /* Mobile baseline: two columns, so thumbnails stay finger-sized
       rather than shrinking to three or four across a narrow screen. */
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
    /* The cap that stops one image stretching the review. */
    max-width: 260px;
    min-width: 0;
}

.opsim-reviews .opsim-review-media-item {
    /* Square cells, so a portrait and a landscape upload sit in the same
       grid without one of them setting the row height. */
    aspect-ratio: 1;
    min-width: 0;
    overflow: hidden;
    border: 1px solid var(--opsim-border, #e5e7eb);
    border-radius: var(--opsim-radius);
    background: var(--opsim-bg-secondary, #f3f4f6);
}

/*
 * Scoped under .opsim-reviews, and that is not cosmetic.
 *
 * woocommerce-layout.css ships `.woocommerce img, .woocommerce-page img
 * { height: auto; max-width: 100% }`, and <body> carries both of those
 * classes on every product page. That selector is (0,1,1); a bare
 * `.opsim-review-media-image` is (0,1,0), so WooCommerce won — its
 * `height: auto` in particular defeats the `height: 100%` below and the
 * image stops filling its cell. The extra class makes this (0,2,0).
 */
.opsim-reviews .opsim-review-media-image,
.opsim-reviews .opsim-review-media-video {
    display: block;
    width: 100%;
    height: 100%;
    /* The cell is square and the upload is not; cover crops it centrally
       rather than letterboxing or distorting it. */
    object-fit: cover;
}

.opsim-reviews .opsim-review-media-video {
    background: #000;
}

/*
 * Video gets two cells and a 16/9 box.
 *
 * A <video controls> in a ~120px square is unusable — the native control
 * bar alone is wider than the cell. Spanning two columns is the smallest
 * change that makes it playable while keeping it inside the same grid,
 * and 16/9 matches what phones actually record.
 */
.opsim-reviews .opsim-review-media-item.is-video {
    grid-column: span 2;
    aspect-ratio: 16 / 9;
}

/* comment_form() wraps its output in #respond; it must not add a second
   box around the panel that already frames the form. */
.opsim-review-form-panel .comment-respond {
    margin: 0;
    padding: 0;
    border: 0;
}

.opsim-review-form .form-submit {
    margin: 4px 0 0;
    text-align: center;
}

/* ── Turnstile (only printed when enabled in Opsim Theme → Integrations) ─ */

/* Reserves the widget's height (65px flexible, 140px compact) so it does not
   shift the submit button when Cloudflare's iframe arrives. */
.opsim-reviews .opsim-review-turnstile {
    align-items: center;
    width: 100%;
    min-width: 0;
}

.opsim-reviews .opsim-review-turnstile-widget {
    width: 100%;
    max-width: 100%;
    min-height: 65px;
    display: flex;
    justify-content: center;
}

.opsim-reviews .opsim-review-turnstile-widget iframe {
    max-width: 100%;
}

.opsim-reviews .opsim-review-turnstile-error {
    margin: 0;
    font-size: 13px;
    line-height: 1.4;
    text-align: center;
    color: var(--opsim-brand);
    font-weight: 700;
}

/* ── Pagination ───────────────────────────────────────────────────────── */

.opsim-reviews-pagination {
    padding: 22px 0 0;
}

.opsim-reviews-pagination .page-numbers {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 6px;
}

.opsim-reviews-pagination a.page-numbers,
.opsim-reviews-pagination span.page-numbers {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 34px;
    height: 34px;
    padding: 0 10px;
    font-size: 0.8125rem;
    text-decoration: none;
    color: var(--opsim-text-primary);
    border: 1px solid var(--opsim-border, #e5e7eb);
    border-radius: var(--opsim-radius);
}

.opsim-reviews-pagination span.page-numbers.current {
    color: var(--opsim-bg-primary, #fff);
    background: var(--opsim-brand);
    border-color: var(--opsim-brand);
}

/* ── Breakpoints ──────────────────────────────────────────────────────── */

/*
 * From a comfortable phone width upward the grid stops being a fixed pair of
 * columns and becomes "as many ~104px cells as fit, up to four". One rule
 * covers large phones, tablets and desktop — there is no separate tablet
 * layout to keep in sync, because auto-fill already is the tablet layout.
 *
 * 4 x 104 + 3 x 8 = 440, so the max-width admits exactly four columns and no
 * fifth. A single photo still occupies one 104px cell.
 */
@media (min-width: 480px) {
    .opsim-reviews .opsim-review-media {
        grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
        max-width: 440px;
    }
}

@media (min-width: 768px) {
    .opsim-reviews-summary {
        /* Distribution gets the extra width: it is the only column whose
           content is a measurement rather than a line of text. */
        grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr) minmax(0, 1fr);
        gap: 32px;
    }

    /* Dividers between the three columns, drawn on the columns themselves so
       they disappear with the grid on narrow viewports. */
    .opsim-reviews-distribution,
    .opsim-reviews-cta {
        padding-left: 32px;
        border-left: 1px solid var(--opsim-border, #e5e7eb);
    }
}

@media (min-width: 992px) {
    .opsim-reviews {
        padding: 60px 0;
    }

    .opsim-reviews-inner {
        max-width: 1300px;
        padding: 0 40px;
    }

    .opsim-reviews-title {
        margin-bottom: 32px;
    }
}

@media (max-width: 767px) {
    /* iOS Safari zooms the page into any focused field under 16px, and the
       14px desktop size triggered it on every tap into the review form.
       16px on phones only — no maximum-scale in the viewport, which would
       also take pinch-zoom away from people who need it. */
    .opsim-reviews .opsim-field-input {
        font-size: 16px;
    }

    .opsim-review-head {
        /* The date stops being a right-hand column and joins the flow, so a
           long author name never squeezes it to an ellipsis. */
        flex-direction: column;
        align-items: flex-start;
        row-gap: 6px;
        column-gap: 0;
    }

    .opsim-review-head-main {
        /* The row's main axis is now vertical, where `flex: 1 1 auto` would
           let this block stretch. It should size to its content and let the
           date sit directly under it. */
        flex: none;
        width: 100%;
    }

    .opsim-review-date {
        /* margin-left: auto pushes to the END of the main axis, which is now
           the BOTTOM — it would drop the date to the foot of the block. */
        margin-left: 0;
    }

    .opsim-reviews-cta .write-review-toggle {
        max-width: none;
    }
}
