/* FusionCharge Wiki — house stylesheet.
 * Tokens mirror design-system/tokens/*.css (FusionCharge SSOT). Edit tokens there via DesignSync;
 * this file re-declares them so the page is self-contained.
 */
@import url('https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;500;600;700;800&family=Noto+Sans+Thai:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap');

:root {
  /* palette — design-system/tokens/colors.css */
  /* 2026-11-04 (unify WP0): re-based on the Smart Charging Network VI (03号规范), as design-system/tokens/colors.css:
     #C7000B is THE brand red; #E9002F survives only as --hw-red-hot (one live emphasis). */
  --hw-red: #C7000B; --hw-red-deep: #A5000A; --hw-red-hot: #E9002F; --hw-red-dark: #7F0000; --hw-red-tint: #FCE5EA;
  --hw-orange: #ED6D00; --hw-amber: #FCC800; --hw-green: #61B230; --hw-teal: #30B5C5;
  --charge-green: #21C45A; --charge-blue: #2155CD;
  --ink: #1D1D1A; --ink-2: #3C3C3A; --ink-3: #666666; --ink-4: #666664;  /* 2026-11-04 (unify WP1): was #8A8A88 (3.3:1 on white); = --fc-ink4, 5.7:1 */
  --line: #DDDDDD; --line-soft: #ECECEC;
  --surface: #FFFFFF; --surface-2: #F6F6F4; --surface-3: #EFEFED; --surface-ink: #141413;
  --text-on-dark: #FFFFFF; --text-on-dark-muted: rgba(255,255,255,.66);
  /* type — tokens/typography.css + fonts.css */
  --font-sans: 'Hanken Grotesk', 'HarmonyOS Sans', 'Huawei Sans', 'Noto Sans Thai', Arial, system-ui, sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  /* effects — tokens/effects.css */
  --radius: 10px; --radius-lg: 16px; --radius-pill: 999px;
  --shadow-xs: 0 1px 2px rgba(29,29,26,.06); --shadow-card: 0 2px 10px rgba(29,29,26,.07);
  --shadow-lg: 0 14px 40px rgba(29,29,26,.16);
  --accent-bar: 4px solid var(--hw-red);
  --ease-out: cubic-bezier(.22,.61,.36,1); --dur-fast: 120ms; --dur-base: 220ms; --dur-slow: 420ms;
  --focus-ring: 0 0 0 3px rgba(199,0,11,.30);
  /* layout */
  --rail-w: 264px; --content-max: 1080px; --pad: clamp(16px, 4vw, 40px);
}
html[data-lang="th"] body { line-height: 1.7; letter-spacing: 0; }

* { box-sizing: border-box; }
/* HIDDEN MEANS HIDDEN (2026-08-17).
 * The UA rule is `[hidden] { display: none }`, which ANY author `display` outranks —
 * so `el.hidden = true` on anything styled `display: grid/flex` sets the attribute and
 * changes nothing on screen. It bit twice in one day: the map's Reset button stayed up
 * at 1x, and then filtering the station list by CPO hid 29 rows that all stayed
 * visible — the map and the header filtered, the list did not. Both were "fixed" one
 * selector at a time, which is why the second one was missed. This is the one rule
 * that closes the class. `!important` is deliberate: nothing may out-specify hidden. */
[hidden] { display: none !important; }
html { scroll-behavior: smooth; }
body {
  margin: 0; font-family: var(--font-sans); color: var(--ink); background: var(--surface-2);
  font-size: 16px; line-height: 1.55; -webkit-font-smoothing: antialiased;
}
a { color: var(--hw-red-deep); text-decoration: none; }
a:hover { text-decoration: underline; }
:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: 4px; }
img { max-width: 100%; display: block; }

/* ---------- chrome ---------- */
.topbar {
  position: sticky; top: 0; z-index: 40; display: flex; align-items: center; gap: 14px;
  background: rgba(255,255,255,.92); backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line-soft); padding: 10px var(--pad); min-height: 56px;
}
.brand { display: flex; align-items: center; gap: 10px; font-weight: 800; letter-spacing: -0.01em; color: var(--ink); white-space: nowrap; }
.brand:hover { text-decoration: none; }
.brand .bolt { width: 26px; height: 26px; border-radius: 7px; background: var(--hw-red); color: #fff; display: grid; place-items: center; font-size: 15px; }
.brand small { font-weight: 600; color: var(--ink-4); font-size: 11px; letter-spacing: .14em; text-transform: uppercase; display: block; }
.topbar .spacer { flex: 1; }
.lang-switch { display: flex; border: 1px solid var(--line); border-radius: var(--radius-pill); overflow: hidden; }
.lang-switch button {
  border: 0; background: transparent; font: inherit; font-weight: 600; font-size: 13px;
  padding: 5px 14px; cursor: pointer; color: var(--ink-3);
}
.lang-switch button.active { background: var(--ink); color: #fff; }
#nav-toggle { display: none; border: 1px solid var(--line); background: #fff; border-radius: 8px; padding: 6px 10px; font-size: 16px; cursor: pointer; }

.layout { display: grid; grid-template-columns: var(--rail-w) minmax(0,1fr); max-width: 1440px; margin: 0 auto; }
.layout > * { min-width: 0; }
.nav { padding: 22px 14px 40px 22px; position: sticky; top: 56px; align-self: start; height: calc(100vh - 56px); overflow-y: auto; }
.nav-group { margin-bottom: 18px; }
.nav-group-label { font-size: 11px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-4); padding: 6px 10px; }
.nav a {
  display: flex; align-items: center; gap: 9px; padding: 7px 10px; border-radius: 8px;
  color: var(--ink-2); font-weight: 500; font-size: 14.5px; border-left: 3px solid transparent;
}
.nav a:hover { background: var(--surface-3); text-decoration: none; }
.nav a.active { background: #fff; border-left: 3px solid var(--hw-red); color: var(--ink); font-weight: 700; box-shadow: var(--shadow-xs); }
.nav-icon { width: 18px; text-align: center; }
#nav-scrim { display: none; }

.page { padding: 30px var(--pad) 80px; max-width: var(--content-max); outline: none; }
.page:focus, .page:focus-visible { box-shadow: none; outline: none; }
/* grid/flex children may not force the page wider than the viewport */
.page section, .roi-grid > *, .cards > *, .map-flex > *, .artifact-blk > * { min-width: 0; }
.page { overflow-wrap: break-word; }
.tbl-wrap { max-width: 100%; }
.page > * + * { margin-top: 26px; }

/* ---------- typography blocks ---------- */
.eyebrow { font-size: 12px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--hw-red-deep); }
h1.page-title { font-size: clamp(30px, 4.6vw, 44px); line-height: 1.08; letter-spacing: -0.02em; margin: 6px 0 8px; font-weight: 800; }
.page-intro { font-size: 18px; color: var(--ink-3); max-width: 62ch; margin: 0; }
h2.sec-h { font-size: clamp(21px, 2.6vw, 27px); letter-spacing: -0.01em; margin: 0 0 14px; font-weight: 700; padding-left: 12px; border-left: var(--accent-bar); }
.prose { max-width: 68ch; }
.prose p { margin: 0 0 12px; color: var(--ink-2); }
.prose ul { margin: 0 0 12px; padding-left: 20px; color: var(--ink-2); }
.prose li { margin-bottom: 6px; }

/* ---------- sections ---------- */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px,1fr)); gap: 12px; }
.stat {
  background: #fff; border: 1px solid var(--line-soft); border-radius: var(--radius);
  padding: 16px 16px 13px; box-shadow: var(--shadow-xs);
}
.stat .v { font-family: var(--font-mono); font-size: clamp(24px, 3vw, 32px); font-weight: 600; letter-spacing: -0.02em; }
.stat .v .suf { font-size: .55em; color: var(--ink-3); margin-left: 3px; font-weight: 500; }
.stat .l { font-size: 13px; color: var(--ink-3); margin-top: 3px; }
.stat .n { font-size: 11.5px; color: var(--ink-4); margin-top: 2px; }

.cards { display: grid; gap: 14px; }
.cards.c2 { grid-template-columns: repeat(auto-fit, minmax(260px,1fr)); }
.cards.c3 { grid-template-columns: repeat(auto-fit, minmax(280px,1fr)); }
.card {
  background: #fff; border: 1px solid var(--line-soft); border-left: var(--accent-bar);
  border-radius: var(--radius); padding: 18px; box-shadow: var(--shadow-xs);
  transition: box-shadow var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
}
a.card { color: inherit; display: block; }
a.card:hover { text-decoration: none; box-shadow: var(--shadow-card); transform: translateY(-2px); }
.card .ic { font-size: 22px; margin-bottom: 8px; }
.card h3 { margin: 0 0 6px; font-size: 17px; letter-spacing: -0.01em; }
.card p { margin: 0; font-size: 14px; color: var(--ink-3); }

figure.img { margin: 0; }
figure.img img { border-radius: var(--radius); box-shadow: var(--shadow-card); width: 100%; object-fit: cover; }
figure.img figcaption { font-size: 13px; color: var(--ink-4); margin-top: 8px; }
.gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px,1fr)); gap: 14px; }
.gallery figure { margin: 0; }
.gallery img { border-radius: var(--radius); box-shadow: var(--shadow-xs); width: 100%; aspect-ratio: 16/10; object-fit: cover; }
.gallery figcaption { font-size: 12.5px; color: var(--ink-4); margin-top: 6px; }
/* Product renders are tall, transparent studio shots — the 16/10 cover crop above
 * would slice the top off a 2.15 m cabinet. Contain them on white instead. */
.gallery.product img {
  aspect-ratio: 3/4; object-fit: contain; background: #fff;
  padding: 14px; border: 1px solid var(--line-soft);
}
.gallery.product figcaption strong { display: block; font-size: 13.5px; color: var(--ink); }

.tbl-wrap { overflow-x: auto; background: #fff; border: 1px solid var(--line-soft); border-radius: var(--radius); box-shadow: var(--shadow-xs); }
table.tbl { border-collapse: collapse; width: 100%; font-size: 14.5px; min-width: 560px; }
.tbl th { text-align: left; font-size: 12px; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-4); font-weight: 700; padding: 12px 14px; border-bottom: 2px solid var(--line); }
.tbl td { padding: 11px 14px; border-bottom: 1px solid var(--line-soft); color: var(--ink-2); }
.tbl tr:last-child td { border-bottom: 0; }
.tbl td.num, .tbl th.num { font-family: var(--font-mono); font-size: 13.5px; text-align: right; white-space: nowrap; }
.tbl-note { font-size: 12.5px; color: var(--ink-4); padding: 10px 14px 12px; border-top: 1px solid var(--line-soft); }

.links-list { display: grid; gap: 10px; }
.link-row {
  display: flex; gap: 12px; align-items: baseline; background: #fff; border: 1px solid var(--line-soft);
  border-radius: var(--radius); padding: 12px 16px; color: inherit;
}
.link-row:hover { text-decoration: none; box-shadow: var(--shadow-card); }
.link-row .t { font-weight: 600; color: var(--hw-red-deep); }
.link-row .d { font-size: 13.5px; color: var(--ink-3); flex: 1; min-width: 40%; }
.badge { font-size: 10.5px; font-weight: 700; letter-spacing: .08em; padding: 2px 8px; border-radius: var(--radius-pill); background: var(--surface-3); color: var(--ink-3); white-space: nowrap; }
.badge.red { background: var(--hw-red-tint); color: var(--hw-red-deep); }

/* ---------- video wall (Resources) ----------
 * Cards are links to YouTube, not embeds. The play badge is the affordance that
 * says "this leaves the page and becomes a video" — without it a 16/9 image in a
 * grid reads as a photo gallery and nobody clicks. */
