/* ==========================================================================
   ERA Sanitary & Plumbing Solutions
   Dark brand ground, amber accent. Bengali copy uses Noto Sans Bengali, which
   is listed after Barlow everywhere so each script falls to the right face.
   ========================================================================== */

/* ==========================================================================
   Web fonts — self-hosted, not loaded from Google
   RULES.md rule 5 bans third-party scripts on customer-facing pages. A
   stylesheet was within the letter of that rule, which is why the Google Fonts
   link survived this long, but it is still a render-blocking request to a host
   we do not control, on the connection where that hurts most. These files were
   the site's last third-party request.

   It saves no bytes — they are the same files Google served. What it buys is
   two fewer DNS+TLS handshakes and one fewer cross-origin round trip before
   any text paints. Cross-site cache partitioning left no shared-cache benefit
   to give up by self-hosting.

   Archivo and Noto Sans Bengali are variable fonts: one file covers every
   weight, which is why each is declared 100 900 rather than once per weight.
   Noto replaced Hind Siliguri, which was static and cost three files and
   213.7 KB to do the same job in 105.2 KB.

   Barlow ships at 400 and 700 only. Every 500 and 600 in this stylesheet sits
   under a rule that sets --font-display, so it resolves to Archivo's variable
   file and never to Barlow — checked by resolving the family for each of those
   rules, and again by crawling all 32 pages and recording what the browser
   actually asked for. Shipping 500 and 600 was 43.7 KB nothing could reach.

   "Bengali Switch" is the same Noto face cut down to the three characters বাং
   and nothing else, 952 bytes. The language switch is the ONLY Bengali on an
   English page, and drawing it from the full face cost 71 KB with Hind Siliguri
   and would have cost 105 KB with Noto — about half an English page's font
   weight, for three glyphs. It needs its own family name: two @font-face rules
   on one family with overlapping unicode-ranges resolve to the last declared,
   so the full face would win and the saving would vanish. See .langswitch below.

   The subsets do the rest. Measured by loading each page and recording every
   woff2 the browser actually asked for: 79.0 KB of fonts on an English page,
   183.2 KB on a Bengali one. Before this face changed, 149.3 and 291.7. No
   English page fetches the full Bengali face at all any more — checked on all
   five page types in both languages, not inferred.

   Generated by tools/fetch-web-fonts.py. Re-run it rather than hand-editing a
   unicode-range: a wrong range does not error, it silently stops a face being
   used for the script it was fetched for. Adding a weight to this stylesheet
   means adding it to WANT in that script, or the browser synthesises it.
   ========================================================================== */

@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("../fonts/archivo-latin-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Barlow";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/barlow-latin-400.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Barlow";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/barlow-latin-700.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Noto Sans Bengali";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("../fonts/noto-sans-bengali-var.woff2") format("woff2");
  unicode-range: U+0951-0952, U+0964-0965, U+0980-09FE, U+1CD0, U+1CD2, U+1CD5-1CD6, U+1CD8, U+1CE1, U+1CEA, U+1CED, U+1CF2, U+1CF5-1CF7, U+200C-200D, U+20B9, U+25CC, U+A8F1, U+11DF0-11DF1;
}
@font-face {
  font-family: "Bengali Switch";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/noto-sans-bengali-switch-700.woff2") format("woff2");
  unicode-range: U+982, U+9ac, U+9be;
}

:root {
  --bg:        #0b0b0d;
  --bg-2:      #0e0f12;
  --surface:   #141519;
  --surface-2: #16171b;
  --line:      #22242a;   /* decorative dividers */
  --line-2:    #2a2c33;   /* decorative borders */
  --line-ui:   #6b6e78;   /* interactive boundaries — 3.75:1 on --bg, meets 1.4.11 */

  --text:   #f3f3f4;
  --muted:  #c9cbd2;
  --muted-2:#a7a9b2;   /* 8.0:1 on --bg */

  --amber:    #f7b500;
  --amber-hi: #ffcb2e;
  --star:     #f7b500;   /* 10.1:1 on --surface; empty stars use --line-ui at 3.6:1 */
  --blue:     #1b69c4;
  --red:      #d8232a;
  --ink:      #151515;

  --font-display: "Archivo", "Noto Sans Bengali", system-ui, sans-serif;
  --font-body:    "Barlow", "Noto Sans Bengali", system-ui, sans-serif;

  --shell: 1200px;
  --r-sm: 8px;
  --r: 12px;
  --r-lg: 18px;
  --section-y: clamp(52px, 6.5vw, 92px);
}

*, *::before, *::after { box-sizing: border-box; }
/* scroll-padding-top is what keeps an in-page jump from parking its heading
   under the header, which is sticky at 85px. Without it #ceramic lands with the
   section at 0 and its heading at 34 — fifty pixels of the heading hidden. One
   declaration covers every anchor on the site: the category jump nav, the hero
   rotator's links into products.html, the home page's category cards, the
   services anchors and the skip link. */
html { scroll-behavior: smooth; scroll-padding-top: 100px; -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.6;
  overflow-x: hidden;
}
img, svg { display: block; max-width: 100%; }
img { height: auto; }
h1, h2, h3 { font-family: var(--font-display); line-height: 1.12; letter-spacing: -0.015em; margin: 0 0 .5em; }
p { margin: 0 0 1em; }
ul, ol { margin: 0; padding: 0; list-style: none; }
a { color: var(--amber); text-decoration: none; }
a:hover { color: var(--amber-hi); }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
table { border-collapse: collapse; width: 100%; }
::selection { background: var(--amber); color: var(--ink); }

:focus-visible { outline: 3px solid var(--amber); outline-offset: 3px; border-radius: 4px; }

.skip-link {
  position: absolute; left: 16px; top: -64px; z-index: 200;
  padding: 10px 18px; background: var(--amber); color: var(--ink);
  border-radius: 0 0 8px 8px; font-weight: 700; transition: top .2s ease;
}
.skip-link:focus { top: 0; }

.shell { width: min(100% - 40px, var(--shell)); margin-inline: auto; }
.section { padding-block: var(--section-y); }
.ic { width: 20px; height: 20px; flex: none; }
.amber { color: var(--amber); }
.muted { display: flex; align-items: center; gap: 8px; color: var(--muted-2); font-size: 14px; }

/* ---------- Topbar + header ---------- */
.topbar {
  background: var(--amber); color: var(--ink);
  font-family: var(--font-display); font-weight: 600; font-size: 13px; letter-spacing: .03em;
}
.topbar__inner { display: flex; flex-wrap: wrap; gap: 6px 28px; justify-content: space-between; padding-block: 9px; }

/* Opaque by default, translucent-and-blurred only at desktop — see the
   min-width: 1025px block at the end of this file. The blur CANNOT live here:
   backdrop-filter makes an element the containing block for its position:fixed
   descendants, and .nav is fixed inside this header below 1025px. With the
   blur applied, the mobile menu resolved top/bottom against this 85px header
   instead of the viewport and rendered 43px tall holding 360px of content —
   the first link sliced in half and every link below it unreachable. The whole
   mobile navigation was dead and it shipped that way. */
.site-header {
  position: sticky; top: 0; z-index: 60;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}
