/* ALU PACK — design system. Hand-written CSS (no build step) for maximum
   portability and fast first paint / high Lighthouse performance scores. */

:root {
  --blue: #1d4ed8;
  --blue-dark: #1e3a8a;
  --blue-light: #eff6ff;
  --ink: #0f172a;
  --ink-soft: #334155;
  --muted: #64748b;
  --border: #e2e8f0;
  --bg-alt: #f8fafc;
  --white: #ffffff;
  --radius: 10px;
  --radius-lg: 16px;
  --shadow: 0 1px 3px rgba(15, 23, 42, 0.08), 0 1px 2px rgba(15, 23, 42, 0.06);
  --shadow-lg: 0 10px 30px rgba(15, 23, 42, 0.12);
  /* Side gutter matched to the reference mock-up, which insets its content
     ~2.6% of the page width on each side. Keeping it proportional means that
     ratio holds at every desktop size instead of only one. The cap is set
     past 2560 so it doesn't bind on any ordinary monitor; on an ultrawide it
     finally centres rather than stretching a text column to 3600px. */
  --container: 2560px;
  --gutter: clamp(16px, 2.6vw, 66px);
  --header-h: 72px;
  font-size: 16px;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; scroll-padding-top: var(--header-h); }
body {
  margin: 0;
  font-family: "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif;
  color: var(--ink-soft);
  background: var(--white);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; }
h1, h2, h3, h4 { color: var(--ink); font-weight: 700; line-height: 1.25; margin: 0 0 0.5em; }
h1 { font-size: clamp(1.9rem, 3.2vw, 2.75rem); }
h2 { font-size: clamp(1.65rem, 2.6vw, 2.3rem); }
h3 { font-size: 1.15rem; }
p { margin: 0 0 1em; }
ul { list-style: none; margin: 0; padding: 0; }
.icon { width: 1.1em; height: 1.1em; vertical-align: -0.15em; }

