@import url("https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,700&family=Sora:wght@400;500;600;700&display=swap");

:root {
  --ink: #0e2433;
  --ink-soft: #243b53;
  --fog: #e8eef3;
  --paper: #f7fafc;
  --cream: #fdf6e9;
  /* Brand palette — Doujin red & gold */
  --red: #c8102e;
  --red-deep: #a00d24;
  --gold: #f2b705;
  --gold-deep: #cf8f04;
  /* Legacy aliases mapped to brand palette */
  --brass: var(--gold);
  --brass-deep: var(--gold-deep);
  --sea: var(--red);
  --sea-deep: var(--red-deep);
  --line: rgba(14, 36, 51, 0.12);
  --shadow: 0 24px 60px rgba(14, 36, 51, 0.12);
  --radius: 4px;
  --font-display: "Fraunces", Georgia, serif;
  --font-body: "Sora", system-ui, sans-serif;
  --max: 1120px;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  color: var(--ink);
  font-family: var(--font-body);
  background: var(--paper);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

img {
  max-width: 100%;
  display: block;
}

a {
  color: inherit;
  text-decoration: none;
}

button,
input,
textarea,
select {
  font: inherit;
}

.skip-link {
  position: absolute;
  top: 0;
  inset-inline-start: 0;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  background: var(--ink);
  color: #fff;
  padding: 0.75rem 1rem;
  z-index: 100;
}

.skip-link:focus {
  width: auto;
  height: auto;
  overflow: visible;
  clip: auto;
  clip-path: none;
  inset-inline-start: 1rem;
  top: 1rem;
}

/* Atmosphere */
.bg-atmosphere {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(ellipse 80% 50% at 10% -10%, rgba(26, 107, 107, 0.18), transparent 55%),
    radial-gradient(ellipse 60% 40% at 90% 10%, rgba(184, 149, 108, 0.22), transparent 50%),
    linear-gradient(180deg, #eef4f7 0%, var(--paper) 40%, #f3f1ec 100%);
}

.bg-atmosphere::after {
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0.35;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cpath d='M0 60h120M60 0v120' stroke='%230e2433' stroke-opacity='0.04' stroke-width='1'/%3E%3C/svg%3E");
}

/* Header */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  backdrop-filter: blur(14px);
  background: rgba(247, 250, 252, 0.85);
  border-bottom: 1px solid var(--line);
}

.header-inner {
  max-width: var(--max);
  margin: 0 auto;
  padding: 0.4rem 1.25rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.logo {
  display: inline-flex;
  align-items: center;
  min-width: 0;
}

/* SUPPLY's cap is 7.1% of the lockup canvas and its divider rule 1.3%, so below
   roughly 95px of logo height they render at a few pixels and hairline. Kept
   at half the original header size, trading their legibility for a shorter
   header; the mark and DOUJIN still carry the brand. */
.logo-img {
  height: 48px;
  width: auto;
  display: block;
}

@media (min-width: 720px) {
  .logo-img {
    height: 56px;
  }
}

.nav {
  display: none;
  align-items: center;
  gap: 1.25rem;
  font-size: 0.9rem;
  font-weight: 500;
}

.nav a {
  opacity: 0.8;
  transition: opacity 0.2s;
}

.nav a:hover {
  opacity: 1;
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 0.65rem;
}

.lang-select {
  appearance: none;
  border: 1px solid var(--line);
  background: #fff;
  border-radius: 999px;
  padding: 0.45rem 2rem 0.45rem 0.85rem;
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--ink);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%230e2433' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.75rem center;
  cursor: pointer;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  border: none;
  border-radius: 999px;
  padding: 0.75rem 1.25rem;
  font-weight: 600;
  font-size: 0.9rem;
  cursor: pointer;
  transition: transform 0.2s, background 0.2s, color 0.2s, box-shadow 0.2s;
}

.btn:hover {
  transform: translateY(-1px);
}

.btn-primary {
  background: var(--red);
  color: #fff;
  box-shadow: 0 10px 24px rgba(200, 16, 46, 0.25);
}

.btn-primary:hover {
  background: var(--red-deep);
}

.btn-ghost {
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--line);
}

.btn-ghost:hover {
  border-color: var(--ink);
}

.btn-brass {
  background: var(--brass);
  color: var(--ink);
}

.btn-brass:hover {
  background: var(--brass-deep);
  color: #fff;
}

.btn-sm {
  padding: 0.5rem 1rem;
  font-size: 0.8rem;
}

/* Hero */
.hero {
  position: relative;
  min-height: min(92vh, 860px);
  display: grid;
  align-items: end;
  overflow: hidden;
}

/* The home page hero, SELF-HOSTED. It was hotlinked from images.unsplash.com,
   which put the front door's largest visual — almost certainly the LCP element —
   behind a third-party DNS lookup and TLS handshake, and sent every visitor's IP
   and referrer to a company they have no relationship with.

   Provenance: unsplash.com/photos/photo-1578575437130-527eed3abbec, downloaded at
   w=2000&q=80. The Unsplash licence permits commercial use without attribution.

   TWO FORMATS, because that is what the old URL's `auto=format` was doing and it
   is worth keeping: Unsplash served AVIF at 276KB to browsers that accept it and
   JPEG at 475KB to the rest, and dropping to one format would either cost every
   modern visitor ~200KB on the LCP image or hand older browsers a file they
   cannot decode. `background` names the JPEG so that a browser which does not
   understand image-set() still gets a photograph; the @supports block then
   upgrades it. The scrim is a custom property only so that it is not written
   twice — the second copy is the one that would drift. */
.hero-visual {
  --hero-scrim: linear-gradient(
    105deg,
    rgba(14, 36, 51, 0.88) 0%,
    rgba(14, 36, 51, 0.55) 42%,
    rgba(14, 36, 51, 0.25) 100%
  );
  position: absolute;
  inset: 0;
  background: var(--hero-scrim), url("../assets/hero-trade-2000.jpg") center/cover no-repeat;
  transform: scale(1.02);
  animation: hero-drift 18s ease-in-out infinite alternate;
}

@supports (background-image: image-set(url("../assets/hero-trade-2000.avif") type("image/avif"))) {
  .hero-visual {
    background-image: var(--hero-scrim),
      image-set(
        url("../assets/hero-trade-2000.avif") type("image/avif"),
        url("../assets/hero-trade-2000.jpg") type("image/jpeg")
      );
  }
}

@keyframes hero-drift {
  from {
    transform: scale(1.02) translate3d(0, 0, 0);
  }
  to {
    transform: scale(1.06) translate3d(-1.5%, -1%, 0);
  }
}

.hero-content {
  position: relative;
  z-index: 1;
  max-width: var(--max);
  width: 100%;
  margin: 0 auto;
  padding: 6.5rem 1.25rem 3.5rem;
  color: #f7fafc;
}

.hero-brand {
  font-family: var(--font-display);
  font-size: clamp(2.8rem, 7vw, 5.2rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 0.95;
  margin: 0 0 0.35rem;
  animation: rise 0.9s ease both;
}

.hero-legal {
  display: inline-block;
  font-size: 0.78rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--brass);
  margin-bottom: 1.75rem;
  animation: rise 0.9s ease 0.1s both;
}

.hero-headline {
  max-width: 18ch;
  font-size: clamp(1.35rem, 2.8vw, 1.85rem);
  font-weight: 500;
  line-height: 1.35;
  margin: 0 0 1rem;
  animation: rise 0.9s ease 0.18s both;
}

.hero-sub {
  max-width: 42ch;
  font-size: 1rem;
  color: rgba(247, 250, 252, 0.86);
  margin: 0 0 1.75rem;
  animation: rise 0.9s ease 0.26s both;
}

.hero-cta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  animation: rise 0.9s ease 0.34s both;
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(18px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Trust strip */
.trust {
  max-width: var(--max);
  margin: -2.25rem auto 0;
  padding: 0 1.25rem;
  position: relative;
  z-index: 2;
}

.trust-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.75rem;
  background: #fff;
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
  padding: 1.25rem;
}

.trust-item {
  padding: 0.5rem 0.25rem;
}

.trust-val {
  font-family: var(--font-display);
  font-size: 1.65rem;
  font-weight: 700;
  color: var(--sea-deep);
  line-height: 1.1;
}

.trust-label {
  font-size: 0.82rem;
  color: var(--ink-soft);
  margin-top: 0.25rem;
}

/* Sections */
.section {
  max-width: var(--max);
  margin: 0 auto;
  padding: 5rem 1.25rem;
}

.eyebrow {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--sea);
  margin: 0 0 0.75rem;
}

.section h2 {
  font-family: var(--font-display);
  font-size: clamp(1.85rem, 3.5vw, 2.6rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.15;
  margin: 0 0 0.85rem;
  max-width: 20ch;
  text-wrap: balance;
}

/* Avoid one- or two-character orphan lines on multi-line headings */
.hero-headline,
.section h3,
.compliance-panel h3,
.warehouse-head h3,
.about-teams-title,
.track-region {
  text-wrap: balance;
}

.section-lead {
  max-width: 52ch;
  color: var(--ink-soft);
  margin: 0 0 2.5rem;
}

.service-grid {
  display: grid;
  gap: 1rem;
}

/* Category block merged inside the Services section */
.services-categories {
  margin-top: 2.5rem;
}

.subsection-title {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 2.6vw, 2rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.15;
  margin: 0 0 0.85rem;
  max-width: 24ch;
  text-wrap: balance;
}

.service-card {
  padding: 1.5rem 1.35rem;
  border-top: 2px solid var(--brass);
  background: linear-gradient(180deg, #fff 0%, rgba(255, 255, 255, 0.4) 100%);
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  border-left: 1px solid var(--line);
  transition: transform 0.25s, box-shadow 0.25s;
}

.service-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow);
}

.service-card h3 {
  margin: 0 0 0.55rem;
  font-size: 1.1rem;
}

.service-card p {
  margin: 0;
  color: var(--ink-soft);
  font-size: 0.95rem;
}

/* Categories — uniform tiles that expand inline into a photo waterfall */
.cat-grid {
  display: grid;
  gap: 1rem;
  margin-top: 1.75rem;
  grid-template-columns: repeat(2, 1fr);
  align-items: start;
}

.cat-tile {
  min-height: 0;
}

.cat-face-front {
  -webkit-appearance: none;
  appearance: none;
  font: inherit;
  text-align: start;
  cursor: pointer;
  padding: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: 12px;
  background: #fff;
  overflow: hidden;
  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}

.cat-face-front:hover {
  box-shadow: var(--shadow);
  transform: translateY(-2px);
}

.cat-tile.is-open .cat-face-front {
  border-color: var(--red);
  box-shadow: var(--shadow);
}

.cat-cover {
  position: relative;
  display: block;
  height: 180px;
  background: linear-gradient(135deg, #f1f3f6, #e6e9ee);
  overflow: hidden;
}

.cat-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.4s ease;
}

.cat-face-front:hover .cat-cover img {
  transform: scale(1.04);
}

.cat-badge {
  position: absolute;
  top: 0.55rem;
  inset-inline-end: 0.55rem;
  width: 1.7rem;
  height: 1.7rem;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: rgba(20, 22, 28, 0.66);
  color: var(--gold);
  font-size: 0.95rem;
  line-height: 1;
  backdrop-filter: blur(2px);
}

.cat-body {
  display: flex;
  flex-direction: column;
  gap: 0.28rem;
  padding: 0.85rem 0.9rem 0.9rem;
  border-top: 3px solid var(--red);
  flex: 1;
  min-height: 0;
}

.cat-name {
  display: block;
  font-size: 0.98rem;
  font-weight: 700;
  line-height: 1.2;
}

.cat-desc {
  display: block;
  font-size: 0.78rem;
  line-height: 1.35;
  color: var(--ink-soft);
}

.cat-hint {
  display: block;
  margin-top: auto;
  padding-top: 0.35rem;
  font-size: 0.76rem;
  font-weight: 700;
  color: var(--red);
}

.cat-tile.is-open .cat-hint {
  color: var(--ink-soft);
}

/* Inline expand panel (full-width row, accordion style) */
.cat-panel {
  grid-column: 1 / -1;
  border: 1px solid var(--line);
  border-top: 4px solid var(--red);
  border-radius: 12px;
  background: #fff;
  padding: 1.1rem 1.15rem 1.25rem;
  box-shadow: var(--shadow);
  animation: catPanelIn 0.28s ease;
}

@keyframes catPanelIn {
  from {
    opacity: 0;
    transform: translateY(-6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.cat-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.35rem;
}

.cat-panel-head h3 {
  margin: 0;
  font-size: 1.15rem;
  color: var(--ink);
}

.cat-panel-close {
  -webkit-appearance: none;
  appearance: none;
  font: inherit;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.28rem 0.85rem;
  cursor: pointer;
  flex: none;
}

.cat-panel-close:hover {
  background: #f1f3f6;
}

.cat-group-label {
  margin: 1rem 0 0.6rem;
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--ink);
  padding-inline-start: 0.7rem;
  border-inline-start: 3px solid var(--gold);
}

.cat-waterfall {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  gap: 0.5rem;
}

.cat-photo {
  -webkit-appearance: none;
  appearance: none;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 0;
  margin: 0;
  cursor: zoom-in;
  background: #f1f3f6;
  aspect-ratio: 1 / 1;
  overflow: hidden;
}

.cat-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.3s ease;
}

.cat-photo:hover img {
  transform: scale(1.06);
}

/* Tagged product tiles (e.g. hotel-supply "酒店专用") */
.cat-photo--tagged {
  position: relative;
}

.cat-badge {
  position: absolute;
  top: 0.4rem;
  inset-inline-start: 0.4rem;
  z-index: 2;
  padding: 0.22rem 0.5rem;
  border-radius: 999px;
  background: var(--gold);
  color: var(--ink);
  font-size: 0.62rem;
  font-weight: 800;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  box-shadow: 0 1px 4px rgba(12, 14, 18, 0.28);
  pointer-events: none;
}

/* "工厂一瞥" factory-tour entry tile */
.cat-tour {
  position: relative;
}

.cat-tour img {
  filter: brightness(0.78) saturate(1.05);
}

.cat-tour:hover img {
  filter: brightness(0.9) saturate(1.05);
}

.cat-tour__play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 999px;
  background: rgba(12, 14, 18, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.7);
  color: #fff;
  font-size: 0.9rem;
  padding-inline-start: 0.16rem;
  pointer-events: none;
  transition: background 0.15s ease, transform 0.15s ease;
}

.cat-tour:hover .cat-tour__play {
  background: rgba(12, 14, 18, 0.72);
  transform: translate(-50%, -50%) scale(1.06);
}

.cat-tour-cap {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  padding: 0.85rem 0.4rem 0.4rem;
  background: linear-gradient(to top, rgba(12, 14, 18, 0.82), rgba(12, 14, 18, 0));
  color: #fff;
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1.25;
  text-align: center;
  pointer-events: none;
}

.cat-tour-cap::before {
  content: "\25b6";
  display: block;
  margin: 0 auto 0.2rem;
  width: 1.55rem;
  height: 1.55rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.16);
  border: 1px solid rgba(255, 255, 255, 0.55);
  font-size: 0.7rem;
  line-height: 1.55rem;
  padding-inline-start: 0.12rem;
}

@media (min-width: 720px) {
  .cat-waterfall {
    grid-template-columns: repeat(auto-fill, minmax(128px, 1fr));
  }
}

/* Prominent pricing / customization callout at top of section */
.cat-pricing-banner {
  margin-top: 1.5rem;
  display: flex;
  align-items: flex-start;
  gap: 0.85rem;
  padding: 1.1rem 1.35rem;
  border: 1px solid rgba(184, 149, 108, 0.4);
  border-inline-start: 5px solid var(--gold);
  border-radius: 12px;
  background: linear-gradient(135deg, rgba(184, 149, 108, 0.14), rgba(184, 149, 108, 0.05));
  box-shadow: 0 8px 24px rgba(20, 22, 28, 0.06);
}

.cat-pricing-icon {
  flex: none;
  margin-top: 0.1rem;
  color: var(--gold-deep, #b8860b);
  font-size: 1rem;
  line-height: 1.5;
}

.cat-pricing-banner p {
  margin: 0;
  max-width: 76ch;
  font-size: 0.98rem;
  line-height: 1.6;
  color: var(--ink);
  font-weight: 500;
}

.cat-legend {
  margin: 1.25rem 0 0;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.82rem;
  color: var(--ink-soft);
}

.cat-legend .cat-star {
  color: var(--gold-deep);
}

.cat-grid {
  margin-top: 0.85rem;
}

/* Lightbox */
body.lb-open {
  overflow: hidden;
}

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 0.5rem;
  padding: clamp(0.75rem, 3vw, 2rem);
}

.lightbox[hidden] {
  display: none;
}

.lb-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(12, 14, 18, 0.9);
  cursor: zoom-out;
}

.lb-stage {
  position: relative;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  max-height: 100%;
}

.lb-img {
  position: relative;
  max-width: min(92vw, 900px);
  max-height: 86vh;
  object-fit: contain;
  border-radius: 8px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
  background: #fff;
}

.lb-video {
  position: relative;
  max-width: min(92vw, 900px);
  max-height: 86vh;
  object-fit: contain;
  border-radius: 8px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
  background: #000;
}

.lb-img[hidden],
.lb-video[hidden] {
  display: none;
}

.lb-btn {
  position: relative;
  z-index: 1;
  -webkit-appearance: none;
  appearance: none;
  border: 0;
  cursor: pointer;
  color: #fff;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.25);
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 999px;
  font-size: 1.4rem;
  line-height: 1;
  display: grid;
  place-items: center;
  transition: background 0.2s ease;
}

.lb-btn:hover {
  background: rgba(255, 255, 255, 0.24);
}

.lb-close {
  position: absolute;
  top: clamp(0.75rem, 3vw, 1.5rem);
  inset-inline-end: clamp(0.75rem, 3vw, 1.5rem);
  width: 2.4rem;
  height: 2.4rem;
  font-size: 1.1rem;
}

.lb-count {
  position: absolute;
  bottom: clamp(0.75rem, 3vw, 1.5rem);
  left: 50%;
  transform: translateX(-50%);
  z-index: 1;
  color: #fff;
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  background: rgba(255, 255, 255, 0.12);
  padding: 0.25rem 0.7rem;
  border-radius: 999px;
}

/* General-merchandise open door */
.cat-open {
  margin-top: 1.75rem;
  padding: 1.5rem 1.6rem;
  background: var(--ink);
  color: #f7fafc;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem 1.5rem;
  border-inline-start: 4px solid var(--gold);
}

.cat-open-text {
  max-width: 60ch;
}

.cat-open-text h3 {
  margin: 0 0 0.3rem;
  font-family: var(--font-display);
  font-size: 1.35rem;
}

.cat-open-text p {
  margin: 0;
  color: rgba(247, 250, 252, 0.85);
}

.cat-open .btn {
  flex: none;
}

/* Process */
.process-list {
  display: grid;
  gap: 1.25rem;
  counter-reset: none;
}

.process-step {
  display: grid;
  gap: 0.75rem;
  padding: 1.5rem 0;
  border-bottom: 1px solid var(--line);
}

.process-step:first-child {
  border-top: 1px solid var(--line);
}

.process-n {
  font-family: var(--font-display);
  font-size: 2rem;
  color: var(--brass);
  line-height: 1;
}

.process-step h3 {
  margin: 0 0 0.35rem;
  font-size: 1.2rem;
}

.process-step p {
  margin: 0;
  color: var(--ink-soft);
  max-width: 56ch;
}

/* Compliance highlight */
.compliance-tracks {
  display: grid;
  gap: 1.25rem;
  margin-top: 1.75rem;
}

.track-card {
  background: #fff;
  border: 1px solid var(--line);
  border-top: 3px solid var(--gold);
  padding: 1.6rem;
  display: grid;
  gap: 0.6rem;
  align-content: start;
}

.track-tag {
  justify-self: start;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.28rem 0.7rem;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--red);
  background: rgba(200, 16, 46, 0.08);
  border-radius: 999px;
}

.track-region {
  margin: 0.2rem 0 0;
  font-family: var(--font-display);
  font-size: 1.25rem;
  line-height: 1.25;
}

.track-body {
  margin: 0;
  color: var(--ink-soft);
}

.track-points {
  list-style: none;
  margin: 0.4rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.6rem;
}

.track-points li {
  position: relative;
  padding-inline-start: 1.5rem;
  color: var(--ink-soft);
}

.track-points li::before {
  content: "✓";
  position: absolute;
  inset-inline-start: 0;
  top: 0;
  color: var(--gold);
  font-weight: 700;
}

@media (min-width: 860px) {
  .compliance-tracks {
    grid-template-columns: 1fr 1fr;
  }
}

.compliance-panel {
  position: relative;
  overflow: hidden;
  background: var(--ink);
  color: #f7fafc;
  padding: 2rem 1.5rem;
  border-inline-start: 4px solid var(--red);
}

.compliance-panel::after {
  content: "";
  position: absolute;
  width: 260px;
  height: 260px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(200, 16, 46, 0.35), transparent 70%);
  top: -90px;
  inset-inline-end: -70px;
  pointer-events: none;
}

.compliance-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.32rem 0.8rem;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #fff;
  background: var(--red);
  border-radius: 999px;
  position: relative;
  z-index: 1;
}

.compliance-badge::before {
  content: "★";
  color: var(--gold);
  font-size: 0.7rem;
}

.compliance-panel h2,
.compliance-panel h3 {
  margin: 1rem 0 0.5rem;
  font-family: var(--font-display);
  font-size: 1.5rem;
  position: relative;
  z-index: 1;
}

.info-grid + .compliance-panel,
.compliance-tracks + .compliance-panel {
  margin-top: 1.75rem;
}

.compliance-lead {
  margin: 0;
  max-width: 60ch;
  color: rgba(247, 250, 252, 0.85);
  position: relative;
  z-index: 1;
}

.compliance-points {
  list-style: none;
  margin: 1.5rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.85rem;
  position: relative;
  z-index: 1;
}

.compliance-points li {
  position: relative;
  padding-inline-start: 1.6rem;
  color: rgba(247, 250, 252, 0.92);
  max-width: 68ch;
}

.compliance-points li::before {
  content: "✓";
  position: absolute;
  inset-inline-start: 0;
  top: 0;
  color: var(--gold);
  font-weight: 700;
}

@media (min-width: 720px) {
  .compliance-panel {
    padding: 2.75rem;
  }
  .compliance-points {
    grid-template-columns: 1fr 1fr;
    gap: 1rem 2rem;
  }
}

/* Warehousing / value-added detail panel */
.warehouse-panel {
  margin-top: 1.75rem;
  padding: 1.75rem 1.5rem;
  background: var(--surface, #f7f8fa);
  border: 1px solid rgba(20, 22, 28, 0.08);
  border-inline-start: 4px solid var(--gold);
  border-radius: 14px;
}

.warehouse-head h3 {
  margin: 0.15rem 0 0;
  font-family: var(--font-display);
  font-size: 1.4rem;
  color: var(--ink);
}

.warehouse-lead {
  margin: 0.5rem 0 0;
  max-width: 68ch;
  color: var(--ink-soft);
}

.warehouse-grid {
  display: grid;
  gap: 1rem;
  margin-top: 1.5rem;
  grid-template-columns: 1fr;
}

.warehouse-item {
  position: relative;
  padding: 1.05rem 1.1rem 1.1rem;
  background: #fff;
  border: 1px solid rgba(20, 22, 28, 0.08);
  border-radius: 12px;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.warehouse-item:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow, 0 12px 30px rgba(20, 22, 28, 0.1));
}

.warehouse-item h4 {
  margin: 0 0 0.35rem;
  padding-inline-start: 1.4rem;
  position: relative;
  font-size: 1.02rem;
  color: var(--ink);
}