.header__inner { display: flex; align-items: center; gap: 22px; padding-block: 13px; }

.brand { display: inline-flex; align-items: center; gap: 12px; color: var(--text); }
.logo img { width: 44px; height: 44px; border-radius: 50%; }
.brand__text strong { display: block; font-family: var(--font-display); font-weight: 800; font-size: 19px; letter-spacing: -0.01em; line-height: 1.05; }
/* Scoped to the class, not to any span inside the strong — the second line of
   the name is a span too, and the old selector would have turned it amber. */
.brand__text strong .brand__gold { color: var(--amber); }
/* The rest of the registered name, subordinate to it: the shopfront sign sets
   "& Plumbing Solutions" the same way. letter-spacing is reset because the
   -0.01em tightening above is for 19px display type, not for 13px. */
.brand__text strong .brand__sub {
  display: block; margin-top: 3px;
  font-size: 13px; font-weight: 600; letter-spacing: 0; line-height: 1.2;
  color: var(--text);
}
.brand__text small { display: block; font-size: 12px; color: var(--muted-2); }

.nav { display: flex; align-items: center; gap: 20px; margin-left: auto; }
.nav__list { display: flex; align-items: center; gap: 4px; }
.nav__list a {
  display: block; padding: 10px 16px; border-radius: var(--r-sm);
  font-family: var(--font-display); font-weight: 600; font-size: 15px; color: var(--muted);
  transition: background-color .18s ease, color .18s ease;
}
.nav__list a:hover { color: var(--amber); background: var(--surface-2); }
.nav__list a[aria-current="page"] { background: var(--surface-2); color: var(--amber); }
.nav__aside { display: flex; align-items: center; gap: 14px; }

.langswitch { display: flex; align-items: center; gap: 2px; margin: 0; padding: 3px; border: 1px solid var(--line-2); border-radius: 999px; }
.langswitch > * {
  display: block; padding: 6px 14px; border-radius: 999px;
  font-family: var(--font-display); font-weight: 700; font-size: 13px;
}
/* After .langswitch above, not before it: that rule sets display at the same
   specificity, so a hide written earlier loses and the bar copy shows up
   beside the brand on a desktop. Above the breakpoint only the .nav__aside
   copy is shown, between the links and the WhatsApp button; below it they
   swap. */
.langswitch--bar { display: none; }

.langswitch__on { background: var(--amber); color: var(--ink); }
.langswitch a { color: var(--muted-2); }
.langswitch a:hover { color: var(--amber); }

/* On an ENGLISH page this <a lang="bn"> holds বাং and is the only Bengali on
   the page. It gets a face cut to those three characters — 952 bytes — instead
   of pulling the whole 105 KB Bengali font to draw them. The full face sits
   behind it as a safety net and is never reached, because the subset covers
   every character here.

   A Bengali page does not match: its own বাং is in the <span class="langswitch__on">,
   which carries no lang attribute and inherits bn from <html>, so it draws from
   the page's face — already downloaded for the rest of the page.

   (0,2,0) against the (0,1,0) of `.langswitch > *` above, which sets
   --font-display. Specificity, not !important.

   Change the label text and the subset no longer covers it. build.js refuses
   the build in that case — see checkLangSwitchSubset(). */
.langswitch [lang="bn"] {
  font-family: "Bengali Switch", "Noto Sans Bengali", sans-serif;
}

.nav-toggle { display: none; margin-left: auto; width: 44px; height: 44px; padding: 10px; }
.nav-toggle span { display: block; height: 2px; border-radius: 2px; background: var(--text); transition: transform .22s ease, opacity .22s ease; }
.nav-toggle span + span { margin-top: 5px; }
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  min-height: 48px; padding: 12px 24px; border-radius: 999px;
  font-family: var(--font-display); font-weight: 700; font-size: 15px;
  border: 1px solid transparent; transition: transform .16s ease, background-color .16s ease, border-color .16s ease;
}
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: none; }
.btn--accent { background: var(--amber); color: var(--ink); }
.btn--accent:hover { background: var(--amber-hi); color: var(--ink); }
.btn--ghost { border-color: var(--line-ui); color: var(--text); }
.btn--ghost:hover { border-color: var(--amber); color: var(--amber); }
.btn--sm { min-height: 40px; padding: 8px 18px; font-size: 14px; }
.btn--block { display: flex; width: 100%; }

.link-more {
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--font-display); font-weight: 700; font-size: 14px; color: var(--amber);
}
.link-more .ic { width: 17px; height: 17px; transition: transform .16s ease; }
.link-more:hover .ic { transform: translateX(3px); }

