/* HT-Beats — WordPress structural tweaks.
   main.css is derived verbatim from the canonical page; this file adapts the
   WordPress-specific structures (nav-menu markup, prose pages, core blocks) so
   they match the design without editing the derived stylesheet. */

/* Ground, color-scheme and the light-mode body swap now live in the canonical
   page (htbeats-site/index.html -> main.css) with the rest of the design. Only
   the pre-paint fallback stays here, because it must apply before any of the
   token layer exists. */
html, body { background: #0B0D12; }
.htb { min-height: 100vh; }

/* ── wp_nav_menu: nav bar (flex row of links) ───────────────────────────── */
.htb-nav .links .menu { display: flex; align-items: center; gap: 6px; list-style: none; margin: 0; padding: 0; }
.htb-nav .links .menu li { margin: 0; }
.htb-nav .links .menu .current-menu-item > a,
.htb-nav .links .menu .current_page_item > a { color: var(--text); background: rgba(255,255,255,.05); }

/* ── wp_nav_menu: footer columns (stacked links) ────────────────────────── */
.htb-foot .col .menu { list-style: none; margin: 0; padding: 0; }
.htb-foot .col .menu li { margin: 0; }

/* ── wp_nav_menu: mobile sheet (stacked) ────────────────────────────────── */
.htb-sheet .panel .menu { display: flex; flex-direction: column; list-style: none; margin: 0; padding: 0; }
.htb-sheet .panel .menu li { margin: 0; }

/* ── Prose pages (legal, docs, About, listings) ─────────────────────────── */
.htb-prose { max-width: 820px; }
.htb-prose .htb-eyebrow { justify-content: flex-start; }
.htb-prose-body { margin-top: 22px; color: var(--text2); font-size: 16px; line-height: 1.75; }
.htb-prose-body > * + * { margin-top: 1.05em; }
.htb-prose-body h2 { font-family: var(--disp); color: var(--text); font-size: 26px; margin-top: 1.6em; letter-spacing: -.02em; }
.htb-prose-body h3 { font-family: var(--disp); color: var(--text); font-size: 20px; margin-top: 1.4em; }
.htb-prose-body h4 { color: var(--text); font-size: 17px; }
.htb-prose-body a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
.htb-prose-body strong { color: var(--text); }
.htb-prose-body ul, .htb-prose-body ol { padding-left: 1.3em; }
.htb-prose-body li { margin-top: .4em; }
.htb-prose-body blockquote { border-left: 3px solid var(--line3); padding-left: 16px; color: var(--muted); }
.htb-prose-body code { font-family: var(--mono); font-size: .9em; background: rgba(255,255,255,.06); border: 1px solid var(--line); border-radius: 6px; padding: 2px 6px; }
.htb-prose-body pre { background: var(--surface); border: 1px solid var(--line2); border-radius: 12px; padding: 16px; overflow-x: auto; }
.htb-prose-body pre code { background: none; border: 0; padding: 0; }
.htb-prose-body img { border-radius: 12px; border: 1px solid var(--line); }
.htb-prose-body table { width: 100%; border-collapse: collapse; display: block; overflow-x: auto; }
.htb-prose-body th, .htb-prose-body td { border: 1px solid var(--line); padding: 10px 12px; text-align: left; font-size: 14.5px; }
.htb-prose-body th { color: var(--text); background: var(--elevated); }
.htb-prose-nav { display: flex; justify-content: space-between; gap: 16px; margin-top: 40px; padding-top: 20px; border-top: 1px solid var(--line); font-size: 14px; }
.htb-prose-nav a { color: var(--accent); }

/* ── Search form ────────────────────────────────────────────────────────── */
.htb .search-form { display: flex; gap: 8px; }
.htb .search-form .search-field { flex: 1; background: var(--surface); border: 1px solid var(--line2); border-radius: 10px; color: var(--text); padding: 12px 14px; font: inherit; }
.htb .search-form .search-submit { background: linear-gradient(180deg,var(--accent),var(--accent2)); color: #fff; border: 0; border-radius: 10px; padding: 12px 18px; font-weight: 600; cursor: pointer; }

/* ── Pagination ─────────────────────────────────────────────────────────── */
.htb .pagination, .htb .nav-links { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; }
.htb .page-numbers { border: 1px solid var(--line2); border-radius: 9px; padding: 8px 13px; color: var(--text2); }
.htb .page-numbers.current { background: var(--accent); color: #fff; border-color: transparent; }

/* ── HT-Beats company mark ──────────────────────────────────────────────── */
/* The company's own logo, shown on the company's pages in place of the SuperSet
   Studio lockup. Two files stacked and swapped by the theme class, exactly as
   the product wordmark is — one rendering cannot carry both a near-black and a
   near-white ground.

   Sized by HEIGHT, never width: the source is a 1024px SQUARE with the artwork
   in the middle, so `width: 40px` would size the canvas and draw the logo at
   about a third of that. tools/prepare-htbeats-logo.py crops the empty margin
   away, but sizing by height is still the honest way to size a lockup. */
.htb .htb-mark--company { gap: 0; }
.htb .htb-comark { position: relative; display: block; line-height: 0; }
.htb .htb-comark__img {
  display: block;
  width: auto;
  /* 310x240 after cropping. Height drives it; the width attribute is there so
     the browser reserves the right box before the file arrives.
     84px is a deliberate doubling of the 42px it launched at — the mark reads
     as the company's, not as a favicon parked next to the menu. */
  height: 84px;
  transition: opacity .25s ease;
}
/* ...and the bar gives back some of the padding it no longer needs, so twice
   the logo does not become twice the sticky header. 84 + 2x10 = 104px rather
   than 112. */
.htb .htb-nav--company .in { padding-block: 10px; }
/* The pair are stacked so the toggle costs no second request. The one that is
   not in play is taken out of flow rather than merely hidden, so it cannot
   contribute to the lockup's height. */
.htb .htb-comark__img--light { position: absolute; inset-block-start: 0; inset-inline-start: 0; opacity: 0; }
.htb-light .htb-comark__img--dark { opacity: 0; }
.htb-light .htb-comark__img--light { opacity: 1; }
@media (prefers-color-scheme: light) {
  .htb:not(.htb-dark) .htb-comark__img--dark { opacity: 0; }
  .htb:not(.htb-dark) .htb-comark__img--light { opacity: 1; }
}

.htb .htb-foot .htb-comark__img { height: 108px; }

@media (max-width: 600px) {
  /* Doubled too, but from a smaller base — a 168px logo would be most of a
     phone's first screen. */
  .htb .htb-comark__img { height: 64px; }
  .htb .htb-foot .htb-comark__img { height: 76px; }
  .htb .htb-nav--company .in { padding-block: 8px; }
}

@media (prefers-reduced-motion: reduce) {
  .htb .htb-comark__img { transition: none; }
}

/* ── Nav cart ───────────────────────────────────────────────────────────── */
/* The badge sits proud of the 38px icon button rather than inside it: a count
   tucked within the border reads as part of the icon and stops being a count.
   Overflow is therefore deliberate — the nav row has the gap to absorb it. */
.htb .htb-cart { position: relative; overflow: visible; flex: 0 0 auto; }
.htb .htb-cart .htb-cart__ic { display: block; }

.htb .htb-cart__n {
  position: absolute; top: -6px; inset-inline-end: -6px;
  min-width: 18px; height: 18px; padding: 0 5px;
  display: grid; place-items: center;
  border-radius: 999px;
  background: linear-gradient(180deg, var(--accent), var(--accent2));
  color: #fff; font-family: var(--sans); font-size: 11px; font-weight: 700;
  line-height: 1; letter-spacing: 0; font-variant-numeric: tabular-nums;
  /* A ring in the page ground keeps the badge legible where it crosses the
     icon's own border, in both themes and over the scrolled glass bar. */
  box-shadow: 0 0 0 2px var(--bg), 0 2px 8px rgba(0, 0, 0, .35);
  pointer-events: none;
}
/* Filled carts earn the accent; an empty one stays as quiet as the theme
   toggle beside it. */
.htb .htb-cart.is-filled { color: var(--text); border-color: var(--line3); }

@media (prefers-reduced-motion: no-preference) {
  /* One short pulse when the count arrives — the fragment refresh replaces the
     whole link, so the animation restarts on every change for free. */
  .htb .htb-cart__n { animation: htb-cart-pop .28s cubic-bezier(.2, .9, .3, 1.4); }
}
@keyframes htb-cart-pop { from { transform: scale(.4); opacity: 0; } to { transform: scale(1); opacity: 1; } }

/* ── Admin bar offset ───────────────────────────────────────────────────── */
.admin-bar .htb-nav { top: 32px; }
@media (max-width: 782px) { .admin-bar .htb-nav { top: 46px; } }

/* ── Core alignment helpers ─────────────────────────────────────────────── */
.htb .aligncenter { margin-inline: auto; display: block; }
.htb .alignright { float: right; margin: 0 0 1em 1.5em; }
.htb .alignleft { float: left; margin: 0 1.5em 1em 0; }
.htb .screen-reader-text { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); }


/* Section rhythm and the brand wordmark moved to the canonical page
   (htbeats-site/index.html), which is where design decisions belong — this
   file is for WordPress-specific STRUCTURE only. Keeping them here meant the
   live site and the canonical page disagreed about how the site looks. */

/* ═══ Contact page ════════════════════════════════════════════════════════
   Every colour below is a token — var(--surface), var(--text), var(--line).
   That is the whole fix for the light/dark toggle: the page this replaces was
   pasted HTML with hard-coded hex values, so it stayed dark whatever the theme
   said. Nothing here names a colour directly, so it follows the theme for free
   and keeps following it if the palette changes.
   ═════════════════════════════════════════════════════════════════════════ */

/* The head block carries the section's top padding; the grid below it should
   sit close, so the page reads as one unit rather than two stacked bands. */
.htb .htb-contact-head { padding-block-end: 0; }

.htb .htb-contact-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  gap: 28px;
  align-items: start;
  padding-block: clamp(32px, 4vw, 52px) clamp(52px, 6.5vw, 96px);
}

/* ── The form panel ─────────────────────────────────────────────────────── */
.htb .htb-contact-main {
  background: linear-gradient(180deg, var(--surface), var(--bg2));
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: clamp(22px, 3vw, 34px);
}

.htb .htb-cform { display: flex; flex-direction: column; gap: 18px; }

/* Two fields abreast on desktop, stacked once that would squeeze them. */
.htb .htb-frow { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }

.htb .htb-field { display: flex; flex-direction: column; gap: 7px; min-width: 0; }

.htb .htb-field label {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text);
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.htb .htb-field label .opt {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 500;
}

.htb .htb-field input,
.htb .htb-field select,
.htb .htb-field textarea {
  width: 100%;
  font: inherit;
  font-size: 15px;
  color: var(--text);
  background: var(--bg2);
  border: 1px solid var(--line2);
  border-radius: var(--r-sm);
  padding: 12px 14px;
  transition: border-color .15s ease, box-shadow .15s ease, background .15s ease;
}
.htb .htb-field textarea { resize: vertical; min-height: 150px; line-height: 1.6; }

/* A visible, deliberate focus state — the default outline disappears against
   the dark ground. */
.htb .htb-field input:focus,
.htb .htb-field select:focus,
.htb .htb-field textarea:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--surface);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
.htb .htb-field input::placeholder,
.htb .htb-field textarea::placeholder { color: var(--muted); }

