/* ============================================================
   ONCE & EVER — CATALOG STYLES
   Used by: catalog.html (and the homepage collection preview,
   which reuses .template-card / .template-grid).
   ============================================================ */

/* ---------- CATALOG HERO ---------- */
.catalog-hero {
  position: relative;
  /* The box takes the plate's own ratio, so the whole gallery wall shows —
     nothing is cropped away at any width. */
  aspect-ratio: 1500 / 586;
  display: flex;
  /* Centred left, matching the homepage hero. */
  align-items: center;
  overflow: hidden;
  /* sampled from the plate's own edge, so any letterboxing at a capped
     height is indistinguishable from the picture */
  background: #2c0a10;
}
.catalog-hero-bg {
  position: absolute; inset: 0;
  z-index: 0;
}
/* <picture> is inline and has no height of its own, so height:100% on the
   img below would resolve against nothing and collapse the image. */
.catalog-hero-bg picture {
  display: block;
  width: 100%; height: 100%;
}
.catalog-hero-bg img {
  width: 100%; height: 100%;
  /* contain, not cover: the frames run to the edges of the plate and cover
     would shave them off. The 1.05 scale went for the same reason. */
  object-fit: contain;
  object-position: 50% 50%;
}
/* No wash at any width. On the wide plate the copy sits on the empty damask
   at the left; on tablet and phone it sits on the empty damask at the top.
   All three measure about 15.5:1 against the ivory text unaided, so an
   overlay would only dull the picture. The element stays as a hook. */
.catalog-hero-bg::after {
  content: '';
  position: absolute; inset: 0;
  background: none;
}
.catalog-hero-content {
  position: relative; z-index: 1;
  width: 100%;
  max-width: 1440px;
  margin: 0 auto;
  padding: clamp(2.5rem, 6vw, 4rem) var(--page);
  color: var(--ivory);
}
.catalog-hero-content .eyebrow { color: var(--gold-light); }
.catalog-hero-content .gold-ornament { justify-content: flex-start; }
.catalog-hero-content .section-title { color: var(--ivory); max-width: 18ch; }
.catalog-hero-content .section-title em { color: var(--gold-light); }

/* ---------- FILTER BAR ---------- */
.filter-bar {
  background: var(--warm-white);
  border-bottom: 1px solid var(--border-soft);
  padding: 1.8rem var(--page);
}
.filter-row {
  max-width: 1440px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .9rem 1rem;
}
.filter-row + .filter-row { margin-top: 1rem; }
.filter-pills { display: flex; flex-wrap: wrap; gap: .9rem 1rem; }
.filter-label {
  font-size: .68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .18em;
  color: var(--muted);
  margin-right: .6rem;
  white-space: nowrap;
}
.filter-pill {
  padding: .55rem 1.15rem;
  font-size: .74rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--charcoal);
  border: 1px solid var(--border);
  background: transparent;
  transition: background .35s var(--ease), color .35s var(--ease), border-color .35s var(--ease);
}
.filter-pill:hover { border-color: var(--gold); color: var(--burgundy); }
.filter-pill.is-active {
  background: var(--burgundy);
  border-color: var(--burgundy);
  color: var(--gold-light);
}
.filter-count {
  margin-left: auto;
  font-size: .78rem;
  color: var(--muted);
  letter-spacing: .04em;
}

/* ---------- GRID ---------- */
.template-grid {
  max-width: 1440px;
  margin: 0 auto;
  padding: var(--gap-lg) var(--page);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(2rem, 3vw, 3.2rem) clamp(1.6rem, 2.5vw, 2.6rem);
}
.no-results {
  grid-column: 1 / -1;
  text-align: center;
  padding: 4rem 1rem;
  color: var(--muted);
  font-size: 1rem;
  display: none;
}
.no-results.is-visible { display: block; }