/* ---------- Photo slots (pending client photography) ---------- */
.slot {
  display: grid; place-items: center; padding: 24px; min-height: 180px;
  border: 1px dashed var(--line-2); border-radius: var(--r-lg);
  background:
    repeating-linear-gradient(135deg, rgb(247 181 0 / 4%) 0 10px, transparent 10px 20px),
    var(--bg-2);
  color: var(--muted-2); text-align: center;
}
.slot span { font-family: var(--font-display); font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
.slot--hero { min-height: 400px; }
.slot--card { min-height: 148px; border-radius: var(--r) var(--r) 0 0; border-bottom: 0; }
.slot--cat { min-height: 260px; }
.slot--why, .slot--about { min-height: 300px; }

/* ---------- Hero rotator ----------
   Seven category pictures taking turns in the one slot the hero photograph
   used to occupy. Nothing travels: the slides are stacked on top of each other
   inside a box fixed at the same 4:3 the single picture had, and changing
   slide is a cross-fade in place. The box therefore sits exactly where the
   photograph sat, and the rest of the hero does not move when it changes.

   Hidden slides get visibility: hidden as well as opacity: 0, delayed until
   the fade is over. Opacity alone leaves them hit-testable and still in the
   accessibility tree, so a pointer or a screen reader would find seven
   overlapping pictures where a visitor sees one. */
.rot { position: relative; aspect-ratio: 4 / 3; border-radius: var(--r); }
.rot__stack { position: absolute; inset: 0; margin: 0; padding: 0; list-style: none; }
.rot__slide {
  position: absolute; inset: 0;
  opacity: 0; visibility: hidden;
  transition: opacity .6s ease, visibility 0s .6s;
}
.rot__slide.is-on { opacity: 1; visibility: visible; transition: opacity .6s ease; }

.rot__link { display: block; position: relative; height: 100%; text-decoration: none; }
.rot__link .photo, .rot__link .slot { height: 100%; border-radius: var(--r); }

/* The name sits on the picture, not under it, so the panel is the same height
   whichever category is showing. The gradient keeps it readable over seven
   photographs whose bottom edges are seven different brightnesses. */
.rot__name {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; align-items: center; gap: 8px;
  /* The bottom padding is clearance for the control row, which sits on the
     same gradient underneath. Share a line with it and a long name — "Plumbing
     accessories & hardware" — collides with the pause button, because seven
     44px dots plus the button leave about 170px of a 546px panel. */
  padding: 60px 18px 54px;
  border-radius: 0 0 var(--r) var(--r);
  background: linear-gradient(to top, rgb(0 0 0 / 78%), rgb(0 0 0 / 0%));
  color: #fff; font-family: var(--font-display); font-weight: 600;
  font-size: clamp(15px, 1.5vw, 18px); line-height: 1.3;
}
.rot__name svg { flex: none; transition: transform .2s ease; }
.rot__link:hover .rot__name svg, .rot__link:focus-visible .rot__name svg { transform: translateX(4px); }

/* The controls ride on the picture too. Put them underneath and they add their
   own height, which pushes the picture off the spot the single photograph
   held — the one thing this change was not allowed to do. Hidden until the
   script marks the rotator live: a pause button that pauses nothing is worse
   than no pause button, and without JavaScript nothing rotates. */
.rot__bar {
  position: absolute; right: 8px; bottom: 4px; z-index: 1;
  display: none; align-items: center;
}
.rot.is-live .rot__bar { display: flex; }
.rot__dots { display: flex; }

/* 44px boxes because that is the smallest comfortable tap target and
   tools/check-mobile.py fails the build below it; flex: none because a target
   flex has shaved to 43px is not a 44px target. The visible dot is drawn by
   ::before, so it stays small while the thing you have to hit does not. */
.rot__dot, .rot__pause {
  flex: none; width: 44px; height: 44px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; background: none; cursor: pointer; color: rgb(255 255 255 / 70%);
}
.rot__dot::before {
  content: ""; width: 8px; height: 8px; border-radius: 50%;
  background: rgb(255 255 255 / 45%);
  box-shadow: 0 0 2px rgb(0 0 0 / 45%);
  transition: background .2s ease, transform .2s ease;
}
.rot__dot:hover::before { background: #fff; }
.rot__dot[aria-current]::before { background: var(--amber); transform: scale(1.3); }
.rot__pause svg { width: 14px; height: 14px; fill: currentColor; filter: drop-shadow(0 0 2px rgb(0 0 0 / 45%)); }
.rot__pause:hover { color: #fff; }
.rot__ico--play { display: none; }
.rot.is-paused .rot__ico--pause { display: none; }
.rot.is-paused .rot__ico--play { display: inline; }

/* On a phone the seven dots plus the button want 352px and the picture is
   about 350px wide, so the dots go and the pause button — the part that has to
   be there — stays. It moves to the top as well: at 350x263 the bottom right
   corner is both where the name is and where the fixed call bar sits over the
   page, so a control left down there is cramped and sometimes covered. */
@media (max-width: 620px) {
  .rot__dots { display: none; }
  .rot__bar { top: 4px; bottom: auto; }
  .rot__name { padding-bottom: 18px; }
}

/* ---------- Google reviews ----------
   Real reviews, typed into content/reviews.json by hand. The section is left
   out entirely while that file is empty, so none of this renders until there is
   something true to put in it. */
.revs { background: var(--bg-2); border-block: 1px solid var(--line); }
.revs .section-head h2 { display: flex; align-items: center; gap: 11px; }
.revs .section-head h2 .ic { color: var(--star); }

.stars { display: inline-flex; gap: 2px; vertical-align: middle; }
.stars .star { display: block; width: 17px; height: 17px; color: var(--line-ui); }
.stars .star.is-on { color: var(--star); }
.stars .ic { display: block; width: 100%; height: 100%; }

.revs__score {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  margin: 0 0 22px; font-family: var(--font-display);
}
.revs__score strong { font-size: 22px; font-weight: 800; }
.revs__score span { color: var(--muted-2); font-size: 14.5px; font-weight: 600; }

.revs__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(14px, 1.8vw, 20px); }
.rev {
  display: flex; flex-direction: column; gap: 12px; margin: 0;
  padding: 22px; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface);
}
.rev blockquote {
  margin: 0; font-size: 15px; line-height: 1.6; color: var(--muted);
  /* A long review should not stretch one card past the others in its row. */
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 7; overflow: hidden;
}
.rev figcaption {
  margin-top: auto; padding-top: 4px;
  font-family: var(--font-display); font-weight: 700; font-size: 14.5px; color: var(--text);
}
.rev figcaption span { display: block; font-weight: 500; font-size: 13px; color: var(--muted-2); }

/* The real photographs, once they exist. Each variant matches the placeholder
   it replaces — same corners, same edges, same position in its grid — so a page
   with nine photos and twelve placeholders still reads as one page rather than
   two half-finished ones. Kept together here, rather than beside each component,
   because what matters is that they agree with the .slot rules above.

   aspect-ratio does the work the placeholders' min-height did: the box is the
   right shape before the image has loaded, so nothing moves as they arrive. The
   files are already cut to these ratios, and object-fit: cover is insurance
   against a hand-placed file that is not. */
.photo {
  display: block; overflow: hidden; border-radius: var(--r-lg);
  background: var(--bg-2);
}
.photo img { display: block; width: 100%; height: 100%; object-fit: cover; }

.photo--hero { aspect-ratio: 4 / 3; }
.photo--why, .photo--about { aspect-ratio: 5 / 4; }
.photo--cat { aspect-ratio: 4 / 3; }
.photo--trade { aspect-ratio: 16 / 10; }
.photo--face { aspect-ratio: 1 / 1; }
.photo--card {
  aspect-ratio: 16 / 9; border-radius: var(--r) var(--r) 0 0;
}
.photo--svc {
  aspect-ratio: 4 / 3; border-radius: 0;
  border-bottom: 1px solid var(--line);
}

/* ---------- Hero ---------- */
.hero { padding-block: clamp(36px, 5vw, 66px) clamp(30px, 4vw, 48px); }
.hero__grid { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr); gap: clamp(28px, 4vw, 52px); align-items: center; }
.badge {
  display: inline-flex; align-items: center; gap: 9px; margin: 0 0 20px;
  padding: 8px 16px; border: 1px solid var(--line-2); border-radius: 999px;
  background: var(--surface); color: var(--muted);
  font-family: var(--font-display); font-weight: 600; font-size: 13px;
}
.badge .ic { color: var(--amber); }
.hero h1 { font-size: clamp(34px, 4.6vw, 56px); font-weight: 800; margin-bottom: 18px; }
.lede { font-size: clamp(16px, 1.3vw, 18px); color: var(--muted); max-width: 56ch; }
.hero__actions { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 28px; }
.facts { display: flex; flex-wrap: wrap; gap: 14px 40px; margin-top: 34px; padding-top: 26px; border-top: 1px solid var(--line); }
.facts strong { display: block; font-family: var(--font-display); font-size: 26px; font-weight: 800; color: var(--amber); line-height: 1.1; }
.facts span { font-size: 14px; color: var(--muted-2); }

/* ---------- Pillars ---------- */
.pillars-band { background: var(--bg-2); border-block: 1px solid var(--line); }
.pillars { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(18px, 2.4vw, 32px); }
.pillar__n { margin: 0 0 8px; font-family: var(--font-display); font-size: 13px; font-weight: 800; letter-spacing: .16em; color: var(--amber); }
.pillar h3 { font-size: 20px; font-weight: 700; margin-bottom: 8px; }
.pillar p { margin: 0; font-size: 15px; color: var(--muted-2); }