/* The native arrow is drawn in the OS colour and vanishes on a dark ground,
   so draw our own from currentColor. */
.htb .htb-field select {
  appearance: none;
  -webkit-appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 18px center, right 13px center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-inline-end: 38px;
}
[dir="rtl"] .htb .htb-field select,
.htb-rtl .htb-field select {
  background-position: left 18px center, left 13px center;
}
/* The dropdown itself is painted by the OS, not by us — name both colours or
   a dark-on-dark list appears in some browsers. */
.htb .htb-field select option { background: var(--surface); color: var(--text); }

.htb .htb-field input[aria-invalid="true"],
.htb .htb-field textarea[aria-invalid="true"],
.htb .htb-field select[aria-invalid="true"] { border-color: var(--err); }

.htb .htb-fe { font-size: 13px; color: var(--err); margin: 0; }
.htb .htb-fh { font-size: 13px; color: var(--muted); margin: 0; }
.htb .htb-fh a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }

.htb .htb-fsubmit {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  margin-top: 4px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}
.htb .htb-fsubmit .htb-fh { flex: 1; min-width: 220px; }

/* Off-screen rather than display:none — a bot that skips hidden inputs is
   exactly the one we want to catch. */
.htb .htb-hp {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(1px, 1px, 1px, 1px);
  white-space: nowrap;
}