/* ---------- TEMPLATE CARD ---------- */
.template-card {
  display: block;
  transition: transform .45s var(--ease);
}
.tc-media {
  display: block;
  width: 100%;
  padding: 0;
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.template-card:hover { transform: translateY(-4px); }
.template-card.is-hidden { display: none; }

.tc-media {
  position: relative;
  overflow: hidden;
  aspect-ratio: 4 / 5;
  background: var(--charcoal);
  border: 1px solid transparent;
  transition: border-color .45s var(--ease);
}
.template-card:hover .tc-media { border-color: var(--gold); }
.tc-media img {
  width: 100%; height: 100%; object-fit: cover;
  transform: scale(1.001);
  transition: transform .8s var(--ease);
}
.template-card:hover .tc-media img { transform: scale(1.03); }

/* mockup previews get the same restrained lift as photography */
.tc-media .mock { transform: scale(1.001); transition: transform .8s var(--ease); }
.template-card:hover .tc-media .mock { transform: scale(1.03); }

.tc-tag {
  position: absolute;
  top: 1rem; left: 1rem;
  background: rgba(var(--ivory-a), .94);
  color: var(--burgundy);
  font-size: .66rem;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  padding: .4rem .75rem;
  z-index: 2;
}
/* The signature tier. Ivory-on-pale would disappear against the butter
   yellow card, so this one inverts: burgundy ground, gold rule and type. */
.tc-tag-signature {
  background: var(--burgundy);
  color: var(--gold-light);
  box-shadow: inset 0 0 0 1px var(--gold);
}

.tc-overlay {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(59,7,15,0) 45%, rgba(59,7,15,.62) 100%);
  opacity: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: 1.8rem;
  transition: opacity .5s var(--ease);
}
.template-card:hover .tc-overlay { opacity: 1; }
.tc-overlay span {
  color: var(--gold-light);
  font-size: .76rem;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  transform: translateY(8px);
  opacity: 0;
  transition: transform .5s var(--ease) .05s, opacity .5s var(--ease) .05s;
}
.template-card:hover .tc-overlay span { transform: translateY(0); opacity: 1; }

.tc-body { padding: 1.4rem .2rem 0; border-bottom: 1px solid var(--border-soft); padding-bottom: 1.6rem; }
.tc-name { font-family: var(--ff-display); font-size: 1.5rem; color: var(--charcoal); transition: color .35s var(--ease); }
.template-card:hover .tc-name { color: var(--gold); }
.tc-style {
  margin-top: .35rem;
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}
.tc-desc { margin-top: .7rem; font-size: .88rem; color: var(--muted); max-width: 34ch; }
.tc-foot {
  margin-top: 1.1rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.tc-price { font-family: var(--ff-serif); font-size: 1.2rem; color: var(--burgundy); font-weight: 600; }
.tc-name a { color: inherit; }
.tc-foot { gap: 1rem; flex-wrap: wrap; }
.tc-actions { display: flex; align-items: center; gap: 1.1rem; }
.tc-foot .link-arrow { color: var(--muted); }
.template-card:hover .tc-foot .link-arrow { color: var(--gold); }
.template-card:hover .tc-foot .link-arrow .arrow { transform: translateX(6px); }

/* ---------- CARD SAVE (heart) ---------- */
/* A heart that fills rather than a button that changes colour — the
   state has to be legible at a glance across a grid of six. */
.tc-save {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  padding: .35rem 0;
  font-family: var(--ff-sub);
  font-size: .66rem;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted);
  background: none;
  border: 0;
  transition: color .3s var(--ease);
}

.tc-save-heart {
  width: 16px;
  height: 16px;
  fill: none;
  color: var(--muted);
  transition: fill .35s var(--ease), color .35s var(--ease), transform .35s var(--ease);
}

.tc-save:hover {
  color: var(--burgundy);
}

.tc-save:hover .tc-save-heart {
  color: var(--burgundy);
  transform: scale(1.08);
}

.tc-save.is-saved {
  color: var(--burgundy);
}

.tc-save.is-saved .tc-save-heart {
  color: var(--burgundy);
  fill: var(--burgundy);
}

/* ---------- MINI ACTION (save / shortlist) ---------- */
.btn-mini {
  padding: .5rem .95rem;
  font-size: .66rem;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--burgundy);
  border: 1px solid var(--border);
  background: transparent;
  transition: background .35s var(--ease), color .35s var(--ease), border-color .35s var(--ease);
  white-space: nowrap;
}
.btn-mini:hover { border-color: var(--gold); color: var(--gold); }
.btn-mini.is-saved {
  background: var(--burgundy);
  border-color: var(--burgundy);
  color: var(--gold-light);
}

/* ---------- QUICK VIEW MODAL ---------- */
.qv {
  position: fixed;
  inset: 0;
  z-index: 900;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(1rem, 4vw, 3rem);
  opacity: 0;
  visibility: hidden;
  transition: opacity .4s var(--ease), visibility 0s .4s;
}
.qv.is-open { opacity: 1; visibility: visible; transition: opacity .4s var(--ease), visibility 0s; }

