/* review.css — product-review / roundup block restyle (tech-review theme).
 *
 * ⛔ THIS FILE IS THE ONLY LEVER. READ BEFORE EDITING ANYTHING ELSE.
 *
 * The .rv-* markup is NOT generated at request time. editor-renderer.service.js
 * runs renderBlock at SAVE time and bakes the HTML permanently into
 * cms_posts.content, which is a single column shared by every theme. So:
 *
 *   - Editing views/admin/editor-blocks/*.ejs changes nothing on existing posts.
 *   - Re-rendering existing posts to "apply" new markup rewrites that shared
 *     column and corrupts the other theme's rendering. It is not reversible.
 *
 * Therefore every aesthetic change to a review block is made HERE, from the
 * outside, in CSS, against the DOM already sitting in the database. If a rule
 * below looks like a workaround for markup you'd rather change — it is, and that
 * is the correct trade.
 *
 * Scoped to .main-content--review / --roundup (article-review.ejs), loaded
 * conditionally from article-layout.ejs.
 *
 * Namespaces:
 *   .rv-card          product-review block
 *   .rv-bar           labelled meter (fill width via .rv-w--<pct>)
 *   .rv-acc           accordion-box (look-for / avoid)
 *   .rv-author        author-box
 *   .rv-criteria      criteria-list
 *   .rv-ranked-badge  roundup rank marker ("1. Best Overall")
 *   .rv-compare       compare-table
 *   .rv-verdict       sticky verdict box (this template's own chrome)
 */

/* The .rv-* locals are ALIASES onto the global palette, not a second palette.
   A literal hex here is a color the customizer can no longer reach. */
.main-content--review,
.main-content--roundup {
    --rv-ink: var(--color-ink);
    --rv-muted: var(--color-muted);
    --rv-accent: var(--color-accent);
    --rv-line: var(--color-line);
    --rv-surface: var(--color-surface);
    --rv-sunken: var(--color-sunken);
    --rv-good: var(--color-score-high);
    --rv-bad: var(--color-score-low);
    --rv-score-high: var(--color-score-high);
    --rv-score-mid: var(--color-score-mid);
    --rv-score-low: var(--color-score-low);
    --rv-radius: var(--radius-card);
}

/* The block wrapper is `.content`, NOT `.editor-content` — the previous rule
   here targeted `.editor-content > .rv-*`, an element that does not exist on
   either template, so block spacing was silently never applied. Verified against
   the served DOM, not the template source. */
.content > .rv-card,
.content > .rv-acc,
.content > .rv-author,
.content > .rv-criteria,
.content > .rv-compare,
.content > .rv-ranked-badge {
    margin: 2rem 0;
}
/* A rank marker belongs to the card under it — pull them together so the pair
   reads as one unit rather than two floating blocks. */
.content > .rv-ranked-badge { margin-bottom: .75rem; }
.content > .rv-ranked-badge + .rv-card { margin-top: 0; }

/* --- proportional fill classes ---------------------------------------- */
.rv-w--0{width:0%;}.rv-w--5{width:5%;}.rv-w--10{width:10%;}.rv-w--15{width:15%;}
.rv-w--20{width:20%;}.rv-w--25{width:25%;}.rv-w--30{width:30%;}.rv-w--35{width:35%;}
.rv-w--40{width:40%;}.rv-w--45{width:45%;}.rv-w--50{width:50%;}.rv-w--55{width:55%;}
.rv-w--60{width:60%;}.rv-w--65{width:65%;}.rv-w--70{width:70%;}.rv-w--75{width:75%;}
.rv-w--80{width:80%;}.rv-w--85{width:85%;}.rv-w--90{width:90%;}.rv-w--95{width:95%;}
.rv-w--100{width:100%;}