/* ── Result banners ─────────────────────────────────────────────────────── */
.htb .htb-note {
  border: 1px solid var(--line2);
  border-radius: var(--r-sm);
  padding: 18px 20px;
  background: var(--bg2);
}
.htb .htb-note + .htb-cform { margin-top: 20px; }
.htb .htb-note h2 { font-family: var(--disp); font-size: 21px; margin: 0 0 6px; color: var(--text); }
.htb .htb-note p { margin: 0; color: var(--text2); font-size: 15px; }
.htb .htb-note strong { color: var(--text); }
/* The status colour rides on the inline-start edge, so it reads in both
   directions without a rule per language. */
.htb .htb-note--ok { border-inline-start: 3px solid var(--ok); }
.htb .htb-note--err { border-inline-start: 3px solid var(--err); }

/* ── Side cards ─────────────────────────────────────────────────────────── */
.htb .htb-contact-side { display: flex; flex-direction: column; gap: 18px; }

.htb .htb-ccard { padding: 22px; }
.htb .htb-ccard p { color: var(--text2); font-size: 14.5px; margin: 10px 0 0; }

.htb .htb-ceyebrow {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--accent);
  font-weight: 600;
  display: block;
}

.htb .htb-cmail {
  display: inline-block;
  margin-top: 10px;
  font-family: var(--disp);
  font-size: clamp(17px, 2vw, 20px);
  font-weight: 700;
  color: var(--text);
  letter-spacing: -.01em;
  word-break: break-word;      /* never let the address widen the card */
  border-bottom: 1px solid var(--line3);
  padding-bottom: 2px;
  transition: color .15s ease, border-color .15s ease;
}
.htb .htb-cmail:hover { color: var(--accent); border-color: var(--accent); }

