/* components part 11 of 16 — split for the host's 16KB FTP ceiling.
   Generated by split_for_ftp.py; edit the prototype, not this. */

.wrap-wide:has(#tr-architect:checked) .tst-list .tst:not([data-r="architect"]){display:none}
.wrap-wide:has(#tr-developer:checked) .tst-list .tst:not([data-r="developer"]){display:none}
.wrap-wide:has(#tr-designer:checked) .tst-list .tst:not([data-r="designer"]){display:none}
.wrap-wide:has(#tr-agency:checked) .tst-list .tst:not([data-r="agency"]){display:none}
.wrap-wide:has(#tr-manufacturer:checked) .tst-list .tst:not([data-r="manufacturer"]){display:none}
.wrap-wide:has(#tf-all:focus-visible) .tst-filters label[for=tf-all],.wrap-wide:has(#tf-no:focus-visible) .tst-filters label[for=tf-no],.wrap-wide:has(#tf-uk:focus-visible) .tst-filters label[for=tf-uk],.wrap-wide:has(#tf-fi:focus-visible) .tst-filters label[for=tf-fi],.wrap-wide:has(#tf-ee:focus-visible) .tst-filters label[for=tf-ee],.wrap-wide:has(#tf-at:focus-visible) .tst-filters label[for=tf-at],.wrap-wide:has(#tf-se:focus-visible) .tst-filters label[for=tf-se],.wrap-wide:has(#tr-all:focus-visible) .tst-filters label[for=tr-all],.wrap-wide:has(#tr-architect:focus-visible) .tst-filters label[for=tr-architect],.wrap-wide:has(#tr-developer:focus-visible) .tst-filters label[for=tr-developer],.wrap-wide:has(#tr-designer:focus-visible) .tst-filters label[for=tr-designer],.wrap-wide:has(#tr-agency:focus-visible) .tst-filters label[for=tr-agency],.wrap-wide:has(#tr-manufacturer:focus-visible) .tst-filters label[for=tr-manufacturer]{outline:2px solid var(--accent);outline-offset:2px}
/* Free render library (/free-renders/, 12 Oct 2026): the same radio + chip filter as the
   client stories above, one category at a time. */
.wrap-wide:has(#fr-all:checked) .tst-filters label[for=fr-all],.wrap-wide:has(#fr-exterior:checked) .tst-filters label[for=fr-exterior],.wrap-wide:has(#fr-interior:checked) .tst-filters label[for=fr-interior],.wrap-wide:has(#fr-aerial:checked) .tst-filters label[for=fr-aerial],.wrap-wide:has(#fr-product:checked) .tst-filters label[for=fr-product],.wrap-wide:has(#fr-floorplan:checked) .tst-filters label[for=fr-floorplan]{background:var(--ink);border-color:var(--ink);color:#fff}
.wrap-wide:has(#fr-all:checked) .tst-filters label[for=fr-all] .n,.wrap-wide:has(#fr-exterior:checked) .tst-filters label[for=fr-exterior] .n,.wrap-wide:has(#fr-interior:checked) .tst-filters label[for=fr-interior] .n,.wrap-wide:has(#fr-aerial:checked) .tst-filters label[for=fr-aerial] .n,.wrap-wide:has(#fr-product:checked) .tst-filters label[for=fr-product] .n,.wrap-wide:has(#fr-floorplan:checked) .tst-filters label[for=fr-floorplan] .n{color:rgba(255,255,255,.7)}
.wrap-wide:has(#fr-all:focus-visible) .tst-filters label[for=fr-all],.wrap-wide:has(#fr-exterior:focus-visible) .tst-filters label[for=fr-exterior],.wrap-wide:has(#fr-interior:focus-visible) .tst-filters label[for=fr-interior],.wrap-wide:has(#fr-aerial:focus-visible) .tst-filters label[for=fr-aerial],.wrap-wide:has(#fr-product:focus-visible) .tst-filters label[for=fr-product],.wrap-wide:has(#fr-floorplan:focus-visible) .tst-filters label[for=fr-floorplan]{outline:2px solid var(--accent);outline-offset:2px}
.wrap-wide:has(#fr-exterior:checked) .fr-grid>[data-cat]:not([data-cat=exterior]),.wrap-wide:has(#fr-interior:checked) .fr-grid>[data-cat]:not([data-cat=interior]),.wrap-wide:has(#fr-aerial:checked) .fr-grid>[data-cat]:not([data-cat=aerial]),.wrap-wide:has(#fr-product:checked) .fr-grid>[data-cat]:not([data-cat=product]),.wrap-wide:has(#fr-floorplan:checked) .fr-grid>[data-cat]:not([data-cat=floorplan]){display:none}
.wrap-wide:has(#frl-all:checked) .tst-filters label[for=frl-all],.wrap-wide:has(#frl-day:checked) .tst-filters label[for=frl-day],.wrap-wide:has(#frl-dusk:checked) .tst-filters label[for=frl-dusk],.wrap-wide:has(#frl-winter:checked) .tst-filters label[for=frl-winter]{background:var(--ink);border-color:var(--ink);color:#fff}
.wrap-wide:has(#frl-all:checked) .tst-filters label[for=frl-all] .n,.wrap-wide:has(#frl-day:checked) .tst-filters label[for=frl-day] .n,.wrap-wide:has(#frl-dusk:checked) .tst-filters label[for=frl-dusk] .n,.wrap-wide:has(#frl-winter:checked) .tst-filters label[for=frl-winter] .n{color:rgba(255,255,255,.7)}
.wrap-wide:has(#frl-all:focus-visible) .tst-filters label[for=frl-all],.wrap-wide:has(#frl-day:focus-visible) .tst-filters label[for=frl-day],.wrap-wide:has(#frl-dusk:focus-visible) .tst-filters label[for=frl-dusk],.wrap-wide:has(#frl-winter:focus-visible) .tst-filters label[for=frl-winter]{outline:2px solid var(--accent);outline-offset:2px}
.wrap-wide:has(#frl-day:checked) .fr-grid>[data-light]:not([data-light=day]),.wrap-wide:has(#frl-dusk:checked) .fr-grid>[data-light]:not([data-light=dusk]),.wrap-wide:has(#frl-winter:checked) .fr-grid>[data-light]:not([data-light=winter]){display:none}
.tst-list{display:grid;gap:clamp(22px,3vw,34px)}
.tst{display:grid;grid-template-columns:minmax(0,1.04fr) minmax(0,1fr);border:1px solid var(--hair);border-radius:14px;overflow:hidden;background:var(--white)}
.tst.solo{grid-template-columns:minmax(0,1.04fr) minmax(0,1fr)}
.tst.solo blockquote{max-width:72ch}
/* A card with no approved image used to collapse to one column, so its body ran the full
   920px while every other card's body sits at 451px in the RIGHT column. Centring it first
   (1 Oct) still read as odd next to its neighbours, because the eye expects the text on the
   right. So keep the normal two-column grid and leave the image half empty: the quote then
   lines up exactly with every card above and below it. Siim, 1 Oct: "half width right align
   on desktop othww its odd". Marian Akra is currently the only text-only card. */
.tst.solo .tst-body{grid-column:2}
.tst-media{position:relative;height:auto;align-self:stretch;min-height:clamp(320px,34vw,460px);background:var(--hair)}
/* 23 Oct 2026: the picture column used to be a fixed 320-460px tall, so a long quote (Voutilainen, 85 words) made the card
   taller than its pictures and left a gap under them. It now stretches to the card; the photo covers it. Mobile keeps
   its own explicit height (one column), see the max-width rule below. */
.tst-mi{position:absolute;inset:0;display:grid;grid-template-rows:1.5fr 1fr;gap:3px}
/* A card can legitimately carry a single image: a client may withdraw the others at any
   time (Antti, 23 Sep). Without this the 1fr sub-row is still reserved and the card shows
   a grey band under the one image, which reads as a broken thumbnail rather than a choice. */
.tst-mi:not(:has(.tst-sub)){grid-template-rows:1fr}
.tst-mi>*{min-width:0;min-height:0}
/* min-height/width:0 matters here specifically for the two .tst-sub images: they're grid
   items of a NESTED grid (.tst-sub inside .tst-mi), and .tst-mi>* only reaches its direct
   child (.tst-sub itself), not these grandchildren. Without it each kept its default
   min-height:auto and rendered at its own intrinsic aspect (~438px) instead of the 183px
   row height:100% asked for -- masked visually by .tst{overflow:hidden} most of the time,
   but it meant object-position was being computed against the wrong, much taller box. */
.tst-mi img{width:100%;height:100%;min-width:0;min-height:0;object-fit:cover;display:block}
/* A review image with a loop is wrapped in .tst-mv[data-motion] (webref/loops.py): the wrapper fills the cell. */
.tst-mi .tst-mv{display:block;width:100%;height:100%;min-width:0;min-height:0}
.tst-sub{display:grid;grid-template-columns:1fr 1fr;gap:3px}
/* A card with only TWO approved images has one sub, and the fixed 2-column grid left the
   other half as a grey hole (Antti Nieminen, 1 Oct: both his projects were shot from a
   single camera, so a third frame would only be a colour variant of the first). Mirrors
   the .tst-mi rule above, which already collapses the rows when there is no sub at all. */
.tst-sub:has(>img:only-child){grid-template-columns:1fr}
.tst-body{padding:clamp(24px,3.2vw,42px);display:flex;flex-direction:column;justify-content:center;min-width:0}
.tst blockquote{margin:0 0 30px;font-family:var(--disp);font-size:clamp(16px,1.35vw,19px);line-height:1.45;color:var(--ink);font-weight:500;letter-spacing:-0.02em;text-wrap:pretty}
/* Review highlights in red, text and underline (Siim chose this from a side-by-side, 27 Sep). */
.tst blockquote strong{font-weight:inherit;color:var(--accent);text-decoration:underline;text-decoration-color:var(--accent);text-decoration-thickness:2px;text-underline-offset:5px}
.tst blockquote p+p{margin-top:13px}
.tst-who{display:flex;align-items:flex-start;gap:14px 16px;flex-wrap:nowrap;padding-top:24px;border-top:1px solid var(--hair)}
.tst-ph{width:54px;height:54px;border-radius:50%;object-fit:cover;flex:0 0 auto}
.tst-id{min-width:0;flex:1 1 0%}
.tst-id .tst-name{display:block;margin:0;font-family:var(--disp);font-size:16.5px;font-weight:700;color:var(--ink);letter-spacing:-0.01em;line-height:1.25}
.tst-role{display:block;font-size:14px;color:var(--muted);line-height:1.4;margin-top:2px}
.tst-link{display:block;font-size:13px;font-weight:600;color:var(--accent-d);margin-top:3px;text-decoration:none}
.tst-link:hover{text-decoration:underline}
.tst-ctry{display:flex;align-items:center;gap:7px;font-size:13px;color:var(--muted);margin-top:3px}
.tst-flag{width:20px;height:14px;border-radius:2px;box-shadow:0 0 0 1px rgba(0,0,0,.14);flex:0 0 auto;display:block}
.tst-logo{flex:0 0 auto;width:calc(112px * var(--logo-scale,1));margin-left:10px;color:var(--ink);display:flex;align-items:center}
/* Same flattening as the hero marks, so every card logo lands on the identical #999
   rather than each keeping its own weight (plain grayscale() left Tokvam and Larsen near
   black, since they inherit currentColor, and Oma's red a dark charcoal).
   --mark-filter is the escape hatch for a mark that flattening would destroy: Oma's is
   white lettering (#F5F1F2) knocked out of a red field (#CF0A2C), and brightness(0)
   drives BOTH to black and swallows the word. That one is set in reviews.py to
   grayscale+opacity instead, tuned so the red field composites to the same #999 while
   the knocked-out lettering stays white. */
/* Client marks render in their OWN colours (Siim, 12 Oct 2026). The grey flattening
   stays on the logo WALL at the top of the page (.logo-cell), which is a different
   component. Checked every mark first: none is white-on-transparent, so nothing
   disappears on the white card. CP Lifestyle reads as mid grey, not white, which an
   earlier preview on a grey background made it look like. */
.tst-logo svg,.tst-logo img{width:100%;height:auto;max-height:52px;display:block;filter:var(--mark-filter,none)}
.tst-meta{margin-top:8px;font-size:13px;color:var(--muted);letter-spacing:.01em;line-height:1.5}
.tst-meta a{color:inherit;text-decoration:underline;text-underline-offset:2px}