/* --- product-review card ---------------------------------------------- */
.rv-card {
    border: 1px solid var(--rv-line);
    border-radius: var(--rv-radius);
    background: var(--rv-surface);
    overflow: hidden;
}
.rv-card__top { display: grid; grid-template-columns: 185px minmax(0, 1fr) 200px; }
.rv-card__ident { display: flex; flex-direction: column; gap: .75rem; padding: 1rem; border-right: 1px solid var(--rv-line); justify-content: center; }
.rv-card__name { margin: 0; font-family: var(--font-display); font-size: 1.2rem; font-weight: 700; letter-spacing: -.02em; color: var(--rv-ink); line-height: 1.2; }
.rv-card__media { text-align: center; }
.rv-card__media img { max-width: 100%; max-height: 130px; object-fit: contain; border-radius: 6px; }
/* The block only emits <img> when the shortcode carries an image= attribute, so
   a post authored without one leaves a bare white cell. An honest labelled slot
   beats a blank: it reads as "no photo", not as a broken asset. */
.rv-card__ident:not(:has(img))::before {
    content: "";
    display: block;
    width: 100%;
    height: 72px;
    border: 1px dashed var(--rv-line);
    border-radius: var(--rv-radius);
    background:
        linear-gradient(var(--rv-line) 1px, transparent 1px),
        linear-gradient(90deg, var(--rv-line) 1px, transparent 1px);
    background-size: 18px 18px;
    background-color: var(--rv-sunken);
}
.rv-card__meta { display: flex; flex-direction: column; }
/* auto-fit, not a fixed 1fr 1fr — rank and price are both optional, and a
   hardcoded two-column grid renders a bordered empty cell when only one is
   present. auto-fit lets a lone cell span the full row. */
.rv-card__cells { display: grid; grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); border-bottom: 1px solid var(--rv-line); }
.rv-cell { display: flex; flex-direction: column; align-items: center; gap: .3rem; padding: .8rem 1rem; text-align: center; }
.rv-cell + .rv-cell { border-left: 1px solid var(--rv-line); }
.rv-cell__label { font-size: .7rem; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--rv-muted); }
.rv-cell__grade, .rv-cell__price {
    font-family: var(--font-display);
    font-size: 1.4rem; font-weight: 700; letter-spacing: -.02em;
    color: var(--rv-ink); line-height: 1;
    font-variant-numeric: tabular-nums;
}

/* --- spec bars --------------------------------------------------------
   Precision Lab shape: LABEL left · NUMBER right on the same line · thin
   square-edged track beneath.

   The saved DOM nests the number INSIDE the fill (.rv-bar__track >
   .rv-bar__fill > .rv-bar__pct), which is what produced a fat 20px pill with a
   percentage floating inside it. The markup cannot be changed (see file header),
   so the number is lifted out of the bar by making .rv-bar the positioning
   context and absolutely placing .rv-bar__pct on the label's baseline. Zero DOM
   change, and a value of 5% no longer needs a min-width hack to stay readable —
   the number was never really in the bar to begin with. */
.rv-card__bars { display: flex; flex-direction: column; gap: .9rem; padding: 1.1rem 1.25rem; }
.rv-bar { position: relative; }
.rv-bar__label {
    display: block;
    margin-bottom: .45rem;
    /* Keep the label clear of the right-aligned number, however long it gets. */
    padding-right: 3.5rem;
    font-size: .85rem; font-weight: 600; color: var(--rv-ink);
}
.rv-bar__pct {
    position: absolute;
    top: 0;
    right: 0;
    padding: 0;
    font-family: var(--font-display);
    font-size: .85rem; font-weight: 700;
    color: var(--rv-ink);
    font-variant-numeric: tabular-nums;
}
.rv-bar__track {
    display: block;
    height: 4px;
    border-radius: 0;
    background: var(--rv-sunken);
    overflow: hidden;
}
.rv-bar__fill {
    display: block;
    height: 100%;
    /* min-width existed only to fit the number inside the fill. The number is
       out now, so a low score must be free to render as a genuinely short bar. */
    min-width: 0;
    border-radius: 0;
    background: var(--rv-accent);
}

