/* ============================================================================
   Durlevel — customer-facing styles.

   Design concept: "a spec book issued to one buyer."
   The art direction is technical drawing: hairlines instead of shadows, a
   monospaced layer for numbers, labels and indexes, hard edges, and paper as
   the background. Everything a buyer came for is a figure — content, ratio,
   CAS number, MOQ — so the type system puts those figures in a font that
   lines them up, and spends its contrast on the table rather than on a hero.

   The type is one OFL face served from this origin, Archivo (see fonts.css),
   across its full 100–900 weight axis. Nothing is fetched from a third party —
   the CSP forbids it, and a Google Fonts request is a blocked request for the
   sales team in mainland China. With a single family, the title page, the body
   and the figures are told apart by weight, size, case and letter-spacing, not
   by a change of typeface.

   Constraints that shaped it:
     - no inline styles and no third-party anything, so the web fonts are
       self-hosted and the one piece of motion lives in /js/site.js;
     - no build step — one hand-written stylesheet, loaded as-is.
   ========================================================================= */

/* ---------- Design tokens ---------- */
:root {
  /* Brand green stays the identity, but it is an accent here, not a theme. */
  --brand: #11684f;
  --brand-dark: #0a4433;
  --brand-light: #e9f1ee;
  --accent: #8d6a28;

  --ink: #15191a;
  --ink-soft: #5f6a65;
  --ink-faint: #677069;

  /* Two weights of line: hairline for rhythm, strong for structure. */
  --line: #e2e5e0;
  --line-strong: #c7ccc5;

  --bg: #f4f4f1;
  --login-bg: #0b0e0f;
  --surface: #ffffff;
  --danger: #9d2f27;

  /* Technical drawing has no soft edges, and paper casts no shadow. */
  --radius: 3px;
  --radius-sm: 2px;
  --shadow: 0 1px 0 rgba(21, 25, 26, .03);

  /* One family for the whole site. Latin first, Chinese after: a Latin face
     carries no hanzi, and a Chinese face's own Latin glyphs are mediocre. */
  --font: "Archivo", "Archivo Fallback", "PingFang SC", "Hiragino Sans GB",
          "Microsoft YaHei", "Noto Sans SC", sans-serif;

  /* These two name a ROLE, not a second face — both resolve to the family
     above. They are kept because the roles are real and every size, weight and
     letter-spacing decision below hangs off them:
       --display  the title page: the largest sizes, tightest tracking
       --label    the figures and annotations: specs, CAS numbers, indexes,
                  sheet numbers, column headers, small capitalised labels
     With a single family, the difference between them has to be carried by
     weight, size, case and tracking instead of by changing typeface. */
  --display: var(--font);
  --label: var(--font);

  /* Spacing: multiples of 4. */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
  --s5: 24px; --s6: 32px; --s7: 48px; --s8: 64px; --s9: 96px;

  /* Type scale, ratio 1.25, carried far enough up that the title page is
     4.5x the body rather than the 2.8x it used to be. */
  --t-xs: 11.5px; --t-sm: 13px; --t-base: 15px; --t-md: 17px;
  --t-lg: 21px;   --t-xl: 26px; --t-2xl: 33px;  --t-3xl: 42px;
  --t-4xl: 54px;  --t-5xl: 68px;

  --ease: cubic-bezier(.2, .8, .2, 1);
  --dur: 180ms;
  /* The signature rule draws at a slower, deliberate pace than a hover. */
  --dur-draw: 520ms;
}

* { box-sizing: border-box; }

/* An explicit `display` on a class beats the UA rule for [hidden], so the
   admin's category-driven field toggling needs this to take effect. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

/* A page fills exactly one screen, whatever the screen is — a laptop, a
   27-inch monitor or a phone — and grows past that only when the content
   needs it. dvh tracks the phone browser's collapsing toolbar; vh is the
   fallback. */
:root { --page-min-height: 100vh; }
@supports (height: 100dvh) {
  :root { --page-min-height: 100dvh; }
}

html { min-height: 100%; }

body {
  margin: 0;
  min-height: var(--page-min-height);
  display: flex;
  flex-direction: column;
  font-family: var(--font);
  font-size: var(--t-base);
  line-height: 1.6;
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

/* The content area takes the slack, so the footer lands on the bottom edge. */
body > main { flex: 1 0 auto; padding-bottom: var(--s8); }
body > .site-footer { flex-shrink: 0; margin-top: 0; }

a { color: var(--brand-dark); }

img { max-width: 100%; }

/* One focus treatment for the whole site, so keyboard users always see the
   same mark. */
:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.wrap { width: min(1240px, 100% - 48px); margin-inline: auto; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* A small uppercase monospaced label — the voice of the drawing. */
.eyebrow {
  font-family: var(--label); font-weight: 500;
  font-size: var(--t-xs);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 10px 18px;
  border: 1px solid var(--brand);
  border-radius: var(--radius);
  background: var(--brand); color: #fff;
  font: inherit; font-size: var(--t-sm); font-weight: 600;
  letter-spacing: .01em;
  text-decoration: none; cursor: pointer;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease),
              color var(--dur) var(--ease), transform 90ms var(--ease);
}
.btn:hover { background: var(--brand-dark); border-color: var(--brand-dark); }
/* Pressed is a stamp, not a bounce: the control sinks one pixel, the distance
   a rubber stamp travels. */
.btn:active { transform: translateY(1px); }
.btn-ghost {
  background: transparent; color: var(--ink);
  border-color: var(--line-strong);
}
.btn-ghost:hover { background: var(--ink); border-color: var(--ink); color: #fff; }
.btn-danger { background: var(--danger); border-color: var(--danger); }
.btn-danger:hover { background: #7f2520; border-color: #7f2520; }
.btn-sm { padding: 6px 12px; font-size: var(--t-xs); }
.btn-block { width: 100%; }

/* ---------- Forms ---------- */
.field { display: block; margin-bottom: var(--s4); }
.field > span {
  display: block; margin-bottom: 6px;
  font-family: var(--label); font-size: var(--t-xs);
  letter-spacing: .12em; text-transform: uppercase; color: var(--ink-soft);
}
.field input, .field select, .field textarea {
  width: 100%; padding: 10px 12px;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--surface);
  font: inherit; font-size: var(--t-base); color: var(--ink);
  transition: border-color var(--dur) var(--ease);
}
.field input:hover, .field select:hover, .field textarea:hover { border-color: var(--ink-faint); }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-light);
}
.field .hint {
  font-family: var(--font); font-weight: 400; font-size: var(--t-xs);
  letter-spacing: 0; text-transform: none; color: var(--ink-faint);
}