.htb .htb-clist { margin: 12px 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 9px; }
.htb .htb-clist li {
  position: relative;
  padding-inline-start: 18px;
  color: var(--text2);
  font-size: 14.5px;
  line-height: 1.55;
}
/* A small square marker, echoing the set-list slot in the product mark. */
.htb .htb-clist li::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: .55em;
  width: 6px; height: 6px;
  border-radius: 2px;
  background: var(--accent);
  opacity: .75;
}

.htb .htb-clinks { display: flex; flex-wrap: wrap; gap: 16px; margin-top: 14px; }
.htb .htb-clinks a { color: var(--accent); font-size: 14.5px; font-weight: 600; }
.htb .htb-clinks a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* ── Narrow screens ─────────────────────────────────────────────────────── */
@media (max-width: 940px) {
  .htb .htb-contact-grid { grid-template-columns: 1fr; gap: 22px; }
}
@media (max-width: 560px) {
  .htb .htb-frow { grid-template-columns: 1fr; }
  .htb .htb-fsubmit .htb-btn { width: 100%; }
}


/* ═══ Company front page + Documentation ══════════════════════════════════
   htbeats.com is the company; the product lives at /supersetstudio/. Tokens
   only, so both follow the light/dark toggle like everything else.
   ═════════════════════════════════════════════════════════════════════════ */

.htb .htb-company-hero { padding-block: clamp(56px, 7vw, 104px) clamp(20px, 3vw, 40px); }
.htb .htb-company-hero h1 { font-size: clamp(40px, 7vw, 84px); letter-spacing: -.03em; }

/* ── Product cards ──────────────────────────────────────────────────────── */
.htb .htb-products {
  display: grid;
  /* One product today, and it should not stretch to a 1180px slab. auto-fit
     with a max keeps a single card readable and lets a second sit beside it. */
  grid-template-columns: repeat(auto-fit, minmax(300px, 520px));
  gap: 22px;
  margin-top: 40px;
}

