/* Customer Reviews — /reviews (promoted out of /concepts 2026-08-20).
   Dark stage, chip-filtered wall of REAL harvested reviews (js/reviews-data.js).
   Follows the concepts-page conventions: full-bleed dark section, inner
   max-width containers (the global main{display:flex} means the outer wrapper
   must set its own width, never rely on margin:auto alone). */

.rvw {
    width: 100%;
    min-width: 0;
    background: #0a0a0b;
    color: #eceae6;
    font-family: 'Public Sans', 'Helvetica Neue', Arial, sans-serif;
    padding-bottom: 90px;
}
.rvw * { box-sizing: border-box; }
.rvw a { color: inherit; }

.rvw-inner { max-width: 1240px; margin: 0 auto; padding: 0 22px; }

/* ---- head ---- */
.rvw-head { padding: 44px 0 10px; }
.rvw-kicker {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px; letter-spacing: .22em; text-transform: uppercase;
    color: #ea3000; margin: 0 0 10px;
}
.rvw-head h1 {
    font-family: 'Oswald', sans-serif; font-weight: 700;
    font-size: clamp(30px, 4.4vw, 52px); line-height: 1.04;
    margin: 0 0 10px; color: #fff; text-transform: uppercase; letter-spacing: .01em;
}
/* <em> is the accent mark on this page, not italics: the phrase that carries
   the headline, and the platform names in the intro. */