.warehouse-item h4::before {
  content: "★";
  position: absolute;
  inset-inline-start: 0;
  top: 0.05rem;
  color: var(--gold-deep, #b8860b);
  font-size: 0.85rem;
}

.warehouse-item p {
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

@media (min-width: 720px) {
  .warehouse-panel {
    padding: 2.25rem;
  }
  .warehouse-grid {
    grid-template-columns: 1fr 1fr;
    gap: 1.1rem 1.5rem;
  }
}

/* Growth / client-success closing band */
.growth-band {
  position: relative;
  overflow: hidden;
  padding: 2.25rem 1.5rem;
  border-radius: 16px;
  border: 1px solid rgba(20, 22, 28, 0.08);
  border-top: 4px solid var(--gold);
  background: linear-gradient(140deg, #ffffff 0%, #faf6ec 100%);
  box-shadow: var(--shadow, 0 18px 44px rgba(20, 22, 28, 0.08));
}

.growth-band::after {
  content: "";
  position: absolute;
  width: 300px;
  height: 300px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(200, 16, 46, 0.1), transparent 70%);
  top: -110px;
  inset-inline-end: -90px;
  pointer-events: none;
}

.growth-band .eyebrow {
  position: relative;
  z-index: 1;
}

.growth-band h2 {
  position: relative;
  z-index: 1;
  margin: 0.5rem 0 0;
  max-width: 24ch;
}

.growth-band .section-lead {
  position: relative;
  z-index: 1;
  max-width: 70ch;
}

.growth-grid {
  position: relative;
  z-index: 1;
  display: grid;
  gap: 1rem;
  margin-top: 1.75rem;
  grid-template-columns: 1fr;
}

.growth-item {
  padding: 1.15rem 1.2rem 1.25rem;
  background: #fff;
  border: 1px solid rgba(20, 22, 28, 0.08);
  border-radius: 12px;
  counter-increment: growth;
}

.growth-item h3 {
  margin: 0 0 0.4rem;
  font-size: 1.05rem;
  color: var(--ink);
}

.growth-item h3::before {
  content: counter(growth);
  display: inline-grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  margin-inline-end: 0.5rem;
  border-radius: 999px;
  background: var(--red);
  color: #fff;
  font-size: 0.8rem;
  font-weight: 700;
  vertical-align: middle;
}

.growth-item p {
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

.growth-grid {
  counter-reset: growth;
}

.growth-cta {
  position: relative;
  z-index: 1;
  margin-top: 1.75rem;
}

@media (min-width: 720px) {
  .growth-band {
    padding: 3rem;
  }
  .growth-grid {
    grid-template-columns: repeat(3, 1fr);
    gap: 1.25rem;
  }
}

/* Partner factories — horizontal scroll */
.journey-step {
  margin: 0 0 0.35rem;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--red);
}

.pf-section {
  margin: 2rem 0 2.25rem;
}

.pf-section-title {
  margin: 0 0 0.45rem;
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 2.4vw, 1.55rem);
  font-weight: 700;
  line-height: 1.25;
  color: var(--ink);
}

.pf-section-lead {
  margin: 0 0 1.25rem;
  max-width: 62ch;
  color: var(--ink-soft);
  font-size: 0.98rem;
  line-height: 1.55;
}

.why-network {
  margin: 0 0 1.5rem;
}

.why-network-title {
  margin: 0 0 0.45rem;
  font-family: var(--font-display);
  font-size: clamp(1.15rem, 2.2vw, 1.45rem);
  font-weight: 700;
  line-height: 1.25;
  color: var(--ink);
}

.why-network-lead {
  margin: 0 0 1.25rem;
  max-width: 62ch;
  color: var(--ink-soft);
  font-size: 0.98rem;
  line-height: 1.55;
}

.pf-scroll-wrap {
  position: relative;
  margin-top: 0;
}

.pf-scroll-wrap::before,
.pf-scroll-wrap::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0.5rem;
  width: 2rem;
  z-index: 2;
  pointer-events: none;
}

.pf-scroll-wrap::before {
  inset-inline-start: 0;
  background: linear-gradient(to right, var(--paper), transparent);
}

.pf-scroll-wrap::after {
  inset-inline-end: 0;
  background: linear-gradient(to left, var(--paper), transparent);
}

.pf-scroll {
  display: flex;
  gap: 1rem;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  padding: 0.25rem 0.5rem 0.75rem;
  scrollbar-width: thin;
  scrollbar-color: rgba(20, 22, 28, 0.25) transparent;
}

.pf-scroll::-webkit-scrollbar {
  height: 6px;
}

.pf-scroll::-webkit-scrollbar-thumb {
  background: rgba(20, 22, 28, 0.22);
  border-radius: 999px;
}

.pf-tile {
  position: relative;
  flex: 0 0 min(280px, 78vw);
  scroll-snap-align: start;
  border: 1px solid rgba(20, 22, 28, 0.1);
  border-radius: 14px;
  background: #fff;
  box-shadow: 0 10px 28px rgba(20, 22, 28, 0.07);
  overflow: hidden;
  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}

.pf-tile:hover {
  transform: translateY(-3px);
  box-shadow: 0 16px 36px rgba(20, 22, 28, 0.12);
  border-color: rgba(200, 16, 46, 0.22);
}

/* Main click target (view inside). A <button> so the whole card opens the
   tour; kept separate from the share <button> to avoid nested buttons. */
.pf-tile__open {
  -webkit-appearance: none;
  appearance: none;
  display: grid;
  gap: 0;
  padding: 0;
  margin: 0;
  width: 100%;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
  text-align: start;
}

.pf-tile__open:focus-visible {
  outline: 2px solid rgba(200, 16, 46, 0.45);
  outline-offset: 2px;
}

/* OEM / private-label pill (top-inline-start corner) */
.pf-tile__oem {
  position: absolute;
  top: 0.55rem;
  inset-inline-start: 0.55rem;
  z-index: 2;
  padding: 0.24rem 0.55rem;
  border-radius: 999px;
  background: var(--gold);
  color: var(--ink);
  font-size: 0.62rem;
  font-weight: 800;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  box-shadow: 0 1px 4px rgba(12, 14, 18, 0.28);
  pointer-events: none;
}

.pf-tile__media {
  position: relative;
  display: block;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: #eef1f5;
}

.pf-tile__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.35s ease;
}

.pf-tile:hover .pf-tile__media img {
  transform: scale(1.04);
}

.pf-tile--video .pf-tile__media img {
  filter: brightness(0.82) saturate(1.05);
}

.pf-tile--video:hover .pf-tile__media img {
  filter: brightness(0.92) saturate(1.05);
}

.pf-tile__play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 999px;
  background: rgba(12, 14, 18, 0.58);
  border: 1px solid rgba(255, 255, 255, 0.72);
  color: #fff;
  font-size: 0.95rem;
  padding-inline-start: 0.18rem;
  pointer-events: none;
  transition: background 0.15s ease, transform 0.15s ease;
}

.pf-tile:hover .pf-tile__play {
  background: rgba(12, 14, 18, 0.74);
  transform: translate(-50%, -50%) scale(1.06);
}

.pf-tile__body {
  display: grid;
  gap: 0.25rem;
  padding: 0.95rem 1rem 0.55rem;
}

.pf-tile__name {
  font-size: 0.98rem;
  font-weight: 700;
  color: var(--ink);
  line-height: 1.3;
}

.pf-tile__tag {
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* Bottom action row: "View inside" CTA + copy-link icon (sibling of open) */
.pf-tile__actions {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0 1rem 1.05rem;
}

.pf-tile__hint {
  -webkit-appearance: none;
  appearance: none;
  padding: 0;
  margin: 0;
  border: 0;
  background: transparent;
  font: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--red);
  cursor: pointer;
  text-align: start;
}

.pf-tile__hint:focus-visible {
  outline: 2px solid rgba(200, 16, 46, 0.45);
  outline-offset: 2px;
  border-radius: 4px;
}

/* Small chain/link icon that copies the deep link */
.pf-tile__link {
  -webkit-appearance: none;
  appearance: none;
  margin-inline-start: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.9rem;
  height: 1.9rem;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--red);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.pf-tile__link:hover {
  background: rgba(200, 16, 46, 0.1);
}

.pf-tile__link:focus-visible {
  outline: 2px solid rgba(200, 16, 46, 0.45);
  outline-offset: 2px;
}

.pf-tile__link.is-copied {
  color: var(--gold-deep);
  background: rgba(242, 183, 5, 0.14);
}

.pf-tile__link svg {
  display: block;
  width: 1.02rem;
  height: 1.02rem;
}

/* Transient "Link copied" confirmation, anchored above the icon */
.pf-tile__copied {
  position: absolute;
  inset-inline-end: 0.9rem;
  bottom: calc(100% - 0.3rem);
  z-index: 3;
  padding: 0.24rem 0.5rem;
  border-radius: 6px;
  background: var(--ink);
  color: #fff;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  white-space: nowrap;
  box-shadow: 0 2px 8px rgba(12, 14, 18, 0.22);
  pointer-events: none;
}

.pf-tile__copied[hidden] {
  display: none;
}

@media (min-width: 720px) {
  .pf-tile {
    flex-basis: min(300px, 32vw);
  }
}

/* Factory-direct band */
.factory-band {
  position: relative;
  overflow: hidden;
  padding: 2.25rem 1.5rem;
  border-radius: 16px;
  border: 1px solid rgba(20, 22, 28, 0.08);
  border-top: 4px solid var(--red);
  background: linear-gradient(140deg, #ffffff 0%, #fbeef0 100%);
  box-shadow: var(--shadow, 0 18px 44px rgba(20, 22, 28, 0.08));
}

.factory-band::after {
  content: "";
  position: absolute;
  width: 300px;
  height: 300px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(200, 16, 46, 0.1), transparent 70%);
  top: -120px;
  inset-inline-end: -90px;
  pointer-events: none;
}

.factory-band .eyebrow,
.factory-band h2,
.factory-band .section-lead {
  position: relative;
  z-index: 1;
}

.factory-band h2 {
  margin: 0.5rem 0 0;
  max-width: 26ch;
}

.factory-band .section-lead {
  max-width: 72ch;
  margin-bottom: 0;
}

.factory-grid {
  position: relative;
  z-index: 1;
  display: grid;
  gap: 1rem;
  margin-top: 1.75rem;
  grid-template-columns: 1fr;
}

.factory-item {
  padding: 1.15rem 1.2rem 1.25rem;
  background: #fff;
  border: 1px solid rgba(20, 22, 28, 0.08);
  border-radius: 12px;
}

.factory-item h3 {
  margin: 0 0 0.4rem;
  font-size: 1.02rem;
  color: var(--ink);
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.factory-item h3::before {
  content: "\2714";
  display: inline-grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  flex: none;
  border-radius: 999px;
  background: var(--red);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 700;
}

.factory-item p {
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

@media (min-width: 720px) {
  .factory-band {
    padding: 3rem;
  }
  .factory-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 1.25rem;
  }
}

@media (min-width: 980px) {
  .factory-grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* Info cards (compliance + partnership) */
.info-grid {
  display: grid;
  gap: 1.25rem;
  margin-top: 1.75rem;
}

.info-card {
  background: #fff;
  border: 1px solid var(--line);
  border-top: 3px solid var(--gold);
  padding: 1.4rem;
  display: grid;
  gap: 0.5rem;
  align-content: start;
}

.info-card h3 {
  margin: 0;
  font-size: 1.1rem;
}

.info-card p {
  margin: 0;
  color: var(--ink-soft);
}

@media (min-width: 720px) {
  .info-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 980px) {
  .info-grid.cols-3 {
    grid-template-columns: repeat(3, 1fr);
  }
  .info-grid.cols-4 {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* Contact */
.contact-grid {
  display: grid;
  gap: 1.5rem;
}

.inquiry-form {
  display: grid;
  gap: 0.85rem;
  align-content: start;
  background: linear-gradient(160deg, #fff, #f3f6f8);
  border: 1px solid var(--line);
  border-top: 3px solid var(--red);
  padding: 1.75rem;
}

.form-heading {
  margin: 0 0 0.25rem;
  font-family: var(--font-display);
  font-size: 1.3rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.inquiry-form label {
  display: grid;
  gap: 0.35rem;
  font-size: 0.85rem;
  font-weight: 500;
}

.inquiry-form input,
.inquiry-form textarea,
.inquiry-form select {
  width: 100%;
  border: 1px solid var(--line);
  background: #fff;
  border-radius: var(--radius);
  padding: 0.75rem 0.85rem;
  color: var(--ink);
}

.inquiry-form input:focus,
.inquiry-form textarea:focus,
.inquiry-form select:focus {
  outline: 2px solid rgba(200, 16, 46, 0.28);
  border-color: var(--red);
}

.form-note {
  margin: 0;
  font-size: 0.78rem;
  color: var(--ink-soft);
}

.form-status {
  margin: 0;
  font-size: 0.85rem;
  font-weight: 600;
  padding: 0.6rem 0.8rem;
  border-radius: 8px;
  border: 1px solid transparent;
}

.form-status--ok {
  color: #166534;
  background: #ecfdf3;
  border-color: #bbf7d0;
}

.form-status--err {
  color: #b42318;
  background: #fef3f2;
  border-color: #fecdca;
}

/* Quick-contact aside */
.contact-aside {
  display: grid;
  gap: 1.35rem;
  align-content: start;
  background: var(--ink);
  color: #f7fafc;
  padding: 1.75rem;
  position: relative;
  overflow: hidden;
}

.contact-aside::after {
  content: "";
  position: absolute;
  width: 220px;
  height: 220px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(242, 183, 5, 0.28), transparent 70%);
  top: -70px;
  inset-inline-end: -60px;
  pointer-events: none;
}

.contact-methods {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 1rem;
  position: relative;
  z-index: 1;
}

.contact-methods li {
  display: grid;
  gap: 0.15rem;
}

.cm-label {
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gold);
  font-weight: 700;
}

.cm-value {
  font-weight: 600;
  color: #f7fafc;
  word-break: break-word;
}

a.cm-value:hover {
  color: var(--gold);
}

.qr-block {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin-top: 0.6rem;
  text-decoration: none;
  color: inherit;
}

a.qr-block:hover .qr-img {
  transform: translateY(-2px);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.28);
}

.qr-img {
  width: 128px;
  height: auto;
  border-radius: var(--radius);
  flex: none;
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}

/* QR PNGs that ship without their own white card need a backing plate */
.qr-img.qr-on-white {
  background: #fff;
  padding: 0.35rem;
}

.wechat-hint {
  font-size: 0.85rem;
  color: rgba(247, 250, 252, 0.82);
  max-width: 8.5rem;
}

.contact-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.65rem;
  position: relative;
  z-index: 1;
}

.btn-ghost-invert {
  color: #f7fafc;
  border-color: rgba(247, 250, 252, 0.4);
}

.btn-ghost-invert:hover {
  border-color: #fff;
  background: rgba(247, 250, 252, 0.08);
}

/* About */
#about .factory-band {
  margin-top: 1.75rem;
}

.about-market {
  display: grid;
  gap: 1.5rem;
  margin-top: 1.75rem;
  align-items: center;
}

.about-market-copy h3 {
  margin: 0 0 0.6rem;
  font-family: var(--font-display);
  font-size: 1.5rem;
}

.about-market-copy p {
  margin: 0;
  color: var(--ink-soft);
  max-width: 60ch;
}

.about-stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.5rem;
}

@media (min-width: 560px) {
  .about-stats {
    grid-template-columns: repeat(3, 1fr);
  }
}

.about-stat {
  background: #fff;
  border: 1px solid var(--line);
  border-top: 2px solid var(--red);
  padding: 0.6rem 0.65rem;
  border-radius: 5px;
}

.about-stat-val {
  font-family: var(--font-display);
  font-size: 1.05rem;
  line-height: 1.1;
  color: var(--red);
}

.about-stat-label {
  margin-top: 0.2rem;
  font-size: 0.68rem;
  line-height: 1.25;
  color: var(--ink-soft);
}

.about-teams {
  margin-top: 2.25rem;
}

.about-teams-title {
  margin: 0 0 0.4rem;
  font-family: var(--font-display);
  font-size: 1.35rem;
}

.about-teams-lead {
  margin: 0;
  color: var(--ink-soft);
  max-width: 66ch;
}

.about-teams-grid {
  display: grid;
  gap: 1.25rem;
  margin-top: 1.25rem;
}

.about-team {
  background: #fff;
  border: 1px solid var(--line);
  border-inline-start: 3px solid var(--red);
  padding: 1.35rem 1.5rem;
  border-radius: 6px;
}

.about-team-place {
  display: inline-block;
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--red);
}

.about-team-role {
  margin: 0.4rem 0 0.5rem;
  font-family: var(--font-display);
  font-size: 1.2rem;
}

.about-team p {
  margin: 0;
  color: var(--ink-soft);
}

@media (min-width: 860px) {
  .about-market {
    grid-template-columns: 1.1fr 0.9fr;
    gap: 2.5rem;
  }
  .about-teams-grid {
    grid-template-columns: 1fr 1fr;
  }
}

.office-tel {
  color: var(--red);
  font-weight: 600;
  cursor: pointer;
}

.tel-x {
  display: none;
}

.office-tel:hover {
  color: var(--red-deep);
}

.legal-note {
  margin: 1.75rem 0 0;
  font-size: 0.8rem;
  color: var(--ink-soft);
  text-align: center;
}

/* Footer */
.site-footer {
  border-top: 1px solid var(--line);
  padding: 2rem 1.25rem 2.5rem;
  background: #fff;
}

.footer-inner {
  max-width: var(--max);
  margin: 0 auto;
  display: grid;
  gap: 1.75rem;
}

.footer-top {
  display: grid;
  gap: 1.75rem;
}

.footer-brand-block {
  display: grid;
  gap: 0.55rem;
}

.footer-brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-weight: 600;
  color: var(--ink);
}

.footer-tagline {
  margin: 0;
  max-width: 36ch;
  font-size: 0.88rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

.footer-section-title {
  margin: 0 0 0.65rem;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--red);
}

.footer-contact-list,
.footer-office-addr,
.footer-office-phone {
  margin: 0;
  padding: 0;
  list-style: none;
}

.footer-contact-list {
  display: grid;
  gap: 0.85rem;
}

.footer-contact-item {
  display: flex;
  align-items: center;
  gap: 0.65rem;
}

.footer-contact-icon {
  flex: none;
  width: 1.125rem;
  height: 1.125rem;
  color: var(--red);
}

.footer-contact-icon svg {
  display: block;
  width: 100%;
  height: 100%;
}

.footer-contact-value {
  color: var(--ink);
  font-weight: 600;
  text-decoration: none;
  font-size: 0.88rem;
  line-height: 1.55;
}

a.footer-contact-value:hover,
.office-tel.footer-contact-value:hover {
  color: var(--red);
  text-decoration: underline;
}

.footer-offices {
  display: grid;
  gap: 1.25rem;
}

.footer-office {
  display: grid;
  gap: 0.35rem;
}

.footer-office-addr {
  display: flex;
  align-items: flex-start;
  gap: 0.65rem;
}

.footer-office-addr .footer-contact-icon {
  margin-top: 0.15rem;
}

.footer-office-addr,
.footer-office-phone {
  font-size: 0.88rem;
  line-height: 1.55;
  color: var(--ink-soft);
}

.footer-office-phone {
  display: flex;
  align-items: center;
  gap: 0.65rem;
}

.footer-office-phone .footer-contact-icon {
  margin-top: 0;
}

.footer-office-addr-zh {
  display: block;
  margin-top: 0.2rem;
}

.footer-office-phone .office-tel {
  margin-inline-start: 0;
}

.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--line);
  font-size: 0.82rem;
  color: var(--ink-soft);
}

.footer-domain {
  color: var(--ink-soft);
  font-weight: 600;
  text-decoration: none;
}

.footer-domain:hover {
  color: var(--red);
  text-decoration: underline;
}

.footer-icon {
  height: 32px;
  width: 32px;
  object-fit: contain;
  flex: none;
}

@media (min-width: 720px) {
  .footer-top {
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr) minmax(0, 1.2fr);
    align-items: start;
    gap: 2rem;
  }

  .footer-offices {
    grid-template-columns: 1fr;
    gap: 1.1rem;
  }
}

@media (min-width: 960px) {
  .footer-offices {
    grid-template-columns: 1fr 1fr;
    gap: 1.25rem 1.5rem;
  }
}

/* Reveal on scroll */
.reveal {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 0.7s ease, transform 0.7s ease;
}

.reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* RTL */
[dir="rtl"] .lang-select {
  background-position: left 0.75rem center;
  padding: 0.45rem 0.85rem 0.45rem 2rem;
}

[dir="rtl"] .hero-headline,
[dir="rtl"] .section h2 {
  max-width: none;
}

/* Responsive */
@media (min-width: 720px) {
  .nav {
    display: flex;
  }

  .trust-grid {
    grid-template-columns: repeat(3, 1fr);
    padding: 1.5rem 1.75rem;
  }

  .service-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  .cat-grid {
    grid-template-columns: repeat(3, 1fr);
  }

  .process-step {
    grid-template-columns: 5rem 1fr;
    align-items: start;
  }
}

@media (min-width: 980px) {
  .cat-grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

@media (min-width: 880px) {
  .contact-grid {
    grid-template-columns: 1.15fr 0.85fr;
    align-items: stretch;
  }
}

@media (min-width: 980px) {
  .service-grid {
    grid-template-columns: repeat(4, 1fr);
  }

  .header-inner .btn-sm.header-cta {
    display: inline-flex;
  }
}

@media (max-width: 719px) {
  .header-cta {
    display: none;
  }
}

.header-signin {
  white-space: nowrap;
}

/* ===== Supply Chain Infrastructure positioning (ASD 2026) ===== */

/* Hero — infrastructure lead */
.hero-kicker {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 1.1rem;
  font-size: 0.78rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--gold);
  animation: rise 0.9s ease both;
}

.hero-kicker-sep {
  opacity: 0.55;
}

.hero-h1 {
  font-family: var(--font-display);
  font-size: clamp(2.4rem, 5.6vw, 4.3rem);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.03;
  max-width: 16ch;
  margin: 0 0 1.1rem;
  color: #f7fafc;
  animation: rise 0.9s ease 0.1s both;
}

.hero-pipeline {
  margin: 1.6rem 0 0;
  max-width: 46ch;
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  color: rgba(247, 250, 252, 0.82);
  animation: rise 0.9s ease 0.42s both;
}

.hero-ghost {
  color: #fff;
  border-color: rgba(255, 255, 255, 0.4);
  background: rgba(255, 255, 255, 0.06);
}

.hero-ghost:hover {
  border-color: #fff;
  background: rgba(255, 255, 255, 0.12);
}

.btn-lg {
  padding: 0.9rem 1.65rem;
  font-size: 0.98rem;
}

/* SOURCE → BUILD → MOVE → PROTECT framework chips */
.framework-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 2rem;
}

.framework-chip {
  display: inline-flex;
  align-items: center;
  padding: 0.4rem 0.9rem;
  border-radius: 999px;
  background: rgba(200, 16, 46, 0.08);
  color: var(--red);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.framework-arrow {
  color: var(--gold-deep);
  font-weight: 700;
}

[dir="rtl"] .framework-arrow {
  display: inline-block;
  transform: scaleX(-1);
}

/* Pillar cards */
.pillars-grid {
  counter-reset: pillar;
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr;
  margin: 0 0 3rem;
}

.pillar-card {
  position: relative;
  padding: 1.6rem 1.4rem;
  background: #fff;
  border: 1px solid var(--line);
  border-top: 3px solid var(--red);
  border-radius: 12px;
  transition: transform 0.25s, box-shadow 0.25s;
}

.pillar-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow);
}

.pillar-card::before {
  counter-increment: pillar;
  content: "0" counter(pillar);
  position: absolute;
  top: 1.3rem;
  inset-inline-end: 1.3rem;
  font-family: var(--font-display);
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--gold-deep);
  opacity: 0.7;
}

.pillar-key {
  display: inline-block;
  margin-bottom: 0.55rem;
  font-family: var(--font-display);
  font-size: 1.4rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--ink);
}

.pillar-body {
  margin: 0;
  color: var(--ink-soft);
  font-size: 0.95rem;
}

.cap-services,
.how-process {
  margin-top: 2.75rem;
}

@media (min-width: 720px) {
  .pillars-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 980px) {
  .pillars-grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* 3-stage flow: You -> Doujin Supply hub (5 functions) -> Delivery, and a
   gold return connector looping Delivery back to Your business (closed loop).
   Mobile-first: stacked vertically; becomes a horizontal row at >=720px. */
.flow {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.6rem;
  /* Phone-friendly cap, left-aligned so it stays fully visible on small screens. */
  margin: 1rem 0 2.5rem;
  max-width: 21rem;
}

.flow__row {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.6rem;
}

.flow__stage {
  text-align: center;
  padding: 0.9rem 1rem;
  border-radius: 12px;
  font-weight: 600;
  font-size: 0.98rem;
  background: #fff;
  border: 1px solid var(--line);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
}

.flow__stage--you {
  background: var(--ink);
  border-color: var(--ink);
  color: #fff;
}

.flow__stage--end {
  border: 2px solid var(--gold-deep);
  color: var(--ink);
  font-weight: 700;
}

/* Central hub card with the five operational functions as chips */
.flow__hub {
  background: linear-gradient(135deg, var(--red), var(--red-deep));
  border: 1px solid var(--red-deep);
  border-radius: 14px;
  color: #fff;
  padding: 1rem 1.1rem 1.15rem;
  box-shadow: 0 10px 24px rgba(200, 16, 46, 0.25);
  text-align: center;
}

.flow__hub-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.15rem;
  margin-bottom: 0.75rem;
}

.flow__chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.45rem;
}

.flow__chip {
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 999px;
  padding: 0.35rem 0.75rem;
  font-size: 0.82rem;
  font-weight: 600;
  line-height: 1.2;
  color: #fff;
  white-space: nowrap;
}

.flow__arrow {
  align-self: center;
  color: var(--gold-deep);
  font-size: 1.35rem;
  line-height: 1;
  transform: rotate(90deg); /* points down when stages are stacked (mobile) */
}

/* Return connector (Delivery -> back to Your business).
   Mobile-first: an up arrow + caption below Delivery. */
.flow__return {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  margin-top: 0.15rem;
}

.flow__return-arrow {
  color: var(--gold-deep);
  font-size: 1.6rem;
  line-height: 1;
}

.flow__return-line {
  display: none; /* desktop-only curved path */
}

.flow__return-caption {
  background: var(--gold-lite);
  border: 1px solid var(--gold);
  color: var(--gold-deep);
  font-size: 0.78rem;
  font-weight: 700;
  padding: 0.14rem 0.62rem;
  border-radius: 999px;
  white-space: nowrap;
}