.htb .htb-product {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 18px;
  padding: clamp(26px, 3.4vw, 40px);
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: linear-gradient(180deg, var(--surface), var(--bg2));
  transition: transform .2s ease, border-color .2s ease, box-shadow .2s ease;
  color: inherit;
}
.htb a.htb-product:hover {
  transform: translateY(-3px);
  border-color: var(--line3);
  box-shadow: 0 18px 44px rgba(0, 0, 0, .22);
}
.htb .htb-product.is-soon { opacity: .62; }

/* The wordmark is the headline here, so it is given real size — roughly twice
   the nav's, and it sets its own height rather than inheriting the nav rule. */
.htb .htb-product__mark { height: clamp(46px, 6vw, 72px); }
.htb .htb-product__name { font-family: var(--disp); font-size: 28px; font-weight: 700; color: var(--text); }
.htb .htb-product__desc { color: var(--text2); font-size: 15.5px; margin: 0; max-width: 52ch; }
.htb .htb-product__go { color: var(--accent); font-weight: 600; font-size: 15px; margin-top: auto; }
.htb .htb-product__go.is-soon { color: var(--muted); }
.htb a.htb-product:hover .htb-product__go .arw { transform: translateX(3px); }
.htb .htb-product__go .arw { display: inline-block; transition: transform .15s ease; }

/* ── News ───────────────────────────────────────────────────────────────── */
.htb .htb-news {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 18px;
  margin-top: 40px;
}
.htb .htb-newsitem__link {
  display: flex;
  flex-direction: column;
  gap: 9px;
  height: 100%;
  padding: 22px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: inherit;
  transition: border-color .2s ease, transform .2s ease;
}
.htb .htb-newsitem__link:hover { border-color: var(--line3); transform: translateY(-2px); }
.htb .htb-newsitem__date {
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}
.htb .htb-newsitem__title { font-family: var(--disp); font-size: 18px; color: var(--text); margin: 0; line-height: 1.3; }
.htb .htb-newsitem__excerpt { color: var(--text2); font-size: 14.5px; margin: 0; }
.htb .htb-newsitem__go { color: var(--accent); font-weight: 600; font-size: 14px; margin-top: auto; padding-top: 6px; }
.htb .htb-news-all { margin-top: 26px; }

/* ── Product screenshots ────────────────────────────────────────────────── */
/* These slots exist only in the WordPress templates, not on the canonical page,
   because they appear or disappear depending on whether a file is present —
   which static HTML cannot express. See ht_beats_shot() in inc/template-tags.php.

   Both rules assume nothing about the image's proportions beyond its width, so
   a re-shot screenshot at a different size cannot break the layout. */

/* Hero: the capture fills the window body, replacing the drawn mock-up. The
   real capture is of the whole application and carries the app's own header,
   so the site's title bar is not drawn at all in that case (--bare) — two
   window frames in one window is the giveaway of a pasted-in screenshot. */
.htb .htb-window .body.htb-window__shot { display: block; min-height: 0; }
.htb .htb-window__shot img { width: 100%; height: auto; display: block; }
.htb .htb-window--bare { min-height: 0; }

/* Gallery: framed image on top, caption beneath. The placeholder it replaces is
   a centred text panel, so almost every property has to be reset. */
.htb .htb-ph--shot {
  padding: 0;
  display: block;
  min-height: 0;
  text-align: start;
  background: var(--surface);
  margin: 0;
}

/* CONTAIN, not cover.
   These are crops of the interface at wildly different proportions — a near
   square Set List panel, a wide export dialog, a small info panel. Cover would
   cut the square ones in half and blow the small ones up past their own
   resolution, which on UI text turns into mush. A fixed frame with the image
   sitting whole inside it keeps four unrelated shapes reading as one set, and
   never shows a pixel that was not captured. */