/* ---------- Section head + category cards ---------- */
.section-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 14px; margin-bottom: clamp(24px, 3vw, 38px); }
.section-head h2 { margin: 0; font-size: clamp(26px, 3vw, 36px); font-weight: 700; }

.cat-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(16px, 2vw, 24px); }
.cat-card {
  display: flex; flex-direction: column; overflow: hidden; color: var(--text);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  transition: border-color .18s ease, transform .18s ease;
}
.cat-card:hover { border-color: var(--amber); transform: translateY(-3px); color: var(--text); }
.cat-card__body { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; padding: 18px; }
.cat-card__size { margin: 0; font-family: var(--font-display); font-size: 12px; font-weight: 700; letter-spacing: .1em; color: var(--amber); }
.cat-card h3 { margin: 0; font-size: 17px; font-weight: 700; }
.cat-card p { margin: 0; font-size: 14px; color: var(--muted-2); }
.cat-card .link-more { margin-top: auto; padding-top: 10px; }

/* An odd category count leaves the last card alone on its row; let it span
   the row and turn side-on rather than sit as a stranded third. */
.cat-grid > .cat-card:last-child:nth-child(3n + 1) { grid-column: 1 / -1; }

@media (min-width: 1025px) {
  .cat-grid > .cat-card:last-child:nth-child(3n + 1) { flex-direction: row; align-items: stretch; }
  .cat-grid > .cat-card:last-child:nth-child(3n + 1) .slot {
    flex: 0 0 32%; min-height: 0; border-radius: var(--r) 0 0 var(--r);
    border-right: 0; border-bottom: 1px dashed var(--line-2);
  }
  .cat-grid > .cat-card:last-child:nth-child(3n + 1) .cat-card__body { flex: 1 1 auto; justify-content: center; padding: 26px 30px; }
}

/* ---------- Why ---------- */
.why__grid { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr); gap: clamp(26px, 3.5vw, 48px); align-items: center; }
.why h2 { font-size: clamp(24px, 2.8vw, 34px); font-weight: 700; margin-bottom: 22px; }
.reasons { display: grid; gap: 18px; }
.reasons li { display: flex; gap: 13px; }
.reasons .ic { color: var(--amber); margin-top: 3px; }
.reasons h3 { margin: 0 0 3px; font-size: 16px; font-weight: 700; }
.reasons p { margin: 0; font-size: 15px; color: var(--muted-2); }

