/* =========================================================================
   BUILD PRICING CARDS — shared by tack-rooms.html and offices.html
   -------------------------------------------------------------------------
   These rules started as an inline <style> block on tack-rooms.html. The
   offices page now uses the identical pattern — a yard switcher above a grid
   of .build-card articles, each listing every spec and one all-in total that
   swaps per yard — so they live in a real stylesheet instead of being copied
   into a second page and drifting apart.

   Behaviour comes from assets/js/build-pricing.js, which keys off the
   #tr-segment radiogroup and the data-total-* attributes on each card.

   .segment / .seg-btn / .seg-thumb come from catalog.css and are authored for
   a dark bar, which is why the switcher sits on an ink panel.
   ========================================================================= */

/* ---- yard switcher bar ---- */
.tr-yardbar{display:flex;align-items:center;gap:clamp(12px,1.8vw,20px);flex-wrap:wrap;
  margin-top:32px;padding:15px 20px;border-radius:var(--radius);background:var(--ink);color:#fff}
.tr-yardbar-label{font-size:11.5px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;
  color:var(--sage-400);white-space:nowrap}
.tr-yardbar-note{display:flex;align-items:center;gap:8px;margin-left:auto;max-width:46ch;
  font-size:12.5px;line-height:1.45;color:rgba(255,255,255,.58)}
.tr-yardbar-note svg{width:14px;height:14px;flex-shrink:0;color:var(--sage-400)}
.tr-yardbar-note b{color:rgba(255,255,255,.88);font-weight:600}

/* ---- the cards ---- */
.build-grid{display:grid;grid-template-columns:1fr 1fr;gap:22px;margin-top:22px;align-items:start}
.build-card{padding:clamp(22px,3vw,30px);display:flex;flex-direction:column;gap:20px}
.build-card .chip{align-self:flex-start}
.build-head h3{font-size:clamp(20px,2.4vw,25px);margin-top:11px}
.build-base{margin-top:7px;font-size:14.5px;color:var(--muted);line-height:1.55}

.build-price{padding:18px 20px;border-radius:var(--radius-sm);
  background:rgba(87,118,86,.08);border:1px solid rgba(87,118,86,.22)}
.build-amt{display:block;font-family:var(--font-display);font-weight:900;line-height:1.05;
  font-size:clamp(30px,4.2vw,40px);letter-spacing:-.02em;color:var(--ink);
  font-variant-numeric:tabular-nums}
.build-amt.flash{animation:priceIn .4s var(--ease)}
.build-sub{display:block;margin-top:7px;font-size:13px;color:var(--muted);line-height:1.5}
.build-sub b{color:var(--sage-600);font-weight:600}

.build-specs h4{font-family:var(--font-display);font-size:12px;font-weight:600;
  letter-spacing:.1em;text-transform:uppercase;color:var(--sage-700)}
.speclist{list-style:none;margin:15px 0 0;padding:0;display:flex;flex-direction:column;gap:12px}
.speclist li{display:flex;gap:11px}
.speclist li>svg{width:16px;height:16px;flex-shrink:0;margin-top:4px;color:var(--sage-700)}
.speclist b{font-family:var(--font-display);font-weight:600;font-size:15px;color:var(--ink)}
.speclist span{display:block;margin-top:3px;font-size:13.5px;color:var(--muted);line-height:1.55}

.build-foot{margin-top:auto;padding-top:4px;display:flex;flex-wrap:wrap;gap:10px}
.build-foot .btn{flex:1 1 190px;justify-content:center;white-space:normal;text-align:center}

/* Offices carry four builds rather than two, so the pair of "Economical"
   cards reads as a tier rather than as a discount. */
.build-card.is-lean .build-price{background:rgba(87,118,86,.05);border-color:rgba(87,118,86,.16)}

@media(max-width:900px){
  .build-grid{grid-template-columns:1fr}
}
@media(max-width:720px){
  .tr-yardbar{gap:11px;padding:13px 14px}
  .tr-yardbar-label{display:none}
  .tr-yardbar .segment{width:100%}
  .tr-yardbar-note{width:100%;margin-left:0;max-width:none;order:5}
}

/* =========================================================================
   SITE SEARCH overlay — assets/js/site-search.js
   The header magnifying glass had no handler on any page; this is the panel
   it now opens. Lives here rather than in a third stylesheet because every
   page that loads builds.css also loads the search.
   ========================================================================= */
.ssearch{position:fixed;inset:0;z-index:400;background:rgba(20,23,26,.55);
  backdrop-filter:blur(3px);opacity:0;transition:opacity .2s var(--ease);
  display:flex;align-items:flex-start;justify-content:center;padding:clamp(16px,8vh,90px) 18px}
/* MUST come after the display:flex above. The panel is toggled with the [hidden]
   attribute, and [hidden] only works because the UA stylesheet sets display:none
   — ANY author display declaration beats it. Without this rule the overlay stays
   in the layout at opacity 0, full-screen and z-index 400, and silently swallows
   every click on the page. It did exactly that on all 15 pages. */
.ssearch[hidden]{display:none}
.ssearch.is-open{opacity:1}
.ssearch-panel{width:100%;max-width:640px;background:var(--paper);border-radius:var(--radius);
  box-shadow:var(--shadow-lg);overflow:hidden;transform:translateY(-10px);
  transition:transform .2s var(--ease);display:flex;flex-direction:column;max-height:100%}
.ssearch.is-open .ssearch-panel{transform:none}

.ssearch-bar{display:flex;align-items:center;gap:11px;padding:14px 16px;
  border-bottom:1px solid var(--line)}
.ssearch-bar>svg{width:19px;height:19px;flex-shrink:0;color:var(--muted)}
.ssearch-bar input{flex:1;min-width:0;border:0;background:none;font-family:inherit;
  font-size:17px;color:var(--text);padding:4px 0}
.ssearch-bar input::-webkit-search-cancel-button{display:none}
.ssearch-close{width:32px;height:32px;flex-shrink:0;display:grid;place-items:center;
  border:1px solid var(--line);border-radius:8px;background:var(--paper);cursor:pointer;color:var(--muted)}
.ssearch-close svg{width:15px;height:15px}
.ssearch-close:hover{border-color:var(--ink);color:var(--ink)}

.ssearch-results{overflow-y:auto;padding:8px}
.ssearch-hit{display:flex;align-items:flex-start;gap:12px;padding:11px 12px;border-radius:9px}
.ssearch-hit:hover{background:var(--sand)}
.ssearch-kind{flex-shrink:0;margin-top:2px;font-size:10.5px;font-weight:600;letter-spacing:.09em;
  text-transform:uppercase;color:var(--sage-600);background:rgba(87,118,86,.10);
  border:1px solid rgba(87,118,86,.18);border-radius:100px;padding:3px 9px}
.ssearch-body{min-width:0}
.ssearch-body b{display:block;font-family:var(--font-display);font-size:15.5px;color:var(--ink)}
.ssearch-body small{display:block;margin-top:2px;font-size:13px;color:var(--muted);line-height:1.5}
.ssearch-empty{margin:0;padding:22px 14px;font-size:14.5px;color:var(--muted);text-align:center}
.ssearch-hint{margin:0;padding:10px 16px;border-top:1px solid var(--line);
  font-size:12px;color:var(--muted-2);background:var(--sand)}
.ssearch-hint kbd{font:inherit;font-size:11px;border:1px solid var(--line-2);border-bottom-width:2px;
  border-radius:5px;padding:1px 5px;background:var(--paper)}

@media(max-width:560px){
  .ssearch{padding:0}
  .ssearch-panel{max-width:none;border-radius:0;height:100%;max-height:100%}
}

/* ---- build photos (offices.html) ----
   One pair per build — outside and inside. Fixed aspect so a real photo
   dropping in cannot change the card's height and reflow the grid. */
.build-shots{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.shot{margin:0}
/* height:auto is load-bearing — without it the img takes the literal height="420"
   attribute and aspect-ratio is ignored, which rendered these 257x420. The
   width/height attributes stay so the box is reserved before the photo loads. */
.shot img{width:100%;height:auto;aspect-ratio:16/10.5;object-fit:cover;border-radius:var(--radius-sm);
  background:var(--sand-2);border:1px solid var(--line)}
.shot figcaption{margin-top:6px;font-size:11.5px;font-weight:600;letter-spacing:.09em;
  text-transform:uppercase;color:var(--muted-2)}

/* ---- photo slideshow -------------------------------------------------------
   Replaced the five-up grid on generator-enclosures. Five shots of one product
   read better one at a time, and a strip copes with mixed orientations: the
   interior shot is portrait and the rest are landscape, which a grid cell could
   only handle by discarding half its height.

   The strip is a scroll-snap track, so it SWIPES AND SCROLLS WITH NO JAVASCRIPT.
   assets/js/slideshow.js adds arrows, dots and keyboard support on top; if it
   never loads, every photograph is still reachable.

   object-fit:contain, not cover. A gallery exists to show the work, so a
   portrait shot is letterboxed rather than cropped in half. The ground behind
   is the same ink as the footer so the bars read as a frame, not a mistake. */
.pslide{position:relative;border-radius:var(--radius);overflow:hidden;background:var(--ink)}
.pslide:focus-visible{outline:2px solid var(--sage-400);outline-offset:3px}

.pslide-track{
  display:flex;overflow-x:auto;scroll-snap-type:x mandatory;
  scrollbar-width:none;-ms-overflow-style:none;
  scroll-behavior:smooth;
}
.pslide-track::-webkit-scrollbar{display:none}
@media(prefers-reduced-motion:reduce){.pslide-track{scroll-behavior:auto}}

.pslide-item{
  margin:0;flex:0 0 100%;scroll-snap-align:center;position:relative;
  aspect-ratio:16/10;display:grid;place-items:center;
}
.pslide-item img{width:100%;height:100%;object-fit:contain;display:block}

.pslide-item figcaption{
  position:absolute;left:0;right:0;bottom:0;padding:26px 16px 12px;
  font-size:12.5px;font-weight:600;letter-spacing:.04em;color:#fff;
  background:linear-gradient(to top,rgba(10,12,14,.82),transparent);
}

/* Controls only exist once the script is driving them — see slideshow.js */
.pslide-arrow{
  position:absolute;top:50%;transform:translateY(-50%);z-index:3;
  width:42px;height:42px;display:grid;place-items:center;
  border:0;border-radius:50%;cursor:pointer;
  background:rgba(255,255,255,.92);color:var(--ink);
  box-shadow:0 2px 10px rgba(0,0,0,.28);
}
.pslide-arrow svg{width:20px;height:20px}
.pslide-prev{left:12px}
.pslide-next{right:12px}
.pslide-arrow:disabled{opacity:.32;cursor:default}
.pslide-arrow:not(:disabled):hover{background:#fff}

.pslide-dots{
  position:absolute;left:0;right:0;bottom:10px;z-index:3;
  display:flex;justify-content:center;gap:7px;
}
.pslide-dot{
  width:8px;height:8px;padding:0;border:0;border-radius:50%;cursor:pointer;
  background:rgba(255,255,255,.45);
}
.pslide-dot.is-on{background:#fff;transform:scale(1.25)}
.pslide-dot:focus-visible{outline:2px solid #fff;outline-offset:2px}

@media(max-width:560px){
  .pslide-item{aspect-ratio:4/3}
  .pslide-arrow{width:36px;height:36px}
  .pslide-prev{left:8px}
  .pslide-next{right:8px}
}

/* =========================================================================
   INTERIOR PAGE HERO — photo variant
   -------------------------------------------------------------------------
   The base .page-hero (catalog.css) is a flat ink panel with two gradient
   washes. Adding `has-photo` puts a real photograph behind the same layout.

   The scrim is the load-bearing part. The headline, breadcrumb and lead are
   all white or near-white and were designed against a near-black panel; drop a
   bright sky behind them and the contrast collapses. The gradient below keeps
   the left third — where all the text sits — dark enough to stay readable,
   while letting the right side of the photo show through. It is a gradient
   rather than a flat tint so the image does not look muddy.
   ========================================================================= */
.page-hero.has-photo{isolation:isolate}
.page-hero.has-photo .ph-photo{
  position:absolute;inset:0;z-index:0;overflow:hidden;
}
.page-hero.has-photo .ph-photo img{
  width:100%;height:100%;object-fit:cover;object-position:center 55%;
  /* the photos are bright outdoor shots; knock them back so white type reads */
  filter:saturate(.92) brightness(.82);
}

/* ---- Compact hero ---------------------------------------------------------
   The hero band's height comes from the text inside it, and the photo simply
   fills whatever that produces. On generator-enclosures that came to 664px —
   86% of a 768px screen — so the photograph read as far too large and the page
   content started below the fold.

   This tightens the padding and the type on that page only. It does NOT crop
   the photo differently: the band and the photo are within 3% of the same
   aspect, so there is almost nothing to re-frame (measured: 97.3% of the photo
   is already visible). Making the band shorter is the only lever that changes
   how big the picture reads. */
.page-hero.ph-compact .container{padding-block:clamp(22px,3vw,34px)}
.page-hero.ph-compact h1{font-size:clamp(26px,3.5vw,40px);margin-top:10px}
.page-hero.ph-compact .lead{margin-top:10px;max-width:52ch}
.page-hero.ph-compact .hero-trust{margin-top:clamp(16px,2vw,22px);padding-top:16px}
/* scrim: strong on the left where the copy is, easing off to the right */
.page-hero.has-photo .ph-photo::after{
  content:"";position:absolute;inset:0;
  background:
    linear-gradient(100deg,
      rgba(20,23,26,.94) 0%,
      rgba(20,23,26,.86) 34%,
      rgba(20,23,26,.58) 62%,
      rgba(20,23,26,.42) 100%),
    linear-gradient(to top, rgba(20,23,26,.55), transparent 45%);
}
/* the base gradient washes sit above the photo, not below it */
.page-hero.has-photo::before{z-index:2}
.page-hero.has-photo::after{z-index:3}

@media(max-width:820px){
  /* on a phone the copy spans the full width, so the scrim has to as well */
  .page-hero.has-photo .ph-photo::after{
    background:
      linear-gradient(180deg, rgba(20,23,26,.88) 0%, rgba(20,23,26,.80) 55%, rgba(20,23,26,.90) 100%);
  }
  .page-hero.has-photo .ph-photo img{object-position:center 50%}
}

/* =========================================================================
   PRIVACY CHOICES panel — assets/js/privacy-choices.js
   Opened from the "Your privacy choices" link in every footer.
   ========================================================================= */
.pchoice{position:fixed;inset:0;z-index:420;background:rgba(20,23,26,.6);
  backdrop-filter:blur(3px);opacity:0;transition:opacity .2s var(--ease);
  display:flex;align-items:center;justify-content:center;padding:20px}
/* MUST follow the display above — [hidden] only works via the UA stylesheet's
   display:none, which any author display declaration beats. Without this the
   panel stays in the layout at opacity 0 and swallows every click. */
.pchoice[hidden]{display:none}
.pchoice.is-open{opacity:1}
.pchoice-panel{position:relative;width:100%;max-width:520px;background:var(--paper);
  border-radius:var(--radius);box-shadow:var(--shadow-lg);padding:clamp(22px,4vw,32px);
  max-height:100%;overflow-y:auto;transform:translateY(-8px);transition:transform .2s var(--ease)}
.pchoice.is-open .pchoice-panel{transform:none}
.pchoice-panel h2{font-size:clamp(20px,2.6vw,25px)}
.pchoice-panel p{margin-top:12px;font-size:14.5px;line-height:1.65;color:var(--muted)}
.pchoice-state{margin-top:14px!important;padding:11px 14px;border-radius:9px;
  background:rgba(87,118,86,.09);border:1px solid rgba(87,118,86,.2);
  color:var(--sage-600)!important;font-weight:600}
.pchoice-actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:20px}
.pchoice-actions .btn{flex:1 1 200px;justify-content:center}
.pchoice-actions .btn[disabled]{opacity:.45;cursor:not-allowed;pointer-events:none}
.pchoice-fine{font-size:12.5px!important;color:var(--muted-2)!important;margin-top:16px!important}
.pchoice-close{position:absolute;top:14px;right:14px;width:32px;height:32px;
  display:grid;place-items:center;border:1px solid var(--line);border-radius:8px;
  background:var(--paper);cursor:pointer;color:var(--muted)}
.pchoice-close svg{width:15px;height:15px}
.pchoice-close:hover{border-color:var(--ink);color:var(--ink)}

/* ---- deep-linked product card (assets/js/deep-link.js) ----
   An ad click arrives at /containers.html?p=<slug>. The ring is a quiet cue
   that this is the container the advertisement was about — enough to orient
   someone, not enough to look like an error state. */
.prod.is-deeplinked{
  outline:3px solid var(--sage-600);
  outline-offset:3px;
  animation:deeplinkPulse 2.4s var(--ease) 1;
}
@keyframes deeplinkPulse{
  0%{outline-color:transparent}
  18%{outline-color:var(--sage-600)}
  100%{outline-color:var(--sage-600)}
}
@media(prefers-reduced-motion:reduce){
  .prod.is-deeplinked{animation:none}
}

/* =========================================================================
   GALLERY (gallery.html)
   -------------------------------------------------------------------------
   A grid of thumbnails that opens a lightbox. The tiles load a 600px image
   and hold the 1600px one on data-full, so the page never fetches a 3-5 MB
   original until someone actually asks to see one.
   ========================================================================= */
.gal-grid{
  display:grid;gap:clamp(12px,1.6vw,18px);
  grid-template-columns:repeat(auto-fill,minmax(230px,1fr));
}
.gal-item{margin:0;position:relative;border-radius:var(--radius);overflow:hidden;background:var(--sand)}
.gal-open{
  display:block;width:100%;padding:0;border:0;background:none;cursor:zoom-in;
}
.gal-item img{
  width:100%;aspect-ratio:4/3;object-fit:cover;display:block;
  transition:transform .5s var(--ease);
}
.gal-item:hover img{transform:scale(1.05)}
.gal-open:focus-visible{outline:2px solid var(--sage-600);outline-offset:-2px}
.gal-item figcaption{
  position:absolute;left:0;right:0;bottom:0;padding:22px 13px 10px;pointer-events:none;
  font-size:12px;font-weight:600;letter-spacing:.04em;color:#fff;
  background:linear-gradient(to top,rgba(10,12,14,.8),transparent);
}
/* Without the script the tiles cannot open, so don't promise that they can. */
.gal-grid:not(.is-live) .gal-open{cursor:default}

/* ---- lightbox ---- */
.lbox{
  position:fixed;inset:0;z-index:120;display:grid;place-items:center;
  background:rgba(8,10,12,.94);padding:clamp(16px,4vw,54px);
}
.lbox-stage{margin:0;max-width:min(1200px,100%);max-height:100%;display:grid;gap:12px}
.lbox-stage img{
  max-width:100%;max-height:78vh;width:auto;height:auto;
  object-fit:contain;display:block;margin:0 auto;border-radius:var(--radius-sm);
}
.lbox-stage figcaption{
  text-align:center;color:rgba(255,255,255,.88);
  font-size:13px;font-weight:600;letter-spacing:.04em;
}
.lbox-close,.lbox-arrow{
  position:absolute;z-index:2;display:grid;place-items:center;
  width:44px;height:44px;border:0;border-radius:50%;cursor:pointer;
  background:rgba(255,255,255,.92);color:var(--ink);
}
.lbox-close{top:18px;right:18px}
.lbox-arrow{top:50%;transform:translateY(-50%)}
.lbox-prev{left:14px}
.lbox-next{right:14px}
.lbox-close svg,.lbox-arrow svg{width:21px;height:21px}
.lbox-close:hover,.lbox-arrow:hover{background:#fff}
.lbox-close:focus-visible,.lbox-arrow:focus-visible{outline:2px solid #fff;outline-offset:3px}
.lbox-count{
  position:absolute;bottom:16px;left:0;right:0;text-align:center;
  color:rgba(255,255,255,.7);font-size:12px;letter-spacing:.06em;
}
@media(max-width:560px){
  .lbox-close,.lbox-arrow{width:38px;height:38px}
  .lbox-stage img{max-height:70vh}
}

/* ---- gallery slideshow ----
   Same component as the generator page, but a gallery grows: past about ten
   photographs a row of dots stops being usable, so .pslide-count carries the
   position instead and the dots are dropped. */
.pslide-gallery .pslide-item{aspect-ratio:16/9}
.pslide-count{
  position:absolute;right:14px;bottom:12px;z-index:3;
  padding:4px 10px;border-radius:999px;
  background:rgba(10,12,14,.62);color:#fff;
  font-size:12px;font-weight:600;letter-spacing:.05em;
}
.pslide-gallery.has-many .pslide-dots{display:none}
@media(max-width:560px){
  .pslide-gallery .pslide-item{aspect-ratio:4/3}
}

/* play/pause for an autoplaying slideshow — required, not decorative:
   WCAG 2.2.2 says moving content lasting over five seconds must be stoppable. */
.pslide-play{
  position:absolute;left:12px;bottom:10px;z-index:3;
  width:32px;height:32px;display:grid;place-items:center;
  border:0;border-radius:50%;cursor:pointer;
  background:rgba(10,12,14,.62);color:#fff;
}
.pslide-play svg{width:15px;height:15px}
.pslide-play:hover{background:rgba(10,12,14,.82)}
.pslide-play:focus-visible{outline:2px solid #fff;outline-offset:2px}