/* --- scorebox --------------------------------------------------------- */
.rv-card__scorebox {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: .35rem; padding: 1rem;
    background: var(--rv-sunken);
    border-left: 1px solid var(--rv-line);
    text-align: center;
}
.rv-medal {
    width: 40px; height: 40px;
    display: flex; align-items: center; justify-content: center;
    color: var(--rv-ink);
    background: var(--rv-surface);
    border: 1px solid var(--rv-line);
    border-radius: var(--rv-radius);
    font-family: var(--font-display);
    font-weight: 700; font-size: 1rem;
}
.rv-score__num {
    font-family: var(--font-display);
    font-size: 2.4rem; font-weight: 700; letter-spacing: -.03em;
    color: var(--rv-ink); line-height: 1;
    font-variant-numeric: tabular-nums;
}

/* ⛔ STARS: GONE.
   A star row is a second, lower-resolution encoding of a number the block
   already prints — 9.2 becomes "four and a half stars", which is less precise
   and, at half-star granularity, sometimes disagrees with the score beside it.
   This site's whole claim is that it measures things.

   display:none also drops the element from the accessibility tree — which is
   correct here and NOT a loss: the stars carry role="img" aria-label="Rated 4.6
   out of 5", duplicating .rv-score__num (9.2) and .rv-score__label (Excellent),
   both of which remain in the DOM as text. A screen reader still gets the score.
   Do not "restore for a11y" without re-reading this. */
.rv-score__stars { display: none; }

.rv-score__label {
    font-size: .72rem; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
    color: var(--rv-ink);
}
/* The verdict word is the score's only non-numeric channel — colour it to the
   same threshold the number uses, so greyscale and colour readers agree. */
.rv-card__scorebox .rv-score__label { color: var(--rv-muted); }
/* --color-muted, not --color-ink-3: this line sits on the SUNKEN scorebox, not on
   white, and ink-3 measures 4.39:1 there — passing on the page ground and failing
   here. A tertiary token is only tertiary against the surface it was checked on. */
.rv-score__count { font-size: .72rem; color: var(--color-muted); font-variant-numeric: tabular-nums; }

