/* =========================================================================
   HARI'S CARPETS — styles
   Palette taken from the shop's own logo: cornflower blue wordmark sitting
   on a double lemon-yellow rule. That double rule is reused all over the
   site as the house divider (.rule / section underlines).
   Type: Jost (display caps, echoes the logo) · Karla (UI + body)
   ========================================================================= */

:root {
  /* --- brand, straight off the logo --- */
  --blue:       #3268b3;   /* wordmark blue */
  --blue-deep:  #1e4174;   /* dark bands, footer */
  --blue-ink:   #14294a;   /* headings on white */
  --blue-tint:  #eaf1fa;   /* washed panels */
  --yellow:     #f5e04e;   /* the double rule */
  --yellow-deep:#e0c521;   /* yellow on white text/underlines */

  /* --- neutrals: clean white shop, not a beige mood board --- */
  --paper:      #ffffff;
  --off:        #f6f7f9;   /* alternating section */
  --line:       #dfe3ea;
  --ink:        #1c2430;   /* body text */
  --ink-soft:   #46505f;
  --muted:      #6b7686;

  --shadow: 0 1px 2px rgba(20,41,74,.06), 0 18px 40px -24px rgba(20,41,74,.35);

  --wrap: 1200px;
  --gap: clamp(16px, 3vw, 32px);

  --f-display: "Jost", "Gill Sans", "Gill Sans MT", Optima, sans-serif;
  --f-ui: "Karla", system-ui, -apple-system, sans-serif;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--f-ui);
  font-size: 17px;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; display: block; }
a { color: inherit; }
button { font-family: inherit; cursor: pointer; }

.wrap { width: min(var(--wrap), 100% - 2*var(--gap)); margin-inline: auto; }
.wrap-wide { width: min(1440px, 100% - 2*var(--gap)); margin-inline: auto; }

h1, h2, h3 {
  font-family: var(--f-display); font-weight: 500; line-height: 1.1;
  color: var(--blue-ink); margin: 0;
}
h2 { font-size: clamp(27px, 3.4vw, 40px); }
.lede { font-size: clamp(17px, 1.3vw, 19px); color: var(--ink-soft); }

/* THE DOUBLE RULE — the shop's mark, drawn as two yellow lines with a gap.
   There were three versions of this: .rule at 62×9 with a 3px gap, the header
   .br with no width at all, and the footer .br at 260px behind an adjacent-
   sibling selector that lost every property if anything moved between it and
   the wordmark. The header one was the visible fault: with no width, its length
   was whatever the wider of the wordmark and the line beneath it happened to
   measure, and it changed as the wordmark's clamp() scaled with the viewport.

   One set of proportions now, in one place. Everything below only says how wide
   and where — never how it is drawn. */
.rule, .br {
  display: block; box-sizing: border-box; height: 9px;
  border-top: 3px solid var(--yellow); border-bottom: 3px solid var(--yellow);
}
.rule { width: 62px; margin: 0 0 18px; }
.br { width: 100%; max-width: 100%; }
.rule.on-dark { border-color: var(--yellow); }
.rule.centred { margin-inline: auto; }

/* small caps label — replaces the old mono "eyebrow" */
.kicker {
  font-family: var(--f-display); font-size: 13px; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase; color: var(--blue);
  margin: 0 0 10px;
}
.band .kicker, .hero .kicker { color: var(--yellow); }