@media (min-width: 720px) {
  .flow {
    max-width: none; /* fill the content width on desktop */
    margin-inline: auto;
  }

  .flow__row {
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: 0.85rem;
  }

  .flow__stage {
    flex: 0 0 auto;
    max-width: 8.5rem;
  }

  /* the hub is the emphasized centerpiece and takes the remaining width */
  .flow__hub {
    flex: 1 1 auto;
    max-width: 640px;
  }

  .flow__arrow {
    transform: none; /* points across on desktop */
  }

  /* Desktop return connector: a curved (⊔) gold path under the row that runs
     from beneath Delivery (right) across to beneath Your business (left),
     ending in an up-pointing arrowhead aimed back at Your business. */
  .flow__return {
    display: block;
    margin-top: 0.9rem;
    padding-bottom: 1.5rem;
  }

  .flow__return-arrow {
    display: none;
  }

  .flow__return-line {
    display: block;
    position: relative;
    height: 30px;
    margin: 0 1.5rem;
    border: 2px solid var(--gold-deep);
    border-top: none;
    border-radius: 0 0 18px 18px;
  }

  /* up-pointing arrowhead sitting atop the left vertical, aimed at Your business */
  .flow__return-line::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    width: 0;
    height: 0;
    border-left: 6px solid transparent;
    border-right: 6px solid transparent;
    border-bottom: 9px solid var(--gold-deep);
    transform: translate(-6px, -8px);
  }

  .flow__return-caption {
    position: absolute;
    left: 50%;
    top: 30px; /* centered on the bottom horizontal line */
    transform: translate(-50%, -50%);
  }
}

/* RTL: mirror the horizontal forward arrows and the return path direction */
[dir="rtl"] .flow__arrow {
  transform: rotate(90deg);
}

@media (min-width: 720px) {
  [dir="rtl"] .flow__arrow {
    transform: scaleX(-1);
  }

  /* flip only the line/arrowhead (not the caption text) so the arrowhead
     points up at Your business on the right in RTL */
  [dir="rtl"] .flow__return-line {
    transform: scaleX(-1);
  }
}

/* Benchmark conversion band */
.benchmark-band {
  position: relative;
  overflow: hidden;
  padding: 2.25rem 1.5rem;
  border-radius: 16px;
  background: var(--ink);
  color: #f7fafc;
  border-inline-start: 4px solid var(--gold);
  box-shadow: var(--shadow);
}

.benchmark-band::after {
  content: "";
  position: absolute;
  width: 300px;
  height: 300px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(242, 183, 5, 0.18), transparent 70%);
  top: -110px;
  inset-inline-end: -90px;
  pointer-events: none;
}

.benchmark-band .eyebrow {
  position: relative;
  z-index: 1;
  color: var(--gold);
}

.benchmark-band h2 {
  position: relative;
  z-index: 1;
  margin: 0.5rem 0 0;
  max-width: 24ch;
}

.benchmark-band .section-lead {
  position: relative;
  z-index: 1;
  margin-bottom: 1.25rem;
  max-width: 62ch;
  color: rgba(247, 250, 252, 0.85);
}

.benchmark-list {
  position: relative;
  z-index: 1;
  list-style: none;
  margin: 0 0 1.5rem;
  padding: 0;
  display: grid;
  gap: 0.6rem;
  grid-template-columns: 1fr;
}

.benchmark-list li {
  position: relative;
  padding-inline-start: 1.7rem;
  color: rgba(247, 250, 252, 0.92);
  font-weight: 500;
}

.benchmark-list li::before {
  content: "→";
  position: absolute;
  inset-inline-start: 0;
  color: var(--gold);
  font-weight: 700;
}

[dir="rtl"] .benchmark-list li::before {
  content: "←";
}

.benchmark-note {
  position: relative;
  z-index: 1;
  margin: 0 0 1.5rem;
  font-size: 0.9rem;
  color: rgba(247, 250, 252, 0.72);
}

.benchmark-cta {
  position: relative;
  z-index: 1;
}

@media (min-width: 640px) {
  .benchmark-list {
    grid-template-columns: 1fr 1fr;
  }
}

@media (min-width: 720px) {
  .benchmark-band {
    padding: 3rem;
  }
  .benchmark-list {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* Product example chips */
.product-chips {
  list-style: none;
  margin: 0 0 2.25rem;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.product-chips li {
  padding: 0.4rem 0.9rem;
  border-radius: 999px;
  background: #fff;
  border: 1px solid var(--line);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--ink-soft);
}

/* Landed economics equation */
.landed-eq {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.55rem 0.6rem;
  margin: 1.75rem 0 0;
  padding: 1.5rem;
  border-radius: 14px;
  background: linear-gradient(140deg, #fff, #faf6ec);
  border: 1px solid var(--line);
  border-top: 3px solid var(--gold);
}

.landed-term {
  padding: 0.5rem 0.9rem;
  border-radius: 8px;
  background: #fff;
  border: 1px solid var(--line);
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--ink);
}

.landed-op {
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--ink-soft);
}

.landed-eq-sign {
  color: var(--red);
  font-size: 1.35rem;
}

.landed-result {
  padding: 0.6rem 1.15rem;
  border-radius: 999px;
  background: var(--red);
  color: #fff;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-size: 0.85rem;
  box-shadow: 0 10px 24px rgba(200, 16, 46, 0.25);
}

.visibility-compliance {
  margin-top: 3rem;
}

/* Long-term partnership quote */
.partnership-quote {
  position: relative;
  z-index: 1;
  margin: 1.25rem 0 0;
  max-width: 46ch;
  font-family: var(--font-display);
  font-size: clamp(1.3rem, 2.6vw, 1.95rem);
  font-weight: 500;
  line-height: 1.32;
  color: var(--ink);
}

.partnership-quote::before {
  content: "\201C";
  color: var(--gold-deep);
}

.partnership-quote::after {
  content: "\201D";
  color: var(--gold-deep);
}

/* ===== ASD Market Week 2026 event announcement bar ===== */
.asd-bar {
  position: relative;
  z-index: 60;
  background: linear-gradient(90deg, var(--ink) 0%, #14304a 55%, var(--ink) 100%);
  color: #f7fafc;
  border-bottom: 2px solid var(--gold);
  font-family: var(--font-body);
}
.asd-bar[hidden] {
  display: none;
}
.asd-bar__inner {
  max-width: var(--max);
  margin: 0 auto;
  padding: 0.5rem 1.15rem;
  display: flex;
  align-items: center;
  gap: 0.9rem;
}
.asd-bar__badge {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.28rem 0.7rem;
  border-radius: 999px;
  background: rgba(242, 183, 5, 0.16);
  color: var(--gold);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
}
.asd-bar__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--gold);
  box-shadow: 0 0 0 0 rgba(242, 183, 5, 0.6);
  animation: asd-pulse 2s ease-out infinite;
}
@keyframes asd-pulse {
  0% {
    box-shadow: 0 0 0 0 rgba(242, 183, 5, 0.55);
  }
  70% {
    box-shadow: 0 0 0 8px rgba(242, 183, 5, 0);
  }
  100% {
    box-shadow: 0 0 0 0 rgba(242, 183, 5, 0);
  }
}
.asd-bar__ticker {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.asd-bar__track {
  display: inline-flex;
  gap: 3rem;
  white-space: nowrap;
  animation: asd-roll 28s linear infinite;
  will-change: transform;
}
.asd-bar__ticker:hover .asd-bar__track,
.asd-bar__ticker:focus-within .asd-bar__track {
  animation-play-state: paused;
}
.asd-bar__msg {
  font-size: 0.86rem;
  font-weight: 600;
  color: #f4f7fa;
}
@keyframes asd-roll {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-50%);
  }
}
[dir="rtl"] .asd-bar__track {
  animation-direction: reverse;
}
.asd-bar__links {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}
.asd-bar__link {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.35rem 0.8rem;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 700;
  background: var(--gold);
  color: var(--ink);
  white-space: nowrap;
  transition: background 0.2s, color 0.2s, transform 0.2s;
}
.asd-bar__link::after {
  content: "\2197";
  font-size: 0.85em;
}
.asd-bar__link:hover {
  background: var(--gold-deep);
  color: #fff;
  transform: translateY(-1px);
}
.asd-bar__link--ghost {
  background: transparent;
  color: #f7fafc;
  border: 1px solid rgba(247, 250, 252, 0.35);
}
.asd-bar__link--ghost:hover {
  background: rgba(247, 250, 252, 0.12);
  color: #fff;
}
.asd-bar__close {
  flex: none;
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  border: none;
  color: rgba(247, 250, 252, 0.7);
  font-size: 1.35rem;
  line-height: 1;
  cursor: pointer;
  padding: 0 0.25rem;
}
.asd-bar__close:hover {
  color: #fff;
}
@media (max-width: 720px) {
  .asd-bar__inner {
    flex-wrap: wrap;
    padding: 0.45rem 0.85rem;
    gap: 0.5rem 0.7rem;
  }
  .asd-bar__ticker {
    order: 3;
    flex-basis: 100%;
  }
  .asd-bar__badge {
    font-size: 0.66rem;
  }
  .asd-bar__links {
    order: 2;
    margin-inline-start: auto;
  }
  .asd-bar__link {
    padding: 0.3rem 0.65rem;
    font-size: 0.72rem;
  }
}
@media (prefers-reduced-motion: reduce) {
  .asd-bar__track {
    animation: none;
  }
  .asd-bar__dot {
    animation: none;
  }
  .asd-bar__msg[aria-hidden="true"] {
    display: none;
  }
}

/* ============================================================
   Product Inventory page (inventory.html)
   ============================================================ */
.pi-page {
  max-width: var(--max);
  margin: 0 auto;
  padding: 2.5rem 1.25rem 4rem;
}

.pi-back {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink-soft);
  margin-bottom: 1.25rem;
}
.pi-back:hover {
  color: var(--red);
}

.pi-hero {
  margin-bottom: 2rem;
}
.pi-hero .eyebrow {
  color: var(--red);
}
.pi-hero h1 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.9rem, 4vw, 2.9rem);
  line-height: 1.1;
  margin: 0.35rem 0 0.75rem;
  color: var(--ink);
}
.pi-lead {
  max-width: 60ch;
  color: var(--ink-soft);
  font-size: 1.02rem;
}

/* Filter toolbar */
.pi-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin: 1.5rem 0 1.75rem;
  padding-bottom: 1.25rem;
  border-bottom: 1px solid var(--line);
}
/* Filter chips are a SHARED vocabulary: the public catalog filters by series
   with them and the signed-in buyer catalog filters by category with them, so
   the two pages read as one site rather than two. Both are multi-select, and
   "All" is simply the empty selection. State lives on aria-pressed as well as
   the class, because which chips are on is information a screen reader has no
   other way to hear. */
.pi-filters,
.f-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
}
.f-chips-l {
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  margin-inline-end: 2px;
}
.f-chip {
  border: 1px solid var(--line);
  background: #fff;
  color: var(--ink-soft);
  padding: 0.5rem 1rem;
  border-radius: 999px;
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  transition:
    background 0.18s ease,
    color 0.18s ease,
    border-color 0.18s ease;
}
.f-chip:hover {
  border-color: var(--red);
  color: var(--red);
}
.f-chip.is-on {
  background: var(--red);
  border-color: var(--red);
  color: #fff;
}
.pi-count {
  margin-inline-start: auto;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink-soft);
  letter-spacing: 0.02em;
}

/* Grid */
.pi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 1.25rem;
}

.pi-card {
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 8px 24px rgba(14, 36, 51, 0.05);
  transition:
    transform 0.18s ease,
    box-shadow 0.18s ease;
}
.pi-card:hover {
  transform: translateY(-3px);
  box-shadow: 0 18px 40px rgba(14, 36, 51, 0.12);
}