/* ---------- Areas marquee ---------- */
.areas { padding-block: clamp(36px, 4vw, 56px); background: var(--bg-2); border-block: 1px solid var(--line); overflow: hidden; }
.areas__title { font-size: clamp(20px, 2.2vw, 26px); font-weight: 700; margin-bottom: 22px; }
.marquee { position: relative; -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); }
.marquee__track { display: flex; gap: 12px; width: max-content; animation: era-marquee 42s linear infinite; }
.marquee__track li {
  padding: 10px 20px; border: 1px solid var(--line-2); border-radius: 999px;
  background: var(--surface); color: var(--muted);
  font-family: var(--font-display); font-weight: 600; font-size: 14px; white-space: nowrap;
}
@keyframes era-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* ---------- Band CTA ---------- */
.band { padding-block: clamp(38px, 4.5vw, 60px); background: linear-gradient(115deg, var(--amber) 0%, #ffd45e 100%); color: var(--ink); }
.band__inner { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 26px; }
.band h2 { margin: 0 0 6px; font-size: clamp(22px, 2.6vw, 30px); font-weight: 800; color: var(--ink); max-width: 30ch; }
.band p { margin: 0; font-size: 15px; max-width: 58ch; color: #3a3118; }
.band__actions { display: flex; flex-wrap: wrap; gap: 12px; }
.band .btn--accent { background: var(--ink); color: #fff; }
.band .btn--accent:hover { background: #000; color: #fff; }
.band .btn--ghost { border-color: rgb(21 21 21 / 35%); color: var(--ink); }
.band .btn--ghost:hover { border-color: var(--ink); color: var(--ink); background: rgb(255 255 255 / 30%); }

/* ---------- Page head + products ---------- */
.page-head { padding-block: clamp(40px, 5vw, 64px) clamp(20px, 2.4vw, 30px); border-bottom: 1px solid var(--line); }
.page-head h1 { font-size: clamp(30px, 4vw, 46px); font-weight: 800; margin-bottom: 14px; }

.cats { display: grid; gap: clamp(28px, 3.4vw, 48px); padding-block: clamp(36px, 4.5vw, 64px); }
.cat { display: grid; grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr); gap: clamp(20px, 3vw, 40px); align-items: center; }
/* Alternate sides, but keep the text measure constant: swap the tracks too,
   otherwise the copy column changes width row to row. */
.cat:nth-child(even) { grid-template-columns: minmax(0, 1.2fr) minmax(0, .8fr); }
.cat:nth-child(even) .slot { order: 2; }
.cat__n { display: flex; align-items: center; gap: 10px; margin: 0 0 8px; font-family: var(--font-display); font-size: 13px; font-weight: 700; letter-spacing: .12em; color: var(--amber); }
.cat__n span { padding: 3px 9px; border-radius: 6px; background: rgb(247 181 0 / 14%); }
.cat h2 { font-size: clamp(22px, 2.6vw, 30px); font-weight: 700; margin-bottom: 10px; }
.cat__long { color: var(--muted); font-size: 16px; }

.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chips li {
  padding: 7px 14px; border: 1px solid var(--line-2); border-radius: 999px;
  background: var(--surface); color: var(--muted); font-size: 14px;
}

/* The jump nav on the products page, which is 22.7 phone screens long. Same
   chip shape as .chips, but these are links, so they need a 44px target rather
   than the 31px a text chip gets away with — tools/check-mobile.py measures the
   rendered box and fails the build below it. */
.jump { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 8px; }
.jump a {
  display: inline-flex; align-items: center; min-height: 44px;
  padding: 0 16px; border: 1px solid var(--line-2); border-radius: 999px;
  background: var(--surface); color: var(--muted);
  font-family: var(--font-display); font-weight: 600; font-size: 14px;
  transition: color .15s ease, border-color .15s ease;
}
.jump a:hover { color: var(--text); border-color: var(--line-ui); }

/* ---------- Product items ----------
   One card per catalogue item, with the plain chip list kept underneath as the
   reference. Each category keeps its own grid inside its own <article>, so
   nothing here is shared between sections and opening one item leaves every
   other section exactly where it was.

   No letter-spacing on .item__name on purpose. Tracking breaks Bengali
   conjuncts, and while html[lang="bn"] body * would reset it anyway at (0,1,2),
   a rule that never sets it cannot be the one that gets overridden wrongly
   later. Eighty-two Bengali labels is not where to find that out. */
.items {
  display: grid; gap: 12px; margin: 0 0 18px; padding: 0; list-style: none;
  grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
}
.item {
  display: flex; flex-direction: column; overflow: hidden;
  border: 1px solid var(--line-2); border-radius: var(--r);
  background: var(--surface);
}
.item__name {
  padding: 8px 10px 10px; font-size: 13px; line-height: 1.35;
  color: var(--muted); text-align: center;
}

/* The whole picture is the control, so the tap target is the card, never a
   small icon on it — this grid is read on a phone. */
.item__hit {
  display: block; width: 100%; padding: 0; border: 0; background: none;
  cursor: zoom-in; color: inherit; font: inherit;
}
.item__hit .photo, .item__hit .slot { border-radius: 0; }
.item__hit:focus-visible { outline: 3px solid var(--amber); outline-offset: -3px; }

.photo--item { aspect-ratio: 1 / 1; border-radius: 0; }
.slot--item {
  aspect-ratio: 1 / 1; min-height: 0; padding: 10px; border: 0;
  border-bottom: 1px dashed var(--line-2); border-radius: 0;
}

/* The reference list, now sitting under the cards rather than instead of them.
   Quieter than it was, because the pictures carry the page and this is the
   thing you scan when you already know the name of the part you want. */
.chips--ref { margin-top: 4px; }
.chips--ref li { font-size: 13px; padding: 5px 11px; color: var(--muted-2); }

/* One dialog per page, reused by every card. */
.lightbox {
  width: min(92vw, 560px); padding: 0; border: 1px solid var(--line-2);
  border-radius: var(--r-lg); background: var(--surface); color: var(--text);
}
.lightbox::backdrop { background: rgb(0 0 0 / 72%); }
.lightbox__img { display: block; width: 100%; height: auto; aspect-ratio: 1 / 1; object-fit: cover; }
.lightbox__cap {
  margin: 0; padding: 14px 16px; text-align: center;
  font-family: var(--font-display); font-weight: 600;
}
.lightbox__close {
  position: absolute; inset-block-start: 10px; inset-inline-end: 10px;
  min-height: 44px; min-width: 44px; padding: 0 14px;
  border: 1px solid var(--line-ui); border-radius: 999px;
  background: var(--bg-2); color: var(--text); font: inherit; cursor: pointer;
}

/* ---------- About ---------- */
.about__grid { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, .8fr); gap: clamp(26px, 3.5vw, 48px); align-items: start; padding-block: clamp(32px, 4vw, 52px); }
.about__copy p { color: var(--muted); }

/* The owner, under the About story.

   A 120px circle, and the size is the whole argument. The photograph is a
   formal studio headshot on a plain white background, which sits at roughly
   20:1 against --bg: not a contrast failure, a weight problem. Drawn at card
   size it is the brightest object on the page and outweighs both the shopfront
   photo beside it and the copy it belongs to. At 120px and round, the white
   reads as a deliberate light disc and the eye sees a face — which is the same
   move .logo img already makes with the gold badge at 44px.

   The ring is --line-ui at a measured 3.75:1 on --bg, so the circle has a
   boundary that meets 1.4.11 without introducing a colour. */
.owner {
  display: flex; align-items: center; gap: 16px;
  /* figure defaults to margin: 1em 40px, and that 40px would indent the block
     out of line with the paragraphs above it. */
  margin: clamp(22px, 3vw, 30px) 0 0;
  padding-top: clamp(22px, 3vw, 30px);
  border-top: 1px solid var(--line);
}
/* The placeholder is cut to the same circle as the photograph, so the box does
   not change shape on the day the file lands. .slot is built for a big dashed
   panel — its 180px floor and 24px padding both have to go. */
.owner .photo, .owner .slot {
  flex: 0 0 120px; width: 120px; min-height: 0;
  aspect-ratio: 1 / 1; padding: 0;
  border-radius: 50%; overflow: hidden;
  border: 1px solid var(--line-ui);
}
/* Size and padding only. A letter-spacing here would be (0,2,1) and beat the
   (0,1,2) Bengali reset at the end of this file, putting tracking back on
   Bengali conjuncts in the one place nobody would look. */
.owner .slot span { font-size: 11px; padding: 0 10px; }
.owner figcaption strong {
  display: block;
  font-family: var(--font-display); font-weight: 700; font-size: 18px;
  color: var(--text);
}
.owner figcaption span { font-size: 14px; color: var(--muted-2); }
.stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 18px; padding: clamp(22px, 2.6vw, 32px); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); }
.stats strong { display: block; font-family: var(--font-display); font-size: clamp(24px, 3vw, 34px); font-weight: 800; color: var(--amber); line-height: 1.05; }
.stats span { font-size: 14px; color: var(--muted-2); }

.two-col { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(26px, 3.5vw, 48px); }
.two-col h2 { display: flex; align-items: center; gap: 10px; font-size: 20px; font-weight: 700; margin-bottom: 16px; }
.two-col h2 .ic { color: var(--amber); }
.ticks { display: grid; gap: 10px; }
.ticks li { display: flex; align-items: center; gap: 10px; font-size: 15px; color: var(--muted); }
.ticks .ic { width: 18px; height: 18px; color: var(--amber); }

/* ---------- Contact ---------- */
.contact__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(18px, 2.2vw, 26px); padding-block: clamp(32px, 4vw, 52px); align-items: start; }
.card { padding: clamp(20px, 2.4vw, 28px); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); }
.card__label { font-family: var(--font-display); font-size: 12px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; color: var(--amber); margin: 0 0 10px; }
.card__label + .card__label,
.card__list + .card__label,
.link-more + .card__label { margin-top: 26px; }
.card__addr { font-size: 16px; color: var(--text); margin-bottom: 10px; }
.card__list { display: grid; gap: 12px; margin-bottom: 22px; }
.card__list li { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.card__list a { font-family: var(--font-display); font-size: 19px; font-weight: 700; }
/* Both numbers take WhatsApp, so each row carries its own chat action. */
.card__list .chat-link {
  display: inline-flex; align-items: center; gap: 6px; margin-left: auto;
  padding: 5px 12px; border: 1px solid var(--line-ui); border-radius: 999px;
  font-size: 13px; font-weight: 600; color: var(--muted);
  transition: border-color .16s ease, color .16s ease;
}
.card__list .chat-link:hover { border-color: var(--amber); color: var(--amber); }
.card__list .chat-link .ic { width: 15px; height: 15px; }
/* The address is long enough to need breaking; the numbers are not. */
.card__list--email a { font-size: 16px; overflow-wrap: anywhere; }
.card .muted { margin: 12px 0 0; }
.card--form h2 { font-size: 20px; font-weight: 700; margin-bottom: 6px; }

.hours th, .hours td { padding: 10px 0; border-bottom: 1px solid var(--line); text-align: left; font-weight: 400; font-size: 15px; }
.hours th { color: var(--muted-2); }
.hours td { color: var(--text); font-family: var(--font-display); font-weight: 600; text-align: right; white-space: nowrap; }
.hours tr:last-child th, .hours tr:last-child td { border-bottom: 0; }

.field { display: grid; gap: 6px; margin: 0 0 14px; }
.field label { font-family: var(--font-display); font-size: 11px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--muted-2); }
.field input, .field select, .field textarea {
  width: 100%; min-height: 46px; padding: 11px 14px;
  background: var(--bg); color: var(--text);
  border: 1px solid var(--line-ui); border-radius: var(--r-sm);
  font-family: var(--font-body); font-size: 16px;
}
.field textarea { min-height: 104px; resize: vertical; }
.field input::placeholder, .field textarea::placeholder { color: #7f818a; }
.field input:focus, .field select:focus, .field textarea:focus { border-color: var(--amber); outline-offset: 1px; }
.form-error { margin: 0 0 12px; padding: 10px 14px; border-radius: var(--r-sm); background: rgb(216 35 42 / 14%); border: 1px solid var(--red); color: #ffb3b6; font-size: 14px; }

/* ---------- Footer ---------- */
.site-footer { border-top: 1px solid var(--line); background: var(--bg-2); padding-top: clamp(38px, 4.5vw, 60px); }
.footer__grid { display: grid; grid-template-columns: 1.6fr 1fr 1.4fr; gap: clamp(24px, 3vw, 44px); }
.footer__brand p { margin: 16px 0 0; font-size: 14px; color: var(--muted-2); max-width: 44ch; }
.footer__col h2 { font-family: var(--font-display); font-size: 12px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; color: var(--amber); margin: 0 0 14px; }
.footer__col li + li { margin-top: 9px; }
.footer__col a { color: var(--muted); font-size: 15px; }
.footer__col a:hover { color: var(--amber); }
.footer__contact li { display: flex; align-items: flex-start; gap: 10px; }
.footer__contact .ic { color: var(--amber); margin-top: 3px; }
.footer__contact .wrap { overflow-wrap: anywhere; }
.footer__legal { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px; margin-top: clamp(30px, 3.5vw, 46px); padding-block: 18px; border-top: 1px solid var(--line); }
.footer__legal p { margin: 0; font-size: 13px; color: var(--muted-2); }

/* ==========================================================================
   Responsive
   ========================================================================== */
@media (max-width: 1024px) {
  .revs__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .nav-toggle { display: block; }
  .nav {
    position: fixed; inset: auto 0 0; top: var(--header-bottom, 108px); z-index: 55;
    flex-direction: column; align-items: stretch; gap: 10px;
    max-height: calc(100dvh - var(--header-bottom, 108px)); overflow-y: auto;
    padding: 16px 20px 26px; background: var(--bg); border-top: 1px solid var(--line);
    transform: translateY(-10px); opacity: 0; visibility: hidden;
    transition: transform .2s ease, opacity .2s ease, visibility .2s;
  }
  .nav.is-open { transform: none; opacity: 1; visibility: visible; }
  .nav__list { flex-direction: column; align-items: stretch; gap: 2px; }
  .nav__list a { padding: 12px 14px; font-size: 16px; }
  .nav__aside { justify-content: space-between; }

  /* The nav is out of flow here, so the bar is brand | switch | toggle. The
     auto margin moves from the toggle to the switch, which now opens the
     right-hand cluster. */
  .langswitch--bar { display: flex; margin-left: auto; }
  .langswitch--nav { display: none; }
  .nav-toggle { margin-left: 0; }
  /* 10px, not 12: at 360px the brand came to 199px against the 201px that
     keeps "ERA SANITARY" on one line, and split it over two for want of two
     pixels. The two gaps give back four. */
  .header__inner { gap: 10px; }
  /* Three things share a 390px bar now. Flex would take the 44px toggle down
     to 42px (39px on the Bengali pages, whose switch is wider) to make room,
     which is under the tap floor — so the two controls are fixed and the
     brand, which is just text, gives up the width instead. */
  .langswitch, .nav-toggle { flex: none; }
  .brand { min-width: 0; }

  /* Only the language you are not on. Measured: the brand needs 201px to keep
     "ERA SANITARY" on one line, 231px on the Bengali pages, and a 360px bar
     leaves 320px once the gutters are off. Both labels come to 102px, which
     drops the brand to 150px and wraps it over four lines, taking the header
     from 64px to 112px on every page. One label is 51px and the brand stays
     whole. The hidden span is the current language, which the page already
     says — what the switch is for is the other one. */
  .langswitch__on { display: none; }
  .langswitch a { color: var(--text); }

  .hero__grid, .why__grid, .about__grid { grid-template-columns: 1fr; }
  .cat-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .contact__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .card--form { grid-column: 1 / -1; }
  .footer__grid { grid-template-columns: 1fr 1fr; }
  .footer__brand { grid-column: 1 / -1; }
}

/* Measured: the brand needs 231px to hold the Bengali tagline on one line,
   201px for the English one, and the bar leaves the brand (width - 159px)
   once the gutters, gaps, switch and toggle are taken. So from 390px up both
   fit, and below it only the Bengali wraps — which grew the header from 85px
   to 104px and dropped the switch 9px the moment a reader tapped বাং, on the
   360px screens that are most of the audience here. The tagline comes off
   both languages below that width instead, so the header is the same height
   whichever one you are reading. It is still in the footer on every page.

   This block holds the header and nothing else. It was once opened in the
   middle of the 1024px block above, which closed that block early and left
   six of its rules in here — the hero kept its two-column desktop split from
   390px to 1024px, which on a phone is a 169px column of text beside a 153px
   picture. Anything added here applies ONLY below 390px. */
@media (max-width: 389px) {
  .brand__text small { display: none; }
  /* The Bengali pages draw "ERA SANITARY" 2px wider than the English ones —
     a slightly narrower logo and the lang-specific face — which was enough to
     wrap it and leave the two headers 6px apart at 360px. Two gaps of 8px
     rather than 10px give back the four it needs. */
  .header__inner { gap: 8px; }
}

@media (max-width: 760px) {
  body { font-size: 16px; }
  .topbar__delivery { display: none; }
  .pillars { grid-template-columns: 1fr; }
  .revs__grid { grid-template-columns: 1fr; }
  /* One per row on a phone, so nothing is clamped that does not need to be. */
  .rev blockquote { -webkit-line-clamp: none; }
  /* The even rule has to be named again, not just .cat: it sets the same
     property at higher specificity, so a bare .cat here loses to it and every
     second category keeps its two-column desktop split on a phone. */
  .cat, .cat:nth-child(even) { grid-template-columns: 1fr; }
  .cat:nth-child(even) .slot { order: 0; }
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .two-col { grid-template-columns: 1fr; }
  .contact__grid { grid-template-columns: 1fr; }
  .band__inner { flex-direction: column; align-items: flex-start; }
  .facts { gap: 14px 28px; }
}

@media (max-width: 520px) {
  .cat-grid { grid-template-columns: 1fr; }
  .footer__grid { grid-template-columns: 1fr; }
  .hero__actions .btn, .band__actions .btn { flex: 1 1 100%; }
  .slot--hero { min-height: 240px; }
  .owner { gap: 14px; }
  .owner .photo, .owner .slot { flex-basis: 96px; width: 96px; }
}

/* ==========================================================================
   Motion + print
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
  .marquee__track { animation: none; flex-wrap: wrap; width: auto; }
  .marquee { -webkit-mask-image: none; mask-image: none; }
}

/* ==========================================================================
   Phone-first
   Most of this audience is on an Android phone on mobile data. These rules
   come from measuring the built site at 390x844, not from taste. Every number
   quoted below was read off that measurement.
   ========================================================================== */

/* ---------- The contact bar ----------
   Hidden at desktop, where the header already carries a WhatsApp button that
   is always on screen. Below 760px that button is inside the collapsed nav and
   therefore NOT visible, which left the phone with one contact action (the
   hero's, at y=458) on a page 7.7 screens tall. */
.callbar { display: none; }

@media (max-width: 760px) {
  .callbar {
    position: fixed; inset: auto 0 0; z-index: 50;
    display: grid; grid-template-columns: 1fr 1fr; gap: 1px;
    background: var(--line-2);
    border-top: 1px solid var(--line-2);
    /* Phones with a home indicator put a strip of unusable screen below the
       viewport bottom. Without this the buttons sit under it. */
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  .callbar__btn {
    display: flex; align-items: center; justify-content: center; gap: 9px;
    min-height: 56px; padding: 10px 12px;
    background: var(--surface-2); color: var(--text);
    font-family: var(--font-display); font-weight: 700; font-size: 15.5px;
  }
  .callbar__btn--wa { background: var(--amber); color: var(--ink); }
  .callbar__btn--wa:hover, .callbar__btn--wa:active { color: var(--ink); }
  /* The bar is fixed, so it would sit on top of the footer and — worse — on
     the quote form's submit button. Reserve its height at the end of the page
     instead of letting it overlap. */
  body { padding-bottom: calc(56px + env(safe-area-inset-bottom, 0px)); }

  /* ---------- Tap targets ----------
     Twelve of 32 measured under 44px, and the worst of them were the two
     things that convert: the phone numbers at 85x24 and 86x24, and the footer
     navigation at 18px tall. 48px is Android's target, not the 44px floor.
     Padding rather than height, so nothing that wraps gets clipped. */
  .footer__col a { display: block; padding-block: 11px; }
  .footer__col li + li { margin-top: 0; }
  .footer__contact a { display: inline-block; padding-block: 12px; }
  .card__list a { display: inline-block; padding-block: 10px; }
  .link-more { padding-block: 11px; }
  /* The link, not `> *`: the other child is the current-language span, which
     the bar rule above hides, and a blanket display:inline-flex here would
     switch it back on — this block is the later one, so it would win. */
  .langswitch a { min-height: 44px; display: inline-flex; align-items: center; }
  .btn--sm { min-height: 48px; }
  /* The per-number WhatsApp pill on the contact page (33px) and the trade
     pages' breadcrumb (14px). Both found by tools/check-mobile.py on pages the
     first mobile pass never looked at. */
  .card__list .chat-link { min-height: 44px; }
  .crumb a { display: inline-block; padding-block: 15px; }
  .crumb { margin-bottom: 0; }

  /* ---------- Placeholder slots ----------
     Nine of them totalled 1,576px — 24% of a 6,522px page — to say "photo
     pending". Shorter on a phone until the photographs arrive. The real images
     use aspect-ratio rather than these heights, so nothing moves on swap and
     this costs nothing once they land. */
  .slot { min-height: 140px; }
  .slot--card { min-height: 104px; }
  .slot--cat { min-height: 170px; }
  .slot--why, .slot--about { min-height: 190px; }

  /* ---------- Chrome above the fold ----------
     The topbar wrapped to 60px on a phone against 41px on desktop, so 145px of
     chrome preceded any content. */
  .topbar { font-size: 12px; }
  .topbar__inner { padding-block: 7px; }
}

/* ==========================================================================
   Bengali
   DESIGN.md has said from the start that "Bengali does not use uppercase or
   letter-spacing — it has no case, and tracking breaks conjuncts." The rule was
   written down and never implemented: the stylesheet had no Bengali selector at
   all, so all eleven letter-spacing rules and all four uppercase rules landed
   on Bengali text as well as Latin.

   Measured on the Bengali home page before this block: 45 elements carrying
   tracking they should not have. Every heading got NEGATIVE tracking
   (-0.51px on the h1), which squeezes conjuncts — the densest part of the
   script — and the category labels got +1.4px, which pulls them apart. The
   uppercase is merely a no-op, since Bengali has no case; the tracking is the
   damage.

   Scoped two ways on purpose. html[lang="bn"] covers the /bn/ pages, and the
   bare [lang="bn"] catches the one Bengali string on an English page: the
   language switch reads বাং and carries its own lang attribute.

   Specificity rather than !important: html[lang="bn"] body * is (0,1,2), which
   beats the single-class rules it has to override without taking a hammer to
   anything that might legitimately need to win later.
   ========================================================================== */
html[lang="bn"] body, html[lang="bn"] body *,
[lang="bn"], [lang="bn"] * {
  letter-spacing: normal;
  text-transform: none;
}

/* Bengali needs more leading than Latin at the same size: the matra joins the
   tops of the letters into a continuous line, and conjuncts hang below the
   baseline, so lines that are comfortable in Barlow collide in a Bengali face.
   1.6 is right for the Latin body copy and tight for the same text in Bengali.
   The heading figure matters more than the body one — 1.12 was set for Latin
   display type and leaves conjuncts touching the line above.

   Both numbers were set for Hind Siliguri and re-measured when Noto replaced
   it, rather than carried over on faith. They stand: the ink above the baseline
   differs by 3–5% between the two faces, and line-height multiplies font-size,
   not the font's own box — which is the metric that actually moved. Noto also
   descends less (41 against 50 per 100px), so 1.35 has more room than before,
   not less. DESIGN.md carries the measurements. */
html[lang="bn"] p, html[lang="bn"] li, html[lang="bn"] .lede,
html[lang="bn"] .cat__long, html[lang="bn"] blockquote {
  line-height: 1.85;
}
html[lang="bn"] h1, html[lang="bn"] h2, html[lang="bn"] h3 {
  line-height: 1.35;
}

/* ==========================================================================
   Desktop only
   The frosted header, quarantined above the breakpoint where .nav stops being
   position: fixed. Anything that establishes a containing block —
   backdrop-filter, filter, transform, perspective, will-change, contain —
   belongs in here and not in a rule that also applies to the phone, because a
   fixed child silently re-parents onto it. That is what killed the mobile
   menu. If a blur is ever wanted on the phone header, the nav has to move out
   of the header in the markup first.
   ========================================================================== */
@media (min-width: 1025px) {
  .site-header {
    background: rgb(11 11 13 / 92%);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
  }
}

@media print {
  .topbar, .site-header, .nav, .skip-link, .marquee, .slot, .callbar { display: none !important; }
  body { padding-bottom: 0; }
  body { background: #fff; color: #000; }
  a { color: #000; }
  .card, .stats, .chips li { border-color: #999; background: #fff; }
  .band { background: #fff; color: #000; }
}

/* ==========================================================================
   Services — the contracting side of the business
   ========================================================================== */

.svc-strip { border-top: 1px solid var(--line); }
.svc-strip .section-head h2 { display: flex; align-items: center; gap: 11px; }
.svc-strip .section-head h2 .ic { color: var(--amber); width: 26px; height: 26px; }
.svc-strip .lede { margin-bottom: 22px; }
.chips--svc li { border-color: var(--line-2); color: var(--text); font-family: var(--font-display); font-weight: 600; }

.svc-grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(16px, 2vw, 24px);
}
/* Each trade gets a photo. Until the client sends them the slot holds a
   labelled placeholder at the right ratio, so nothing reflows on swap. */
.svc {
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  transition: border-color .18s ease, transform .18s ease;
}
.svc:hover { border-color: var(--amber); transform: translateY(-3px); }
.svc:hover .slot--svc { border-color: var(--amber); }
.slot--svc {
  min-height: 0; aspect-ratio: 4 / 3; padding: 16px;
  border-radius: 0; border-width: 0 0 1px 0; border-color: var(--line);
}
.svc__body { display: flex; flex-direction: column; gap: 5px; padding: 20px 22px 24px; }
.svc__n { margin: 0; font-family: var(--font-display); font-size: 12px; font-weight: 800; letter-spacing: .16em; color: var(--amber); }
.svc h3 { margin: 0; font-size: 19px; font-weight: 700; }
.svc p { margin: 0; font-size: 14.5px; line-height: 1.55; color: var(--muted-2); }

/* The works block sits on the alternate ground so it reads as its own half
   of the page, separate from the supply grid above it. */
.works { background: var(--bg-2); border-block: 1px solid var(--line); }
.works .section-head h2 { display: flex; align-items: center; gap: 11px; }
.works .section-head h2 .ic { color: var(--amber); width: 26px; height: 26px; }
.works .lede { margin-bottom: 26px; }

/* A trailing "etc." closes a list; it is not a product, so it reads quieter. */
.chips li.is-etc { background: transparent; border-style: dashed; color: var(--muted-2); font-style: italic; }

@media (max-width: 1024px) {
  .svc-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* Five nav items no longer fit beside the brand and the WhatsApp button. */
  .nav__list { gap: 2px; }
}
@media (max-width: 640px) {
  .svc-grid { grid-template-columns: 1fr; }
}

/* ==========================================================================
   Open-now badge, trade pages, Google review block
   ========================================================================== */

/* The badge is display:flex, and a flex container ignores the [hidden]
   attribute — the browser's UA rule is display:none, which any display
   declaration overrides. Without this the badge would flash its empty shell
   on every load and stay visible with JavaScript off. */
[hidden] { display: none !important; }

.openbadge {
  display: inline-flex; align-items: center; gap: 7px; margin: 0 0 0 14px;
  padding: 2px 10px 2px 8px; border-radius: 999px;
  background: rgb(21 21 21 / 10%); color: var(--ink);
  font-family: var(--font-display); font-weight: 700; font-size: 12px;
  vertical-align: middle;
}
.openbadge::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor;
}
.openbadge.is-open::before { background: #0a7d32; }
.openbadge.is-closed::before { background: #b3261e; }

/* ---------- Trade pages ---------- */
.crumb { margin: 0 0 10px; font-family: var(--font-display); font-size: 13px; font-weight: 600; }
.crumb a { color: var(--amber); }
.page-head .hero__actions { margin-top: 26px; }

.trade__grid {
  display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, .65fr);
  gap: clamp(20px, 3vw, 40px); align-items: start;
  padding-block: clamp(28px, 3.5vw, 48px);
}
.slot--trade { min-height: 0; aspect-ratio: 16 / 10; }
.trade__side h2 {
  display: flex; align-items: center; gap: 10px;
  font-size: 18px; font-weight: 700; margin-bottom: 14px;
}
.trade__side h2 .ic { color: var(--amber); }

.trade__others { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.trade__others a {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 14px 16px; border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface); color: var(--text);
  font-family: var(--font-display); font-weight: 600; font-size: 14.5px;
  transition: border-color .18s ease, color .18s ease;
}
.trade__others a:hover { border-color: var(--amber); color: var(--amber); }
.trade__others .ic { width: 16px; height: 16px; flex: none; }

/* The whole service card is the link now that each trade has a page. */
/* Each card is clickable as a whole AND carries its own quote button. A link
   inside a link is invalid and RULES.md forbids nesting interactive elements,
   so the heading's link is stretched over the card with ::after and the button
   sits above it in the stacking order. Two sibling links, each with its own
   accessible name, reached by the keyboard in reading order. */
.svc { position: relative; }
.svc__link { color: inherit; }
.svc__link:hover { color: inherit; }
.svc__link::after { content: ''; position: absolute; inset: 0; }
/* The stretched link has no box of its own to ring, so the ring goes on the
   overlay — without this the card gives no focus feedback at all. */
.svc__link:focus-visible { outline: none; }
.svc__link:focus-visible::after { outline: 3px solid var(--amber); outline-offset: -3px; border-radius: var(--r); }
.svc__body .link-more { margin-top: auto; padding-top: 10px; }

/* Above the overlay, or the card's link would swallow the button's clicks. */
.svc__cta { position: relative; z-index: 1; margin: 16px 0 0; }
.svc__cta .btn { width: 100%; }

/* ---------- Google review block ---------- */
.review { display: flex; align-items: center; gap: 18px; }
.review__qr {
  flex: none; width: 132px; height: 132px; padding: 8px;
  background: #fff; border-radius: var(--r-sm);
}
.review__title { margin: 0 0 6px; font-size: 17px; font-weight: 700; }
.review__body p { margin: 0 0 10px; font-size: 14px; color: var(--muted-2); }
.review--footer { margin-top: 24px; padding-top: 22px; border-top: 1px solid var(--line); }
.review--footer .review__qr { width: 104px; height: 104px; }
.review-band { background: var(--bg-2); border-block: 1px solid var(--line); }
.review--page { max-width: 640px; }

/* The honeypot: off-screen rather than display:none, because some bots skip
   fields they can tell are not rendered. Never shown, never focusable. */
.hp {
  position: absolute; left: -9999px; width: 1px; height: 1px;
  overflow: hidden; margin: 0;
}

@media (max-width: 1024px) {
  .trade__grid { grid-template-columns: 1fr; }
  .trade__others { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 760px) {
  .openbadge { margin-left: 10px; font-size: 11px; }
  .trade__others { grid-template-columns: 1fr; }
  .review { flex-direction: column; align-items: flex-start; gap: 14px; }
}

/* The item card's second action. The picture above it opens the lightbox;
   this asks the price of that one item on WhatsApp.

   margin-top:auto pins it to the bottom of the card. Names run one or two
   lines, the grid stretches every card in a row to the tallest, and without
   this the buttons would sit at different heights across a row — the one
   thing that makes a grid look broken rather than full.

   min-height 44px because tools/check-mobile.py fails the build below that,
   and this is the smallest control on the busiest page on the site. */
.item__cta {
  margin-top: auto;
  display: flex; align-items: center; justify-content: center; gap: 6px;
  min-height: 44px; padding: 0 8px;
  border-top: 1px solid var(--line-2);
  font-family: var(--font-display); font-size: 12.5px; font-weight: 600;
  color: var(--muted-2); text-align: center;
  transition: background-color .18s ease, color .18s ease;
}
.item__cta svg { flex: none; width: 15px; height: 15px; }
/* The label is what goes when the card is narrow; the icon never does, so
   the control still reads as "message us" at 132px. */
.item__cta span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.item__cta:hover, .item__cta:focus-visible { background: var(--amber); color: var(--ink); }
.item__cta:focus-visible { outline: 3px solid var(--amber); outline-offset: -3px; }