/* ---- buttons: squared off, not pills ---- */
.btn {
  display: inline-flex; align-items: center; gap: .55em;
  padding: 14px 26px; border-radius: 2px;
  font-family: var(--f-display); font-size: 15px; font-weight: 500;
  letter-spacing: .06em; text-transform: uppercase; text-decoration: none;
  border: 2px solid transparent; transition: background .18s, color .18s, border-color .18s;
}
.btn-solid { background: var(--blue); color: #fff; }
.btn-solid:hover { background: var(--blue-deep); }
.btn-yellow { background: var(--yellow); color: var(--blue-ink); }
.btn-yellow:hover { background: #fff; }
.btn-ghost { background: transparent; color: var(--blue); border-color: var(--blue); }
.btn-ghost:hover { background: var(--blue); color: #fff; }
.btn-ghost.on-dark { color: #fff; border-color: rgba(255,255,255,.6); }
.btn-ghost.on-dark:hover { background: #fff; color: var(--blue-ink); border-color: #fff; }
.btn-sm { padding: 10px 17px; font-size: 13px; }

/* arrow link, Westex-style */
.arrow-link {
  font-family: var(--f-display); font-size: 15px; font-weight: 500; letter-spacing: .06em;
  text-transform: uppercase; text-decoration: none; color: var(--blue);
  display: inline-flex; align-items: center; gap: 10px; padding-bottom: 3px;
  border-bottom: 2px solid var(--yellow);
}
.arrow-link:hover { color: var(--blue-deep); border-color: var(--blue); }
.arrow-link.on-dark { color: #fff; }
.arrow-link.on-dark:hover { border-color: #fff; }

/* =========================================================================
   PHOTO SLOTS
   Every photo sits in .ph. Until the real jpg lands in /assets the woven
   fallback texture underneath shows through, so nothing renders broken.
   ========================================================================= */
.ph {
  position: relative; overflow: hidden; background: #b9c3d1;
  /* a woven-pile suggestion rather than a "missing image" box */
  background-image:
    repeating-linear-gradient(115deg, rgba(255,255,255,.16) 0 3px, transparent 3px 7px),
    repeating-linear-gradient(65deg, rgba(20,41,74,.13) 0 3px, transparent 3px 8px),
    radial-gradient(120% 90% at 25% 15%, rgba(255,255,255,.5), transparent 60%),
    linear-gradient(160deg, #d3dae5 0%, #9fabbd 100%);
}
.ph img { width: 100%; height: 100%; object-fit: cover; position: relative; z-index: 1; }
.ph.r-16-9 { aspect-ratio: 16/9; }
.ph.r-4-3  { aspect-ratio: 4/3; }
.ph.r-3-2  { aspect-ratio: 3/2; }
.ph.r-1-1  { aspect-ratio: 1/1; }
.ph.r-3-4  { aspect-ratio: 3/4; }
/* caption shown only while the photo is missing */
.ph::after {
  content: attr(data-ph); position: absolute; inset: auto 0 0 0; z-index: 0;
  font-family: var(--f-display); font-size: 11px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--blue-deep); opacity: .7;
  padding: 10px 12px; text-align: center;
}

/* =========================================================================
   HEADER
   ========================================================================= */
.site-header { position: sticky; top: 0; z-index: 50; background: var(--paper); border-bottom: 1px solid var(--line); }
/* the header shares the body's 1200px container so the wordmark lines up with
   the page content rather than sitting outboard of it */
.site-header .wrap { display: flex; align-items: center; gap: 22px; height: 76px; }

.brand-mark { text-decoration: none; display: block; line-height: 1; flex: none; white-space: nowrap; }
.brand-mark .bn {
  font-family: var(--f-display); font-size: clamp(20px, 2vw, 25px); font-weight: 500;
  letter-spacing: .07em; text-transform: uppercase; color: var(--blue); display: block;
}
/* The logo's twin yellow rules, rebuilt in CSS. See the .br base rule near the
   top: the header only sets where it sits, never how it is drawn. */
.brand-mark .br { margin-top: 5px; }
.brand-mark small {
  display: block; margin-top: 6px; font-size: 10.5px; letter-spacing: .17em;
  text-transform: uppercase; color: var(--muted);
}

.nav { display: flex; gap: 26px; margin-left: auto; }
.nav a {
  text-decoration: none; color: var(--ink-soft); font-size: 15px; white-space: nowrap;
  font-family: var(--f-display); letter-spacing: .05em; text-transform: uppercase;
  padding: 6px 0; border-bottom: 3px solid transparent; transition: color .18s, border-color .18s;
}
.nav a:hover, .nav a.active { color: var(--blue); border-color: var(--yellow); }
.header-cta { margin-left: 8px; white-space: nowrap; flex: none; }
.nav-toggle { display: none; margin-left: auto; background: none; border: 0; font-size: 22px; color: var(--blue); }

/* favourites pill */
.fav-btn {
  margin-left: 6px; white-space: nowrap; flex: none; background: var(--blue-tint); color: var(--blue-deep); text-decoration: none;
  border: 1px solid transparent; border-radius: 2px; padding: 10px 14px;
  font-family: var(--f-display); font-size: 13px; letter-spacing: .06em; text-transform: uppercase;
  display: inline-flex; align-items: center; gap: 7px; transition: background .18s, color .18s;
}
.fav-btn:hover { background: var(--blue); color: #fff; }
.fav-btn.has { background: var(--yellow); color: var(--blue-ink); }
.fav-btn .count { background: var(--blue); color: #fff; border-radius: 2px; padding: 1px 7px; font-size: 12px; }
.fav-btn.has .count { background: var(--blue-ink); }

/* =========================================================================
   HERO — full-bleed photograph, text over image
   ========================================================================= */
.hero { position: relative; min-height: clamp(460px, 74vh, 720px); display: flex; align-items: flex-end; }
.hero .ph { position: absolute; inset: 0; aspect-ratio: auto; }
.hero .ph::after { display: none; }
/* deliberately NOT called .scrim — the tray's fixed .scrim sets opacity:0,
   which leaks onto any element sharing that class name */
.hero-scrim {
  position: absolute; inset: 0; z-index: 2;
  background:
    linear-gradient(90deg, rgba(10,22,42,.86) 0%, rgba(10,22,42,.62) 46%, rgba(10,22,42,.18) 100%),
    linear-gradient(0deg, rgba(10,22,42,.55) 0%, transparent 55%);
}
.hero .wrap { position: relative; z-index: 3; padding: 0 0 clamp(38px, 6vw, 74px); color: #fff; }
.hero h1 { color: #fff; font-size: clamp(36px, 5.6vw, 66px); max-width: 15ch; font-weight: 400; }
.hero p { max-width: 46ch; margin: 18px 0 30px; font-size: clamp(16px, 1.4vw, 19px); color: rgba(255,255,255,.9); }
.hero-cta { display: flex; gap: 12px; flex-wrap: wrap; }

/* The banner carousel. Slides are stacked and cross-faded rather than slid
   sideways: nothing under the headline moves, so the words stay readable the
   whole way through. Only the slide marked .on is visible, and the first one
   carries that class in the markup — with the script blocked or a single
   picture set, this is simply a photograph. */
.hero-slides { position: absolute; inset: 0; z-index: 1; }
.hero-slide { position: absolute; inset: 0; opacity: 0; transition: opacity .9s ease; }
.hero-slide.on { opacity: 1; }
/* an inner .wrap so the dots line up with the right-hand edge of the copy
   rather than the edge of the screen */
.hero-dots { position: absolute; z-index: 4; left: 0; right: 0; bottom: clamp(16px, 2.4vw, 26px); pointer-events: none; }
.hero-dots .wrap { display: flex; justify-content: flex-end; gap: 9px; }
.hero-dots button {
  pointer-events: auto; width: 10px; height: 10px; padding: 0; border-radius: 50%;
  border: 1px solid rgba(255,255,255,.85); background: transparent; cursor: pointer;
  transition: background .25s ease, transform .25s ease;
}
.hero-dots button:hover { background: rgba(255,255,255,.55); }
.hero-dots button.on { background: var(--yellow); border-color: var(--yellow); transform: scale(1.15); }
.hero-dots button:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) { .hero-slide { transition: none; } }

/* thin blue proof bar under the hero */
.proof { background: var(--blue-deep); color: #fff; }
.proof .wrap { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 10px 26px; padding: 18px 0; }
.proof span { display: flex; align-items: center; gap: 10px; font-size: 15px; }
.proof span::before { content: ""; width: 9px; height: 9px; background: var(--yellow); flex: none; }
.proof b { font-family: var(--f-display); font-weight: 500; letter-spacing: .03em; }

/* =========================================================================
   SECTIONS
   ========================================================================= */
section { padding: clamp(42px, 6vw, 82px) 0; }
.sec-alt { background: var(--off); }
.section-head { display: flex; align-items: end; justify-content: space-between; gap: 22px; margin-bottom: 34px; flex-wrap: wrap; }
.section-head p { color: var(--muted); margin: 10px 0 0; max-width: 56ch; }

.grid { display: grid; gap: 22px; }
.grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
.grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
.grid.cols-4 { grid-template-columns: repeat(4, 1fr); }

/* --- category tiles: photo-led, first one runs double width --- */
.cat-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 20px; }
/* row 1 = two wide tiles (3+3), row 2 = three (2+2+2) */
.cat-tile { grid-column: span 2; text-decoration: none; color: inherit; display: block; position: relative; overflow: hidden; }
.cat-tile.lead { grid-column: span 3; }
.cat-tile .ph { aspect-ratio: 4/3; }
.cat-tile.lead .ph { aspect-ratio: 16/10; }
.cat-tile .ph img { transition: transform .5s ease; }
.cat-tile:hover .ph img { transform: scale(1.045); }
.cat-cap { position: absolute; inset: auto 0 0 0; z-index: 3; padding: 22px;
  background: linear-gradient(transparent, rgba(12,26,48,.86)); color: #fff; }
.cat-cap h3 { color: #fff; font-size: clamp(20px, 2vw, 27px); font-weight: 400; }
.cat-cap p { margin: 6px 0 0; font-size: 14.5px; color: rgba(255,255,255,.85); max-width: 40ch; }
.cat-cap .go { display: inline-block; margin-top: 12px; font-family: var(--f-display); font-size: 13px;
  letter-spacing: .1em; text-transform: uppercase; border-bottom: 2px solid var(--yellow); padding-bottom: 2px; }

/* --- editorial split: photo one side, copy the other --- */
.split { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(28px, 5vw, 64px); align-items: center; }
.split.flip .split-media { order: 2; }
/* Centring is right for a short block beside a picture. Where the column
   carries the heading *and* paragraphs — About — the two have to start on the
   same line, or the text floats in the middle of the photograph. */
.split.split-top { align-items: start; }
.split-copy .prose { margin-top: 18px; }
.split-copy .prose p:last-child { margin-bottom: 0; }
.split-copy h2 { margin-bottom: 16px; }
.split-copy p { color: var(--ink-soft); }
/* a small photo tucked over the corner of the big one */
.stack { position: relative; }
.stack .inset { position: absolute; right: -28px; bottom: -32px; width: 45%; border: 8px solid #fff; box-shadow: var(--shadow); }

/* --- range / brand cards, now photo-topped --- */
.card { background: var(--paper); border: 1px solid var(--line); text-decoration: none; color: inherit;
  display: flex; flex-direction: column; transition: box-shadow .2s, transform .2s, border-color .2s; }
.card:hover { transform: translateY(-3px); box-shadow: var(--shadow); border-color: var(--blue); }
.card .ph img { transition: transform .5s ease; }
.card:hover .ph img { transform: scale(1.04); }
.card-body { padding: 18px 18px 22px; display: flex; flex-direction: column; gap: 7px; flex: 1; }
.card-body .k { font-family: var(--f-display); font-size: 12px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.card-body h3 { font-size: 22px; }
.card-body .desc { color: var(--ink-soft); font-size: 15px; }
.card-foot { margin-top: auto; padding-top: 14px; font-family: var(--f-display); font-size: 13.5px;
  letter-spacing: .08em; text-transform: uppercase; color: var(--blue); }
.card-swatches { display: grid; grid-template-columns: repeat(8, 1fr); height: 8px; }
.tag-placeholder { align-self: flex-start; font-family: var(--f-display); font-size: 10.5px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--blue-deep); background: var(--yellow); padding: 3px 8px; }

/* --- gallery strip --- */
.strip { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
.strip .ph { aspect-ratio: 3/4; }
.strip .ph img { transition: transform .5s ease; }
.strip .ph:hover img { transform: scale(1.05); }

/* =========================================================================
   DARK BLUE BANDS
   ========================================================================= */
.band { background: var(--blue-deep); color: #fff; }
.band h2, .band h3 { color: #fff; }
.band .section-head p { color: rgba(255,255,255,.75); }
.band .lede { color: rgba(255,255,255,.85); }
.svc { border-top: 3px solid var(--yellow); padding: 18px 0 0; }
.svc h3 { font-size: 19px; margin-bottom: 6px; font-weight: 500; }
.svc p { margin: 0; font-size: 15px; color: rgba(255,255,255,.78); }
/* services on a light background */
.sec-alt .svc, section:not(.band) .svc { border-color: var(--yellow-deep); }
.sec-alt .svc h3, section:not(.band) .svc h3 { color: var(--blue-ink); }
.sec-alt .svc p, section:not(.band) .svc p { color: var(--ink-soft); }

/* =========================================================================
   RANGE DETAIL
   ========================================================================= */
.crumbs { font-family: var(--f-display); font-size: 12.5px; letter-spacing: .09em; text-transform: uppercase;
  color: var(--muted); padding-top: 26px; }
.crumbs a { color: var(--muted); text-decoration: none; }
.crumbs a:hover { color: var(--blue); }

.range-hero { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(24px, 4vw, 52px); align-items: start; padding-top: 22px; }
.range-hero h1 { font-size: clamp(32px, 4.6vw, 52px); margin-bottom: 12px; font-weight: 400; }
.spec { list-style: none; margin: 24px 0 0; padding: 22px 0 0; border-top: 1px solid var(--line);
  display: grid; grid-template-columns: 1fr 1fr; gap: 15px 24px; }
.spec div { font-size: 14.5px; }
.spec .sl { font-family: var(--f-display); font-size: 11.5px; letter-spacing: .11em; text-transform: uppercase;
  color: var(--muted); display: block; margin-bottom: 3px; }

.colours-head { display: flex; align-items: baseline; justify-content: space-between; margin: 8px 0 20px; flex-wrap: wrap; gap: 10px; }
.colours-head .hint { color: var(--muted); font-size: 14px; }
.swatch-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); gap: 14px; }
.swatch { border: 1px solid var(--line); overflow: hidden; background: var(--paper); text-align: left; padding: 0;
  transition: transform .15s, box-shadow .15s, border-color .15s; position: relative; }
.swatch:hover { transform: translateY(-2px); box-shadow: var(--shadow); }
.swatch .col { height: 76px; }
.swatch .meta { padding: 9px 10px 11px; }
.swatch .cn { font-size: 13.5px; font-weight: 600; }
.swatch .cc { font-size: 12px; color: var(--muted); }
/* static (non-selectable) colour tiles when the sample tray is switched off */
.swatch.static { cursor: default; }
.swatch.static:hover { transform: none; box-shadow: none; }
.swatch.selected { border-color: var(--blue); box-shadow: 0 0 0 2px var(--blue); }
.swatch .pick { position: absolute; top: 8px; right: 8px; width: 22px; height: 22px; border-radius: 50%;
  background: rgba(255,255,255,.9); color: var(--blue); display: grid; place-items: center; font-size: 13px;
  opacity: 0; transition: opacity .15s; }
.swatch:hover .pick, .swatch.selected .pick { opacity: 1; }
.swatch.selected .pick { background: var(--blue); color: #fff; }

/* =========================================================================
   FORMS / CONTACT
   ========================================================================= */
.contact-grid { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(28px, 5vw, 56px); }
.field { margin-bottom: 16px; }
.field label { display: block; font-family: var(--f-display); font-size: 12.5px; letter-spacing: .09em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 6px; }
.field input, .field textarea, .field select {
  width: 100%; padding: 12px 14px; border: 1px solid var(--line); border-radius: 2px;
  background: var(--paper); font: inherit; color: var(--ink);
}
.field input:focus, .field textarea:focus, .field select:focus { outline: 2px solid var(--blue); outline-offset: 1px; border-color: transparent; }
.info-row { display: flex; gap: 12px; padding: 14px 0; border-bottom: 1px solid var(--line); }
.info-row .il { font-family: var(--f-display); font-size: 11.5px; letter-spacing: .11em; text-transform: uppercase;
  color: var(--muted); width: 92px; flex: none; padding-top: 4px; }
.info-row a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--line); }
.info-row a:hover { border-color: var(--blue); }
.form-note { font-size: 13px; color: var(--muted); margin-top: 10px; }
.form-note.bad { color: #a02020; }
/* The honeypot. Taken out of the layout and out of the accessibility tree, but
   still a real text input a bot will happily fill in. Not display:none and not
   type=hidden — both are what the scripts look for. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.form-card { background: var(--off); border: 1px solid var(--line); padding: 24px; }
.socials { display: flex; gap: 16px; flex-wrap: wrap; }
.socials a { color: var(--blue); text-decoration: none; display: inline-flex; align-items: center; gap: 6px; }
.socials a:hover { text-decoration: underline; }
/* sized off the surrounding text so the mark and the word stay in proportion
   whatever the font is doing */
.si { width: 1.15em; height: 1.15em; fill: currentColor; flex: none; }

.prose { max-width: 66ch; }
.prose p { margin: 0 0 18px; color: var(--ink-soft); font-size: 17.5px; line-height: 1.72; }

/* build note for whoever picks this up next */
.ph-note { font-size: 13.5px; color: var(--blue-deep); background: var(--blue-tint);
  border-left: 4px solid var(--yellow); padding: 12px 16px; margin: 0 0 26px; }

/* =========================================================================
   FOOTER
   ========================================================================= */
.site-footer { background: var(--blue-deep); color: #fff; padding: 58px 0 30px; }
.site-footer .wrap { display: grid; grid-template-columns: 2fr 1fr 1fr; gap: 32px; }
.site-footer h4 { font-family: var(--f-display); font-size: 12px; letter-spacing: .15em; text-transform: uppercase;
  color: var(--yellow); margin: 0 0 14px; font-weight: 500; }
.site-footer a { color: #fff; text-decoration: none; opacity: .85; display: block; padding: 4px 0; }
.site-footer a:hover { opacity: 1; }
.site-footer .fbrand { font-family: var(--f-display); font-size: 25px; letter-spacing: .07em; text-transform: uppercase; }
/* The rule is as wide as the wordmark, and it has to stay that way in whatever
   font actually loads.

   It was a fixed 232px, measured off the wordmark — but measured in this
   container, where Jost is blocked and a fallback was rendering. On the live
   site Jost is narrower, so the rule hung out past the end of the words. A
   number in pixels was the wrong tool: the width belongs to the text.

   .fbrand-block shrinks to the wordmark and .br fills it, which is exactly what
   the header already does with .brand-mark. Nothing to keep in step. */
.site-footer .fbrand-block { width: fit-content; max-width: 100%; }
.site-footer .br { margin-top: 8px; }
.site-footer .fine { grid-column: 1 / -1; border-top: 1px solid rgba(255,255,255,.16); margin-top: 26px;
  padding-top: 18px; font-size: 13px; color: rgba(255,255,255,.7); display: flex; justify-content: space-between;
  flex-wrap: wrap; gap: 10px; }

/* =========================================================================
   RESPONSIVE
   ========================================================================= */
@media (max-width: 980px) {
  .split, .range-hero, .contact-grid { grid-template-columns: 1fr; }
  .split.flip .split-media { order: 0; }
  .stack .inset { display: none; }
  .cat-grid { grid-template-columns: repeat(4, 1fr); }
  .cat-tile, .cat-tile.lead { grid-column: span 2; }
  .cat-tile.lead .ph { aspect-ratio: 4/3; }
  .grid.cols-3, .grid.cols-4 { grid-template-columns: repeat(2, 1fr); }
  .site-footer .wrap { grid-template-columns: 1fr 1fr; }
  .strip { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 620px) {
  .nav, .header-cta { display: none; }
  .nav.show { display: flex; position: absolute; top: 76px; left: 0; right: 0; flex-direction: column;
    background: var(--paper); border-bottom: 3px solid var(--yellow); padding: 12px var(--gap); gap: 0; }
  .nav.show a { padding: 11px 0; border-bottom: 1px solid var(--line); }
  .nav-toggle { display: block; }
  .cat-grid, .grid.cols-3, .grid.cols-2, .grid.cols-4, .spec { grid-template-columns: 1fr; }
  .cat-tile, .cat-tile.lead { grid-column: span 1; }
  .site-footer .wrap { grid-template-columns: 1fr; }
  .proof .wrap { flex-direction: column; gap: 10px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; scroll-behavior: auto; }
}


/* =========================================================================
   STYLE TILES — "what sort of carpet are you after?"
   Big photographs, because this is the step where somebody is deciding by
   eye. The tiles come before the filters on the page for the same reason:
   a picture of a loop pile explains "loop pile" faster than the words do.
   ========================================================================= */
.styles { margin: 0 0 42px; }
.styles-head {
  font-family: var(--f-display); font-size: clamp(19px, 2.2vw, 24px);
  font-weight: 500; margin: 0 0 20px;
}
/* 320px floors this at three across in the 1200px wrap. Three is deliberate:
   the carpet styles number five, and 3+2 sits better than a row of four with
   one stranded underneath. */
.style-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 20px; }

.style-tile {
  display: flex; flex-direction: column; text-decoration: none; color: inherit;
  border: 1px solid var(--line); background: var(--paper);
  transition: transform .22s ease, box-shadow .22s ease, border-color .22s ease;
}
.style-tile:hover { transform: translateY(-3px); box-shadow: var(--shadow); border-color: var(--blue); }
.style-ph { position: relative; aspect-ratio: 4 / 3; overflow: hidden; background: var(--off); }
.style-ph img { width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform .5s ease; }
.style-tile:hover .style-ph img { transform: scale(1.05); }

/* a strip of the actual colours in the style, so the tile promises stock and
   not just a mood */
.style-swatches { position: absolute; left: 0; right: 0; bottom: 0; display: flex; height: 10px; }
.style-swatches span { flex: 1; }

.style-cap { padding: 16px 18px 18px; display: flex; flex-direction: column; gap: 7px; flex: 1; }
.style-cap h3 { font-family: var(--f-display); font-size: 19px; font-weight: 500; margin: 0; }
.style-cap p { margin: 0; font-size: 14px; color: var(--muted); line-height: 1.5; flex: 1; }
.style-n {
  font-family: var(--f-display); font-size: 11.5px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--blue-deep);
}
.styles-alt { margin: 18px 0 0; font-size: 14.5px; color: var(--muted); }

/* ---- one style: its collections, each with its colours ---- */
.style-range { margin: 0 0 44px; }
.style-range-head {
  display: flex; align-items: end; justify-content: space-between; gap: 18px;
  flex-wrap: wrap; border-bottom: 1px solid var(--line); padding-bottom: 12px; margin-bottom: 18px;
}
.style-range-head h3 { font-family: var(--f-display); font-size: 21px; font-weight: 500; margin: 0; }
.style-range-head h3 a { color: inherit; text-decoration: none; }
.style-range-head h3 a:hover { color: var(--blue); }
.style-range-head .k {
  margin: 5px 0 0; font-family: var(--f-display); font-size: 11.5px;
  letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
}

.style-more { border-top: 1px solid var(--line); padding-top: 22px; }
.style-chips { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }

/* =========================================================================
   FILTER CHIPS — catalogue and visualiser share these
   ========================================================================= */
.filters { border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  padding: 6px 0 14px; margin: 0 0 30px; }
.filter-row { display: flex; align-items: baseline; gap: 14px; padding: 10px 0; flex-wrap: wrap; }
.filter-label { font-family: var(--f-display); font-size: 11.5px; letter-spacing: .13em;
  text-transform: uppercase; color: var(--muted); width: 62px; flex: none; }
.chips { display: flex; gap: 8px; flex-wrap: wrap; flex: 1; }
.chip {
  background: var(--paper); border: 1px solid var(--line); border-radius: 2px; color: var(--ink-soft);
  font-family: var(--f-display); font-size: 13px; letter-spacing: .05em;
  padding: 8px 14px; text-decoration: none; display: inline-flex; align-items: center; gap: 8px;
  transition: border-color .18s, color .18s, background .18s;
}
.chip:hover { border-color: var(--blue); color: var(--blue); }
.chip.on { background: var(--blue); border-color: var(--blue); color: #fff; }
.chip i { width: 13px; height: 13px; display: block; border: 1px solid rgba(0,0,0,.15); }

.empty-note { color: var(--muted); padding: 40px 0; text-align: center; }

/* =========================================================================
   SWATCHES + FAVOURITES
   ========================================================================= */
.swatch { position: relative; border: 1px solid var(--line); background: var(--paper);
  transition: transform .15s, box-shadow .15s, border-color .15s; }
.swatch:hover { transform: translateY(-2px); box-shadow: var(--shadow); border-color: var(--blue); }
.swatch .col { display: block; height: 88px; }
.swatch .meta { display: block; padding: 9px 11px 12px; }
.swatch .cn { display: block; font-size: 13.5px; font-weight: 600; line-height: 1.3; }
.swatch .cc { display: block; font-size: 12px; color: var(--muted); }
/* wraps rather than truncating — "Westex · Elysi…" tells you nothing */
.swatch .cr { display: block; font-size: 11.5px; color: var(--muted); margin-top: 4px; line-height: 1.35; }

/* the heart — the one control that saves a shade */
.fav {
  position: absolute; top: 7px; right: 7px; z-index: 2;
  width: 28px; height: 28px; border-radius: 50%; border: 0; padding: 0;
  background: rgba(255,255,255,.88); color: #b9bfc8; font-size: 15px; line-height: 1;
  display: grid; place-items: center; transition: color .18s, transform .15s, background .18s;
}
.fav:hover { color: var(--blue); transform: scale(1.12); background: #fff; }
.fav.on { color: #d64545; background: #fff; }
.fav.on:hover { color: #b53434; }

.fav-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.fav-group { margin-bottom: 34px; }
.fav-group h3 { font-size: 20px; margin-bottom: 14px; display: flex; align-items: center; gap: 10px; }
.fav-group h3 .n { font-family: var(--f-ui); font-size: 12px; font-weight: 700; color: var(--blue-deep);
  background: var(--blue-tint); border-radius: 2px; padding: 2px 8px; }

.linkish { background: none; border: 0; padding: 0; color: var(--muted);
  text-decoration: underline; font: inherit; font-size: 13px; }
.linkish:hover { color: var(--blue); }

/* visualiser palette headings link back to the range */
.vis-group h4 a { color: inherit; text-decoration: none; }
.vis-group h4 a:hover { color: var(--blue); }
.vis-current .fav { position: static; margin-left: auto; }

@media (max-width: 620px) {
  .filter-label { width: 100%; }
  .fav-btn { display: none; }
  .nav.show + .fav-btn { display: inline-flex; }
}


/* =========================================================================
   CATALOGUE — result bar, facet counts, swatch photography, lightbox
   ========================================================================= */
.result-bar { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin: 0 0 22px; }
.result-count { margin: 0; color: var(--muted); font-size: 15px; }
.result-count b { color: var(--ink); font-family: var(--f-display); font-size: 18px; }
.active-filters { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.pill {
  background: var(--blue); color: #fff; border: 0; border-radius: 2px;
  font-family: var(--f-display); font-size: 12.5px; letter-spacing: .05em;
  padding: 6px 10px; display: inline-flex; align-items: center; gap: 8px;
}
.pill i { font-style: normal; opacity: .75; font-size: 15px; }
.pill:hover i { opacity: 1; }

.chip .n { font-size: 11.5px; opacity: .6; font-family: var(--f-ui); }
.chip.on .n { opacity: .85; }
.chip.off { opacity: .38; cursor: not-allowed; }
.chip.off:hover { border-color: var(--line); color: var(--ink-soft); }

/* swatch tiles now carry the supplier's own photograph of the pile */
.swatch .col {
  /* height:auto overrides the flat-colour rule further up, which fixed the tile
     at 88px and squashed the photograph */
  display: block; width: 100%; height: auto; aspect-ratio: 1; padding: 0; border: 0;
  position: relative; overflow: hidden; cursor: zoom-in;
}
.swatch .col img { width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform .35s ease; }

/* Shades the supplier only ever photographed in a room. Framing the bottom of
   the shot crops it to the floor, which is the one part of a roomset that is
   the product — so the tile shows real grain and pile instead of a thumbnail
   of a sofa. Better than falling back to the flat measured hex too: several of
   these colours were read off a lit floor and carry the room's cast with them,
   and the photograph at least never claims to be more exact than it is.

   object-position alone cannot do this: cover has already fitted the image to
   the tile, so there is no overflow left to slide. Scaling about a point low
   in the frame is what actually crops it. */
.swatch .col img.floor-crop { transform: scale(2.4); transform-origin: 50% 90%; }
.swatch:hover .col img.floor-crop { transform: scale(2.55); }
.swatch:hover .col img { transform: scale(1.06); }
.swatch .pin {
  position: absolute; left: 7px; bottom: 7px; z-index: 2;
  background: rgba(20,41,74,.82); color: #fff; border-radius: 2px;
  font-family: var(--f-display); font-size: 10px; letter-spacing: .1em;
  text-transform: uppercase; padding: 3px 7px;
}
.swatch-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }

/* ---- lightbox ---- */
.zoom {
  position: fixed; inset: 0; z-index: 100; display: none;
  background: rgba(12,26,48,.72); padding: clamp(12px, 4vw, 48px);
  overflow: auto; align-items: flex-start; justify-content: center;
}
.zoom.open { display: flex; }
.zoom-panel {
  position: relative; background: var(--paper); max-width: 1000px; width: 100%;
  box-shadow: 0 30px 80px -30px rgba(0,0,0,.6);
}
.zoom-x {
  position: absolute; top: 8px; right: 10px; z-index: 3; border: 0; background: rgba(255,255,255,.9);
  width: 38px; height: 38px; border-radius: 50%; font-size: 24px; line-height: 1; color: var(--ink);
}
.zoom-x:hover { background: #fff; color: var(--blue); }
#zoomBody { display: grid; grid-template-columns: 1.25fr 1fr; }
.zoom-media { min-width: 0; }
.zoom-main { aspect-ratio: 1; }
.zoom-main img { width: 100%; height: 100%; object-fit: cover; display: block; }
.zoom-room { position: relative; }
.zoom-room img { width: 100%; display: block; }
.zoom-cap { position: absolute; left: 0; bottom: 0; right: 0; padding: 10px 14px;
  background: linear-gradient(transparent, rgba(12,26,48,.8)); color: #fff; font-size: 13px; }
.zoom-info { padding: clamp(20px, 3vw, 34px); display: flex; flex-direction: column; gap: 10px; align-items: flex-start; }
.zoom-info h3 { font-size: clamp(24px, 3vw, 32px); }
.zoom-sub { margin: 0; color: var(--muted); font-size: 14px; }
.zoom-sub a { color: var(--blue); }
.zoom-actions { display: flex; align-items: center; gap: 10px; margin: 8px 0 6px; }
.zoom-actions .fav { position: static; width: 40px; height: 40px; font-size: 19px;
  border: 1px solid var(--line); background: var(--paper); }
.zoom-favlabel { font-size: 14px; color: var(--muted); }
.zoom-info .btn { width: 100%; justify-content: center; }

@media (max-width: 760px) {
  #zoomBody { grid-template-columns: 1fr; }
  .swatch-grid { grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); }
}


/* =========================================================================
   MOTION
   One idea, applied consistently: things fade up a short distance the first
   time they are seen, and interactive elements respond quickly. Anything
   longer than ~450ms starts to feel like waiting rather than polish, and the
   whole lot is dropped for prefers-reduced-motion (see the block at the end).
   ========================================================================= */
.will-reveal { opacity: 0; transform: translateY(14px); }
.will-reveal.seen {
  opacity: 1; transform: none;
  transition: opacity .5s ease, transform .5s cubic-bezier(.22,.61,.36,1);
}
/* cards in a grid arrive together rather than one behind the other — a
   stagger long enough to notice is a stagger long enough to annoy */
#app .card.will-reveal, #app .cat-tile.will-reveal { transition-duration: .42s; }

.btn, .chip, .fav, .arrow-link, .card, .cat-tile, .swatch {
  transition-property: background-color, color, border-color, box-shadow, transform, opacity;
  transition-duration: .18s; transition-timing-function: ease;
}
.btn:active, .chip:active { transform: translateY(1px); }
.cat-tile:hover .cat-cap { background: linear-gradient(transparent, rgba(12,26,48,.92)); }

/* the lightbox scales in from the tile rather than appearing */
.zoom-panel { animation: zoomIn .24s cubic-bezier(.22,.61,.36,1); }
@keyframes zoomIn { from { opacity: 0; transform: translateY(10px) scale(.985); } to { opacity: 1; transform: none; } }
.zoom { animation: fadeIn .2s ease; }
@keyframes fadeIn { from { opacity: 0 } to { opacity: 1 } }

/* focus that is visible without being loud — keyboard users get a real ring */
:where(a, button, input, select, textarea):focus-visible {
  outline: 3px solid var(--blue); outline-offset: 2px; border-radius: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .will-reveal, .will-reveal.seen { opacity: 1 !important; transform: none !important; }
  .zoom-panel, .zoom { animation: none !important; }
}

/* A line the shop can put across the top of the home page from /admin — a bank
   holiday, a delivery delay. Absent unless they have written one. */
.notice {
  background: var(--blue); color: #fff; font-size: 14.5px;
  padding: 11px 0; text-align: center;
}

/* =========================================================================
   MAP, REVIEWS, INSTAGRAM
   ========================================================================= */

/* Google's embed is a fixed 600x450 out of the box. Wrapping it in a ratio box
   and stretching the iframe is what makes it behave on a phone. */
/* the form deserves the wider half now that it has a page of its own */
.enquire-grid { display: grid; grid-template-columns: 1fr 1.05fr; gap: clamp(28px, 5vw, 64px); align-items: start; }
@media (max-width: 900px) { .enquire-grid { grid-template-columns: 1fr; } }

.map-embed {
  position: relative; aspect-ratio: 16 / 9; border: 1px solid var(--line);
  background: var(--off); overflow: hidden;
}
.map-embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
@media (max-width: 700px) { /* the form deserves the wider half now that it has a page of its own */
.enquire-grid { display: grid; grid-template-columns: 1fr 1.05fr; gap: clamp(28px, 5vw, 64px); align-items: start; }
@media (max-width: 900px) { .enquire-grid { grid-template-columns: 1fr; } }

.map-embed { aspect-ratio: 4 / 3; } }

/* Deliberately a strip rather than a section: corroboration on the way past,
   not a page of testimonials. Laid out like the review widgets people already
   recognise — the rating first, because that is the bit anybody reads. */
.reviews { padding: clamp(28px, 4vw, 44px) 0; border-bottom: 1px solid var(--line); }
.reviews-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 18px;
  flex-wrap: wrap; background: var(--off); border: 1px solid var(--line);
  padding: 14px 18px; margin-bottom: 18px;
}
.reviews-brand {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  font-family: var(--f-display); font-size: 17px; color: var(--ink-soft);
}
.reviews-brand b { color: var(--blue-ink); font-weight: 500; }
.reviews-score {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--f-display); font-size: 20px; color: var(--blue-ink);
}
.reviews-score i { font-style: normal; font-size: 14px; color: var(--muted); }
.stars { color: var(--yellow-deep); letter-spacing: .06em; font-size: .85em; }
/* the unearned stars stay in place so the row never changes width */
.stars-off { color: var(--line); }

.reviews-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 16px; }
.rv {
  margin: 0; background: var(--paper); border: 1px solid var(--line);
  padding: 16px; display: flex; flex-direction: column; gap: 9px;
}
.rv figcaption { display: flex; align-items: center; gap: 11px; }
.rv-face {
  width: 34px; height: 34px; flex: none; border-radius: 50%; color: #fff;
  display: grid; place-items: center;
  font-family: var(--f-display); font-size: 15px;
}
.rv-who b { display: block; font-size: 14.5px; font-weight: 600; color: var(--ink); }
.rv-who small { color: var(--muted); font-size: 12.5px; }
.rv p {
  margin: 0; font-size: 14.5px; color: var(--ink-soft); line-height: 1.6;
  /* four lines, then it stops — otherwise one chatty review sets the height of
     the whole row */
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden;
}

/* THE INSTAGRAM WALL
   Nine posts with the newest one at twice the size. Nine is not arbitrary: with
   the lead tile spanning two columns and two rows, nine fills the wall exactly
   at two, three and four columns — 4 + 8 cells, 2 + 6, 4 + 4 + ... — so no
   screen width leaves a hole in it or an orphan tile on a row of its own. The
   previous eight equal squares gave seven across and a straggler underneath. */
.ig-wall { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.ig-tile {
  display: block; position: relative; aspect-ratio: 1; overflow: hidden;
  background: var(--off); text-decoration: none;
}
.ig-tile.lead { grid-column: span 2; grid-row: span 2; }
.ig-tile img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s ease; }
.ig-tile:hover img, .ig-tile:focus-visible img { transform: scale(1.05); }
.ig-tile:focus-visible { outline: 3px solid var(--blue); outline-offset: 2px; }

/* The caption, over the foot of the picture. Hidden until the tile is hovered
   or focused, except on the lead tile where there is room for it to live. */
.ig-cap {
  position: absolute; inset: auto 0 0 0; padding: 34px 12px 11px; color: #fff;
  font-size: 13.5px; line-height: 1.45; opacity: 0; transition: opacity .28s ease;
  background: linear-gradient(0deg, rgba(10,22,42,.88) 0%, rgba(10,22,42,.55) 55%, transparent 100%);
}
.ig-cap > span {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.ig-tile:hover .ig-cap, .ig-tile:focus-visible .ig-cap { opacity: 1; }
/* Touch has no hover, so the biggest one keeps its caption on show — otherwise
   captions would be a desktop-only feature nobody on a phone ever sees. */
.ig-tile.lead .ig-cap { opacity: 1; font-size: 15px; padding: 46px 16px 15px; }
.ig-tile.lead .ig-cap > span { -webkit-line-clamp: 3; }

/* the account, so it reads as a live feed rather than a gallery of stills */
.ig-who { display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap; margin-top: 12px;
  font-size: 14.5px; color: var(--muted); }
.ig-who b { font-family: var(--f-display); font-size: 16px; font-weight: 500; color: var(--blue); }
.ig-follow { display: inline-flex; align-items: center; gap: 8px; }
.ig-follow .si { width: 1.1em; height: 1.1em; }

@media (max-width: 900px) { .ig-wall { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 560px) { .ig-wall { grid-template-columns: repeat(2, 1fr); gap: 7px; } }

/* PORTFOLIO — bigger than the Instagram tiles on purpose. These are the shop's
   own jobs shot properly, so they get room to be looked at rather than being
   thumbnails in a wall. */
.folio { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: clamp(20px, 3vw, 34px); margin-top: 8px; }
.folio-item { margin: 0; }
.folio-item figcaption { padding-top: 14px; }
.folio-item h3 { font-family: var(--f-display); font-size: 18px; font-weight: 500; }
.folio-item figcaption p { color: var(--ink-soft); font-size: 15px; margin-top: 4px; }

/* Privacy and data-deletion sit in the fine print, where people look for them,
   rather than in the main navigation where they would compete with the shop. */
/* Marks, not more link text. Sized off the surrounding font and painted with
   currentColor, so they follow the footer's own colours. */
.foot-socials { display: flex; gap: 16px; margin-top: 14px; }
.foot-socials a {
  display: inline-flex; padding: 0; opacity: .85;
  transition: opacity .18s ease, color .18s ease;
}
.foot-socials a:hover { opacity: 1; color: var(--yellow); }
.foot-socials a:focus-visible { outline: 2px solid var(--yellow); outline-offset: 3px; }
.foot-socials .si { width: 21px; height: 21px; }

.site-footer .fine-links { display: flex; gap: 18px; flex-wrap: wrap; }
.site-footer .fine-links a { display: inline; padding: 0; text-decoration: underline; }

/* =========================================================================
   THE WHATSAPP BUTTON
   Only on the page at all when the shop has put a number in /admin. Bottom
   right, above everything, and out of the way of the footer links on a phone.
   ========================================================================= */
.wa-float {
  position: fixed; right: 18px; bottom: 18px; z-index: 60;
  display: inline-flex; align-items: center; gap: 9px;
  background: #25d366; color: #fff; text-decoration: none;
  padding: 12px 18px 12px 15px; border-radius: 999px;
  box-shadow: 0 6px 20px rgba(0,0,0,.28);
  font-family: var(--f-display); font-size: 14px; letter-spacing: .04em;
  transition: transform .18s, box-shadow .18s;
}
.wa-float:hover { transform: translateY(-2px); box-shadow: 0 10px 26px rgba(0,0,0,.34); }
.wa-float .si { width: 22px; height: 22px; }
/* On a phone the label costs more room than it earns, and the thumb is aiming
   at the mark anyway. */
@media (max-width: 620px) {
  .wa-float { padding: 14px; right: 14px; bottom: 14px; }
  .wa-float span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
}
@media (prefers-reduced-motion: reduce) { .wa-float { transition: none; } }