.qv-backdrop {
  position: absolute; inset: 0;
  background: rgba(37, 35, 35, .72);
  backdrop-filter: blur(2px);
}

.qv-panel {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1.05fr .95fr;
  /* minmax(0,1fr) pins the row to the panel's capped height so .qv-body
     scrolls internally instead of the panel clipping its last rows. */
  grid-template-rows: minmax(0, 1fr);
  width: min(1060px, 100%);
  max-height: 88vh;
  background: var(--warm-white);
  border: 1px solid var(--gold-border);
  box-shadow: 0 40px 90px -40px rgba(0, 0, 0, .6);
  overflow: hidden;
  transform: translateY(14px);
  transition: transform .5s var(--ease);
}
.qv.is-open .qv-panel { transform: translateY(0); }

.qv-close {
  position: absolute;
  top: .9rem; right: .9rem;
  z-index: 3;
  width: 38px; height: 38px;
  display: flex; align-items: center; justify-content: center;
  background: var(--warm-white);
  border: 1px solid var(--border);
  color: var(--charcoal);
  transition: background .3s var(--ease), color .3s var(--ease), border-color .3s var(--ease);
}
.qv-close svg { width: 16px; height: 16px; }
.qv-close:hover { background: var(--burgundy); border-color: var(--gold); color: var(--gold-light); }

/* Grid children must be allowed to shrink, or .qv-body's own scrolling
   never kicks in and the panel clips its last rows. */
.qv-gallery, .qv-body { min-height: 0; }

/* Gallery */
.qv-gallery { position: relative; overflow: hidden; background: var(--warm-white); }
.qv-track { display: flex; height: 100%; transition: transform .6s var(--ease); }
.qv-track > * { flex: 0 0 100%; width: 100%; height: 100%; }
/* contain, not cover — these are full page layouts and cropping them
   hides the very thing the customer is buying.
   The inset lives on the image, not on .qv-gallery: the gallery is what
   clips the track, and padding there would let the neighbouring slides
   show through the padding band instead of being hidden. */
.qv-track img {
  object-fit: contain;
  padding: clamp(1.8rem, 3.5vw, 3rem);
}
.qv-nav {
  position: absolute;
  /* against the padded box, so they sit on the artwork rather than the mat */
  top: 50%; transform: translateY(-50%);
  width: 40px; height: 40px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(var(--ivory-a), .9);
  color: var(--burgundy);
  border: 1px solid var(--gold-border);
  font-size: .9rem;
  transition: background .3s var(--ease), color .3s var(--ease);
}
.qv-nav:hover { background: var(--gold); color: var(--deep-burgundy); }
.qv-prev { left: 1rem; }
.qv-next { right: 1rem; }
.qv-dots {
  position: absolute;
  left: 0; right: 0; bottom: 1.1rem;
  display: flex; justify-content: center; gap: .5rem;
}
.qv-dots span {
  width: 6px; height: 6px; border-radius: 50%;
  background: rgba(var(--ivory-a), .45);
  transition: background .3s var(--ease), transform .3s var(--ease);
}
.qv-dots span.is-active { background: var(--gold); transform: scale(1.3); }
.qv-gallery.is-single .qv-nav, .qv-gallery.is-single .qv-dots { display: none; }

/* Body */
.qv-body {
  padding: clamp(1.8rem, 3.5vw, 3rem);
  overflow-y: auto;
}
.qv-index {
  font-family: var(--ff-serif);
  font-size: .95rem;
  letter-spacing: .08em;
  color: var(--muted);
}
.qv-title {
  font-family: var(--ff-display);
  font-size: clamp(1.8rem, 3vw, 2.4rem);
  line-height: 1.1;
  margin: .4rem 0 .5rem;
  color: var(--charcoal);
}
.qv-style {
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--burgundy);
}
.qv-desc { margin-top: 1.1rem; color: var(--muted); font-size: .95rem; }

/* The included list runs to eleven items. Beside the swatches it had a
   220px column, six of the eleven wrapped, and the price and Choose This
   Design button were pushed below the modal's fold. Full width instead,
   with the swatches stacked underneath: every item fits on one line and
   the CTA stays in view. */
