/*
 * The listing page's design system.
 *
 * WHY IT IS A FILE AND NOT INLINE. The design harness carried all 56 KB of this
 * inside every profile — so 173 pages each re-downloaded the same stylesheet,
 * and none of them could cache it. As a file with a version in its URL a
 * browser fetches it once for the whole site.
 *
 * WHY EVERY RULE IS SCOPED TO .rg-listing. The theme's own shell — header, nav,
 * footer, buttons — is namespaced `rg-`, and this stylesheet is not: it styles
 * `.card`, `.chip`, `.rail`, `.btn`. Left unscoped the two would fight over any
 * page that showed both. The scope also means a listing page loads one extra
 * stylesheet rather than a replacement for the theme's, so the header and footer
 * still look like the rest of the site.
 *
 * WHY THERE IS NO :root BLOCK. The harness defined its own palette. The colours
 * are the theme's now, driven by the Customizer, and the block below only
 * aliases the short names the rules use onto the theme's tokens. Change the
 * brand colour once and this follows — which is the whole reason the theme is
 * portable to a vertical it has never heard of.
 */

.rg-listing {
  --brand:  var(--rg-brand);
  --bd:     var(--rg-brand-dark);
  --navy:   var(--rg-deep);
  --navy2:  var(--rg-deep-2);
  --ink:    var(--rg-ink);
  --muted:  var(--rg-muted);
  --line:   var(--rg-line);
  --bg:     var(--rg-bg);
  --card:   var(--rg-surface);
  --good:   var(--rg-good);
  --bad:    var(--rg-bad);
  --warn:   var(--rg-warn);
  --star:   var(--rg-star);
  --r:      var(--rg-radius);
  --gap:    var(--rg-gap);
  --shadow: var(--rg-shadow);

  color: var(--ink);
  font-family: var(--rg-font);
}

.rg-listing,.rg-listing *{box-sizing:border-box}
.rg-listing{margin:0;font:16px/1.55 Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Arial,sans-serif;
     color:var(--ink);background:var(--bg);-webkit-font-smoothing:antialiased}
.rg-listing a{color:var(--bd)}
.rg-listing .wrap{max-width:1160px;margin:0 auto;padding:18px 20px 60px}
.rg-listing .crumb{font-size:12px;color:var(--muted);margin:14px 0 12px}
.rg-listing .crumb a{color:var(--muted)}
.rg-listing .crumb b{color:var(--ink)}
/* ── cards ─────────────────────────────────────────────────────────────── */
.rg-listing .card{background:var(--card);border:1px solid var(--line);border-radius:var(--r);
      padding:22px;box-shadow:var(--shadow);min-width:0;display:flex;flex-direction:column}
.rg-listing .card h2{margin:0 0 4px;font-size:16px;color:var(--navy);display:flex;align-items:center;gap:8px}
.rg-listing .card h2:before{content:"";width:18px;height:3px;background:var(--brand);border-radius:2px;flex:none}
.rg-listing .ch{font-size:13px;color:var(--muted);margin:0 0 14px}
/* The only .src left on the page is the owner's "supplied by" line on the
   claimed cards. Every FACT source now lives in the ledger at the foot. */
.rg-listing .src{font-size:11.5px;color:#8a98ad;margin:14px 0 0;padding-top:12px;border-top:1px solid var(--line)}
.rg-listing .card .src{margin-top:auto}
.rg-listing .empty{font-size:13px;color:var(--muted);font-style:italic;background:#f8fafc;
       border:1px dashed var(--line);border-radius:8px;padding:14px}
/* ── bento: 3 columns, every row sums to 3, no row spans ──────────────── */
.rg-listing .bento{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--gap);margin-top:var(--gap)}
.rg-listing .c1{grid-column:span 1}
.rg-listing .c2{grid-column:span 2}
.rg-listing .c3{grid-column:span 3}
/* the one row span that earns its keep — a tall card with two short ones
   stacked beside it, rather than a short card stretched to a tall neighbour */
.rg-listing .r2{grid-row:span 2}
/* ── header — one row, like the original. Depth comes from the raised stat
      tiles, not from outline pills; the pills were the flat "brows". ────── */
.rg-listing .hero{background:var(--card);border:1px solid var(--line);border-radius:var(--r);
      padding:18px 22px;box-shadow:var(--shadow);display:flex;gap:22px;
      align-items:center;flex-wrap:wrap}
.rg-listing .hero-id{display:flex;gap:14px;align-items:center;flex:1;min-width:250px}
/* 108px is the height of the stat block beside it — the rating tile, a 6px gap
   and the row of counts. A 54px chip next to a 108px block read as an
   afterthought; matching it makes the header one object. */
.rg-listing .ini{width:108px;height:108px;border-radius:12px;background:var(--brand);color:#fff;flex:none;
     display:flex;align-items:center;justify-content:center;font-weight:800;font-size:34px;
     overflow:hidden}
/* A logo is white-on-white against the card, so the tile edge only showed where
   the drop shadow fell — bottom and right — and read as a lopsided frame. With
   a real border it is even on all four sides; the shadow is now a lift, not the
   outline. Initials keep the solid brand tile and need no border. */
.rg-listing .ini--img{background:#fff;border:1px solid var(--line);
          box-shadow:0 1px 2px rgba(16,32,64,.07)}
.rg-listing .ini img{width:100%;height:100%;object-fit:contain;background:#fff;padding:5px;
         box-sizing:border-box}