.alert {
  margin: 0 0 var(--s4);
  padding: 12px 14px 12px 16px;
  border: 1px solid var(--line);
  border-left-width: 3px;
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-size: var(--t-sm);
}
.alert-error { border-left-color: var(--danger); color: var(--danger); }
.alert-ok { border-left-color: var(--brand); color: var(--brand-dark); }

/* ---------- Sign in ----------
   The one page a customer sees before anything else, so it carries the
   concept hardest: a dark drafting panel on the left with a faint coordinate
   grid, the form on the right. */
.login-body { background: var(--login-bg); }

.login-shell {
  flex: 1 0 auto;
  display: grid; place-items: center;
  padding: var(--s7) var(--s5);
}

.login-card {
  width: min(880px, 100%);
  display: grid;
  grid-template-columns: 1fr;
  background: var(--surface);
  border: 1px solid rgba(255, 255, 255, .12);
  border-radius: var(--radius);
  overflow: hidden;
}

.login-aside {
  position: relative;
  padding: var(--s7) var(--s6);
  background: #1b2021;
  color: #fff;
  display: flex; flex-direction: column; justify-content: space-between;
  gap: var(--s6);
  min-height: 180px;
}
/* The drafting grid: two hairline gradients, no image to load. */
.login-aside::before {
  content: "";
  position: absolute; inset: 0;
  background-image:
    linear-gradient(to right, rgba(255, 255, 255, .085) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255, 255, 255, .085) 1px, transparent 1px);
  background-size: 32px 32px;
  pointer-events: none;
}
.login-aside > * { position: relative; }
.login-aside .eyebrow { color: rgba(255, 255, 255, .66); }
.login-aside .logo-img { max-height: 64px; max-width: 220px; }
.login-aside .logo-text {
  margin: var(--s4) 0 0;
  font-family: var(--display); font-weight: 600;
  font-size: clamp(31px, 4.4vw, var(--t-3xl));
  letter-spacing: -.03em; line-height: 1;
}
.login-aside .tagline {
  margin: var(--s4) 0 0;
  color: rgba(255, 255, 255, .72);
  font-size: var(--t-sm); line-height: 1.6; max-width: 34ch;
}

/* The contents of the book, numbered like a table of contents — and read from
   the categories the administrator has actually published. */
.login-index {
  list-style: none; margin: 0; padding: var(--s4) 0 0;
  border-top: 1px solid rgba(255, 255, 255, .18);
}
.login-index li {
  display: flex; align-items: baseline; gap: var(--s3);
  padding: 7px 0;
  font-family: var(--label); font-size: var(--t-xs);
  letter-spacing: .12em; text-transform: uppercase;
  color: rgba(255, 255, 255, .78);
}
.login-index li + li { border-top: 1px solid rgba(255, 255, 255, .09); }
.login-index .n {
  color: rgba(255, 255, 255, .5);
  font-variant-numeric: tabular-nums; letter-spacing: .06em;
}

.login-form { padding: var(--s7) var(--s6); }
.login-form h1 {
  margin: var(--s3) 0 var(--s6);
  font-family: var(--display); font-weight: 600;
  font-size: clamp(21px, 2.6vw, var(--t-xl));
  letter-spacing: -.02em; line-height: 1.12;
}
.login-note {
  margin: var(--s5) 0 0; padding-top: var(--s4);
  border-top: 1px solid var(--line);
  color: var(--ink-soft); font-size: var(--t-xs); line-height: 1.7;
}

@media (min-width: 760px) {
  .login-card { grid-template-columns: 1.05fr 1fr; }
  .login-aside { min-height: 420px; }
}
@media (max-width: 480px) {
  .login-shell { padding: var(--s4); }
  .login-aside, .login-form { padding: var(--s5) var(--s4); }
}

/* ---------- Masthead ---------- */
.site-header {
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 20;
}
.site-header .bar {
  display: flex; align-items: center; gap: var(--s5);
  min-height: 60px;
}
.brand-link {
  display: inline-flex; align-items: center; gap: 10px;
  color: var(--ink); text-decoration: none;
  font-family: var(--display); font-weight: 600;
  font-size: var(--t-md); letter-spacing: -.01em;
}
.brand-link img { max-height: 34px; }

.site-nav { display: flex; align-items: center; gap: var(--s1); margin-left: auto; }
.site-nav a {
  position: relative;
  padding: 10px; min-height: 44px; display: inline-flex; align-items: center;
  color: var(--ink-soft); text-decoration: none;
  font-family: var(--label); font-size: var(--t-xs); font-weight: 400;
  letter-spacing: .1em; text-transform: uppercase;
  transition: color var(--dur) var(--ease);
}
.site-nav a:hover { color: var(--ink); }
/* The active page is marked with a rule under it, the way a drawing marks a
   current sheet — not with a filled pill. */
.site-nav a.active { color: var(--ink); }
.site-nav a.active::after {
  content: ""; position: absolute; left: 10px; right: 10px; bottom: 8px;
  height: 2px; background: var(--brand);
}
.nav-count {
  display: inline-block; min-width: 18px; margin-left: 6px; padding: 0 5px;
  border-radius: 999px; background: var(--brand); color: #fff;
  font-family: var(--label); font-size: 10.5px; line-height: 18px; text-align: center;
}
.user-chip {
  display: inline-flex; align-items: center; gap: var(--s3);
  margin-left: var(--s4); padding-left: var(--s4);
  border-left: 1px solid var(--line);
  font-size: var(--t-sm); color: var(--ink-soft);
}
.logout-btn {
  border: 0; background: none; padding: 0;
  font: inherit; font-size: var(--t-sm); color: var(--ink-soft);
  text-decoration: underline; text-underline-offset: 3px; cursor: pointer;
}
.logout-btn:hover { color: var(--ink); }

@media (max-width: 860px) {
  .site-header { position: static; }
  .site-header .bar { flex-wrap: wrap; padding: var(--s2) 0; gap: 0 var(--s2); min-height: 0; }
  /* The account sits beside the wordmark on the first row and the links take
     the second, instead of each taking a row of its own. */
  .brand-link { order: 1; }
  .user-chip { order: 2; margin-left: auto; padding-left: 0; border-left: 0; }
  .site-nav { order: 3; margin-left: -10px; width: 100%; flex-wrap: wrap; }
}