.qv-meta {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
  margin: 1.1rem 0;
  padding: 1.1rem 0;
  border-top: 1px solid var(--gold-border);
  border-bottom: 1px solid var(--gold-border);
}
.qv-meta h4 {
  margin-bottom: .55rem;
  font-size: .66rem;
  font-weight: 700;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--muted);
}
.qv-meta ul { display: flex; flex-direction: column; gap: .25rem; }
.qv-meta li { font-size: .9rem; color: var(--charcoal); }
.qv-meta li::before { content: '— '; color: var(--gold); }

.qv-swatches { display: flex; flex-wrap: wrap; gap: .6rem; }
.qv-swatch {
  width: 26px; height: 26px;
  border-radius: 50%;
  border: 1px solid var(--border);
  display: block;
}
.qv-swatch.is-active { box-shadow: 0 0 0 2px var(--warm-white), 0 0 0 3px var(--gold); }

.qv-price {
  font-family: var(--ff-serif);
  font-size: 1.8rem;
  font-weight: 600;
  color: var(--burgundy);
  margin-bottom: 1.4rem;
}
.qv-actions { display: flex; align-items: center; flex-wrap: wrap; gap: .9rem; }
.qv-save { padding: .85rem 1.2rem; font-size: .68rem; }
.qv-full { margin-top: 1.4rem; display: inline-flex; }

@media (max-width: 899px) {
  /* Stacked: the whole panel scrolls rather than the body pane. */
  .qv-panel { grid-template-columns: 1fr; grid-template-rows: auto auto; max-height: 92vh; overflow-y: auto; }
  .qv-gallery { aspect-ratio: 4/3; }
  .qv-body { overflow: visible; }
}
@media (max-width: 640px) {
  .qv-meta { grid-template-columns: 1fr; gap: 1.2rem; }
  .qv { padding: 0; }
  .qv-panel { max-height: 100vh; height: 100%; border: none; }
}

@media (max-width: 1199px) {
  .template-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
  .template-grid { grid-template-columns: 1fr; }

  /* Phones: the pills run off the side of the screen rather than
     stacking into four ragged lines. The label sits above so the track
     gets the full width, and the track bleeds to both edges — a pill cut
     off at the right is what tells you there is more to swipe to. */
  .filter-row { display: block; }
  .filter-label { display: block; margin: 0 0 .7rem; }
  .filter-pills {
    flex-wrap: nowrap;
    gap: .6rem;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
    margin: 0 calc(var(--page) * -1);
    padding: 0 var(--page);
    /* the bar is only 2.4rem tall — a scrollbar would crowd it */
    scrollbar-width: none;
  }
  .filter-pills::-webkit-scrollbar { display: none; }
  .filter-pill { flex: 0 0 auto; }
  .filter-count { display: block; margin: .9rem 0 0; }
}

/* Tablet and phone are served the portrait plate, so the box takes its
   ratio instead. Capped, because 9:16 at 1023px wide is 1818px tall — past
   that the picture is centred on its own colour rather than cropped. */
/* Each breakpoint takes the ratio of the plate it is served, so the whole
   picture shows and nothing is trimmed. Width has to be stated: with only
   aspect-ratio and a height cap the box keeps its ratio by narrowing, and
   the hero stops being full-bleed. */
@media (min-width: 768px) and (max-width: 1023px) {
  .catalog-hero {
    width: 100%;
    aspect-ratio: 1086 / 1448;
    max-height: 100vh;
  }
}

@media (max-width: 767px) {
  .catalog-hero {
    width: 100%;
    aspect-ratio: 900 / 1600;
    max-height: 100vh;
  }
}

/* On the portrait plate the headline crosses the framed suites, which are
   the brightest thing in the picture — bare, it measures 2.5:1. This is the
   least that carries it: a soft band over the copy only, easing away before
   the crest and the wordmark so neither is dulled. */
/* Tablet and phone: the plates are portrait and their artwork fills the
   middle, so the copy moves to the top of the frame and centres — at the
   left, vertically centred, it sat across the crest. */
@media (max-width: 1023px) {
  .catalog-hero {
    align-items: flex-start;
  }

  .catalog-hero-content {
    text-align: center;
    padding-top: clamp(2rem, 5vw, 3.2rem);
  }

  .catalog-hero-content .gold-ornament {
    justify-content: center;
  }

  .catalog-hero-content .section-title {
    max-width: 22ch;
    margin-inline: auto;
  }
}