.rg-listing .ini--dark img{background:var(--navy)}
/* a white logo needs a dark tile */
.rg-listing .hero-who{min-width:0}
.rg-listing .hero h1{margin:0 0 3px;font-size:21px;color:var(--navy);line-height:1.2;
         display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.rg-listing .vmark{font-size:12px;font-weight:800;color:#fff;background:var(--good);width:18px;height:18px;
       border-radius:50%;display:inline-flex;align-items:center;justify-content:center;
       box-shadow:0 1px 2px rgba(16,32,64,.2)}
/* --herogap drives BOTH the drop from the address line to the contact row and
   the gap between the items inside it, so the two spaces are equal by
   construction rather than by two numbers that happen to match. */
.rg-listing .hero-who{--herogap:10px}
.rg-listing .hero .addr{font-size:13px;color:var(--muted);margin:0 0 var(--herogap)}
.rg-listing .hero .addr a{color:var(--muted);text-decoration:none}
.rg-listing .hero .addr a:hover{color:var(--brand);text-decoration:underline}
.rg-listing .quietnote{font-style:normal;color:var(--warn);font-weight:600}
.rg-listing .hero-links{display:flex;gap:var(--herogap);flex-wrap:wrap}
.rg-listing .hero-links a{font-size:13.5px;font-weight:600;color:var(--bd);text-decoration:none;
              white-space:nowrap;display:inline-flex;align-items:center;gap:6px}
.rg-listing .hero-links .hi{width:14px;height:14px;flex:none;opacity:.72}
/* one number by default; the rest behind a +N. A franchise with seventeen
   switchboard lines is not seventeen ways to reach a builder. */
.rg-listing .hero-links a[hidden]{display:none}
.rg-listing .ph-more{font:inherit;font-size:12px;font-weight:700;color:var(--bd);cursor:pointer;
         background:#f3f6fb;border:1px solid var(--line);border-radius:999px;padding:2px 9px}
.rg-listing .ph-more:hover{border-color:var(--brand)}
.rg-listing .hero--dark .ph-more{background:rgba(255,255,255,.1);border-color:rgba(255,255,255,.2);color:#a8c4f5}
/* No underline: these are buttons. The address line above is inline text and keeps its. */
.rg-listing .hero-links a:hover{color:var(--brand)}
/* the raised tiles — this is the bit that reads as depth */
/* A grid, not a wrapping flex row: with flex the rating's 100% basis resolved
   against the wrong box and it came out about twice the width of the three
   counts beneath it. Three equal columns means the rating spanning all three is
   the exact width of the row below, by construction and at any content width. */
/* --tiles comes from the data: a builder with no county tile has two counts,
   not three, and a fixed three-column grid left an empty cell beside them. */
.rg-listing .hf-stats{display:grid;grid-template-columns:repeat(var(--tiles,3),minmax(70px,1fr));
          gap:6px;flex:none}
/* The inset `0 1px 0 #fff` drew a white line immediately inside the top border,
   which read as the frame sitting a pixel proud of its own content — the
   "drifting upwards" edge. The lift comes from the drop shadow alone now. */
.rg-listing .hf-s{background:var(--bg);border:1px solid var(--line);border-radius:8px;padding:9px 15px;
      text-align:center;min-width:70px;display:flex;flex-direction:column;
      justify-content:center;box-shadow:0 1px 2px rgba(16,32,64,.06)}
/* the rating is one of the tiles now, just the gold one */
/* the rating is the headline, so it sits above the three counts rather than
   reading as a fourth one */
.rg-listing .hf-s--rate{grid-column:1/-1}
.rg-listing .hf-s--rate b{font-size:20px}
.rg-listing .hf-s b{display:block;font-size:18px;font-weight:800;color:var(--navy);line-height:1.15}
/* AFTER .hf-s b, not before: same specificity, so source order decides — the
   rating was inheriting navy on light and white on dark, never the star gold */
.rg-listing .hf-s--rate b{color:var(--star)}
.rg-listing .hf-s span{font-size:11px;color:var(--muted);display:block}
/* the button was 39px against 56px tiles and read as a different family */
.rg-listing .hero-cta{display:flex;flex-direction:column;gap:7px;flex:none;align-items:stretch}
.rg-listing .hero-cta .btn{min-height:56px;display:flex;align-items:center;justify-content:center}
.rg-listing .btn{padding:11px 18px;border-radius:8px;font-weight:700;font-size:14px;text-decoration:none;
     border:0;display:block;text-align:center;cursor:pointer;font-family:inherit}
.rg-listing .btn.primary{background:var(--brand);color:#fff;box-shadow:0 1px 2px rgba(29,78,216,.35),
             0 1px 0 rgba(255,255,255,.25) inset}
.rg-listing .btn.primary:hover{background:var(--bd)}
.rg-listing .btn.ghost{background:#fff;color:var(--bd);border:1px solid var(--line)}
.rg-listing .btn.ghost:hover{border-color:var(--brand)}
/* ── the phone mark ─────────────────────────────────────────────────────
   A mask rather than an image, so the icon takes the COLOUR of whatever it
   sits in — brand blue on a ghost button, green on Call now, pale blue on the
   navy header — without shipping four tinted copies of the same glyph. */
/* .btn declares display:block and is defined further down the sheet, so the
   bare .tel rule lost on source order and the pseudo-element collapsed to
   nothing on every button. Sits after .btn now, and carries the compound
   selectors so it cannot lose again if the order shifts. */
.rg-listing .tel,.rg-listing a.tel,.rg-listing .btn.tel{display:inline-flex;align-items:center;justify-content:center;gap:8px}
.rg-listing .tel:before{content:"";display:block;width:15px;height:15px;flex:none;background:currentColor;
            -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' d='M3.654 1.328a.678.678 0 0 0-1.015-.063L1.605 2.3c-.483.484-.661 1.169-.45 1.77a17.6 17.6 0 0 0 4.168 6.608 17.6 17.6 0 0 0 6.608 4.168c.601.211 1.286.033 1.77-.45l1.034-1.034a.678.678 0 0 0-.063-1.015l-2.307-1.794a.68.68 0 0 0-.58-.122l-2.19.547a1.75 1.75 0 0 1-1.657-.459L5.482 8.062a1.75 1.75 0 0 1-.46-1.657l.548-2.19a.68.68 0 0 0-.122-.58z'/%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' d='M3.654 1.328a.678.678 0 0 0-1.015-.063L1.605 2.3c-.483.484-.661 1.169-.45 1.77a17.6 17.6 0 0 0 4.168 6.608 17.6 17.6 0 0 0 6.608 4.168c.601.211 1.286.033 1.77-.45l1.034-1.034a.678.678 0 0 0-.063-1.015l-2.307-1.794a.68.68 0 0 0-.58-.122l-2.19.547a1.75 1.75 0 0 1-1.657-.459L5.482 8.062a1.75 1.75 0 0 1-.46-1.657l.548-2.19a.68.68 0 0 0-.122-.58z'/%3E%3C/svg%3E") center/contain no-repeat}
.rg-listing .hero-links .tel:before{width:14px;height:14px;opacity:.72}
.rg-listing .links a.tel{justify-content:flex-start;gap:9px}
.rg-listing .links a.tel:before{width:14px;height:14px;opacity:.7}
@media(max-width:980px){
.rg-listing .hf-stats{order:5}
.rg-listing .hero-cta{order:6}
.rg-listing .hero--prem .addr{white-space:normal}
}
/* MOBILE HEADER. The stat tiles were sizing to their own content while the
   button stretched, so the two blocks stacked at different widths. Both take
   the full row now, and the tiles keep their 3-column grid inside it — so the
   rating spans the same width as the button underneath it. */
@media(max-width:700px){
.rg-listing .hero{gap:16px}
.rg-listing .hero-id{min-width:100%;gap:16px}
.rg-listing .ini{width:96px;height:96px;border-radius:12px;font-size:30px}
/* PREMIUM stacks. Its logo is a wide mark, up to 250px, so side by side with
     the name there was nothing left for the name — and a long one wrapped into
     a column two words wide next to the Premium pill. Logo on its own full
     width row, everything else under it. */
.rg-listing .hero--prem .hero-id{flex-direction:column;align-items:stretch;gap:14px}
.rg-listing .hero--prem .ini--img{width:100%;max-width:none;justify-content:flex-start}
.rg-listing .hero--prem .ini--img img{height:54px;max-width:100%}
.rg-listing .hero--prem .ini:not(.ini--img){width:100%;height:84px}
/* the tiles and the button both take the whole row, so they line up */
.rg-listing .hf-stats{width:100%;flex:1 1 100%}
.rg-listing .hero-cta{width:100%;flex:1 1 100%}
.rg-listing .hero-cta .btn{width:100%}
}
/* ── ratings ──────────────────────────────────────────────────────────── */
.rg-listing .rbar{display:flex;align-items:center;gap:10px;margin-bottom:10px;text-decoration:none;color:inherit}
.rg-listing .rbar .pl{width:52px;font-size:13px;font-weight:600;color:var(--navy);flex:none}
.rg-listing .rbar .track{flex:1;height:9px;border-radius:4px;background:#eef2f6;overflow:hidden}
.rg-listing .rbar .fill{display:block;height:100%;background:var(--good);border-radius:4px}
.rg-listing .rbar .v{font-size:13px;font-weight:700;min-width:78px;text-align:right;color:var(--navy);flex:none}
.rg-listing a.rbar:hover .pl{color:var(--brand)}
.rg-listing .agg{margin-top:14px;padding:14px;border-radius:8px;background:#f6f9fd;border:1px solid var(--line)}
.rg-listing .agg .big{font-size:1.5rem;font-weight:800;color:var(--navy);line-height:1.1}
.rg-listing .agg .lb{font-size:12px;color:var(--muted)}
.rg-listing .agg .spread{font-size:12.5px;color:var(--warn);margin:8px 0 0;line-height:1.45}
/* "Still active" alone is a verdict with nothing behind it — the state, the
   month it was last seen, and what the signal actually was */
.rg-listing .lvhead{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}
.rg-listing .lvhead b{font-size:14.5px;font-weight:800}
.rg-listing .lvhead span{font-size:12.5px;color:var(--muted);font-weight:600}
.rg-listing .lvhead .lvdot{width:9px;height:9px;border-radius:50%;flex:none;align-self:center}
.rg-listing .lvhead.on b{color:var(--good)}
.rg-listing .lvhead.on .lvdot{background:var(--good);
              box-shadow:0 0 0 3px rgba(26,156,84,.18)}
.rg-listing .lvhead.off b{color:var(--warn)}
.rg-listing .lvhead.off .lvdot{background:var(--warn);
              box-shadow:0 0 0 3px rgba(214,146,21,.18)}
/* Was bold green/red, which made a background fact compete with the score
   itself — and read as an alarm on premium. It is context, so it is styled like
   context. (The average is recomputed from our own corpus on every build, so it
   tracks the data rather than going stale.) */
.rg-listing .vsline{font-size:12.5px;margin:8px 0 0;line-height:1.45;font-weight:400;color:var(--muted)}
/* ── topics — compact, colour-coded, proportional. No quotes: they arrive
      clipped mid-sentence from the source and made the card unreadable. ── */
.rg-listing .topics{display:grid;grid-template-columns:1fr 1fr;gap:14px 26px}
.rg-listing .tcol h3{margin:0 0 11px;font-size:11.5px;font-weight:800;letter-spacing:.07em;
         text-transform:uppercase;display:flex;align-items:center;gap:7px}
.rg-listing .tcol h3 i{width:8px;height:8px;border-radius:50%;display:inline-block}
.rg-listing .tcol.pos h3{color:var(--good)}
.rg-listing .tcol.pos h3 i{background:var(--good)}
.rg-listing .tcol.neg h3{color:var(--warn)}
.rg-listing .tcol.neg h3 i{background:var(--warn)}
.rg-listing .topic{margin-bottom:13px}
.rg-listing .topic:last-child{margin-bottom:0}
.rg-listing .topic .hd{display:flex;align-items:baseline;gap:8px;margin-bottom:5px}
.rg-listing .topic .tn{font-size:14px;font-weight:700;color:var(--navy);text-transform:capitalize}
.rg-listing .topic .cnt{font-size:11.5px;font-weight:700;margin-left:auto;white-space:nowrap;
            padding:1px 8px;border-radius:999px}
.rg-listing .tcol.pos .cnt{color:#15723e;background:#eafaf1}
.rg-listing .tcol.neg .cnt{color:var(--warn);background:#fff6e6}
.rg-listing .topic .bar{height:6px;border-radius:2px;background:#eef2f6;overflow:hidden;margin-bottom:6px}
.rg-listing .topic .bar i{display:block;height:100%;border-radius:2px}
.rg-listing .tcol.pos .bar i{background:var(--good)}
.rg-listing .tcol.neg .bar i{background:var(--star)}
.rg-listing .terms{display:flex;gap:5px;flex-wrap:wrap}
.rg-listing .term{font-size:11.5px;padding:2px 8px;border-radius:6px;border:1px solid var(--line)}
.rg-listing .tcol.pos .term{background:#f2fbf6;color:#15723e;border-color:#cdeedb}
.rg-listing .tcol.neg .term{background:#fffaf0;color:var(--warn);border-color:#f1dcae}
/* ── reviews: one panel per platform, tabbed when both carry text ────── */
/* ── reviews rail ───────────────────────────────────────────────────────
   Reviews stacked vertically made this the tallest card on the page, so nothing
   could sit beside it without a column of white. Across a full-width row the
   height is fixed by the card, not by how much people wrote, and the overflow
   scrolls sideways. Arrows for the mouse, scroll-snap for touch, and the rail
   is focusable so the keyboard can reach it too. */
.rg-listing .railwrap{position:relative}
.rg-listing .rail{display:flex;gap:12px;overflow-x:auto;scroll-behavior:smooth;
      scroll-snap-type:x mandatory;padding:2px;scrollbar-width:thin}
.rg-listing .rail::-webkit-scrollbar{height:7px}
.rg-listing .rail::-webkit-scrollbar-thumb{background:#d3dbe6;border-radius:4px}
.rg-listing .rail:focus-visible{outline:2px solid var(--brand);outline-offset:2px;border-radius:8px}
/* Head variant: the arrows live in the card's own header, on the line with the
   caption, ABOVE the item they page through. Used where the rail shows one item
   at a time — a license — so an arrow beside or over the card would sit on top
   of the number you are trying to read. */
.rg-listing .railwrap.head .railhead{display:flex;align-items:center;gap:12px;margin-bottom:11px}
.rg-listing .railwrap.head .railhead .ch{margin:0;flex:1;min-width:0}
.rg-listing .railnav{display:flex;gap:6px;flex:none}
.rg-listing .railwrap.head .railbtn{position:static;transform:none;flex:none;box-shadow:none;
                        width:26px;height:26px;font-size:16px}
.rg-listing .railbtn[hidden]{display:none}
.rg-listing .railbtn{position:absolute;top:50%;transform:translateY(-50%);z-index:2;width:34px;height:34px;
         border-radius:50%;border:1px solid var(--line);background:#fff;color:var(--navy);
         font-size:20px;line-height:1;cursor:pointer;box-shadow:0 2px 10px rgba(10,21,53,.16)}
.rg-listing .railbtn.prev{left:-8px}
.rg-listing .railbtn.next{right:-8px}
.rg-listing .railbtn:hover:not(:disabled){border-color:var(--brand);color:var(--brand)}
.rg-listing .railbtn:disabled{opacity:0;pointer-events:none}
.rg-listing .rall{display:flex;gap:8px;flex-wrap:wrap;margin-top:14px}
.rg-listing .rall .btn{font-size:12.5px;padding:9px 14px}
/* ── reviews — full text, clamped, expandable ─────────────────────────── */
/* grow to fill when there are only two or three reviews, never shrink below
   300px — so a thin builder fills the row and a rich one scrolls */
.rg-listing .rev{flex:1 0 300px;scroll-snap-align:start;display:flex;flex-direction:column;
     border:1px solid var(--line);border-radius:8px;padding:14px;background:#fbfcfe}
@media(max-width:560px){
.rg-listing .rev{flex-basis:80%}
}
.rg-listing .rev .meta{display:flex;align-items:center;gap:8px;margin-bottom:7px;flex-wrap:wrap}
.rg-listing .stars{color:var(--star);font-size:13px;letter-spacing:1px}
.rg-listing .stars.low{color:var(--bad)}
.rg-listing .badge{font-size:10.5px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;
       background:#eef4fb;border:1px solid var(--line);color:var(--bd);padding:2px 8px;border-radius:999px}
.rg-listing .rev .who{font-size:12px;color:var(--muted);margin-left:auto}
.rg-listing .rev p{margin:0;font-size:13.5px;line-height:1.55;color:#33404f}
.rg-listing .rev .body{display:-webkit-box;-webkit-line-clamp:7;-webkit-box-orient:vertical;overflow:hidden}
.rg-listing .rev.open .body{display:block}
.rg-listing .rev .tog{background:none;border:0;color:var(--bd);font:inherit;font-size:12.5px;font-weight:700;
          cursor:pointer;padding:8px 0 0}
.rg-listing .rev .tog:hover{text-decoration:underline}
.rg-listing .rev .reply{margin-top:10px;padding:10px 12px;background:#f3f6fb;border-radius:6px;
            font-size:12.5px;color:#46515f}
.rg-listing .rev .reply b{color:var(--navy);display:block;font-size:11.5px;margin-bottom:3px}
/* ── license ──────────────────────────────────────────────────────────── */
/* One license per view, arrows for the rest. A handful of national outfits
   carry eight or more qualifiers — the worst case on file has eight — and
   stacked they made this card taller than the map it sits beside, which broke
   the two-row block. Same rail as the reviews: fixed height, scroll for depth.
   90% of builders hold one license and never see an arrow. */
.rg-listing .lic{flex:1 0 100%;scroll-snap-align:start;box-sizing:border-box;
     border:1px solid var(--line);border-radius:8px;padding:13px 14px;background:#fbfcfe}
.rg-listing .lic .num{display:block;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:14px;
          font-weight:700;color:var(--navy)}
.rg-listing .lic .tp{font-size:12px;color:var(--muted);margin:3px 0 9px}
/* `minmax(0,1fr)`, not `1fr` — a grid track's default minimum is its content, so
   one unbroken string (a pasted URL where a coverage limit was expected) pushed
   the value column past the card and out of the layout. `anywhere` lets it break
   mid-string, which is the only place a URL CAN break. */
.rg-listing .kv{display:grid;grid-template-columns:auto minmax(0,1fr);gap:5px 12px;font-size:13px;margin:0}
.rg-listing .kv dt{color:var(--muted)}
.rg-listing .kv dd{margin:0;font-weight:600;color:var(--navy);min-width:0;overflow-wrap:anywhere}
.rg-listing .vb{font-size:11px;font-weight:700;border-radius:6px;padding:3px 8px;white-space:nowrap}
/* Licence status. Four colours and a no-colour, named by the directory — the
   theme does not know what a trade's statuses mean.
   ⚠️ `.ok` is KEPT as an alias for green: other cards use it, and grepping
   before renaming a class is the standing rule here. */
.rg-listing .vb.ok,
.rg-listing .vb.green{background:#eafaf1;color:#15723e;border:1px solid #b9e7cd}
.rg-listing .vb.blue{background:#eef4fb;color:#1d4e89;border:1px solid #c3d8ef}
.rg-listing .vb.yellow{background:#fdf6e3;color:#8a6100;border:1px solid #ecd9a3}
.rg-listing .vb.red,
.rg-listing .vb.no{background:#fdecec;color:#a11b1b;border:1px solid #f2c4c4}
/* ── chips → service pages ────────────────────────────────────────────── */
.rg-listing .chips{display:flex;gap:6px;flex-wrap:wrap}
.rg-listing .chip{background:#eef4fb;border:1px solid var(--line);border-radius:6px;padding:6px 11px;
      font-size:13px;font-weight:600;color:var(--navy);text-decoration:none;display:inline-block}
.rg-listing a.chip:hover{border-color:var(--brand);color:var(--brand)}
/* premium only — the tick means the OWNER confirmed this service, which is why
   it appears on no other tier. See the cabinet note in build.py. */
.rg-listing .chip.ok{background:#f1fbf5;border-color:#c3e9d3;display:inline-flex;align-items:center;gap:6px}
.rg-listing .chip.ok .tick{width:13px;height:13px;border-radius:50%;background:var(--good);flex:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.6 6.35 4.9 8.65 9.4 3.65' fill='none' stroke='%23fff' stroke-width='2.1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:center;background-size:74% 74%}
.rg-listing a.chip.ok:hover{border-color:var(--good);color:var(--good)}
/* the CAMERA mark is a different claim from the tick: not "the owner says so"
   but "we can see it in a photograph they published". Deliberately quieter than
   the tick — evidence we gathered should never out-shout a person's own word. */
.rg-listing .chip.shot{background:#f7f6fb;border-color:#dcd7ea;display:inline-flex;align-items:center;gap:6px}
.rg-listing .chip.shot .shot-mark{width:13px;height:11px;flex:none;background-repeat:no-repeat;background-position:center;background-size:contain;
      background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 13'%3E%3Cpath d='M1.4 3.6h2.7l1-1.6h5.8l1 1.6h2.7v7.8H1.4z' fill='none' stroke='%237a6ea8' stroke-width='1.5' stroke-linejoin='round'/%3E%3Ccircle cx='8' cy='7.4' r='2.5' fill='none' stroke='%237a6ea8' stroke-width='1.5'/%3E%3C/svg%3E")}
.rg-listing a.chip.shot:hover{border-color:#7a6ea8;color:#5b5185}
/* the group headings inside "What they build". Small, uppercase, no rule — the
   chips already carry the visual weight and a second border would box-in-a-box. */
.rg-listing .build-group{font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
      color:var(--muted);margin:0 0 7px}

/* ── the groups sit in columns, so the card fills whatever width it gets ──────
   The packer widens the last card of a row to close a gap, so "what they build"
   can end up spanning three columns whatever it asked for. Stacked, seven
   headings with a chip or two under each left three quarters of that width
   empty. Laid out in columns the same blocks fill it and the card is a third as
   tall.

   ⚠️ GRID, NOT `column-count`. Multi-column fragments its content: the first
   attempt split a group across two columns and left a chip standing alone in
   the third with no heading over it, reading as a service nobody had labelled.
   `break-inside:avoid` did not hold it — a flex row of chips is exactly the
   thing multicol will break. A grid ITEM cannot be split, so it cannot happen.

   And the column count follows the card's real width rather than its span, so
   one, two or three falls out of the measurement instead of being asserted in
   three places. `align-items:start` keeps short groups at the top of their cell
   rather than floating in the middle of a tall row. */
.rg-listing .build-cols{display:grid;align-items:start;
      grid-template-columns:repeat(auto-fill,minmax(200px,1fr));
      gap:15px 26px}
.rg-listing .build-grp{min-width:0}
/* ⚠️ WAS MISSING FROM THIS STYLESHEET ENTIRELY. It is defined in cabinet.css,
   so the owner cabinet hid its screen-reader labels correctly and the public
   profile printed "Confirmed by the owner:" as visible text next to every
   ticked chip. Nobody caught it because the tick only appears on claimed
   listings, of which the sandbox has one. */
.rg-listing .screen-reader-text{border:0;clip:rect(1px,1px,1px,1px);clip-path:inset(50%);height:1px;
      margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;white-space:nowrap}
/* an explicit display on .chip beats the UA [hidden]{display:none} rule, so the
   hidden overflow chips were still being laid out — the card grew by ~1000px */
.rg-listing .chip[hidden]{display:none}
.rg-listing .chip-btn{font:inherit;font-size:13px;font-weight:600;cursor:pointer;background:#f8fafc;
          color:var(--muted);border:1px dashed var(--line)}
.rg-listing .chip-btn:hover{border-color:var(--brand);color:var(--brand)}
.rg-listing .none{font-size:12.5px;color:var(--muted);font-style:italic;margin:0}
/* ── works-in map — county choropleth, styled after the live site: flat
      fills, WHITE county borders, colour depth carrying the signal. Real
      the region from Census county polygons, supplied as data. No tile server, no
      Leaflet. Dots and per-city labels are gone: a scatter of dots reads as
      noise where shaded counties read as a map at a glance. ────────────── */
.rg-listing .mapwrap{background:#f5f8fc;border:1px solid var(--line);border-radius:8px;padding:10px;
         position:relative;overflow:hidden}
.rg-listing svg.fl{cursor:grab;touch-action:none}
.rg-listing svg.fl.drag{cursor:grabbing}
/* zoom controls — the static fit is the default view, these are on top of it */
.rg-listing .mapzoom{position:absolute;top:16px;left:16px;display:flex;flex-direction:column;gap:-1px;
         border-radius:6px;overflow:hidden;box-shadow:0 1px 4px rgba(10,21,53,.22)}
.rg-listing .mapzoom button{width:28px;height:28px;border:0;background:#fff;color:var(--navy);
                font:inherit;font-size:15px;font-weight:700;cursor:pointer;line-height:1;
                border-bottom:1px solid var(--line);padding:0}
.rg-listing .mapzoom button:last-child{border-bottom:0;font-size:13px}
.rg-listing .mapzoom button:hover{background:var(--brand);color:#fff}
.rg-listing .mapzoom button:disabled{color:#c3cddb;background:#fff;cursor:default}
/* hover label, after the original's .leaflet-tooltip.nb */
.rg-listing .maptip{position:absolute;pointer-events:none;z-index:3;background:var(--navy);color:#fff;
        font-size:12px;font-weight:600;padding:5px 9px;border-radius:6px;white-space:nowrap;
        box-shadow:0 2px 8px rgba(10,21,53,.35);transform:translate(-50%,-140%)}
.rg-listing .maptip:after{content:"";position:absolute;left:50%;bottom:-4px;transform:translateX(-50%);
              border:4px solid transparent;border-top-color:var(--navy);border-bottom:0}
.rg-listing .maptip .ck{color:#7fe3a8;margin-right:4px}
.rg-listing .maptip .hm{color:#9fb6d6;font-weight:400}
.rg-listing svg.fl{display:block;width:100%;height:auto;max-height:340px}
.rg-listing .fl .county{fill:#dbe3ec;stroke:#fff;stroke-width:1.1;vector-effect:non-scaling-stroke}
.rg-listing .fl .cwash{fill:var(--brand);stroke:#fff;stroke-width:1.1;vector-effect:non-scaling-stroke;
           pointer-events:none}
/* dots, not outlines — a heavier white ring so overlapping dots stay separable
   in a tight metro, and a cursor, because they are the only thing you can hover */
.rg-listing .fl .city{fill:var(--brand);stroke:#fff;stroke-width:1.6;vector-effect:non-scaling-stroke;
          cursor:default;transition:fill-opacity .15s,r .15s}
.rg-listing .fl .city:hover{stroke-width:2.2}
/* dark blue on hover, never near-black: --navy is #0A1535 and read as a hole
   punched in the map. --bd is the brand's own dark blue. */
.rg-listing .fl .city:hover{fill:var(--bd);fill-opacity:1}
.rg-listing .fl .city.home{fill:var(--good)}
.rg-listing .fl .city.home:hover{fill:#15803d;fill-opacity:1}
.rg-listing .chip.base{background:#eafaf1;border-color:#b9e7cd;color:#15723e}
.rg-listing .maplegend{display:flex;align-items:center;gap:8px 18px;flex-wrap:wrap;margin:11px 0 0;
           font-size:11.5px;color:var(--muted);line-height:1.5}
.rg-listing .maplegend b{display:inline-flex;align-items:center;gap:5px;white-space:nowrap;font-weight:400}
/* the swatches are dots at three sizes now, because the map is dots at three
   sizes — they were still rectangles at three opacities, describing the outline
   map that no longer exists */
.rg-listing .maplegend .sw{display:inline-block;border-radius:50%;flex:none;background:var(--brand);
               border:1.5px solid #fff}
.rg-listing .maplegend .sw.a{width:7px;height:7px;opacity:.5}
.rg-listing .maplegend .sw.b{width:11px;height:11px;opacity:.75}
.rg-listing .maplegend .sw.c{width:15px;height:15px;opacity:1}
.rg-listing .maplegend .sw.home{width:13px;height:13px;background:var(--good);opacity:1}
.rg-listing .maplegend .sw.co{width:15px;height:11px;border-radius:2px;background:var(--brand);
                  opacity:.22;border-color:#fff}
/* ── verified ─────────────────────────────────────────────────────────── */
/* A checked claim, framed in green on all four sides — the left-rule-and-tint
   treatment read as a generic callout box rather than as something verified. */
.rg-listing .vitem{position:relative;border:1.5px solid #b9e7cd;border-radius:8px;
       padding:13px 15px 13px 42px;margin-bottom:10px;background:#f4fcf7}
.rg-listing .vitem:last-of-type{margin-bottom:0}
/* A rotated pair of CSS borders never sat right inside the circle — it was
   half a pixel off at every size and drifted the moment the radius changed.
   One vector tick, centred by background-position, cannot drift. */
.rg-listing .vitem:before{content:"";position:absolute;left:14px;top:14px;width:18px;height:18px;
              border-radius:50%;background:var(--good);
              box-shadow:0 1px 3px rgba(26,156,84,.32);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.6 6.35 4.9 8.65 9.4 3.65' fill='none' stroke='%23fff' stroke-width='2.1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:center;background-size:74% 74%}
.rg-listing .vitem .q{display:inline-block;font-size:14px;font-weight:800;color:#146c40;
          margin:0 8px 5px 0;letter-spacing:-.01em}
.rg-listing .vitem p{margin:0;font-size:12.5px;color:#46515f;line-height:1.55}
/* ── about / faq ──────────────────────────────────────────────────────── */
.rg-listing .about h3{font-size:11.5px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;
          color:var(--brand);margin:16px 0 6px}
.rg-listing .about h3:first-child{margin-top:0}
.rg-listing .about p{margin:0;font-size:14px;line-height:1.6;color:#33404f}
/* About is clamped to a readable first bite, then expands. Height is matched
   to its row-mate by PAIRING (see the bento order), not by guessing a px value:
   flex-filling made About define the row and starved the card beside it. */
.rg-listing .about{position:relative;max-height:252px;overflow:hidden;transition:max-height .3s}
.rg-listing .about.open,.rg-listing .about.nofade{max-height:none}
/* ⚠️ `:not(.nofade)` IS LOAD-BEARING, and its absence was a real fault.
   listing.js measures every clamped box after layout and adds `.nofade` where
   there is nothing behind the clamp — that has always worked. But
   `.about.nofade:after` and `.about:not(.open):after` carry the SAME specificity
   (`:not()` counts its argument), so the later rule simply won and the curtain
   drew anyway. A three-line description sat permanently behind a 64px fade that
   was taller than the text under it, and the "read it all" button revealed
   nothing. Reported by Timothy on the demo — About and Warranty both, because
   the warranty card reuses `.about`. */
.rg-listing .about.nofade:after{content:none}
.rg-listing .about:not(.open):not(.nofade):after{content:"";position:absolute;left:0;right:0;bottom:0;height:64px;
  background:linear-gradient(180deg,rgba(255,255,255,0),#fff)}
.rg-listing .abtog{background:none;border:0;color:var(--bd);font:inherit;font-size:12.5px;font-weight:700;
  cursor:pointer;padding:10px 0 0;align-self:flex-start}
.rg-listing .abtog:hover{text-decoration:underline}
/* One question under another. Two text columns only when the card ends up
   full width, which is the fallback rather than the intent. */
.rg-listing .faqgrid{columns:1}
.rg-listing .c3 .faqgrid{columns:2;column-gap:34px}
@media(max-width:760px){
.rg-listing .c3 .faqgrid{columns:1}
}
.rg-listing .qa{border-bottom:1px solid var(--line);padding:12px 0;break-inside:avoid}
.rg-listing .qa summary{font-size:14px;font-weight:600;color:var(--navy);cursor:pointer;list-style:none;
            display:flex;gap:10px;align-items:flex-start}
.rg-listing .qa summary::-webkit-details-marker{display:none}
.rg-listing .qa summary:before{content:"+";color:var(--brand);font-weight:800;flex:none;width:12px}
.rg-listing .qa[open] summary:before{content:"−"}
.rg-listing .qa p{margin:9px 0 0 22px;font-size:13.5px;color:#46515f;line-height:1.6}
/* ── gallery ──────────────────────────────────────────────────────────── */
/* Fixed 4:3 tiles. An earlier version let the grid stretch to fill its row,
   which sounded right and wasn't: with nothing bounding it the gallery grew to
   1165px on the claimed page and starved the card beside it. Predictable tiles
   plus deliberate pairing beats an elastic card. */
.rg-listing .gal{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}
/* MOBILE: a 3-up grid of thumbnails is unreadable on a phone. One photo at a
   time instead, scrolled sideways — at 86% the edge of the next one stays in
   frame, which is what tells you there ARE more without a caption saying so. */
@media(max-width:700px){
.rg-listing .gal{display:flex;grid-template-columns:none;overflow-x:auto;gap:8px;
       scroll-snap-type:x mandatory;scroll-behavior:smooth;scrollbar-width:thin}
.rg-listing .gal::-webkit-scrollbar{height:6px}
.rg-listing .gal::-webkit-scrollbar-thumb{background:#d3dbe6;border-radius:2px}
.rg-listing .gal button{flex:0 0 86%;scroll-snap-align:start}
.rg-listing .gal .more{flex:0 0 46%}
}
/* premium runs the gallery across the full row, where three columns would make
   each photo enormous — more columns, same tile proportions. */
.rg-listing .c3 .gal{grid-template-columns:repeat(6,1fr)}
.rg-listing .gal .hidden-photo[hidden]{display:none}

/* OPENED: one row that scrolls sideways, not a taller grid.
   Same tile width as the closed grid — three across at half width, six across
   the full row — so opening the gallery moves nothing else on the page. Below
   700px the mobile rules above already do this and must keep their 86% tile,
   hence min-width. */
@media(min-width:701px){
/* ⚠️ TWO ROWS, SCROLLING SIDEWAYS — NOT ONE.
   The closed gallery is two rows deep, and the card's height comes from the grid
   row it shares with its neighbour. Opening into a SINGLE row shrank the
   photographs to a third of the card and left two thirds of it empty. Same two
   rows, same tile size, same card height: only the direction of travel changes. */
.rg-listing .gal--reel{display:grid;grid-auto-flow:column;
       grid-template-columns:none;grid-template-rows:repeat(2,auto);
       grid-auto-columns:calc((100% - 26px)/3.35);
       overflow-x:auto;gap:8px;padding-bottom:8px;
       scroll-snap-type:x proximity;scroll-behavior:smooth;
       scrollbar-width:thin;overscroll-behavior-x:contain}
/* The full-width gallery closes as SIX across — five photographs and the
   toggle, one row exactly — so its reel is one row too. Two would double the
   card instead of leaving it where it was, which is the whole point. */
.rg-listing .c3 .gal--reel{grid-auto-columns:calc((100% - 52px)/6.35);
       grid-template-rows:repeat(1,auto)}
.rg-listing .gal--reel::-webkit-scrollbar{height:6px}
.rg-listing .gal--reel::-webkit-scrollbar-thumb{background:#d3dbe6;border-radius:3px}
.rg-listing .gal--reel::-webkit-scrollbar-track{background:#f1f4f9;border-radius:3px}
/* A shade narrower than a clean third, on purpose: at exactly a third the row
   ends flush and nothing says there are twenty-four more behind it. The phone
   shows 86% of a tile for the same reason. */
.rg-listing .gal--reel > button{scroll-snap-align:start}
.rg-listing .gal--reel > .more{aspect-ratio:auto}
}
.rg-listing .gal button{padding:0;border:0;background:#eef2f6;aspect-ratio:4/3;border-radius:8px;
            overflow:hidden;cursor:pointer;display:block;position:relative}
.rg-listing .gal img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .3s}
.rg-listing .gal button:hover img{transform:scale(1.05)}
.rg-listing .gal button:after{content:"";position:absolute;inset:0;box-shadow:inset 0 0 0 2px transparent;
                  border-radius:8px;transition:box-shadow .15s}
.rg-listing .gal button:hover:after{box-shadow:inset 0 0 0 2px var(--brand)}
.rg-listing .gal .more{display:flex;align-items:center;justify-content:center;background:#f3f6fb;
           border:1px dashed var(--line);color:var(--muted);font-size:13px;font-weight:600;
           aspect-ratio:4/3;border-radius:8px;cursor:pointer}
.rg-listing .gal .more:hover{border-color:var(--brand);color:var(--brand)}
/* ── lightbox — container class is .lightbox, NOT .lb: `.lb` is the
      ratings label class and an unscoped .lb{display:none} hid all of them ─────────────────────────────────────────────────────────── */
.rg-listing .lightbox{position:fixed;inset:0;z-index:900;background:rgba(10,21,53,.9);
    backdrop-filter:blur(6px);display:none;padding:24px}
.rg-listing .lightbox[open]{display:flex;align-items:center;justify-content:center}
.rg-listing .lb-box{background:var(--card);border-radius:var(--r);max-width:1000px;width:100%;
        max-height:92vh;overflow:auto;display:grid;grid-template-columns:1.35fr 1fr}
@media(max-width:820px){
.rg-listing .lb-box{grid-template-columns:1fr}
}
.rg-listing .lb-img{background:#0a1535;display:flex;align-items:center;justify-content:center}
.rg-listing .lb-img img{width:100%;height:100%;max-height:70vh;object-fit:contain;display:block}
.rg-listing .lb-info{padding:24px 26px;min-width:0}
.rg-listing .lb-info h3{margin:0 0 4px;font-size:11.5px;font-weight:800;letter-spacing:.07em;
            text-transform:uppercase;color:var(--brand)}
.rg-listing .lb-desc{font-size:15px;line-height:1.55;color:var(--ink);margin:0 0 18px}
.rg-listing .lb-info h4{margin:16px 0 7px;font-size:11px;font-weight:800;letter-spacing:.07em;
            text-transform:uppercase;color:var(--muted)}
.rg-listing .lb-attrs{display:grid;grid-template-columns:auto 1fr;gap:5px 12px;font-size:13px;margin:0}
.rg-listing .lb-attrs dt{color:var(--muted)}
.rg-listing .lb-attrs dd{margin:0;font-weight:600;color:var(--navy);text-transform:capitalize}
.rg-listing .lb-cost{font-family:ui-monospace,Menlo,Consolas,monospace;font-weight:700;color:var(--navy)}
.rg-listing .lb-nav{display:flex;gap:8px;align-items:center;margin-top:22px;padding-top:16px;
        border-top:1px solid var(--line)}
.rg-listing .lb-nav button{background:#fff;border:1px solid var(--line);border-radius:6px;padding:8px 14px;
               font:inherit;font-size:13px;font-weight:700;color:var(--bd);cursor:pointer}
.rg-listing .lb-nav button:hover{border-color:var(--brand)}
.rg-listing .lb-nav .ct{margin-left:auto;font-size:12px;color:var(--muted);font-variant-numeric:tabular-nums}
.rg-listing .lb-x{position:absolute;top:18px;right:20px;width:36px;height:36px;border-radius:8px;
      border:1px solid rgba(255,255,255,.25);background:rgba(255,255,255,.1);color:#fff;
      font-size:18px;cursor:pointer;z-index:2}
.rg-listing .lb-x:hover{background:rgba(255,255,255,.2)}
/* ── find online ──────────────────────────────────────────────────────── */
/* Ran as one tall column, which made "Find them online" the tallest card in
   its row and left its neighbour (warranty) stretched around a block of white.
   Wrapping rows fit the same links into a third of the height. */
.rg-listing .links{display:flex;flex-wrap:wrap;gap:7px}
/* overflow-wrap: an email address is one unbreakable token and was running
   straight through the card's right border. */
.rg-listing .links a{flex:1 1 132px;min-width:0;overflow-wrap:anywhere;display:flex;align-items:center;gap:9px;
         border:1px solid var(--line);border-radius:8px;
         padding:9px 12px;text-decoration:none;color:var(--ink);font-weight:600;font-size:13.5px}
.rg-listing .links a:hover{border-color:var(--brand);color:var(--bd)}
.rg-listing .links a .ar{margin-left:auto;padding-left:6px;color:var(--muted);font-weight:400}
/* Full width at the foot of the grid: the links size to their own text instead
   of stretching to a 132px basis, so seven of them read as one thin strip
   rather than a block of half-empty buttons. */
.rg-listing .c3 .links{gap:8px}
.rg-listing .c3 .links a{flex:0 1 auto}
.rg-listing .c3 .links a .ar{margin-left:4px}
/* contact rows, not a link grid — two per row cramped the phone number onto
   two lines in a one-column card */
.rg-listing .links--stack{flex-direction:column;flex-wrap:nowrap}
.rg-listing .links--stack a{flex:0 0 auto;white-space:normal}
.rg-listing .opennow{font-style:normal;font-size:10.5px;font-weight:800;letter-spacing:.04em;
         color:#15723e;background:#eafaf1;border:1px solid #b9e7cd;border-radius:999px;
         padding:2px 8px;margin-left:7px;text-transform:none;letter-spacing:0}
.rg-listing .hrs{display:flex;flex-direction:column;gap:0;margin:0}
.rg-listing .hrs .hrow{display:flex;align-items:baseline;justify-content:space-between;gap:12px;
           padding:7px 0;border-bottom:1px solid var(--line);font-size:13.5px}
.rg-listing .hrs .hrow:last-child{border-bottom:0}
.rg-listing .hrs dt{color:var(--muted);font-weight:600;margin:0}
.rg-listing .hrs dd{margin:0;font-weight:600;font-variant-numeric:tabular-nums;color:var(--navy)}
.rg-listing .hrs dd.shut{font-weight:400;color:var(--muted)}
/* ── sources — compact, technical, collapsed by default ───────────────── */
.rg-listing .srcstrip{margin-top:var(--gap);padding:16px 22px;background:#f8fafc;box-shadow:none}
.rg-listing .srcstrip h2{font-size:13px}
.rg-listing .srcstrip h2:before{width:12px;height:2px}
.rg-listing .srcstrip .ch{margin:0 0 10px;font-size:12px}
.rg-listing details.srcdet summary{font-size:12.5px;font-weight:700;color:var(--bd);cursor:pointer;
                       list-style:none;display:flex;gap:7px;align-items:center}
.rg-listing details.srcdet summary::-webkit-details-marker{display:none}
.rg-listing details.srcdet summary:before{content:"+";font-weight:800;width:10px}
.rg-listing details.srcdet[open] summary:before{content:"−"}
/* The photograph credit — one line per page, weighted like the count above it.
   ⚠️ `gal-credit`, not `credit`: grep before naming a class here, the theme's
   primitives are ordinary words and reusing one restyles half the site. */
.rg-listing .gal-credit{font-size:12px;color:var(--muted);margin:-2px 0 10px}
.rg-listing .ledger{width:100%;border-collapse:collapse;font-size:12px;margin-top:12px}
.rg-listing .ledger th{text-align:left;font-size:10px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;
           color:var(--muted);padding:0 8px 6px 0;border-bottom:1px solid var(--line)}
.rg-listing .ledger td{padding:7px 8px 7px 0;border-bottom:1px solid var(--line);vertical-align:top}
.rg-listing .ledger tr:last-child td{border-bottom:0}
.rg-listing .ledger .fact{font-weight:600;color:var(--navy)}
.rg-listing .ledger .no{color:var(--warn);font-weight:700}
.rg-listing .ledger .note{font-size:11px;color:var(--muted);font-style:italic}
/* ── claimed: the owner's own material ────────────────────────────────── */
/* poster + play, never an embedded player — no third-party JS on any profile */
.rg-listing .vid{position:relative;display:block;width:100%;aspect-ratio:16/9;border-radius:8px;
     overflow:hidden;background:#0b1622;border:1px solid var(--line);text-decoration:none}
.rg-listing .vid img{width:100%;height:100%;object-fit:cover;display:block;opacity:.82;transition:opacity .2s}
.rg-listing .vid:hover img{opacity:1}
.rg-listing .vid .play{position:absolute;inset:0;margin:auto;width:54px;height:54px;border-radius:50%;
           background:rgba(255,255,255,.92);box-shadow:0 6px 20px rgba(10,21,53,.35)}
.rg-listing .vid .play:after{content:"";position:absolute;top:50%;left:54%;transform:translate(-50%,-50%);
                 border-left:16px solid var(--navy);border-top:10px solid transparent;
                 border-bottom:10px solid transparent}
.rg-listing .vid .vlab{position:absolute;left:10px;bottom:10px;font-size:11.5px;font-weight:700;color:#fff;
           background:rgba(10,21,53,.72);border-radius:6px;padding:4px 9px}
/* several videos, scrolled sideways. Fixed-width children so the rail overflows
   instead of squeezing; scroll-snap so a swipe lands on a whole card. */
.rg-listing .vidrail{display:flex;gap:14px;overflow-x:auto;scroll-snap-type:x mandatory;
         padding-bottom:6px;margin:0 -2px;scrollbar-width:thin}
.rg-listing .vidrail::-webkit-scrollbar{height:7px}
.rg-listing .vidrail::-webkit-scrollbar-thumb{background:#d3dbe6;border-radius:4px}
.rg-listing .vcard{flex:0 0 260px;scroll-snap-align:start;margin:0;display:flex;flex-direction:column;gap:9px}
.rg-listing .vcard figcaption{display:flex;flex-direction:column;gap:5px;flex:1}
.rg-listing .vcard b{font-size:14px;color:var(--navy);line-height:1.35}
.rg-listing .vcard .vex{font-size:12.5px;color:var(--muted);line-height:1.5}
.rg-listing .vcard .vbtn{margin-top:auto;align-self:start;font-size:12px;font-weight:700;
             color:var(--bd);text-decoration:none;border:1px solid var(--line);
             border-radius:6px;padding:6px 11px;background:#fbfcfe}
.rg-listing .vcard .vbtn:hover{border-color:var(--brand);color:var(--brand)}
.rg-listing .ownertext{margin:0;font-size:14px;line-height:1.62;color:#33404f}
/* the flag reads "terms on file", not "we approve of them" — see build.py */
.rg-listing .okflag{display:inline-flex;align-items:center;gap:5px;vertical-align:middle;
        font-size:10.5px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;
        color:#15723e;background:#eafaf1;border:1px solid #b9e7cd;
        border-radius:999px;padding:3px 9px 3px 7px;margin-left:8px}
.rg-listing .okflag .tick{width:12px;height:12px;border-radius:50%;background:var(--good);flex:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.6 6.35 4.9 8.65 9.4 3.65' fill='none' stroke='%23fff' stroke-width='2.1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:center;background-size:74% 74%}
/* margin-top:auto takes the slack BEFORE .src does, so the spare height in the
   row lands between the terms text and the button instead of below both. */
/* A warranty is a list of terms, so it reads as one. The block folds at the
   same height as About and uses the same toggle, which means an owner can paste
   their entire document in here without the card running away. */
.rg-listing .warrbody .ownertext{margin-bottom:11px}
.rg-listing .wlist{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:9px}
.rg-listing .wlist li{position:relative;padding-left:19px;font-size:14px;line-height:1.55;color:#33404f}
.rg-listing .wlist li:before{content:"";position:absolute;left:2px;top:8px;width:6px;height:6px;
                 border-radius:50%;background:var(--brand);opacity:.55}
.rg-listing .wlist li b{color:var(--navy);font-weight:700}
/* the button was floating in the middle of the card's spare height; it sits on
   the floor now, with the show-more control on the same line */
.rg-listing .warrbody+.abtog{padding-bottom:12px}
.rg-listing .termsbtn{margin-top:auto;align-self:flex-start}
.rg-listing .insgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:10px}
.rg-listing .ins{border:1px solid var(--line);border-radius:8px;padding:13px 14px;margin-bottom:10px;
     background:#fbfcfe}
.rg-listing .ins{margin-bottom:0}
.rg-listing .ins .hd{display:flex;align-items:center;gap:8px;margin-bottom:8px;flex-wrap:wrap}
.rg-listing .ins .nm{font-size:14px;font-weight:700;color:var(--navy)}
/* ── premium ──────────────────────────────────────────────────────────── */
.rg-listing .ppill{font-size:10.5px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
       color:#7a5410;background:linear-gradient(180deg,#f7e3ac,#e9cf86);
       border:1px solid #dcbd6b;padding:3px 9px;border-radius:999px;
       box-shadow:0 1px 0 rgba(255,255,255,.6) inset}
/* ── PREMIUM HEADER, TWO THEMES ─────────────────────────────────────────
   Which one a listing gets is decided by its own logo, by the same
   luminance measurement resolve_logo() already runs for the small tile:

     logo drawn WHITE  → hero--dark  · navy header, logo sits straight on it
     logo drawn DARK   → hero--light · white header, logo sits straight on it
     no logo at all    → hero--dark  · nothing to clash with

   The logo is never recoloured, masked, plated or scrimmed. The BACKGROUND
   moves to suit the mark, which is the only way to keep a black logo legible
   without touching it. Only the header changes either way — the cards below
   stay white, so the page reads as one design with a heavier top.
   Every colour is re-stated on the dark theme: inheriting muted greys onto
   navy is what makes dark headers unreadable. */
.rg-listing .hero--dark{background:linear-gradient(135deg,var(--navy) 0%,var(--navy2) 62%,#1d3272 100%);
            border-color:var(--navy);box-shadow:0 10px 30px rgba(10,21,53,.28)}
.rg-listing .hero--dark h1{color:#fff}
.rg-listing .hero--dark .addr{color:#9fb0d0}
.rg-listing .hero--dark .addr a{color:#9fb0d0}
.rg-listing .hero--dark .addr a:hover{color:#fff}
.rg-listing .hero--dark .quietnote{color:#f0b849}
.rg-listing .hero--dark .hero-links a{color:#a8c4f5}
.rg-listing .hero--dark .hero-links a:hover{color:#fff}
.rg-listing .hero--dark .hf-s{background:rgba(255,255,255,.07);border-color:rgba(255,255,255,.16)}
.rg-listing .hero--dark .hf-s b{color:#fff}
.rg-listing .hero--dark .hf-s--rate b{color:var(--star)}
.rg-listing .hero--dark .hf-s span{color:#9fb0d0}
/* ── the logo, BOTH themes ──────────────────────────────────────────────
   No plate on either. A tile floating on the header is the thing that looked
   bolted on, and a square tile wasted most of its area on a mark that is
   624x167. Free width, fixed height, so a wide logo is actually legible.
   The logo keeps ITS OWN COLOURS. No fill, no tint, no plate, no scrim — it is
   the listing's own mark and it renders as drawn. Legibility is handled by
   choosing the header behind it, above, not by touching the artwork. */
.rg-listing .hero--prem .ini--img{width:auto;height:auto;max-width:250px;border:0;border-radius:0;
                      background:transparent;box-shadow:none;overflow:visible}
.rg-listing .hero--prem .ini--img img{width:auto;height:58px;max-width:250px;padding:0;
                          background:transparent;object-fit:contain}
.rg-listing .hero--prem .hero-id{gap:18px}
/* the quiet-since note broke onto its own line once the logo took its width */
.rg-listing .hero--prem .addr{white-space:nowrap}
/* no logo on file — initials keep a tile, in whichever header they landed in */
.rg-listing .hero--dark .ini:not(.ini--img){width:80px;height:80px;border-radius:12px;font-size:26px;
                                background:rgba(255,255,255,.12);
                                border:1px solid rgba(255,255,255,.22)}
.rg-listing .hero--light .ini:not(.ini--img){width:80px;height:80px;border-radius:12px;font-size:26px}
.rg-listing .hero--dark .btn.primary{background:#fff;color:var(--navy);border-color:#fff}
.rg-listing .hero--dark .btn.primary:hover{background:#e8eefb}
.rg-listing .hero--dark .vmark{background:rgba(255,255,255,.14);color:#7fe3a8}
.rg-listing .btn.call{background:#fff;color:var(--good);border:1px solid #b9e7cd;
          box-shadow:0 1px 2px rgba(26,156,84,.18)}
.rg-listing .btn.call:hover{background:#eafaf1}
.rg-listing .offerband{display:flex;align-items:center;gap:16px;flex-wrap:wrap;margin-top:var(--gap);
           background:linear-gradient(135deg,#b07d1e,#8f6417);color:#fff;
           border-radius:var(--r);padding:16px 22px;box-shadow:var(--shadow)}
/* The mark now holds either an inline icon or the owner's own file, so it needs
   a box rather than a font size. Capped, because an uploaded logo-sized PNG in a
   one-line band is the way this stops being a band. */
.rg-listing .offerband .oi{flex:none;opacity:.95;display:inline-flex;align-items:center;
  justify-content:center;width:26px;height:26px}
.rg-listing .offerband .oi img{max-width:100%;max-height:100%;object-fit:contain;display:block}
.rg-listing .offerband .oi svg{display:block}
.rg-listing .offerband .ot{flex:1;min-width:220px}
.rg-listing .offerband .ot b{display:block;font-size:11px;font-weight:800;letter-spacing:.09em;
                 text-transform:uppercase;opacity:.82;margin-bottom:3px}
.rg-listing .offerband .ot span{font-size:15px;font-weight:600;line-height:1.4}
.rg-listing .offerband .btn{background:#fff;color:#8f6417;flex:none}
.rg-listing .bagrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:16px}
.rg-listing .ba{margin:0}
/* ── before/after wipe ──────────────────────────────────────────────────
   The AFTER photo is the base layer at full size; the BEFORE photo sits on
   top and is clipped by --x. Clipping (not resizing a container) is what
   keeps both frames aligned — a width-constrained wrapper would squash the
   image it holds and the two halves would stop lining up.
   The control is a real <input type=range> stretched over the frame and made
   invisible: drag, touch and arrow keys all come from the browser, so there
   is no pointer maths to get wrong and it is keyboard-reachable. */
.rg-listing .bacmp{position:relative;border-radius:8px;overflow:hidden;background:#eef2f6;--x:50%}
.rg-listing .bacmp .bimg{width:100%;aspect-ratio:4/3;object-fit:cover;display:block}
.rg-listing .bacmp .bfore{position:absolute;inset:0;height:100%;
              clip-path:inset(0 calc(100% - var(--x)) 0 0)}
.rg-listing .bacmp .balab{position:absolute;bottom:7px;font-style:normal;font-size:10px;font-weight:800;
          letter-spacing:.05em;text-transform:uppercase;color:#fff;background:rgba(10,21,53,.72);
          border-radius:4px;padding:2px 7px}
.rg-listing .bacmp .balab.l{left:7px}
.rg-listing .bacmp .balab.r{right:7px}
.rg-listing .bahandle{position:absolute;top:0;bottom:0;left:var(--x);width:2px;margin-left:-1px;
          background:rgba(255,255,255,.92);box-shadow:0 0 0 1px rgba(10,21,53,.22);pointer-events:none}
.rg-listing .bahandle:after{content:"";position:absolute;top:50%;left:50%;width:32px;height:32px;
                transform:translate(-50%,-50%);border-radius:50%;background:#fff;
                box-shadow:0 2px 10px rgba(10,21,53,.35)}
.rg-listing .bahandle:before{content:"";position:absolute;top:50%;left:50%;z-index:1;width:11px;height:7px;
                 transform:translate(-50%,-50%);
                 background:linear-gradient(90deg,var(--navy) 0 3px,transparent 3px 8px,var(--navy) 8px 11px);
                 clip-path:polygon(0 50%,3px 0,3px 100%,100% 50%,8px 0,8px 100%)}
.rg-listing .barange{position:absolute;inset:0;width:100%;height:100%;margin:0;padding:0;opacity:0;
         cursor:ew-resize;-webkit-appearance:none;appearance:none;background:transparent}
.rg-listing .barange::-webkit-slider-thumb{-webkit-appearance:none;width:44px;height:100%;cursor:ew-resize}
.rg-listing .barange::-moz-range-thumb{width:44px;height:100%;border:0;background:transparent;cursor:ew-resize}
.rg-listing .barange:focus-visible{opacity:1;outline:2px solid var(--brand);outline-offset:2px}
.rg-listing .ba figcaption{font-size:12.5px;color:var(--muted);margin-top:7px;line-height:1.45}
.rg-listing .ba figcaption b{color:var(--navy)}
.rg-listing .ba figcaption span{display:block;margin-top:2px}
.rg-listing .person{display:flex;gap:12px;align-items:center}
.rg-listing .person img{width:56px;height:56px;border-radius:50%;object-fit:cover;flex:none;
            border:2px solid #fff;box-shadow:0 1px 4px rgba(16,32,64,.2)}
.rg-listing .person b{display:block;font-size:15px;color:var(--navy)}
.rg-listing .person span{font-size:12px;color:var(--muted)}
.rg-listing .pnote{font-size:13px;color:#46515f;line-height:1.55;margin:12px 0 0}
/* ── quote form · free tier is the panel alone, premium adds the spec column ── */
.rg-listing .qgrid{display:grid;grid-template-columns:1fr;gap:18px;align-items:stretch}
.rg-listing .qgrid--calc{grid-template-columns:1.15fr 1fr}
@media(max-width:820px){
.rg-listing .qgrid--calc{grid-template-columns:1fr}
}
.rg-listing .qspec{display:flex;flex-direction:column;gap:7px}
.rg-listing .qspec .clab{margin-top:5px}
.rg-listing .qspec .clab:first-child{margin-top:0}
/* size and finish are options you click, not a dropdown you hunt through —
   the whole point is seeing what is on offer without opening anything */
.rg-listing .copts{display:grid;grid-template-columns:repeat(auto-fit,minmax(112px,1fr));gap:6px}
.rg-listing .copt{position:relative;cursor:pointer}
.rg-listing .copt input{position:absolute;opacity:0;width:0;height:0}
.rg-listing .copt span{display:flex;flex-direction:column;gap:1px;height:100%;background:#f6f9fd;
           border:1px solid var(--line);border-radius:8px;padding:9px 11px}
.rg-listing .copt b{font-size:13px;font-weight:700;color:var(--navy);line-height:1.25}
.rg-listing .copt i{font-style:normal;font-size:11px;color:var(--muted);line-height:1.3}
.rg-listing .copt:hover span{border-color:var(--brand)}
.rg-listing .copt input:checked+span{background:#e8f0fe;border-color:var(--brand);
                         box-shadow:0 0 0 1px var(--brand) inset}
.rg-listing .copt input:focus-visible+span{outline:2px solid var(--brand);outline-offset:2px}
.rg-listing .clab{display:block;font-size:11.5px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
      color:var(--muted);margin-bottom:6px}
.rg-listing .cfeats{display:flex;flex-wrap:wrap;gap:6px}
.rg-listing .cfeats label{display:inline-flex;gap:6px;align-items:center;background:#f3f6fb;
              border:1px solid var(--line);padding:6px 10px;border-radius:6px;font-size:13px;
              color:var(--navy);cursor:pointer}
.rg-listing .cfeats label:hover{border-color:var(--brand)}
/* The panel is the conversion unit: on premium it carries the number AND the
   form, so the answer and the way to act on it are one object rather than a
   figure in one place and a button somewhere else. */
.rg-listing .qpanel{border:1px solid var(--line);border-radius:8px;padding:16px;background:#f6f9fd;
        display:flex;flex-direction:column;gap:12px}
.rg-listing .qgrid--calc .qpanel{background:linear-gradient(160deg,var(--navy),var(--navy2));border:0;
                     box-shadow:0 4px 16px rgba(10,21,53,.18)}
.rg-listing .cout{text-align:center;display:flex;flex-direction:column;gap:3px;
      padding-bottom:13px;border-bottom:1px solid rgba(255,255,255,.14)}
.rg-listing .cout .cl{display:block;font-size:10.5px;font-weight:800;letter-spacing:.07em;
          text-transform:uppercase;color:#9fb0d0}
.rg-listing .cout b{font-size:1.9rem;font-weight:800;color:#fff;line-height:1.15;
        font-variant-numeric:tabular-nums}
.rg-listing .cout .cfoot{font-size:11px;color:#9fb0d0;line-height:1.4}
.rg-listing .qform{display:flex;flex-direction:column;gap:8px}
/* ⚠️ `:not([type=checkbox])` IS LOAD-BEARING. Without it `width:100%` catches the
   consent tick as well, and a checkbox stretched to the full width of the card
   pushes its own label into a 73px gutter — the words came out one per line,
   spilling past the edge of the card. The select is listed here too: left out,
   it kept the browser's own grey chrome and read as a spare tooth in a row of
   matching boxes. */
.rg-listing .qform input:not([type="checkbox"]),
.rg-listing .qform select,
.rg-listing .qform textarea{width:100%;font:inherit;font-size:13.5px;color:var(--ink);
      background:#fff;border:1px solid var(--line);border-radius:6px;padding:10px 12px;
      box-sizing:border-box;resize:vertical}
.rg-listing .qform select{appearance:none;-webkit-appearance:none;cursor:pointer;
      padding-right:32px;color:var(--ink);
      background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5 6 6.5l5-5' stroke='%238a98ad' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
      background-repeat:no-repeat;background-position:right 12px center}
/* ⚠️ NOT `:valid` — the field is optional, so an empty select is valid too and
   the placeholder rendered in full ink while every other field's placeholder sat
   in grey. `:has()` asks the only question that matters: is the empty option the
   one currently chosen? */
.rg-listing .qform select:has(option[value=""]:checked){color:#8a98ad}
.rg-listing .qform select option{color:var(--ink)}
.rg-listing .qform input::placeholder,.rg-listing .qform textarea::placeholder{color:#8a98ad}
.rg-listing .qform input:focus,.rg-listing .qform select:focus,
.rg-listing .qform textarea:focus{outline:2px solid var(--brand);outline-offset:1px;
                                         border-color:var(--brand)}
.rg-listing .qform .btn{margin-top:2px}
.rg-listing .qnote{margin:0;font-size:11px;color:var(--muted);line-height:1.45}
/* on the navy panel the fields need their own contrast, not the light card's */
.rg-listing .qgrid--calc .qform input,.rg-listing .qgrid--calc .qform textarea{background:rgba(255,255,255,.09);
      border-color:rgba(255,255,255,.2);color:#fff}
.rg-listing .qgrid--calc .qform input::placeholder,.rg-listing .qgrid--calc .qform textarea::placeholder{color:#93a6c6}
.rg-listing .qgrid--calc .qform input:focus,.rg-listing .qgrid--calc .qform textarea:focus{background:rgba(255,255,255,.16);
      border-color:#fff;outline-color:#fff}
.rg-listing .qgrid--calc .qform .btn.primary{background:#fff;color:var(--navy);box-shadow:none}
.rg-listing .qgrid--calc .qform .btn.primary:hover{background:#e8eefb}
.rg-listing .qgrid--calc .qnote{color:#93a6c6}
.rg-listing .cfeats label{transition:border-color .15s,background .15s}
.rg-listing .cfeats label:has(input:checked){background:#e8f0fe;border-color:var(--brand);color:var(--bd)}
/* ── footer ───────────────────────────────────────────────────────────── */
/* Where a reader goes next: three businesses near this one, then the two place
   pages. It replaced a "Get a quote from {business}" band — see single-listing.php
   for why that could not stay. Deliberately quiet: it is a way onward, not an
   offer, so no gradient, no primary button, nothing that reads as a pitch. */
.rg-listing .onward{margin-top:calc(var(--gap) * 1.5)}
.rg-listing .onward h2{margin:0 0 4px;font-size:16px;color:var(--navy);display:flex;align-items:center;gap:8px}
.rg-listing .onward h2:before{content:"";width:18px;height:3px;background:var(--brand);border-radius:2px;flex:none}
.rg-listing .onward-lede{margin:0 0 18px;font-size:13.5px;color:var(--muted);max-width:60ch}
/* `rg-grid` brings the columns and the breakpoints; this only sets the gap so it
   matches the bento above it rather than the front page it was borrowed from. */
.rg-listing .onward-row{gap:var(--gap)}
.rg-listing .onward-places{display:grid;grid-template-columns:1fr;gap:12px;margin-top:var(--gap)}
@media(min-width:700px){.rg-listing .onward-places{grid-template-columns:repeat(2,minmax(0,1fr))}}
.rg-listing .onward-place{display:flex;flex-direction:column;gap:2px;padding:16px 18px;text-decoration:none;
      background:var(--card);border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow);
      transition:box-shadow var(--t-ui,.18s) var(--ease,ease),border-color .15s}
.rg-listing .onward-place:hover{border-color:var(--brand);box-shadow:0 1px 2px rgba(16,32,64,.06),0 12px 32px rgba(16,32,64,.10)}
.rg-listing .onward-place b{font-size:14.5px;color:var(--navy);font-weight:650}
.rg-listing .onward-place span{font-size:12.5px;color:var(--muted)}
/* ⚠️ `.band` below is no longer drawn by any template on this page — the quote
   band it styled was removed on 4 Sep. Kept because the class is generic and a
   concept may still use it; delete it with the next stylesheet sweep. */
.rg-listing .band{border-radius:var(--r);padding:24px 28px;box-shadow:var(--shadow);margin-top:var(--gap);
      display:flex;gap:24px;align-items:center;flex-wrap:wrap;
      background:linear-gradient(135deg,var(--navy),var(--navy2));color:#fff}
.rg-listing .band h2{margin:0;font-size:16px;color:#fff;display:flex;align-items:center;gap:8px}
.rg-listing .band h2:before{content:"";width:18px;height:3px;background:#fff;opacity:.6;border-radius:2px}
.rg-listing .band p{margin:6px 0 0;font-size:13.5px;color:#c9d6e8;max-width:52ch}
.rg-listing .band .act{margin-left:auto;display:flex;gap:10px;flex-wrap:wrap}
.rg-listing .band .btn.primary{background:#fff;color:var(--navy)}
.rg-listing .band .btn.ghost{background:rgba(255,255,255,.1);color:#fff;border-color:rgba(255,255,255,.3)}
.rg-listing .claimline{margin-top:var(--gap);text-align:center;font-size:13px;color:var(--muted)}
.rg-listing .claimline a{font-weight:600}
@media(max-width:900px){
.rg-listing .bento{grid-template-columns:repeat(2,1fr)}
.rg-listing .c2,.rg-listing .c3{grid-column:span 2}
.rg-listing .topics{grid-template-columns:1fr}
}
@media(max-width:620px){
.rg-listing .bento{grid-template-columns:1fr}
.rg-listing .c1,.rg-listing .c2,.rg-listing .c3{grid-column:span 1}
.rg-listing .hero{padding:20px}
.rg-listing .hero-cta{width:100%}
.rg-listing .gal{grid-template-columns:repeat(2,1fr)}
.rg-listing .ledger thead{display:none}
.rg-listing .ledger td{display:block;border:0;padding:2px 0}
.rg-listing .ledger tr{display:block;padding:10px 0;border-bottom:1px solid var(--line)}
}
/* ══ RESTRAINT ══════════════════════════════════════════════════════════
   Three radii (2 / 4 / 8 / 12), squared-off badges, no outline buttons, no
   box inside a box, one shadow. Zero motion, zero JS. ═════════════════ */
.rg-listing{ --shadow:0 1px 2px rgba(16,32,64,.05),0 2px 8px rgba(16,32,64,.05); }
.rg-listing .ppill,.rg-listing .okflag,.rg-listing .opennow,.rg-listing .chip,.rg-listing .term,.rg-listing .badge,.rg-listing .maplegend .sw.home,.rg-listing .maplegend .sw{border-radius:4px}
.rg-listing .vmark,.rg-listing .lvhead .lvdot,.rg-listing .chip.ok .tick,.rg-listing .okflag .tick{border-radius:50%}
/* outline buttons read as disabled next to a filled one — fill them quietly */
.rg-listing .btn.ghost{background:#eef2f8;border-color:transparent;color:var(--bd)}
.rg-listing .btn.ghost:hover{background:#e2e9f5}
/* a bordered box inside a bordered card is the card-in-card tell: keep the
   tint, drop the frame, let spacing do the separating */
.rg-listing .agg,.rg-listing .qpanel:not(.qgrid--calc .qpanel),.rg-listing .ins,.rg-listing .copt span,.rg-listing .rev,.rg-listing .lic,.rg-listing .hf-s{
  border-color:transparent}
.rg-listing .agg,.rg-listing .ins,.rg-listing .rev,.rg-listing .lic{background:#f5f8fc}
.rg-listing .hf-s{background:#f2f6fb}
.rg-listing .card{box-shadow:0 1px 2px rgba(16,32,64,.05)}
.rg-listing .card h2{letter-spacing:-.015em}
/* ══ MOTION ═════════════════════════════════════════════════════════════
   One easing token, one duration scale. Bars RUN, the score reacts, the live
   pills answer a hover. Every motion is transform or opacity — nothing here
   animates a width, so nothing here causes a layout pass. ══════════════ */
.rg-listing{
  --ease:cubic-bezier(.23,1,.32,1);      /* ease-OUT — never ease-in on entry */
  --t-press:120ms; --t-quick:160ms; --t-ui:220ms; --t-panel:280ms;
  --t-bar:640ms;                          /* a bar filling is a chart, not a UI
                                             tick — it is allowed to take its
                                             time. Nothing waits on it. */
}
.rg-listing .btn,.rg-listing .chip,.rg-listing .links a,.rg-listing .copt span,.rg-listing .railbtn,.rg-listing .gal button,.rg-listing .ph-more,.rg-listing .abtog,.rg-listing .rbar,.rg-listing .hf-s{
  transition:transform var(--t-quick) var(--ease),
             opacity var(--t-quick) var(--ease),
             background var(--t-quick) var(--ease),
             border-color var(--t-quick) var(--ease),
             box-shadow var(--t-quick) var(--ease);
}
.rg-listing .btn:active,.rg-listing .railbtn:active:not(:disabled),.rg-listing .chip:active{transform:scale(.985)}
/* ── the bars RUN to the right ──────────────────────────────────────────
   scaleX from a left origin, not width — same picture, no reflow. The inline
   width the builder writes stays the truth; the transform only reveals it. */
.rg-listing .topics .bar i,.rg-listing .rbar .fill{transform-origin:left center}
.rg-listing [data-bars] .topics .bar i,.rg-listing [data-bars] .rbar .fill{transform:scaleX(0)}
.rg-listing [data-bars].in .topics .bar i,.rg-listing [data-bars].in .rbar .fill{
  transform:scaleX(1);transition:transform var(--t-bar) var(--ease)}
/* each bar leaves a beat after the one above — 60ms, inside the 30-80 rule */
.rg-listing [data-bars].in .topic:nth-child(1) .bar i{transition-delay:0ms}
.rg-listing [data-bars].in .topic:nth-child(2) .bar i{transition-delay:60ms}
.rg-listing [data-bars].in .topic:nth-child(3) .bar i{transition-delay:120ms}
.rg-listing [data-bars].in .topic:nth-child(4) .bar i{transition-delay:180ms}
.rg-listing [data-bars].in .rbar:nth-of-type(1) .fill{transition-delay:0ms}
.rg-listing [data-bars].in .rbar:nth-of-type(2) .fill{transition-delay:80ms}
/* ── the score reacts ───────────────────────────────────────────────────── */
@media (hover:hover) and (pointer:fine){
.rg-listing .card:hover{box-shadow:0 1px 2px rgba(16,32,64,.06),0 12px 32px rgba(16,32,64,.10)}
.rg-listing .card{transition:box-shadow var(--t-ui) var(--ease)}
.rg-listing .hf-s--rate:hover{transform:translateY(-2px)}
.rg-listing .hf-s--rate b{display:block;transition:transform var(--t-ui) var(--ease)}
.rg-listing .hf-s--rate:hover b{transform:scale(1.09)}
.rg-listing .links a:hover,.rg-listing .chip:hover{transform:translateY(-1px)}
.rg-listing .gal button:hover img{transform:scale(1.04)}
.rg-listing .rbar:hover{transform:translateX(2px)}
/* the green live markers bloom ONCE on hover — a ring that grows and fades.
     Not a loop: a dot that pulses forever is a spinner pretending to be a
     status, and it never stops asking for attention. */
.rg-listing .lvhead.on .lvdot,.rg-listing .okflag .tick,.rg-listing .chip.ok .tick,.rg-listing .vitem:before{
    transition:box-shadow var(--t-panel) var(--ease),transform var(--t-panel) var(--ease)}
.rg-listing .livebox:hover .lvdot{transform:scale(1.15);box-shadow:0 0 0 7px rgba(26,156,84,.14)}
.rg-listing .okflag:hover .tick,.rg-listing .chip.ok:hover .tick,.rg-listing .vitem:hover:before{
    transform:scale(1.12);box-shadow:0 0 0 5px rgba(26,156,84,.16)}
.rg-listing .vitem{transition:border-color var(--t-ui) var(--ease),background var(--t-ui) var(--ease)}
.rg-listing .vitem:hover{border-color:var(--good);background:#eefaf3}
}
.rg-listing .gal button img{transition:transform var(--t-panel) var(--ease)}
/* entry: 8px of travel, no scale(0), staggered down the page */
.rg-listing .card[data-rise]{opacity:0;transform:translateY(8px)}
.rg-listing .card[data-rise].in{opacity:1;transform:none;
  transition:opacity var(--t-ui) var(--ease),transform var(--t-ui) var(--ease)}
.rg-listing .lightbox{transition:opacity var(--t-ui) var(--ease)}
.rg-listing .lightbox .lb-box{transform:scale(.97);transition:transform var(--t-panel) var(--ease)}
.rg-listing .lightbox[open] .lb-box{transform:none}
@media (prefers-reduced-motion:reduce){
.rg-listing,.rg-listing *,.rg-listing *:before,.rg-listing *:after{transition-duration:1ms !important;animation-duration:1ms !important}
.rg-listing .card[data-rise]{opacity:1;transform:none}
.rg-listing [data-bars] .topics .bar i,.rg-listing [data-bars] .rbar .fill{transform:none}
}

/* The monogram.
   It is the tile's content when there is no logo, and it is the standby when
   there is: 518 of the logos on file are URLs on somebody else's server, and a
   file that fails left an empty white square as the most prominent thing on the
   page. Hidden while a logo is present — a transparent PNG would otherwise sit
   over the initials and read as a printing fault — and revealed by the script
   the moment an image reports that it failed. */
.rg-listing .ini{position:relative}
.rg-listing .ini .ini-mono{position:absolute;inset:0;display:flex;align-items:center;
  justify-content:center;font-weight:800;font-size:34px;color:#fff}
.rg-listing .ini--img .ini-mono{display:none;color:var(--navy)}
.rg-listing .ini--img.is-logoless .ini-mono{display:flex}
.rg-listing .ini img{position:relative;z-index:1}

/* A PALE LOGO GETS A DARK TILE, and this is not a nicety.
   A quarter of the logos on file are white or near-white. On the white tile
   above they render as an empty square in the most prominent place on the page,
   and nothing errors — the file loads, it is simply invisible. The data says
   which ones: every hosted logo is measured for mean luminance over the pixels
   that are actually painted, and anything at 200 or above lands here. */
.rg-listing .ini--dark{background:var(--navy);border-color:var(--navy)}
.rg-listing .ini--dark img{background:var(--navy)}
.rg-listing .ini--dark .ini-mono{color:#fff}

/* Quote form: the consent line and the result message. Deliberately plain — the
   consent must read as part of the form, not as a cookie banner. */
.rg-listing .qconsent{display:flex;gap:9px;align-items:center;margin:2px 0 4px;
          font-size:12.5px;line-height:1.5;color:var(--rg-muted,#62707f);cursor:pointer}
.rg-listing .qconsent input{flex:none;width:15px;height:15px;margin:0;accent-color:var(--brand)}
.rg-listing .qconsent span{flex:1 1 auto;min-width:0}
.rg-listing .qconsent a{color:inherit;text-decoration:underline}
.qresult{margin:10px 0 0;padding:9px 12px;border-radius:8px;font-size:13px;font-weight:600}
.qresult.ok{background:#eafaf1;color:#15723e}
.qresult.bad{background:#fef2f2;color:#b4232a}
/* City and budget share a row: two more boxes on a quote form costs completion,
   so they take one line between them rather than two. */
.qpair{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.qpair > *{min-width:0}
@media (max-width:420px){.qpair{grid-template-columns:1fr}}

/* The outbound check on a license card. Quiet — it is an offer to verify, not
   a call to action, and it must not compete with the number above it. */
.rg-listing .licverify {
  display: inline-block; margin-top: 10px;
  font-size: 12px; font-weight: 600; color: var(--brand);
  text-decoration: none; border-bottom: 1px solid transparent;
}
.rg-listing .licverify:hover { border-bottom-color: currentColor; }

/* ═ Where this business is based, above the quote form. ⚠️ IT LIVES HERE, not
   in cabinet.css — that stylesheet only loads on the owner's own screen, so a
   rule for a public profile put there is a rule that never arrives. ═ */
.qbase{font-size:13px;color:var(--muted,#7e939b);margin:0 0 14px;line-height:1.5}
.qbase .qelse{display:inline-block;margin-left:4px;font-weight:600;color:var(--brand,#1d4ed8);
   text-decoration:none;border-bottom:1px solid currentColor}
.qbase .qelse:hover{opacity:.8}

/* ═ SPOTLIGHT ═════════════════════════════════════════════════════════════
   Articles written about this business. A row of picture-led cards inside the
   full-width card, dropping to a single column on a phone.

   ⚠️ SCOPED UNDER `.rg-listing`, like everything else in this file. `.spot` is
   an ordinary word and a bare `.spot` would be a name the rest of the site can
   collide with — see the theme's own history of exactly that.
   ═══════════════════════════════════════════════════════════════════════ */
.rg-listing .spotlist{
   display:grid;gap:16px;
   grid-template-columns:repeat(auto-fit,minmax(min(100%,240px),1fr))}
/* ⚠️ ONE ARTICLE LIES DOWN. `auto-fit` stretches a lone card across the whole
   three-column row, which blows a 16:9 featured image up to 1,100px wide and
   makes one blog post the largest thing on the profile. The common case IS one
   article, so it gets its own shape: picture beside the words. */
@media (min-width:700px){
   .rg-listing .spot:only-child{flex-direction:row;align-items:stretch}
   .rg-listing .spot:only-child .spot__img{flex:0 0 320px;aspect-ratio:4/3}
   .rg-listing .spot:only-child .spot__body{justify-content:center;padding:20px 22px}
   .rg-listing .spot:only-child .spot__title{font-size:17px}
}
.rg-listing .spot{
   display:flex;flex-direction:column;min-width:0;
   border:1px solid var(--line);border-radius:12px;overflow:hidden;
   background:var(--card,#fff);color:inherit;text-decoration:none;
   transition:box-shadow .18s ease,transform .18s ease}
.rg-listing .spot:hover{box-shadow:var(--shadow);transform:translateY(-2px)}
.rg-listing .spot__img{display:block;aspect-ratio:16/9;background:var(--bg,#eef2f6)}
.rg-listing .spot__img img{width:100%;height:100%;object-fit:cover;display:block}
.rg-listing .spot__body{display:flex;flex-direction:column;gap:6px;padding:14px}
.rg-listing .spot__title{font-weight:700;font-size:15px;line-height:1.3;color:var(--navy,#0a1535)}
.rg-listing .spot:hover .spot__title{color:var(--brand,#1d4ed8)}
.rg-listing .spot__ex{font-size:13.5px;color:var(--muted);line-height:1.5}
.rg-listing .spot__date{font-size:12px;color:var(--muted);margin-top:auto}