/* ---------- Title block ----------
   The signature moment. Every engineering drawing carries a ruled title block
   naming what the sheet is, who it was issued to and when; this is that block,
   and because its cells are filled from the signed-in account, no two buyers
   are looking at the same page. It also gives the title somewhere to sit other
   than floating in a half-empty band. */
.page-head {
  padding: clamp(var(--s6), 6vw, var(--s9)) 0 0;
  margin-bottom: var(--s7);
  border-bottom: 1px solid var(--ink);
}
.page-head h1 {
  margin: var(--s3) 0 0;
  font-family: var(--display); font-weight: 600;
  font-size: clamp(31px, 5.4vw, var(--t-4xl));
  line-height: 1.04; letter-spacing: -.035em;
  text-wrap: balance;
}
.page-head .lede {
  margin: var(--s4) 0 0; max-width: 54ch;
  color: var(--ink-soft); font-size: var(--t-md); line-height: 1.65;
}

/* Wide screens get a two-column masthead — title left, the standing text set
   against its baseline on the right — rather than a tall title with half the
   screen empty beside it. The ruled block always spans both. */
@media (min-width: 1000px) {
  .page-head {
    display: grid; align-items: end;
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
    column-gap: var(--s8);
  }
  .page-head .title-main { grid-column: 1; }
  .page-head .lede { grid-column: 2; margin: 0 0 10px; }
  .page-head .title-block { grid-column: 1 / -1; }
}

/* The ruled cells. A real title block has hard divisions, so these are table
   rules rather than cards: one hairline grid, no gaps, no shadows. */
.title-block {
  display: grid; margin-top: clamp(var(--s6), 5vw, var(--s8));
  grid-template-columns: repeat(2, 1fr);
  border-top: 1px solid var(--line-strong);
  border-left: 1px solid var(--line-strong);
}
.title-block > div {
  padding: var(--s4);
  border-right: 1px solid var(--line-strong);
  border-bottom: 1px solid var(--line-strong);
  min-width: 0;
}
.title-block dt {
  margin: 0;
  font-family: var(--label); font-size: var(--t-xs); font-weight: 400;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink-faint);
}
.title-block dd {
  margin: 8px 0 0;
  font-family: var(--label); font-size: clamp(16px, 1.5vw, var(--t-lg));
  font-weight: 500; line-height: 1.2;
  font-variant-numeric: tabular-nums; letter-spacing: -.01em; color: var(--ink);
  overflow-wrap: anywhere;
}
@media (min-width: 680px) { .title-block { grid-template-columns: repeat(4, 1fr); } }

/* Pages other than the catalogue keep the same masthead without the block. */
.page-head.plain { margin-bottom: var(--s6); padding-bottom: var(--s4); }

/* ---------- Sheet counter ----------
   The other half of the signature: a drawing is leafed through, so the margin
   of the page tells you which sheet you are on, and changes as you scroll.
   Desktop only — the same information is in every section header, so a phone
   loses nothing by not showing it. */
.sheet-counter { display: none; }

/* A 1440 screen leaves a 100px gutter — too narrow for the category name, but
   not too narrow for the sheet number, which is the half that matters. */
@media (min-width: 1360px) and (max-width: 1559px) {
  .sheet-counter {
    display: block; position: fixed; z-index: 15;
    left: calc((100vw - 1240px) / 2 - 76px);
    top: 50%; transform: translateY(-50%);
    width: 56px; text-align: right;
    padding-right: var(--s3);
    border-right: 1px solid var(--line-strong);
    font-family: var(--label); font-size: var(--t-xs);
    letter-spacing: .08em; text-transform: uppercase; color: var(--ink-faint);
    pointer-events: none;
  }
  .sheet-counter .num {
    display: block; margin-bottom: 3px;
    font-size: var(--t-lg); font-weight: 500; line-height: 1;
    font-variant-numeric: tabular-nums; color: var(--ink); letter-spacing: 0;
  }
  .sheet-counter .of { display: block; }
  /* No room for the name at this width; the section heading carries it. */
  .sheet-counter .label { display: none; }
}

@media (min-width: 1560px) {
  .sheet-counter {
    display: block; position: fixed; z-index: 15;
    /* In the left margin, flush against the text block, the way a bound book
       carries its tabs — and where a long category name has room to sit on
       two lines without crowding the table. */
    left: calc((100vw - 1240px) / 2 - 152px);
    top: 50%; transform: translateY(-50%);
    width: 128px; text-align: right;
    font-family: var(--label); font-size: var(--t-xs);
    letter-spacing: .1em; text-transform: uppercase; color: var(--ink-faint);
    border-right: 1px solid var(--line-strong);
    padding-right: var(--s4);
    pointer-events: none;
  }
  .sheet-counter .num {
    display: block; margin-bottom: 4px;
    font-size: var(--t-xl); font-weight: 500; line-height: 1;
    font-variant-numeric: tabular-nums; color: var(--ink);
    letter-spacing: 0;
  }
  .sheet-counter .of { display: block; }
  .sheet-counter .label {
    display: block; margin-top: var(--s2); color: var(--ink-soft);
    letter-spacing: .1em;
  }
}


/* ---------- Toolbar: search + category index ---------- */
.toolbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s4) var(--s5);
  padding-bottom: var(--s5);
}
.search-form { display: flex; gap: var(--s2); flex: 1 1 280px; min-width: 0; }
.search-form input {
  flex: 1; min-width: 0; padding: 10px 14px;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--surface); font: inherit; font-size: var(--t-sm);
}
.search-form input:focus {
  outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-light);
}

.cat-nav { display: flex; flex-wrap: wrap; gap: 0 var(--s5); }
.cat-nav a {
  position: relative;
  display: inline-flex; align-items: baseline; gap: 8px;
  padding: 10px 0; min-height: 44px;
  color: var(--ink); text-decoration: none;
  font-family: var(--label); font-size: var(--t-xs);
  letter-spacing: .1em; text-transform: uppercase;
}
/* The rule grows from the left on hover, the same gesture as a sheet's rule
   drawing itself — so the index behaves like the thing it indexes. */