.main-content--review .rv-card__cta,
.main-content--roundup .rv-card__cta {
    display: inline-flex; align-items: center; justify-content: center;
    min-height: 44px; box-sizing: border-box; margin-top: .5rem; padding: .6rem 1.25rem;
    font-weight: 600; font-size: .9rem; white-space: nowrap; color: var(--color-on-accent);
    background: var(--rv-accent); border-radius: var(--rv-radius); text-decoration: none;
    transition: background .15s ease;
}
.main-content--review .rv-card__cta:hover,
.main-content--roundup .rv-card__cta:hover {
    background: color-mix(in oklab, var(--rv-accent) 86%, #000);
    color: var(--color-on-accent);
}

/* --- pros / cons ------------------------------------------------------- */
.rv-proscons { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; padding: 1.5rem; border-top: 1px solid var(--rv-line); }
.rv-proscons__title {
    margin: 0 0 .8rem;
    font-family: var(--font-display);
    font-size: .72rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
    color: var(--rv-muted);
}
/* .content-scoped selectors: the theme's `.content li` rule sets
   list-style-type: disc directly on li (same specificity, so a ul-level
   list-style:none loses) — these must out-specify it or every pros/cons row
   renders a browser bullet next to the +/− marker. */
.rv-pros, .rv-cons,
.content .rv-pros, .content .rv-cons { list-style: none; margin: 0; padding: 0; }
.content .rv-pros li, .content .rv-cons li { list-style: none; margin-inline-start: 0; }
.rv-pros li, .rv-cons li { position: relative; padding-left: 1.6rem; margin: .5rem 0; font-size: .92rem; line-height: 1.55; color: var(--rv-ink); }
.rv-pros li::before { content: "+"; position: absolute; left: .25rem; top: 0; color: var(--rv-good); font-weight: 700; }
.rv-cons li::before { content: "\2212"; position: absolute; left: .25rem; top: 0; color: var(--rv-bad); font-weight: 700; }

.rv-card__review { padding: 0 1.5rem 1.5rem; }
.rv-card__review-toggle {
    display: flex; align-items: center; gap: .55rem;
    min-height: 44px; padding: .7rem 1rem;
    background: var(--rv-sunken);
    border: 1px solid var(--rv-line);
    border-radius: var(--rv-radius);
    font-size: .8rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
    color: var(--rv-ink); cursor: pointer; list-style: none;
}
.rv-card__review-toggle::-webkit-details-marker { display: none; }
.rv-card__review-body { padding: 1rem .25rem 0; color: var(--rv-muted); font-size: .95rem; line-height: 1.75; }
.rv-card__review-body p { margin: 0 0 .95rem; }
.rv-card__review-body > :last-child { margin-bottom: 0; }

/* --- accordion-box: look-for / avoid ---------------------------------- */
.rv-acc { border: 1px solid var(--rv-line); border-left-width: 3px; border-radius: var(--rv-radius); padding: 1.1rem 1.35rem; background: var(--rv-surface); }
.rv-acc--look-for { border-left-color: var(--rv-good); }
.rv-acc--avoid { border-left-color: var(--rv-bad); }
.rv-acc__heading { margin: 0 0 .6rem; font-family: var(--font-display); font-size: 1.05rem; font-weight: 700; letter-spacing: -.02em; }
.rv-acc--look-for .rv-acc__heading { color: var(--rv-good); }
.rv-acc--avoid .rv-acc__heading { color: var(--rv-bad); }
.rv-acc__item { border-top: 1px solid var(--rv-line); padding: .65rem 0; }
.rv-acc__item:first-of-type { border-top: 0; }
.rv-acc__q { display: flex; align-items: center; gap: .6rem; cursor: pointer; font-weight: 600; color: var(--rv-ink); list-style: none; min-height: 44px; }
.rv-acc__q::-webkit-details-marker { display: none; }
.rv-acc__a { margin-top: .45rem; padding-left: 0; color: var(--rv-muted); font-size: .95rem; line-height: 1.65; }

/* --- author-box ------------------------------------------------------- */
.rv-author { display: flex; gap: 1.25rem; align-items: center; border: 1px solid var(--rv-line); border-radius: var(--rv-radius); padding: 1.35rem 1.5rem; background: var(--rv-sunken); }
/* The bio is a flex item whose auto basis resolves to the paragraph's max-content
   width — far wider than the box — so the browser shrinks EVERY item to fit,
   squeezing a 72px avatar down to ~12px. Pin the avatar (never shrink) and let the
   bio be the one that gives (min-width:0 lets it fall below its min-content basis). */
.rv-author__avatar { flex: 0 0 auto; }
.rv-author__body { min-width: 0; }
.rv-author__avatar img,
.rv-author img {
    width: 72px; height: 72px; flex-shrink: 0;
    /* .content img (tailwind) adds padding-block: 1rem to every in-body image; on a
       fixed-height avatar that padding eats the picture instead of framing it. */
    padding: 0;
    border-radius: var(--rv-radius); object-fit: cover;
}
.rv-author strong { font-family: var(--font-display); font-size: 1.05rem; letter-spacing: -.02em; }

/* --- criteria-list ---------------------------------------------------- */
.rv-criteria { border: 1px solid var(--rv-line); border-radius: var(--rv-radius); background: var(--rv-surface); overflow: hidden; }
.rv-criteria__heading {
    margin: 0; padding: .9rem 1.35rem;
    font-family: var(--font-display);
    font-size: .75rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
    color: var(--rv-muted);
    background: var(--rv-sunken);
    border-bottom: 1px solid var(--rv-line);
}
.rv-criteria__list { display: grid; grid-template-columns: 1fr 1fr; gap: .1rem 2rem; margin: 0; padding: 1.1rem 1.35rem; list-style: none; }
.rv-criteria__item { position: relative; padding: .5rem 0 .5rem 1.4rem; }
.rv-criteria__item::before { content: "\2022"; position: absolute; left: 0; top: .5rem; color: var(--rv-accent); font-weight: 700; }
.rv-criteria__label { font-weight: 600; color: var(--rv-ink); }
.rv-criteria__desc { color: var(--rv-muted); }
.rv-criteria__desc::before { content: " \2014 "; color: var(--rv-muted); }

/* --- ranked-badge (roundup rank marker) -------------------------------- */
.rv-ranked-badge { display: flex; align-items: baseline; gap: .75rem; scroll-margin-top: 5.5rem; }
.rv-ranked-badge__num {
    display: inline-flex; align-items: center; justify-content: center;
    width: 2.1rem; height: 2.1rem; flex: 0 0 auto;
    border-radius: var(--rv-radius);
    background: var(--rv-surface);
    border: 1px solid var(--rv-ink);
    color: var(--rv-ink);
    font-family: var(--font-display);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}
.rv-ranked-badge__label { font-family: var(--font-display); font-size: 1.35rem; font-weight: 700; letter-spacing: -.025em; color: var(--rv-ink); }

/* --- compare-table ------------------------------------------------------ */
.rv-compare { border: 1px solid var(--rv-line); border-radius: var(--rv-radius); overflow: hidden; background: var(--rv-surface); }
/* The table scrolls INSIDE this box. The page body must never scroll sideways —
   that is what min-width on the table would otherwise cause at 320px. */
.rv-compare__scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.rv-compare__table { width: 100%; border-collapse: collapse; min-width: 480px; }
.rv-compare__table th {
    position: sticky; top: 0;
    background: var(--rv-sunken); color: var(--rv-muted);
    font-size: .7rem; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
    text-align: left; padding: .75rem 1rem;
    border-bottom: 1px solid var(--rv-line);
}
.rv-compare__table td {
    padding: .8rem 1rem; border-top: 1px solid var(--rv-line);
    font-size: .92rem; color: var(--rv-ink);
    font-variant-numeric: tabular-nums;
}
/* First column pinned: scroll the specs, keep the product name in view — a
   comparison you have to scroll back to identify is not a comparison. */
.rv-compare__table th:first-child,
.rv-compare__table td:first-child {
    position: sticky; left: 0; z-index: 1;
    background: var(--rv-surface);
    border-right: 1px solid var(--rv-line);
    font-weight: 600;
}
.rv-compare__table th:first-child { background: var(--rv-sunken); z-index: 2; }
.rv-compare__table tbody tr:nth-child(even) td { background: var(--rv-sunken); }
.rv-compare__table tbody tr:nth-child(even) td:first-child { background: var(--rv-sunken); }
/* A score in a table cell: number + coloured rule under it. Not a filled chip —
   a grid of coloured chips turns a comparison into a traffic light. */
.rv-compare__score {
    display: inline-block;
    min-width: 2.2rem;
    padding: 0 0 .2rem;
    font-family: var(--font-display);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--rv-ink);
    border-bottom: 2px solid var(--rv-line);
}
.rv-compare__score--high { border-bottom-color: var(--rv-score-high); }
.rv-compare__score--mid  { border-bottom-color: var(--rv-score-mid); }
.rv-compare__score--low  { border-bottom-color: var(--rv-score-low); }