.vid-groups { display: grid; gap: 30px; }
.vid-intro { color: var(--ink-3); font-size: 14.5px; margin: -4px 0 14px; max-width: 68ch; }
.vid-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 232px), 1fr)); gap: 14px; }
.vid-card {
  display: flex; flex-direction: column; background: #fff; border: 1px solid var(--line-soft);
  border-radius: var(--radius); overflow: hidden; color: var(--ink);
  transition: box-shadow var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.vid-card:hover { text-decoration: none; box-shadow: var(--shadow-card); transform: translateY(-2px); }
.vid-card:focus-visible { box-shadow: var(--focus-ring); }
.vid-thumb { position: relative; display: block; background: var(--surface-3); aspect-ratio: 16 / 9; }
.vid-thumb img { width: 100%; height: 100%; object-fit: cover; }
/* Play triangle drawn in CSS — one more image per card would be 38 more requests
 * for a shape that is four lines of border. */
.vid-play {
  position: absolute; inset: 0; margin: auto; width: 42px; height: 42px; border-radius: 50%;
  background: rgba(20,20,19,.62); backdrop-filter: blur(2px);
  transition: background var(--dur-fast) var(--ease-out);
}
.vid-play::after {
  content: ''; position: absolute; inset: 0; margin: auto; width: 0; height: 0;
  border-style: solid; border-width: 8px 0 8px 13px; border-color: transparent transparent transparent #fff;
  transform: translateX(2px);
}
.vid-card:hover .vid-play { background: var(--hw-red); }
.vid-meta { display: flex; flex-direction: column; gap: 5px; padding: 11px 12px 12px; flex: 1; }
.vid-title { font-size: 13.5px; font-weight: 600; line-height: 1.35; }
.vid-by { font-size: 12px; color: var(--ink-4); }
.vid-tags { display: flex; gap: 6px; flex-wrap: wrap; margin-top: auto; padding-top: 7px; }
.vid-tag { font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; padding: 2px 7px; border-radius: var(--radius-pill); white-space: nowrap; }
.vid-tag.official { background: var(--hw-red-tint); color: var(--hw-red-deep); }
.vid-tag.partner { background: #FFF3DC; color: #8A5A00; }
.vid-tag.indep { background: #E6F4EA; color: #1B6E3C; }
.vid-tag.lang { background: var(--surface-3); color: var(--ink-3); }
@media (prefers-reduced-motion: reduce) { .vid-card:hover { transform: none; } }

.compare { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px,1fr)); gap: 14px; }
.compare .col { background: #fff; border-radius: var(--radius); border: 1px solid var(--line-soft); padding: 18px; }
.compare .col.us { border-top: 4px solid var(--hw-red); }
.compare .col.them { border-top: 4px solid var(--ink-4); }
.compare h3 { margin: 0 0 10px; font-size: 16px; }
.compare ul { margin: 0; padding-left: 18px; font-size: 14.5px; color: var(--ink-2); }
.compare li { margin-bottom: 7px; }

.faq .qa { background: #fff; border: 1px solid var(--line-soft); border-radius: var(--radius); margin-bottom: 10px; padding: 4px 18px; }
.faq summary { font-weight: 600; cursor: pointer; padding: 10px 0; }
.faq .qa p { color: var(--ink-2); margin: 0 0 12px; font-size: 14.5px; }

.timeline { position: relative; display: grid; gap: 0; }
.tl-item { display: grid; grid-template-columns: 52px minmax(0,1fr); gap: 14px; position: relative; padding-bottom: 22px; }
.tl-item::before { content: ''; position: absolute; left: 25px; top: 34px; bottom: 0; width: 2px; background: var(--line); }
.tl-item:last-child::before { display: none; }
.tl-step {
  width: 50px; height: 34px; border-radius: 8px; background: var(--surface-ink); color: #fff;
  font-family: var(--font-mono); font-size: 14px; display: grid; place-items: center; font-weight: 600;
}
.tl-item h3 { margin: 4px 0 4px; font-size: 16.5px; }
.tl-item p { margin: 0; font-size: 14.5px; color: var(--ink-3); max-width: 60ch; }

blockquote.quote { margin: 0; background: var(--surface-ink); color: var(--text-on-dark); border-radius: var(--radius-lg); padding: 26px 28px; }
blockquote.quote p { margin: 0 0 8px; font-size: 19px; line-height: 1.4; }
blockquote.quote cite { color: var(--text-on-dark-muted); font-style: normal; font-size: 13.5px; }

/* One quiet credit line on the home page, in place of the old full CTA card. */
.byline-note { font-size: 13px; color: var(--ink-4); margin: 14px 0 0; max-width: 68ch; }
.byline-note a { font-weight: 600; }

/* Red callout — reserved for a single load-bearing product fact per page. Used on
 * Products for the flagship gun's 600 kW bonded-port claim (Ralph, 2026-07-29). */
.callout.red {
  border-left: 4px solid var(--hw-red); background: var(--hw-red-tint);
  border-radius: 0 var(--radius) var(--radius) 0; padding: 16px 18px; margin: 4px 0 0;
  color: var(--hw-red-dark); font-size: 15px; line-height: 1.6;
}
.callout.red strong { color: var(--hw-red-deep); }

.cta-blk { background: var(--surface-ink); color: var(--text-on-dark); border-radius: var(--radius-lg); padding: 30px; }
.cta-blk h2 { margin: 0 0 6px; font-size: 24px; letter-spacing: -0.01em; }
.cta-blk p { margin: 0 0 18px; color: var(--text-on-dark-muted); max-width: 60ch; }
.btn {
  display: inline-flex; align-items: center; gap: 8px; border-radius: var(--radius-pill);
  padding: 10px 22px; font-weight: 700; font-size: 15px; border: 1.5px solid transparent; cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.btn:hover { text-decoration: none; transform: translateY(-1px); }
.btn.primary { background: var(--hw-red); color: #fff; box-shadow: 0 8px 26px rgba(199,0,11,.26); }
.btn.primary:hover { background: var(--hw-red-deep); }
.btn.ghost { border-color: var(--line); color: var(--ink); background: #fff; }
.cta-blk .btn.ghost { background: transparent; color: #fff; border-color: rgba(255,255,255,.35); }
.btn-row { display: flex; gap: 12px; flex-wrap: wrap; }

/* LINE — the site's contact channel since 2026-07-31. Carries LINE's own brand
 * green rather than Huawei red on purpose: this is the one control on the page
 * that hands the reader to a different app, and it should look like that app so
 * nobody taps it expecting a form. It keeps the house pill geometry, so it still
 * reads as one of our buttons and not a pasted-in third-party badge. */
.btn.line { background: #06C755; color: #fff; border-color: #06C755; box-shadow: 0 8px 26px rgba(6,199,85,.24); }
.btn.line:hover { background: #05B14C; border-color: #05B14C; }
.btn.line .line-glyph { flex: none; }
.btn.line:focus-visible { box-shadow: 0 0 0 3px rgba(6,199,85,.38); }
/* On the dark contact block the solid green would out-shout the primary action
 * next to it, so it drops to an outline and keeps the green in the mark only. */
.cta-blk .btn.line.on-dark { background: transparent; color: #fff; border-color: rgba(6,199,85,.85); box-shadow: none; }
.cta-blk .btn.line.on-dark .line-glyph { color: #06C755; }
.cta-blk .btn.line.on-dark:hover { background: #06C755; border-color: #06C755; }
.cta-blk .btn.line.on-dark:hover .line-glyph { color: #fff; }

.artifact-blk { background: #fff; border: 1px solid var(--line-soft); border-radius: var(--radius-lg); overflow: hidden; box-shadow: var(--shadow-xs); display: grid; grid-template-columns: minmax(0,1.1fr) minmax(0,.9fr); }
.artifact-blk .txt { padding: 24px; }
.artifact-blk h2 { margin: 0 0 8px; font-size: 20px; }
.artifact-blk p { color: var(--ink-3); font-size: 14.5px; margin: 0 0 16px; }
.artifact-blk .shot { background: var(--surface-3); min-height: 190px; }
.artifact-blk .shot img { width: 100%; height: 100%; object-fit: cover; object-position: top left; }

/* ---------- home hero + power-bus sim ---------- */
.hero {
  background: var(--surface-ink); color: var(--text-on-dark); border-radius: var(--radius-lg);
  padding: clamp(26px, 4.5vw, 52px); position: relative; overflow: hidden;
}
.hero .eyebrow { color: var(--hw-amber); }
.hero h1 { font-size: clamp(32px, 5.4vw, 56px); line-height: 1.04; letter-spacing: -0.02em; margin: 10px 0 12px; font-weight: 800; max-width: 17ch; }
.hero .sub { color: var(--text-on-dark-muted); font-size: clamp(15px, 1.8vw, 18px); max-width: 56ch; margin: 0 0 20px; }
.hero .pulse-chips { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
.chip {
  display: inline-flex; align-items: center; gap: 7px; font-size: 13px; font-weight: 600;
  background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.14);
  border-radius: var(--radius-pill); padding: 6px 13px; color: #fff;
}
.chip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--charge-green); animation: pulse 2s infinite; }
.chip b { font-family: var(--font-mono); font-weight: 600; }
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }

/* Chips are links to the page each figure comes from. The arrow is the only
 * affordance added — a chip that also grew an underline on hover looked like
 * body copy, and these sit on the hero where the type is already doing work.
 * min-height keeps them past the 32px tap target the form-factor test asserts. */
a.chip { min-height: 32px; text-decoration: none; transition: background var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out); }
a.chip:hover { background: rgba(255,255,255,.15); border-color: rgba(255,255,255,.32); text-decoration: none; }
a.chip .chip-go { opacity: .5; font-weight: 700; transition: transform var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out); }
a.chip:hover .chip-go { opacity: 1; transform: translateX(2px); }
a.chip:focus-visible { box-shadow: var(--focus-ring); }
@media (prefers-reduced-motion: reduce) {
  .chip .dot { animation: none; }
  a.chip:hover .chip-go { transform: none; }
}

.sim { margin-top: 26px; background: rgba(255,255,255,.045); border: 1px solid rgba(255,255,255,.1); border-radius: var(--radius-lg); padding: 18px 18px 12px; }
.sim-title { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 4px; }
.sim-title .t { font-weight: 700; font-size: 14.5px; }
.sim-title .h { font-size: 12.5px; color: var(--text-on-dark-muted); }
.sim svg { width: 100%; height: auto; display: block; }
.sim text { font-family: var(--font-mono); }
.sim .disp { cursor: pointer; }
.sim .disp:hover .disp-body { stroke: var(--hw-amber); }
.sim .disp:focus-visible .disp-body { stroke: var(--hw-amber); stroke-width: 2; }
/* Station-size switcher. Three real builds, flagship first (Ralph, 2026-07-29). */
.sim-tabs { display: flex; gap: 6px; flex-wrap: wrap; margin: 8px 0 4px; }
.sim-tab {
  font: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
  padding: 5px 12px; border-radius: var(--radius-pill); color: var(--text-on-dark-muted);
  background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.16);
}
.sim-tab:hover { color: #fff; border-color: rgba(255,255,255,.34); }
.sim-tab.active { background: var(--hw-red); border-color: var(--hw-red); color: #fff; }
.busline { stroke-dasharray: 6 7; animation: flow 1.1s linear infinite; }
@keyframes flow { to { stroke-dashoffset: -13; } }

/* ---------- roi ---------- */
.roi-grid { display: grid; grid-template-columns: minmax(280px, 340px) minmax(0,1fr); gap: 20px; align-items: start; }
.panel { background: #fff; border: 1px solid var(--line-soft); border-radius: var(--radius); box-shadow: var(--shadow-xs); padding: 18px; }
.panel h3 { margin: 0 0 12px; font-size: 15px; }
.field { margin-bottom: 12px; }
.field label { display: flex; justify-content: space-between; font-size: 13px; font-weight: 600; color: var(--ink-2); margin-bottom: 5px; gap: 8px; }
.field label .val { font-family: var(--font-mono); color: var(--ink); }
.field input[type=range] { width: 100%; accent-color: var(--hw-red); }
.field input[type=number], .field select {
  width: 100%; font: inherit; font-family: var(--font-mono); font-size: 14px; padding: 7px 10px;
  border: 1px solid var(--line); border-radius: 8px; background: #fff; color: var(--ink);
}
.seg { display: flex; border: 1px solid var(--line); border-radius: var(--radius-pill); overflow: hidden; }
.seg button { flex: 1; border: 0; background: transparent; font: inherit; font-size: 13px; font-weight: 600; padding: 7px 4px; cursor: pointer; color: var(--ink-3); }
.seg button.active { background: var(--ink); color: #fff; }
/* Two up where there is room. At two presets a single stacked column was fine;
 * at four it pushed every input below the fold on a laptop. minmax floor is the
 * width of the longest label ("20-gun hub · 960 kW") so nothing wraps mid-name. */
.preset-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr)); gap: 8px; margin-bottom: 14px; }
/* Sits under an input to state a limit the input enforces — never a limit it
 * doesn't, or the note becomes the only thing stopping a bad number. */
.field-note { font-size: 11.5px; line-height: 1.45; color: var(--ink-4); margin-top: 5px; }
.preset-row button {
  text-align: left; border: 1.5px solid var(--line); background: #fff; border-radius: var(--radius);
  padding: 10px 12px; font: inherit; cursor: pointer;
}
.preset-row button.active { border-color: var(--hw-red); background: var(--hw-red-tint); }
.preset-row .pt { font-weight: 700; font-size: 14px; }
.preset-row .pd { font-size: 12px; color: var(--ink-3); }

.scen-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px,1fr)); gap: 12px; }
.scen {
  background: #fff; border: 1px solid var(--line-soft); border-radius: var(--radius); padding: 16px;
  box-shadow: var(--shadow-xs); border-top: 4px solid var(--ink-4); position: relative;
}
.scen.worst { border-top-color: #2155CD; }
.scen.average { border-top-color: var(--hw-red); }
.scen.best { border-top-color: #128A40; }
.scen.no1 { border-top-color: var(--hw-amber); background: linear-gradient(180deg,#FFFDF4,#fff); }
.scen .sl { font-size: 13px; font-weight: 700; }
.scen .sn { font-size: 11.5px; color: var(--ink-4); margin-bottom: 10px; }
.scen .pv { font-family: var(--font-mono); font-size: 30px; font-weight: 600; letter-spacing: -0.02em; }
.scen .pu { font-size: 12px; color: var(--ink-3); }
.scen .sm { font-size: 12.5px; color: var(--ink-3); margin-top: 8px; }
.scen .sm b { font-family: var(--font-mono); font-weight: 600; color: var(--ink); }
.hero-num {
  background: var(--surface-ink); color: #fff; border-radius: var(--radius-lg); padding: 22px 26px;
  display: flex; align-items: baseline; gap: 18px; flex-wrap: wrap;
}
.hero-num .hv { font-family: var(--font-mono); font-size: clamp(34px,5vw,52px); font-weight: 600; letter-spacing: -0.03em; }
.hero-num .hl { font-size: 14px; color: var(--text-on-dark-muted); max-width: 46ch; }
.ledger { font-size: 13px; overflow-x: auto; }
.ledger table { width: 100%; border-collapse: collapse; }
.ledger td { padding: 7px 10px; border-bottom: 1px solid var(--line-soft); color: var(--ink-2); }
.ledger td.src { color: var(--ink-4); font-size: 12px; }
.ledger td.val { font-family: var(--font-mono); white-space: nowrap; }

.chart-wrap { background: #fff; border: 1px solid var(--line-soft); border-radius: var(--radius); box-shadow: var(--shadow-xs); padding: 18px; }
.chart-wrap h3 { margin: 0 0 10px; font-size: 15px; }
.chart-wrap svg { width: 100%; height: auto; display: block; }
.chart-tip {
  position: fixed; pointer-events: none; background: var(--ink); color: #fff; font-size: 12.5px;
  font-family: var(--font-mono); padding: 6px 10px; border-radius: 7px; z-index: 90; display: none; white-space: nowrap;
}

/* ---------- live page ---------- */
.top10 { display: grid; gap: 8px; }
.t10-row {
  display: grid; grid-template-columns: 40px minmax(0,1fr) auto; gap: 12px; align-items: center;
  background: #fff; border: 1px solid var(--line-soft); border-radius: var(--radius); padding: 10px 14px;
}
.t10-row.hw { border-left: var(--accent-bar); }
.t10-rank { font-family: var(--font-mono); font-weight: 600; font-size: 18px; color: var(--ink-4); text-align: center; }
.t10-row.hw .t10-rank { color: var(--hw-red); }
.t10-bar-track { height: 10px; border-radius: 5px; background: var(--surface-3); overflow: hidden; margin-top: 6px; }
.t10-bar { height: 100%; border-radius: 5px; background: var(--ink-4); transition: width var(--dur-slow) var(--ease-out); }
.t10-row.hw .t10-bar { background: var(--hw-red); }
.t10-meta { font-size: 12.5px; color: var(--ink-3); display: flex; gap: 10px; flex-wrap: wrap; }
.t10-val { font-family: var(--font-mono); font-size: 15px; font-weight: 600; text-align: right; }
.t10-val small { display: block; font-size: 10.5px; color: var(--ink-4); font-weight: 400; }

/* ---------- map page ---------- */
.map-wrap { background: #fff; border: 1px solid var(--line-soft); border-radius: var(--radius-lg); box-shadow: var(--shadow-xs); overflow: hidden; }
.map-flex { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(260px, .95fr); }
.map-svg-box { padding: 10px; }
.map-svg-box svg { width: 100%; height: auto; display: block; }
.map-list { border-left: 1px solid var(--line-soft); max-height: 640px; overflow-y: auto; }
/* A grid item stretches to the row height by default, and the row is as tall as the
 * map — so max-height alone never shrinks the list. Filtered to a 2-station network
 * that left 470px of empty white beside a full-height map. Shrink to content instead;
 * max-height still caps the unfiltered 45-row list and keeps it scrolling.
 * .page-map-scoped: the pool page reuses .map-list and wants the stretched column. */
.page-map .map-list { align-self: start; }
.map-st {
  display: grid; grid-template-columns: minmax(0,1fr) auto; gap: 4px 10px; padding: 10px 16px;
  border-bottom: 1px solid var(--line-soft); cursor: pointer; font-size: 14px;
}
.map-st:hover, .map-st.sel { background: var(--hw-red-tint); }
.map-st .mn { font-weight: 600; }
.map-st .mc { font-size: 12px; color: var(--ink-3); grid-column: 1; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.map-st .open { font-size: 12px; font-weight: 700; color: var(--hw-red-deep); align-self: center; grid-row: span 2; }
.pin { cursor: pointer; }
/* The pool page reuses .pin and grows the dot on hover/select. The station map cannot:
 * its radii are recomputed on every zoom step so a pin stays the same size on screen,
 * and a CSS `r` would fight that. Keep the old behaviour where it still applies. */
.page-pool .pin circle { transition: r var(--dur-fast) var(--ease-out); }
.page-pool .pin:hover circle.core, .page-pool .pin.sel circle.core { r: 7; }
.pin .core { stroke: #fff; transition: opacity var(--dur-base) var(--ease-out); }
.pin .halo { opacity: .15; transition: opacity var(--dur-base) var(--ease-out); }
.pin:hover .halo, .pin.sel .halo { opacity: .32; }
.pin.dim { opacity: .12; pointer-events: none; }
/* A tap focuses the <g>, and Chrome then paints its amber `outline: auto 5px` around
 * it — a colour that belongs to no network, boxed around a round pin, on a page whose
 * whole point is that colour means something. `:focus-visible` alone did not cover it
 * (a touch tap matches :focus but NOT :focus-visible), so the ring only ever appeared
 * on the device where nobody was testing with a keyboard. */
.pin:focus { outline: none; }
/* Keyboard focus still has to be visible, and it cannot use `r` — that is recomputed
 * on every zoom step. The core's stroke goes from white to ink, which reads at any
 * scale and against every brand colour. */
.pin:focus-visible .halo { opacity: .5; }
.pin:focus-visible .core { stroke: var(--ink); }
/* Land had to get darker than the #F1F1EE it was at 1×. Zoomed in, a single province
 * fills the box and the old fill was all but invisible against the white card — the
 * pins floated on nothing. This still reads as a quiet base map, not a subject. */
.map-land { fill: #E7E7E2; stroke: #CFCFC9; }
/* The pipeline ring used to be Huawei orange. Now that pin colour means "network",
 * orange is one hue away from Spark (#EA580C) and EVolt (#FD8223) and read as a third
 * operator. Slate says what is actually true of these sites: no network published yet. */
.bpin .bhalo { fill: rgba(71,85,105,.10); }
.bpin .bcore { fill: none; stroke: #475569; }

/* --- zoom + pan (2026-08-17) ---------------------------------------------
 * The stage is the gesture surface; `touch-action` is set from JS because the
 * correct value depends on zoom level. At 1× it must be `pan-y` so a thumb-swipe
 * over a full-width map still scrolls the page; only once the reader zooms in does
 * the map take the gesture. Nothing here may set touch-action unconditionally. */
.map-stage {
  position: relative; touch-action: pan-y;
  background: var(--surface-2); border-radius: var(--radius); overflow: hidden;
}
.map-stage.zoomed { cursor: grab; }
.map-stage.zoomed:active { cursor: grabbing; }
.map-stage svg:focus-visible { outline: 2px solid var(--hw-red); outline-offset: 2px; border-radius: var(--radius); }
.map-ctls {
  position: absolute; top: 8px; right: 8px; display: flex; flex-direction: column; gap: 6px;
  align-items: stretch; z-index: 2;
}
.map-ctl {
  /* 40px is the tested minimum target on a phone (tests/form_factors.py) and these
   * are the only way to zoom without a trackpad or a second finger. */
  min-width: 40px; min-height: 40px; padding: 0 10px;
  display: grid; place-items: center; font: inherit; font-size: 19px; font-weight: 700;
  line-height: 1; color: var(--ink-2); background: rgba(255,255,255,.94);
  border: 1px solid var(--line); border-radius: 10px; box-shadow: var(--shadow-xs);
  cursor: pointer; backdrop-filter: blur(6px);
}
.map-ctl.wide { font-size: 13px; font-weight: 700; letter-spacing: .01em; }
/* (Reset hides itself at 1x — handled by the global `[hidden]` rule at the top.) */
.map-ctl:hover:not(:disabled) { background: #fff; border-color: var(--ink-4); }
.map-ctl:disabled { opacity: .38; cursor: default; }
.map-ctl:focus-visible { outline: 2px solid var(--hw-red); outline-offset: 1px; }
.map-hint {
  position: absolute; inset: auto 0 10px 0; margin: 0 auto; width: max-content; max-width: 90%;
  padding: 7px 14px; border-radius: var(--radius-pill); font-size: 12.5px; font-weight: 600;
  color: #fff; background: rgba(20,20,19,.82); pointer-events: none;
  opacity: 0; transition: opacity var(--dur-base) var(--ease-out);
}
.map-stage.show-hint .map-hint { opacity: 1; }
/* No mouse wheel to hold a modifier with → the hint is meaningless. */
@media (hover: none) { .map-hint { display: none; } }

/* --- CPO brand marks ------------------------------------------------------
 * A colour block + two-letter monogram, never a third-party logo file: we do not
 * hold those marks. Small on purpose (Ralph, 2026-08-17) — the station name is the
 * headline, the network is the answer to "which app". */
.cpo-mark {
  flex: none; display: grid; place-items: center; width: 18px; height: 18px;
  border-radius: 5px; background: var(--cpo, var(--ink-4)); color: #fff;
  font-size: 9.5px; font-weight: 800; font-style: normal; letter-spacing: -.02em;
}
.cpo-chip { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
.cpo-chip .cpo-name {
  font-size: 11.5px; font-weight: 700; color: var(--cpo, var(--ink-3));
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cpo-legend { display: flex; flex-wrap: wrap; gap: 6px; padding: 10px 4px 4px; }
.cpo-filter {
  display: inline-flex; align-items: center; gap: 7px; min-height: 34px; padding: 4px 10px 4px 5px;
  font: inherit; font-size: 12.5px; font-weight: 600; color: var(--ink-2);
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius-pill); cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.cpo-filter .cpo-n { font-family: var(--font-mono); font-size: 11px; color: var(--ink-4); }
.cpo-filter:hover { border-color: var(--cpo); }
.cpo-filter.on { border-color: var(--cpo); background: color-mix(in srgb, var(--cpo) 10%, #fff); color: var(--ink); }
.cpo-filter.on .cpo-n { color: var(--cpo); }
.cpo-filter:focus-visible { outline: 2px solid var(--hw-red); outline-offset: 1px; }
/* While a filter is on, the others recede rather than vanish — the reader needs to
 * see what else there is in order to switch. */
.cpo-legend.filtering .cpo-filter:not(.on) { opacity: .5; }

/* --- the pin popup ---
 * Anchored to a pin inside the stage. Width is capped in vw as well as px so it fits
 * a 280px cover screen without the caret sliding off its own card. */
.map-pop {
  position: absolute; z-index: 3; width: max-content; max-width: min(272px, calc(100% - 16px));
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-lg); padding: 11px 13px 12px; text-align: left;
  animation: mp-in var(--dur-fast) var(--ease-out);
}
@keyframes mp-in { from { opacity: 0; transform: translateY(3px); } }
.mp-arrow {
  position: absolute; bottom: -6px; width: 11px; height: 11px; margin-left: -5.5px;
  background: #fff; border-right: 1px solid var(--line); border-bottom: 1px solid var(--line);
  transform: rotate(45deg);
}
.map-pop.below .mp-arrow { bottom: auto; top: -6px; transform: rotate(225deg); }
/* Narrow screens: a bottom sheet rather than a callout. Set from JS on the measured
 * stage width, not in a media query — the stage is not the viewport, and the pool page
 * puts the same component in a narrower column at the same viewport width. */
.map-pop.sheet {
  left: 8px; right: 8px; bottom: 8px; top: auto; width: auto; max-width: none;
  animation-name: mp-up;
}
.map-pop.sheet .mp-arrow { display: none; }
@keyframes mp-up { from { opacity: 0; transform: translateY(10px); } }
.mp-head { display: flex; align-items: flex-start; gap: 8px; }
.mp-name { font-size: 14px; line-height: 1.3; font-weight: 700; overflow-wrap: anywhere; }
.mp-x {
  flex: none; margin: -6px -6px 0 auto; width: 30px; height: 30px; display: grid; place-items: center;
  border: 0; background: none; font: inherit; font-size: 13px; color: var(--ink-4); cursor: pointer;
  border-radius: 7px;
}
.mp-x:hover { background: var(--surface-3); color: var(--ink-2); }
.mp-x:focus-visible { outline: 2px solid var(--hw-red); outline-offset: -2px; }
.mp-spec { margin-top: 5px; font-size: 12.5px; color: var(--ink-2); display: flex; gap: 6px; flex-wrap: wrap; }
.mp-spec .mp-kw { font-weight: 700; }
.mp-spec .mp-dot { color: var(--ink-4); }
.mp-cpo { margin-top: 8px; display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.mp-cpo-lbl { font-size: 11px; color: var(--ink-4); }
.mp-go {
  display: block; margin-top: 11px; padding: 9px 12px; min-height: 40px; line-height: 22px;
  text-align: center; font-size: 13px; font-weight: 700; color: #fff; background: var(--hw-red);
  border-radius: var(--radius-pill);
}
.mp-go:hover { background: var(--hw-red-deep); text-decoration: none; }
.mp-go:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

.map-key { display: flex; gap: 8px 16px; flex-wrap: wrap; padding: 6px 4px 12px; font-size: 12.5px; color: var(--ink-3); }
/* Both labels wrap to two lines on a phone, so the swatch aligns to the FIRST line
 * rather than floating in the middle of the block. */
.map-key .mk { display: flex; align-items: flex-start; gap: 7px; }
.map-key .mk-dot, .map-key .mk-ring { margin-top: 4px; width: 9px; height: 9px; border-radius: 50%; flex: none; }
.map-key .mk-dot { background: var(--hw-red); }
.map-key .mk-ring { border: 1.6px dashed #475569; }
.map-clear {
  margin-left: auto; border: 0; background: none; padding: 0 0 0 10px; font: inherit; font-size: 12px;
  font-weight: 700; letter-spacing: 0; text-transform: none; color: var(--hw-red-deep);
  cursor: pointer; text-decoration: underline;
}

/* ---------- footer ---------- */
.site-foot { border-top: 1px solid var(--line-soft); margin-top: 40px; padding: 26px var(--pad) 40px; font-size: 13px; color: var(--ink-4); }
.site-foot a { font-weight: 600; }
.site-foot .byline { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; color: var(--ink-3); }

/* ---------- responsive ---------- */
@media (max-width: 980px) {
  .layout { grid-template-columns: 1fr; }
  #nav-toggle { display: block; }
  .nav {
    position: fixed; inset: 56px auto 0 0; width: min(300px, 84vw); background: #fff; z-index: 60;
    transform: translateX(-102%); transition: transform var(--dur-base) var(--ease-out);
    box-shadow: var(--shadow-lg); height: calc(100vh - 56px); padding-top: 16px;
  }
  body.nav-open .nav { transform: none; }
  #nav-scrim { display: block; position: fixed; inset: 56px 0 0 0; background: rgba(20,20,19,.4); z-index: 55; opacity: 0; pointer-events: none; transition: opacity var(--dur-base); }
  body.nav-open #nav-scrim { opacity: 1; pointer-events: auto; }
  .roi-grid { grid-template-columns: 1fr; }
  .artifact-blk { grid-template-columns: 1fr; }
  .artifact-blk .shot { min-height: 150px; order: -1; }
  .map-flex { grid-template-columns: 1fr; }
  .map-list { border-left: 0; border-top: 1px solid var(--line-soft); max-height: 320px; }
}

/* ---------- review-pass polish ---------- */
/* 2-col tables may shrink to fit mobile */
table.tbl.tbl-fit { min-width: 0; }
/* The ROI working table and sensitivity table are label + number pairs. Below
 * ~360px the label column cannot compress far enough and the table starts to
 * scroll inside its wrapper — readable, but a scroll nobody expects on a
 * two-column table. Tightening the type and padding makes it fit outright on a
 * folded phone instead. */
@media (max-width: 400px) {
  table.tbl.tbl-fit { font-size: 12.5px; }
  table.tbl.tbl-fit th, table.tbl.tbl-fit td { padding-left: 8px; padding-right: 8px; }
  table.tbl.tbl-fit td:first-child, table.tbl.tbl-fit th:first-child { overflow-wrap: anywhere; }
}
/* the sim keeps legible scale on mobile via internal scroll — only the drawing
 * scrolls, so the title, hint and footnote stay put while it is panned */
.sim-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
/* The drawing is a fixed 1050-unit viewBox; below ~940px the per-gun readouts
 * stop being legible, so it scrolls rather than shrinks. */
.sim-scroll svg { min-width: 720px; }
/* …but scrollable is not the same as discoverable: at 390px, 412px of the sim sits
 * off-screen, so 4 of 6 dispensers read as absent. Say it in words. */
.sim-hint {
  display: none; font-size: 11.5px; color: var(--hw-amber);
  letter-spacing: .02em; margin: 2px 0 6px;
}
@media (max-width: 760px) { .sim-hint { display: block; } }
.sim-note {
  font-size: 11.5px; line-height: 1.5; color: var(--text-on-dark-muted);
  margin-top: 8px; padding-top: 8px; border-top: 1px solid rgba(255,255,255,.1);
}
/* scenario cards: 4 across when space allows (No.1 card joins the row) */
.scen-cards { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
/* product renders must not fill a viewport */
figure.img img { max-height: 540px; object-fit: contain; background: #fff; }
.page-home figure.img img { max-height: none; object-fit: cover; }
/* FAQ: styled disclosure */
.faq summary { list-style: none; position: relative; padding: 14px 34px 14px 0; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: ''; position: absolute; right: 6px; top: 50%; width: 9px; height: 9px;
  border-right: 2px solid var(--hw-red); border-bottom: 2px solid var(--hw-red);
  transform: translateY(-70%) rotate(45deg); transition: transform var(--dur-fast) var(--ease-out);
}
.faq details[open] summary::after { transform: translateY(-30%) rotate(225deg); }
.faq .qa:hover { box-shadow: var(--shadow-xs); }
/* link rows: aligned columns instead of ragged inline text */
.link-row { display: grid; grid-template-columns: minmax(170px, 250px) 1fr auto auto; align-items: baseline; }
@media (max-width: 700px) { .link-row { grid-template-columns: 1fr auto; } .link-row .d { grid-column: 1 / -1; } }
/* Thai has no italics — render emphasis upright + semibold */
html[data-lang="th"] em, html[data-lang="th"] i, html[data-lang="th"] cite { font-style: normal; font-weight: 600; }
/* mobile top-10 rows: tighter grid */
@media (max-width: 640px) {
  .t10-row { grid-template-columns: 32px minmax(0,1fr) auto; padding: 10px 12px; gap: 8px; }
  .t10-rank { font-size: 15px; }
}
/* Folded-phone width. Three columns cannot compress far enough here: the station
 * label and the "est. sessions · 24h" caption both hit their minimum and the row
 * silently clipped ~85px with no scrollbar — the reader lost the session count
 * entirely. Drop to two columns and let the count sit under the name. */
@media (max-width: 400px) {
  .t10-row { grid-template-columns: 30px minmax(0,1fr); }
  .t10-val { grid-column: 2; text-align: left; margin-top: 4px; }
  .t10-val small { display: inline; margin-left: 5px; }
  .t10-row b { overflow-wrap: anywhere; }
}
/* map list header hint */
.map-list-head {
  position: sticky; top: 0; background: #fff; z-index: 2; font-size: 12px; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--ink-4);
  padding: 10px 16px; border-bottom: 1px solid var(--line);
  display: flex; align-items: center; gap: 8px;
}

/* ---------- contact capture ---------- */
.lead-form { display: grid; gap: 12px; margin-top: 16px; text-align: left; }
.lead-field { display: grid; gap: 5px; }
.lead-field label { font-size: 13px; font-weight: 600; }
.lead-field .req { color: var(--hw-red); }
.lead-field input, .lead-field textarea {
  font: inherit; font-size: 15px; padding: 10px 12px; border-radius: var(--radius);
  border: 1px solid var(--line); background: #fff; color: var(--ink); width: 100%;
}
.lead-field textarea { resize: vertical; min-height: 76px; }
.lead-field input:focus-visible, .lead-field textarea:focus-visible { outline: 2px solid var(--hw-red); outline-offset: 1px; }
.lead-consent { display: grid; grid-template-columns: auto minmax(0,1fr); gap: 9px; align-items: start; }
.lead-consent input { margin-top: 3px; width: 17px; height: 17px; flex: none; }
.lead-consent label { font-size: 12.5px; line-height: 1.5; }
.lead-status { font-size: 13.5px; line-height: 1.5; min-height: 1em; }
.lead-status.good { color: #128A40; font-weight: 600; }
.lead-status.warn { color: var(--hw-red-deep); }
/* Now holds a prompt plus the LINE button rather than a line of text, so it
 * needs to wrap on a phone instead of pushing the button off the edge. */
.lead-alt { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; font-size: 13px; margin-top: 16px; padding-top: 16px; border-top: 1px solid rgba(255,255,255,.14); color: var(--text-on-dark-muted); }
.lead-alt .btn { padding: 8px 18px; font-size: 14px; }
.lead-blk .btn-row { justify-content: flex-start; }
/* the block is a CTA, but a form inside a centred CTA reads badly */
.cta-blk.lead-blk { text-align: left; }

.hero-stamp { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; margin-top: 12px; }
.hero-stamp-note { font-size: 11.5px; color: var(--text-on-dark-muted); line-height: 1.5; }

/* ---------- data-state stamp (live vs cached) ----------
 * Killing the feed used to leave the same UI with a confident timestamp off the
 * bundled fallback — on a site whose tagline is "you can verify", that is the
 * one thing that must not happen. Colour + words, never colour alone. */
.feed-stamp {
  display: inline-flex; align-items: center; gap: 7px; margin-top: 10px;
  font-size: 12.5px; color: var(--ink-3);
  padding: 5px 11px 5px 9px; border-radius: var(--radius-pill);
  background: var(--surface-3); border: 1px solid var(--line-soft);
}
.feed-stamp .fs-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ink-4); flex: none; }
.feed-stamp.live { background: #EAF7EF; border-color: #BFE3CC; color: #14622F; }
.feed-stamp.live .fs-dot { background: #128A40; }
.feed-stamp.stale { background: var(--surface-3); border-color: var(--line); color: var(--ink-3); }
.feed-stamp.stale .fs-dot { background: var(--hw-orange); }
/* on the dark hero the same component has to invert */
.hero .feed-stamp { background: rgba(255,255,255,.08); border-color: rgba(255,255,255,.16); color: var(--text-on-dark-muted); }
.hero .feed-stamp.live { background: rgba(18,138,64,.22); border-color: rgba(33,196,90,.4); color: #A9EFC2; }
.hero .feed-stamp.live .fs-dot { background: #21C45A; }
.hero .feed-stamp.stale .fs-dot { background: var(--hw-amber); }

/* ---------- mobile: wide tables reflow into stacked cards ----------
 * Ten-persona test, 2026-07-27: at 360–390px the last column of every wide table
 * sat off-screen with no scroll affordance — including the Price column on
 * Configurations, the page that exists to show the price. The wrap was swipeable
 * but nothing said so, so the column read as missing. Below 640px each row
 * becomes its own card and every cell renders its column header from data-label
 * (set in render.js). Only .stackable wraps reflow — the ROI page's 2-column
 * label/value tables (.tbl-fit) already fit and stay as they are. */
@media (max-width: 640px) {
  .tbl-wrap.stackable { overflow-x: visible; background: transparent; border: 0; box-shadow: none; border-radius: 0; }
  .stackable table.tbl { min-width: 0; display: block; }
  .stackable table.tbl thead { display: none; }
  .stackable table.tbl tbody { display: grid; gap: 10px; }
  .stackable table.tbl tr {
    display: block; background: #fff; border: 1px solid var(--line-soft);
    border-radius: var(--radius); box-shadow: var(--shadow-xs); padding: 2px 14px;
  }
  .stackable table.tbl td {
    display: grid; grid-template-columns: minmax(0, 38%) minmax(0, 62%);
    gap: 12px; align-items: baseline; padding: 10px 0;
    border-bottom: 1px solid var(--line-soft);
  }
  .stackable table.tbl tr td:last-child { border-bottom: 0; }
  .stackable table.tbl td::before {
    content: attr(data-label);
    font-size: 11px; letter-spacing: .05em; text-transform: uppercase;
    color: var(--ink-4); font-weight: 700; line-height: 1.4;
  }
  /* first cell of each card is the row's subject — render it as the card title */
  .stackable table.tbl td:first-child {
    display: block; font-weight: 700; color: var(--ink); font-size: 15px;
    padding-top: 12px;
  }
  .stackable table.tbl td:first-child::before { display: none; }
  /* numeric cells keep the mono face but stop right-aligning into a 2-col grid */
  .stackable table.tbl td.num { text-align: left; white-space: normal; }
  .stackable .tbl-note { border-top: 0; padding: 8px 2px 0; }

  /* ROI assumption ledger — 3 columns (label · value · source), no thead */
  .ledger { overflow-x: visible; }
  .ledger table, .ledger tbody, .ledger tr, .ledger td { display: block; }
  .ledger tr { padding: 10px 0; border-bottom: 1px solid var(--line-soft); }
  .ledger tr:last-child { border-bottom: 0; }
  .ledger td { padding: 1px 0; border-bottom: 0; }
  .ledger td:first-child { font-weight: 600; color: var(--ink); }
  .ledger td.val { font-size: 14px; color: var(--ink); white-space: normal; }
  .ledger td.src { font-size: 11.5px; line-height: 1.45; margin-top: 2px; }
}

/* ---------- reduced motion / print ---------- */
@media (prefers-reduced-motion: reduce) {
  .busline { animation: none; }
  .chip .dot { animation: none; }
  * { transition: none !important; scroll-behavior: auto; }
  html { scroll-behavior: auto; }   /* beats the top-level html { scroll-behavior: smooth } (a type selector outranks *) */
}
/* --- narrow-viewport grid + button fixes (found by tests/form_factors.py, 2026-07-30) ---
 * At 280px — a folded Galaxy Fold / Flip cover screen, the narrowest real viewport — a
 * bare `minmax(260px, 1fr)` track cannot shrink below its 260px minimum, so the grid grew
 * wider than the page and silently clipped its right-hand edge: 12px off .cards.c2, 32px
 * off .cards.c3 and .compare. min(100%, …) lets the track collapse to the container when
 * the container is the smaller of the two and changes nothing at any wider width.
 * This was a site-wide defect (home, journey, tools, ralph and the pool), so it is fixed
 * here at the root rather than overridden per page. */
.cards.c2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); }
.cards.c3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); }
.compare { grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); }
.stats { grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr)); }
.scen-cards { grid-template-columns: repeat(auto-fit, minmax(min(100%, 170px), 1fr)); }
/* Same class of bug on the buttons: the contact block's second button is the literal
 * email address, which at 15px with 22px of side padding is wider than a folded phone's
 * content column, so .btn-row overflowed by 34px on every page that carries a CTA. A
 * button may now wrap its label and may never exceed its row. */
.btn { max-width: 100%; overflow-wrap: anywhere; }
@media (max-width: 380px) {
  .btn { padding-inline: 15px; font-size: 14.5px; }
  .cta-blk { padding: 22px 18px; }
}

/* ================= investible locations (pool) — Ralph, 2026-07-30 =================
 * The map view deliberately reuses .map-wrap/.map-flex/.map-list/.map-st/.pin from the
 * station map, so it inherits that page's already-tested responsive collapse rather
 * than growing a second set of breakpoints that can disagree with it.
 * Sizing rule throughout: every grid track is minmax(0, …) and every horizontal strip
 * either wraps or scrolls, so the page survives a 280px folded phone, a 1024px tablet
 * and a desktop without a horizontal scrollbar on the body. */

/* --- data stamp --- */
.pool-stamp { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--ink-3); }
.pool-stamp .ps-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ink-4); flex: none; }

/* --- controls --- */
.pool-bar {
  display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
  padding: 12px; background: var(--surface-2); border: 1px solid var(--line-soft);
  border-radius: var(--radius);
}
.pool-view { display: inline-flex; border: 1px solid var(--line); border-radius: var(--radius-pill); overflow: hidden; background: #fff; }
.pool-view button {
  border: 0; background: none; padding: 8px 15px; font: inherit; font-size: 13.5px;
  font-weight: 600; color: var(--ink-3); cursor: pointer;
}
.pool-view button.on { background: var(--hw-red); color: #fff; }
.pool-sel { display: inline-flex; min-width: 0; }
.pool-sel select, .pool-q {
  font: inherit; font-size: 13.5px; color: var(--ink); background: #fff;
  border: 1px solid var(--line); border-radius: var(--radius-pill); padding: 8px 13px;
  max-width: 100%; min-width: 0;
}
.pool-q { flex: 1 1 190px; }
.pool-sel select:focus-visible, .pool-q:focus-visible, .pool-view button:focus-visible { outline: 2px solid var(--hw-red); outline-offset: 1px; }
.pool-count { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; font-size: 13.5px; color: var(--ink-3); margin: 4px 0 12px; }
.pool-count .pc-of { color: var(--ink-4); }
.pool-clear { border: 0; background: none; padding: 0; font: inherit; font-size: 13px; font-weight: 700; color: var(--hw-red-deep); cursor: pointer; text-decoration: underline; }

/* --- states --- */
.pool-load { display: grid; justify-items: center; gap: 12px; text-align: center; color: var(--ink-3); padding: 40px 18px; }
.pl-spin { width: 26px; height: 26px; border: 3px solid var(--line); border-top-color: var(--hw-red); border-radius: 50%; animation: poolspin .8s linear infinite; }
@keyframes poolspin { to { transform: rotate(360deg); } }
.pool-empty { text-align: center; padding: 40px 18px; background: #fff; border: 1px dashed var(--line); border-radius: var(--radius); }
.pool-empty p { margin: 0; font-weight: 600; }
.pool-empty .pe-sub { margin-top: 6px; font-weight: 400; font-size: 13.5px; color: var(--ink-3); }
.pool-note { font-size: 12.5px; line-height: 1.55; color: var(--ink-3); background: var(--surface-2); border-left: 3px solid var(--line); border-radius: 0 var(--radius) var(--radius) 0; padding: 10px 13px; margin-top: 12px; }
.pool-note.warn { border-left-color: var(--hw-amber); color: var(--ink-2); }

/* --- cards --- */
.pool-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 268px), 1fr)); gap: 14px; }
.pool-card {
  display: flex; flex-direction: column; gap: 8px; min-width: 0;
  background: #fff; border: 1px solid var(--line-soft); border-radius: var(--radius);
  box-shadow: var(--shadow-xs); padding: 14px; cursor: pointer;
  transition: box-shadow var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
}
.pool-card:hover { box-shadow: var(--shadow-card); transform: translateY(-2px); }
.pool-card:focus-visible { outline: 0; box-shadow: var(--focus-ring); }
.pk-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.pk-score { font-family: var(--font-mono); font-size: 14px; font-weight: 700; color: #fff; border-radius: var(--radius-pill); padding: 3px 10px; }
.pk-band { font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; }
.pk-id { margin-left: auto; font-family: var(--font-mono); font-size: 11px; color: var(--ink-4); }
.pk-name { margin: 0; font-size: 15.5px; line-height: 1.35; font-weight: 700; }
.pk-meta { font-size: 12.5px; color: var(--ink-3); display: flex; gap: 6px; flex-wrap: wrap; }
.pk-meta .dot { color: var(--ink-4); }
.pk-spec { display: flex; gap: 6px; flex-wrap: wrap; }
.pk-spec .pv { font-size: 12px; font-weight: 700; background: var(--hw-red-tint); color: var(--hw-red-dark); border-radius: var(--radius-pill); padding: 3px 9px; }
.pk-spec .pv.soft { background: var(--surface-3); color: var(--ink-3); }
.pk-comp { font-size: 12.5px; color: var(--ink-2); }
.pk-am { display: flex; gap: 5px; flex-wrap: wrap; }
.pchip { display: inline-block; font-size: 11.5px; font-weight: 600; color: var(--ink-3); background: var(--surface-2); border: 1px solid var(--line-soft); border-radius: var(--radius-pill); padding: 2px 9px; }
.pchip.more { color: var(--ink-4); }
.pk-go { display: flex; align-items: center; gap: 6px; margin-top: auto; padding-top: 4px; font-size: 13px; font-weight: 700; color: var(--hw-red-deep); }
.pool-card:hover .pk-go .arr { transform: translateX(3px); }
.pk-go .arr { transition: transform var(--dur-fast) var(--ease-out); }

/* --- map extras --- */
.pool-legend { display: flex; gap: 14px; flex-wrap: wrap; padding: 4px 4px 10px; font-size: 12px; color: var(--ink-3); }
.pool-legend i { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: 5px; }
.pool-legend .pl-hint { color: var(--ink-4); }
.map-st.pool-st .open { font-family: var(--font-mono); font-weight: 700; }

/* --- detail --- */
/* "← All locations" is the only way back out of a location on a phone, so it gets a real
 * 40px target rather than the 21px a bare text button gives (tests/form_factors.py). */
.pool-back {
  display: inline-flex; align-items: center; align-self: start; border: 0; background: none;
  padding: 9px 0; min-height: 40px; font: inherit; font-size: 13.5px; font-weight: 700;
  color: var(--hw-red-deep); cursor: pointer;
}
.pool-back:hover { text-decoration: underline; }
.pool-back:focus-visible { outline: 2px solid var(--hw-red); outline-offset: 2px; border-radius: 4px; }
.pool-back.bottom { margin-top: 8px; }
.pd-head { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 16px; align-items: start; }
.pd-id { font-family: var(--font-mono); font-size: 12px; color: var(--ink-4); letter-spacing: .04em; }
.pd-head .page-title { margin: 4px 0 6px; }
.pd-meta { display: flex; gap: 7px; flex-wrap: wrap; font-size: 13.5px; color: var(--ink-3); }
.pd-meta .dot { color: var(--ink-4); }
.pd-score { flex: none; text-align: center; border: 2px solid; border-radius: var(--radius-lg); padding: 10px 16px; background: #fff; }
.pd-score .pv { font-family: var(--font-mono); font-size: clamp(24px, 6vw, 32px); font-weight: 800; line-height: 1; }
.pd-score .pl { font-size: 11px; color: var(--ink-4); margin-top: 2px; }
.pd-score .pb { font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; margin-top: 4px; }
.pd-summary { background: var(--surface-2); border-left: var(--accent-bar); border-radius: 0 var(--radius) var(--radius) 0; padding: 14px 16px; }
.pd-summary p { margin: 0; font-size: 14.5px; line-height: 1.65; color: var(--ink-2); }

/* option picker */
.pd-opts { display: flex; gap: 10px; flex-wrap: wrap; }
.pd-opt {
  flex: 1 1 150px; min-width: 0; display: grid; gap: 2px; text-align: left; cursor: pointer;
  font: inherit; background: #fff; border: 1px solid var(--line); border-radius: var(--radius); padding: 10px 12px;
}
.pd-opt .n { font-size: 14px; font-weight: 700; }
.pd-opt .g { font-size: 12px; color: var(--ink-3); }
.pd-opt .rec { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--charge-green); }
.pd-opt.on { border-color: var(--hw-red); background: var(--hw-red-tint); box-shadow: inset 0 0 0 1px var(--hw-red); }
.pd-opt:focus-visible { outline: 2px solid var(--hw-red); outline-offset: 1px; }
.pd-spec { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 92px), 1fr)); gap: 10px; margin-top: 14px; }
.pd-spec .ps { background: var(--surface-ink); color: #fff; border-radius: var(--radius); padding: 12px 10px; text-align: center; }
.pd-spec .ps .v { font-family: var(--font-mono); font-size: clamp(15px, 3.4vw, 19px); font-weight: 700; }
.pd-spec .ps .l { font-size: 11px; color: var(--text-on-dark-muted); margin-top: 3px; line-height: 1.35; }
.pd-3d { margin-top: 14px; }
.pd-3d img { width: 100%; max-width: 640px; height: auto; display: block; border-radius: var(--radius); border: 1px solid var(--line-soft); }
.pd-3d .p3-cap { font-size: 11.5px; color: var(--ink-4); margin-top: 6px; }
.pd-sug { font-size: 13.5px; color: var(--ink-2); margin: 14px 0 0; }

/* score breakdown */
.pd-fnote { font-size: 13.5px; color: var(--ink-3); margin: 0 0 12px; }
.pd-f { border-bottom: 1px solid var(--line-soft); }
.pd-f summary {
  list-style: none; cursor: pointer; display: grid; align-items: center; gap: 4px 10px;
  grid-template-columns: minmax(0, 1fr) auto minmax(60px, 130px) 38px; padding: 11px 0;
}
.pd-f summary::-webkit-details-marker { display: none; }
.pd-f summary:hover .pf-l { color: var(--hw-red-deep); }
.pd-f .pf-l { font-size: 14px; font-weight: 600; }
.pd-f .pf-w { font-family: var(--font-mono); font-size: 11.5px; color: var(--ink-4); }
.pd-f .pf-bar { height: 7px; border-radius: var(--radius-pill); background: var(--surface-3); overflow: hidden; }
.pd-f .pf-bar i { display: block; height: 100%; border-radius: var(--radius-pill); }
.pd-f .pf-s { font-family: var(--font-mono); font-size: 13px; font-weight: 700; text-align: right; }
.pf-body { padding: 0 0 14px; display: grid; gap: 6px; }
.pf-in { font-size: 13.5px; font-weight: 600; color: var(--ink-2); }
.pf-ms { font-size: 12.5px; line-height: 1.6; color: var(--ink-3); }
.pd-weights { font-size: 11.5px; color: var(--ink-4); margin-top: 10px; }

/* site context */
.pd-ctx { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr)); gap: 14px; }
.pc-item { background: #fff; border: 1px solid var(--line-soft); border-radius: var(--radius); box-shadow: var(--shadow-xs); padding: 14px; min-width: 0; }
.pc-k { font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-4); }
.pc-v { font-size: 14.5px; font-weight: 600; margin-top: 5px; }
.pc-v.chips { display: flex; gap: 5px; flex-wrap: wrap; font-weight: 400; }
.pc-n { font-size: 11.5px; line-height: 1.55; color: var(--ink-4); margin-top: 7px; }
.pc-link { display: inline-block; margin-top: 8px; font-size: 13px; font-weight: 700; }

/* the reference chip on a location-tagged lead form */
.lead-ref { display: inline-flex; align-items: center; gap: 8px; margin-top: 10px; background: var(--surface-2); border: 1px solid var(--line-soft); border-radius: var(--radius-pill); padding: 5px 12px; max-width: 100%; }
.lead-ref .lr-k { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-4); flex: none; }
.lead-ref .lr-v { font-family: var(--font-mono); font-size: 12.5px; font-weight: 600; color: var(--ink-2); overflow-wrap: anywhere; }

@media (max-width: 760px) {
  .pd-head { grid-template-columns: minmax(0, 1fr); }
  .pd-score { justify-self: start; display: flex; align-items: baseline; gap: 9px; padding: 8px 14px; }
  .pd-score .pl, .pd-score .pb { margin-top: 0; }
  /* the factor row loses the weight column first — the bar and score carry the meaning */
  .pd-f summary { grid-template-columns: minmax(0, 1fr) auto 34px; }
  .pd-f .pf-bar { grid-column: 1 / -1; }
}
@media (max-width: 520px) {
  .pool-bar { gap: 8px; padding: 10px; }
  /* stacked full-width controls beat a cramped row: each stays tappable at 44px */
  .pool-sel, .pool-sel select, .pool-q { flex: 1 1 100%; width: 100%; }
  .pool-view { width: 100%; }
  .pool-view button { flex: 1; }
  .pool-cards { grid-template-columns: minmax(0, 1fr); }
  .pd-opts { gap: 8px; }
  .pd-opt { flex: 1 1 100%; }
  /* auto-fit gives 3-across then a lonely 4th on a 390px phone; a fixed 2×2 reads as
   * one block of specs instead of a ragged row */
  .pd-spec { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (prefers-reduced-motion: reduce) {
  .pool-card, .pk-go .arr { transition: none; }
  .pool-card:hover { transform: none; }
  .pl-spin { animation-duration: 2.4s; }
}

@media print {
  .topbar, .nav, #nav-scrim, .site-foot .no-print, .btn-row, .cta-blk { display: none !important; }
  .layout { display: block; }
  body { background: #fff; }
  .page { max-width: none; padding: 0; }
  .panel, .scen, .stat, .chart-wrap { box-shadow: none; break-inside: avoid; }
}

/* The hand-off from the hero's power-sharing diagram to the full simulator.
   Sits inside the dark hero, so it is styled against ink, not paper. */
.sim-cta {
  display: flex; align-items: center; gap: 13px; margin-top: 18px;
  background: rgba(255,255,255,.07); border: 1px solid rgba(255,255,255,.16);
  border-radius: var(--radius); padding: 13px 16px; color: #fff;
  transition: background var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out);
}
.sim-cta:hover { text-decoration: none; background: rgba(255,255,255,.12); border-color: var(--hw-amber); }
.sim-cta-ic { font-size: 21px; line-height: 1; flex: none; }
.sim-cta-tx { min-width: 0; }
.sim-cta-tx b { display: block; font-size: 15.5px; font-weight: 700; letter-spacing: -0.01em; }
.sim-cta-tx span { display: block; font-size: 13px; color: var(--text-on-dark-muted); margin-top: 1px; }
.sim-cta-go { margin-left: auto; color: var(--hw-amber); font-size: 17px; flex: none; }
@media (prefers-reduced-motion: reduce) { .sim-cta { transition: none; } }

/* ============================================================================
   #/simulator — the charging race.
   A telemetry surface, not a brochure: mono tabular figures that do not jitter
   as they count, hairline rules, and colour reserved for the one thing colour
   is carrying — WHY a gun is not going faster. Everything decorative is grey.
   ========================================================================== */
.simx { display: block; }
.sim-loading { color: var(--ink-4); font-size: 14px; }

.sim-hero {
  background: var(--surface-ink); color: var(--text-on-dark);
  border-radius: var(--radius-lg); padding: clamp(24px, 4.5vw, 46px);
  background-image: repeating-linear-gradient(90deg, rgba(255,255,255,.05) 0 1px, transparent 1px 44px);
}
.sim-hero .eyebrow { color: var(--hw-amber); }
.sim-hero h1 {
  font-size: clamp(30px, 5vw, 52px); line-height: 1.03; letter-spacing: -0.025em;
  margin: 10px 0 12px; font-weight: 800; max-width: 16ch;
}
.sim-hero .sub { color: var(--text-on-dark-muted); font-size: clamp(15px, 1.7vw, 17.5px); max-width: 62ch; margin: 0; }

/* ---- station pickers ---- */
.sim-pick { display: flex; align-items: end; gap: 12px; margin: 22px 0 14px; flex-wrap: wrap; }
/* 340px basis, not 260: the two pickers now stack one per row below ~740px,
   because a station name carrying its best connector needs ~310px and a
   <select> can neither wrap nor ellipsize what it is showing. */
.sim-pick label { display: flex; flex-direction: column; gap: 5px; flex: 1 1 340px; min-width: 0; }
.sim-pick label > span { font-size: 11.5px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-4); }
.sim-vs { font-family: var(--font-mono); font-size: 13px; color: var(--ink-4); padding-bottom: 10px; }
.sim-sel, .sim-carsel {
  font: inherit; font-size: 14.5px; font-weight: 600; color: var(--ink);
  background: #fff; border: 1px solid var(--line); border-radius: 8px;
  padding: 9px 11px; width: 100%; min-width: 0;
}
.sim-pick-1 .sim-sel { border-left: 3px solid var(--hw-red); }
.sim-pick-2 .sim-sel { border-left: 3px solid var(--ink-3); }

/* ---- spec cards ---- */
.sim-specs { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.sim-spec {
  background: #fff; border: 1px solid var(--line-soft); border-top: 3px solid var(--ink-3);
  border-radius: var(--radius); padding: 15px 16px; box-shadow: var(--shadow-xs);
}
.sim-spec.hw { border-top-color: var(--hw-red); }
.sim-spec-brand { font-size: 11.5px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-4); }
.sim-spec-name { font-size: 17px; font-weight: 700; letter-spacing: -0.01em; margin-top: 2px; }
.sim-spec-kind { font-size: 12.5px; color: var(--ink-3); margin-top: 2px; }
.sim-spec-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(78px, 1fr)); gap: 10px 8px; margin: 13px 0 0; }
.sim-spec-grid b { display: block; font-family: var(--font-mono); font-size: 15px; font-weight: 600; letter-spacing: -0.02em; }
.sim-spec-grid span { display: block; font-size: 11px; color: var(--ink-4); line-height: 1.3; margin-top: 1px; }
.sim-spec-photos { display: flex; gap: 10px; align-items: flex-end; height: 96px; margin-top: 13px; }
.sim-spec-photos img { height: 100%; width: auto; object-fit: contain; }
.sim-spec-note { font-size: 12.5px; color: var(--ink-3); margin: 11px 0 0; }
.sim-spec-src { font-size: 11.5px; color: var(--ink-4); margin: 8px 0 0; }

/* ---- fleet ---- */
.sim-note { font-size: 13.5px; color: var(--ink-3); margin: 0 0 12px; max-width: 78ch; }
.sim-socrow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 0 0 10px; }
/* The four fill/share pills, on their own line so the flexible sliders above cannot
   shove them to a different alignment from each other (Ralph, 2026-08-02). */
.sim-btnrow { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; margin: 0 0 8px; }
.sim-soclab { font-size: 12px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-4); }
.sim-range { flex: 1 1 110px; max-width: 190px; accent-color: var(--hw-red); }
.sim-socval { font-family: var(--font-mono); font-size: 14px; min-width: 10ch; }
.sim-btn {
  font: inherit; font-size: 13.5px; font-weight: 600; cursor: pointer;
  background: #fff; color: var(--ink); border: 1px solid var(--line);
  border-radius: var(--radius-pill); padding: 8px 15px; display: inline-flex; align-items: center; gap: 7px;
}
.sim-btn:hover { border-color: var(--ink-4); }
.sim-btn.primary { background: var(--ink); color: #fff; border-color: var(--ink); }

/* 224px, not 168: the longest option in the picker is "GEELY RIDDARA RD6 · 100 kW",
   and at 168 the select cut it off with no scrollbar at every width from 768 up
   (form_factors.py). Widening the slot keeps the rated kW in the dropdown, where it
   is what you choose on. */
.sim-slots { display: grid; grid-template-columns: repeat(auto-fill, minmax(224px, 1fr)); gap: 9px; margin-bottom: 24px; }
.sim-slot {
  background: #fff; border: 1px solid var(--line-soft); border-radius: var(--radius);
  padding: 9px 10px 10px; position: relative;
}
.sim-slot-n {
  position: absolute; top: 8px; right: 9px; font-family: var(--font-mono);
  font-size: 11px; color: var(--ink-4);
}
.sim-slot .sim-carsel { font-size: 13px; font-weight: 600; padding: 6px 8px; padding-right: 22px; }
.sim-slot-spec { display: flex; gap: 7px; flex-wrap: wrap; margin-top: 7px; font-size: 11px; color: var(--ink-4); }
.sim-slot-spec span { font-family: var(--font-mono); }
.sim-slot-spec .kw { color: var(--ink-2); font-weight: 600; }
.sim-slot-spec .tier-A { color: var(--hw-green); font-weight: 600; }
.sim-slot-spec .tier-B { color: var(--hw-teal); }
.sim-slot-spec .tier-C { color: var(--hw-orange); }
.sim-slot-soc { display: flex; align-items: center; gap: 4px; margin-top: 7px; }
.sim-soci {
  font: inherit; font-family: var(--font-mono); font-size: 12.5px; width: 4.4em;
  border: 1px solid var(--line); border-radius: 6px; padding: 3px 5px; text-align: right;
  -moz-appearance: textfield; appearance: textfield;
}
/* The spinners stole the width that "100" needs, and clipped it to "10(". */
.sim-soci::-webkit-outer-spin-button, .sim-soci::-webkit-inner-spin-button {
  -webkit-appearance: none; margin: 0;
}
.sim-slot-soc span { font-size: 12px; color: var(--ink-4); }
.sim-slot-empty { font-size: 11.5px; color: var(--ink-4); margin-top: 7px; }

/* ---- transport ---- */
.sim-transport {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  background: var(--surface-ink); color: #fff; border-radius: var(--radius);
  padding: 11px 14px; margin-bottom: 14px; position: sticky; top: 60px; z-index: 20;
}
.sim-play {
  font: inherit; font-size: 15px; line-height: 1; cursor: pointer; flex: none;
  width: 42px; height: 42px; border-radius: 50%; border: 0;
  background: var(--hw-red); color: #fff;
}
.sim-play:hover { background: var(--hw-red-deep); }
.sim-clock { font-family: var(--font-mono); font-size: 15px; min-width: 5ch; font-variant-numeric: tabular-nums; }
.sim-scrub { flex: 1 1 180px; min-width: 120px; accent-color: var(--hw-amber); }
.sim-speeds { display: flex; border: 1px solid rgba(255,255,255,.2); border-radius: var(--radius-pill); overflow: hidden; flex: none; }
.sim-speeds button {
  font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer;
  background: transparent; color: rgba(255,255,255,.7); border: 0; padding: 6px 13px;
}
.sim-speeds button.active { background: #fff; color: var(--ink); }

/* ---- live forecourts ---- */
.sim-courts { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 26px; }
.sim-court {
  background: #fff; border: 1px solid var(--line-soft); border-radius: var(--radius);
  padding: 13px 14px 12px; border-top: 3px solid var(--ink-3);
}
.sim-court.hw { border-top-color: var(--hw-red); }
.sim-court-head { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.sim-court-head b { font-size: 14px; letter-spacing: -0.01em; }
.sim-court-kw { font-family: var(--font-mono); font-size: 17px; font-weight: 600; font-variant-numeric: tabular-nums; }
.sim-court-bar { height: 5px; border-radius: 3px; background: var(--surface-3); margin: 8px 0 11px; overflow: hidden; }
.sim-court-bar i { display: block; height: 100%; background: var(--hw-amber); border-radius: 3px; transition: width 90ms linear; }
.sim-court.hw .sim-court-bar i { background: var(--hw-red); }
.sim-bays { display: grid; grid-template-columns: repeat(auto-fill, minmax(74px, 1fr)); gap: 6px; }

/* ---- sealed power pools (all-in-one boxes, Tesla cabinets) ---------------
   A machine whose connectors cannot share power with the connectors beside them is
   drawn as the separate machine it is. The flat auto-fill grid above is kept for the
   architectures that really are one pool — the A5 cabinet and the split-type rivals —
   so the difference between "one pool of twelve" and "six pools of two" is visible
   before a word of the copy is read. */
/* Flex rather than grid, because a pool's width has to follow how many connectors are
   in it: a 2-gun all-in-one box and a 4-post Tesla cabinet cannot share one column
   width without the cabinet squeezing its posts into an unreadable 40 px. Each pool
   asks for room proportional to its connector count (--n, set inline) and the row
   wraps when it runs out. */
.sim-bays.grouped { display: flex; flex-wrap: wrap; gap: 8px; }
.sim-pool {
  border: 1px solid var(--line); border-radius: 9px; padding: 6px 6px 7px;
  background: var(--surface-3); min-width: 0;
  flex: 1 1 176px;
}
.sim-pool-head {
  display: flex; justify-content: space-between; align-items: baseline; gap: 6px;
  padding: 0 2px 4px; font-size: 10px; color: var(--ink-3);
}
.sim-pool-head b { font-size: 10.5px; color: var(--ink-2); letter-spacing: -0.01em; }
.sim-pool-kw { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.sim-pool-bar {
  height: 3px; border-radius: 2px; background: var(--surface-1);
  margin: 0 2px 6px; overflow: hidden;
}
.sim-pool-bar i {
  display: block; height: 100%; background: var(--hw-amber); border-radius: 2px;
  transition: width 90ms linear;
}
/* Pegged and empty are the two states the argument is made of, so they are the two
   states that get a colour: a box at its ceiling beside a box at zero. */
.sim-pool.full { border-color: #F3C9CE; background: #FFF6F6; }
.sim-pool.full .sim-pool-bar i { background: var(--hw-red-deep); }
.sim-pool.full .sim-pool-kw { color: var(--hw-red-deep); font-weight: 600; }
.sim-pool.idle { opacity: 0.72; }
.sim-pool-bays { display: grid; gap: 5px; }
.bay {
  border: 1px solid var(--line-soft); border-radius: 7px; padding: 5px 6px 0;
  background: var(--surface-2); position: relative; overflow: hidden; min-width: 0;
}
.bay.on { background: #fff; border-color: var(--line); }
.bay-n {
  display: flex; justify-content: space-between; gap: 4px;
  font-family: var(--font-mono); font-size: 9.5px; color: var(--ink-4);
}
.bay-a { color: var(--ink-3); cursor: help; }
/* Wraps to two lines rather than truncating. An ellipsis is invisible to a phone
   reader with no hover, and mobile_audit.py is right to call a nowrap box that
   overflows its bay "silently clipped" — so the text is made to fit instead. */
.bay-car {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  font-size: 10px; line-height: 1.15; color: var(--ink-3);
  overflow: hidden; overflow-wrap: anywhere; min-height: 23px;
}
.bay-kw {
  display: block; font-family: var(--font-mono); font-size: 14px; font-weight: 600;
  letter-spacing: -0.02em; font-variant-numeric: tabular-nums; color: var(--ink-4);
}
.bay.on .bay-kw { color: var(--ink); }
.bay-soc { height: 3px; background: var(--surface-3); border-radius: 2px; margin: 3px 0 4px; overflow: hidden; }
.bay-soc i { display: block; height: 100%; background: var(--charge-green); transition: width 90ms linear; }
.bay-lim { height: 4px; margin: 0 -6px; background: transparent; }
.sim-queue { font-size: 12px; color: var(--ink-4); margin-top: 9px; min-height: 1.2em; }
.sim-queue.on { color: var(--hw-red-deep); font-weight: 600; }

/* ---- the tape ---- */
.sim-legend { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: 0 0 14px; }
.sim-lg { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--ink-3); cursor: help; }
.sim-lg i { width: 11px; height: 11px; border-radius: 3px; flex: none; }
.sim-tapes { display: grid; gap: 12px; margin-bottom: 26px; }
.sim-tape { margin: 0; background: #fff; border: 1px solid var(--line-soft); border-left: 3px solid var(--ink-3); border-radius: var(--radius); padding: 10px 12px 12px; }
.sim-tape.hw { border-left-color: var(--hw-red); }
.sim-tape figcaption { font-size: 12.5px; font-weight: 700; color: var(--ink-2); margin-bottom: 8px; }
.sim-tape-cv { width: 100%; display: block; }

/* ---- scoreboard ---- */
.sim-score { background: #fff; border: 1px solid var(--line-soft); border-radius: var(--radius); overflow: hidden; }
.sim-score-head, .sim-score-row { display: grid; grid-template-columns: minmax(0,1.35fr) 1fr 1fr; gap: 12px; align-items: center; padding: 9px 14px; }
.sim-score-head { border-bottom: 2px solid var(--line); font-size: 11.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-4); }
.sim-score-head b { font-size: 12px; letter-spacing: 0; text-transform: none; }
.sim-score-head .a { color: var(--hw-red-deep); }
.sim-score-row { border-bottom: 1px solid var(--line-soft); }
.sim-score-row:last-child { border-bottom: 0; }
.sim-score-lab span { font-size: 13.5px; color: var(--ink-2); display: block; }
.sim-score-lab small { font-size: 11.5px; color: var(--ink-4); display: block; margin-top: 2px; line-height: 1.35; }
.sim-score-v { position: relative; padding: 3px 0; min-width: 0; }
.sim-score-v i {
  display: block; height: 4px; border-radius: 2px; background: var(--surface-3);
  margin-bottom: 4px; transition: width 160ms var(--ease-out);
}
.sim-score-v b { font-family: var(--font-mono); font-size: 14.5px; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--ink-3); }
.sim-score-v.win b { color: var(--ink); }
.sim-score-v.win i { background: var(--charge-green); }

.sim-assume { margin-top: 18px; background: var(--surface-3); border-radius: var(--radius); padding: 15px 18px; }
.sim-assume h3 { font-size: 13px; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-4); margin: 0 0 9px; }
.sim-assume ul { margin: 0; padding-left: 18px; }
.sim-assume li { font-size: 13px; color: var(--ink-2); margin-bottom: 7px; line-height: 1.5; }
.sim-toggle { display: flex; gap: 9px; align-items: flex-start; margin-top: 12px; font-size: 13px; color: var(--ink-2); cursor: pointer; }
.sim-toggle input { margin-top: 3px; accent-color: var(--hw-red); flex: none; }
.sim-assume .sim-note { margin: 7px 0 0; font-size: 12px; }

@media (max-width: 860px) {
  .sim-specs, .sim-courts { grid-template-columns: 1fr; }
  .sim-transport { top: 56px; }
}
@media (max-width: 560px) {
  .sim-slots { grid-template-columns: 1fr 1fr; }
  /* One car per row on a phone. Two columns left roughly 124 px for an option like
     "GEELY RIDDARA RD6 · 100 kW", which the select then cut off with no way to see
     the rest — and a full-width row is a better tap target anyway. */
  @media (max-width: 430px) { .sim-slots { grid-template-columns: 1fr; } }
  .sim-score-head, .sim-score-row { grid-template-columns: 1fr; gap: 4px; padding: 10px 13px; }
  .sim-score-head { display: none; }
  .sim-score-v { display: grid; grid-template-columns: 1fr auto; gap: 9px; align-items: center; }
  .sim-score-v i { margin: 0; }
  .sim-score-v::before {
    content: attr(data-who); font-size: 11px; color: var(--ink-4);
    grid-column: 1 / -1; margin-bottom: -2px;
  }
  .sim-bays { grid-template-columns: repeat(auto-fill, minmax(64px, 1fr)); }

}
@media (prefers-reduced-motion: reduce) {
  .sim-court-bar i, .bay-soc i, .sim-score-v i, .sim-pool-bar i, .pb i { transition: none; }
  .sim-real-go, .sim-real-go:hover { transition: none; transform: none; }
}

/* The generated read-out at the top of the scoreboard. It is prose, not a number, so
   it gets prose measure and sits on ink to separate it from the table below. */
.sim-verdict {
  background: var(--surface-ink); color: #fff; padding: 16px 18px;
  border-bottom: 2px solid var(--line);
}
.sim-verdict p { margin: 0 0 8px; font-size: 14.5px; line-height: 1.5; max-width: 74ch; color: rgba(255,255,255,.9); }
.sim-verdict p:first-child { font-size: 16px; font-weight: 600; color: #fff; }
.sim-verdict p:last-child { margin-bottom: 0; }

/* ============================================================================
   Live connector telemetry, the brand-lock refusal, and the 100-BEV challenge.
   Added 2026-08-01.
   ========================================================================= */

/* ---- volts × amps = kilowatts -------------------------------------------
   The bay already showed kilowatts. What it could not show is where they came
   from. Volts and amps are set in two cool hues that sit outside the warm
   limit palette (green / amber / orange / red), so a reader never confuses a
   telemetry colour with a reason-it-is-limited colour. The × is not
   decoration: it is the arithmetic, and it is the point of the line. */
:root { --sim-volt: #2155CD; --sim-amp: #0E7C8A; }
.bay-va {
  display: flex; align-items: baseline; gap: 3px; font-family: var(--font-mono);
  font-size: 9.5px; font-weight: 600; font-variant-numeric: tabular-nums;
  line-height: 1.2; margin-top: 1px; min-height: 12px; white-space: nowrap;
}
.bay-v { color: var(--sim-volt); }
.bay-a-live { color: var(--sim-amp); }
.bay-x { color: var(--ink-4); font-weight: 400; }
.sim-bays { grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); }


/* ---- a car the network refuses ------------------------------------------ */
.sim-slot.refused { border-color: #F3C9CE; background: #FFFBFB; }
.sim-slot-x {
  color: var(--hw-red-deep); font-weight: 700; cursor: help;
  font-family: var(--font-sans); letter-spacing: -0.01em;
}
.sim-turned { display: none; font-size: 12px; font-weight: 600; color: var(--hw-red-deep); margin-top: 8px; }
.sim-turned.on { display: block; }
.sim-queue.on + .sim-turned.on { margin-top: 3px; }

/* ---- the way in --------------------------------------------------------- */
/* ---- Real fill + the network pulse ---------------------------------------
   Real fill is a pill in the row of pills, the same size as the dice and Clear
   (Ralph, 2026-08-02) — it does the same KIND of thing they do, so it sizes like
   them and stands out on COLOUR alone. It was a full-width red banner below the
   row, which read as a separate louder feature rather than the sibling action it
   is. It keeps its own class on top of .sim-btn rather than borrowing
   .sim-btn.primary or .sim-race-go — both of those are how the Playwright suite
   selects the random-fill and challenge controls, and a second element answering
   either selector would have three tests clicking blind. */
.sim-real-go {
  background: linear-gradient(101deg, var(--hw-red) 0%, var(--hw-red-deep) 62%, var(--hw-red-dark) 100%);
  color: #fff; border-color: transparent;
  box-shadow: 0 4px 14px rgba(199, 0, 11, .24);
  transition: transform var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
}
.sim-real-go:hover { border-color: transparent; transform: translateY(-1px); box-shadow: 0 8px 20px rgba(199, 0, 11, .32); }
.sim-real-go:active { transform: translateY(0); }
.sim-real-go.loading { opacity: .7; cursor: progress; }
/* The sentence the button used to carry as a subtitle, now under the row. */
.sim-fillnote { font-size: 12.5px; color: var(--ink-4); margin: 0 0 20px; max-width: 92ch; }

/* The chart. Twenty-four bars must FIT at 280 px — the mobile and form-factor
   gates only tolerate a horizontal scroller on an allow-listed class, and a
   chart nobody can see the right-hand end of is not a chart. 24 columns of
   minmax(0,1fr) does it at any width. */
.sim-pulse { margin: 0 0 24px; }
.sim-pulse-head {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 10px; flex-wrap: wrap; margin-bottom: 10px;
}
.sim-pulse-head h3 { font-size: 15px; font-weight: 700; letter-spacing: -0.015em; margin: 0; }
.sim-pulse-as { font-size: 11.5px; color: var(--ink-4); font-family: var(--font-mono); }
.sim-pulse-now {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); gap: 8px;
  background: var(--surface-2); border: 1px solid var(--line-soft);
  border-left: var(--accent-bar); border-radius: var(--radius);
  padding: 11px 14px; margin-bottom: 12px;
}
.pn-i b { display: block; font-size: 17px; font-weight: 700; letter-spacing: -0.02em; }
.pn-i span { display: block; font-size: 11px; color: var(--ink-3); margin-top: 1px; }
.sim-pulse-bars {
  display: grid; grid-template-columns: repeat(24, minmax(0, 1fr)); gap: 2px;
  align-items: end; height: 84px;
}
.pb { height: 100%; display: flex; align-items: flex-end; }
.pb i {
  display: block; width: 100%; background: var(--surface-3); border-radius: 2px 2px 0 0;
  transition: height var(--dur-slow) var(--ease-out);
}
.pb.on i { background: var(--hw-red); }
/* The hour the run was drawn from is the point of the chart, so it is marked twice:
   the bar is red and the column behind it is tinted, which survives a screenshot at
   9 px per bar where a 1 px outline would not. */
.pb.on { background: var(--hw-red-tint); border-radius: 3px 3px 0 0; }
.sim-pulse-axis {
  display: grid; grid-template-columns: repeat(24, minmax(0, 1fr)); gap: 2px;
  margin-top: 4px; font-family: var(--font-mono); font-size: 9px; color: var(--ink-4);
  text-align: center;
}
.sim-pulse-axis span.on { color: var(--hw-red); font-weight: 700; }
.sim-pulse-foot { font-size: 12px; color: var(--ink-3); margin: 9px 0 0; line-height: 1.55; }
.sim-pulse-err { font-size: 13px; color: var(--hw-red-deep); margin: 0; }

.sim-race-go {
  display: flex; align-items: center; gap: 13px; width: 100%; text-align: left;
  background: var(--surface-ink); color: #fff; border: 0; border-left: var(--accent-bar);
  border-radius: var(--radius); padding: 15px 18px; cursor: pointer; font: inherit;
  margin: 0 0 26px; transition: background var(--dur-base) var(--ease-out);
}
.sim-race-go:hover { background: #23231F; }
.rg-flag { font-size: 24px; line-height: 1; flex: none; }
.rg-t { min-width: 0; flex: 1; }
.rg-t b { display: block; font-size: 16px; font-weight: 800; letter-spacing: -0.015em; }
.rg-t span { display: block; font-size: 13px; color: var(--text-on-dark-muted); margin-top: 2px; }
.rg-go { font-size: 19px; color: var(--hw-amber); flex: none; }

/* ---- the start line -----------------------------------------------------
   One sweep, one line, out. It marks the switch between two quite different
   views so the reader can follow it, and then it leaves. */
.sim-flag {
  position: fixed; inset: 0; z-index: 200; display: grid; place-items: center;
  background: #0E0E0C; opacity: 0; transition: opacity 320ms var(--ease-out);
  overflow: hidden; padding: var(--pad);
}
.sim-flag.on { opacity: 1; }
.sim-flag.out { opacity: 0; }
.sim-flag::before {
  content: ''; position: absolute; inset: -40% -60%; background: var(--hw-red);
  transform: translateX(-115%) skewX(-14deg); opacity: .92;
}
.sim-flag.on::before { animation: flagsweep 1150ms var(--ease-out) forwards; }
@keyframes flagsweep {
  0%   { transform: translateX(-115%) skewX(-14deg); }
  100% { transform: translateX(115%) skewX(-14deg); }
}
/* ch resolves against THIS element's font-size, not the headline's — 22ch here was
   176 px and broke the line five ways. Sized in px against the display face instead. */
.sim-flag-in { position: relative; text-align: center; max-width: min(92vw, 880px); }
.sim-flag.on .sim-flag-in { animation: flagin 620ms var(--ease-out) 360ms both; }
@keyframes flagin { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
.sim-flag-k {
  font-family: var(--font-mono); font-size: 12px; font-weight: 600;
  letter-spacing: .34em; text-transform: uppercase; color: var(--hw-amber); margin-bottom: 10px;
}
.sim-flag h2 {
  margin: 0; color: #fff; font-size: clamp(30px, 6.5vw, 62px); line-height: 1.02;
  font-weight: 800; letter-spacing: -0.035em;
}
html[data-lang="th"] .sim-flag h2 { letter-spacing: -0.01em; line-height: 1.2; }
.sim-flag p {
  margin: 16px 0 0; color: var(--text-on-dark-muted); font-size: 13.5px;
  font-family: var(--font-mono); max-width: none;
}

/* ---- the track ----------------------------------------------------------
   Dark, because it is a mode and not a section. Two lanes, one bar each, and
   a ghost marker on the trailing lane at the leader's position — a gap you
   can see beats a difference you have to subtract. */
.sim-race { background: var(--surface-ink); border-radius: var(--radius-lg); padding: 16px 18px 18px; margin-bottom: 26px; }
.sim-race-head { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }
.sim-race-flag { font-size: 21px; line-height: 1; flex: none; }
.sim-race-t { flex: 1; min-width: 0; }
.sim-race-t b { display: block; color: #fff; font-size: 15.5px; font-weight: 800; letter-spacing: -0.015em; }
.sim-race-t span { display: block; color: var(--text-on-dark-muted); font-size: 12.5px; margin-top: 1px; }
.sim-race-x {
  flex: none; background: transparent; border: 1px solid rgba(255,255,255,.24); color: #fff;
  border-radius: var(--radius-pill); width: 30px; height: 30px; cursor: pointer; font-size: 13px;
}
.sim-race-x:hover { background: rgba(255,255,255,.12); }

.sim-lane { padding: 12px 0; border-top: 1px solid rgba(255,255,255,.1); }
.sim-lane:first-of-type { border-top: 0; }
.lane-top { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.lane-name { color: rgba(255,255,255,.82); font-size: 13px; font-weight: 600; min-width: 0; }
.lane-score { flex: none; font-family: var(--font-mono); font-variant-numeric: tabular-nums; color: var(--text-on-dark-muted); font-size: 12px; }
.lane-n { color: #fff; font-size: 22px; font-weight: 600; letter-spacing: -0.03em; margin-right: 3px; }
.lane-track {
  position: relative; height: 12px; border-radius: 6px; margin: 8px 0 9px;
  background: rgba(255,255,255,.09); overflow: hidden;
}
.lane-fill { display: block; height: 100%; width: 0; border-radius: 6px; background: var(--charge-blue); transition: width 140ms linear; }
.sim-lane.hw .lane-fill { background: var(--hw-red); }
.lane-ghost {
  display: none; position: absolute; top: -2px; bottom: -2px; width: 2px;
  background: var(--hw-amber); transform: translateX(-1px);
}
.lane-q { display: flex; align-items: center; gap: 8px; }
.lane-qlab { flex: none; font-size: 10px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-4); }
.lane-chips { display: flex; flex-wrap: wrap; gap: 2px; flex: 1; min-width: 0; align-content: center; }
.lane-chips i {
  width: 6px; height: 10px; border-radius: 1.5px; background: rgba(255,255,255,.4);
  transition: opacity 180ms var(--ease-out), transform 180ms var(--ease-out);
}
.sim-lane.hw .lane-chips i { background: rgba(199,0,11,.72); }
.lane-chips i.gone { opacity: 0; transform: scale(.3); }
.sim-lane.drained .lane-q { visibility: hidden; }
.lane-wait { flex: none; font-family: var(--font-mono); font-variant-numeric: tabular-nums; color: var(--text-on-dark-muted); font-size: 12px; min-width: 3ch; text-align: right; }

/* The finish. The winning lane brightens; nothing else moves. */
.sim-lane.won .lane-track { box-shadow: 0 0 0 2px var(--hw-amber); }
.sim-lane.won .lane-n { color: var(--hw-amber); }
.sim-lane.lost { opacity: .62; }
.sim-race-win { display: none; border-top: 1px solid rgba(255,255,255,.14); margin-top: 4px; padding-top: 15px; }
.sim-race-win.on { display: block; animation: winin 480ms var(--ease-out) both; }
@keyframes winin { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.win-k {
  font-family: var(--font-mono); font-size: 11.5px; font-weight: 600; letter-spacing: .26em;
  text-transform: uppercase; color: var(--hw-amber); margin-bottom: 7px;
}
.sim-race-win p { margin: 0; color: #fff; font-size: 16px; line-height: 1.45; max-width: 66ch; }

@media (max-width: 560px) {
  /* The station names carry their best connector — "(max 800A@600kW)" — and a <select>
     cannot wrap, so on a phone the type comes down to keep the whole label on screen
     rather than the label coming down to keep the type up. */
  .sim-sel { font-size: 13px; padding-right: 24px; }
  .sim-bays { grid-template-columns: repeat(auto-fill, minmax(78px, 1fr)); }
  /* One pool per row on a phone rather than two half-legible ones. */
  .sim-pool { flex-basis: 100%; }
  .lane-q { flex-wrap: wrap; }
  .lane-chips i { width: 5px; height: 9px; }
  .sim-race { padding: 14px 13px 16px; }
  .sim-race-go { padding: 13px 14px; gap: 10px; }
}
@media (prefers-reduced-motion: reduce) {
  .sim-flag, .sim-flag::before, .sim-flag-in, .sim-race-win,
  .lane-fill, .lane-chips i { animation: none !important; transition: none !important; }
}

/* The connector ladder, and the two source states a rival card can be in. */
.sim-spec-ladder {
  font-family: var(--font-mono); font-size: 11.5px; color: var(--ink-3);
  margin: 11px 0 0; padding-top: 9px; border-top: 1px dashed var(--line);
}
.sim-spec-ladder span:first-child {
  font-family: var(--font-sans); font-size: 10.5px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--ink-4); margin-right: 7px;
}
.sim-spec-src.off { color: var(--hw-orange); }
.sim-spec-src.off a { color: inherit; text-decoration: underline; }
/* Field-sourced: a stronger claim than a datasheet for what a machine does on site, and
   a weaker one for checkability — so it reads as a note, not as a warning. */
.sim-spec-src.field { color: var(--ink-3); }
/* The taxi layer is an assumption sitting on measured data, so it reads as a distinct
   cell rather than blending into the measured ones beside it. */
.pn-i.pn-taxi b { color: var(--hw-orange); }
.sim-pulse-taxi { font-size: 12px; color: var(--ink-4); margin: 8px 0 0; max-width: 92ch; }

/* A row that cannot be scored because the two sides did not do the same job. */
.sim-score-caveat {
  font-size: 12.5px; color: var(--ink-3); background: var(--surface-2);
  padding: 10px 16px; border-bottom: 1px solid var(--line-soft);
}
.sim-score-row.unscored .sim-score-v i { opacity: .38; }

/* ── Thai typography ─────────────────────────────────────────────────────────
 * Thai stacks up to two marks above a consonant and a vowel below it, so a line
 * height tuned for Latin display type is not merely tight — it collides. At the
 * headline setting of 1.03–1.08 a two-line Thai title puts the lower vowel of
 * ผู้/สุด directly into the tone mark of ที่/คุ้ม on the line beneath. Verified by
 * render at 360px, not by inspection (2026-08-01, go-live R4).
 *
 * Scoped to lang="th" — app.js sets document.documentElement.lang on every
 * language switch — so the English side keeps the tight display leading it was
 * designed with. Nothing here changes a single English pixel.
 *
 * Word WRAPPING needs no fix: Chrome breaks Thai with ICU's dictionary and every
 * break already lands on a word boundary. tests/thai_linebreak.py proves it, and
 * proves itself by catching 48 mid-word breaks when break-all is forced on.
 */
:root[lang="th"] h1.page-title,
:root[lang="th"] .hero h1,
:root[lang="th"] .sim-hero h1 { line-height: 1.32; }

/* Body copy is already ≥1.5, but the smaller UI type sits at 1.2, which leaves a
 * tone mark grazing the box above it. 1.35 is enough and does not reflow layout,
 * because these are single-line controls sized by padding. */
:root[lang="th"] .btn,
:root[lang="th"] .badge,
:root[lang="th"] .t10-meta,
:root[lang="th"] .eyebrow { line-height: 1.35; }