.htb .htb-ph--shot .shot {
  display: flex;
  align-items: center;
  justify-content: center;
  /* Explicit, because the placeholder this replaces is a centred grid and the
     frame was otherwise shrink-wrapping to the image — a 421px panel floating
     inside a 678px card, off to one side. */
  width: 100%;
  min-height: 180px;
  padding: 20px;
  background: var(--bg2);
  border-block-end: 1px solid var(--line);
}
.htb .htb-ph--shot img {
  display: block;
  /* Fill the column, capped at the file's OWN width (--nat, set inline from the
     real pixel dimensions).
     It used to be width:auto — sized purely from the file. That is fine until
     something upstream decides what to serve by measuring the rendered box:
     the box came from the image, the image came from the guess, and captures
     that measured 436px in a bare browser arrived on the live site at about
     40px. Driving the width from the layout instead makes the picture's size a
     property of the design rather than of whatever bytes turn up, while --nat
     still stops anything being enlarged past its own resolution. */
  width: 100%;
  max-width: min(100%, var(--nat, 100%));
  height: auto;
  /* Height capped in absolute pixels. A percentage max-height was the wrong
     tool twice over: it resolves against neither an aspect-ratio-derived
     height nor an auto-sized grid row, so a tall capture overflowed its frame
     and was clipped both times. A px cap always resolves, and the browser
     narrows the width to match rather than squashing the picture.
     Frames therefore differ in height, which is why the grid is start-aligned
     rather than stretched. */
  max-height: 420px;
  border-radius: 8px;
  /* The captures include both the app's light and its dark theme. A hairline
     and a shadow give each one an edge of its own, so a white panel and a
     charcoal one read as two screenshots rather than as an inconsistency. */
  border: 1px solid var(--line2);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .30);
}
/* Cards keep their own height rather than stretching to the tallest in the
   row — a short capture padded out to match a tall one is mostly empty box. */
.htb .htb-gal { align-items: start; }

/* ── One capture per row ─────────────────────────────────────────────────
   Used once real screenshots exist (.htb-gal--rows, set in the template).
   The 2x2 grid it replaces gave four equal cells to four very unequal
   pictures: the near-square Set List panel shrank to fit its column, and the
   two wide dialogs came out too small to read. A row gives every capture the
   same height and hands the remaining width to the words. */
.htb .htb-gal--rows { grid-template-columns: 1fr; gap: 14px; }

.htb .htb-gal--rows .htb-ph--shot {
  display: grid;
  /* The picture takes the larger half now, and the side it sits on alternates
     down the section (below). A fixed narrow column on the left of every row
     made a tidy stripe and tiny pictures; alternating carries the eye down the
     page and buys the capture real width without the layout going monotonous. */
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  align-items: stretch;
}
.htb .htb-gal--rows .htb-ph--shot .shot {
  height: 100%;
  min-height: 0;
  padding: 18px;
  border-block-end: 0;
  border-inline-end: 1px solid var(--line);
  transition: background .16s ease;
}
.htb .htb-gal--rows .htb-ph--shot .shot:hover { background: var(--tile); }
/* The size cap is expressed entirely as a max-WIDTH, including the height
   limit, which is converted through the picture's own aspect ratio.

   max-height cannot be used here. The image has a definite width (width:100%
   above), and clamping the height of a replaced element with a definite width
   does NOT recompute that width — it squashes the picture. The two near-square
   captures were measurably 33% and 27% wider than they should have been.
   Capping the width and leaving height:auto keeps the ratio by construction.

   --ar is the natural ratio, set inline from the file's real dimensions;
   the fallback of 99 makes the height term irrelevant if it is ever missing,
   so a picture without it is merely uncapped in height rather than distorted. */
.htb .htb-gal--rows .htb-ph--shot img {
  /* The cap every capture is measured against. The middle term of the clamp is
     what binds on a desktop — 27vw is ~346px at 1280 — so that is the number to
     move to resize the whole section at once. */
  --shot-h: clamp(180px, 27vw, 360px);   /* the tallest a capture may be */
  --shot-w: 460px;                        /* and the widest */
  /* --k scales one picture against that shared cap, set inline per capture
     (see page-supersetstudio.php). These four are not equally worth looking
     at: the Set List is what the section is about, and the export run is wide
     and finely printed, so both are given more room than the cap allows, while
     the two info panels read perfectly well at it. Because --k multiplies both
     terms, a picture can only get bigger or smaller — never reshaped. */
  max-width: min(
    100%,
    var(--nat, 100%),
    calc(var(--shot-h) * var(--ar, 99) * var(--k, 1)),
    calc(var(--shot-w) * var(--k, 1))
  );
  max-height: none;
}