.pi-media {
  aspect-ratio: 4 / 3;
  background: #f4f6f8;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.pi-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.pi-media--placeholder {
  flex-direction: column;
  gap: 0.5rem;
  padding: 1rem;
  text-align: center;
  background:
    radial-gradient(120% 120% at 50% 0%, color-mix(in srgb, var(--pi-accent, #999) 18%, #fff) 0%, #fff 70%);
  border-bottom: 3px solid var(--pi-accent, var(--gold));
}
.pi-ph-type {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--pi-accent, var(--ink));
}
.pi-ph-note {
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
  opacity: 0.75;
}

.pi-body {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  padding: 1rem 1.05rem 1.15rem;
  flex: 1;
}
.pi-tag {
  align-self: flex-start;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--pi-accent, var(--ink));
  background: color-mix(in srgb, var(--pi-accent, #999) 12%, #fff);
  border: 1px solid color-mix(in srgb, var(--pi-accent, #999) 30%, #fff);
  padding: 0.2rem 0.55rem;
  border-radius: 999px;
}
.pi-name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.02rem;
  line-height: 1.25;
  margin: 0;
  color: var(--ink);
}
.pi-ids {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.75rem;
  margin: 0;
  font-size: 0.74rem;
  font-weight: 600;
  color: var(--ink-soft);
}
.pi-sku {
  color: var(--red);
}
.pi-model {
  opacity: 0.8;
}

.pi-specs {
  margin: 0.15rem 0 0;
  display: grid;
  gap: 0.3rem;
}
.pi-spec {
  display: grid;
  grid-template-columns: 6.5rem 1fr;
  gap: 0.5rem;
  font-size: 0.8rem;
  line-height: 1.35;
}
.pi-spec dt {
  color: var(--ink-soft);
  opacity: 0.72;
  font-weight: 500;
}
.pi-spec dd {
  margin: 0;
  color: var(--ink);
}

.pi-inquire {
  margin-top: 2.5rem;
  padding: 1.75rem;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: linear-gradient(180deg, #fff, #fbfcfd);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}
.pi-inquire p {
  margin: 0;
  color: var(--ink-soft);
  max-width: 46ch;
}

.pi-toolbar-end {
  margin-inline-start: auto;
  display: flex;
  align-items: center;
  gap: 1rem;
}
.pi-toolbar-end .pi-count {
  margin-inline-start: 0;
}
.pi-slideshow-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.5rem 0.95rem;
  border: 1px solid var(--red);
  border-radius: 999px;
  background: var(--red);
  color: #fff;
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  cursor: pointer;
  transition:
    background 0.18s ease,
    box-shadow 0.18s ease,
    transform 0.12s ease;
}
.pi-slideshow-btn:hover {
  background: color-mix(in srgb, var(--red) 88%, #000);
  box-shadow: 0 8px 20px -8px rgba(200, 16, 46, 0.6);
}
.pi-slideshow-btn:active {
  transform: translateY(1px);
}
.pi-slideshow-btn svg {
  flex: none;
}

/* Make image cards feel interactive (open slideshow) */
.pi-card--clickable {
  cursor: pointer;
}
.pi-card--clickable .pi-media img {
  transition: transform 0.4s ease;
}
.pi-card--clickable:hover .pi-media img {
  transform: scale(1.05);
}

/* ============ Slideshow (fullscreen paged gallery) ============ */
.pi-slideshow {
  position: fixed;
  inset: 0;
  z-index: 1000;
}
.pi-slideshow[hidden] {
  display: none;
}
.pi-ss-backdrop {
  position: absolute;
  inset: 0;
  background: #0b1c29;
}
.pi-ss-fs {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  color: #eaf1f6;
  background: radial-gradient(120% 90% at 50% -10%, #17384d 0%, #0b1c29 70%);
  animation: piSsFade 0.25s ease;
}
@keyframes piSsFade {
  from {
    opacity: 0;
  }
}

.pi-ss-progress {
  flex: none;
  height: 3px;
  background: rgba(255, 255, 255, 0.14);
}
.pi-ss-progress-bar {
  height: 100%;
  width: 0;
  background: var(--gold, #f2b705);
}
.pi-ss-progress-bar.is-running {
  animation: piSsBar linear forwards;
}
@keyframes piSsBar {
  from {
    width: 0;
  }
  to {
    width: 100%;
  }
}

.pi-ss-header {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: clamp(0.9rem, 2vw, 1.5rem) clamp(1rem, 3vw, 2.75rem);
}
.pi-ss-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.1rem, 2vw, 1.6rem);
  color: #fff;
  letter-spacing: 0.01em;
}
.pi-ss-close {
  flex: none;
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(255, 255, 255, 0.06);
  color: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition:
    background 0.18s ease,
    border-color 0.18s ease;
}
.pi-ss-close:hover {
  background: var(--red, #c8102e);
  border-color: var(--red, #c8102e);
}

.pi-ss-viewport {
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0 clamp(0.5rem, 2.5vw, 2rem);
}
.pi-ss-pagegrid {
  flex: 1;
  min-width: 0;
  height: 100%;
  display: grid;
  grid-template-columns: repeat(var(--ss-cols, 4), 1fr);
  gap: clamp(0.75rem, 1.5vw, 1.5rem);
  align-content: center;
  padding: clamp(0.5rem, 1.5vh, 1.25rem) 0;
}
.pi-ss-pagegrid.is-swap {
  animation: piSsSwap 0.35s ease;
}
@keyframes piSsSwap {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
}

.pi-ss-tile {
  background: #0f2333;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 14px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  min-height: 0;
  box-shadow: 0 24px 50px -30px rgba(0, 0, 0, 0.7);
}
.pi-ss-tile-media {
  flex: 1;
  min-height: 0;
  background: #12293b;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.pi-ss-tile-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.pi-ss-tile-media.is-placeholder {
  flex-direction: column;
  gap: 0.4rem;
  text-align: center;
  padding: 1rem;
  background: radial-gradient(120% 120% at 50% 0%, color-mix(in srgb, var(--pi-accent, #37699a) 30%, #12293b) 0%, #0b1c29 75%);
}
.pi-ss-tile-media .pi-ss-ph-type {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  color: #fff;
}
.pi-ss-tile-media .pi-ph-note {
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #9fb4c2;
}
.pi-ss-tile-body {
  flex: none;
  padding: 0.8rem 0.95rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.pi-ss-tag {
  align-self: flex-start;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: #fff;
  background: var(--pi-accent, #37699a);
  padding: 0.2rem 0.55rem;
  border-radius: 999px;
}
.pi-ss-name {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(0.92rem, 1.15vw, 1.12rem);
  line-height: 1.22;
  color: #fff;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.pi-ss-ids {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem 0.7rem;
  margin: 0;
  font-size: 0.72rem;
  font-weight: 600;
}
.pi-ss-ids .pi-ss-sku {
  color: var(--gold, #f2b705);
}
.pi-ss-ids .pi-ss-model {
  color: #9fb4c2;
}
.pi-ss-specs {
  margin: 0.15rem 0 0;
  display: grid;
  gap: 0.25rem;
}
.pi-ss-specs .pi-spec {
  grid-template-columns: 5.5rem 1fr;
  font-size: 0.74rem;
  gap: 0.4rem;
}
.pi-ss-specs .pi-spec dt {
  color: #9fb4c2;
  opacity: 1;
  font-weight: 500;
}
.pi-ss-specs .pi-spec dd {
  color: #eaf1f6;
}

.pi-ss-nav {
  flex: none;
  width: 2.8rem;
  height: 2.8rem;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(255, 255, 255, 0.06);
  color: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition:
    background 0.18s ease,
    transform 0.12s ease;
}
.pi-ss-nav:hover {
  background: var(--red, #c8102e);
  border-color: var(--red, #c8102e);
}
.pi-ss-nav:active {
  transform: scale(0.94);
}
[dir="rtl"] .pi-ss-nav svg {
  transform: scaleX(-1);
}

.pi-ss-controls {
  flex: none;
  display: flex;
  align-items: center;
  gap: 0.85rem;
  padding: clamp(0.6rem, 1.2vw, 1rem) clamp(1rem, 3vw, 2.75rem);
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.pi-ss-play {
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.2);
  background: transparent;
  color: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition:
    background 0.18s ease,
    border-color 0.18s ease;
}
.pi-ss-play:hover {
  background: rgba(255, 255, 255, 0.12);
  border-color: #fff;
}
.pi-ss-counter {
  font-size: 0.85rem;
  font-weight: 600;
  color: #cdd9e2;
  font-variant-numeric: tabular-nums;
}
.pi-ss-dots {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-inline-start: auto;
  max-width: 55%;
  justify-content: flex-end;
}
.pi-ss-dot {
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  border: none;
  padding: 0;
  background: rgba(255, 255, 255, 0.28);
  cursor: pointer;
  transition:
    background 0.18s ease,
    transform 0.12s ease;
}
.pi-ss-dot:hover {
  background: rgba(255, 255, 255, 0.6);
}
.pi-ss-dot.is-active {
  background: var(--gold, #f2b705);
  transform: scale(1.25);
}

@media (max-width: 700px) {
  .pi-ss-nav {
    width: 2.3rem;
    height: 2.3rem;
  }
  .pi-ss-dots {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .pi-ss-fs,
  .pi-ss-pagegrid.is-swap {
    animation: none;
  }
  .pi-ss-progress-bar.is-running {
    animation: none;
    width: 100%;
  }
}

[dir="rtl"] .pi-count {
  margin-inline-start: auto;
}
[dir="rtl"] .pi-spec {
  grid-template-columns: 7rem 1fr;
}

@media (max-width: 560px) {
  .pi-grid {
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: 0.85rem;
  }
  .pi-spec {
    grid-template-columns: 5.5rem 1fr;
  }
}

/* Homepage entry point → Product Inventory */
.inventory-cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1.25rem 1.75rem;
  margin: 1.75rem 0 0.5rem;
  padding: 1.5rem 1.75rem;
  border: 1px solid var(--line);
  border-radius: 18px;
  background: linear-gradient(120deg, rgba(200, 16, 46, 0.06), rgba(242, 183, 5, 0.08));
  box-shadow: 0 10px 30px -22px rgba(14, 36, 51, 0.5);
  text-decoration: none;
  color: inherit;
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.inventory-cta:hover {
  transform: translateY(-2px);
  box-shadow: 0 18px 40px -24px rgba(14, 36, 51, 0.55);
  border-color: rgba(200, 16, 46, 0.35);
}
.inventory-cta-text {
  flex: 1 1 320px;
  min-width: 0;
}
.inventory-cta-text .eyebrow {
  display: block;
  margin-bottom: 0.35rem;
}
.inventory-cta-text h3 {
  margin: 0 0 0.4rem;
  font-family: var(--display, "Fraunces", serif);
  font-size: clamp(1.2rem, 2.4vw, 1.55rem);
  color: var(--ink);
}
.inventory-cta-text p {
  margin: 0;
  color: var(--ink-soft);
  max-width: 62ch;
}
.inventory-cta-btn {
  flex: 0 0 auto;
  white-space: nowrap;
}
[dir="rtl"] .inventory-cta {
  background: linear-gradient(240deg, rgba(200, 16, 46, 0.06), rgba(242, 183, 5, 0.08));
}
@media (max-width: 560px) {
  .inventory-cta {
    padding: 1.25rem;
  }
  .inventory-cta-btn {
    width: 100%;
    text-align: center;
  }
}

/* ===================================================================== */
/* Partner Portal — account + admin SPA (Phases 2–5)                     */
/* ===================================================================== */
.portal {
  min-height: 100vh;
  background: linear-gradient(180deg, var(--paper), #fff 240px);
  color: var(--ink);
  font-family: var(--font-body);
}
.portal-header {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px 20px;
  background: rgba(255, 255, 255, 0.9);
  backdrop-filter: saturate(1.2) blur(8px);
  border-bottom: 1px solid var(--line);
}
.portal-brand {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.15rem;
  color: var(--ink);
  text-decoration: none;
  display: flex;
  align-items: center;
  gap: 10px;
}
.portal-brand .dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--red);
  box-shadow: 0 0 0 3px rgba(200, 16, 46, 0.15);
}
.portal-logo {
  height: 48px;
  width: auto;
  display: block;
}
@media (min-width: 720px) {
  .portal-logo {
    height: 56px;
  }
}
.portal-brand small {
  color: var(--red);
  font-family: var(--font-body);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-size: 0.62rem;
}
.portal-nav {
  display: flex;
  gap: 4px;
  margin-inline-start: auto;
  flex-wrap: wrap;
  align-items: center;
}
.portal-nav a {
  padding: 7px 12px;
  border-radius: var(--radius);
  color: var(--ink-soft);
  text-decoration: none;
  font-size: 0.86rem;
  font-weight: 500;
  cursor: pointer;
}
.portal-nav a:hover { background: var(--fog); }
.portal-nav a.active { background: var(--ink); color: #fff; }
.portal-nav a.nav-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.portal-nav .nav-ico {
  display: inline-flex;
  width: 15px;
  height: 15px;
  flex-shrink: 0;
}
.portal-nav .nav-ico svg { width: 15px; height: 15px; }
.portal-nav .nav-count {
  min-width: 1.2em;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--ink);
  color: #fff;
  font-size: 0.68rem;
  font-weight: 700;
  line-height: 1.45;
  text-align: center;
}
.portal-nav a.active .nav-count { background: #fff; color: var(--ink); }
.portal-nav select {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 6px 8px;
  font-family: var(--font-body);
  background: #fff;
  color: var(--ink);
}
.portal-main {
  max-width: var(--max);
  margin: 0 auto;
  padding: 32px 20px 80px;
}
.portal-card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: var(--shadow);
  padding: 28px;
}
/* ---- auth cards: sign in, apply, forgot, reset ----
   The front door, and the surface a partner lands on from a reset email. The
   gold top rule and the eyebrow are the portal's own vocabulary (the same rule
   heads .p-group and the same eyebrow scale is used across .acct-* and
   .pf-sec-t), so this reads as the same product rather than a login page
   borrowed from somewhere else. */
.auth-wrap {
  max-width: 452px;
  margin: 40px auto;
  border-top: 3px solid var(--gold);
  padding: 26px 28px 24px;
}
.auth-wrap--wide { max-width: 620px; }
.auth-eyebrow {
  font-size: 0.66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--gold-deep);
  margin: 0 0 7px;
}
.auth-wrap h1 {
  font-family: var(--font-display);
  font-size: 1.7rem;
  margin: 0 0 6px;
}
.auth-wrap .sub {
  color: var(--ink-soft);
  margin: 0 0 22px;
  font-size: 0.95rem;
}
/* Remember-me and the forgot link sit on one line: they are the two decisions a
   returning partner makes about this form, and the link used to be stranded
   under the primary button where it read as an afterthought. */
.auth-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 18px;
}
.auth-row .check-row { margin-bottom: 0; }
.auth-link { font-size: 0.84rem; color: var(--red); font-weight: 600; }
.auth-link:hover { text-decoration: underline; }
/* The alternate route out of this card, fenced off from the form above it. */
.auth-foot { border-top: 1px solid var(--line); padding-top: 15px; margin-top: 20px; }
.field { margin-bottom: 16px; }
.field label {
  display: block;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--ink-soft);
  margin-bottom: 6px;
  letter-spacing: 0.01em;
}
.field input,
.field select,
.field textarea {
  width: 100%;
  padding: 11px 13px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-family: var(--font-body);
  font-size: 0.95rem;
  color: var(--ink);
  background: #fff;
  box-sizing: border-box;
}
.field textarea { min-height: 90px; resize: vertical; }
.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--red);
  box-shadow: 0 0 0 3px rgba(200, 16, 46, 0.12);
}
.field-row { display: flex; gap: 14px; }
.field-row .field { flex: 1; }
/* ---- company address block (signup form, admin create-account modal) ----
   A real <fieldset>, not a div with a heading: these four inputs are one answer
   to one question, and the grouping has to exist for a screen reader too — the
   legend is what makes "Province / State" mean the company's, not the contact's.
   A UA fieldset carries its own border and inline padding, both reset here so it
   sits flush with the fields above and below it. */
.reg-addr {
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: 12px 14px 2px;
  margin: 0 0 16px;
  min-inline-size: 0;
}
.reg-addr > legend {
  padding: 0 6px;
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
}
.reg-addr > .sub { margin: 0 0 12px; font-size: 0.75rem; line-height: 1.4; }
/* Nothing to say and nothing to show: the block is hidden for a buyer or an
   admin, and a fieldset's UA display beats [hidden] without this. */
.reg-addr[hidden] { display: none; }
.check-row { display: flex; align-items: center; gap: 8px; margin-bottom: 16px; }
.check-row input { width: auto; }
.check-row label { margin: 0; }
.portal .btn {
  border: none;
  cursor: pointer;
  font-family: var(--font-body);
  font-weight: 600;
  padding: 11px 20px;
  border-radius: var(--radius);
  font-size: 0.92rem;
}
.btn-block { width: 100%; }
.btn-ghost {
  background: transparent;
  border: 1px solid var(--line);
  color: var(--ink);
}
.btn-danger { background: var(--red); color: #fff; }
.btn-sm { padding: 6px 12px; font-size: 0.8rem; }
.auth-alt { text-align: center; margin-top: 18px; font-size: 0.88rem; color: var(--ink-soft); }
.auth-alt a { color: var(--red); font-weight: 600; cursor: pointer; }
.notice {
  padding: 12px 14px;
  border-radius: var(--radius);
  font-size: 0.88rem;
  margin-bottom: 16px;
  border: 1px solid transparent;
}
.notice.err { background: #fdecef; color: var(--red-deep); border-color: rgba(200,16,46,0.25); }
.notice.ok { background: #eafaf0; color: #166534; border-color: rgba(22,101,52,0.2); }
.notice.info { background: #eef4fb; color: var(--ink-soft); border-color: var(--line); }
.notice.warn { background: #fff7e6; color: var(--gold-deep); border-color: rgba(207,143,4,0.3); }
.section-title {
  font-family: var(--font-display);
  font-size: 1.4rem;
  margin: 0 0 4px;
}
.section-lead { color: var(--ink-soft); margin: 0 0 22px; font-size: 0.92rem; }
/* The pill shape used to be written `.p-card .tag`, scoped to a card class no
   JS ever rendered — so the four live .tag sites (the restricted-price marker,
   the draft marker, and review-reason tags in both portals) had a background
   colour and nothing else: a coloured word, not a pill. Unscoped now. */
.tag {
  display: inline-block;
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 2px 8px;
  border-radius: 999px;
  font-weight: 700;
}
.tag.active { background: #eafaf0; color: #166534; }
.tag.draft { background: var(--fog); color: var(--ink-soft); }
.tag.archived { background: #f3e8e8; color: var(--red-deep); }
.tag.restricted { background: #fff7e6; color: var(--gold-deep); }
.data-table { width: 100%; border-collapse: collapse; font-size: 0.86rem; }
.data-table th, .data-table td {
  text-align: start;
  padding: 10px 12px;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
.data-table th { color: var(--ink-soft); font-weight: 600; font-size: 0.74rem; text-transform: uppercase; letter-spacing: 0.04em; }
.pill {
  font-size: 0.7rem;
  padding: 2px 9px;
  border-radius: 999px;
  font-weight: 700;
}
.pill.pending { background: #fff7e6; color: var(--gold-deep); }
.pill.approved { background: #eafaf0; color: #166534; }
.pill.rejected, .pill.suspended { background: #fdecef; color: var(--red-deep); }
.perm-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 6px; margin: 10px 0; }
.perm-grid label { display: flex; gap: 7px; align-items: center; font-size: 0.8rem; font-weight: 500; color: var(--ink-soft); }
.perm-grid input { width: auto; }
.spinner { text-align: center; padding: 60px; color: var(--ink-soft); }
.modal-back {
  position: fixed; inset: 0; background: rgba(14,36,51,0.5);
  display: flex; align-items: center; justify-content: center; z-index: 50; padding: 20px;
}
.modal {
  background: #fff; border-radius: 10px; max-width: 620px; width: 100%;
  max-height: 90vh; overflow: auto; padding: 26px; box-shadow: var(--shadow);
}
.modal h2 { font-family: var(--font-display); margin: 0 0 16px; }
.modal-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px; }
[dir="rtl"] .portal-nav { margin-inline-start: auto; }
@media (max-width: 640px) {
  .field-row { flex-direction: column; gap: 0; }
  /* Brand takes its own line so the wrapping nav cannot crowd the logo. */
  .portal-header { flex-wrap: wrap; gap: 8px 12px; padding: 10px 14px; }
  .portal-brand { flex: 1 0 100%; }
  .portal-nav { margin-inline-start: 0; gap: 2px; }
  .portal-nav a { padding: 6px 8px; font-size: 0.78rem; }
}

/* ---- status badges (icon + color + label), shared supplier/admin ---- */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 3px 9px;
  border-radius: 999px;
  line-height: 1;
  white-space: nowrap;
}
.badge svg { width: 12px; height: 12px; flex: none; }
/* The same badge with the word removed, for the corner of a product thumbnail.
   Square padding and no gap, so it is a round chip rather than a pill with
   nothing in it. The accessible name is not optional here and is supplied by
   UI.badgeIcon (role="img" + aria-label + title) — an icon that replaces text and
   keeps no name is a status a screen reader cannot report. */
.badge-icon { gap: 0; padding: 3px; border-radius: 999px; }
.badge-icon svg { width: 13px; height: 13px; }
.badge-approved { background: #eafaf0; color: #166534; }
.badge-pending  { background: #fff7e6; color: var(--gold-deep); }
.badge-draft    { background: var(--fog); color: var(--ink-soft); }
.badge-rejected { background: #fdecef; color: var(--red-deep); }
.badge-archived { background: #eef1f4; color: #64748b; }
/* In progress but already published to the counterparty (a released quote). */
.badge-info     { background: #eef4fb; color: #1d4e89; }
/* Brand-new and untouched (an unanswered RFQ). Solid ink rather than another
   pastel: it must not read as one of the five soft states, and it belongs at the
   top of the working queue. */
.badge-new      { background: var(--ink); color: var(--paper); }

/* ---- compact icon action buttons with tooltips ---- */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: #fff;
  color: var(--ink-soft);
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.icon-btn svg { width: 16px; height: 16px; }
.icon-btn:hover { background: var(--fog); color: var(--ink); }
.icon-btn:focus-visible { outline: 2px solid var(--gold-deep); outline-offset: 1px; }
.icon-btn.danger:hover { background: #fdecef; color: var(--red-deep); border-color: #f4c7d0; }
.icon-btn.primary:hover { background: #eafaf0; color: #166534; border-color: #bfe6cd; }
.icon-btn[disabled] { opacity: 0.4; cursor: not-allowed; }
/* The display:inline-flex above outranks the UA's `[hidden] { display: none }`,
   so `btn.hidden = true` on an icon button would otherwise do nothing. Declared
   once here for every variant instead of per-container. */
.icon-btn[hidden] { display: none; }
.icon-actions { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }

/* ---- product list thumbnails ---- */
.row-thumb {
  width: 46px; height: 46px; border-radius: 6px;
  background: var(--fog) center/cover no-repeat;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--ink-soft); font-size: 0.62rem; flex: none;
  border: 1px solid var(--line);
}
/* `.p-card`, `.grid-cards`, `.p-card .card-select` and `.select-bar` used to
   live here. All four were dead — no JS or HTML in the repo ever rendered them
   — and leaving a dead `.p-card` beside the live `.pc-card` was a trap: the
   next person to restyle a product card had even odds of editing the copy that
   does nothing. The live vocabularies are `.pc-*` (admin and supplier product
   cards) and `.c-*` (the buyer catalog). */

/* ---- company sections in the admin product list ----
   The admin list is grouped by company (see adminListHtml in js/admin.js), so the
   grid is repeated once per supplier under a heading instead of running flat.

   The heading is a quiet rule-and-label rather than a card: it separates, and it
   must not compete with the products under it for attention. */
.pc-co + .pc-co { margin-top: 16px; }
.pc-co-h {
  display: flex;
  align-items: baseline;
  gap: 7px;
  margin: 0 0 8px;
  padding-bottom: 5px;
  border-bottom: 1px solid var(--line);
}
.pc-co-i {
  flex: none;
  color: var(--ink-soft);
  opacity: 0.7;
  /* Optical, not baseline: the glyph's mass sits above the text baseline. */
  align-self: center;
  line-height: 0;
}
.pc-co-i > svg { inline-size: 13px; block-size: 13px; }
.pc-co-n {
  margin: 0;
  min-width: 0;
  font-family: var(--font-display);
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pc-co-w { font-size: 0.7rem; color: var(--ink-soft); white-space: nowrap; }
/* Pushed to the far end, and reads as a count of what is on screen rather than of
   what the company has — the wording carries that, this only positions it. */
.pc-co-c {
  margin-inline-start: auto;
  flex: none;
  font-size: 0.66rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
}

/* ---- select-all strip above a card grid ----
   A card grid has no header row to hold the checkbox that used to sit there. */
.pc-selall {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin: 0 0 9px;
  font-size: 0.76rem;
  font-weight: 600;
  color: var(--ink-soft);
  cursor: pointer;
}
.pc-selall input {
  width: 16px;
  height: 16px;
  cursor: pointer;
}

/* ---- supplier batch action bar ---- */
.batch-actions {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 14px; margin: 0 0 14px;
  background: var(--fog); border: 1px solid var(--line); border-radius: 8px;
}
.batch-actions .count { font-weight: 700; font-size: 0.85rem; }

/* ---- profile: grouped sections + required marks ---- */
.form-section { margin: 0 0 22px; }
.form-section > h3 {
  font-family: var(--font-display); font-size: 1rem; margin: 0 0 4px;
}
.form-section > .hint { color: var(--ink-soft); font-size: 0.82rem; margin: 0 0 12px; }
.req { color: var(--red-deep); font-weight: 700; margin-inline-start: 2px; }
.field.invalid input, .field.invalid select { border-color: var(--red-deep); background: #fff7f8; }
.field .field-note { font-size: 0.74rem; color: var(--ink-soft); margin-top: 4px; }

/* ============================================================
   Account page: identity header + completeness meter + a dense
   two-column profile form beside the security panel.
   Colour is carried by ONE per-account accent pair (--acct-a /
   --acct-b), always drawn from the brand palette.
   ============================================================ */
.acct-hero {
  --acct-a: var(--gold);
  --acct-b: var(--gold-deep);
  position: relative;
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: var(--shadow);
  padding: 18px 22px;
  margin-bottom: 18px;
  overflow: hidden;
}
.acct-hero::before {
  content: "";
  position: absolute;
  top: 0;
  inset-inline: 0;
  height: 3px;
  background: linear-gradient(to right, var(--acct-a), var(--acct-b));
}
.acct-tone-1 { --acct-a: var(--red); --acct-b: var(--red-deep); }
.acct-tone-2 { --acct-a: var(--gold-deep); --acct-b: var(--red); }
.acct-tone-3 { --acct-a: var(--ink-soft); --acct-b: var(--ink); }

.acct-ava {
  width: 62px;
  height: 62px;
  flex: none;
  border-radius: 15px;
  background: linear-gradient(135deg, var(--acct-a), var(--acct-b));
  color: #fff;
  font-family: var(--font-display);
  font-size: 1.4rem;
  font-weight: 600;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 6px 16px rgba(14, 36, 51, 0.18);
  user-select: none;
}
.acct-id { flex: 1 1 260px; min-width: 0; }
.acct-greet { font-size: 0.78rem; color: var(--ink-soft); margin: 0; }
.acct-name {
  font-family: var(--font-display);
  font-size: 1.3rem;
  margin: 1px 0 3px;
  line-height: 1.2;
  overflow-wrap: anywhere;
}
.acct-meta { font-size: 0.8rem; color: var(--ink-soft); margin: 0 0 8px; overflow-wrap: anywhere; }
.acct-dot { opacity: 0.5; }
.acct-chips { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.acct-chip {
  display: inline-flex;
  align-items: center;
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  line-height: 1;
  padding: 4px 9px;
  border-radius: 999px;
  background: var(--fog);
  color: var(--ink-soft);
  white-space: nowrap;
}

/* completeness meter — the only place a colour means "how are you doing" */
.acct-meter { flex: 0 1 240px; min-width: 190px; }
.acct-meter-top { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.acct-meter-l {
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
}
.acct-meter-v { font-size: 1rem; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ink); }
.acct-bar { height: 6px; border-radius: 999px; background: var(--fog); overflow: hidden; margin: 6px 0 5px; }
.acct-bar > i { display: block; height: 100%; border-radius: 999px; background: var(--gold); transition: width 0.3s ease; }
.acct-meter.is-low .acct-bar > i { background: var(--red); }
.acct-meter.is-full .acct-bar > i { background: #2e8b57; }
.acct-meter.is-full .acct-meter-v { color: #166534; }
.acct-meter-note { font-size: 0.72rem; color: var(--ink-soft); margin: 0; line-height: 1.4; }

/* form beside the security panel; one column once that stops being readable */
.acct-cols { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: 18px; align-items: start; }
.acct-card { padding: 20px 22px 22px; }
.acct-card-t { font-family: var(--font-display); font-size: 1.06rem; margin: 0 0 3px; }
/* Any icon in a card heading needs an explicit box: an inline SVG with no
   intrinsic size fills whatever the heading gives it. */
.acct-card-t { display: flex; align-items: center; gap: 7px; }
.acct-card-t svg { width: 15px; height: 15px; flex: none; color: var(--gold-deep); }
/* The change-password block, full width below the two profile columns. */
.acct-pw { margin-top: 18px; }
.acct-card-sub { font-size: 0.8rem; color: var(--ink-soft); margin: 0 0 14px; }

.acct-sec { border-top: 1px solid var(--line); padding: 13px 0 2px; }
.acct-sec:first-child { border-top: 0; padding-top: 0; }
.acct-sec-t {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  margin: 0 0 10px;
  font-family: var(--font-body);
}
.acct-sec-t svg { width: 13px; height: 13px; flex: none; }
/* A sub-heading INSIDE a section — the company address block within the private
   contact section. Reuses the section heading's type so the two read as one
   hierarchy, and only steps the spacing so it does not look like a new section. */
.acct-sec-t--sub { margin-top: 15px; }
.acct-sec-hint { font-size: 0.75rem; color: var(--ink-soft); margin: -5px 0 11px; line-height: 1.4; }
/* Private block: same cordoned-off treatment as the product form's
   admin-only pricing section, plus a standing "admin only" marker. */
.acct-sec--private {
  background: #fffdf5;
  border: 1px solid #f0dca8;
  border-radius: 9px;
  padding: 13px 15px 15px;
  margin-top: 16px;
}
.acct-sec--private > .acct-sec-t { color: var(--gold-deep); }
.acct-lockchip {
  margin-inline-start: auto;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: var(--gold-deep);
  color: #fff;
  border-radius: 999px;
  padding: 3px 8px;
  line-height: 1;
  white-space: nowrap;
}

.acct-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 11px 16px; }
.acct-grid--1 { grid-template-columns: minmax(0, 1fr); }
/* The change-password form, now full width below the profile: three fields in
   one row read as one task, where a stacked column read as three. */
.acct-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.acct-span2 { grid-column: 1 / -1; }
/* The grid gap owns the vertical rhythm, and the inputs match the product
   form's compact scale so the whole portal reads as one system. */
.acct-form .field { margin-bottom: 0; }
.acct-form .field label { font-size: 0.72rem; margin-bottom: 4px; }
.acct-form .field input,
.acct-form .field select,
.acct-form .field textarea { padding: 8px 10px; font-size: 0.88rem; border-radius: 7px; }
.acct-form .field textarea { min-height: 74px; }
.acct-actions { display: flex; justify-content: flex-end; margin-top: 16px; }

/* ---- account statistics (the slot the password panel used to hold) ----
   Aggregates from GET /api/stats. Tiles are deliberately quiet: one label, one
   number, and a footnote only where the DATA needs qualifying. */
.acct-statcard { padding-bottom: 18px; }
/* .portal-card sets a display, which beats the UA's `[hidden] { display:none }`
   because author styles outrank UA styles. Declared explicitly so the card can
   actually be hidden when an account has no statistics to show. */
.acct-statcard[hidden] { display: none; }
.acct-stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 9px; }
.acct-stat {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: 10px 12px 11px;
  min-width: 0;
}
.acct-stat--wide,
.acct-stat--chart { grid-column: 1 / -1; }
.acct-stat--ok { background: #f4fbf7; border-color: #cdeada; }
.acct-stat--warn { background: #fffaf0; border-color: #f0d79a; }
.acct-stat--money { background: var(--cream); border-color: #e8dcc2; }
.acct-stat-l {
  font-size: 0.66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  margin: 0 0 4px;
}
.acct-stat-v {
  font-family: var(--font-display);
  font-size: 1.32rem;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  margin: 0;
  /* A long money string must wrap rather than push the tile wide and break the
     grid — currency code plus grouped digits gets long in some locales. */
  overflow-wrap: anywhere;
}
/* The currency code and its figure are one unit: isolate keeps the bidi
   algorithm from reordering them against neighbouring text, and no wrap point is
   allowed inside. Deliberately NOT `direction: ltr` — that would pin the value
   to the left edge of an RTL card; isolation alone renders "USD 44,906.50" in
   the right order while the block still follows the page direction. */
.acct-stat--money .acct-stat-v {
  unicode-bidi: isolate;
  overflow-wrap: normal;
  white-space: nowrap;
}
.acct-stat--money .acct-stat-sub { font-weight: 600; color: var(--gold-deep); }
.acct-stat-sub { font-size: 0.7rem; color: var(--ink-soft); margin: 4px 0 0; line-height: 1.35; }
/* The data caveats — "USD orders only", the non-USD disclosure, closed orders
   with no quoted total. Legible but subordinate: they qualify the number above
   them, they are not a disclaimer paragraph. */
.acct-stat-note {
  font-size: 0.66rem;
  color: var(--ink-soft);
  opacity: 0.85;
  margin: 5px 0 0;
  line-height: 1.4;
  border-top: 1px dashed var(--line);
  padding-top: 5px;
}
.acct-stat-empty { font-size: 0.76rem; color: var(--ink-soft); margin: 2px 0 0; line-height: 1.45; }
.acct-stat--chart.is-empty { background: repeating-linear-gradient(135deg, var(--paper), var(--paper) 7px, var(--fog) 7px, var(--fog) 8px); }

/* Twelve-bar month series, inline SVG (no charting library in this project).
   EMITTED BY UI.chart() in portal-ui.js, not by any one page: the account page's
   chart tile and the dashboard's statistics section both draw through it. The
   wrapper carries dir="ltr" from there — a time axis reads oldest-to-newest in
   Arabic too. */
.acct-spark-wrap { margin: 8px 0 2px; }
.acct-spark { display: block; width: 100%; height: 42px; }
.acct-spark-b { fill: var(--gold); }
/* An empty month keeps its slot as a 1px stub so the time axis stays even. */
.acct-spark-b.is-zero { fill: var(--line); }

/* Status distribution: the shared badge colours from portal-ui.js, so a status
   means the same thing here, on the orders list and in the admin console. */
.acct-spread { display: flex; flex-wrap: wrap; gap: 6px; }
.acct-spread-i { display: inline-flex; align-items: center; gap: 5px; }
.acct-spread-i > b { font-size: 0.82rem; font-variant-numeric: tabular-nums; color: var(--ink); }

/* ---- ranked breakdowns ----
   The one horizontal-bar shape in the project: the category and province charts
   on the admin console's Catalog tab, and every ranked breakdown in the portal
   dashboard's statistics section. The markup is EMITTED BY UI.chart() in
   portal-ui.js for all of them, so these rules have exactly one writer. The
   console frames them in the .acct-stat tiles above (same component, different
   audience); the dashboard frames them in nothing.

   Three columns — name, bar, count — on a fixed grid so the bars share one
   baseline and can actually be compared down the column. The count is last and
   tabular so the digits line up under each other. */
.cat-stats { margin-top: 12px; }
.cat-bars { list-style: none; margin: 6px 0 0; padding: 0; display: grid; gap: 5px; }
.cat-bar {
  display: grid;
  grid-template-columns: minmax(0, 11em) minmax(0, 1fr) auto;
  align-items: center;
  gap: 9px;
}
.cat-bar-l {
  display: flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
  font-size: 0.74rem;
  color: var(--ink);
}
.cat-bar-l > svg { flex: none; inline-size: 11px; block-size: 11px; opacity: 0.55; }
.cat-bar-t { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.cat-bar-track {
  block-size: 8px;
  border-radius: 4px;
  background: var(--fog);
  overflow: hidden;
}
/* inline-size, not width: the fill is set from JS as a percentage and grows from
   the reading edge, which is the right edge in Arabic. The console is EN/ZH only
   today, but this rule is shared stylesheet-wide and the portal is not. */
.cat-bar-fill { display: block; block-size: 100%; background: var(--gold); border-radius: 4px; }
.cat-bar-n {
  font-size: 0.76rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
/* The rows that are NOT a real value: the Uncategorized bucket, the Unknown
   province bucket, and the folded tail. Drawn — dropping them would make a
   partial breakdown look complete — but visibly subordinate to the named rows,
   so nobody reads "Unknown" as a place. */
.cat-bar.is-muted .cat-bar-l { color: var(--ink-soft); font-style: italic; }
.cat-bar.is-muted .cat-bar-fill { background: var(--line); }
.cat-bar.is-muted .cat-bar-n { color: var(--ink-soft); font-weight: 600; }
.cat-cta { margin-top: 14px; }

@media (max-width: 560px) {
  /* The name column stops being a fixed measure before it squeezes the bar out of
     existence; below this width the bar is the first thing to go, not the label. */
  .cat-bar { grid-template-columns: minmax(0, 1fr) auto; }
  .cat-bar-track { display: none; }
}

@media (max-width: 900px) {
  .acct-cols { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 640px) {
  .acct-grid--3 { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 560px) {
  .acct-grid { grid-template-columns: minmax(0, 1fr); }
  .acct-hero { padding: 16px; gap: 14px; }
  .acct-ava { width: 50px; height: 50px; border-radius: 13px; font-size: 1.15rem; }
  .acct-meter { flex: 1 1 100%; }
}

/* ---- import: fixed-value constants grid ---- */
.fixed-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px 16px; margin: 8px 0 4px; }

/* ---- admin product detail gallery ---- */
.detail-gallery { display: flex; gap: 8px; flex-wrap: wrap; margin: 4px 0 14px; }
.detail-gallery img { width: 96px; height: 96px; object-fit: cover; border-radius: 8px; border: 1px solid var(--line); }
.detail-specs td:first-child { font-weight: 600; color: var(--ink-soft); width: 40%; }

/* ---- My products: header + group tabs ---- */
/* Not a title — the nav already says where you are — but two lines that earn the
   space the actions were sitting alone in: what this list is FOR, and what is
   currently in it. space-between rather than flex-end now that there is something
   at the inline start; the actions stay at the inline end in LTR and RTL alike. */
.products-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; flex-wrap: wrap; }
.products-head-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
/* min-width: 0 so the two lines can actually clip instead of forcing the row to
   wrap the buttons onto a line of their own; flex: 1 1 260px so that BELOW roughly
   360px of copy width they wrap deliberately rather than being squeezed to three
   words. Checked at 1280 / 900 / 600 / 380px. */
.products-lede { flex: 1 1 260px; min-width: 0; }
.products-slogan {
  margin: 0;
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--ink);
  line-height: 1.3;
}
/* One line, clipped, with the full text in a title: the count of states is
   open-ended (five are possible) and this must not become two lines of header. */
.products-status {
  margin: 2px 0 0;
  font-size: 0.76rem;
  color: var(--ink-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tabs {
  display: flex; gap: 4px; flex-wrap: wrap; align-items: center;
  border-bottom: 1px solid var(--line); margin: 10px 0 16px; padding-bottom: 0;
}
.tab {
  border: 0; background: none; cursor: pointer; padding: 8px 12px;
  font-size: 0.86rem; font-weight: 600; color: var(--ink-soft);
  border-bottom: 2px solid transparent; margin-bottom: -1px; border-radius: 6px 6px 0 0;
}
.tab:hover { color: var(--ink); background: var(--fog); }
.tab.active { color: var(--gold-deep); border-bottom-color: var(--gold-deep); }
.tab-count { font-size: 0.72rem; color: var(--ink-soft); background: var(--fog); border-radius: 999px; padding: 1px 7px; margin-inline-start: 3px; }
.tab.active .tab-count { background: #fff7e6; color: var(--gold-deep); }
.tab-add {
  border: 1px dashed var(--line); background: none; cursor: pointer; color: var(--ink-soft);
  width: 28px; height: 28px; border-radius: 6px; font-size: 1.1rem; line-height: 1; margin-inline-start: 4px;
}
.tab-add:hover { color: var(--gold-deep); border-color: var(--gold-deep); }

/* A real group tab: label + inline rename input + delete icon share one
   underline, so the wrapper (not the button) carries the active state. */
.tab-group {
  display: inline-flex; align-items: center;
  border-bottom: 2px solid transparent; margin-bottom: -1px; border-radius: 6px 6px 0 0;
}
.tab-group > .tab { border-bottom: 0; margin-bottom: 0; border-radius: 6px 6px 0 0; }
.tab-group.active { border-bottom-color: var(--gold-deep); }
.tab-group:hover { background: var(--fog); }
.tab-group.active:hover { background: none; }
/* Kept in the layout at low contrast so the tab strip never reflows on hover. */
.tab-group > .icon-btn {
  width: 20px; height: 20px; border: 0; border-radius: 5px; background: none;
  margin-inline: -6px 2px; opacity: 0.3; transition: opacity 0.12s ease;
}
.tab-group > .icon-btn svg { width: 13px; height: 13px; }
.tab-group:hover > .icon-btn,
.tab-group.active > .icon-btn,
.tab-group:focus-within > .icon-btn { opacity: 1; }
.tab-group.is-busy { opacity: 0.5; pointer-events: none; }
.tab-group.is-renaming > .tab,
.tab-group.is-renaming > .icon-btn { display: none; }
.tab-rename {
  font-size: 0.86rem; font-weight: 600; color: var(--ink); background: #fff;
  border: 1px solid var(--gold-deep); border-radius: 6px; outline: none;
  padding: 5px 8px; margin: 2px 4px; min-width: 8ch; width: 14ch;
}

/* ============================================================
   Compact product list (shared: supplier / admin / buyer)
   Column template is supplied per-view as the --p-cols variable.
   ============================================================ */
.p-list {
  border: 1px solid var(--line);
  border-radius: 9px;
  background: #fff;
  overflow: hidden;
}
.p-row {
  display: grid;
  grid-template-columns: var(--p-cols, 26px 46px minmax(0, 1fr) auto);
  align-items: center;
  gap: 12px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--line);
  transition: background 0.15s ease;
}
.p-row:last-child { border-bottom: 0; }
.p-row:hover { background: #fbfcfd; }
.p-row.is-selected { background: #fffdf4; box-shadow: inset 3px 0 0 var(--gold-deep); }
.p-row.is-busy { opacity: 0.55; pointer-events: none; }
.p-row.flash-ok { animation: pRowOk 1.1s ease; }
.p-row.flash-err { animation: pRowErr 1.1s ease; }
@keyframes pRowOk { 0%, 35% { background: #eafaf0; } 100% { background: transparent; } }
@keyframes pRowErr { 0%, 35% { background: #fdecef; } 100% { background: transparent; } }

/* header row */
.p-row.p-head {
  background: var(--fog);
  border-bottom: 1px solid var(--line);
  padding-top: 7px;
  padding-bottom: 7px;
  position: sticky;
  top: 0;
  z-index: 3;
}
.p-row.p-head > * {
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
}
.p-row.p-head:hover { background: var(--fog); }

.p-sel { width: 17px; height: 17px; cursor: pointer; margin: 0; }

/* clickable thumbnail -> lightbox */
.p-thumb {
  position: relative;
  width: 46px;
  height: 46px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--fog) center/cover no-repeat;
  cursor: zoom-in;
  color: var(--ink-soft);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  overflow: hidden;
}
.p-thumb svg { width: 16px; height: 16px; opacity: 0.5; }
.p-thumb--md { width: 52px; height: 52px; }
.p-thumb--lg { width: 76px; height: 76px; border-radius: 8px; }
.p-thumb--lg svg { width: 22px; height: 22px; }
.p-thumb.has-img::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(14, 36, 51, 0.42) center/16px 16px no-repeat;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23fff' stroke-width='1.7' stroke-linecap='round'%3E%3Ccircle cx='7' cy='7' r='4.6'/%3E%3Cpath d='M10.4 10.4L14 14M7 5v4M5 7h4'/%3E%3C/svg%3E");
  opacity: 0;
  transition: opacity 0.15s ease;
}
.p-thumb.has-img:hover::after { opacity: 1; }
.p-thumb .p-imgn {
  position: absolute;
  bottom: 0;
  inset-inline-end: 0;
  background: rgba(14, 36, 51, 0.72);
  color: #fff;
  font-size: 0.6rem;
  font-weight: 700;
  line-height: 1;
  padding: 2px 4px;
  border-start-start-radius: 4px;
}

/* main cell: name + meta */
.p-main { min-width: 0; }
.p-name {
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--ink);
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.p-sub {
  font-size: 0.75rem;
  color: var(--ink-soft);
  margin: 1px 0 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.p-cell { font-size: 0.8rem; color: var(--ink-soft); min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.p-cell.strong { color: var(--ink); font-weight: 600; }
.p-acts { display: flex; gap: 5px; justify-content: flex-end; align-items: center; flex-wrap: nowrap; }
.p-acts .icon-btn { width: 29px; height: 29px; border-radius: 6px; }
.p-acts .icon-btn svg { width: 15px; height: 15px; }

/* `.p-flag` was the inline rejection strip under a product ROW. Both of its
   renderers now build cards, so it went the way of `.p-card` rather than
   lingering as a second dead card-era class. Its replacement is `.pc-flag`. */

/* ---- buyer catalog: spec-complete product cards ------------------------------
   Labelled, unlike .pc-card, and for a stated reason: a buyer has never seen
   these products, so a bare "Ceramic" is a guess about which field it belongs to.
   But narrower and tighter than the first version. The long tail of
   supplier-entered specs moved into a <details> inside the card, so the visible
   part is a fixed four rows and no longer has to be sized for the worst spec
   sheet in the catalog — which is what let the column shrink from 260px. */
.c-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(215px, 1fr));
  gap: 12px;
  margin: 0 0 16px;
  /* Same reason as .pc-grid: one product with a photo must not inflate the
     height of every card beside it. */
  align-items: start;
}
.c-card {
  border: 1px solid var(--line);
  border-radius: 9px;
  background: #fff;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.c-cover {
  aspect-ratio: 4 / 3;
  background: var(--fog) center / cover no-repeat;
  border: 0;
  border-bottom: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-soft);
  position: relative;
  padding: 0;
  width: 100%;
}
.c-cover.has-img {
  cursor: zoom-in;
}
/* A product with no photo gets a strip, not a hole. Most of this catalog has no
   images yet, and reserving 4:3 for each of them pushed the specs — the reason
   the card exists — below the fold. */
.c-cover--none {
  aspect-ratio: auto;
  min-height: 38px;
}
.c-cover--none svg {
  width: 18px;
  height: 18px;
  opacity: 0.55;
}
.c-cover svg {
  width: 28px;
  height: 28px;
}
.c-body {
  padding: 9px 12px 11px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1;
}
.c-name {
  margin: 0;
  font-size: 0.88rem;
  font-weight: 700;
  color: var(--ink);
  line-height: 1.28;
}
/* The `.c-tag` category/type pills are gone with the buyer card's fixed spec set.
   They were the two attributes shown WITHOUT any name at all, which `replace` made
   untenable twice over: they duplicated codes the supplier can now choose (so a card
   could show `type` twice, once as a pill and once as a row), and they showed them
   whether the supplier had chosen them or not. Both now render through
   catalogFieldRow like every other field. Not to be confused with the icon chips
   below, which are a different thing: those carry the attribute name in a glyph. */

/* The buyer card's attributes, as icon chips. This REPLACES `.c-specs`, a two-column
   label/value grid whose label column had to clear the longest fixed label before any
   value could start — measured at a 6.4em floor, found by shrinking past it until the
   English "Dimensions" broke mid-word. A glyph costs 14px where that column cost most
   of a third of the line, and the label it replaces is carried in each chip's
   accessible name and in the row's own `title` rather than dropped.

   The chip vocabulary itself (.pc-attr, .pc-attr-v) is the product card's, shared
   rather than restated: three surfaces draw the same attributes now, and a second
   spelling of "the glyph is 11px and quiet" is a second one that can drift. Only the
   size differs, because this card is 215px wide where those are 142px. */
.c-attrs {
  margin: 0;
  font-size: 0.75rem;
  color: var(--ink);
  /* .pc-attrs supplies the clamp; the overflow it clamps against comes from
     .pc-meta on the product cards, which this one does not carry. */
  overflow: hidden;
}
/* A FLOOR field the supplier has not filled in. It has to stay legible, because it
   carries real information — the listing is incomplete, which is a question to ask
   before ordering — but it must not read as a value, or a buyer scanning a row of
   materials would take "not specified" for one. Italic rather than dimmed alone:
   dimming at this 0.75rem size drops below comfortable contrast, and the slant marks
   it as our words rather than the supplier's.

   Matched on the class alone, so it lands on the chip on the card face AND on the
   <dd> in the details popup. It used to be `.c-specs dd.c-none`, which reached only
   the face — the popup renders the same floor placeholder through the same helper and
   got none of this, so "not specified" sat there looking exactly like a value. */
.c-none {
  color: var(--ink-soft);
  font-style: italic;
}
.c-colors {
  display: inline-flex;
  gap: 3px;
  vertical-align: middle;
  margin-inline-end: 5px;
}
/* Prose is clamped in CSS rather than cut in JS, so the whole sentence stays
   selectable and findable with the browser's own search. */
.c-desc {
  margin: 0;
  font-size: 0.75rem;
  color: var(--ink-soft);
  line-height: 1.42;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* Two controls now: the details popup's info icon and the order button. The icon
   goes first in READING order, because it is the one that answers a question about
   the product and the order button is the commitment. */
.c-foot {
  margin-top: auto;
  padding-top: 4px;
  display: flex;
  align-items: center;
  gap: 6px;
  justify-content: flex-end;
}
.c-group {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 4px 0 10px;
}

/* ---- compact product card: the work-queue counterpart to .c-card ------------
   DELIBERATELY NOT .c-card. A buyer COMPARES products they have never seen, so
   that card labels every spec and spends the width to do it. An admin or a
   supplier SCANS inventory they already know, five or six across, and a label
   in front of every value would be more furniture than content at this size —
   so values here are bare and the column header they used to sit under is gone
   with the row. What replaces the header is `title`: every truncated value
   carries its full text.

   Five or six per row is the whole constraint. Everything below follows from
   the roughly 120px of usable width that leaves. */
/* SIX ACROSS, WITH ROOM FOR SIX. Measured before changing anything: at a 1440px
   viewport this grid was ALREADY six columns — .portal-main caps at var(--max)
   1120px, less its 20px padding and .portal-card's 28px, leaving 1022px, which
   auto-fill divides into six 162px tracks. So "fit 6 cards" was not a column count
   problem; it was that six 162px cards on a 1440px screen left 320px of the screen
   unused while the meta line had just gone to two lines to fit four attributes.

   --max is NOT what widens. It is the shared page measure — the marketing sections,
   the account views, the auth cards and the portal all read it — so raising it
   would reflow the entire site to fix one grid. Instead account.js puts
   `view-products` on <body> for that one view and takes it off again on every other
   render, and the override below is scoped to it.

   The columns are then PINNED to six rather than left to auto-fill. In a 1304px
   grid auto-fill would have produced eight 152px tracks, which is narrower cards
   and more of them — the opposite of the request. Six explicit tracks spend the
   whole width on the six. Below the media query nothing changes: auto-fill still
   decides, and still lands on five, four, three as the window narrows. */
.pc-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(142px, 1fr));
  gap: 10px;
  /* Cards take their own height instead of stretching to the tallest in the row.
     Almost nothing in this catalog has a photo yet, so a single product that
     does would otherwise inflate its five neighbours by the height of a cover
     they do not have — measured at 302px a card where the content needed 193.
     A ragged bottom edge costs nothing here; that much dead space costs the
     density this grid exists for. */
  align-items: start;
}
/* 1180px is where the widened panel is actually wider than the shared measure it
   replaces: below it, `min()` has already clamped to 100% and the override is a
   no-op, so the breakpoint is where the behaviour changes rather than a guess.
   The dashboard rides the same override for the same reason — it is a
   conversation with a 300px notifications rail beside it, and at the shared
   1120px measure the rail takes a third of the line. Widening the PAGE does not
   widen the chat: .dash-flow stays capped at 820px and centres in the track it
   is given, which is the whole point of spending the extra width on the rail
   rather than on the text. */
@media (min-width: 1180px) {
  body.view-products .portal-main,
  body.view-dashboard .portal-main {
    max-width: 1400px;
  }
  body.view-products .pc-grid {
    grid-template-columns: repeat(6, minmax(0, 1fr));
  }
}
.pc-card {
  position: relative;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: #fff;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
/* Same selected treatment as the row it replaces, so a supplier who knew the
   old list recognises it: gold edge plus a warm fill, never colour alone. */
.pc-card.is-selected {
  outline: 2px solid var(--gold-deep);
  outline-offset: -1px;
  background: #fffdf4;
}
.pc-thumb {
  width: 100%;
  aspect-ratio: 4 / 3;
  background: var(--fog) center / cover no-repeat;
  border: 0;
  border-bottom: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-soft);
  padding: 0;
  position: relative;
}
.pc-thumb.has-img {
  cursor: zoom-in;
}
.pc-thumb svg {
  width: 22px;
  height: 22px;
}
/* No photo: a strip, not a 4:3 hole — the same lesson as .c-cover--none. */
.pc-thumb--none {
  aspect-ratio: auto;
  min-height: 34px;
}
.pc-thumb--none svg {
  width: 16px;
  height: 16px;
  opacity: 0.55;
}
/* The checkbox rides on the thumbnail because the card has no spare column for
   it, and it has to stay reachable when the thumbnail is a zoom button. */
.pc-sel {
  position: absolute;
  top: 6px;
  inset-inline-start: 6px;
  z-index: 2;
  width: 17px;
  height: 17px;
  cursor: pointer;
}
/* Review status, on the cover, opposite the checkbox. inset-inline-end, not
   right: this portal runs in Arabic, where the checkbox at inset-inline-start is
   on the right and this has to move to the left with it.

   A sibling of the thumbnail rather than a child, because the thumbnail is a
   <button> when there is a photo and a bare 34px <span> strip when there is not —
   .pc-card is already position: relative, so one placement works on both, and
   nothing labelled ends up nested inside the zoom button.

   The pastel badge fills were chosen to sit on a white card, not on an arbitrary
   supplier JPEG, so this adds its own separation: a white ring plus a soft shadow.
   Without them a beige badge on a beige product photo is invisible. */
.pc-status {
  position: absolute;
  top: 6px;
  inset-inline-end: 6px;
  z-index: 2;
  box-shadow: 0 0 0 1.5px #fff, 0 1px 3px rgba(14, 36, 51, 0.32);
}
/* The image-count badge shares this thumbnail and was only ever styled under
   `.p-thumb`, so inside a card it rendered as an unstyled number floating in the
   middle of the photo. Given the same treatment as its .p-thumb twin, which also
   puts it at the BLOCK-END corner — diagonally opposite the status chip, so the
   two overlays on this thumbnail cannot meet at any card width. */
.pc-thumb .p-imgn {
  position: absolute;
  bottom: 0;
  inset-inline-end: 0;
  background: rgba(14, 36, 51, 0.72);
  color: #fff;
  font-size: 0.6rem;
  font-weight: 700;
  line-height: 1;
  padding: 2px 4px;
  border-start-start-radius: 4px;
}
.pc-body {
  padding: 8px 9px 9px;
  display: flex;
  flex-direction: column;
  gap: 5px;
  flex: 1;
}
.pc-name {
  margin: 0;
  font-size: 0.82rem;
  font-weight: 700;
  line-height: 1.28;
  color: var(--ink);
  /* Two lines, then ellipsis. The full name is in the title attribute. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* One line, truncated rather than wrapped, so a product with six attributes does
   not make its whole row of cards tall. Both product cards opt out of the single
   line via .pc-attrs below, which is where the attribute values live; what is left
   on this base rule is .pc-cell — the price and the group name, one value each. */
.pc-meta,
.pc-cell {
  margin: 0;
  font-size: 0.72rem;
  color: var(--ink-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* An attribute the supplier CHOSE and has not filled in: dimmed, so a row of them
   never reads as data. Without this the value was simply dropped, and a supplier who
   ticked a field their products leave empty saw no change at all and reported the
   save as broken. It is a modifier on .pc-attr now, so what is dimmed is the glyph
   and the em dash together — WHICH attribute is missing is said by the glyph, and by
   the chip's accessible name, where it used to be said by a printed word. */
.pc-meta-none {
  color: var(--ink-faint, var(--ink-soft));
  opacity: 0.75;
}

/* ---- attributes as icon + value (all three product-card surfaces) ----
   Each value is preceded by a glyph naming the attribute it is, so the reader is
   not decoding position — see UI.attrChip in js/portal-ui.js for why the word it
   replaces survives in the accessible name.

   THREE LINES, not one, and measured at a 142px track where this line is 122px
   wide. A chip is exactly 14px wider than the bare value it replaces: an 11px glyph
   plus its 3px gap, with the 8px trailing margin paying for the " · " it drops.
   Four English values dot-joined ran to 46px in a 30px two-line box, so the
   supplier's fourth pick was not on the card; four chips are 52px and all four are.
   Two or three chips still settle at 34.5px, which is the common case and 4px more
   than the words cost. Clamped rather than left to grow, so no product can make its
   card tower over its neighbours, and .pc-grid's `align-items: start` means a
   three-line card pays its own height rather than its row's.

   This REPLACED .pc-meta--multi, the supplier card's two-line dot-joined rule,
   rather than sitting beside it: at two lines the fourth chip is cut, and four is
   what the supplier's cap allows. One rule for all three surfaces is also the point
   — they draw the same attributes from the same glyph table, and two clamp values
   would be two answers to one question. */
.pc-attrs {
  white-space: normal;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  text-overflow: ellipsis;
  line-height: 1.5;
}
/* One chip does not break across lines: a glyph on the end of one line with its
   value at the start of the next is a glyph labelling the wrong thing. Long
   values ellipsize inside the chip instead. */
.pc-attr {
  display: inline-block;
  max-inline-size: 100%;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  vertical-align: bottom;
  margin-inline-end: 8px;
}
/* Quieter than the value it marks — the glyph is the label, and a label should
   not outweigh its data. Sized in absolute units rather than em so it stays
   optically matched to the 0.72rem text if the card's font size is ever tuned. */
.pc-attr > svg {
  inline-size: 11px;
  block-size: 11px;
  vertical-align: -1px;
  margin-inline-end: 3px;
  opacity: 0.6;
}
/* Isolated, not direction-set: a supplier's value may be Arabic in an English
   console, and it must not reorder itself against the glyph beside it. */
.pc-attr-v { unicode-bidi: isolate; }
.pc-cell.strong {
  color: var(--ink);
  font-weight: 700;
}
.pc-foot {
  margin-top: auto;
  padding-top: 5px;
  display: flex;
  align-items: center;
  gap: 5px;
  flex-wrap: wrap;
}
.pc-foot .badge {
  font-size: 0.66rem;
  padding: 1px 6px;
}
/* Actions wrap on purpose: admin products carry five of them, which cannot fit
   one line inside a 142px card, and wrapping is better than shrinking them
   below a comfortable touch target. */
.pc-acts {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  padding-top: 2px;
}
.pc-acts .icon-btn {
  width: 26px;
  height: 26px;
  border-radius: 6px;
}
.pc-acts .icon-btn svg {
  width: 14px;
  height: 14px;
}
.pc-status-word {
  margin-inline-start: auto;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  color: var(--ink-soft);
  align-self: center;
  white-space: nowrap;
}
/* Colour as colour, not as a word. A supplier scanning their own inventory
   reads "navy" faster off a navy dot than off four letters, and the dots cost a
   fraction of the width at five cards a row. The names are not thrown away —
   they move to the row's aria-label and to each dot's title, so the information
   leaves the visual layer without leaving the accessibility tree. */
.pc-colors {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  align-items: center;
}
.pc-colors .pf-sw {
  width: 12px;
  height: 12px;
}

/* ---- the details POPUP, and the icon that opens it ----
   The supplier's product cards and the buyer's catalog cards used to fold their
   detail open in place with a native <details>. They now open one shared modal
   (DoujinPortalUI.detailPanel), which is a child of <body> and therefore survives
   the wholesale card repaints — replaceRow — that were the original argument FOR
   <details>. What <details> also arrived with, and what portal-ui.js now builds by
   hand, is the keyboard contract: focus in, focus back out to the trigger, Escape,
   and a focus trap.

   THE ADMIN CONSOLE IS NOT ONE OF THESE SURFACES. It briefly was; it now has one
   details affordance only, the eye that opens admin.js's own read-only detail
   modal, which is a superset of what this panel showed a reviewer. Two controls
   onto the same information is a reader deciding which one is the real one.

   The TRIGGER is an ordinary .icon-btn in the card's existing action row rather
   than a new control class. It is icon-only — the words "All details" are gone at
   the user's request — so it depends entirely on the aria-label every caller
   passes, which is why the trigger reuses iconBtn() instead of being hand-written
   per surface: that helper cannot emit a button without a name.

   The chevron glyph that lived here is gone with the <summary> it decorated. Its
   lesson is not: it was built from PHYSICAL border-right/border-bottom because
   `border-inline-end` plus rotate(45deg) mirrors into a sideways chevron in Arabic.
   SVG.chev in portal-ui.js is the surviving instance of the same reasoning. */
.dj-panel {
  /* Above .modal-back's own stacking so a details panel opened from a card sits
     over the portal, and below .dj-lb so the lightbox opens over the panel. */
  z-index: 60;
}
.dj-panel-box {
  max-width: min(94vw, 680px);
  width: 100%;
  max-height: 86vh;
  display: flex;
  flex-direction: column;
  padding: 0;
  /* .modal scrolls itself; here the BODY scrolls and the heading with the close
     button stays put, so Escape's visible equivalent never scrolls out of reach. */
  overflow: hidden;
}
.dj-panel-head {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 18px 20px 12px;
  border-bottom: 1px solid var(--line);
}
.dj-panel-head h2 {
  margin: 0;
  flex: 1;
  min-width: 0;
  font-size: 1.05rem;
  overflow-wrap: anywhere;
}
.dj-panel-x {
  flex: none;
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: #fff;
  color: var(--ink-soft);
  cursor: pointer;
}
.dj-panel-x:hover { background: var(--fog); color: var(--ink); }
.dj-panel-x:focus-visible { outline: 2px solid var(--gold-deep); outline-offset: 1px; }
.dj-panel-x svg { width: 14px; height: 14px; }
/* The panel scrolls, not the page: a product with twenty custom spec rows is the
   normal case this replaced a fold to handle. */
.dj-panel-body {
  padding: 14px 20px 20px;
  overflow: auto;
  font-size: 0.85rem;
}
.pc-more-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
/* The photo strip was sized for a 142px card, where 40px was all there was. In a
   680px panel the same 40px squares are a worse view of a photo than the card's
   own cover, which defeats the reason a reviewer opens this. */
.dj-panel-body .pc-shot {
  width: 74px;
  height: 74px;
}
.dj-panel-body .pc-kv dt { margin-top: 4px; }
/* Bare key/value pairs, one per line: at this width a two-column table would
   give the value four characters. */
.pc-kv {
  margin: 0;
  line-height: 1.4;
}
.pc-kv dt {
  color: var(--ink-soft);
  font-weight: 600;
}
.pc-kv dd {
  margin: 0 0 4px;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.pc-desc {
  margin: 0;
  color: var(--ink-soft);
  line-height: 1.45;
  overflow-wrap: anywhere;
}
/* Every photo on the record, small. The cover only ever showed the first one,
   and a reviewer judging a listing needs to see the set. */
.pc-shots {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.pc-shot {
  width: 40px;
  height: 40px;
  border: 1px solid var(--line);
  border-radius: 5px;
  background: var(--fog) center / cover no-repeat;
  padding: 0;
  cursor: zoom-in;
}

/* ---- "which attributes does a card show" dialog ----
   The scope sentence is not a subtitle to be skimmed past: this dialog writes
   either ONE group or the account-wide default, and getting that wrong is the way
   the feature hurts someone. So it is set at body weight, above the choices, and
   the inherit note under it carries the accent border that the rest of the portal
   uses for "read this". */
.cf-scope {
  margin: 0 0 6px;
  font-size: 0.86rem;
  line-height: 1.45;
  color: var(--ink);
}
.cf-inherit {
  margin: 0 0 10px;
  font-size: 0.8rem;
  line-height: 1.4;
  color: var(--ink-soft);
  border-inline-start: 3px solid var(--gold-deep);
  padding-inline-start: 8px;
}
.cf-hint { margin: 0 0 8px; font-size: 0.8rem; color: var(--ink-soft); }
/* Two columns at any sane dialog width; nine options in one column would push the
   buttons below the fold on a laptop. */
.cf-opts {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 4px 10px;
  margin-bottom: 10px;
}
.cf-opt {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 6px 8px;
  border: 1px solid var(--line);
  border-radius: 6px;
  font-size: 0.86rem;
  cursor: pointer;
}
.cf-opt.is-on { border-color: var(--gold-deep); background: #fffdf4; font-weight: 600; }
/* A slot that is full is not a slot you can add to. The row says so rather than
   just refusing on save — but the cap is still enforced server-side. */
.cf-opt:has(input:disabled) { opacity: 0.45; cursor: not-allowed; }
.cf-opt input { cursor: inherit; }
.cf-none {
  margin: 0 0 4px;
  font-size: 0.8rem;
  color: var(--ink-soft);
}

/* Requested-changes strip, full width inside the card. */
.pc-flag {
  margin: 0;
  font-size: 0.68rem;
  line-height: 1.35;
  background: #fdecef;
  border-top: 1px solid #f4c7d0;
  color: var(--red-deep);
  padding: 5px 9px;
}
.pc-flag .tag {
  background: #fff;
  border: 1px solid #f4c7d0;
  font-size: 0.62rem;
  padding: 1px 5px;
  margin-inline-end: 3px;
}

/* Two across on a phone: one card per line wastes the screen, and the card is
   already sized to survive being narrow. */
@media (max-width: 560px) {
  .pc-grid {
    grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
    gap: 8px;
  }
}

/* On a phone a wrapped chip row is nine chips tall and pushes every product off
   the screen — the cost the .p-facet-btn comment warned about, arriving by a
   different door. One swipeable line instead. The row still holds a bounded
   vocabulary, so nothing here scrolls forever. */
@media (max-width: 760px) {
  .f-chips,
  .pi-filters {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: thin;
    /* Room for the scrollbar, and a hint that the row continues past the edge. */
    padding-bottom: 4px;
    -webkit-overflow-scrolling: touch;
  }
  .f-chips > *,
  .pi-filters > * {
    flex: 0 0 auto;
  }
  .f-chip {
    padding: 0.42rem 0.8rem;
    font-size: 0.8rem;
  }
}
.c-group-t {
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
}
.c-group-n {
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--ink-soft);
  background: var(--fog);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 1px 7px;
}

/* inline permission panel under an admin row (Accounts page) */
.p-perms {
  grid-column: 1 / -1;
  margin: 4px 0 2px;
  padding: 10px 12px;
  background: var(--fog);
  border: 1px solid var(--line);
  border-radius: 7px;
}
.p-perms-h {
  margin: 0 0 7px;
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
}
.p-perms-note { margin: 0; font-size: 0.8rem; color: var(--ink); }
.p-perms-note.muted { color: var(--ink-soft); }
.p-perms-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.p-perm {
  font-size: 0.76rem;
  padding: 3px 9px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: #fff;
  color: var(--ink);
}
/* a grant that predates the key becoming super-admin-only: still stored,
   confers nothing. Struck through AND labelled, never colour alone. */
.p-perm.is-stale {
  border-style: dashed;
  color: var(--ink-soft);
  text-decoration: line-through;
  background: transparent;
}
.p-perm-x {
  /* inline-block, not text-decoration:none — a strike propagates into inline
     children and cannot be cancelled there, but it does not cross into an
     atomic inline box. Only the permission name should read as struck. */
  display: inline-block;
  margin-inline-start: 6px;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}
.p-perms .btn-row { margin-top: 9px; }
/* the panel sits on --fog, where a transparent ghost button loses its edge */
.p-perms .btn-ghost { background: #fff; }
.p-perms .spinner { padding: 0; }
/* the chevron points down when collapsed, up when the panel is open */
.p-exp > svg { transition: transform 0.16s ease; }
.p-exp[aria-expanded="true"] > svg { transform: rotate(180deg); }

/* toolbar above the list */
.p-toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: 0 0 10px;
}
.p-toolbar .p-search {
  flex: 1 1 190px;
  min-width: 150px;
  padding: 7px 11px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: 0.85rem;
  background: #fff;
  color: var(--ink);
}
.p-count-note { font-size: 0.78rem; color: var(--ink-soft); margin-inline-start: auto; white-space: nowrap; }
.filter-bar { display: flex; gap: 6px; flex-wrap: wrap; margin: 0 0 12px; }

/* ---- vocabulary filters in the toolbar (buyer catalog) ----
   MULTI-SELECT: each filter is a button that opens a panel of checkboxes, so
   several values can be chosen per filter (OR) and the filters combine (AND).
   The button carries the field name and a count, which keeps the filter row one
   line tall however many values are ticked — a row of inline chips would have
   grown without limit across three vocabularies of a dozen values each.
   Everything here is a native <button> or <input type=checkbox>: keyboard and
   touch work without custom key handling, and nothing depends on hover. */
.p-filters { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.p-filter {
  padding: 6px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: 0.82rem;
  background: #fff;
  color: var(--ink-soft);
  max-width: 17ch;
  text-overflow: ellipsis;
}
.p-filter:hover { border-color: var(--gold); }
.p-filter.is-on { border-color: var(--gold-deep); background: #fffdf5; color: var(--ink); font-weight: 600; }
.p-filter:focus-visible { outline: 2px solid var(--gold-deep); outline-offset: 1px; }
.p-filter-color { display: inline-flex; align-items: center; gap: 5px; }
/* Blockifies the swatch inside: .pf-sw only gets its box from a flex parent. */
.p-filter-sw { display: inline-flex; align-items: center; }
.p-filter-sw:empty { display: none; }
/* Only rendered while something is filtered, so it can afford to be loud. */
.p-toolbar .icon-btn { width: 29px; height: 29px; border-radius: 999px; flex: none; }
.p-toolbar [data-cclearf] { border-color: var(--gold-deep); color: var(--gold-deep); }
.p-toolbar [data-cclearf]:hover { background: #fffdf5; color: var(--gold-deep); }

/* ---- multi-select facet: the button and its checkbox panel ---- */
.p-facet { position: relative; display: inline-flex; }
.p-facet-btn { display: inline-flex; align-items: center; gap: 5px; cursor: pointer; max-width: 22ch; }
.p-facet-btn > svg { width: 11px; height: 11px; flex: none; opacity: 0.6; transition: transform 0.15s ease; }
.p-facet-btn[aria-expanded="true"] > svg { transform: rotate(180deg); }
.p-facet-btn[aria-expanded="true"] { border-color: var(--gold-deep); background: #fffdf5; }
/* The live count of ticked values: the panel can hold six choices and the
   toolbar must still say so at a glance while closed. */
.p-facet-n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--gold-deep);
  color: #fff;
  font-size: 0.66rem;
  font-weight: 700;
  line-height: 1;
}
.p-facet-n[hidden] { display: none; }
.p-facet-pop {
  position: absolute;
  z-index: 40;
  top: calc(100% + 5px);
  inset-inline-start: 0;
  display: flex;
  flex-direction: column;
  min-width: 190px;
  max-width: 260px;
  padding: 8px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 10px 26px rgba(24, 22, 20, 0.14);
}
/* .p-facet-pop declares display:flex, which outranks the UA's
   `[hidden] { display: none }` because author styles beat UA styles. Without
   this the panel would never close. */
.p-facet-pop[hidden] { display: none; }
.p-facet-hint {
  font-size: 0.64rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  padding: 0 4px 6px;
}
.p-facet-list { display: flex; flex-direction: column; max-height: 248px; overflow-y: auto; }
.p-facet-o {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 5px 6px;
  border-radius: 6px;
  font-size: 0.84rem;
  color: var(--ink);
  cursor: pointer;
}
.p-facet-o:hover { background: var(--fog); }
.p-facet-o.is-on { background: #fffdf5; font-weight: 600; }
.p-facet-o input { flex: none; margin: 0; accent-color: var(--gold-deep); }
.p-facet-o > span { min-width: 0; overflow-wrap: anywhere; }
.p-facet-o:focus-within { outline: 2px solid var(--gold-deep); outline-offset: -1px; }
.p-facet-foot { display: flex; justify-content: flex-end; border-top: 1px solid var(--line); margin-top: 6px; padding-top: 6px; }

/* ---- active filter chips ----
   Each ticked value gets a chip that removes only itself. The toolbar buttons
   show counts; this row shows WHICH values, and is the only place a single
   choice can be undone without reopening the panel it came from. */
.p-active { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin: 0 0 10px; }
.p-active[hidden] { display: none; }
.p-active-l {
  font-size: 0.66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
}
.p-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 9px;
  border: 1px solid #e8dcc2;
  border-radius: 999px;
  background: var(--cream);
  font-family: var(--font-body);
  font-size: 0.78rem;
  color: var(--ink);
  cursor: pointer;
}
.p-chip > svg { width: 10px; height: 10px; flex: none; opacity: 0.55; }
.p-chip:hover { border-color: var(--red); color: var(--red-deep); }
.p-chip:hover > svg { opacity: 1; }
.p-chip:focus-visible { outline: 2px solid var(--gold-deep); outline-offset: 1px; }
/* The AND across filters, said once. Hidden below the toolbar's own breakpoint
   rather than shortened: with one filter active there is no composition to
   explain, and the renderer only emits it past that point. */
.p-active-hint { font-size: 0.7rem; color: var(--ink-soft); margin-inline-start: 4px; }
@media (max-width: 560px) {
  .p-active-hint { display: none; }
  .p-facet-pop { max-width: none; min-width: 168px; }
}

/* ---- category group heading inside a compact list ---- */
.p-group {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 7px 12px;
  padding-inline-start: 9px;
  background: #fffdf5;
  border-bottom: 1px solid var(--line);
  border-inline-start: 3px solid var(--gold);
}
.p-group-t {
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--gold-deep);
}
.p-group-n {
  font-size: 0.68rem;
  font-weight: 700;
  color: var(--ink-soft);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 1px 7px;
  line-height: 1.5;
}

/* ---- colour strip in a catalog row ---- */
.p-dots { display: flex; align-items: center; gap: 4px; flex-wrap: nowrap; }
.p-dots .pf-sw { width: 12px; height: 12px; }
.p-dots-n { font-size: 0.68rem; color: var(--ink-soft); font-weight: 600; }

/* empty state that offers its own way out */
.p-noresult { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }

/* sticky batch bar */
/* top must equal .portal-header's height (padding twice plus .portal-logo). */
.batch-actions.is-sticky { position: sticky; top: 76px; z-index: 4; }
@media (min-width: 720px) {
  .batch-actions.is-sticky { top: 84px; }
}
@media (max-width: 640px) {
  .batch-actions.is-sticky { top: 68px; }
}

/* On narrow screens the column grid becomes a wrapping card: identity on the
   first line, meta on the second, actions on their own. Flex rather than a
   collapsed grid, because the supplier/admin/catalog lists each have a
   different column count and cells are positional, not classed. */
@media (max-width: 760px) {
  .p-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
  .p-row.p-head { display: none; }
  .p-row > * { flex: 0 0 auto; }
  .p-row > .p-main { flex: 1 1 55%; }
  .p-row > .p-cell { font-size: 0.76rem; max-width: 100%; }
  .p-row > .p-cell:empty { display: none; }
  .p-row > .p-acts { flex: 1 0 100%; justify-content: flex-end; }
  .p-row > .p-perms { flex: 1 0 100%; margin-top: 0; }
}

/* ============================================================
   Admin console list controls (orders, accounts)
   Reuses the compact .p-list grid; only the extras below are new.

   NAMING: the .o-* prefix is historical — these primitives were written for the
   orders queue and are now the shared vocabulary for every server-filtered,
   server-paged admin list (the Accounts page uses .o-filters / .o-filter /
   .o-pager verbatim). They are deliberately NOT duplicated under a second
   prefix: one rule, one appearance. The .o-status.o-st--* block further down IS
   orders-specific.
   ============================================================ */

/* Server-side filter row. Sits under the search toolbar and wraps freely, so a
   narrow screen stacks it instead of scrolling sideways. */
.o-filters { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin: 0 0 12px; }
.o-filter {
  padding: 5px 8px;
  font-family: var(--font-body);
  font-size: 0.78rem;
  color: var(--ink);
  background: #fff;
  border: 1px solid var(--fog);
  border-radius: 8px;
  max-width: 210px;
}
.o-filter:focus { outline: 2px solid var(--gold); outline-offset: 1px; }

/* Pager: two icon steps around a plain "Page n of m", matching the icon-led
   language of the row actions rather than adding big text buttons. */
.o-pager { display: flex; align-items: center; justify-content: center; gap: 10px; margin: 14px 0 2px; }
.o-pager-at { font-size: 0.78rem; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.o-pager .icon-btn[disabled] { opacity: 0.35; cursor: not-allowed; }

/* Inline status change inside a row: sized to sit next to a badge, not to
   dominate the row the way a full-width control would. */
.o-status {
  width: 100%;
  max-width: 128px;
  padding: 4px 6px;
  font-family: var(--font-body);
  font-size: 0.76rem;
  color: var(--ink);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 6px;
}
.o-status:focus-visible { outline: 2px solid var(--gold-deep); outline-offset: 1px; }

/* Per-status colour on the inline status control, matched pair-for-pair to the
   .badge-* colours the read-only viewer sees for the same status. Kept as a
   tint + matching border rather than a solid fill so the option text stays
   readable; the status NAME is the selected option, so colour is an aid, never
   the only signal. `submitted` is the exception that gets the solid ink of
   .badge-new, because an untouched RFQ should pull the eye. */
.o-status.o-st--submitted { background: var(--ink);  color: var(--paper);   border-color: var(--ink); font-weight: 600; }
.o-status.o-st--in_review { background: #fff7e6;     color: var(--gold-deep); border-color: #f0d79a; }
.o-status.o-st--quoted    { background: #eef4fb;     color: #1d4e89;        border-color: #c7dcf0; }
.o-status.o-st--closed    { background: #eafaf0;     color: #166534;        border-color: #b7e4c7; }
.o-status.o-st--rejected  { background: #fdecef;     color: var(--red-deep); border-color: #f3c6cf; }
.o-status.o-st--canceled  { background: #eef1f4;     color: #64748b;        border-color: #d7dee6; }

/* Which tier am I looking at? Blue = the buyer sees it, gold = internal only.
   .pf-sec--private (gold) already carries "admin only" in the product form, so
   the internal block reuses it verbatim and only the buyer-facing block is new. */
.pf-sec--buyer {
  background: #f5f9fd;
  border: 1px solid #c7dcf0;
  border-radius: 9px;
  padding: 12px 14px 14px;
  margin-top: 12px;
}
.pf-sec--buyer > .pf-sec-t { color: #1d4e89; }

/* The one-line reminder under each tier heading. */
.o-tier-note { font-size: 0.74rem; line-height: 1.4; color: var(--ink-soft); margin: -3px 0 10px; }

/* A checkbox that sits inside a .field next to its own caption label. Needs to
   out-specify `.pform .field label` (block, small, bold) and `.field input`
   (full width), hence the deliberately long selectors. */
.pform .field label.o-check {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 0.84rem;
  font-weight: 400;
  color: var(--ink);
  margin: 0;
  cursor: pointer;
}
.pform .field label.o-check input { width: 16px; height: 16px; margin: 0; flex: none; }
.pform .field label.o-check input[disabled] { cursor: not-allowed; }

/* Header strip of the detail modal: status, release state, order number. */
.o-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0 0 12px; }
.o-head .o-when { font-size: 0.76rem; color: var(--ink-soft); margin-inline-start: auto; }

/* Long-form notes rendered as Markdown in a read-only context. */
.o-md {
  font-size: 0.84rem;
  line-height: 1.5;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 7px;
  padding: 8px 11px;
}
.o-md > :first-child { margin-top: 0; }
.o-md > :last-child { margin-bottom: 0; }

/* Money reads as money: aligned digits, always LTR even in an RTL portal. */
.o-money { font-variant-numeric: tabular-nums; unicode-bidi: isolate; direction: ltr; }
.o-items input[type="number"] { width: 100%; max-width: 120px; text-align: end; }

/* ============================================================
   Buyer portal: orders list and cart
   Both were plain .data-table before. They now use the same compact
   .p-list / .p-row grid as the catalog and the supplier product list,
   so the three buyer surfaces read as one system.
   ============================================================ */

/* Order rows are taller than product rows because a row carries its whole line
   list, so the cells align to the top rather than the middle. */
.o-list .p-row.o-row { align-items: start; padding-top: 10px; padding-bottom: 10px; }
.o-num { font-variant-numeric: tabular-nums; unicode-bidi: isolate; }
.o-lines { display: flex; flex-direction: column; gap: 2px; }
.o-line { font-size: 0.82rem; color: var(--ink); line-height: 1.4; }
.o-line > b { font-variant-numeric: tabular-nums; color: var(--ink-soft); font-weight: 700; }
.o-line > em { font-style: normal; color: var(--ink-soft); }
button.o-name-link {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-align: start;
  text-decoration: none;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}
button.o-name-link:hover {
  color: var(--gold-deep, #8a6a12);
  text-decoration: underline;
}
.o-price-was {
  text-decoration: line-through;
  color: var(--ink-soft);
  font-weight: 400;
  margin-inline-end: 0.45em;
}

/* Buyer portal: the released quote, hung under its order row. Blue-tinted and
   inset, matching .pf-sec--buyer: the tint means "this is the tier you can see",
   the same thing it means to staff in the admin console. */
.o-quote-cell {
  background: #f5f9fd;
  border-bottom: 1px solid var(--line);
  border-inline-start: 3px solid #c7dcf0;
  padding: 10px 14px 12px;
}
.o-list > .o-quote-cell:last-child { border-bottom: 0; }
.o-quote-t { font-size: 0.72rem; color: var(--ink-soft); margin: 0 0 7px; letter-spacing: 0.03em; }
.o-quote-t strong { color: #1d4e89; text-transform: uppercase; font-size: 0.68rem; letter-spacing: 0.05em; }
.o-quote-exp { color: var(--red-deep); font-weight: 600; }
.o-quote-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px 24px;
  margin: 0;
  font-size: 0.84rem;
}
/* Caption above value, both left-aligned: a spread-apart label/value pair
   reads as two unrelated columns once the grid gets wide. */
.o-quote-grid > div { min-width: 0; }
.o-quote-grid dt {
  margin: 0;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.o-quote-grid dd { margin: 2px 0 0; font-weight: 600; overflow-wrap: anywhere; }

.o-quote-lines { margin: 0 0 12px; font-size: 0.84rem; }
.o-quote-lines th { font-size: 0.68rem; }
.o-quote-lines tfoot th { text-align: end; font-weight: 600; }
.o-quote-lines tfoot td { font-weight: 700; }
.o-quote-acts {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}
.o-quote-reject { color: var(--red-deep); }
.o-quote-decision {
  margin: 10px 0 0;
  font-size: 0.82rem;
  font-weight: 600;
  color: #1d4e89;
}
.o-arch-t {
  font-family: var(--font-display);
  font-size: 1.05rem;
  margin: 22px 0 10px;
  color: var(--ink-soft);
}
.o-replies { margin-top: 14px; padding-top: 10px; border-top: 1px dashed #c7dcf0; }
.o-reply { margin: 0 0 10px; font-size: 0.84rem; }
.o-reply-t { margin: 0 0 4px; color: var(--ink-soft); font-size: 0.78rem; }
.o-reply ul { margin: 4px 0 0; padding-inline-start: 1.1em; }

/* ---- cart ----
   The list on one side, a summary carrying the note and the single primary
   action on the other, so "what am I asking for" and "send it" are never the
   same glance. */
.k-cols { display: grid; grid-template-columns: minmax(0, 1fr) 268px; gap: 18px; align-items: start; }
.k-sum {
  background: var(--cream);
  border: 1px solid #e8dcc2;
  border-radius: 10px;
  padding: 14px 16px 16px;
}
.k-sum-t { font-family: var(--font-display); font-size: 1rem; margin: 0 0 10px; }
.k-sum-g {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px 14px;
  margin: 0 0 14px;
}
.k-sum-g dt {
  font-size: 0.64rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  margin: 0;
}
.k-sum-g dd {
  font-family: var(--font-display);
  font-size: 1.16rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  margin: 1px 0 0;
}
.k-note { margin-bottom: 12px; }
.k-note label { font-size: 0.72rem; }
.k-note textarea { min-height: 76px; padding: 8px 10px; font-size: 0.88rem; border-radius: 7px; }
/* Says plainly that sending this costs nothing — the primary button reads as a
   checkout and this is a request for a quote, not a purchase. */
.k-sum-hint { font-size: 0.68rem; color: var(--ink-soft); line-height: 1.45; margin: 9px 0 0; }
.k-sum .notice { margin: 0 0 12px; }

/* Quantity stepper: −, the figure, +. A step commits on the spot, which is why
   there is no separate save control in the row. */
.k-qty {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--line);
  border-radius: 7px;
  overflow: hidden;
  background: #fff;
}
.k-step {
  width: 28px;
  border: 0;
  background: var(--fog);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 0.95rem;
  line-height: 1;
  cursor: pointer;
  /* 28px is below the 44px touch target, so the row's own height carries it:
     the button stretches to the full stepper height. */
  padding: 0;
}
.k-step:hover { background: #f0e6cf; color: var(--gold-deep); }
.k-step:focus-visible { outline: 2px solid var(--gold-deep); outline-offset: -2px; }
.k-qty input {
  width: 56px;
  border: 0;
  border-inline: 1px solid var(--line);
  padding: 6px 4px;
  text-align: center;
  font-family: var(--font-body);
  font-size: 0.88rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  background: #fff;
  /* The native spinners duplicate the − / + buttons either side of them. */
  appearance: textfield;
  -moz-appearance: textfield;
}
.k-qty input::-webkit-outer-spin-button,
.k-qty input::-webkit-inner-spin-button { appearance: none; margin: 0; }
.k-qty input:focus-visible { outline: 2px solid var(--gold-deep); outline-offset: -2px; }

/* An empty cart is a dead end unless it points somewhere. */
.k-empty { text-align: center; padding: 34px 20px 38px; color: var(--ink-soft); }
.k-empty > svg { width: 34px; height: 34px; opacity: 0.4; }
.k-empty > p { font-size: 0.92rem; margin: 10px 0 18px; }

@media (max-width: 860px) {
  .k-cols { grid-template-columns: minmax(0, 1fr); }
}
/* Below this the five-column order row and four-column cart row stop fitting, so
   both fall back to a stacked card: every cell keeps its own line and the header
   row, which has nothing left to head, is removed. */
@media (max-width: 640px) {
  .o-list .p-row.o-row,
  .k-list .p-row.k-row {
    grid-template-columns: minmax(0, 1fr);
    gap: 7px;
    padding: 11px 12px;
  }
  .o-list .p-row.p-head,
  .k-list .p-row.p-head { display: none; }
  .k-list .p-row.k-row .p-thumb { display: none; }
  .o-list .p-acts,
  .k-list .p-acts { justify-content: flex-start; }
  .o-quote-grid { grid-template-columns: minmax(0, 1fr); gap: 9px; }
}

/* ============================================================
   Product edit form (shared: supplier portal / admin console)
   Grouped sections + a dense two-column field grid, so the whole
   record fits in one glance instead of a long stack of inputs.
   ============================================================ */
.modal-wide { max-width: 820px; padding: 22px 24px; }

.pf-head { display: flex; align-items: center; gap: 10px; margin: 0 0 3px; }
.pf-head h2 { margin: 0; }
.pf-lead { color: var(--ink-soft); font-size: 0.8rem; margin: 0 0 14px; }

.pf-sec {
  border-top: 1px solid var(--line);
  padding: 10px 0 11px;
}
.pf-sec:first-child { border-top: 0; padding-top: 0; }
.pf-sec-t {
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  margin: 0 0 8px;
}
/* Admin-only pricing block reads as a distinct, cordoned-off area. */
.pf-sec--private {
  background: #fffdf5;
  border: 1px solid #f0dca8;
  border-radius: 9px;
  padding: 12px 14px 14px;
  margin-top: 12px;
}
.pf-sec--private > .pf-sec-t { color: var(--gold-deep); }

.pf-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 16px; }
.pf-span2 { grid-column: 1 / -1; }
/* The grid gap owns the vertical rhythm; compact inputs keep the form short. */
.pform .field { margin-bottom: 0; }
.pform .field label { font-size: 0.72rem; margin-bottom: 4px; }
.pform .field input,
.pform .field select,
.pform .field textarea { padding: 8px 10px; font-size: 0.88rem; border-radius: 7px; }
/* .field .field-note is scoped to a field wrapper; the form also hangs notes
   directly off a section (under the Markdown editor, the colour chips…). */
.pform .field-note,
.pform > .pf-sec .field-note { font-size: 0.74rem; color: var(--ink-soft); margin: 5px 0 0; line-height: 1.35; }

/* free-text escape revealed by the "Other…" option of a vocabulary select */
.pf-other { margin-top: 6px; }
.pf-other[hidden] { display: none; }

/* L × W × H sharing one trailing unit select */
.pf-dims { display: flex; align-items: center; gap: 5px; }
.pf-dims input { min-width: 0; flex: 1 1 0; text-align: center; }
.pf-x { color: var(--ink-soft); font-size: 0.8rem; flex: none; }
.pf-inline { display: flex; align-items: center; gap: 5px; }
.pf-inline input { min-width: 0; flex: 1 1 0; }
/* Storage unit is fixed (mm / g): an affix, not a picker. */
.pf-unit-fixed {
  flex: none;
  min-width: 26px;
  padding-inline: 4px;
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
  text-align: center;
  direction: ltr;
}
/* Live echo of the same measurement in the viewer's own units. */
.pf-conv { color: var(--ink-soft); font-variant-numeric: tabular-nums; unicode-bidi: isolate; }

.pf-specs { min-height: 82px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.8rem; }

/* colour picker: checkbox chips carrying a swatch */
.pf-colors { display: flex; flex-wrap: wrap; gap: 6px; }
.pf-color {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px 4px 7px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: #fff;
  cursor: pointer;
  font-size: 0.78rem;
  color: var(--ink-soft);
  user-select: none;
}
.pf-color:hover { border-color: var(--gold); }
.pf-color.is-on { border-color: var(--gold-deep); background: #fffdf5; color: var(--ink); font-weight: 600; }
.pf-color input { position: absolute; opacity: 0; width: 0; height: 0; }
.pf-color:focus-within { outline: 2px solid var(--gold-deep); outline-offset: 1px; }
.pf-sw {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 1px solid rgba(14, 36, 51, 0.2);
  flex: none;
}
.pf-sw--clear { background: linear-gradient(135deg, #fff 45%, #d7dee4 45%, #d7dee4 55%, #fff 55%); }
.pf-sw--multi { background: conic-gradient(#c8102e, #f2c744, #3f8f5c, #2f77c4, #7a5aa6, #c8102e); }

/* image strip inside the form */
.pf-imgs { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 9px; }
.pf-imgs:empty { display: none; }
/* Tame the default file-input chrome: the native button is the only unstyled
   control left on the form, and it reads as a foreign element next to the
   rest of the field styling. */
.pf-file {
  font-size: 0.82rem;
  color: var(--ink-soft);
  max-width: 100%;
}
.pf-file::file-selector-button {
  font: inherit;
  font-weight: 600;
  color: var(--ink);
  background: #f7f8f9;
  border: 1px solid var(--line);
  border-radius: 7px;
  padding: 7px 12px;
  margin-inline-end: 10px;
  cursor: pointer;
}
.pf-file::file-selector-button:hover {
  background: #fff;
  border-color: var(--ink-soft);
}

/* ---- Markdown editor: write / preview + glyph tool buttons ---- */
.md-ed { border: 1px solid var(--line); border-radius: 8px; overflow: hidden; background: #fff; }
.md-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 6px;
  background: var(--fog);
  border-bottom: 1px solid var(--line);
}
.md-tools { display: flex; gap: 3px; }
.md-tool {
  width: 26px;
  height: 26px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 6px;
  background: none;
  cursor: pointer;
  color: var(--ink-soft);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.85rem;
  line-height: 1;
}
.md-tool:hover { background: #fff; border-color: var(--line); color: var(--ink); }
.md-tool[disabled] { opacity: 0.35; cursor: not-allowed; }
.md-tool[data-md-ins="bold"] { font-weight: 800; }
.md-tool[data-md-ins="italic"] { font-style: italic; }
.md-tabs { margin-inline-start: auto; display: flex; gap: 2px; }
.md-tab {
  border: 0;
  background: none;
  cursor: pointer;
  padding: 4px 10px;
  border-radius: 6px;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--ink-soft);
}
.md-tab:hover { color: var(--ink); }
.md-tab.is-on { background: #fff; color: var(--ink); box-shadow: 0 1px 2px rgba(14, 36, 51, 0.1); }
.pform .md-ta {
  width: 100%;
  box-sizing: border-box;
  border: 0;
  border-radius: 0;
  min-height: 130px;
  display: block;
  padding: 9px 11px;
  font-family: var(--font-body);
  font-size: 0.88rem;
  line-height: 1.5;
  color: var(--ink);
  resize: vertical;
}
.pform .md-ta:focus { box-shadow: none; }
.md-ta[hidden] { display: none; }
.md-prev { padding: 10px 12px; min-height: 130px; }
.md-prev[hidden] { display: none; }
.pf-empty { color: var(--ink-soft); font-size: 0.84rem; margin: 0; }

/* ---- rendered Markdown (descriptions, everywhere they display) ---- */
.md { font-size: 0.88rem; line-height: 1.55; color: var(--ink); overflow-wrap: break-word; }
.md > :first-child { margin-top: 0; }
.md > :last-child { margin-bottom: 0; }
.md p { margin: 0 0 9px; }
.md h3, .md h4, .md h5, .md h6 { font-family: var(--font-display); margin: 14px 0 6px; line-height: 1.3; }
.md h3 { font-size: 1.02rem; }
.md h4 { font-size: 0.94rem; }
.md h5, .md h6 { font-size: 0.88rem; }
.md ul, .md ol { margin: 0 0 9px; padding-inline-start: 20px; }
.md li { margin: 2px 0; }
.md blockquote {
  margin: 0 0 9px;
  padding: 2px 0 2px 11px;
  border-inline-start: 3px solid var(--line);
  color: var(--ink-soft);
}
.md code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.84em;
  background: var(--fog);
  border-radius: 4px;
  padding: 1px 4px;
}
.md pre {
  margin: 0 0 9px;
  padding: 9px 11px;
  background: var(--fog);
  border: 1px solid var(--line);
  border-radius: 7px;
  overflow-x: auto;
}
.md pre code { background: none; padding: 0; font-size: 0.8rem; }
.md hr { border: 0; border-top: 1px solid var(--line); margin: 12px 0; }
.md a { color: var(--red-deep); font-weight: 600; }
.md table { display: none; } /* the renderer emits no tables; belt and braces */

@media (max-width: 760px) {
  .modal-wide { padding: 18px 16px; }
  .pf-grid { grid-template-columns: minmax(0, 1fr); gap: 11px; }
  .pf-span2 { grid-column: auto; }
  .pf-sec--private { padding: 11px 12px 12px; }
}

/* ============================================================
   Toasts — per-record feedback without re-rendering the list
   ============================================================ */
.dj-toasts {
  position: fixed;
  z-index: 1200;
  bottom: 18px;
  inset-inline-start: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: center;
  pointer-events: none;
}
.dj-toast {
  pointer-events: auto;
  max-width: 92vw;
  padding: 9px 16px;
  border-radius: 999px;
  font-size: 0.85rem;
  font-weight: 600;
  color: #fff;
  background: var(--ink);
  box-shadow: 0 12px 30px -12px rgba(14, 36, 51, 0.6);
  animation: djToastIn 0.2s ease;
}
.dj-toast.ok { background: #166534; }
.dj-toast.err { background: var(--red-deep); }
.dj-toast.warn { background: var(--gold-deep); }
.dj-toast.out { opacity: 0; transform: translateY(6px); transition: opacity 0.2s, transform 0.2s; }
@keyframes djToastIn { from { opacity: 0; transform: translateY(8px); } }

/* ============================================================
   Lightbox — click a product image to enlarge / zoom / pan
   ============================================================ */
.dj-lb { position: fixed; inset: 0; z-index: 1100; }
.dj-lb[hidden] { display: none; }
.dj-lb-bg { position: absolute; inset: 0; background: rgba(9, 20, 28, 0.985); }
.dj-lb-shell {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  color: #eaf1f6;
  animation: djLbIn 0.18s ease;
}
@keyframes djLbIn { from { opacity: 0; } }
.dj-lb-bar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  background: rgba(9, 20, 28, 0.8);
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.dj-lb-title {
  font-size: 0.88rem;
  font-weight: 600;
  color: #fff;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dj-lb-count { font-size: 0.8rem; color: #9fb4c2; font-variant-numeric: tabular-nums; flex: none; }
.dj-lb-tools { margin-inline-start: auto; display: flex; align-items: center; gap: 6px; flex: none; }
.dj-lb-zoom {
  font-size: 0.76rem;
  color: #cdd9e2;
  font-variant-numeric: tabular-nums;
  min-width: 42px;
  text-align: center;
}
.dj-lb-btn {
  width: 32px;
  height: 32px;
  border-radius: 7px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(255, 255, 255, 0.06);
  color: #fff;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.dj-lb-btn svg { width: 16px; height: 16px; }
.dj-lb-btn:hover { background: rgba(255, 255, 255, 0.16); }
.dj-lb-btn.dj-lb-x:hover { background: var(--red); border-color: var(--red); }
.dj-lb-stage {
  flex: 1;
  min-height: 0;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  touch-action: none;
}
.dj-lb-img {
  max-width: 94%;
  max-height: 94%;
  object-fit: contain;
  display: block;
  transform-origin: center center;
  will-change: transform;
  user-select: none;
  -webkit-user-drag: none;
}
/* Zoom scales the FITTED image, so the max-width/height caps must stay — the
   transform multiplies the laid-out size rather than the natural one. */
.dj-lb-img.zoomed { cursor: grab; }
.dj-lb-img.grabbing { cursor: grabbing; }
.dj-lb-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(9, 20, 28, 0.55);
  color: #fff;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
}
.dj-lb-nav svg { width: 20px; height: 20px; }
/* Same inline-flex-vs-[hidden] trap as .icon-btn: showLb() hides both arrows
   on a single-image lightbox, and without this they stayed on screen. */
.dj-lb-nav[hidden] { display: none; }
.dj-lb-nav:hover { background: var(--red); border-color: var(--red); }
.dj-lb-prev { inset-inline-start: 14px; }
.dj-lb-next { inset-inline-end: 14px; }
[dir="rtl"] .dj-lb-nav svg { transform: scaleX(-1); }
.dj-lb-thumbs {
  flex: none;
  display: flex;
  gap: 7px;
  justify-content: center;
  flex-wrap: wrap;
  padding: 10px 14px;
  max-height: 22vh;
  overflow-y: auto;
}
.dj-lb-thumb {
  width: 52px;
  height: 52px;
  border-radius: 6px;
  border: 2px solid transparent;
  background: #12293b center/cover no-repeat;
  cursor: pointer;
  padding: 0;
  opacity: 0.6;
}
.dj-lb-thumb:hover { opacity: 1; }
.dj-lb-thumb.active { border-color: var(--gold); opacity: 1; }

/* ============================================================
   AI assistant side panel
   ============================================================ */
.dj-ai-fab {
  position: fixed;
  z-index: 900;
  bottom: 20px;
  inset-inline-end: 20px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 11px 17px;
  border: 0;
  border-radius: 999px;
  background: var(--ink);
  color: #fff;
  font-family: var(--font-body);
  font-size: 0.86rem;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 14px 34px -14px rgba(14, 36, 51, 0.7);
  transition: transform 0.14s ease, background 0.15s ease;
}
.dj-ai-fab:hover { background: #16344a; transform: translateY(-1px); }
.dj-ai-fab svg { width: 17px; height: 17px; }
.dj-ai-fab .dj-ai-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--gold);
}
.dj-ai-fab[hidden] { display: none; }

.dj-ai {
  position: fixed;
  z-index: 950;
  top: 0;
  bottom: 0;
  inset-inline-end: 0;
  width: 400px;
  max-width: 100vw;
  background: #fff;
  border-inline-start: 1px solid var(--line);
  box-shadow: -18px 0 46px -28px rgba(14, 36, 51, 0.5);
  display: flex;
  flex-direction: column;
  animation: djAiIn 0.2s ease;
}
.dj-ai[hidden] { display: none; }
@keyframes djAiIn { from { transform: translateX(14px); opacity: 0; } }
[dir="rtl"] .dj-ai { animation: djAiInRtl 0.2s ease; }
@keyframes djAiInRtl { from { transform: translateX(-14px); opacity: 0; } }

/* ---- inline mode: the dashboard's pane -----------------------------------
   The same panel, the same markup, the same machinery — appended into a view's
   DOM instead of <body>. Everything the drawer has BECAUSE it floats over the
   page is undone: it sits in the flow, it carries no FAB, and it never sets
   body.dj-ai-open, so nothing is pushed aside.

   The slide-in goes too, both directions. The drawer animates because it
   arrives; the pane is simply part of the dashboard, and a panel that slid every
   time render() ran would read as a page reload.

   AND IT DRAWS NOTHING. The pane used to sit inside a .portal-card, so it
   dropped its own border and let the card frame it. There is no card now — the
   centre of this page is plain by request — so the panel is transparent, the
   header is gone (the display headline above it already says what this is, and
   the panel keeps its accessible name from role="dialog" + aria-label), and the
   only thing left that draws a frame is the composer, which needs one to read
   as a text field at all.

   THE HEIGHT IS THE LOG'S, NOT THE PANEL'S. The drawer is a full-height column,
   so the pane inherited a fixed height and let .dj-ai-log's `flex: 1` fill it.
   On a page whose composer is the first thing you are meant to reach, that put
   400px of empty log between the headline and the box — measured, and visible.
   So the panel takes its content's height and the LOG carries the bound
   instead: short under a greeting, growing with the conversation, and scrolling
   once it would push the composer off the screen. */
.dj-ai-inline {
  position: static;
  z-index: auto;
  width: auto;
  max-width: none;
  height: auto;
  border: 0;
  background: transparent;
  box-shadow: none;
  animation: none;
}
[dir="rtl"] .dj-ai-inline { animation: none; }
.dj-ai-inline .dj-ai-head { display: none; }
.dj-ai-inline .dj-ai-log {
  flex: 0 1 auto;
  max-height: 46vh;
  padding: 4px 2px 16px;
}
/* The reference's composer: one rounded box, the textarea seamless inside it,
   the footnote and the send button on the row beneath. The border moves from the
   textarea to the form so the whole thing is a single field. */
.dj-ai-inline .dj-ai-form {
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 12px 14px 10px;
  background: #fff;
}
.dj-ai-inline .dj-ai-form:focus-within { border-color: var(--gold); }
.dj-ai-inline .dj-ai-form textarea {
  min-height: 72px;
  border: 0;
  border-radius: 0;
  padding: 0;
  resize: none;
}
.dj-ai-inline .dj-ai-form textarea:focus { outline: none; }
/* Nothing to close: close() is a no-op inline, so the header's X would be a
   control that does nothing. display:none also takes it out of the tab order,
   which `hidden` alone would not do against .dj-ai-head's flex layout. */
.dj-ai-inline .dj-ai-x { display: none; }
/* And no selection bar when the host offers no selection — set from
   syncContext(), which is the function that owns that row. */
.dj-ai-ctx[hidden] { display: none; }
@media (max-width: 760px) {
  .dj-ai-inline .dj-ai-log { max-height: 52vh; }
}

/* On wide screens the drawer is a true side panel: the page shrinks to fit
   beside it so the product list stays fully readable. Narrower viewports keep
   the drawer as an overlay. */
@media (min-width: 1120px) {
  body.dj-ai-open .portal-main,
  body.dj-ai-open .portal-header {
    padding-inline-end: 416px;
  }
  body.dj-ai-open .batch-actions.is-sticky { z-index: 4; }
}

.dj-ai-head {
  flex: none;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 13px 15px;
  border-bottom: 1px solid var(--line);
  background: var(--paper);
}
.dj-ai-head h2 {
  font-family: var(--font-display);
  font-size: 1rem;
  margin: 0;
  display: flex;
  align-items: center;
  gap: 7px;
}
.dj-ai-head h2 svg { width: 16px; height: 16px; color: var(--gold-deep); }
.dj-ai-head .dj-ai-x { margin-inline-start: auto; }

.dj-ai-ctx {
  flex: none;
  padding: 8px 15px;
  border-bottom: 1px solid var(--line);
  font-size: 0.78rem;
  color: var(--ink-soft);
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  background: #fbfcfd;
}
.dj-ai-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  /* Takes the whole row when it needs to, then clips — never a second line. */
  max-width: 100%;
  min-width: 0;
  white-space: nowrap;
  background: #fff7e6;
  color: var(--gold-deep);
  border: 1px solid #f0dca8;
  border-radius: 999px;
  padding: 2px 9px;
  font-weight: 700;
  font-size: 0.73rem;
}
/* The icon had no size of its own, so it collapsed to nothing as a shrinkable
   flex item. Sized like every other chip icon in the drawer. */
.dj-ai-chip > svg { width: 13px; height: 13px; flex: none; }

/* A one-line label that may outgrow its chip. Clipped with an ellipsis; when it
   really is clipped, assistant.js upgrades it into a focusable control that
   reveals the rest, so the full text is not hover-only. */
.dj-ai-t {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dj-ai-t.is-trunc {
  cursor: pointer;
  text-decoration: underline dotted currentColor;
  text-underline-offset: 2px;
}
.dj-ai-t.is-trunc:focus-visible {
  outline: 2px solid var(--gold-deep);
  outline-offset: 2px;
  border-radius: 3px;
}
.dj-ai-t.is-full {
  overflow: visible;
  white-space: normal;
  overflow-wrap: anywhere;
  text-decoration: none;
}
.dj-ai-chip.is-full,
.dj-ai-att.is-full,
.dj-ai-msg-att.is-full { white-space: normal; max-width: 100%; }

.dj-ai-log {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 14px 15px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.dj-ai-msg {
  max-width: 92%;
  padding: 9px 12px;
  border-radius: 12px;
  font-size: 0.85rem;
  line-height: 1.45;
  white-space: pre-wrap;
  word-wrap: break-word;
}
.dj-ai-msg.user { align-self: flex-end; background: var(--ink); color: #fff; border-end-end-radius: 4px; }
.dj-ai-msg.bot { align-self: flex-start; background: var(--fog); color: var(--ink); border-end-start-radius: 4px; }
.dj-ai-msg.sys { align-self: center; background: #fff7e6; color: var(--gold-deep); font-size: 0.78rem; font-weight: 600; }
.dj-ai-msg.err { align-self: flex-start; background: #fdecef; color: var(--red-deep); }
.dj-ai-typing { align-self: flex-start; display: inline-flex; gap: 4px; padding: 10px 13px; }
.dj-ai-typing i {
  width: 6px; height: 6px; border-radius: 50%; background: var(--ink-soft);
  animation: djAiBlink 1.1s infinite;
}
.dj-ai-typing i:nth-child(2) { animation-delay: 0.16s; }
.dj-ai-typing i:nth-child(3) { animation-delay: 0.32s; }
@keyframes djAiBlink { 0%, 60%, 100% { opacity: 0.25; } 30% { opacity: 1; } }

/* "the assistant looked something up" — sits just above the reply it grounds */
.dj-ai-look {
  align-self: flex-start;
  max-width: 100%;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 5px;
  font-size: 0.72rem;
  color: var(--ink-soft);
}
.dj-ai-look > svg { width: 12px; height: 12px; flex: none; color: var(--gold-deep); }
.dj-ai-look .dj-ai-chip {
  background: var(--fog);
  color: var(--ink-soft);
  border-color: var(--line);
  font-weight: 600;
}
.dj-ai-look .dj-ai-chip.is-err { background: #fdecef; color: var(--red-deep); border-color: #f3c9d2; }

/* proposed batch action awaiting confirmation */
.dj-ai-prop {
  align-self: stretch;
  border: 1px solid var(--gold);
  background: #fffdf5;
  border-radius: 10px;
  padding: 12px;
  font-size: 0.83rem;
}
.dj-ai-prop h4 {
  margin: 0 0 6px;
  font-family: var(--font-display);
  font-size: 0.92rem;
  display: flex;
  align-items: center;
  gap: 6px;
}
.dj-ai-prop .dj-ai-diff {
  margin: 8px 0;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: #fff;
  max-height: 190px;
  overflow: auto;
}
.dj-ai-prop .dj-ai-diff div {
  padding: 5px 9px;
  border-bottom: 1px solid var(--line);
  font-size: 0.77rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dj-ai-prop .dj-ai-diff div:last-child { border-bottom: 0; }
.dj-ai-prop .dj-ai-field {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.76rem;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 7px 9px;
  margin: 6px 0;
  white-space: pre-wrap;
  max-height: 150px;
  overflow: auto;
}
.dj-ai-prop-acts { display: flex; gap: 8px; margin-top: 10px; }

.dj-ai-form {
  flex: none;
  border-top: 1px solid var(--line);
  padding: 10px 12px 12px;
  background: #fff;
}
.dj-ai-form textarea {
  width: 100%;
  min-height: 62px;
  max-height: 150px;
  resize: vertical;
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: 9px 11px;
  font-family: var(--font-body);
  font-size: 0.86rem;
  color: var(--ink);
}
.dj-ai-form-row { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
.dj-ai-hint { font-size: 0.72rem; color: var(--ink-soft); }
/* Sits under .dj-ai-hint on the confirmation card and says how many of the
   affected products are live and will go back into review. It carries
   --gold-deep, which is the ink .badge-pending uses, so the warning is the same
   colour as the status it is predicting rather than a generic alarm. Keeps the
   hint's size, so it reads as part of that summary and not as an error. */
.dj-ai-warn { font-size: 0.72rem; color: var(--gold-deep); margin-top: 2px; }
.dj-ai-form-row .btn { margin-inline-start: auto; }
.dj-ai-clip { width: 30px; height: 30px; border-radius: 7px; flex: none; }
.dj-ai-clip svg { width: 15px; height: 15px; }

/* staged attachments, above the composer */
.dj-ai-atts { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
/* display:flex beats the UA [hidden] rule, so the empty strip kept its
   margin-bottom above the composer whenever nothing was attached. */
.dj-ai-atts[hidden] { display: none; }
.dj-ai-att {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  /* Capped on the chip, not on the name inside it, so a clipped name always
     fills the chip instead of leaving a gap before the remove button. */
  max-width: min(100%, 18rem);
  min-width: 0;
  white-space: nowrap;
  padding: 4px 22px 4px 7px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: var(--fog);
  font-size: 0.72rem;
  color: var(--ink-soft);
}
.dj-ai-att svg { width: 13px; height: 13px; flex: none; }
.dj-ai-att-name { flex: 1 1 auto; }
.dj-ai-att.is-img { padding: 0; border: 0; background: none; }
.dj-ai-att-thumb {
  width: 46px;
  height: 46px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: var(--fog) center/cover no-repeat;
  cursor: zoom-in;
  display: block;
}
.dj-ai-att-x {
  position: absolute;
  top: -5px;
  inset-inline-end: -5px;
  width: 17px;
  height: 17px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: #fff;
  color: var(--ink-soft);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}
.dj-ai-att-x svg { width: 9px; height: 9px; }
.dj-ai-att-x:hover { color: var(--red); border-color: var(--red); }

/* attachments echoed inside a sent message */
.dj-ai-msg-atts { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
.dj-ai-msg-att {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  max-width: min(100%, 13rem);
  min-width: 0;
  padding: 2px 6px;
  border-radius: 5px;
  background: rgba(255, 255, 255, 0.22);
  font-size: 0.68rem;
  overflow: hidden;
  white-space: nowrap;
}
.dj-ai-msg-att svg { width: 11px; height: 11px; flex: none; }
.dj-ai-msg-att.is-img {
  width: 42px;
  height: 42px;
  padding: 0;
  background: center/cover no-repeat;
  cursor: zoom-in;
}

/* drop target */
.dj-ai.is-drop { outline: 2px dashed var(--gold-deep); outline-offset: -6px; }
.dj-ai.is-drop .dj-ai-form { background: #fffdf4; }

@media (max-width: 620px) {
  .dj-ai { width: 100vw; border-inline-start: 0; }
  .dj-ai-fab { bottom: 14px; inset-inline-end: 14px; padding: 10px 14px; }
}

/* ============================================================
   Bulk import wizard (supplier portal, #import)
   Dense, step-led surface built from the same primitives as the
   compact product list. Gold marks "you are here", green marks
   "already done" — red is reserved for genuine failure.
   ============================================================ */
.imp { padding-top: 18px; }

.imp-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 0 0 12px; }
.imp-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-display);
  font-size: 1.18rem;
  margin: 0;
  color: var(--ink);
}
.imp-title svg { width: 18px; height: 18px; color: var(--red); flex: none; }
/* The file under review, always in view once one is picked. */
.imp-fchip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
  max-width: min(100%, 26rem);
  margin-inline-start: auto;
  padding: 3px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--paper);
  font-size: 0.74rem;
  font-weight: 600;
  color: var(--ink-soft);
}
.imp-fchip svg { width: 12px; height: 12px; flex: none; color: var(--gold-deep); }
.imp-fchip span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- stepper ---- */
.imp-steps {
  display: flex;
  align-items: center;
  gap: 3px;
  flex-wrap: wrap;
  list-style: none;
  margin: 0 0 16px;
  padding: 5px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--paper);
}
.imp-step { flex: 1 1 auto; min-width: 0; }
.imp-step-x,
.imp-step-b {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  padding: 5px 10px;
  border: 0;
  border-radius: 999px;
  background: none;
  font-family: var(--font-body);
  font-size: 0.76rem;
  font-weight: 600;
  color: var(--ink-soft);
  opacity: 0.55;
}
.imp-step-b { cursor: pointer; }
.imp-step-b:hover { background: var(--fog); opacity: 1; }
.imp-step-b:focus-visible { outline: 2px solid var(--gold-deep); outline-offset: 1px; }
.imp-step-n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 19px;
  height: 19px;
  flex: none;
  border-radius: 50%;
  background: var(--fog);
  color: var(--ink-soft);
  font-size: 0.68rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.imp-step-n svg { width: 11px; height: 11px; }
.imp-step-t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.imp-step.is-done .imp-step-x,
.imp-step.is-done .imp-step-b { opacity: 1; }
.imp-step.is-done .imp-step-n { background: #d7f0e0; color: #166534; }
.imp-step.is-now .imp-step-x { opacity: 1; background: #fff; box-shadow: 0 1px 3px rgba(14, 36, 51, 0.12); color: var(--ink); }
.imp-step.is-now .imp-step-n { background: var(--gold); color: #4a3300; }

/* ---- step 1: drop zone ---- */
.imp-lead { font-size: 0.85rem; line-height: 1.5; color: var(--ink-soft); margin: 0 0 14px; max-width: 72ch; }
.imp-drop {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 20px;
  border: 2px dashed rgba(200, 16, 46, 0.28);
  border-radius: 12px;
  background: linear-gradient(180deg, #fffdf7 0%, #fff 70%);
  cursor: pointer;
  text-align: center;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.imp-drop:hover { border-color: var(--red); background: #fffaf2; }
.imp-drop.is-over { border-color: var(--gold-deep); background: #fffdf0; }
.imp-drop:focus-within { outline: 2px solid var(--gold-deep); outline-offset: 2px; }
.imp-drop-i {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border-radius: 12px;
  background: #fff;
  border: 1px solid var(--line);
  color: var(--red);
  margin-bottom: 4px;
}
.imp-drop-i svg { width: 19px; height: 19px; }
.imp-drop-t { font-size: 0.95rem; font-weight: 700; color: var(--ink); }
.imp-drop-s { font-size: 0.78rem; color: var(--ink-soft); max-width: 64ch; line-height: 1.45; }
/* The native control stays focusable but the label is what you see. */
.imp-input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }

.imp-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 8px;
  list-style: none;
  margin: 14px 0 0;
  padding: 0;
}
.imp-facts li {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 9px 11px;
  border: 1px solid var(--line);
  border-radius: 9px;
  background: var(--paper);
  font-size: 0.76rem;
  line-height: 1.4;
  color: var(--ink-soft);
}
.imp-facts svg { width: 14px; height: 14px; flex: none; margin-top: 1px; color: var(--gold-deep); }
.imp-tip {
  display: flex;
  align-items: flex-start;
  gap: 7px;
  margin: 12px 0 0;
  font-size: 0.76rem;
  color: var(--ink-soft);
}
.imp-tip svg { width: 14px; height: 14px; flex: none; margin-top: 2px; }
.imp-tip a { color: var(--red-deep); font-weight: 600; }

/* ---- section heading shared by the inner steps ---- */
.imp-sec {
  font-family: var(--font-display);
  font-size: 0.98rem;
  margin: 0 0 10px;
  color: var(--ink);
}
.imp-note { font-size: 0.76rem; line-height: 1.45; color: var(--ink-soft); margin: 0 0 9px; }
.imp-more { font-size: 0.76rem; color: var(--ink-soft); margin: 8px 0 0; text-align: center; }

/* ---- step 2: sheet picker ---- */
.imp-sheets { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 8px; }
.imp-sheet {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #fff;
  cursor: pointer;
  min-width: 0;
}
.imp-sheet:hover { border-color: var(--gold); }
.imp-sheet.is-on { border-color: var(--gold-deep); background: #fffdf5; box-shadow: inset 3px 0 0 var(--gold-deep); }
.imp-sheet input { position: absolute; opacity: 0; width: 0; height: 0; }
.imp-sheet:focus-within { outline: 2px solid var(--gold-deep); outline-offset: 1px; }
.imp-sheet-i {
  display: inline-flex;
  width: 30px;
  height: 30px;
  flex: none;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  background: var(--fog);
  color: var(--ink-soft);
}
.imp-sheet.is-on .imp-sheet-i { background: #fff7e6; color: var(--gold-deep); }
.imp-sheet-i svg { width: 15px; height: 15px; }
.imp-sheet-m { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.imp-sheet-m b { font-size: 0.86rem; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.imp-tags { display: flex; gap: 4px; flex-wrap: wrap; }
.imp-tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--fog);
  color: var(--ink-soft);
  font-size: 0.68rem;
  font-weight: 700;
  line-height: 1.6;
}
.imp-tag svg { width: 10px; height: 10px; }
.imp-tag.is-img { background: #eef4fb; color: #1d4e89; }
.imp-tag.is-on { background: #d7f0e0; color: #166534; }
.imp-tag.is-ai { background: #fff7e6; color: var(--gold-deep); }

/* ---- step 3: mapping ---- */
.imp-mbar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: 0 0 10px;
}
.imp-mnum { display: inline-flex; align-items: center; gap: 6px; }
.imp-mnum label { font-size: 0.72rem; font-weight: 600; color: var(--ink-soft); margin: 0; }
.imp-mnum input {
  width: 62px;
  padding: 5px 7px;
  border: 1px solid var(--line);
  border-radius: 7px;
  font-family: var(--font-body);
  font-size: 0.82rem;
  text-align: center;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  background: #fff;
}
.imp-mnum input:focus-visible { outline: 2px solid var(--gold-deep); outline-offset: 1px; }

.imp-map {
  border: 1px solid var(--line);
  border-radius: 9px;
  background: #fff;
  overflow: hidden;
}
.imp-mrow {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr) 20px 178px;
  align-items: center;
  gap: 10px;
  padding: 6px 11px;
  border-bottom: 1px solid var(--line);
}
.imp-mrow:last-child { border-bottom: 0; }
.imp-mrow.imp-mhead {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--fog);
}
.imp-mrow.imp-mhead > span {
  font-size: 0.66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
}
.imp-mrow.is-on { box-shadow: inset 3px 0 0 #3f8f5c; }
.imp-mrow.is-off { background: #fcfdfd; }
.imp-mrow.is-off .imp-mcol,
.imp-mrow.is-off .imp-msample { opacity: 0.55; }
.imp-mcol {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.imp-msample {
  font-size: 0.76rem;
  color: var(--ink-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.imp-arrow { color: var(--ink-soft); opacity: 0.45; display: inline-flex; justify-content: center; }
.imp-arrow svg { width: 13px; height: 13px; }
[dir="rtl"] .imp-arrow svg { transform: scaleX(-1); }
.imp-msel {
  width: 100%;
  padding: 5px 7px;
  border: 1px solid var(--line);
  border-radius: 7px;
  font-family: var(--font-body);
  font-size: 0.79rem;
  color: var(--ink-soft);
  background: #fff;
}
.imp-mrow.is-on .imp-msel { color: var(--ink); font-weight: 600; border-color: #bfe6cd; background: #fbfefc; }
.imp-msel:focus-visible { outline: 2px solid var(--gold-deep); outline-offset: 1px; }

/* ---- collapsible "fixed values for all rows" ---- */
.imp-fold {
  margin: 12px 0 0;
  border: 1px solid var(--line);
  border-radius: 9px;
  background: var(--paper);
  padding: 0 12px;
}
.imp-fold > summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 0;
  cursor: pointer;
  list-style: none;
  font-size: 0.8rem;
  color: var(--ink-soft);
}
.imp-fold > summary::-webkit-details-marker { display: none; }
.imp-fold > summary:focus-visible { outline: 2px solid var(--gold-deep); outline-offset: 2px; border-radius: 6px; }
.imp-fold > summary > svg { width: 14px; height: 14px; flex: none; color: var(--gold-deep); }
.imp-fold-t { font-weight: 700; color: var(--ink); }
.imp-fold-s { margin-inline-start: auto; font-size: 0.72rem; }
.imp-fold .imp-tag { margin-inline-start: auto; }
.imp-fold[open] { padding-bottom: 12px; }
.imp-consts .field { margin-bottom: 0; }
.imp-consts .field label { font-size: 0.7rem; margin-bottom: 3px; }
.imp-consts .field input,
.imp-consts .field select { padding: 6px 8px; font-size: 0.82rem; border-radius: 7px; }

/* ---- step 4: review ---- */
.imp-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; margin: 0 0 12px; }
.imp-stat {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #fff;
}
.imp-stat-i {
  display: inline-flex;
  width: 28px;
  height: 28px;
  flex: none;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  background: #fff7e6;
  color: var(--gold-deep);
}
.imp-stat-i svg { width: 14px; height: 14px; }
.imp-stat-v {
  font-family: var(--font-display);
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.imp-stat-l { font-size: 0.72rem; color: var(--ink-soft); }

/* Where the import lands. Deliberately a panel and not a lone select: the
   proposed group name is a decision, so it is shown rather than applied. */
.imp-group {
  border: 1px solid #f0dca8;
  border-radius: 10px;
  background: #fffdf5;
  padding: 11px 13px 13px;
  margin: 0 0 12px;
}
.imp-group-h { display: flex; align-items: center; gap: 7px; margin: 0 0 9px; font-size: 0.84rem; color: var(--ink); }
.imp-group-h svg { width: 15px; height: 15px; color: var(--gold-deep); flex: none; }
.imp-gopts { display: flex; gap: 6px; flex-wrap: wrap; margin: 0 0 9px; }
.imp-gopt {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 5px 11px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: #fff;
  cursor: pointer;
  font-size: 0.78rem;
  color: var(--ink-soft);
  user-select: none;
}
.imp-gopt:hover { border-color: var(--gold); }
.imp-gopt.is-on { border-color: var(--gold-deep); background: #fff7e6; color: var(--ink); }
.imp-gopt input { position: absolute; opacity: 0; width: 0; height: 0; }
.imp-gopt:focus-within { outline: 2px solid var(--gold-deep); outline-offset: 1px; }
.imp-gopt-t { font-weight: 700; }
.imp-gopt-s { font-size: 0.7rem; opacity: 0.8; }

.imp-gname {
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: 460px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: #fff;
}
.imp-gname:focus-within { border-color: var(--gold-deep); box-shadow: 0 0 0 2px rgba(207, 143, 4, 0.18); }
.imp-gname > svg { width: 14px; height: 14px; flex: none; color: var(--ink-soft); }
.imp-gname input,
.imp-gname select {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  outline: none;
  background: none;
  padding: 8px 0;
  font-family: var(--font-body);
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--ink);
}
.imp-gcount {
  flex: none;
  font-size: 0.68rem;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
  direction: ltr;
}
.imp-gmsg { margin: 7px 0 0; min-height: 0; }
.imp-gmsg:empty { display: none; }
/* Inline flow, not a flex row: on a narrow screen the sentence has to wrap and
   a flex row would leave the icon stranded on a line of its own. */
.imp-gclash { display: block; font-size: 0.74rem; line-height: 1.5; color: var(--gold-deep); }
.imp-gclash svg { width: 12px; height: 12px; vertical-align: -2px; margin-inline-end: 4px; }
.imp-gclash .imp-link { margin-inline-start: 4px; }
.imp-link {
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 0.74rem;
  font-weight: 700;
  color: var(--red-deep);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.imp-link:focus-visible { outline: 2px solid var(--gold-deep); outline-offset: 2px; border-radius: 3px; }

/* The preview list is the product list, one step early. */
.imp-plist { max-height: 46vh; overflow-y: auto; }

/* ---- step 5: done ---- */
.imp-done {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 16px;
  border: 1px solid #bfe6cd;
  border-radius: 11px;
  background: #f4fcf7;
}
.imp-done-i {
  display: inline-flex;
  width: 32px;
  height: 32px;
  flex: none;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: #d7f0e0;
  color: #166534;
}
.imp-done-i svg { width: 16px; height: 16px; }
.imp-done-t { margin: 3px 0 0; font-size: 0.92rem; font-weight: 700; color: #14532d; }
.imp-done-g { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin: 8px 0 0; font-size: 0.76rem; color: var(--ink-soft); }

/* ---- footer actions, shared by every step ---- */
.imp-acts { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 14px 0 0; }
.imp-acts-note { font-size: 0.74rem; color: var(--ink-soft); }
.imp-acts .imp-next { margin-inline-start: auto; display: inline-flex; align-items: center; gap: 7px; }
.imp-acts .imp-next svg { width: 14px; height: 14px; }

@media (max-width: 760px) {
  /* One unbroken row: the other steps shrink to their number, the step you are
     on keeps its label and takes the slack. Wrapping five pills reads as a
     jumble rather than as progress. */
  .imp-steps { flex-wrap: nowrap; }
  .imp-step { flex: 0 0 auto; }
  .imp-step.is-now { flex: 1 1 auto; }
  .imp-step-x,
  .imp-step-b { padding-inline: 6px; }
  .imp-step-t { display: none; }
  .imp-step.is-now .imp-step-x { padding-inline: 10px; }
  .imp-step.is-now .imp-step-t { display: inline; }
  .imp-mrow { grid-template-columns: minmax(0, 1fr) 20px 150px; }
  /* The sample value is the first thing to go: the header name and the target
     are what the supplier is actually deciding between. */
  .imp-mrow > .imp-msample { display: none; }
  .imp-mrow.imp-mhead > span:nth-child(2) { display: none; }
  .imp-drop { padding: 22px 14px; }
  .imp-acts .imp-next { margin-inline-start: 0; flex: 1 1 100%; justify-content: center; }
}

/* ============================================================
   Portal dashboard — a conversation, with notifications beside it

   TWO GRID TRACKS, NOT A FLOAT. The rail is `1fr 300px`, so the writing mode
   decides which side it lands on: right in English, left in Arabic, from one
   declaration. The divider between them is border-inline-start for the same
   reason — a rule between two columns is a LOGICAL edge and has to flip. (Note
   this is the opposite of the rule for border-built glyphs like .pf-chev, which
   must be physical: a logical border there points the arrow sideways.)

   The centre is capped well below the track it sits in. A chat line the full
   width of a 1400px page is unreadable — the eye loses the start of the next
   line — so the conversation and its composer stay at 820px and centre in
   whatever room the track has.
   ============================================================ */
.dash-wrap {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: 26px;
  align-items: start;
}
.dash-flow { min-width: 0; width: 100%; max-width: 820px; margin-inline: auto; }
.dash-rail {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 14px;
  border-inline-start: 1px solid var(--line);
  padding-inline-start: 24px;
}
.dash-rail-id { padding: 2px 0 2px; }
/* In a 300px rail the badges cannot share a line with an email address. */
.dash-rail .dash-head { display: block; margin: 0; }
.dash-rail .dash-badges { margin-inline-start: 0; margin-top: 9px; }
.dash-rail .dash-email { max-width: 100%; }

/* ---- the chat-first centre ---- */
.dash-hero { text-align: center; padding: 30px 0 22px; }
.dash-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  margin: 0 0 16px;
  border-radius: 13px;
  background: var(--ink);
  color: var(--gold);
}
.dash-mark svg { width: 21px; height: 21px; }
.dash-hero h1 {
  font-family: var(--font-display);
  font-size: 1.86rem;
  line-height: 1.24;
  margin: 0;
  color: var(--ink);
}
.dash-hero-sub {
  margin: 11px auto 0;
  max-width: 58ch;
  font-size: 0.86rem;
  line-height: 1.6;
  color: var(--ink-soft);
}

/* Prompt starters. A chip fills the composer and submits it, so it is a button
   and not a link — nothing here navigates. */
.dash-chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin: 18px 0 0;
}
.dash-chip {
  max-width: 100%;
  padding: 7px 14px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: #fff;
  font-family: var(--font-body);
  font-size: 0.78rem;
  line-height: 1.4;
  color: var(--ink-soft);
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dash-chip:hover { border-color: var(--gold); color: var(--ink); background: #fffdf7; }
.dash-chip:focus-visible { outline: 2px solid var(--gold-deep); outline-offset: 2px; }

/* Quick entries. A hairline and a radius, deliberately NOT .portal-card: these
   are the only framed things in the centre and they must not read as the panels
   this rebuild removed. */
.dash-quick { margin: 32px 0 0; }
.dash-quick h2 {
  font-family: var(--font-display);
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--ink-soft);
  margin: 0 0 12px;
}
.dash-tasks { margin: 26px 0 0; }
.dash-tasks h2 {
  font-family: var(--font-display);
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--ink-soft);
  margin: 0 0 10px;
}
.dash-task-list { display: flex; flex-direction: column; gap: 8px; }
.dash-task {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 11px 13px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #fff;
  text-decoration: none;
  color: var(--ink);
  font-size: 0.88rem;
  font-weight: 500;
}
.dash-task:hover { border-color: var(--gold); background: #fffdf7; }
.dash-task .nav-count {
  min-width: 1.2em;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--ink);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1.45;
  text-align: center;
}
.dash-tasks-clear { margin: 0; font-size: 0.86rem; color: var(--ink-soft); }
.dash-q-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(216px, 1fr)); gap: 10px; }
.dash-q {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  padding: 13px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #fff;
  text-decoration: none;
}
.dash-q:hover { border-color: var(--gold); background: #fffdf7; }
.dash-q:focus-visible { outline: 2px solid var(--gold-deep); outline-offset: 2px; }
.dash-q-i {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 8px;
  background: var(--fog);
  color: var(--red);
}
.dash-q-i svg { width: 15px; height: 15px; }
.dash-q-b { min-width: 0; }
.dash-q-t { display: block; font-size: 0.84rem; font-weight: 700; color: var(--ink); }
.dash-q-s { display: block; margin-top: 2px; font-size: 0.73rem; line-height: 1.45; color: var(--ink-soft); }

/* ---- the statistics section ----
   The last block in the centre column, and PLAINER than the quick entries above
   it: those are the only framed things here, and a chart inside a second frame
   would read as the panel this rebuild removed. A heading, a row of text
   buttons, and the chart.

   THE CHART MARKUP IS NOT LOCAL. It comes from UI.chart() in portal-ui.js, so
   the bars are styled by .cat-bars / .cat-bar-* and .acct-spark-* further up
   this file — the same rules the admin console's catalog tab and the account
   page's statistics tiles are drawn with. Only the frame is styled here, and
   there is deliberately no second copy of a bar rule under a .dash- name. */
.dash-stats { margin: 34px 0 0; }
.dash-stats[hidden] { display: none; }
.dash-stats > h2 {
  font-family: var(--font-display);
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--ink-soft);
  margin: 0 0 10px;
}
.dash-stat-dims { display: flex; flex-wrap: wrap; gap: 4px; margin: 0 0 14px; }
.dash-stat-dim {
  padding: 5px 11px;
  border: 0;
  border-radius: 7px;
  background: none;
  font-family: var(--font-body);
  font-size: 0.78rem;
  line-height: 1.4;
  color: var(--ink-soft);
  cursor: pointer;
}
.dash-stat-dim:hover { background: var(--fog); color: var(--ink); }
.dash-stat-dim:focus-visible { outline: 2px solid var(--gold-deep); outline-offset: 1px; }
/* The pressed choice is the CAPTION of the chart beneath it — there is no second
   heading saying what is drawn — so it has to read as the current one rather
   than merely as hovered. aria-pressed carries the same fact to a reader who
   cannot see the fill. */
.dash-stat-dim.is-on { background: var(--ink); color: #fff; font-weight: 700; }
.dash-stat-sub { margin: 9px 0 0; font-size: 0.72rem; color: var(--ink-soft); }
.dash-stat-note { margin: 7px 0 0; font-size: 0.7rem; line-height: 1.5; color: var(--ink-soft); }
/* A breakdown with nothing in it says so in a sentence. Twelve 1px stubs under a
   heading read as a chart that failed to load. */
.dash-stat-empty { margin: 0; font-size: 0.8rem; line-height: 1.5; color: var(--ink-soft); }

/* The state for an account the assistant cannot serve: the banner IS the page. */
.dash-gate { max-width: 620px; margin: 44px auto 0; }

.dash-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 0 0 14px; }
.dash-id { min-width: 0; }
.dash-hi { font-family: var(--font-display); font-size: 1.18rem; margin: 0; color: var(--ink); }
.dash-email {
  margin: 2px 0 0;
  font-size: 0.8rem;
  color: var(--ink-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 42ch;
}
.dash-badges { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-inline-start: auto; }
.dash-cta { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 4px; }
.dash-cta .btn { display: inline-flex; align-items: center; gap: 7px; }
.dash-cta .btn svg { width: 14px; height: 14px; }

/* The AI pane lives in the CENTRE COLUMN and the two notices panels live in the
   RAIL, so they are in different subtrees entirely. That placement is
   load-bearing: repaintNotices() rewrites the innerHTML of #dash-notes and
   #dash-rules on every feed refresh, seconds after first paint and with no user
   action, so a pane inside either would be detached mid-conversation. Out here,
   only a full render() can replace it — which assistant.js detects and rebuilds
   from, carrying the transcript across. */
.dash-ai { margin: 20px 0 0; }
.dash-ai-hand {
  margin: 0;
  padding: 11px 2px 0;
  border-top: 1px solid var(--line);
  font-size: 0.76rem;
  line-height: 1.55;
  color: var(--ink-soft);
}
/* The destination is named in bold rather than linked — the quick-entry cards
   below carry the one link to each of these places. */
.dash-ai-hand b { color: var(--ink); font-weight: 700; }

/* Rail panels: a hairline and a radius, no shadow. The rail is reference
   material beside a conversation and must not compete with it. */
.dash-panel {
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: #fff;
}
.dash-panel[hidden] { display: none; }
.dash-panel-h {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 11px 14px;
  border-bottom: 1px solid var(--line);
  background: var(--paper);
}
.dash-panel-h > svg { width: 15px; height: 15px; flex: none; color: var(--red); }
.dash-panel-h h2 { font-family: var(--font-display); font-size: 0.96rem; margin: 0; color: var(--ink); }
.dash-panel-h .icon-btn { margin-inline-start: auto; width: 27px; height: 27px; }
.dash-unread {
  padding: 1px 8px;
  border-radius: 999px;
  background: var(--red);
  color: #fff;
  font-size: 0.66rem;
  font-weight: 700;
  line-height: 1.7;
}
.dash-unread[hidden] { display: none; }
.dash-unread + .icon-btn { margin-inline-start: auto; }

.dash-list { max-height: 52vh; overflow-y: auto; }
.dash-empty { padding: 18px 14px; margin: 0; font-size: 0.8rem; color: var(--ink-soft); text-align: center; }
.dash-foot { margin: 0; padding: 8px 14px; border-top: 1px solid var(--line); font-size: 0.7rem; color: var(--ink-soft); }

.dash-note,
.dash-rule { border-bottom: 1px solid var(--line); }
.dash-note:last-child,
.dash-rule:last-child { border-bottom: 0; }
.dash-note > summary,
.dash-rule > summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 14px;
  cursor: pointer;
  list-style: none;
}
.dash-note > summary::-webkit-details-marker,
.dash-rule > summary::-webkit-details-marker { display: none; }
.dash-note > summary:hover,
.dash-rule > summary:hover { background: #fbfcfd; }
.dash-note > summary:focus-visible,
.dash-rule > summary:focus-visible { outline: 2px solid var(--gold-deep); outline-offset: -2px; }
.dash-note-t {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Unread reads heavier and carries the only red dot on the page. */
.dash-note.is-unread { background: #fffdf7; }
.dash-note.is-unread .dash-note-t { color: var(--ink); font-weight: 700; }
.dash-dot { width: 7px; height: 7px; flex: none; border-radius: 50%; background: transparent; }
.dash-note.is-unread .dash-dot { background: var(--red); }
.dash-pin { display: inline-flex; flex: none; color: var(--gold-deep); }
.dash-pin svg { width: 12px; height: 12px; }
.dash-lvl { flex: none; font-size: 0.64rem; padding: 1px 7px; }
.dash-when { flex: none; font-size: 0.68rem; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.dash-note[open] > summary { border-bottom: 1px solid var(--line); }
.dash-rule[open] > summary { border-bottom: 1px solid var(--line); }
.dash-body { padding: 10px 14px 13px; background: #fff; }
.dash-body .md { font-size: 0.83rem; }

.dash-rn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  flex: none;
  border-radius: 6px;
  background: var(--fog);
  color: var(--ink-soft);
  font-size: 0.68rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.dash-rule[open] .dash-rn { background: #fff7e6; color: var(--gold-deep); }
.dash-aud {
  flex: none;
  padding: 1px 7px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: #fff;
  font-size: 0.64rem;
  font-weight: 700;
  color: var(--ink-soft);
}

/* The same 900px the two notices panels used to stack at. Below it the rail goes
   under the conversation, loses the divider that separated two columns, and
   gains the horizontal one that separates two stacked blocks. .portal-main's own
   20px keeps the centred column off the edge. */
@media (max-width: 900px) {
  .dash-wrap { grid-template-columns: minmax(0, 1fr); gap: 20px; }
  .dash-rail {
    border-inline-start: 0;
    padding-inline-start: 0;
    border-top: 1px solid var(--line);
    padding-top: 20px;
  }
  .dash-rail .dash-head { display: flex; }
  .dash-hero { padding-top: 18px; }
  .dash-hero h1 { font-size: 1.52rem; }
}
@media (max-width: 760px) {
  .dash-badges { margin-inline-start: 0; }
  .dash-when { display: none; }
  .dash-list { max-height: none; }
  .dash-chip { font-size: 0.75rem; padding: 6px 12px; }
}

/* Screen-reader-only text. This lived as a byte-identical <style> block at the
   bottom of BOTH index.html and inventory.html, while both pages already loaded
   this file — two copies of one rule, kept in step by nothing.
   It is here rather than there because an inline <style> element is exactly what
   style-src cannot allow without 'unsafe-inline', and with this moved out,
   index.html needs no inline style allowance at all.
   LAST IN THE FILE on purpose: it used to sit after everything, so among rules of
   equal specificity it won on source order. Appending preserves that; moving it
   higher would not. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
}