.container { max-width: var(--container); margin: 0 auto; padding: 0 var(--gutter); }
.skip-link { position: absolute; left: -999px; top: 0; background: var(--blue); color: #fff; padding: 10px 16px; z-index: 200; }
.skip-link:focus { left: 8px; top: 8px; }

/* Buttons */
.btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 12px 22px; border-radius: 8px; font-weight: 600; font-size: 0.95rem;
  border: 1px solid transparent; cursor: pointer; transition: background .18s ease, border-color .18s ease, color .18s ease, transform .18s ease, box-shadow .18s ease;
  white-space: nowrap;
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }
.btn-primary { background: var(--blue); color: #fff; }
.btn-primary:hover { background: var(--blue-dark); box-shadow: 0 8px 20px -6px rgba(29, 78, 216, 0.45); }
.btn-outline { background: #fff; color: var(--ink); border-color: var(--border); }
.btn-outline:hover { border-color: var(--blue); color: var(--blue); }
.btn-white { background: #fff; color: var(--blue); }
.btn-white:hover { background: var(--blue-light); }
.btn-sm { padding: 9px 16px; font-size: 0.85rem; }
.btn-danger { background: #dc2626; color: #fff; }
.btn:disabled { opacity: 0.4; cursor: not-allowed; background: #94a3b8; box-shadow: none; transform: none; }
.btn:disabled:hover { transform: none; box-shadow: none; }
.btn-block { width: 100%; justify-content: center; }

/* Header */
.site-header { position: sticky; top: 0; z-index: 100; background: rgba(255,255,255,0.96); backdrop-filter: blur(8px); border-bottom: 1px solid var(--border); }
.header-inner { display: flex; align-items: center; justify-content: space-between; height: var(--header-h); gap: 24px; }
.logo { font-size: 1.4rem; font-weight: 800; letter-spacing: 0.5px; flex-shrink: 0; }
.logo-primary { color: var(--blue); }
.logo-secondary { color: var(--ink); }
.main-nav ul { display: flex; gap: 4px; }
.main-nav a { padding: 10px 14px; border-radius: 6px; font-weight: 500; font-size: 0.94rem; color: var(--ink-soft); transition: background .2s ease, color .2s ease; }
.main-nav a:hover, .main-nav a.active { color: var(--blue); background: var(--blue-light); }
.header-actions { display: flex; align-items: center; gap: 10px; }
.nav-toggle { display: none; background: none; border: 1px solid var(--border); border-radius: 8px; padding: 8px; cursor: pointer; color: var(--ink); }
.mobile-nav { display: none; border-top: 1px solid var(--border); }
.mobile-nav ul { padding: 8px 24px 16px; }
.mobile-nav a { display: block; padding: 12px 0; border-bottom: 1px solid var(--border); }
body.nav-open .mobile-nav { display: block; }

/* Hero */
.hero {
  position: relative; overflow: hidden;
  background-color: #0b1220;
  /* Banner artwork is authored at 1980 × 900 (2.2:1). aspect-ratio keeps that
     exact shape on wide screens so nothing is cropped, while min-height stops
     it collapsing on short/narrow viewports where the ratio would be too flat
     to hold the headline. */
  width: 100%;
  aspect-ratio: 1980 / 900;
  min-height: 460px;
  max-height: 900px;
  /* Slides stack in one grid cell rather than being absolutely positioned, so
     the section is still sized by its tallest slide — absolute positioning
     would collapse the height and clip long copy on narrow screens. */
  display: grid;
}
.hero-slide {
  grid-area: 1 / 1;
  background-size: cover; background-position: center;
  padding: 96px 0 100px;
  display: flex; align-items: center;
  opacity: 0; pointer-events: none;
  transition: opacity .8s ease;
}
.hero-slide.is-active { opacity: 1; pointer-events: auto; }
/* Video banner: fills the slide behind the copy, same scrim as an image. */
.hero-video {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
  pointer-events: none;
}
.hero-slide::after {
  /* extra readability scrim on top of the banner image's own baked-in gradient */
  content: ""; position: absolute; inset: 0; z-index: 0;
  background: linear-gradient(100deg, rgba(5,8,16,0.72) 0%, rgba(5,8,16,0.42) 45%, rgba(5,8,16,0.08) 75%);
}
.hero-inner { position: relative; z-index: 1; display: block; width: 100%; }
.hero-copy { max-width: 640px; }
/* Scoped to the active slide so the copy re-animates each time a slide
   becomes current (the rule starts matching, which restarts the animation). */
.hero-slide.is-active .hero-copy > * { opacity: 0; transform: translateY(16px); animation: heroFadeUp 0.7s cubic-bezier(.16,.8,.24,1) forwards; }

/* ---- Banner carousel controls (only rendered when >1 slide) ---- */
.hero-nav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 3;
  width: 46px; height: 46px; border-radius: 50%;
  border: 1px solid rgba(255,255,255,.35); background: rgba(8,12,22,.4);
  color: #fff; font-size: 1.7rem; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background .2s ease, border-color .2s ease, opacity .2s ease;
  opacity: 0;
}
.hero-carousel:hover .hero-nav, .hero-nav:focus-visible { opacity: 1; }
.hero-nav:hover { background: rgba(8,12,22,.75); border-color: #fff; }
.hero-prev { left: 18px; }
.hero-next { right: 18px; }
.hero-dots {
  position: absolute; z-index: 3; bottom: 22px; left: 0; right: 0;
  display: flex; justify-content: center; gap: 10px;
}
.hero-dot {
  width: 34px; height: 4px; padding: 0; border: none; border-radius: 999px;
  background: rgba(255,255,255,.38); cursor: pointer;
  transition: background .25s ease, width .25s ease;
}
.hero-dot:hover { background: rgba(255,255,255,.65); }
.hero-dot.is-active { background: #fff; width: 48px; }
.hero-copy h1 { color: #fff; animation-delay: .05s; }
.hero-copy .hero-subtitle { animation-delay: .16s; }
.hero-copy .hero-badges { animation-delay: .27s; }
.hero-copy .hero-ctas { animation-delay: .38s; }
.hero-subtitle { font-size: 1.1rem; color: rgba(255,255,255,0.82); max-width: 46ch; }
.hero-badges { display: flex; flex-wrap: wrap; gap: 14px 22px; margin: 18px 0 26px; }
.hero-badges li { display: flex; align-items: center; gap: 6px; font-size: 0.9rem; color: rgba(255,255,255,0.78); font-weight: 500; }
.hero-badges .icon { color: #60a5fa; }
.hero-ctas { display: flex; flex-wrap: wrap; gap: 14px; }
.btn-outline-light { background: transparent; color: #fff; border-color: rgba(255,255,255,0.55); }
.btn-outline-light:hover { border-color: #fff; background: rgba(255,255,255,0.1); }
@keyframes heroFadeUp { to { opacity: 1; transform: translateY(0); } }

/* Sections */
.section { padding: 88px 0; }
.section-alt { background: var(--bg-alt); }
.section-heading { text-align: center; margin-bottom: 52px; }
.section-heading h2 { position: relative; display: inline-block; padding-bottom: 18px; }
.section-heading h2::after { content: ""; position: absolute; left: 50%; bottom: 0; transform: translateX(-50%); width: 68px; height: 3px; background: var(--blue); border-radius: 2px; }
.center-cta { text-align: center; margin-top: 48px; }

/* Scroll-reveal utility — elements fade/rise into view as they enter the
   viewport (see data-reveal init in main.js). Staggered via --reveal-delay,
   set per-element by JS based on position among sibling .reveal items. */
.reveal { opacity: 0; transform: translateY(18px); transition: opacity .6s cubic-bezier(.16,.8,.24,1), transform .6s cubic-bezier(.16,.8,.24,1); transition-delay: var(--reveal-delay, 0s); }
.reveal.is-visible { opacity: 1; transform: translateY(0); }

/* Products grid */
.products-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 28px; }
.product-card { background: #fff; border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; display: flex; flex-direction: column; transition: box-shadow .28s ease, transform .28s ease; }
.product-card:hover { box-shadow: var(--shadow-lg); transform: translateY(-4px); }
/* Square media: a tin photographed head-on is round, so a 1:1 crop shows the
   whole lid where a 4:3 letterbox cropped the top and bottom off it. */
.product-card-media { display: block; aspect-ratio: 1/1; overflow: hidden; background: var(--bg-alt); }
.product-card-media img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s cubic-bezier(.16,.8,.24,1); }
.product-card:hover .product-card-media img { transform: scale(1.07); }
.product-card-body { padding: 24px 24px 28px; display: flex; flex-direction: column; flex: 1; }
.product-card-body h3 { margin-bottom: 14px; font-size: 1.16rem; }
.product-card-body h3 a:hover { color: var(--blue); }
.spec-list { margin-bottom: 22px; }
.spec-list li { display: flex; align-items: center; gap: 8px; font-size: 0.93rem; color: var(--muted); margin-bottom: 7px; }
.spec-list .icon { color: var(--blue); width: 0.95em; height: 0.95em; }
.card-link { color: var(--blue); font-weight: 600; font-size: 0.95rem; display: inline-flex; align-items: center; gap: 5px; margin-top: auto; align-self: flex-start; }
/* The auto margin moved out to the row: with two buttons in it, putting it on
   the link alone stopped bottom-aligning the card and the rows drifted apart
   whenever one product title wrapped to a second line. */
.card-actions { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-top: auto; }
.card-actions .card-link { margin-top: 0; }

/* Feature grid (Why choose us) */
/* gap:0 + a hairline on each card draws the column rules between the four
   reasons. The rule lives on the card (not a separate divider element) so it
   wraps correctly at every breakpoint — the overrides in the media queries
   just clear whichever card starts a row. */
.feature-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0; }
.feature-card { text-align: center; padding: 18px 30px; border-left: 1px solid var(--border); }
.feature-card:nth-child(4n + 1) { border-left: none; }
.feature-icon { width: 112px; height: 112px; border-radius: 50%; background: #fff; border: 1px solid #d7e1f4; color: var(--blue); display: flex; align-items: center; justify-content: center; margin: 0 auto 24px; transition: background .3s ease, border-color .3s ease, color .3s ease, transform .3s ease; }
.feature-card:hover .feature-icon { background: var(--blue); border-color: var(--blue); color: #fff; transform: scale(1.05); }
.feature-icon .icon { width: 62px; height: 62px; }
.feature-card h3 { font-size: 1.2rem; margin-bottom: 10px; }
.feature-card p { color: var(--muted); font-size: 0.95rem; margin: 0 auto; max-width: 34ch; }

/* Applications */
/* Six across, as in the reference — the section grew by making each tile's
   photo square instead of a squat 4:3 strip, not by dropping columns. */
.applications-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 20px; }
.application-tile { display: block; border-radius: var(--radius-lg); overflow: hidden; text-align: center; border: 1px solid var(--border); background: #fff; transition: box-shadow .25s ease, border-color .25s ease, transform .25s ease; }
.application-tile:hover { box-shadow: var(--shadow-lg); border-color: var(--blue); transform: translateY(-3px); }
.application-tile img { width: 100%; aspect-ratio: 1/1; object-fit: cover; transition: transform .5s cubic-bezier(.16,.8,.24,1); }
.application-tile:hover img { transform: scale(1.08); }
.application-tile span { display: block; padding: 18px 8px; font-weight: 600; font-size: 0.98rem; color: var(--ink); transition: color .2s ease; }
.application-tile:hover span { color: var(--blue); }

/* Process */
.process-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 4px; flex-wrap: wrap; }
.process-step { flex: 1 1 150px; text-align: center; min-width: 140px; }
.process-icon { width: 118px; height: 118px; border-radius: 50%; background: #fff; border: 1px solid #d7e1f4; color: var(--blue); display: flex; align-items: center; justify-content: center; margin: 0 auto 22px; transition: background .3s ease, border-color .3s ease, color .3s ease, transform .3s ease; }
/* Without this the glyph falls back to the global `.icon { 1.1em }` and sits
   as an 18px speck inside a 92px circle. */
.process-icon .icon { width: 64px; height: 64px; }
.process-step:hover .process-icon { background: var(--blue); border-color: var(--blue); color: #fff; transform: scale(1.05); }
.process-step h3 { font-size: 1.12rem; margin-bottom: 8px; }
.process-step p { font-size: 0.92rem; color: var(--muted); margin: 0 auto; max-width: 26ch; }
/* Dashed connector: the ::before is the rule, the glyph is its head, and
   margin-top lines the pair up with the middle of the 92px circle. */
.process-arrow { display: flex; align-items: center; color: #94a3b8; margin-top: 48px; flex: 0 0 auto; }
.process-arrow::before { content: ""; width: 30px; border-top: 2px dashed #cbd5e1; }
.process-arrow .icon { width: 20px; height: 20px; margin-left: -5px; }

/* Strength */
/* Five across, as in the reference. The section got taller because each photo
   is now square instead of a short 4:3 crop — not because columns were cut. */
.strength-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 24px; }
.strength-card { transition: transform .3s ease; }
.strength-card:hover { transform: translateY(-4px); }
.strength-card img { width: 100%; border-radius: var(--radius-lg); aspect-ratio: 1/1; object-fit: cover; margin-bottom: 18px; overflow: hidden; transition: box-shadow .3s ease; }
.strength-card:hover img { box-shadow: var(--shadow-lg); }
.strength-card h3 { font-size: 1.08rem; margin-bottom: 8px; }
.strength-card p { font-size: 0.9rem; color: var(--muted); margin: 0; }

/* CTA banner */
.cta-banner {
  background-image: linear-gradient(100deg, rgba(15,23,58,0.55), rgba(29,78,216,0.35)), url('/images/cta-bg.svg');
  background-size: cover; background-position: center; background-color: var(--blue-dark);
  color: #fff; padding: 78px 0;
}
.cta-banner-inner { display: flex; align-items: center; gap: 30px; flex-wrap: wrap; }
.cta-icon { width: 92px; height: 92px; border-radius: 50%; background: #fff; color: var(--blue); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.cta-icon .icon { width: 44px; height: 44px; }
.cta-copy { flex: 1 1 400px; }
.cta-copy h2 { color: #fff; margin-bottom: 6px; }
.cta-copy p { color: rgba(255,255,255,0.85); margin: 0; max-width: 60ch; font-size: 1.05rem; }

/* Footer */
.site-footer { background: #0f172a; color: #cbd5e1; padding: 76px 0 0; }
.footer-grid { display: grid; grid-template-columns: 1.55fr 1.25fr 1fr 1fr 1fr; gap: 36px; padding-bottom: 48px; }
.footer-col h4 { color: #fff; font-size: 1.02rem; margin-bottom: 20px; }
.footer-col ul li { margin-bottom: 12px; }
.footer-col a { color: #94a3b8; font-size: 0.92rem; }
.footer-col a:hover { color: #fff; }
.footer-about p { font-size: 0.9rem; color: #94a3b8; }
/* Contact sits second, right after the brand block — it is the one thing a
   buyer scrolls to the footer looking for. Each row is a badge plus a labelled
   value, so "WhatsApp" and "Address" read as headings rather than guesswork. */
.footer-contact p { display: flex; gap: 14px; align-items: flex-start; margin-bottom: 20px; line-height: 1.5; }
.fc-badge {
  width: 42px; height: 42px; border-radius: 50%; flex-shrink: 0;
  border: 1px solid rgba(255,255,255,0.22); background: rgba(255,255,255,0.05);
  display: flex; align-items: center; justify-content: center; color: #cbd5e1;
}
.fc-badge .icon { width: 1.25rem; height: 1.25rem; }
.fc-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.fc-body b { color: #fff; font-size: 0.92rem; font-weight: 600; }
.fc-body span, .footer-contact a { color: #94a3b8; font-size: 0.88rem; word-break: break-word; }
.footer-contact a:hover { color: #fff; }
/* Two real bugs lived here. (1) `.footer-col a` is (0,1,1) and was beating a
   bare `.logo-footer` (0,1,0), so the footer wordmark had always been rendering
   at link size instead of logo size — scoping it to `.footer-col .logo-footer`
   (0,2,0) wins. (2) `.logo-secondary` is var(--ink) = #0f172a, which is exactly
   the footer background, so the "PACK" half of the wordmark was invisible down
   there. */
.footer-col .logo-footer { display: inline-block; font-size: 2rem; line-height: 1.15; margin-bottom: 18px; }
.site-footer .logo-secondary { color: #ffffff; }
.social-links { display: flex; gap: 10px; margin-top: 20px; }
.social-links a { width: 38px; height: 38px; border-radius: 50%; background: rgba(255,255,255,0.08); display: flex; align-items: center; justify-content: center; font-size: 0.8rem; font-weight: 700; }
.social-links a:hover { background: var(--blue); }
.footer-bottom { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 10px; padding: 20px var(--gutter); border-top: 1px solid rgba(255,255,255,0.08); font-size: 0.82rem; }
.footer-bottom-links { display: flex; gap: 18px; }
.footer-bottom-links a:hover { color: #fff; }

/* Breadcrumbs */
.breadcrumbs { background: var(--bg-alt); border-bottom: 1px solid var(--border); padding: 12px 0; font-size: 0.86rem; color: var(--muted); }
.breadcrumbs a:hover { color: var(--blue); }
.breadcrumbs .sep { margin: 0 6px; }

/* Product detail */
.product-detail { padding: 40px 0 20px; }
.product-detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 44px; }
.pd-gallery-main { position: relative; border-radius: var(--radius-lg); overflow: hidden; border: 1px solid var(--border); aspect-ratio: 4/3; background: var(--bg-alt); cursor: zoom-in; }
.pd-gallery-main img { width: 100%; height: 100%; object-fit: cover; transition: transform .35s cubic-bezier(.16,.8,.24,1), opacity .18s ease; will-change: transform; }
.pd-gallery-main:hover img { transform: scale(1.5); }
.pd-thumbs { display: flex; gap: 10px; margin-top: 12px; }
.pd-thumbs img { width: 76px; height: 76px; object-fit: cover; border-radius: 8px; border: 2px solid var(--border); cursor: pointer; transition: border-color .2s ease, transform .2s ease; }
.pd-thumbs img:hover { transform: translateY(-2px); }
.pd-thumbs img.active { border-color: var(--blue); }

/* Lightbox (opened from the product gallery main image) */
.lightbox-overlay { position: fixed; inset: 0; background: rgba(15, 23, 42, 0.85); display: flex; align-items: center; justify-content: center; padding: 32px; z-index: 1000; opacity: 0; animation: lightboxIn .18s ease forwards; cursor: zoom-out; }
.lightbox-overlay img { max-width: min(90vw, 900px); max-height: 85vh; border-radius: 12px; box-shadow: 0 24px 60px rgba(0,0,0,0.45); cursor: default; }
.lightbox-close { position: absolute; top: 20px; right: 24px; width: 40px; height: 40px; border-radius: 50%; background: rgba(255,255,255,0.14); color: #fff; border: none; font-size: 1.2rem; line-height: 1; cursor: pointer; display: flex; align-items: center; justify-content: center; }
.lightbox-close:hover { background: rgba(255,255,255,0.24); }
@keyframes lightboxIn { to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .lightbox-overlay { animation: none; opacity: 1; }
}
.pd-info h1 { margin-bottom: 10px; }
.pd-specs { margin: 18px 0 22px; }
.pd-specs li { display: flex; gap: 8px; font-size: 0.94rem; margin-bottom: 8px; }
.pd-specs li strong { min-width: 110px; color: var(--ink); }
.pd-ctas { display: flex; gap: 12px; margin-bottom: 24px; flex-wrap: wrap; }
.trust-badges { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; padding: 24px; background: var(--bg-alt); border-radius: var(--radius); margin: 32px 0; }
.trust-badge { text-align: center; font-size: 0.82rem; color: var(--ink-soft); }
.trust-badge .icon-wrap { width: 44px; height: 44px; border-radius: 50%; background: #fff; color: var(--blue); display: flex; align-items: center; justify-content: center; margin: 0 auto 10px; box-shadow: var(--shadow); }
.tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border); margin: 40px 0 28px; overflow-x: auto; }
.tab-btn { padding: 12px 18px; font-weight: 600; font-size: 0.92rem; color: var(--muted); border-bottom: 2px solid transparent; white-space: nowrap; cursor: pointer; background: none; border-top: none; border-left: none; border-right: none; }
.tab-btn.active, .tab-btn:hover { color: var(--blue); border-bottom-color: var(--blue); }
.tab-panel { display: none; }
.tab-panel.active { display: block; }
.spec-table { width: 100%; border-collapse: collapse; }
.spec-table th, .spec-table td { text-align: left; padding: 10px 14px; border-bottom: 1px solid var(--border); font-size: 0.92rem; }
.spec-table th { width: 200px; background: var(--bg-alt); color: var(--ink); font-weight: 600; }
.custom-options-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px; margin-top: 20px; }
.option-card { text-align: center; padding: 20px 12px; border: 1px solid var(--border); border-radius: var(--radius); }
.option-card .icon { width: 30px; height: 30px; color: var(--blue); margin-bottom: 10px; }
.pd-apps-inquiry { display: grid; grid-template-columns: 1.2fr 1fr; gap: 40px; margin-top: 10px; }
.app-mini-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.app-mini-grid img { border-radius: 8px; aspect-ratio: 1; object-fit: cover; }

/* Forms */
.form-field { margin-bottom: 16px; }
.form-field label { display: block; font-weight: 600; font-size: 0.88rem; margin-bottom: 6px; color: var(--ink); }
.form-field input, .form-field textarea, .form-field select {
  width: 100%; padding: 11px 14px; border: 1px solid var(--border); border-radius: 8px;
  font-size: 0.94rem; font-family: inherit; background: #fff; color: var(--ink);
}
.form-field input:focus, .form-field textarea:focus, .form-field select:focus { outline: none; border-color: var(--blue); box-shadow: 0 0 0 3px var(--blue-light); }
.form-card { background: #fff; border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 28px; box-shadow: var(--shadow); }
/* Was an inline style on the contact page. Inline styles outrank media
   queries, so the two-column split survived onto phones and pushed the form
   off the right edge; as a class it can collapse like everything else. */
.contact-grid { display: grid; grid-template-columns: 1fr 1.1fr; gap: 40px; }
.form-note { font-size: 0.82rem; color: var(--muted); margin-top: 10px; }
.form-success { background: #ecfdf5; border: 1px solid #a7f3d0; color: #065f46; padding: 14px 16px; border-radius: 8px; margin-bottom: 16px; }
.form-error { background: #fef2f2; border: 1px solid #fecaca; color: #991b1b; padding: 14px 16px; border-radius: 8px; margin-bottom: 16px; }

/* Related products / news list */
.related-title { margin: 0 0 22px; }

/* News */
.news-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
.news-card { background: #fff; border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; }
.news-card-media img { aspect-ratio: 16/10; object-fit: cover; }
.news-card-body { padding: 18px 20px; }
.news-card-body time { font-size: 0.8rem; color: var(--muted); }
.news-card-body h3 { margin: 6px 0 10px; font-size: 1.05rem; }
.news-card-body p { font-size: 0.9rem; color: var(--muted); }
/* .news-article sits on the SAME element as .container (<article class="news-article
   container">), so a `padding` shorthand here silently overwrites the container's
   horizontal gutter — same specificity, later rule wins. Every article was
   therefore running edge to edge on a phone. Vertical padding only. */
.news-article { max-width: 780px; margin: 0 auto; padding-top: 40px; padding-bottom: 80px; }
.news-article img.cover { border-radius: var(--radius-lg); margin-bottom: 24px; aspect-ratio: 16/9; object-fit: cover; }
.news-article .meta { color: var(--muted); font-size: 0.88rem; margin-bottom: 10px; }
.news-article .content { font-size: 1.02rem; }
.news-article .content p { margin-bottom: 1.2em; }
.tag-list { display: flex; gap: 8px; margin: 20px 0; flex-wrap: wrap; }
.tag-list span { background: var(--blue-light); color: var(--blue); padding: 5px 12px; border-radius: 999px; font-size: 0.8rem; font-weight: 600; }

/* Generic content page */
.page-header { background: var(--bg-alt); padding: 48px 0; text-align: center; border-bottom: 1px solid var(--border); }
.page-header p { max-width: 60ch; margin: 0 auto; color: var(--muted); }
.prose { max-width: 820px; margin: 0 auto; }
.prose p { font-size: 1.02rem; }
.stats-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin-top: 40px; text-align: center; }
.stat-card { background: var(--bg-alt); border-radius: var(--radius); padding: 26px 12px; }
.stat-card .value { font-size: 2rem; font-weight: 800; color: var(--blue); }
.stat-card .label { font-size: 0.86rem; color: var(--muted); margin-top: 4px; }
.cert-list { display: flex; flex-wrap: wrap; gap: 14px; justify-content: center; margin-top: 24px; }
.cert-list span { background: #fff; border: 1px solid var(--border); padding: 10px 18px; border-radius: 8px; font-weight: 600; color: var(--ink); }

/* Product filter bar */
.filter-bar { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 32px; }
.filter-chip { padding: 8px 16px; border-radius: 999px; border: 1px solid var(--border); font-size: 0.88rem; font-weight: 600; color: var(--ink-soft); }
.filter-chip.active, .filter-chip:hover { border-color: var(--blue); color: var(--blue); background: var(--blue-light); }
.empty-state { text-align: center; padding: 60px 20px; color: var(--muted); }

/* Pagination */
.pagination { display: flex; justify-content: center; gap: 8px; margin-top: 40px; }
.pagination a, .pagination span { padding: 8px 14px; border-radius: 8px; border: 1px solid var(--border); font-weight: 600; font-size: 0.88rem; }
.pagination a:hover { border-color: var(--blue); color: var(--blue); }
.pagination .current { background: var(--blue); color: #fff; border-color: var(--blue); }

/* Reduced motion — disable all decorative animation added above; content
   remains fully visible and interactive, it just stops moving. */
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  .hero-copy > *, .hero-slide.is-active .hero-copy > * { animation: none !important; opacity: 1; transform: none; }
  /* Carousel still switches slides (JS honours reduced-motion by not
     auto-rotating), but the cross-fade itself is dropped. */
  .hero-slide { transition: none; }
  .btn, .btn:hover, .btn:active { transform: none; }
  .product-card:hover, .feature-card:hover, .strength-card:hover, .application-tile:hover,
  .pd-thumbs img:hover, .pd-gallery-main:hover img { transform: none; }
  * { scroll-behavior: auto !important; }
}

/* Responsive */
@media (max-width: 1080px) {
  .products-grid { grid-template-columns: repeat(2, 1fr); }
  .feature-grid { grid-template-columns: repeat(2, 1fr); }
  /* two-up: restore the rule on every card, then clear it on whichever card
     starts a row, and add a horizontal rule above the second row. */
  .feature-card:nth-child(4n + 1) { border-left: 1px solid var(--border); }
  .feature-card:nth-child(2n + 1) { border-left: none; }
  .feature-card:nth-child(n + 3) { border-top: 1px solid var(--border); padding-top: 34px; }
  .applications-grid { grid-template-columns: repeat(3, 1fr); }
  .strength-grid { grid-template-columns: repeat(3, 1fr); }
  .custom-options-grid { grid-template-columns: repeat(3, 1fr); }
  .footer-grid { grid-template-columns: repeat(3, 1fr); }
  .news-grid { grid-template-columns: repeat(2, 1fr); }
  .stats-row { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 1024px) {
  .main-nav, .header-actions .btn-primary { display: none; }
  .nav-toggle { display: inline-flex; }
}
@media (max-width: 860px) {
  .contact-grid { grid-template-columns: 1fr; gap: 28px; }
  .hero { aspect-ratio: auto; min-height: 440px; max-height: none; }
  .hero-slide { padding: 72px 0 80px; }
  .hero-nav { width: 38px; height: 38px; font-size: 1.4rem; opacity: 1; }
  .hero-prev { left: 8px; }
  .hero-next { right: 8px; }
  .product-detail-grid, .pd-apps-inquiry { grid-template-columns: 1fr; gap: 28px; }
  .trust-badges { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
  /* Phones get the tighter rhythm back — the desktop sizes above are what the
     wide layout needs, not what a 390px screen needs. */
  .section { padding: 56px 0; }
  .section-heading { margin-bottom: 36px; }
  .center-cta { margin-top: 32px; }
  .products-grid, .applications-grid, .strength-grid, .custom-options-grid, .news-grid { grid-template-columns: repeat(2, 1fr); }
  .product-card-body { padding: 16px 16px 20px; }
  .product-card-body h3 { font-size: 1rem; margin-bottom: 10px; }
  .spec-list li { font-size: 0.84rem; margin-bottom: 5px; }
  .application-tile span { padding: 14px 8px; font-size: 0.95rem; }
  .strength-card h3 { font-size: 1.02rem; }
  .strength-card p { font-size: 0.86rem; }
  .feature-grid, .stats-row { grid-template-columns: 1fr; }
  .feature-card { border-left: none; padding: 30px 8px; }
  .feature-card:nth-child(4n + 1), .feature-card:nth-child(2n + 1) { border-left: none; }
  .feature-card:nth-child(n + 2) { border-top: 1px solid var(--border); }
  .feature-card:first-child { border-top: none; }
  .feature-icon { width: 96px; height: 96px; }
  .feature-icon .icon { width: 52px; height: 52px; }
  .process-icon { width: 96px; height: 96px; }
  .process-icon .icon { width: 52px; height: 52px; }
  /* Link columns pair up rather than forming one long stack — five columns
     end to end is a lot of thumb-scrolling past the real content. */
  .footer-grid { grid-template-columns: 1fr 1fr; gap: 30px 24px; text-align: left; }
  .footer-about, .footer-contact { grid-column: 1 / -1; }
  .process-row { flex-direction: column; align-items: stretch; }
  .process-arrow { display: none; }
  .cta-banner { padding: 56px 0; }
  .cta-banner-inner { text-align: center; justify-content: center; }
}

/* ---- Admin ---- */
.admin-body { background: var(--bg-alt); min-height: 100vh; }
.admin-shell { display: grid; grid-template-columns: 240px 1fr; min-height: 100vh; }
.admin-sidebar { background: #14181f; color: #cbd5e1; padding: 22px 0; }
.admin-sidebar .logo { color: #fff; margin-bottom: 12px; display: block; padding: 0 20px; }
.admin-sidebar nav { border-top: 1px solid rgba(255,255,255,0.06); }
.admin-sidebar nav a {
  display: flex; align-items: center; gap: 12px; padding: 15px 20px;
  font-size: 0.92rem; color: #8b93a3; text-decoration: none;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  border-left: 3px solid transparent;
  transition: background .18s ease, color .18s ease, border-color .18s ease;
}
.admin-sidebar nav a:hover { background: rgba(255,255,255,0.04); color: #fff; }
.admin-sidebar nav a.active { background: rgba(255,255,255,0.06); color: #fff; border-left-color: #d97757; }
.admin-sidebar nav a .icon { flex-shrink: 0; width: 18px; height: 18px; transition: transform .18s ease; }
.admin-sidebar nav a:hover .icon { transform: scale(1.1); }
.admin-sidebar nav a .nav-label { display: flex; align-items: baseline; gap: 7px; }
.admin-sidebar nav a .nav-label b { font-weight: 700; }
.admin-sidebar nav a .nav-label-en { font-size: 0.78rem; color: #64748b; font-weight: 400; }
.admin-sidebar nav a.active .nav-label-en { color: #94a3b8; }
.admin-sidebar .logo, .admin-sidebar > div { padding-left: 20px; padding-right: 20px; }
.admin-sidebar .nav-group { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em; color: #64748b; margin: 18px 0 6px; padding: 0 20px; }
.admin-main { padding: 28px 32px 60px; max-width: 1200px; }
.admin-topbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; flex-wrap: wrap; gap: 12px; }
.admin-topbar h1 { margin: 0; font-size: 1.5rem; }
.admin-card { background: #fff; border: 1px solid var(--border); border-radius: var(--radius); padding: 22px 24px; margin-bottom: 22px; }
.admin-card h2 { font-size: 1.05rem; margin-bottom: 16px; }
.stat-tiles { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 24px; }
.stat-tile { background: #fff; border: 1px solid var(--border); border-radius: var(--radius); padding: 18px 20px; transition: box-shadow .2s ease, transform .2s ease; }
.stat-tile:hover { box-shadow: var(--shadow); transform: translateY(-2px); }
.stat-tile .label { font-size: 0.8rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
.stat-tile .value { font-size: 1.8rem; font-weight: 800; color: var(--ink); margin-top: 4px; font-variant-numeric: tabular-nums; }
.stat-tile .delta { font-size: 0.78rem; margin-top: 4px; }
.stat-tile .delta.up { color: #16a34a; }
.stat-tile .delta.down { color: #dc2626; }
/* .admin-main is a grid item; without min-width:0 a wide table stretches the
   whole column past the window and the page scrolls sideways at laptop
   widths too, not just on phones. The table scrolls inside its own card
   instead — the standard responsive-table pattern, applied at every width
   rather than only under a breakpoint. */
.admin-main, .admin-card { min-width: 0; }
/* The CARD scrolls, not the table: making the table itself display:block
   would shrink-to-fit its content and stop filling the card on a wide
   screen. */
.admin-card { overflow-x: auto; }
.admin-table { width: 100%; border-collapse: collapse; }
.admin-table th, .admin-table td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--border); font-size: 0.88rem; }
.admin-table th { color: var(--muted); font-weight: 600; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.03em; }
/* No transition here: on a 150-row table it is 900 cells the browser
   has to keep animatable for a hover effect you see on one of them. */
.admin-table tr:hover td { background: var(--bg-alt); }
.badge { display: inline-block; padding: 3px 10px; border-radius: 999px; font-size: 0.74rem; font-weight: 700; white-space: nowrap; }
.badge-green { background: #dcfce7; color: #166534; }
.badge-gray { background: #f1f5f9; color: #475569; }
.badge-blue { background: var(--blue-light); color: var(--blue); }
.badge-amber { background: #fef3c7; color: #92400e; }
.login-shell { min-height: 100vh; display: flex; align-items: center; justify-content: center; background: linear-gradient(140deg, var(--blue-dark), var(--blue)); padding: 20px; }
.login-card { background: #fff; border-radius: var(--radius-lg); padding: 40px 36px; width: 100%; max-width: 380px; box-shadow: var(--shadow-lg); opacity: 0; transform: translateY(14px) scale(.98); animation: loginCardIn .5s cubic-bezier(.16,.8,.24,1) forwards; }
.login-card h1 { text-align: center; font-size: 1.3rem; }
.login-card .logo { display: block; text-align: center; margin-bottom: 8px; font-size: 1.6rem; }
@keyframes loginCardIn { to { opacity: 1; transform: translateY(0) scale(1); } }
.admin-actions { display: flex; gap: 8px; }
.admin-actions a, .admin-actions button { font-size: 0.8rem; padding: 6px 12px; }
.section-tabs { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 20px; }
.section-tab { padding: 8px 14px; border-radius: 8px; border: 1px solid var(--border); background: #fff; font-size: 0.84rem; font-weight: 600; color: var(--ink-soft); transition: all .18s ease; }
.section-tab.active { background: var(--blue); color: #fff; border-color: var(--blue); }
.repeater-item { border: 1px solid var(--border); border-radius: 8px; padding: 14px 16px; margin-bottom: 12px; background: var(--bg-alt); transition: opacity .22s ease, transform .22s ease, max-height .22s ease; }
.repeater-item-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
.repeater-item-head button { transition: background .15s ease, color .15s ease, border-color .15s ease; }
.field-row { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
.toolbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 18px; flex-wrap: wrap; gap: 12px; }
.chart-wrap { overflow-x: auto; }
.chart-wrap svg rect { transition: fill-opacity .15s ease; }
.chart-wrap svg rect:hover { fill-opacity: 1; }
.ai-box { background: var(--blue-light); border: 1px dashed var(--blue); border-radius: 8px; padding: 16px; margin-bottom: 16px; }
.helper-text { font-size: 0.8rem; color: var(--muted); margin-top: 4px; }
.upload-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 12px; }
.upload-tile { border: 1px solid var(--border); border-radius: 8px; overflow: hidden; background: #fff; transition: box-shadow .2s ease, transform .2s ease; }
.upload-tile:hover { box-shadow: var(--shadow-lg); transform: translateY(-2px); }
.upload-tile img { aspect-ratio: 1; object-fit: cover; }
.upload-tile .path { font-size: 0.7rem; color: var(--muted); padding: 6px 8px; word-break: break-all; }

/* Admin content entrance — cards/tiles settle in on page load, staggered by
   position so a dense dashboard doesn't feel like it "jumps" into place.
   Table rows and upload tiles are deliberately NOT in this list: a list of
   150 enquiries or 200 photos meant 150-200 elements animating opacity and
   transform at once, which is exactly what made a full inbox feel sluggish
   to open. The cards around them still settle in, so the page reads the
   same. */
.admin-main > *,
.stat-tiles > * {
  animation: adminSettle .38s cubic-bezier(.16,.8,.24,1) both;
}
.admin-main > *:nth-child(1) { animation-delay: 0s; }
.admin-main > *:nth-child(2) { animation-delay: .04s; }
.admin-main > *:nth-child(3) { animation-delay: .08s; }
.admin-main > *:nth-child(4) { animation-delay: .12s; }
.admin-main > *:nth-child(n+5) { animation-delay: .16s; }
.stat-tiles > *:nth-child(1) { animation-delay: .03s; }
.stat-tiles > *:nth-child(2) { animation-delay: .07s; }
.stat-tiles > *:nth-child(3) { animation-delay: .11s; }
.stat-tiles > *:nth-child(4) { animation-delay: .15s; }
@keyframes adminSettle { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }

/* Flash banners (save confirmations, form errors) slide in at the top of the page */
.admin-main .form-success, .admin-main .form-error { animation: flashIn .3s cubic-bezier(.16,.8,.24,1) both; }
@keyframes flashIn { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: translateY(0); } }

/* Repeater add/remove transition hooks (toggled via JS in admin.js) */
.repeater-item.is-leaving { opacity: 0; transform: scale(.97); }
.repeater-item.is-entering { opacity: 0; transform: translateY(-6px); }

/* Inline spinner for the AI-generate button's loading state */
.spinner { display: inline-block; width: 13px; height: 13px; border: 2px solid rgba(29,78,216,0.25); border-top-color: var(--blue); border-radius: 50%; animation: spin .7s linear infinite; vertical-align: -2px; margin-right: 6px; }
@keyframes spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .login-card { animation: none; opacity: 1; transform: none; }
  .admin-main > *, .stat-tiles > * { animation: none; opacity: 1; transform: none; }
  .admin-main .form-success, .admin-main .form-error { animation: none; opacity: 1; transform: none; }
  .admin-sidebar nav a:hover { transform: none; }
  .stat-tile:hover, .upload-tile:hover { transform: none; }
  .spinner { animation: none; border-top-color: rgba(29,78,216,0.25); }
}

/* ---- Mobile admin ----
   Under 900px the sidebar becomes a fixed off-canvas drawer opened by the
   hamburger in .admin-mobile-bar. It used to be display:none, which left the
   admin with no navigation at all on a phone — you could log in and then go
   nowhere. */
.admin-mobile-bar { display: none; }
.admin-backdrop { display: none; }

@media (max-width: 900px) {
  .admin-mobile-bar {
    display: flex; align-items: center; gap: 12px;
    position: sticky; top: 0; z-index: 120;
    background: #14181f; color: #fff; padding: 10px 14px;
  }
  .admin-mobile-title { font-size: 0.9rem; font-weight: 600; flex: 1; }
  .admin-mobile-title strong { color: var(--blue); }
  .admin-mobile-view { color: #94a3b8; font-size: 0.78rem; white-space: nowrap; }
  .admin-nav-toggle {
    width: 38px; height: 34px; flex: 0 0 auto;
    display: flex; flex-direction: column; justify-content: center; gap: 5px;
    background: transparent; border: 1px solid rgba(255,255,255,.25);
    border-radius: 7px; padding: 0 8px; cursor: pointer;
  }
  .admin-nav-toggle span { display: block; height: 2px; background: #fff; border-radius: 2px; }

  .admin-shell { grid-template-columns: 1fr; }
  /* The off-canvas drawer is translated off to the left but still counts
     toward document scrollWidth, which would let the whole admin scroll
     sideways on a phone. Clip it at the root. */
  html:has(.admin-body), .admin-body { overflow-x: hidden; }
  .admin-sidebar {
    position: fixed; top: 0; bottom: 0; left: 0; width: 270px;
    z-index: 200; overflow-y: auto; -webkit-overflow-scrolling: touch;
    transform: translateX(-100%); transition: transform .25s ease;
    padding: 18px 0 28px;
  }
  body.admin-nav-open .admin-sidebar { transform: none; }
  body.admin-nav-open { overflow: hidden; }
  .admin-backdrop {
    display: block; position: fixed; inset: 0; z-index: 150;
    background: rgba(3,7,18,.55);
  }
  .admin-backdrop[hidden] { display: none; }

  /* .admin-main is a grid item, so its default min-width:auto lets one wide
     child (a table, a 4-across tile grid) push the whole column past the
     screen — the page then renders at 600px+ inside a 390px viewport and the
     right-hand side of every heading is simply cut off. */
  .admin-main { padding: 18px 14px 40px; min-width: 0; }
  .admin-card { min-width: 0; }
  .admin-topbar { align-items: flex-start; }
  .admin-topbar h1 { font-size: 1.2rem; }
  .stat-tiles { grid-template-columns: repeat(2, 1fr); }
  /* Two-column layouts in the dashboard / forms stack on a phone */
  .admin-main > div[style*="grid-template-columns:1fr 1fr"] { grid-template-columns: 1fr !important; }
  .field-row { grid-template-columns: 1fr; }
  .admin-card { padding: 16px 14px; }
  /* Wide tables scroll inside their own container instead of the page.
     Descendant, not child: some pages wrap their table in a div, and a
     child-only selector quietly missed those. */
  .admin-card table.admin-table { white-space: nowrap; }
  /* A short key/value table has nothing to gain from scrolling — it just
     needs permission to wrap. */
  .admin-card table.admin-table.wrap { white-space: normal; }
  .admin-card table.admin-table.wrap th,
  .admin-card table.admin-table.wrap td { width: auto !important; word-break: break-word; }
  .admin-actions { flex-wrap: wrap; }
  .image-field-row { flex-direction: column; align-items: stretch; }
  .image-upload-btn { justify-content: center; }
  .richtext-toolbar { gap: 3px; padding: 6px; }
  .rt-btn { min-width: 30px; height: 30px; padding: 0 6px; }
  .richtext-toolbar .rt-select { height: 30px; font-size: 0.74rem; }
  .richtext-editor { min-height: 200px; padding: 12px; }
}

/* Inline "upload from my computer" image widget — a text path input plus a
   real file-upload button, used on the hero/applications/products/news
   forms. The file input itself is visually hidden; the <label> is the
   button (this is the standard accessible pattern for a styled upload
   control). */
.image-field-row { display: flex; gap: 8px; flex-wrap: wrap; }
.image-field-row input[type="text"] { flex: 1 1 220px; }
.image-upload-btn { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; white-space: nowrap; flex: 0 0 auto; }
.image-upload-btn input[type="file"] { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); }
.image-upload-status { margin-left: 4px; }
.image-preview { margin-top: 10px; }
.image-preview img, .image-preview video { max-width: 200px; max-height: 130px; object-fit: cover; border-radius: 8px; border: 1px solid var(--border); display: block; background: #0b1220; }

/* Rich-text editor (news article body) — a contenteditable surface plus a
   small toolbar, degrading gracefully to the plain <textarea> it wraps if
   JavaScript never runs (see initRichText() in admin.js). */
.richtext-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; padding: 8px; border: 1px solid var(--border); border-bottom: none; border-radius: 8px 8px 0 0; background: var(--bg-alt); }
.rt-btn { min-width: 32px; height: 32px; padding: 0 8px; border: 1px solid transparent; border-radius: 6px; background: transparent; color: var(--ink); font-size: 0.85rem; cursor: pointer; transition: background .15s ease, border-color .15s ease; }
.rt-btn:hover { background: #fff; border-color: var(--border); }
/* Scoped as `.richtext-toolbar .rt-select` (specificity 0,2,0) on purpose:
   the toolbar sits inside .form-field, and `.form-field select` (0,1,1)
   would otherwise win and stretch each dropdown to a full row, breaking the
   toolbar onto three lines. */
.richtext-toolbar .rt-select {
  width: auto; flex: 0 0 auto; height: 32px; padding: 0 24px 0 8px;
  border: 1px solid var(--border); border-radius: 6px;
  font-size: 0.8rem; line-height: 1.2; background: #fff; color: var(--ink);
  box-shadow: none;
}
.rt-sep { width: 1px; height: 22px; background: var(--border); margin: 0 4px; }
.richtext-editor { min-height: 260px; padding: 16px; border: 1px solid var(--border); border-radius: 0 0 8px 8px; font-size: 0.95rem; line-height: 1.7; color: var(--ink); }
.richtext-editor:focus { outline: none; border-color: var(--blue); box-shadow: 0 0 0 3px var(--blue-light); }
.richtext-editor h2, .richtext-editor h3 { margin: 0.6em 0 0.3em; }
.richtext-editor blockquote { margin: 0.6em 0; padding-left: 14px; border-left: 3px solid var(--border); color: var(--muted); }
.richtext-editor a { color: var(--blue); text-decoration: underline; }
.richtext-editor ul, .richtext-editor ol { padding-left: 1.4em; margin: 0.5em 0; }
.richtext-editor img { max-width: 100%; height: auto; border-radius: 6px; display: block; }
.richtext-editor figure { margin: 14px 0; }
.richtext-editor figcaption { margin-top: 6px; font-size: 0.82rem; color: var(--muted); text-align: center; }
/* The 图片 button is a <label> wrapping a hidden file input, so it needs the
   .rt-btn look plus the flex centring a real <button> gets for free. */
.rt-upload-btn { display: inline-flex; align-items: center; gap: 5px; }
.rt-upload-btn .icon { width: 15px; height: 15px; }
.richtext-toolbar .rt-color-select { min-width: 118px; }
.rt-upload-status { margin-left: 6px; color: var(--blue); }
[data-richtext-source] { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.82rem; }


/* ---- Analytics dashboard widgets ---- */
.stat-tile .stat-sub { font-size: 0.74rem; color: var(--muted); margin-top: 3px; }
.analytics-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; }
.analytics-grid .admin-card { margin-bottom: 0; }

/* Ranked breakdown: one hue for every row — each row is the same measure, so
   a per-row colour would encode nothing. */
.rank-list { display: flex; flex-direction: column; gap: 9px; }
.rank-row { display: grid; grid-template-columns: minmax(0, 1fr) 90px auto; align-items: center; gap: 10px; }
.rank-label {
  font-size: 0.85rem; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rank-track { height: 7px; border-radius: 999px; background: var(--bg-alt); overflow: hidden; }
.rank-bar { height: 100%; border-radius: 999px; background: var(--blue); min-width: 3px; }
.rank-val {
  font-size: 0.8rem; color: var(--ink-soft); white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.rank-unit { color: var(--muted); margin: 0 2px 0 1px; }
.rank-pct { color: var(--muted); margin-left: 6px; }

.split-wrap { display: flex; flex-direction: column; gap: 10px; }
.split-bar { display: flex; height: 14px; border-radius: 999px; overflow: hidden; background: var(--bg-alt); }
.split-bar > div { height: 100%; }
.split-legend { display: flex; flex-wrap: wrap; gap: 6px 18px; font-size: 0.82rem; color: var(--ink-soft); }
.split-legend i { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: 6px; vertical-align: -1px; }
.split-legend b { font-variant-numeric: tabular-nums; }

@media (max-width: 900px) {
  .analytics-grid { grid-template-columns: 1fr; gap: 16px; }
  .rank-row { grid-template-columns: minmax(0, 1fr) 60px auto; }
}

/* ---- SEO 体检 ---- */
/* Severity is carried by the badge's words as well as its colour — a stripe
   alone would be unreadable to anyone who can't separate the two hues. */
.seo-list { display: flex; flex-direction: column; gap: 12px; margin-bottom: 22px; }
.seo-item {
  display: flex; gap: 16px; align-items: flex-start;
  background: #fff; border: 1px solid var(--border); border-left: 4px solid var(--border);
  border-radius: var(--radius); padding: 18px 20px;
}
.seo-item .seo-badge {
  flex: 0 0 auto; font-size: 0.78rem; font-weight: 700; padding: 5px 10px;
  border-radius: 999px; white-space: nowrap; margin-top: 2px;
}
.seo-blocker { border-left-color: #b91c1c; }
.seo-blocker .seo-badge { background: #fee2e2; color: #991b1b; }
.seo-warn { border-left-color: #b45309; }
.seo-warn .seo-badge { background: #fef3c7; color: #92400e; }
.seo-info { border-left-color: #1d4ed8; }
.seo-info .seo-badge { background: var(--blue-light); color: var(--blue-dark); }
.seo-body { min-width: 0; }
.seo-body h3 { font-size: 1.02rem; margin: 0 0 6px; }
.seo-body p { margin: 0 0 8px; font-size: 0.92rem; color: var(--ink-soft); line-height: 1.65; }
.seo-body p:last-child { margin-bottom: 0; }
.seo-fix { color: var(--muted) !important; }
.seo-fix b { color: var(--ink); }
.seo-steps { list-style: decimal; padding-left: 22px; margin: 0; }
.seo-steps li { margin-bottom: 12px; line-height: 1.7; font-size: 0.94rem; }
.seo-steps code { background: var(--bg-alt); padding: 2px 7px; border-radius: 5px; font-size: 0.88em; }
.seo-done { list-style: none; padding: 0; margin: 0; }
.seo-done li { position: relative; padding-left: 26px; margin-bottom: 10px; font-size: 0.93rem; line-height: 1.6; }
.seo-done li::before { content: "✓"; position: absolute; left: 0; top: 0; color: #15803d; font-weight: 700; }
@media (max-width: 640px) {
  .seo-item { flex-direction: column; gap: 10px; }
}
/* Status pills — the word carries the meaning; the colour only reinforces it. */
.pill { display: inline-block; padding: 3px 10px; border-radius: 999px; font-size: 0.8rem; font-weight: 600; background: var(--bg-alt); color: var(--ink-soft); border: 1px solid var(--border); }
.pill-ok { background: #dcfce7; color: #15803d; border-color: #bbf7d0; }
.pill-warn { background: #fef3c7; color: #92400e; border-color: #fde68a; }
/* A checkbox belongs beside its label, not at the far end of a block-level
   one — the default .form-field label is display:block, which strands it. */
.form-field label.check-row { display: flex; align-items: center; gap: 10px; cursor: pointer; font-weight: 600; }
.form-field label.check-row input[type="checkbox"] { width: 17px; height: 17px; margin: 0; flex: 0 0 auto; accent-color: var(--blue); }

/* ---- Trust strip (under the banner) ---- */
/* Four numbers, evenly weighted — this is a row of facts, not a hierarchy, so
   nothing here competes for emphasis with the banner above it. */
.trust-bar { background: var(--bg-alt); border-bottom: 1px solid var(--border); }
.trust-bar-inner { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0; }
.trust-stat { text-align: center; padding: 26px 16px; border-left: 1px solid var(--border); }
.trust-stat:first-child { border-left: none; }
.trust-value { font-size: clamp(1.5rem, 2.2vw, 2.1rem); font-weight: 800; color: var(--blue); line-height: 1.15; font-variant-numeric: tabular-nums; }
.trust-label { font-size: 0.9rem; color: var(--muted); margin-top: 6px; }

/* ---- Quick inquiry (home page) ---- */
.quick-inquiry { background: var(--bg-alt); }
.quick-inquiry-grid { display: grid; grid-template-columns: 1fr 1.05fr; gap: 56px; align-items: start; }
.quick-inquiry-copy h2 { margin-bottom: 14px; }
.quick-lead { font-size: 1.06rem; color: var(--ink-soft); max-width: 46ch; }
.quick-points { margin: 22px 0 26px; display: grid; gap: 12px; }
.quick-points li { display: flex; align-items: flex-start; gap: 10px; color: var(--ink-soft); }
.quick-points .icon { color: var(--blue); flex-shrink: 0; margin-top: 5px; }
.quick-direct { display: flex; flex-wrap: wrap; gap: 12px 26px; padding-top: 20px; border-top: 1px solid var(--border); }
.quick-direct a { display: inline-flex; align-items: center; gap: 8px; font-weight: 600; color: var(--ink); }
.quick-direct a:hover { color: var(--blue); }
.quick-direct .icon { color: var(--blue); }
.quick-inquiry-form { background: #fff; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px; }

/* ---- Floating contact launcher ---- */
/* Fixed rather than sticky so it survives every scroll container, and above
   the lightbox's backdrop but below its image. */
.fab-stack { position: fixed; right: 18px; bottom: 18px; z-index: 90; display: flex; flex-direction: column; gap: 10px; }
.fab {
  display: inline-flex; align-items: center; gap: 9px;
  height: 48px; padding: 0 18px 0 15px; border-radius: 999px;
  color: #fff; font-weight: 600; font-size: 0.92rem; white-space: nowrap;
  box-shadow: 0 6px 20px rgba(15, 23, 42, 0.22);
  transition: transform .18s ease, box-shadow .18s ease;
}
.fab:hover { transform: translateY(-2px); box-shadow: 0 10px 26px rgba(15, 23, 42, 0.3); }
.fab .icon { width: 1.25rem; height: 1.25rem; flex-shrink: 0; }
.fab-wa { background: #25d366; }
.fab-mail { background: var(--blue); }

@media (max-width: 900px) {
  .quick-inquiry-grid { grid-template-columns: 1fr; gap: 32px; }
}
@media (max-width: 640px) {
  .trust-bar-inner { grid-template-columns: repeat(2, 1fr); }
  /* Two-up: the third and fourth need a rule above them, and the third starts
     a row so it must lose its left border. */
  .trust-stat:nth-child(2n + 1) { border-left: none; }
  .trust-stat:nth-child(n + 3) { border-top: 1px solid var(--border); }
  .trust-stat { padding: 20px 10px; }
  .form-row { grid-template-columns: 1fr; }
  /* On a phone the labels would eat the screen; the icons alone are understood. */
  .fab-stack { right: 12px; bottom: 12px; gap: 8px; }
  .fab { height: 46px; width: 46px; padding: 0; justify-content: center; }
  .fab span { display: none; }
}
/* A field that needs the whole width of the two-column editor row. */
.form-field.field-full { grid-column: 1 / -1; }
.repeater-item-tools { display: inline-flex; gap: 6px; }
.repeater-item-tools .btn { padding: 6px 11px; line-height: 1; }

/* --- 询盘列表与详情 --------------------------------------------------- */
/* The list column shows one line of the message and nothing more; the cell
   is allowed to be the flexible one so the fixed columns keep their width. */
.cell-preview {
  max-width: 320px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--muted);
}
.admin-table tr.row-unread td { background: #f8fbff; }
.admin-table tr.row-unread:hover td { background: var(--bg-alt); }

.detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 14px 28px;
}
.detail-label {
  color: var(--muted);
  font-size: 0.74rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 4px;
}
.detail-value { font-size: 0.95rem; word-break: break-word; }

/* The buyer's own line breaks are information — a paragraph break between a
   spec list and a question should survive into the admin. */
.inquiry-message {
  white-space: pre-wrap;
  line-height: 1.75;
  font-size: 0.95rem;
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px 20px;
}
@media (max-width: 640px) {
  .cell-preview { max-width: 200px; }
}


/* Pager for long admin lists (素材库). */
.pager { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; flex-wrap: wrap; }
.pager-info { color: var(--muted); font-size: 0.85rem; }
.list-search { display: flex; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; }
.list-search input[type="search"] {
  flex: 1 1 260px; min-width: 0; padding: 8px 12px;
  border: 1px solid var(--border); border-radius: 8px; font-size: 0.88rem;
}

/* --- 手机端：把数据表格变成一行一张卡片 -------------------------------
   A seven-column table on a 390px screen shows three columns and hides the
   buttons off the right edge, so every single action needs a sideways scroll
   first. Below 680px the marked tables (.stacked) become one card per record
   instead: every field on its own line with its column name above it, and the
   buttons in a full-width row at the bottom of the card. The column names
   come from data-label on each <td>, because CSS cannot reach the <th>. */
@media (max-width: 680px) {
  .admin-card table.admin-table.stacked {
    display: block;
    overflow: visible;
    white-space: normal;
  }
  .admin-table.stacked thead { display: none; }
  .admin-table.stacked tbody,
  .admin-table.stacked tr,
  .admin-table.stacked td { display: block; width: 100%; }
  .admin-table.stacked tr {
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 14px 16px;
    margin-bottom: 12px;
    background: #fff;
  }
  .admin-table.stacked tr.row-unread { background: #f8fbff; border-color: #bfd7ff; }
  /* Hover is meaningless on touch and would repaint the card grey. */
  .admin-table.stacked tr:hover td,
  .admin-table.stacked tr td { background: transparent; }
  .admin-table.stacked td {
    border: none;
    padding: 5px 0;
    font-size: 0.92rem;
    word-break: break-word;
  }
  .admin-table.stacked td[data-label]::before {
    content: attr(data-label);
    display: block;
    font-size: 0.68rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--muted);
    margin-bottom: 3px;
  }
  /* An empty cell on a card is a stray label with nothing under it. */
  .admin-table.stacked td:empty { display: none; }
  .admin-table.stacked td.cell-thumb { padding-bottom: 10px; }
  .admin-table.stacked td.cell-thumb img { width: 84px !important; height: 63px !important; }
  .admin-table.stacked td.cell-preview {
    max-width: none;
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  /* Buttons get a real row of their own, each one a comfortable thumb target. */
  .admin-table.stacked td.admin-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    margin-top: 10px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
  }
  .admin-table.stacked td.admin-actions::before { display: none; }
  .admin-table.stacked td.admin-actions > * { flex: 1 1 96px; }
  .admin-table.stacked td.admin-actions .btn {
    display: flex; align-items: center; justify-content: center;
    min-height: 40px; width: 100%; font-size: 0.85rem;
  }

  /* The first real field of a card is its title: the name or headline is
     what you scan for, so it does not need a caption above it saying so. */
  .admin-table.stacked td.cell-title::before { display: none; }
  .admin-table.stacked td.cell-title {
    font-size: 1.02rem;
    font-weight: 600;
    padding-bottom: 8px;
  }

  /* Everything you tap should be at least a finger wide. */
  .admin-main .btn-sm { min-height: 38px; display: inline-flex; align-items: center; }
  .admin-main .repeater-item-tools .btn { min-width: 44px; justify-content: center; }
  /* The repeater's title and its "+ 添加项目" button fight for one line at
     390px, and the title loses — it wraps under the button. Stack them. */
  .repeater-item-head { flex-wrap: wrap; gap: 8px; }
  .admin-card > .repeater-item-head { align-items: flex-start; }
  .admin-card > .repeater-item-head > strong { flex: 1 1 100%; }

  .pager { justify-content: space-between; }
  .pager .btn { min-height: 38px; }
  /* Search field and its button stay on one line — a full-width 搜索 button
     on its own row reads like the page's main action, which it is not. */
  .list-search input[type="search"] { flex: 1 1 140px; min-height: 40px; }
  .list-search .btn { flex: 0 0 auto; justify-content: center; }

  /* A read-only table that stays a table still needs to say so. */
  .admin-card table.admin-table:not(.stacked) { position: relative; }
  .table-scroll-hint { display: block; color: var(--muted); font-size: 0.75rem; margin-bottom: 6px; }
}
.table-scroll-hint { display: none; }

/* Stacking .image-field-row on a phone turns its flex axis vertical, and the
   `flex: 1 1 220px` that gives the path input its width on a desktop then
   becomes a 220px-TALL text box on the phone — a huge empty rectangle where
   a one-line field belongs. This has to sit after the base rule (same
   specificity, later wins), not inside the earlier media block. */
@media (max-width: 900px) {
  .image-field-row input[type="text"] { flex: 0 0 auto; }
}

/* AI writer: the rotation of article shapes. */
.angle-list { display: flex; flex-wrap: wrap; gap: 8px; }
.angle-chip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 13px; border: 1px solid var(--border); border-radius: 999px;
  font-size: 0.85rem; color: var(--muted); background: var(--bg-alt);
}
.angle-chip.is-next { border-color: var(--blue); color: var(--ink); background: #fff; }

/* --- Article: the parts written to be quoted ------------------------- */
/* The direct answer sits above the body in its own frame, because it is
   deliberately the passage a search engine or an AI assistant lifts. */
.article-answer {
  border-left: 3px solid var(--blue);
  background: var(--bg-alt);
  padding: 18px 22px;
  border-radius: 0 10px 10px 0;
  margin: 22px 0 26px;
}
.article-answer p { margin: 0; font-size: 1.08rem; line-height: 1.7; color: var(--ink); }

.article-takeaways {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px 24px;
  margin: 0 0 30px;
  background: #fff;
}
.article-takeaways h2 { font-size: 1rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); margin: 0 0 12px; }
.article-takeaways ul { margin: 0; padding-left: 20px; list-style: disc; }
.article-takeaways li { margin-bottom: 8px; line-height: 1.65; }
.article-takeaways li:last-child { margin-bottom: 0; }

.article-faq { margin-top: 42px; }
.article-faq > h2 { font-size: 1.5rem; margin-bottom: 16px; }
.faq-item { border: 1px solid var(--border); border-radius: 10px; margin-bottom: 10px; background: #fff; }
.faq-item summary {
  cursor: pointer; padding: 16px 20px; font-weight: 600; list-style: none;
  display: flex; justify-content: space-between; gap: 14px; align-items: center;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after { content: "+"; color: var(--blue); font-size: 1.3rem; line-height: 1; flex: 0 0 auto; }
.faq-item[open] summary::after { content: "–"; }
.faq-answer { padding: 0 20px 18px; color: var(--muted); line-height: 1.75; }
.faq-answer p { margin: 0; }

/* The related-product cards at the foot of an article. They carry the same
   visual weight as the More Articles cards directly below them — two blocks
   of the same kind of thing (here is somewhere else to go) should not be one
   large and one tiny. Picture on top, name under it, same 3-up grid. */
.article-tail { background: var(--bg-alt); border-top: 1px solid var(--border); padding: 40px 0 48px; }
.article-tail .article-products { max-width: 780px; margin: 0 auto; }
/* Both tails follow the article's own column. One of them sitting at full
   container width while the other lines up with the text reads as a mistake. */
/* +gutters, so the container's CONTENT is 780px and lines up with the
   article above rather than sitting one gutter further in. */
.article-more > .container { max-width: calc(780px + 2 * var(--gutter)); }
.article-more { padding-top: 44px; }
.article-products { margin: 0; }
.article-products > h2 { font-size: 1.5rem; margin-bottom: 20px; }

.related-products { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
.related-product {
  display: flex; flex-direction: column;
  background: #fff; border: 1px solid var(--border); border-radius: var(--radius-lg);
  overflow: hidden; color: var(--ink); text-decoration: none;
  transition: box-shadow .25s ease, transform .25s ease, border-color .25s ease;
}
.related-product:hover { border-color: var(--blue); box-shadow: var(--shadow-lg); transform: translateY(-3px); }
.related-product-media { display: block; background: var(--bg-alt); overflow: hidden; }
.related-product-media img {
  display: block; width: 100%; height: 100%;
  aspect-ratio: 4 / 3; object-fit: cover;
  transition: transform .5s cubic-bezier(.16,.8,.24,1);
}
.related-product:hover .related-product-media img { transform: scale(1.06); }
/* A flex column with the link pushed to the bottom, so a two-line product
   name does not leave that card's "View Details" sitting lower than its
   neighbours'. */
.related-product-body { display: flex; flex-direction: column; flex: 1; padding: 16px 18px 18px; }
.related-product-name { display: block; font-size: 1.02rem; font-weight: 600; line-height: 1.4; }
.related-product-link {
  display: inline-flex; align-items: center; gap: 6px; align-self: flex-start;
  margin-top: auto; padding-top: 10px; color: var(--blue); font-size: 0.88rem; font-weight: 600;
}
.related-product-link .icon { width: 15px; height: 15px; }
@media (max-width: 640px) {
  .related-products { grid-template-columns: repeat(2, 1fr); gap: 14px; }
  .related-product-body { padding: 12px 14px 14px; }
  .related-product-name { font-size: 0.92rem; }
}
}

/* Body copy inside an AI-written article: tables and headings need the same
   care as the hand-written pages. */
.news-article .content h2 { font-size: 1.45rem; margin: 34px 0 12px; }
.news-article .content h3 { font-size: 1.15rem; margin: 24px 0 10px; }
.news-article .content ul { margin: 0 0 18px; padding-left: 22px; list-style: disc; }
.news-article .content ol { margin: 0 0 18px; padding-left: 22px; list-style: decimal; }
.news-article .content li { margin-bottom: 7px; line-height: 1.75; }
.news-article .content table {
  width: 100%; border-collapse: collapse; margin: 20px 0; font-size: 0.94rem;
  display: block; overflow-x: auto;
  /* Scroll shadows: the two `local` layers are painted with the content and
     slide away as it scrolls, uncovering the fixed `scroll` shadow on the side
     that still has content off-screen. On a ten-table article this is the only
     cue a phone reader gets that a column is hidden. */
  background:
    linear-gradient(to right, #fff 30%, rgba(255, 255, 255, 0)) left center,
    linear-gradient(to left, #fff 30%, rgba(255, 255, 255, 0)) right center,
    radial-gradient(farthest-side at 0 50%, rgba(15, 23, 42, 0.16), rgba(15, 23, 42, 0)) left center,
    radial-gradient(farthest-side at 100% 50%, rgba(15, 23, 42, 0.16), rgba(15, 23, 42, 0)) right center;
  background-repeat: no-repeat;
  background-size: 34px 100%, 34px 100%, 13px 100%, 13px 100%;
  background-attachment: local, local, scroll, scroll;
}
.news-article .content th, .news-article .content td {
  border: 1px solid var(--border); padding: 10px 14px; text-align: left; vertical-align: top;
}
.news-article .content th { background: var(--bg-alt); font-weight: 600; }
/* Formulas and codes in an article body. `overflow-wrap: anywhere` rather
   than nothing: a formula that wraps is readable, a formula clipped by the
   right edge of a phone is gone. */
.news-article .content code {
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 2px 6px;
  font-size: 0.92em;
  overflow-wrap: anywhere;
}
.news-article .content pre {
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
  overflow-x: auto;
  margin: 0 0 18px;
}
.news-article .content pre code { background: none; border: none; padding: 0; }

/* --- Images placed inside an article body or a product description ------
   Both surfaces share one figure treatment so a picture inserted from the
   editor's 图片 button looks the same wherever it is used. Width is capped
   at the text column, never the viewport, so a 4000px photo cannot push the
   page sideways on a phone. */
.news-article .content img,
.rich-text img {
  max-width: 100%;
  height: auto;
  border-radius: var(--radius);
  display: block;
}
.news-article .content figure,
.rich-text figure { margin: 26px 0; }
.news-article .content figcaption,
.rich-text figcaption {
  margin-top: 8px;
  font-size: 0.86rem;
  line-height: 1.6;
  color: var(--muted);
  text-align: center;
}

/* --- Product long description (the 描述 tab) --------------------------
   The field used to be plain text and is now a rich-text editor, so the tab
   has to style real markup. Kept deliberately close to the article body: a
   buyer reading a spec list should get the same typography either way. */
.rich-text { font-size: 0.97rem; line-height: 1.75; color: var(--ink); }
.rich-text > :first-child { margin-top: 0; }
.rich-text > :last-child { margin-bottom: 0; }
.rich-text p { margin: 0 0 1.1em; }
.rich-text h2 { font-size: 1.25rem; margin: 26px 0 10px; }
.rich-text h3 { font-size: 1.05rem; margin: 20px 0 8px; }
.rich-text ul { margin: 0 0 16px; padding-left: 22px; list-style: disc; }
.rich-text ol { margin: 0 0 16px; padding-left: 22px; list-style: decimal; }
.rich-text li { margin-bottom: 6px; }
.rich-text a { color: var(--blue); text-decoration: underline; }
.rich-text blockquote { margin: 0 0 16px; padding-left: 14px; border-left: 3px solid var(--border); color: var(--muted); }
/* A pasted-in table can be wider than the column; let it scroll inside the
   tab rather than widening the page. */
.rich-text table { width: 100%; border-collapse: collapse; margin: 0 0 18px; display: block; overflow-x: auto; }
.rich-text th, .rich-text td { border: 1px solid var(--border); padding: 9px 12px; text-align: left; font-size: 0.92rem; }
.rich-text th { background: var(--bg-alt); font-weight: 600; }

/* Draft preview banner on an unpublished article (admins only). */
.draft-banner { background: #fef3c7; border-bottom: 1px solid #fcd34d; padding: 12px 0; font-size: 0.92rem; color: #78350f; }
.draft-banner a { color: #78350f; text-decoration: underline; margin-left: 8px; }

/* --- 发文节奏风险提示 ------------------------------------------------- */
.risk-card { border: 1px solid var(--border); border-left-width: 4px; border-radius: var(--radius); padding: 20px 24px; margin-bottom: 22px; background: #fff; }
.risk-card h2 { font-size: 1.05rem; margin-bottom: 8px; }
.risk-card p { margin: 0 0 6px; line-height: 1.7; }
.risk-card p:last-child { margin-bottom: 0; }
.risk-title { font-weight: 600; }
.risk-ok { border-left-color: #16a34a; }
.risk-ok h2 { color: #15803d; }
.risk-warn { border-left-color: #f59e0b; background: #fffbeb; }
.risk-warn h2 { color: #b45309; }
.risk-danger { border-left-color: #dc2626; background: #fef2f2; }
.risk-danger h2 { color: #b91c1c; }

/* --- 信息增量自检 ----------------------------------------------------- */
.gain-pill {
  display: inline-block; padding: 3px 10px; border-radius: 999px;
  font-size: 0.78rem; font-weight: 700; text-decoration: none;
  font-variant-numeric: tabular-nums;
}
.gain-good { background: #dcfce7; color: #15803d; }
.gain-ok { background: #fef3c7; color: #b45309; }
.gain-weak { background: #fee2e2; color: #b91c1c; }

.gain-card { border-left: 4px solid var(--border); }
.gain-card-good { border-left-color: #16a34a; }
.gain-card-ok { border-left-color: #f59e0b; }
.gain-card-weak { border-left-color: #dc2626; }
.gain-head { display: flex; align-items: center; gap: 18px; margin-bottom: 16px; }
.gain-score {
  flex: 0 0 auto; display: inline-flex; align-items: baseline;
  padding: 10px 16px; border-radius: 12px;
  font-size: 1.7rem; font-weight: 800; font-variant-numeric: tabular-nums;
}
.gain-score span { font-size: 0.95rem; font-weight: 600; opacity: 0.65; }

.gain-list { list-style: none; margin: 0 0 16px; padding: 0; }
.gain-list li { padding: 10px 0; border-bottom: 1px solid var(--border); }
.gain-list li:last-child { border-bottom: none; }
.gain-line { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; font-size: 0.93rem; }
.gain-mark { flex: 0 0 auto; font-weight: 800; width: 16px; }
.gain-list .is-ok .gain-mark { color: #16a34a; }
.gain-list .is-bad .gain-mark { color: #dc2626; }
.gain-detail { color: var(--muted); font-size: 0.86rem; }
.gain-why { margin: 6px 0 0 26px; color: var(--muted); font-size: 0.86rem; line-height: 1.65; }
@media (max-width: 640px) {
  .gain-head { flex-direction: column; align-items: flex-start; gap: 10px; }
  .gain-why { margin-left: 0; }
}

/* --- 导航下拉菜单 ----------------------------------------------------- */
.main-nav li.has-children { position: relative; }
.main-nav .nav-caret { width: 13px; height: 13px; margin-left: 2px; vertical-align: -2px; opacity: .65; transition: transform .18s ease; }
.main-nav li.has-children:hover .nav-caret,
.main-nav li.has-children:focus-within .nav-caret { transform: rotate(180deg); }
.nav-dropdown {
  position: absolute; top: 100%; left: 0; z-index: 90;
  display: block !important;               /* beats `.main-nav ul { display:flex }` */
  min-width: 268px; padding: 8px;
  background: #fff; border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow-lg);
  opacity: 0; visibility: hidden; transform: translateY(6px);
  transition: opacity .18s ease, transform .18s ease, visibility .18s;
}
/* No gap between the trigger and the panel, or the menu closes as the
   pointer crosses the space between them. */
.main-nav li.has-children::after { content: ""; position: absolute; left: 0; right: 0; top: 100%; height: 10px; }
.main-nav li.has-children:hover > .nav-dropdown,
.main-nav li.has-children:focus-within > .nav-dropdown {
  opacity: 1; visibility: visible; transform: translateY(0);
}
.nav-dropdown li { display: block; }
.nav-dropdown a {
  display: block; padding: 9px 12px; border-radius: 7px;
  font-size: 0.9rem; line-height: 1.4; white-space: normal;
}

/* Mobile drawer: tap to expand, no hover involved. */
.mobile-nav .mobile-group > details > summary {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 12px 0; border-bottom: 1px solid var(--border);
  cursor: pointer; list-style: none; font-weight: 500;
}
.mobile-nav .mobile-group > details > summary::-webkit-details-marker { display: none; }
.mobile-nav .mobile-group > details[open] > summary .nav-caret { transform: rotate(180deg); }
.mobile-nav .mobile-group .nav-caret { width: 15px; height: 15px; transition: transform .18s ease; }
.mobile-nav .mobile-group ul { padding: 0 0 0 14px; }
.mobile-nav .mobile-group ul a { font-size: 0.92rem; color: var(--ink-soft); }

/* --- 聚合页 ----------------------------------------------------------- */
.hub-header { padding-bottom: 8px; }
.hub-eyebrow {
  text-transform: uppercase; letter-spacing: 0.12em;
  font-size: 0.74rem; font-weight: 700; color: var(--blue); margin-bottom: 10px;
}
/* padding-top/bottom only — the shorthand would reset the .container's own
   left/right gutter to 0 and let the whole page bleed to the window edge. */
.hub-body { padding-top: 40px; padding-bottom: 72px; }
/* The intro is prose, so it keeps a reading measure and sits centred under
   the centred page header; the grids below it use the full width. */
.hub-intro { max-width: 780px; margin: 0 auto 30px; font-size: 1.05rem; line-height: 1.85; }
.hub-intro p { margin-bottom: 1.1em; }
.hub-intro h2 { font-size: 1.35rem; margin: 28px 0 12px; }
.hub-intro ul, .hub-intro ol { margin: 0 0 18px; padding-left: 22px; list-style: disc; }
.hub-intro li { margin-bottom: 7px; line-height: 1.75; }

.hub-points {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 12px 26px; margin: 0 auto 48px; max-width: 900px;
  padding: 20px 24px; background: var(--bg-alt); border-radius: var(--radius);
}
.hub-points li { display: flex; align-items: flex-start; gap: 9px; line-height: 1.6; }
.hub-points .icon { width: 18px; height: 18px; flex: 0 0 18px; margin-top: 3px; color: var(--blue); }

.hub-section { margin-bottom: 56px; }
.hub-section > h2 { font-size: 1.55rem; margin-bottom: 22px; }

.hub-cta {
  background: var(--bg-alt); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 36px 40px; text-align: center;
}
.hub-cta h2 { font-size: 1.4rem; margin-bottom: 10px; }
.hub-cta p { color: var(--muted); max-width: 560px; margin: 0 auto 22px; line-height: 1.7; }

.hub-index-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
.hub-index-card {
  display: flex; flex-direction: column; overflow: hidden;
  background: #fff; border: 1px solid var(--border); border-radius: var(--radius-lg);
  color: var(--ink); text-decoration: none;
  transition: box-shadow .25s ease, transform .25s ease, border-color .25s ease;
}
.hub-index-card:hover { border-color: var(--blue); box-shadow: var(--shadow-lg); transform: translateY(-3px); }
.hub-index-media { display: block; background: var(--bg-alt); }
.hub-index-media img { display: block; width: 100%; aspect-ratio: 3 / 2; object-fit: cover; }
.hub-index-body { display: flex; flex-direction: column; flex: 1; padding: 20px 22px 22px; }
.hub-index-name { font-size: 1.1rem; font-weight: 600; line-height: 1.4; }
.hub-index-sub { margin-top: 8px; color: var(--muted); font-size: 0.92rem; line-height: 1.6; }
.hub-index-link {
  display: inline-flex; align-items: center; gap: 6px; align-self: flex-start;
  margin-top: auto; padding-top: 14px; color: var(--blue); font-weight: 600; font-size: 0.88rem;
}
.hub-index-link .icon { width: 15px; height: 15px; }

@media (max-width: 900px) {
  .hub-index-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
  .hub-index-grid { grid-template-columns: 1fr; gap: 14px; }
  .hub-cta { padding: 26px 20px; }
  .hub-section { margin-bottom: 40px; }
  .hub-section > h2 { font-size: 1.3rem; }
}

/* --- 访问统计自检卡片 -------------------------------------------------- */
.selfcheck { border-left: 4px solid var(--border); }
.selfcheck-ok { border-left-color: #16a34a; }
.selfcheck-warn { border-left-color: #d97706; }
.selfcheck-danger { border-left-color: #dc2626; }
.selfcheck-title { font-weight: 600; margin: 0 0 6px; }
.selfcheck-ok .selfcheck-title { color: #15803d; }
.selfcheck-warn .selfcheck-title { color: #b45309; }
.selfcheck-danger .selfcheck-title { color: #b91c1c; }
.selfcheck-facts { margin: 0; padding-left: 20px; list-style: disc; font-size: 0.86rem; line-height: 1.9; color: var(--muted); }
.selfcheck-facts code { font-size: 0.82rem; }