/* --- sticky verdict box (article-review.ejs chrome) --------------------- */
.rv-verdict {
    border: 1px solid var(--rv-line); border-radius: var(--rv-radius); background: var(--rv-surface);
    padding: 1.25rem;
}
.rv-verdict__score {
    display: block;
    font-family: var(--font-display);
    font-size: 3.25rem; font-weight: 700; letter-spacing: -.03em; line-height: 1;
    color: var(--rv-ink);
    font-variant-numeric: tabular-nums;
}
/* Score colour lives in the rule under the number, never in the number itself:
   a 5.4 printed in red reads as an error state, not a low score. */
.rv-verdict__score::after {
    content: "";
    display: block;
    width: 100%;
    height: 3px;
    margin-top: .5rem;
    background: var(--rv-line);
}
.rv-verdict__score--high::after { background: var(--rv-score-high); }
.rv-verdict__score--mid::after  { background: var(--rv-score-mid); }
.rv-verdict__score--low::after  { background: var(--rv-score-low); }
.rv-verdict__label { margin-top: .6rem; font-size: .75rem; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; }
.rv-verdict__score--high + .rv-verdict__label { color: var(--rv-score-high); }
.rv-verdict__score--mid + .rv-verdict__label  { color: var(--rv-score-mid); }
.rv-verdict__score--low + .rv-verdict__label  { color: var(--rv-score-low); }
.rv-verdict__scale { font-size: .8rem; color: var(--color-ink-3); margin-top: .15rem; font-variant-numeric: tabular-nums; }
.rv-verdict__link {
    display: inline-flex; min-height: 44px; align-items: center; justify-content: center;
    margin-top: 1.1rem; padding: .6rem 1.25rem; width: 100%; box-sizing: border-box;
    background: var(--rv-accent); color: var(--color-on-accent); border-radius: var(--rv-radius);
    text-decoration: none; font-weight: 600;
    transition: background .15s ease;
}
.rv-verdict__link:hover { background: color-mix(in oklab, var(--rv-accent) 86%, #000); }
.rv-verdict__disclosure { margin-top: .75rem; font-size: .75rem; color: var(--color-ink-3); line-height: 1.5; }

@media (min-width: 1024px) {
    /* The verdict sidebar must live OUTSIDE the 720px reading measure, not eat
       into it: 720px text column + 2.5rem gap + 280px sidebar = 1040px wrapper.
       Header and hero stay capped at reading width so they align with the text
       column instead of stretching across the widened wrapper. Below 1024px the
       grid is single-column and the default 720px wrapper applies. */
    .main-body--article.main-body--article--wide { max-width: 1040px; }
    .main-content--review > header,
    .main-content--review > img { max-width: 720px; }

    /* minmax(0, 1fr), NOT bare 1fr — a bare 1fr track has an implicit
       min-width:auto, so wide inline content in .rv-main (e.g. the
       product-review card's 3-column .rv-card__top) can force the whole grid
       wider than its container instead of shrinking, pushing the fixed 280px
       sidebar past the viewport edge. */
    .rv-layout { display: grid; grid-template-columns: minmax(0, 1fr) 280px; gap: 2.5rem; align-items: start; }
    /* Sticky, but bounded: on a long roundup an unbounded sticky rail leaves
       thousands of px of empty column below it. align-self:start + the sticky top
       keeps it pinned only while there is article beside it. */
    .rv-verdict { position: sticky; top: 5.5rem; }
}

/* --- Table of contents -------------------------------------------------- */
.rv-toc { border: 1px solid var(--rv-line); border-radius: var(--rv-radius); padding: 1rem 1.25rem; margin: 1.5rem 0; background: var(--rv-surface); }
.rv-toc__title { font-family: var(--font-display); font-size: .72rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; color: var(--rv-muted); margin: 0 0 .6rem; }
.rv-toc__list { list-style: none; margin: 0; padding: 0; }
.rv-toc__list a { display: block; padding: .35rem 0; color: var(--rv-muted); text-decoration: none; font-size: .9rem; }
.rv-toc__list a[data-level="3"] { padding-left: 1rem; }
.rv-toc__list a.is-active { color: var(--rv-ink); font-weight: 600; }
.rv-toc__list a:hover { color: var(--rv-accent); }

/* Every anchor target must clear the sticky header, or a jump-link lands with
   its heading hidden underneath it. */
.main-content--review [id],
.main-content--roundup [id] { scroll-margin-top: 5.5rem; }

/* --- a11y + interaction polish ------------------------------------------ */
.rv-acc__q, .rv-card__review-toggle { min-height: 44px; }
.main-content--review a:focus-visible,
.main-content--review summary:focus-visible,
.main-content--roundup a:focus-visible,
.main-content--roundup summary:focus-visible {
    outline: 3px solid var(--rv-accent); outline-offset: 2px; border-radius: 4px;
}
@media (prefers-reduced-motion: reduce) {
    .main-content--review .rv-card__cta { transition: none; }
}

/* --- responsive ---------------------------------------------------------- */
@media (max-width: 680px) {
    .rv-card__top { grid-template-columns: 1fr; }
    .rv-card__ident { border-right: 0; border-bottom: 1px solid var(--rv-line); }
    .rv-card__scorebox { border-left: 0; border-top: 1px solid var(--rv-line); }
    .rv-proscons { grid-template-columns: 1fr; }
    .rv-criteria__list { grid-template-columns: 1fr; }
    .rv-ranked-badge__label { font-size: 1.1rem; }
}