.cat-nav a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 6px; height: 1px;
  background: var(--brand);
  transform: scaleX(0); transform-origin: left center;
  transition: transform var(--dur) var(--ease);
}
.cat-nav a:hover::after, .cat-nav a:focus-visible::after { transform: scaleX(1); }
.cat-nav .n { color: var(--ink-faint); letter-spacing: .06em; }
.cat-nav .count { color: var(--ink-faint); }

.search-summary {
  margin: 0 0 var(--s4);
  color: var(--ink-soft); font-size: var(--t-sm);
}

/* ---------- Product sections ----------
   Each category is a numbered sheet. The spacing deliberately alternates —
   a large gap before a sheet, a medium one inside it — so scrolling has a
   rhythm instead of one repeated padding value. */
.product-section {
  margin-top: clamp(var(--s7), 6vw, var(--s9));
  scroll-margin-top: 84px;
}
.product-section:first-of-type { margin-top: var(--s6); }
.section-head {
  position: relative;
  display: flex; align-items: baseline; gap: var(--s3) var(--s4);
  padding-bottom: var(--s4);
  margin-bottom: var(--s5);
}
/* The rule is a pseudo-element rather than a border so it can be drawn. */
.section-head::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: var(--ink);
  transform-origin: left center;
}
.section-head .index {
  font-family: var(--label); font-size: var(--t-sm); font-weight: 500;
  color: var(--ink-faint); letter-spacing: .08em;
  font-variant-numeric: tabular-nums;
}
/* The display serif again, so the concept survives past the first screen
   instead of degrading into a generic bold heading. */
.section-head h2 {
  margin: 0;
  font-family: var(--display); font-weight: 600;
  font-size: clamp(20px, 2.6vw, var(--t-xl));
  line-height: 1.15; letter-spacing: -.025em;
}
.section-head .count {
  margin-left: auto; white-space: nowrap;
  font-family: var(--label); font-size: var(--t-xs);
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-faint);
}
@media (max-width: 600px) {
  .section-head { flex-wrap: wrap; }
  .section-head .count { margin-left: 0; width: 100%; }
}

/* The signature animation: reaching a sheet draws its rule across the page,
   left to right, once. The class on <html> is set by /js/site.js, so without
   JavaScript — and under reduced motion — every rule is simply already there. */
@media (prefers-reduced-motion: no-preference) {
  .js-draw .section-head::after { transform: scaleX(0); }
  .js-draw .section-head.is-drawn::after {
    transform: scaleX(1);
    transition: transform var(--dur-draw) var(--ease);
  }
}
/* Tight screens: the letter-spacing that makes a label read as a drawing
   annotation is what makes it wrap on a phone. */
@media (max-width: 420px) {
  .eyebrow, .cat-nav a, table.products th { letter-spacing: .08em; }
}

/* `position: relative` keeps any visually-hidden text inside this box: left
   loose it stretches the page and creates a horizontal scrollbar. */