/* Every second row mirrors: picture on the right, words on the left, and the
   dividing rule swaps edge with it. */
.htb .htb-gal--rows .htb-ph--shot:nth-child(even) { grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); }
.htb .htb-gal--rows .htb-ph--shot:nth-child(even) .shot {
  order: 2;
  border-inline-end: 0;
  border-inline-start: 1px solid var(--line);
}
.htb .htb-gal--rows .htb-ph--shot:nth-child(even) figcaption { order: 1; }

.htb .htb-gal--rows .htb-ph--shot figcaption {
  border-block-start: 0;
  align-content: center;
  padding: 22px 26px;
  gap: 7px;
}
.htb .htb-gal--rows .htb-ph--shot figcaption b { font-size: 18px; }
.htb .htb-gal--rows .htb-ph--shot figcaption span { font-size: 14.5px; line-height: 1.6; }

/* The magnifier badge — the only thing saying the picture opens. It sits over
   the capture rather than beside it, because a control next to a picture is a
   second thing to read and the picture is already the target. */
.htb .htb-ph--shot .shot { position: relative; }
.htb .htb-ph--shot .shot .zoom {
  position: absolute;
  inset-block-end: 12px;
  inset-inline-end: 12px;
  width: 32px; height: 32px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--line2);
  color: var(--text2);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .30);
  opacity: 0;
  transform: translateY(4px);
  transition: opacity .16s ease, transform .16s ease, color .16s ease;
  pointer-events: none;
}
.htb .htb-ph--shot .shot:hover .zoom,
.htb .htb-ph--shot .shot:focus-visible .zoom { opacity: 1; transform: none; color: var(--accent); }
/* Touch has no hover, so the badge is simply always there. */
@media (hover: none) {
  .htb .htb-ph--shot .shot .zoom { opacity: 1; transform: none; }
}

/* ── The lightbox ────────────────────────────────────────────────────────
   Built by assets/js/lightbox.js. The markup is a plain anchor to the image,
   so without JavaScript the picture is still reachable — this only replaces
   navigating away with showing it in place. */
.htb-lb {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  place-items: center;
  padding: clamp(16px, 4vw, 48px);
  background: rgba(4, 6, 10, .82);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  opacity: 0;
  transition: opacity .18s ease;
}
.htb-lb.is-open { opacity: 1; }
.htb-lb[hidden] { display: none; }

.htb-lb__fig { margin: 0; display: grid; gap: 14px; justify-items: center; max-width: 100%; }
.htb-lb__fig img {
  display: block;
  max-width: 100%;
  /* Room for the caption underneath, so a tall capture never pushes it off. */
  max-height: 82vh;
  width: auto; height: auto;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, .14);
  box-shadow: 0 30px 90px rgba(0, 0, 0, .55);
  transform: scale(.97);
  transition: transform .18s ease;
}
.htb-lb.is-open .htb-lb__fig img { transform: none; }
.htb-lb__fig figcaption {
  color: #E8ECF3;
  font-family: var(--sans, system-ui, sans-serif);
  font-size: 14.5px;
  font-weight: 600;
  text-align: center;
}