.rvw-head h1 em { font-style: normal; color: #ea3000; }
.rvw-lede { max-width: 78ch; color: #9c9a94; font-size: 15px; line-height: 1.55; margin: 0 0 18px; }
.rvw-lede em { font-style: normal; color: #ea3000; font-weight: 600; }
.rvw-lede strong { color: #eceae6; }

/* ---- filter bar ---- */
/* Pinned below the sticky site header (z-index 1000), so a top-0 bar would
   park behind it — the house convention is site-map.css, why-feniex.

   70px, not the header's resting 80: #header carries height:80px but
   shrinks to height:70px once JS adds .scrolled, and this bar only ever
   pins AFTER that has happened. Held at 80 it sat 10px below the shrunken
   header and review cards scrolled visibly through the slot between them.
   Overlapping the resting header's last 10px costs nothing: at rest this
   bar is still in flow, far down the page. */
.rvw-filters {
    position: sticky; top: 70px; z-index: 30;
    background: rgba(10,10,11,.96); backdrop-filter: blur(6px);
    border-top: 1px solid #1c1c1f; border-bottom: 1px solid #1c1c1f;
    padding: 14px 0 12px; margin-bottom: 26px;
}
.rvw-frow { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 8px; }
.rvw-frow:last-child { margin-bottom: 0; }
.rvw-flabel {
    font-family: 'JetBrains Mono', monospace; font-size: 10px; letter-spacing: .18em;
    text-transform: uppercase; color: #6f6d68; min-width: 86px;
}
.rvw-chip {
    font-family: 'Public Sans', sans-serif; font-size: 13px; cursor: pointer;
    background: #131315; color: #c9c7c1; border: 1px solid #2a2a2e; border-radius: 99px;
    padding: 5px 13px; transition: all .13s; user-select: none;
}
.rvw-chip:hover { border-color: #55534e; color: #fff; }
.rvw-chip.on { background: #ea3000; border-color: #ea3000; color: #fff; }

/* ---- grid ---- */
/* Every card is exactly the same size, so the grid reads as a wall rather than
   a ragged masonry. The review body owns whatever vertical space is left over
   after the stars, the optional title and the footer, and is cut off with a
   fade; "Read more" (rendered on every card, hidden when it isn't needed, so
   the height never depends on it) lets one card grow past the rest. */
.rvw-grid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 14px; align-items: start;
}
.rvw-card {
    background: #131315; border: 1px solid #222226; border-radius: 8px;
    padding: 15px 16px 11px; display: flex; flex-direction: column;
    height: 264px; overflow: hidden;
}
.rvw-card.open { height: auto; }
/* A card past the current load-more page, or filtered out by a chip. The
   server renders every card into the HTML (crawlers read all of them) with
   61+ carrying .is-off from the start; the script then toggles it. display,
   not visibility — the grid must reflow as if the card were absent. The
   view's <noscript> block overrides this back to flex when no script runs. */
.rvw-card.is-off { display: none; }
/* Script-created message for a filter that matches nothing — latent while
   every chip has matches, but a blank grid must never be the answer. Spans
   the whole grid row; toggled with the same .is-off convention as the cards. */
.rvw-empty {
    grid-column: 1 / -1; margin: 0; padding: 34px 0;
    font-size: 14px; color: #9c9a94; text-align: center;
}
.rvw-empty.is-off { display: none; }

.rvw-card-top { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; margin-bottom: 7px; }
.rvw-stars { color: #f0a828; font-size: 12px; letter-spacing: .14em; white-space: nowrap; }

/* Title + body share the flexible middle. min-height:0 is what lets a flex
   child actually shrink and clip instead of blowing the card out. */
.rvw-body { flex: 1 1 auto; min-height: 0; position: relative; overflow: hidden; }
/* The fade only belongs on a card whose text is actually cut off. */
.rvw-card.is-clipped:not(.open) .rvw-body::after {
    content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 40px;
    background: linear-gradient(to bottom, rgba(19,19,21,0), #131315);
    pointer-events: none;
}
.rvw-card h3 {
    font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 14.5px;
    color: #fff; margin: 0 0 5px; line-height: 1.3;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.rvw-text { font-size: 13.5px; line-height: 1.55; color: #c9c7c1; margin: 0; }

.rvw-more {
    flex: 0 0 auto; align-self: flex-start; background: none; border: none;
    color: #ea3000; font-size: 11.5px; cursor: pointer; padding: 6px 0 2px;
    font-family: 'JetBrains Mono', monospace; letter-spacing: .04em;
}
.rvw-more:hover { text-decoration: underline; }
/* Kept in the layout, just not shown — a button that disappeared would change
   the card's height. Deliberately a class and not the [hidden] attribute:
   other stylesheets on this site force [hidden] to display:none !important. */
.rvw-more.is-off { visibility: hidden; pointer-events: none; }

.rvw-meta {
    flex: 0 0 auto; padding-top: 9px; border-top: 1px solid #202024;
    display: flex; gap: 4px 9px; align-items: baseline; min-width: 0;
}
.rvw-who { font-size: 12.5px; font-weight: 600; color: #eceae6; white-space: nowrap; }
.rvw-sub {
    font-family: 'JetBrains Mono', monospace; font-size: 10px; color: #7d7b76;
    letter-spacing: .04em; white-space: nowrap;
}
/* The product name is the only variable-width thing down here — let it be the
   one that truncates so the footer can never wrap onto a second line. */
.rvw-sub.rvw-prod { overflow: hidden; text-overflow: ellipsis; min-width: 0; flex: 0 1 auto; }
/* When the product resolves to a page the name becomes the way back to it.
   Underlined rather than recoloured: the meta row is a deliberately quiet
   monospace strip, and a bright link in it would outshout the review. */
a.rvw-prod-link { text-decoration: underline; text-underline-offset: 2px; text-decoration-color: rgba(125,123,118,.5); }
a.rvw-prod-link:hover, a.rvw-prod-link:focus-visible { color: #d8d7d0; text-decoration-color: currentColor; }
/* Category fallback: the product has no page of its own, so the link goes to
   its band on /products. Dotted rather than solid so it does not read as the
   product's own page at a glance - a different destination, shown differently. */
a.rvw-prod-cat { text-decoration: underline dotted; text-underline-offset: 2px; text-decoration-color: rgba(125,123,118,.45); }
a.rvw-prod-cat:hover, a.rvw-prod-cat:focus-visible { color: #d8d7d0; text-decoration-color: currentColor; }
/* The category link's destination, for screen readers. `title` is hover-only,
   so it is the mouse user's affordance and no one else's. Defined here rather
   than reusing the sitewide .sr-only: that class is not in this page's own
   stylesheet, so scoping it locally keeps the disclosure from depending on
   what the shared bundle happens to carry. Absolute, so the product name's
   ellipsis truncation still measures only the visible text. */
.rvw-sr {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
    overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.rvw-sub.rvw-date { margin-left: auto; flex: 0 0 auto; }
.rvw-badge {
    font-family: 'JetBrains Mono', monospace; font-size: 9px; letter-spacing: .08em;
    text-transform: uppercase; color: #3fae6a; border: 1px solid rgba(63,174,106,.4);
    border-radius: 3px; padding: 1px 5px;
}
.rvw-badge.vine { color: #c9a227; border-color: rgba(201,162,39,.4); }

/* ---- load more / footer ---- */
.rvw-loadwrap { text-align: center; padding: 30px 0 6px; }
.rvw-load {
    font-family: 'Oswald', sans-serif; font-size: 15px; letter-spacing: .08em; text-transform: uppercase;
    background: none; border: 1px solid #ea3000; color: #fff; border-radius: 4px;
    padding: 11px 34px; cursor: pointer; transition: background .15s;
}
.rvw-load:hover { background: #ea3000; }

@media (max-width: 640px) {
    .rvw-flabel { min-width: 0; width: 100%; }
    /* The filter block is three rows tall at phone width — sticky here would
       hold ~180px of a small screen hostage, so it scrolls with the page. */
    .rvw-filters { position: static; }
    /* One column means no row heights to equalize: short reviews shed the
       fixed 264px box's dead space, long ones still clip at the same line
       and keep the fade + Read more. markOverflowing() measures live, so
       the button logic follows automatically. */
    .rvw-card { height: auto; max-height: 264px; }
    .rvw-card.open { max-height: none; }
}