.table-scroll {
  position: relative;
  overflow-x: auto;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

table.products {
  width: 100%; border-collapse: collapse; font-size: var(--t-sm);
  /* Fixed, so the name column cannot swallow the row and leave the figures
     floating in the middle with the action button far off to the right. */
  table-layout: fixed;
}
table.products th {
  padding: 12px 16px;
  border-bottom: 1px solid var(--line-strong);
  background: var(--surface);
  text-align: left; vertical-align: bottom;
  font-family: var(--label); font-size: var(--t-xs); font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase; color: var(--ink-faint);
  white-space: nowrap;
}
table.products td {
  padding: 16px;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
table.products tbody tr:last-child td { border-bottom: 0; }
/* The one piece of motion on this page: the row being read draws a brand edge
   and lifts its paper a shade. */
table.products tbody tr {
  transition: background var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
table.products tbody tr:hover,
table.products tbody tr:focus-within {
  background: #fbfbf9;
  box-shadow: inset 3px 0 0 var(--brand);
}
/* Spec values are figures, so they get the figure font. */
table.products td:not(.col-name):not(.col-act):not(.col-img) {
  font-family: var(--label); font-size: var(--t-sm);
  font-variant-numeric: tabular-nums; color: var(--ink-soft);
}
/* 76px 列减去单元格左右各 16px 的内边距，只剩 44px —— 比缩略图本身还窄，
   方框被压成竖条，横幅照片于是被裁掉两边。列宽要容得下图 + 内边距。 */
.col-img { width: 100px; }

/* A specification value is short — a ratio, a percentage, a CAS number — so
   its column is capped instead of taking an equal share of the table. Before
   this, "10:1" sat in a 293px column while the buttons beside it were being
   cut in half. The percentage keeps the cap from crowding out the product
   name on a narrow laptop, where 14% is the smaller of the two. */
.col-spec { width: 168px; }
/* Two spec columns can afford to be wide enough that "Rosmarinic acid 5%"
   stays on one line; five cannot, or the product name is left with nothing.
   The class comes from the template, which is the only place the count is
   known. */
.products.cols-1 .col-spec { width: 240px; }
.products.cols-2 .col-spec { width: 204px; }
.products.cols-3 .col-spec { width: 184px; }
.products.cols-4 .col-spec { width: 160px; }
.products.cols-5 .col-spec { width: 140px; }

/* On a laptop every cap has to give, so they go back to a share of the table.
   A percentage rather than min(168px, 13%): a math function containing a
   percentage is ignored for a column in a fixed table layout, which is how
   "10:1" came to sit in a 354px column in the first place. */
@media (max-width: 1180px) {
  .col-spec,
  .products.cols-1 .col-spec, .products.cols-2 .col-spec,
  .products.cols-3 .col-spec, .products.cols-4 .col-spec,
  .products.cols-5 .col-spec { width: 15%; }
  .products.cols-4 .col-spec { width: 13%; }
  .products.cols-5 .col-spec { width: 11%; }
}

/* Wide enough for a spec-sheet button and "+ Inquiry" side by side. A product
   with several documents wraps onto a second line rather than overflowing:
   a taller row beats a button sliced down the middle. */
.col-act { width: 176px; text-align: right; }

/* ---------- The table, re-composed for a phone ----------
   A specification table does not survive being shrunk, and asking a buyer to
   swipe a table sideways on a phone is not a design. Below 860px every row
   becomes its own spec card: the name and picture on the first line, then one
   ruled label/value line per attribute, then the actions. The labels come from
   the `data-label` attributes on the cells, so the column headers and the card
   labels can never drift apart. */
@media (max-width: 860px) {
  .table-scroll {
    overflow-x: visible;
    background: none; border: 0; border-radius: 0;
  }
  table.products { display: block; table-layout: auto; }
  table.products thead { display: none; }
  table.products tbody { display: block; }

  table.products tbody tr {
    display: grid; grid-template-columns: auto minmax(0, 1fr);
    gap: 0 var(--s4);
    margin-bottom: var(--s3);
    padding: var(--s4);
    background: var(--surface);
    border: 1px solid var(--line);
    border-left: 3px solid var(--line-strong);
    border-radius: var(--radius);
  }
  table.products tbody tr:last-child { margin-bottom: 0; }
  /* The hover edge was an inset shadow on a table row; as a card it is the
     left border's colour instead. */
  table.products tbody tr:hover,
  table.products tbody tr:focus-within {
    background: var(--surface); box-shadow: none;
    border-left-color: var(--brand);
  }

  table.products td { display: block; padding: 0; border: 0; }
  table.products td.col-img { grid-column: 1; width: auto; }
  table.products td.col-name { grid-column: 2; }
  table.products tbody tr > td:not(.col-img):not(.col-name) { grid-column: 1 / -1; }

  /* One ruled line per attribute, label left, figure right. */
  table.products td[data-label] {
    display: flex; align-items: baseline;
    justify-content: space-between; gap: var(--s4);
    margin-top: var(--s3); padding-top: var(--s3);
    border-top: 1px solid var(--line);
    text-align: right;
  }
  table.products td[data-label] + td[data-label] { margin-top: 0; }
  table.products td[data-label]::before {
    content: attr(data-label);
    flex: 0 0 auto; text-align: left;
    font-family: var(--label); font-size: var(--t-xs); font-weight: 400;
    letter-spacing: .12em; text-transform: uppercase; color: var(--ink-faint);
  }

  table.products td.col-act {
    width: auto; text-align: left; white-space: normal;
    margin-top: var(--s4); padding-top: var(--s4);
    border-top: 1px solid var(--line-strong);
  }
  table.products td.col-act .row-act { justify-content: flex-start; flex-wrap: wrap; }
  /* The action is the point of the card on a phone, so it stops being a ghost
     button that only wakes up on a hover no phone can perform. */
  table.products tbody tr .row-act .btn-ghost {
    border-color: var(--brand); color: var(--brand-dark);
  }
  table.products tbody tr .row-act .btn-sm { padding: 9px 14px; }
  /* 43x27 is not a thumb target: on a phone the spec-sheet buttons get the
     same height as every other control. */
  .col-act .doc-link { min-height: 44px; padding: 0 14px; }
  .empty-row { padding: var(--s6) 0; text-align: left; }
}

/* No width of its own: in a fixed layout the unsized column takes whatever is
   left over, and the product name is the one thing in this table that is
   genuinely long. */
.col-name { min-width: 200px; }
.col-name .name {
  display: block; font-size: var(--t-md); font-weight: 600; letter-spacing: -.01em;
}
.col-name .tag-list { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 8px; }
.col-name .remark {
  display: block; margin-top: 6px;
  color: var(--ink-soft); font-size: var(--t-xs); line-height: 1.6;
}
.tag {
  display: inline-block; padding: 2px 8px;
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  font-family: var(--label); font-size: 10.5px;
  letter-spacing: .06em; text-transform: uppercase; color: var(--ink-soft);
}
/* contain 而不是 cover：买家要看的是这批货长什么样，不是一个构图好看的方块。
   cover 会把横幅照片的左右两边直接切掉，圆盘、袋子、瓶身都只剩中间一条。
   留白落在边框里，读起来像一个画框，不像缺了一块。 */
.thumb {
  display: block; width: 68px; height: 68px; aspect-ratio: 1;
  object-fit: contain; object-position: center;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface);
}
.row-act {
  display: inline-flex; flex-wrap: wrap; align-items: center;
  gap: 8px; justify-content: flex-end;
}
/* The group wraps; the individual buttons never do. Dropping nowrap from the
   column is what lets a second document move to its own line, and this is what
   stops "+ Inquiry" breaking across two in the process. */
.row-act > * { white-space: nowrap; }
.row-act .btn-sm {
  font-family: var(--label); font-size: 10.5px;
  letter-spacing: .08em; text-transform: uppercase;
}
/* The row being read brings its action forward. */
table.products tbody tr:hover .row-act .btn-ghost,
table.products tbody tr:focus-within .row-act .btn-ghost {
  border-color: var(--brand); color: var(--brand-dark); background: transparent;
}
table.products tbody tr .row-act .btn-ghost:hover { background: var(--brand); border-color: var(--brand); color: #fff; }
.empty-row { padding: var(--s7) var(--s4); text-align: center; color: var(--ink-faint); }

.doc-link {
  display: inline-flex; align-items: center; padding: 4px 10px;
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  color: var(--ink-soft); text-decoration: none;
  font-family: var(--label); font-size: 10.5px;
  letter-spacing: .08em; text-transform: uppercase;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.doc-link:hover { color: var(--brand-dark); border-color: var(--brand); }
.in-basket {
  font-family: var(--label); font-size: var(--t-xs); font-weight: 500;
  letter-spacing: .06em; color: var(--brand); white-space: nowrap;
}
.muted { color: var(--ink-soft); }

/* ---------- Viewer ----------
   Looking at a photograph, a product shot or a spec sheet without leaving the
   page. It is the same drawing office as everything else: the dark panel from
   the sign-in page, a ruled bar across the top, monospaced controls. The
   picture itself sits on paper, because a specification photograph is a plate
   in a book, not a slide in a gallery.

   The trigger is always a real link to the real file, so none of this is
   load-bearing: with the script blocked, clicking opens the file in a tab. */
.viewer {
  position: fixed; inset: 0; z-index: 60;
  display: grid; grid-template-rows: auto minmax(0, 1fr) auto;
  /* Opaque enough that the page behind stops competing for attention: at .94
     the headings and the table were still readable through it. */
  background: rgba(11, 14, 15, .985);
}
.viewer[hidden] { display: none; }
/* The page behind must not scroll under an open viewer. */
.viewer-open, .viewer-open body { overflow: hidden; }

.viewer-bar {
  display: flex; align-items: center; gap: var(--s4);
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid rgba(255, 255, 255, .18);
}
.viewer-title {
  margin: 0; min-width: 0; flex: 1 1 auto;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: var(--label); font-size: var(--t-xs);
  letter-spacing: .12em; text-transform: uppercase;
  color: rgba(255, 255, 255, .88);
}
.viewer-tools { display: flex; align-items: center; gap: var(--s1); flex-shrink: 0; }
.viewer-zoom {
  min-width: 56px; margin-right: var(--s2); text-align: right;
  font-family: var(--label); font-size: var(--t-xs);
  font-variant-numeric: tabular-nums; color: rgba(255, 255, 255, .7);
}
.viewer-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; height: 44px; padding: 0 var(--s3);
  border: 1px solid rgba(255, 255, 255, .28); border-radius: var(--radius-sm);
  background: transparent; color: #fff; text-decoration: none;
  font-family: var(--label); font-size: var(--t-xs); font-weight: 400;
  letter-spacing: .08em; text-transform: uppercase; cursor: pointer;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.viewer-btn:hover { background: rgba(255, 255, 255, .12); border-color: rgba(255, 255, 255, .55); }
.viewer-btn:active { transform: translateY(1px); }
.viewer-btn[disabled] { opacity: .35; cursor: not-allowed; }
.viewer-btn[hidden] { display: none; }
.viewer-btn-wide { letter-spacing: .1em; }
.viewer-btn-close { border-color: rgba(255, 255, 255, .5); font-size: var(--t-sm); }
.viewer :focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

.viewer-stage {
  position: relative; overflow: hidden;
  display: grid; place-items: center;
  padding: var(--s4);
  touch-action: none;   /* we handle pinch and drag ourselves */
}
.viewer-image {
  max-width: 100%; max-height: 100%;
  background: var(--surface);
  transform-origin: center center;
  transition: transform var(--dur) var(--ease);
  cursor: zoom-in; user-select: none; -webkit-user-drag: none;
}
.viewer-stage.is-zoomed .viewer-image { cursor: grab; }
.viewer-stage.is-zoomed .viewer-image:active { cursor: grabbing; }
/* Dragging a zoomed picture must track the finger exactly, so the easing that
   makes a button press feel considered is wrong here. */
.viewer-stage.is-zoomed .viewer-image { transition: none; }
.viewer-frame {
  width: 100%; height: 100%; border: 0;
  background: var(--surface); border-radius: var(--radius-sm);
}
.viewer-frame[hidden], .viewer-image[hidden] { display: none; }

.viewer-hint {
  margin: 0; padding: var(--s3) var(--s4);
  border-top: 1px solid rgba(255, 255, 255, .14);
  font-family: var(--label); font-size: var(--t-xs);
  letter-spacing: .08em; color: rgba(255, 255, 255, .62);
  text-align: center;
}
.viewer.is-still .viewer-image { transition: none; }

@media (max-width: 640px) {
  .viewer-bar { flex-wrap: wrap; gap: var(--s2); padding-inline: var(--s3); }
  .viewer-title { flex: 1 0 100%; }
  .viewer-tools { width: 100%; justify-content: flex-end; flex-wrap: nowrap; }
  .viewer-zoom { margin-right: auto; min-width: 0; text-align: left; }
  /* Six controls will not fit one row on a phone, and two rows of buttons
     over the picture is worse than one control fewer: "open in a new tab"
     goes, because Download already gets the file out of here. */
  .viewer-btn-wide:not([download]):not(.viewer-btn-close) { display: none; }
  .viewer-btn { min-width: 46px; padding: 0 var(--s2); }
  .viewer-hint { font-size: 10.5px; letter-spacing: .04em; }
}

/* ---------- Things that open the viewer ---------- */
/* A thumbnail is a door, so it says so on hover and on focus. */
.thumb-link {
  display: inline-block; position: relative;
  border-radius: var(--radius-sm);
  transition: border-color var(--dur) var(--ease);
}
.thumb-link .thumb, .thumb-link .staff-photo, .thumb-link .rep-photo,
.thumb-link .mini-thumb {
  transition: opacity var(--dur) var(--ease);
}
.thumb-link:hover .thumb, .thumb-link:hover .staff-photo,
.thumb-link:hover .rep-photo, .thumb-link:hover .mini-thumb {
  border-color: var(--brand); opacity: .88;
}
.thumb-link::after {
  content: "⤢";
  position: absolute; right: 3px; bottom: 3px;
  width: 18px; height: 18px; line-height: 17px; text-align: center;
  border-radius: var(--radius-sm);
  background: var(--ink); color: #fff; font-size: 10px;
  opacity: 0; transition: opacity var(--dur) var(--ease);
}
.thumb-link:hover::after, .thumb-link:focus-visible::after { opacity: 1; }

/* ---------- Contact ---------- */
/* Three to a row, fixed — not auto-fill. auto-fill packed as many 300px
   columns as the 1240px page would take, which is four, and a 310px column
   leaves the body 174px once the photo and the padding are paid for. An
   address does not fit in what is left after the label, so it broke as
   "info@durlevel." / "com". Three columns give each card ~413px, and the
   longest line on it — an address, a WhatsApp number — sits on one line. */
.staff-grid {
  display: grid; gap: 0;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  /* Paid back by each card's -1px margin, so the frame still starts exactly
     where the rule above it does. */
  padding: 1px 0 0 1px;
}
/* The frame is drawn by the cards, not by the grid around them. Every card
   carries all four sides and is pulled a pixel up and left so touching
   borders land on each other. A row with an empty slot — two people in a
   three-column grid — then stops at the last card instead of trailing a
   hairline off into blank paper. */
.staff-card {
  display: flex; gap: var(--s4);
  padding: var(--s5);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  margin: -1px 0 0 -1px;
}
.staff-photo, .staff-photo-empty {
  width: 72px; height: 72px; aspect-ratio: 1; flex-shrink: 0;
  border-radius: var(--radius-sm);
}
.staff-photo { object-fit: cover; border: 1px solid var(--line); }
.staff-photo-empty {
  display: grid; place-items: center;
  background: var(--ink); color: #fff;
  font-size: var(--t-lg); font-weight: 600;
}
.staff-body { min-width: 0; }
.staff-body h3 { margin: 0 0 2px; font-size: var(--t-md); font-weight: 600; }
.staff-body .role {
  margin: 0 0 var(--s3);
  font-family: var(--label); font-size: var(--t-xs);
  letter-spacing: .1em; text-transform: uppercase; color: var(--accent);
}
.staff-contact { list-style: none; margin: 0; padding: 0; font-size: var(--t-sm); }
.staff-contact li {
  display: flex; gap: var(--s3); padding: 5px 0;
  border-top: 1px solid var(--line);
}
.staff-contact li:first-child { border-top: 0; }
.staff-contact .label {
  flex-shrink: 0; min-width: 76px;
  font-family: var(--label); font-size: var(--t-xs);
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-faint);
}
.staff-contact a { text-decoration: none; font-weight: 500; }
.staff-contact a:hover { text-decoration: underline; }
/* Numbers are figures: they line up and they never break mid-number. */
.staff-contact li a[href^="tel:"], .staff-contact li a[href*="wa.me"] {
  font-family: var(--label); font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* An address may wrap, but only where an address has joints. */
.staff-contact a[href^="mailto:"] { word-break: break-word; overflow-wrap: break-word; }

/* ---------- Copy to clipboard ----------
   One button per contact row, pushed to the right-hand edge so the three of
   them line up down the card instead of trailing each value at a different
   indent. Quiet until the row is hovered or the button is focused: it is a
   convenience, not the point of the row. */
.copy-btn {
  flex: 0 0 auto; margin-left: auto; align-self: center;
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0;
  background: none; border: 1px solid transparent; border-radius: var(--radius-sm);
  color: var(--ink-faint); cursor: pointer;
  opacity: 0; transition: opacity .15s ease, color .15s ease, border-color .15s ease;
}
.copy-i { width: 14px; height: 14px; fill: none; stroke: currentColor;
  stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.copy-i-done { display: none; }

/* Revealed on hover, and always for keyboard users — who cannot hover, and
   would otherwise tab onto a control they cannot see. */
.staff-contact li:hover .copy-btn,
.copy-btn:focus-visible { opacity: 1; }
.copy-btn:hover { color: var(--brand); border-color: var(--line); }
.copy-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

.copy-btn.is-copied { opacity: 1; color: var(--brand); border-color: transparent; }
.copy-btn.is-copied .copy-i-copy { display: none; }
.copy-btn.is-copied .copy-i-done { display: block; }
.copy-btn.is-failed { opacity: 1; color: var(--danger); }

/* Touch screens have no hover, so the button has to be there from the start --
   and a 26px target is below what a thumb can reliably hit, which is the case
   that matters most here: copying a number on a phone. The icon stays the same
   size; the box around it grows. */
@media (hover: none) {
  .copy-btn { opacity: 1; width: 44px; height: 44px; margin-right: -9px; }
}

/* The fallback path puts the text in a field and selects it; it must be out
   of sight without being display:none, which would make it unselectable. */
.copy-offscreen {
  position: fixed; top: 0; left: -9999px;
  width: 1px; height: 1px; padding: 0; border: 0; opacity: 0;
}

/* Below these widths a third column would squeeze the body back under the
   width an address needs, so drop a column rather than break the line. */
@media (max-width: 1099px) {
  .staff-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 759px) {
  .staff-grid { grid-template-columns: minmax(0, 1fr); }
}

/* Narrow screens: the label sits above its value on its own line, so a 250px
   column can no longer split "+86 138 0000 1111" across two. */
@media (max-width: 520px) {
  .staff-card { display: block; }
  .staff-photo, .staff-photo-empty { margin-bottom: var(--s4); }
  .staff-contact li { display: block; padding: 7px 0; }
  .staff-contact .label { display: block; margin-bottom: 3px; min-width: 0; }
}

/* A line under a section heading that says what the section is for — used by
   the write-in products block, where the invitation matters more than the
   label above it. */
.section-note {
  margin: calc(var(--s2) * -1) 0 var(--s4);
  max-width: 62ch;
  font-size: var(--t-sm); color: var(--ink-soft);
}
/* A write-in row is a field, not a fact, so its name cell takes an input the
   same width as the catalogue names beside it. */
.products [data-custom-row] .col-name input { width: 100%; }
.products [data-custom-row] input {
  width: 100%; min-width: 0; padding: 7px 9px;
  font: inherit; font-size: var(--t-sm);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--ink);
}
.products [data-custom-row] input:focus {
  outline: none; border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-light);
}
#other-products .btn-ghost { margin-top: var(--s4); }

/* A dialling code and a number are one answer, so they sit on one line and
   share one border treatment. The code box is sized to the longest entry in
   the list rather than left to the browser, which would otherwise make it as
   wide as the country names inside it. */
/* The site is drawn in hairlines; a native dropdown arrow is the one piece of
   browser chrome that would show through. Same treatment the admin console
   gives its selects — a chevron drawn in ink. */
.field select {
  appearance: none; -webkit-appearance: none;
  padding-right: 30px;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--ink-soft) 50%),
    linear-gradient(135deg, var(--ink-soft) 50%, transparent 50%);
  background-position: calc(100% - 15px) 52%, calc(100% - 10px) 52%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.phone-field { display: flex; gap: var(--s2); align-items: stretch; }
.phone-field select {
  flex: 0 0 auto; width: 9.5rem; min-width: 0;
  /* The option text is long; the closed control only has to show the code. */
  text-overflow: ellipsis;
}
.phone-field input { flex: 1 1 auto; min-width: 0; }
@media (max-width: 420px) {
  /* Two controls will not both be usable across a phone, so they stack. */
  .phone-field { flex-wrap: wrap; }
  .phone-field select { width: 100%; }
}
/* ---------- Footer ---------- */
/* The back cover. A judge scrolls to the bottom, so it is built the same way
   as the title block at the top: ruled cells, mono labels, nothing thrown in.
   Its own colour, not an inversion of the page — the greens are lifted so the
   hairlines stay visible on a real screen. */
.site-footer {
  padding: var(--s7) 0 var(--s6);
  background: var(--ink);
  color: rgba(255, 255, 255, .78);
  font-size: var(--t-xs);
}
.site-footer .wrap {
  display: grid; gap: var(--s5);
  grid-template-columns: 1fr;
}
.site-footer .colophon {
  font-family: var(--display); font-weight: 600;
  font-size: var(--t-lg); line-height: 1.1; letter-spacing: -.02em; color: #fff;
  margin: 0;
}
.site-footer .foot-grid {
  display: grid; gap: 0;
  grid-template-columns: 1fr;
  border-top: 1px solid rgba(255, 255, 255, .22);
}
.site-footer .foot-grid > div {
  padding: var(--s3) 0;
  border-bottom: 1px solid rgba(255, 255, 255, .1);
}
.site-footer dt {
  margin: 0; font-family: var(--label); font-size: var(--t-xs);
  letter-spacing: .14em; text-transform: uppercase;
  color: rgba(255, 255, 255, .55);
}
.site-footer dd {
  margin: 5px 0 0; font-size: var(--t-sm); line-height: 1.6;
  color: rgba(255, 255, 255, .86);
}
.site-footer .notice {
  font-family: var(--label); font-size: var(--t-xs);
  letter-spacing: .12em; text-transform: uppercase;
  color: rgba(255, 255, 255, .6);
}
@media (min-width: 720px) {
  .site-footer .wrap { grid-template-columns: minmax(0, .5fr) minmax(0, 2fr); gap: var(--s7); }
  .site-footer .foot-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: var(--s6); }
}

/* ---------- Message pages (404, errors, empty states) ---------- */
.message-page {
  flex: 1 0 auto; display: grid; place-items: center;
  padding: var(--s9) var(--s5); text-align: center;
}
.message-page h1 { margin: 0 0 var(--s3); font-size: var(--t-xl); letter-spacing: -.02em; }
.message-page p { margin: 0 0 var(--s5); color: var(--ink-soft); max-width: 48ch; }

/* ---------- Inquiry (RFQ) ---------- */
.rfq-section { margin-bottom: var(--s7); }
.rfq-section h2 {
  display: flex; align-items: baseline; gap: var(--s3);
  margin: 0 0 var(--s4); padding-bottom: var(--s3);
  border-bottom: 1px solid var(--line-strong);
  font-size: var(--t-lg); font-weight: 600; letter-spacing: -.015em;
}
.rfq-section h2 .count {
  margin-left: auto;
  font-family: var(--label); font-size: var(--t-xs);
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-faint);
}
.rfq-section table.products td { vertical-align: middle; }
.rfq-section table.products input {
  width: 100%; min-width: 120px; padding: 8px 10px;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  font: inherit; font-size: var(--t-sm); background: var(--surface);
}
.rfq-section table.products input:focus {
  outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-light);
}
.rfq-grid { display: grid; gap: 0 var(--s5); grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.rfq-section textarea {
  width: 100%; padding: 10px 12px; font: inherit; font-size: var(--t-base);
  border: 1px solid var(--line-strong); border-radius: var(--radius); background: var(--surface);
}
.rfq-section .form-actions, .rfq-actions {
  display: flex; flex-wrap: wrap; gap: var(--s3); align-items: center;
  padding-top: var(--s4); border-top: 1px solid var(--line);
}
.link-danger {
  border: 0; background: none; padding: 0;
  color: var(--danger); font: inherit; font-size: var(--t-sm);
  text-decoration: underline; text-underline-offset: 3px; cursor: pointer;
}
.hidden-form { display: none; }
.clear-form { padding-bottom: var(--s5); }

.empty-panel, .sent-panel {
  margin: var(--s7) 0; padding: var(--s8) var(--s6);
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius);
}
.empty-panel p, .sent-panel p { color: var(--ink-soft); margin: 0 0 var(--s5); }
/* A blank sheet still carries its label and its ruled edge. */
.empty-panel .eyebrow {
  margin: 0 0 var(--s4); padding-bottom: var(--s4);
  border-bottom: 1px solid var(--line);
}
.empty-panel p:not(.eyebrow) { max-width: 46ch; margin-inline: auto; line-height: 1.7; }
.sent-panel h1 { margin: 0 0 var(--s3); font-size: var(--t-xl); text-wrap: balance; letter-spacing: -.02em; }
.sent-panel .sent-mark {
  display: grid; place-items: center;
  width: 48px; height: 48px; margin: 0 auto var(--s4);
  border: 1px solid var(--brand); border-radius: 50%;
  color: var(--brand); font-size: var(--t-lg);
}
.sent-panel .form-actions { display: flex; gap: var(--s3); justify-content: center; flex-wrap: wrap; }
.sent-rep { margin: 0 0 var(--s4); font-size: var(--t-sm); }