.htb-lb__x {
  position: absolute;
  inset-block-start: clamp(12px, 2.5vw, 24px);
  inset-inline-end: clamp(12px, 2.5vw, 24px);
  width: 42px; height: 42px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, .18);
  background: rgba(255, 255, 255, .08);
  color: #fff;
  cursor: pointer;
  transition: background .15s ease, transform .15s ease;
}
.htb-lb__x:hover { background: rgba(255, 255, 255, .16); transform: scale(1.05); }
.htb-lb__x:focus-visible { outline: 2px solid var(--accent, #3B9EFF); outline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  .htb-lb, .htb-lb__fig img, .htb .htb-ph--shot .shot .zoom { transition: none; }
  .htb-lb__fig img { transform: none; }
}

@media (max-width: 760px) {
  /* Stacked: the picture back on top for EVERY row — alternating sides means
     nothing in one column, and without resetting the order the mirrored rows
     would put their caption above the picture and read inconsistently. */
  .htb .htb-gal--rows .htb-ph--shot,
  .htb .htb-gal--rows .htb-ph--shot:nth-child(even) { grid-template-columns: 1fr; }
  .htb .htb-gal--rows .htb-ph--shot .shot,
  .htb .htb-gal--rows .htb-ph--shot:nth-child(even) .shot {
    order: 0;
    border-inline: 0;
    border-block-end: 1px solid var(--line);
  }
  .htb .htb-gal--rows .htb-ph--shot:nth-child(even) figcaption { order: 0; }
  .htb .htb-gal--rows .htb-ph--shot figcaption { padding: 18px 20px; }
}
.htb .htb-ph--shot figcaption {
  display: grid;
  gap: 4px;
  padding: 15px 18px 17px;
  border-block-start: 1px solid var(--line);
}
.htb .htb-ph--shot figcaption b { font-family: var(--disp); font-size: 16px; color: var(--text); letter-spacing: -.015em; }
.htb .htb-ph--shot figcaption span { font-size: 13px; line-height: 1.5; color: var(--text2); }

/* ── "Read the documentation" under the FAQ ─────────────────────────────── */
.htb .htb-faq-more { margin-top: 26px; font-size: 15px; color: var(--text2); }
.htb .htb-faq-more a { color: var(--accent); font-weight: 600; margin-inline-start: 6px; }
.htb .htb-faq-more a:hover { text-decoration: underline; text-underline-offset: 3px; }
.htb .htb-faq-more .arw { transition: transform .18s ease; display: inline-block; }
.htb .htb-faq-more a:hover .arw { transform: translateX(3px); }

/* ── Shared heading ─────────────────────────────────────────────────────── */
.htb .htb-h3 { font-family: var(--disp); font-size: clamp(22px, 2.6vw, 30px); color: var(--text); margin: 0 0 22px; }

/* ── Documentation ──────────────────────────────────────────────────────── */
/* The /docs/ section outgrew a shared stylesheet. Its whole design now lives in
   assets/css/docs.css, loaded ONLY on that page template (see inc/enqueue.php)
   so the front page does not carry a stylesheet it never uses. */

@media (max-width: 600px) {
  .htb .htb-products { grid-template-columns: 1fr; }
}

/* ── Contact form: telling people what is expected ──────────────────────── */

/* Error summary. Leads the form so a rejected submission is met, not hunted. */
.htb .htb-errsum { margin-bottom: 20px; }
.htb .htb-errsum:focus { outline: 2px solid var(--err); outline-offset: 3px; }
.htb .htb-errsum h2 { font-family: var(--disp); font-size: 18px; margin: 0 0 8px; color: var(--text); }
.htb .htb-errsum ul { margin: 0; padding-inline-start: 1.1em; }
.htb .htb-errsum li { color: var(--text2); font-size: 14.5px; margin-top: 4px; }
.htb .htb-errsum a { color: var(--err); text-decoration: underline; text-underline-offset: 2px; }

/* The live count only asserts itself while the message is genuinely short —
   the rest of the time it stays an ordinary hint rather than a nag. */
.htb .htb-counter.is-short { color: var(--warn); font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------------------
   Demo video — click-to-load poster, then the player in its place.

   The poster is a real <img> rather than a CSS background so it can carry alt
   text and be lazy-loaded; the play button sits over it. Once clicked, the
   iframe replaces the contents and fills the same box, so nothing on the page
   moves — the layout is owned by .poster's aspect ratio, not by its children.
   --------------------------------------------------------------------------- */
.htb .htb-video .poster.has-video {
  position: relative;
  overflow: hidden;
  cursor: pointer;
  aspect-ratio: 16 / 9;
  padding: 0;
}

.htb .htb-video .poster.has-video .shot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform .4s ease, filter .4s ease;
}

.htb .htb-video .poster.has-video:hover .shot {
  transform: scale(1.02);
  filter: brightness(.92);
}

/* Keep the button above the image and centred whatever the poster's own
   layout rules were doing before it had an image to sit on. */
.htb .htb-video .poster.has-video .play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
}

.htb .htb-video .poster.is-playing {
  cursor: default;
  background: #000;
}

.htb .htb-video .poster.is-playing .play { display: none; }

.htb .htb-video__frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}