/* ---------- The customer's own sales rep, on the inquiry page ---------- */
.rep-panel {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s4) var(--s5);
  margin-bottom: var(--s6); padding: var(--s5);
  background: var(--surface);
  border: 1px solid var(--line); border-left: 3px solid var(--brand);
  border-radius: var(--radius);
}
.rep-photo, .rep-photo-empty {
  width: 56px; height: 56px; aspect-ratio: 1; flex-shrink: 0;
  border-radius: var(--radius-sm);
}
.rep-photo { object-fit: cover; border: 1px solid var(--line); }
.rep-photo-empty {
  display: grid; place-items: center; background: var(--ink); color: #fff;
  font-size: var(--t-md); font-weight: 600;
}
.rep-body { flex: 1 1 220px; min-width: 0; }
.rep-label {
  font-family: var(--label); font-size: var(--t-xs);
  letter-spacing: .12em; text-transform: uppercase; color: var(--ink-faint);
}
.rep-body h2 { margin: 2px 0 0; font-size: var(--t-md); font-weight: 600; }
.rep-title { margin: 2px 0 0; font-size: var(--t-xs); color: var(--accent);
  font-family: var(--label); letter-spacing: .08em; text-transform: uppercase; }
.rep-number { font-family: var(--label); font-size: var(--t-sm); color: var(--ink-soft); }
.rep-actions { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; }
.rep-email { font-size: var(--t-xs); color: var(--ink-soft); overflow-wrap: anywhere; }
.rep-note { margin: var(--s3) 0 0; font-size: var(--t-xs); color: var(--ink-faint); }

/* WhatsApp keeps its own colour: it is a recognised button, not our brand. */
.btn-wa { background: #25d366; border-color: #25d366; color: #06230f; }
.btn-wa:hover { background: #1db954; border-color: #1db954; color: #06230f; }

/* ---------- Motion preferences ----------
   Everything above is decoration on top of a static page, so switching it off
   costs nothing. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}
