/* BuyToken public site.
   Light "paper" surface, hairline borders, one deep-green accent, generous
   vertical rhythm. Everything is plain CSS on purpose — no build step, so the
   page ships inside the relay binary as-is. */

:root {
  --ink: #1f1f1f;
  --ink-2: #5f6368;
  --ink-3: #9aa0a6;

  --green: #047857;
  --green-d: #065f46;
  --green-l: #34d399;
  --tint: #f6faf7;
  --tint-2: #ecfdf3;

  --line: #eaecef;
  --line-2: #e5e7eb;
  --line-3: #dadce0;

  --panel: #0f1115;
  --panel-2: #1a1d23;

  --wrap: none;
  --pad: clamp(14px, 1.25vw, 20px);

  /* Right-edge fade for the horizontal chip strips phones fall back to. */
  --strip-fade: linear-gradient(to right, #000 0, #000 calc(100% - 22px), transparent 100%);

  --r-sm: 10px;
  --r: 16px;
  --r-lg: 24px;

  --sh-card: 0 8px 30px rgba(15, 23, 42, .06);
  --sh-hero: 0 20px 60px rgba(15, 23, 42, .08);

  --font: -apple-system, system-ui, BlinkMacSystemFont, "PingFang SC",
    "Helvetica Neue", "Segoe UI", "Microsoft YaHei", sans-serif;
  --mono: ui-monospace, "SF Mono", Menlo, Monaco, Consolas, "Courier New", monospace;
}

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

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

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: #fff;
  color: var(--ink);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Short public pages should still finish at the bottom of the viewport instead
   of leaving their footer floating halfway up a tall monitor. */
body > main,
body > .app { flex: 1 0 auto; }
body > .foot { margin-top: auto; }

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

h1, h2, h3 { margin: 0; font-weight: 600; }

p { margin: 0; }

code {
  font-family: var(--mono);
  font-size: .92em;
}

/* Horizontal longhands, not the `padding` shorthand: these container classes
   get composed with others that set vertical padding (e.g. `wrap-wide hdr-in`),
   and a shorthand would silently zero that out depending on source order. */
.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin: 0 auto;
  padding-left: var(--pad);
  padding-right: var(--pad);
}

/* ---------- header ---------- */

.hdr {
  position: sticky;
  top: 0;
  z-index: 30;
  background: rgba(255, 255, 255, .9);
  backdrop-filter: saturate(180%) blur(12px);
  -webkit-backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid var(--line);
}

.hdr-in {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  min-height: 70px; /* floor, so the bar keeps its shape whatever it holds */
  padding-top: 14px;
  padding-bottom: 14px;
}

.brand { display: flex; align-items: center; gap: 10px; }

.brand-lockup {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
}

.brand-domain {
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: .02em;
  color: var(--green-d);
  line-height: 1.2;
}

.brand-mark {
  display: grid;
  place-items: center;
  width: 148px;
  height: 34px;
  flex: 0 0 auto;
}

.brand-logo {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: contain;
  object-position: left center;
}

.brand-name {
  font-size: 16px;
  font-weight: 500;
  letter-spacing: -.005em;
}

.nav {
  display: none;
  align-items: center;
  gap: 32px;
  font-size: 14px;
  color: var(--ink-2);
}

.nav a { transition: color .15s ease; }
.nav a:hover { color: var(--ink); }

.hdr-cta { display: flex; align-items: center; gap: 12px; }

/* Console header QQ entry. Sits left of the language switch; the number shows
   on wide screens and the label collapses to the glyph on narrow ones so the
   account block and 退出 keep their room. */
.hdr-qq { display: inline-flex; align-items: center; gap: 7px; }

.hdr-qq-pop {
  position: relative;
  flex: none;
}

.hdr-qq-pop > summary {
  list-style: none;
  cursor: pointer;
}

.hdr-qq-pop > summary::-webkit-details-marker { display: none; }

.hdr-qq-panel {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  width: 244px;
  padding: 14px;
  border: 1px solid var(--line);
  border-radius: 16px;
  background: #fff;
  box-shadow: 0 18px 50px rgba(20, 35, 30, .16);
  display: none;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  text-align: center;
}

.hdr-qq-pop[open] .hdr-qq-panel { display: flex; }

/* Only where a pointer can actually hover: on a touch screen the hover state
   sticks after a tap, which would hold the panel open after the script has
   already closed it. */
@media (hover: hover) {
  .hdr-qq-pop:hover .hdr-qq-panel { display: flex; }
}

.hdr-qq-panel img {
  width: 210px;
  height: 210px;
  display: block;
  border-radius: 12px;
}

.hdr-qq-panel strong { margin-top: 2px; font-size: 14px; }
.hdr-qq-panel span { font-size: 12px; color: var(--ink-2); }
.hdr-qq-panel a { font-size: 12px; color: var(--green-d); }

.hdr-qq-ico {
  width: 16px;
  height: 16px;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.hdr-qq-num {
  font-family: var(--mono);
  font-size: 12.5px;
  letter-spacing: .01em;
  color: var(--ink-3);
}

/* Only on a真正 narrow phone does the label go: an unlabelled glyph in the
   header reads as an unknown icon, so it is the last thing dropped. */
@media (max-width: 520px) {
  .hdr-qq-label { display: none; }
  .hdr-qq { padding-left: 10px; padding-right: 10px; }
  .hdr-qq-panel {
    position: fixed;
    top: 68px;
    right: 12px;
    width: min(244px, calc(100vw - 24px));
  }
}

.lang-switch {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--ink-3);
  font-size: 13px;
  white-space: nowrap;
}

.lang-switch a { color: var(--ink-2); }
.lang-switch a:hover,
.lang-switch a[aria-current="true"] { color: var(--green); font-weight: 600; }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid transparent;
  border-radius: 999px;
  padding: 9px 20px;
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}

.btn:focus-visible {
  outline: 2px solid rgba(4, 120, 87, .35);
  outline-offset: 2px;
}

.btn-solid { background: var(--green); color: #fff; }
.btn-solid:hover { background: var(--green-d); }

.btn-ghost {
  border: 1px solid var(--line-3);
  color: var(--green);
  background: #fff;
}
.btn-ghost:hover { background: var(--tint); border-color: #c7d9d0; }
.pricing-label-short { display: none; }

.btn-lg { padding: 12px 26px; }

/* ---------- shared section furniture ---------- */

.sec {
  border-top: 1px solid var(--line);
  padding: 88px 0;
  /* keep anchor targets clear of the sticky header */
  scroll-margin-top: 64px;
}

.sec-head {
  display: grid;
  gap: 20px;
  margin-bottom: 44px;
}

.eyebrow {
  font-size: 14px;
  font-weight: 500;
  color: var(--green);
  margin-bottom: 12px;
}

.sec-head h2 {
  font-size: clamp(27px, 3.4vw, 40px);
  line-height: 1.2;
  letter-spacing: -.01em;
}

.sec-note {
  font-size: 15px;
  line-height: 1.75;
  color: var(--ink-2);
  max-width: 460px;
}

.sec-note code {
  background: var(--tint-2);
  color: var(--green-d);
  padding: 2px 6px;
  border-radius: 6px;
}

.sec-foot {
  margin-top: 20px;
  font-size: 13px;
  color: var(--ink-3);
}
.sec-foot a { color: var(--green); }
.sec-foot a:hover { text-decoration: underline; }

.pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  align-self: flex-start;
  border: 1px solid var(--line-2);
  border-radius: 999px;
  background: #fff;
  padding: 6px 12px;
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-2);
  box-shadow: 0 1px 2px rgba(15, 23, 42, .04);
}

.pill-g { background: var(--tint-2); border-color: #c6f0da; color: var(--green-d); }

.dot {
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--green);
}

.ico {
  width: 22px;
  height: 22px;
  /* Icons live next to text that wraps on a phone; without this they get
     squashed off their viewBox ratio instead of holding their size. */
  flex: none;
  fill: none;
  stroke: var(--green);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ---------- hero ---------- */

/* Launch strip above the hero. Amber rather than the brand green so it reads as
   news instead of another call to action, and compact enough on a phone that it
   does not push the ¥ gift chip out of the first screen. */
.deal-strip {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 20px 0 0;
  padding: 14px 20px;
  border: 1px solid #f0c98a;
  border-left: 5px solid #d97706;
  border-radius: 14px;
  background: linear-gradient(90deg, #fff8ec, #fffdf8);
  text-decoration: none;
  color: var(--ink);
  transition: transform .15s ease, box-shadow .15s ease;
}

.deal-strip:hover {
  transform: translateY(-1px);
  box-shadow: 0 8px 22px rgba(217, 119, 6, .18);
  color: var(--ink);
}

.deal-flag {
  flex: none;
  padding: 3px 9px;
  border-radius: 999px;
  background: #d97706;
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .06em;
}

.deal-main {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 12px;
  min-width: 0;
}

.deal-title {
  font-size: clamp(17px, 2.1vw, 23px);
  font-weight: 700;
  letter-spacing: -.01em;
}

.deal-note { font-size: 13px; color: var(--ink-2); }

.deal-off {
  flex: none;
  margin-left: auto;
  padding: 6px 14px;
  border-radius: 999px;
  background: #d97706;
  color: #fff;
  font-size: 15px;
  font-weight: 700;
  white-space: nowrap;
}

.deal-arw { flex: none; font-size: 17px; font-weight: 700; color: #d97706; }

@media (max-width: 620px) {
  .deal-strip { gap: 9px; padding: 12px 14px; border-radius: 12px; }
  .deal-title { font-size: 16px; }
  /* The note is the first thing to go: the headline and the 折 are what make
     this worth a row of the first screen. */
  .deal-note { display: none; }
  .deal-off { margin-left: auto; padding: 5px 11px; font-size: 13px; }
  .deal-arw { display: none; }
}

.hero {
  display: grid;
  grid-template-columns: 1fr;
  gap: 48px;
  padding: 72px 0 88px;
}

.hero-copy { display: flex; flex-direction: column; }

.hero h1 {
  margin-top: 24px;
  max-width: 640px;
  font-size: clamp(31px, 4.6vw, 50px);
  line-height: 1.16;
  letter-spacing: -.02em;
}

.hero h1 .g { color: var(--green); }

.lead {
  margin-top: 24px;
  max-width: 560px;
  font-size: 15px;
  line-height: 1.85;
  color: var(--ink-2);
}

.mini-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 12px;
  margin-top: 32px;
}

.mini {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: rgba(255, 255, 255, .8);
  padding: 16px;
  transition: transform .15s ease, border-color .15s ease, box-shadow .15s ease;
}

.mini:hover {
  transform: translateY(-1px);
  border-color: #d2e7dc;
  box-shadow: 0 6px 20px rgba(15, 23, 42, .05);
}

.mini h3 { margin-top: 10px; font-size: 14px; }

.mini p {
  margin-top: 6px;
  font-size: 12px;
  line-height: 1.6;
  color: var(--ink-2);
}

.ticks {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 24px;
  margin: 28px 0 0;
  padding: 0;
  list-style: none;
  font-size: 13px;
  color: var(--ink-2);
}

.ticks li { display: flex; align-items: center; gap: 7px; }

.ticks li::before {
  content: "";
  width: 14px;
  height: 14px;
  flex: none;
  border-radius: 999px;
  background: var(--tint-2);
  /* check mark */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.6 6.2 4.8 8.4 9.4 3.8' fill='none' stroke='%23047857' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-size: 12px 12px;
  background-position: center;
  background-repeat: no-repeat;
}

/* first-screen invoice banner, links to the invoice section */

.hero-inv {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 12px;
  margin-top: 20px;
  border: 1px solid #fecaca;
  border-radius: var(--r);
  background: #fef2f2;
  padding: 14px 18px;
  color: #b91c1c;
  transition: border-color .15s ease, box-shadow .15s ease;
}

.hero-inv:hover {
  border-color: rgba(185, 28, 28, .45);
  box-shadow: var(--sh-card);
}

.hero-inv .ico { width: 20px; height: 20px; flex: none; stroke: #dc2626; }

.hero-inv-t { font-size: 15px; font-weight: 600; white-space: nowrap; }

.hero-inv-d {
  flex: 1;
  min-width: 200px;
  font-size: 13px;
  color: var(--ink-2);
}

.hero-inv-arw { flex: none; font-size: 15px; transition: transform .15s ease; }

.hero-inv:hover .hero-inv-arw { transform: translateX(3px); }

/* ---------- hero card ---------- */

/* The hero's default second column. A campaign replaces it while one runs;
   with neither, the copy stretches the full width and the right half of the
   first screen is empty. */
.hero-card {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-self: start;
  border: 1px solid var(--line-2);
  border-radius: var(--r-lg);
  background: #fff;
  padding: 28px;
  box-shadow: var(--sh-hero);
}

.glow {
  position: absolute;
  top: -120px;
  right: -100px;
  width: 280px;
  height: 280px;
  border-radius: 999px;
  background: radial-gradient(circle, rgba(4, 120, 87, .12), transparent 68%);
  pointer-events: none;
}

.card-title {
  position: relative;
  margin-top: 18px;
  font-size: 22px;
  letter-spacing: -.02em;
}

.card-sub {
  position: relative;
  margin-top: 8px;
  font-size: 13px;
  line-height: 1.65;
  color: var(--ink-2);
}

.kv {
  position: relative;
  margin: 20px 0 0;
  display: grid;
  gap: 10px;
}

.kv > div {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
}

.kv dt { font-size: 12px; color: var(--ink-3); }

.kv dd {
  margin: 0;
  min-width: 0;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink);
  overflow-wrap: anywhere;
}

.card-link {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
  font-size: 14px;
  font-weight: 500;
  color: var(--green);
}

.card-link:hover { color: var(--green-d); }
.card-link .arw { transition: transform .15s ease; }
.card-link:hover .arw { transform: translateX(3px); }

/* ---------- hero ad slot ---------- */

/* Artwork is desktop-only. On a phone the 3:2 poster used to lead the hero
   (order: -1) and pushed 注册立得 ¥1 below the fold; the compact chip next
   to the gift is the campaign on that width. */
.hero-ad {
  display: none;
}

/* Solid, not outlined: on a phone this chip is the whole campaign, so it has
   to read as loudly as the ¥1 gift it sits under rather than as a quiet tag. */
.hero-ad-compact {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  max-width: 100%;
  padding: 10px 18px;
  border-radius: 999px;
  background: linear-gradient(135deg, #ea580c, #b45309);
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: .01em;
  text-decoration: none;
  box-shadow: 0 6px 18px rgba(234, 88, 12, .34);
  transition: transform .15s ease, box-shadow .15s ease;
  /* Two lines on a phone, and CJK breaks between any two characters: without
     this the wrap lands mid-word and leaves 方 6 折 dangling. Breaks are then
     only allowed at the spaces, and the discount itself is spelled with
     non-breaking ones. */
  word-break: keep-all;
}

.hero-ad-compact:hover {
  transform: translateY(-1px);
  box-shadow: 0 8px 22px rgba(234, 88, 12, .42);
  color: #fff;
}

.hero-ad-new {
  flex: none;
  padding: 2px 7px;
  border-radius: 6px;
  background: #fff;
  color: #b45309;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .06em;
}

.hero-ad-link {
  display: block;
  overflow: hidden;
  border-radius: var(--r-lg);
  box-shadow: var(--sh-hero);
  transition: transform .15s ease, box-shadow .15s ease;
}

.hero-ad-link:hover {
  transform: translateY(-2px);
  box-shadow: 0 26px 70px rgba(15, 23, 42, .14);
}

.hero-ad-link img {
  display: block;
  width: 100%;
  height: auto;
}

.code {
  position: relative;
  margin-top: 20px;
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--panel);
}

.code-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 12px;
  border-bottom: 1px solid rgba(255, 255, 255, .08);
}

.code-name {
  font-family: var(--mono);
  font-size: 11px;
  color: #8a8f98;
}

.copy {
  border: 1px solid rgba(255, 255, 255, .16);
  border-radius: 999px;
  background: transparent;
  color: #c9cdd4;
  font-family: inherit;
  font-size: 11px;
  padding: 3px 11px;
  cursor: pointer;
  transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}

.copy:hover { background: rgba(255, 255, 255, .08); color: #fff; }
.copy.ok { background: var(--green-l); border-color: var(--green-l); color: #06281c; }

.code pre {
  margin: 0;
  padding: 16px;
  overflow-x: auto;
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.85;
  color: #e8e8ec;
}

.code .cm { color: #6b7280; }
.code .kw { color: #7dd3fc; }
.code .st { color: var(--green-l); }

/* ---------- feature grid ---------- */

.grid-3 {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 16px;
}

.vendor-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 16px;
  padding: 8px 0 28px;
}
.vendor-card {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: #fff;
  padding: 22px 24px;
}
.vendor-card h2 {
  margin: 0;
  font-size: 18px;
  font-weight: 650;
  letter-spacing: -.02em;
}
.vendor-card p {
  margin: 8px 0 0;
  color: var(--ink-3);
  font-size: 13px;
}

.card {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: #fff;
  padding: 24px;
  transition: border-color .15s ease, box-shadow .15s ease;
}

.card:hover {
  border-color: rgba(4, 120, 87, .3);
  box-shadow: var(--sh-card);
}

.card h3 { margin-top: 14px; font-size: 18px; }

.card p {
  margin-top: 8px;
  font-size: 14px;
  line-height: 1.75;
  color: var(--ink-2);
}

.chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 28px;
}

.chips-label {
  font-size: 13px;
  color: var(--ink-3);
  margin-right: 4px;
}

.chip {
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 5px 12px;
  font-size: 12px;
  color: var(--ink-2);
  background: var(--tint);
}

/* ---------- invoice ---------- */

.inv-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 16px;
  margin-bottom: 16px;
}

.inv-card {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: #fff;
  padding: 24px;
  transition: border-color .15s ease, box-shadow .15s ease;
}

.inv-card:hover {
  border-color: rgba(4, 120, 87, .3);
  box-shadow: var(--sh-card);
}

.inv-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.inv-head h3 { font-size: 18px; }

.inv-head .tag {
  margin-left: auto;
  border-radius: 999px;
  background: var(--tint-2);
  color: var(--green-d);
  font-size: 12px;
  font-weight: 500;
  padding: 3px 10px;
}

.inv-card > p {
  margin-top: 10px;
  font-size: 14px;
  line-height: 1.75;
  color: var(--ink-2);
}

.inv-list {
  margin: 14px 0 0;
  padding: 14px 0 0;
  list-style: none;
  display: grid;
  gap: 8px;
  font-size: 13px;
  color: var(--ink-2);
  border-top: 1px dashed var(--line);
}

.inv-list li { display: flex; align-items: center; gap: 7px; }

.inv-list li::before {
  content: "";
  width: 14px;
  height: 14px;
  flex: none;
  border-radius: 999px;
  background: var(--tint-2);
  /* check mark */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.6 6.2 4.8 8.4 9.4 3.8' fill='none' stroke='%23047857' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* ---------- contact page ---------- */

.contact-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 16px;
}

.contact-card {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: #fff;
  padding: 32px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  text-align: center;
}

.contact-ico { width: 28px; height: 28px; flex: none; }

.contact-label { font-size: 13px; color: var(--ink-3); }

.contact-num {
  display: flex;
  align-items: center;
  gap: 12px;
}

.contact-num code {
  font-family: var(--mono);
  font-size: 28px;
  font-weight: 600;
  letter-spacing: .02em;
  color: var(--ink);
}

.contact-num .key-btn { font-size: 12px; padding: 5px 14px; }

.contact-hint { font-size: 13px; color: var(--ink-2); }

.contact-qr {
  width: min(240px, 100%);
  height: auto;
  display: block;
  border-radius: 18px;
}

/* Join button inside a centred contact card: the card's own gap is tuned for
   text rows, so the button needs its own breathing room above it. */
.contact-cta { margin-top: 6px; }

/* Home page QQ community block. Horizontal on desktop so the join button sits
   on the same line as the number, stacked below 620px. */
.qq-card {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: #fff;
  padding: 28px 32px;
  display: flex;
  align-items: center;
  gap: 20px;
}

.qq-ico { width: 28px; height: 28px; flex: none; }

.qq-qr {
  width: 176px;
  flex: none;
  margin: 0;
  text-align: center;
}

.qq-qr img {
  width: 176px;
  height: 176px;
  display: block;
  border-radius: 14px;
}

.qq-qr figcaption {
  margin-top: 7px;
  font-size: 12px;
  color: var(--ink-3);
}

.qq-body { flex: 1; min-width: 0; }

.qq-label { font-size: 13px; color: var(--ink-3); }

.qq-num {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 4px;
}

.qq-num code {
  font-family: var(--mono);
  font-size: 26px;
  font-weight: 600;
  letter-spacing: .02em;
  color: var(--ink);
}

.qq-num .key-btn { font-size: 12px; padding: 5px 14px; }

.qq-hint { margin-top: 8px; font-size: 13px; color: var(--ink-2); }

.qq-join { flex: none; }

@media (max-width: 620px) {
  .qq-card { flex-direction: column; align-items: center; padding: 24px; text-align: center; }
  .qq-body { width: 100%; }
  .qq-num { justify-content: center; }
  .qq-num code { font-size: 22px; }
  .qq-join { width: 100%; justify-content: center; }
}

.contact-list {
  margin: 10px 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 10px;
  font-size: 14px;
  line-height: 1.7;
  color: var(--ink-2);
}

/* ---------- tables ---------- */

.tbl-card {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: #fff;
  overflow: hidden;
}

.tbl-card + .tbl-card { margin-top: 16px; }

.tbl-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 18px 24px;
  border-bottom: 1px solid var(--line);
  background: var(--tint);
}

.tbl-head h3 { font-size: 16px; }

.tag {
  font-size: 12px;
  color: var(--ink-2);
}

.tbl-scroll { overflow-x: auto; }

.tbl {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}

.tbl th,
.tbl td {
  padding: 13px 24px;
  text-align: left;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}

.tbl thead th {
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-3);
  background: #fff;
}

.tbl tbody tr:last-child th,
.tbl tbody tr:last-child td { border-bottom: none; }

.tbl tbody tr { transition: background-color .12s ease; }
.tbl tbody tr:hover { background: var(--tint); }

.tbl .r { text-align: right; }
.tbl .muted { color: var(--ink-2); font-weight: 400; }

.tbl .num {
  font-variant-numeric: tabular-nums;
  font-weight: 500;
}

.tbl code { color: var(--ink); }

.tbl-kv th {
  width: 128px;
  font-weight: 500;
  color: var(--ink);
  vertical-align: top;
}

.tbl-kv td { white-space: normal; line-height: 1.7; }

.badge {
  display: inline-block;
  margin-left: 6px;
  border-radius: 999px;
  background: var(--tint-2);
  color: var(--green-d);
  font-size: 11px;
  font-weight: 500;
  padding: 1px 8px;
  vertical-align: 1px;
}

.badge-peak {
  background: #fdf0e6;
  color: #b25a1b;
}

/* Group badge on the pricing table: distinguishes the same model listed at
   two prices (list vs promo group). Blue, to stay clear of the green 推荐
   and orange 高峰 badges. */
.badge-grp {
  background: #e8f0fe;
  color: #1a56db;
}

.peak-p {
  font-size: 11px;
  font-weight: 400;
  color: #b25a1b;
  line-height: 1.4;
}

.peak-foot {
  color: #b25a1b;
}

.tbl-price td.note {
  font-size: 12px;
  color: var(--muted);
  white-space: nowrap;
}

.tbl-price td.note-discount {
  color: var(--green-d);
  font-weight: 500;
}

/* ---------- home pricing teaser ---------- */

/* The band under the hero: live headline prices so a first-time visitor —
   especially on a phone — sees what models cost without opening /pricing. */
.pt {
  display: flex;
  align-items: center;
  gap: 22px;
  padding: 16px 20px;
  border: 1px solid var(--line);
  border-radius: 18px;
  background: #fff;
  transition: border-color .15s ease, box-shadow .15s ease;
}

.pt:hover { border-color: var(--green-l); box-shadow: 0 8px 24px rgba(15, 76, 92, .08); }

.pt-head { flex: 0 0 auto; }

.pt-label {
  display: block;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -.01em;
}

.pt-unit {
  display: block;
  margin-top: 2px;
  font-size: 11.5px;
  color: var(--ink-3);
}

.pt-items {
  flex: 1;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
}

.pt-item {
  border: 1px solid var(--line-2);
  border-radius: 12px;
  padding: 8px 12px;
  min-width: 0;
}

.pt-item code {
  display: block;
  font-size: 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.pt-price {
  display: block;
  margin-top: 2px;
  font-size: 15px;
  font-weight: 650;
  color: var(--green-d);
  white-space: nowrap;
}

.pt-sep { color: var(--ink-3); font-weight: 400; margin: 0 2px; }

.pt-more {
  flex: 0 0 auto;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--green-d);
  white-space: nowrap;
}

.pt-more .arw { margin-left: 4px; }

@media (max-width: 899.98px) {
  .pt { flex-direction: column; align-items: stretch; gap: 12px; }
  .pt-head { display: flex; align-items: baseline; justify-content: space-between; }
  .pt-items { grid-template-columns: 1fr 1fr; }
  .pt-more { text-align: center; padding-top: 2px; }
}

/* ---------- steps ---------- */

.steps {
  display: grid;
  gap: 16px;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: step;
}

.steps li {
  display: flex;
  gap: 18px;
  /* Same reason as `.doc-body section > *`: a grid item's automatic minimum is
     min-content, so an unwrappable code sample inside a step would widen the
     whole page instead of scrolling inside its own block. */
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: #fff;
  padding: 22px 24px;
  transition: border-color .15s ease, box-shadow .15s ease;
}

.steps li:hover {
  border-color: rgba(4, 120, 87, .3);
  box-shadow: var(--sh-card);
}

.step-n {
  display: grid;
  place-items: center;
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: 999px;
  background: var(--tint-2);
  color: var(--green-d);
  font-size: 13px;
  font-weight: 600;
}

.steps h3 { font-size: 17px; }

.steps p {
  margin-top: 6px;
  font-size: 14px;
  line-height: 1.75;
  color: var(--ink-2);
}

/* ---------- faq ---------- */

.faq { display: grid; gap: 10px; }

.faq details {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: #fff;
  transition: border-color .15s ease;
}

.faq details[open] { border-color: #d2e7dc; }
.faq details:hover { border-color: #d2e7dc; }

.faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 24px;
  font-size: 15px;
  font-weight: 500;
  cursor: pointer;
  list-style: none;
}

.faq summary::-webkit-details-marker { display: none; }

.chev {
  flex: none;
  width: 9px;
  height: 9px;
  border-right: 1.6px solid var(--ink-3);
  border-bottom: 1.6px solid var(--ink-3);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform .18s ease;
}

.faq details[open] .chev { transform: rotate(-135deg) translate(-2px, -2px); }

.faq p {
  padding: 0 24px 20px;
  font-size: 14px;
  line-height: 1.8;
  color: var(--ink-2);
}

/* ---------- closing cta ---------- */

.sec-cta { padding-bottom: 96px; }

.cta {
  display: grid;
  gap: 28px;
  border: 1px solid #d7ebe2;
  border-radius: var(--r-lg);
  background: linear-gradient(135deg, var(--tint-2), var(--tint) 70%);
  padding: 40px;
}

.cta-h {
  font-size: clamp(24px, 3vw, 32px);
  letter-spacing: -.015em;
  line-height: 1.25;
}

.cta-p {
  margin-top: 12px;
  max-width: 520px;
  font-size: 15px;
  line-height: 1.75;
  color: var(--ink-2);
}

.cta-btns { display: flex; flex-wrap: wrap; gap: 12px; }

.cta-act { display: flex; flex-direction: column; align-items: flex-start; gap: 14px; }

.cta-gift {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 9px 18px;
  border-radius: 999px;
  background: var(--green);
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: .01em;
  box-shadow: 0 6px 18px rgba(22, 163, 74, .38);
}

.cta-gift b { font-size: 20px; font-weight: 700; }

/* hero 第一屏的营销胶囊：注册礼金（绿）+ 首充折扣（橙），可直接点击。 */
.hero-ctas {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin: 22px 0 0;
}

.hero-gift,
.hero-deal {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 12px 24px;
  border-radius: 999px;
  color: #fff;
  font-size: 16px;
  font-weight: 600;
  letter-spacing: .01em;
  text-decoration: none;
  transition: transform .15s ease, box-shadow .15s ease;
}

.hero-gift {
  background: var(--green);
  box-shadow: 0 6px 18px rgba(22, 163, 74, .38);
}

.hero-gift:hover {
  transform: translateY(-1px);
  box-shadow: 0 8px 22px rgba(22, 163, 74, .45);
  color: #fff;
}

.hero-deal {
  background: #d97706;
  box-shadow: 0 6px 18px rgba(217, 119, 6, .35);
}

.hero-deal:hover {
  transform: translateY(-1px);
  box-shadow: 0 8px 22px rgba(217, 119, 6, .45);
  color: #fff;
}

.hero-gift b,
.hero-deal b { font-size: 24px; font-weight: 700; }

.hero-gift-arw { font-weight: 700; }

/* The strip already supplies the space above the headline, so the hero drops
   most of its own top padding rather than opening a gap under the strip. */
.deal-strip + .hero { padding-top: 34px; }

@media (max-width: 620px) {
  /* Full-width so the gift is unmissable on the first screen instead of a
     small pill competing with the headline. */
  .hero-gift,
  .hero-deal {
    width: 100%;
    justify-content: center;
    padding: 13px 18px;
  }
  .hero-gift b,
  .hero-deal b { font-size: 22px; }
  .deal-strip + .hero { padding-top: 24px; }
}

/* 钱包页首充提示与实付价 */
.topup-deal {
  margin-bottom: 14px;
  padding: 10px 16px;
  border: 1px solid #fcd9a8;
  border-radius: var(--r-md, 10px);
  background: #fff7ed;
  color: #9a5b0b;
  font-size: 14px;
  line-height: 1.6;
}

.topup-deal b { color: #d97706; }

.topup-sub .topup-pay { color: #d97706; font-weight: 600; }

.topup-paid { font-size: 12px; }

/* ---------- footer ---------- */

.foot {
  border-top: 1px solid var(--line);
  padding: 32px 0 40px;
  font-size: 13px;
  color: var(--ink-2);
}

.foot-in {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px 24px;
}

.foot-copy { color: var(--ink-2); }
.foot-nav { display: flex; flex-wrap: wrap; gap: 20px; }
.foot-nav a:hover { color: var(--ink); }
.foot-icp { color: var(--ink-3); }
.foot-icp:hover { color: var(--ink-2); }

/* ============================================================
   Console
   Same tokens as the marketing pages, different furniture:
   sidebar shell, stat cards, dense tables, forms.
   ============================================================ */

/* Same max-width as .wrap: the console used to run 1400px while public pages
   ran 1280px, so the header's logo visibly jumped when crossing between them. */
.wrap-wide {
  width: 100%;
  max-width: var(--wrap);
  margin: 0 auto;
  padding-left: var(--pad);
  padding-right: var(--pad);
}

.brand-tag {
  border: 1px solid var(--line-2);
  border-radius: 999px;
  padding: 2px 9px;
  font-size: 11px;
  color: var(--ink-2);
}

/* The rename note sits next to the wordmark on the public header and auth
   pages, where a returning GimmitkAPI user is deciding whether they landed
   on the right site. It has to read at a glance, so it gets the brand
   gradient and white text rather than the muted tag treatment. */
.brand-note {
  border-radius: 999px;
  padding: 3px 10px;
  font-size: 12px;
  font-weight: 600;
  color: #fff;
  background: linear-gradient(135deg, #0f4c5c, #14b8a6);
}

.inline-form { display: inline-flex; }

.acct { display: flex; align-items: center; gap: 9px; }

.acct-name { font-size: 14px; color: var(--ink-2); }

.avatar {
  display: grid;
  place-items: center;
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: 999px;
  background: var(--tint-2);
  color: var(--green-d);
  font-size: 13px;
  font-weight: 600;
}

.avatar-sm {
  width: 20px;
  height: 20px;
  margin-right: 7px;
  font-size: 11px;
  background: rgba(255, 255, 255, .22);
  color: #fff;
}

/* ---------- shell ---------- */

.app {
  display: grid;
  grid-template-columns: 1fr;
  gap: 32px;
  /* keeps .wrap-wide's gutter: a bare `0` here would let the sidebar's
     negative margin escape the viewport on mobile */
  padding: 28px var(--pad) 72px;
  align-items: start;
}

.side { display: flex; flex-direction: column; gap: 16px; }

.side nav { display: grid; gap: 2px; }

.side-item {
  display: flex;
  align-items: center;
  gap: 11px;
  border-radius: var(--r-sm);
  padding: 9px 12px;
  font-size: 14px;
  color: var(--ink-2);
  transition: background-color .12s ease, color .12s ease;
}

.side-item:hover { background: var(--tint); color: var(--ink); }

.side-item.on {
  background: var(--tint-2);
  color: var(--green-d);
  font-weight: 500;
}

.side-ico {
  width: 17px;
  height: 17px;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: .8;
}

.app-main { min-width: 0; }

.page-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  margin-bottom: 28px;
}

.page-h {
  font-size: clamp(24px, 3vw, 32px);
  letter-spacing: -.015em;
  line-height: 1.25;
  margin-top: 10px;
}

/* ---------- stat cards ---------- */

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 14px;
  margin-bottom: 32px;
}

.stat {
  display: flex;
  flex-direction: column;
  gap: 8px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: #fff;
  padding: 20px 22px;
}

.stat-warn { border-color: #f5d9a8; background: #fffbf2; }

.stat-label { font-size: 13px; color: var(--ink-2); }

.stat-value {
  font-size: 30px;
  font-weight: 600;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

.stat-warn .stat-value { color: #b45309; }

.stat-note { font-size: 12px; color: var(--ink-3); }

/* ---------- content blocks ---------- */

.block { margin-bottom: 32px; }

.block-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}

.block-head h2 { font-size: 17px; }

.block-note { font-size: 12px; color: var(--ink-3); }
.block-note code { color: var(--ink-2); }

.block-link { font-size: 13px; color: var(--green); }
.block-link:hover { color: var(--green-d); }

/* minmax(0,…) rather than 1fr: the track's auto minimum would otherwise be the
   table's min-content, widening the column instead of letting .tbl-scroll scroll */
.cols { display: grid; grid-template-columns: minmax(0, 1fr); gap: 32px; }

.empty {
  border: 1px dashed var(--line-3);
  border-radius: var(--r);
  padding: 28px;
  text-align: center;
  font-size: 14px;
  color: var(--ink-3);
  background: var(--tint);
}

/* ---------- api keys ---------- */

.keys {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 14px;
}

.key {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: #fff;
  padding: 18px 20px;
}

.key-off { opacity: .6; }

.key-top {
  display: flex;
  align-items: center;
  gap: 10px 14px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}

.key-id {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  min-width: 0;
}

.key-name { font-size: 15px; font-weight: 500; }

.badge-off { background: #f1f3f5; color: var(--ink-2); }
.badge-warn { background: #fef3c7; color: #92400e; }

.key-val {
  display: flex;
  align-items: center;
  gap: 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--tint);
  padding: 8px 10px;
}

.key-text {
  flex: 1;
  min-width: 0;
  font-size: 12px;
  overflow-wrap: anywhere;
  color: var(--ink);
}

.key-btn {
  flex: none;
  border: 1px solid var(--line-3);
  border-radius: 999px;
  background: #fff;
  color: var(--ink-2);
  font-family: inherit;
  font-size: 11px;
  padding: 3px 11px;
  cursor: pointer;
  transition: background-color .12s ease, color .12s ease, border-color .12s ease;
}

.key-btn:hover { color: var(--green-d); border-color: #c7d9d0; background: var(--tint-2); }
.key-btn.ok { background: var(--green); border-color: var(--green); color: #fff; }

.key-btn-danger:hover { color: #a33a3a; border-color: #e8c4c4; background: #fef4f4; }

/* Per-image pricing cell on the pricing table: one inline tier list instead
   of four per-token columns. */
.tbl-price td.img-tier { white-space: nowrap; font-size: 13px; }
.tbl-price td.img-tier .sep { color: var(--ink-3); margin: 0 6px; }

.key-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
  margin-top: 12px;
  font-size: 12px;
  color: var(--ink-3);
}

.key-meta code { color: var(--ink-2); }

/* A per-key cap is invisible until it bites, so the two states that actually
   break requests get colour rather than another grey line of metadata. */
.key-meta .key-bad { color: #b42318; font-weight: 500; }
.key-meta .key-warn { color: #b45309; font-weight: 500; }

.key-alert {
  margin: 10px 0 0;
  padding: 9px 12px;
  border: 1px solid #f5d9a8;
  border-radius: var(--r-sm);
  background: #fffbf2;
  font-size: 12.5px;
  line-height: 1.7;
  color: #92400e;
}

/* ---------- usage bars ---------- */

.bars { display: grid; gap: 16px; }

.bar-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  font-size: 13px;
}

.bar-val { font-variant-numeric: tabular-nums; font-weight: 500; }

.bar-track {
  height: 6px;
  margin-top: 7px;
  border-radius: 999px;
  background: #eef1f0;
  overflow: hidden;
}

.bar-fill {
  height: 100%;
  border-radius: 999px;
  background: var(--green);
}

.bar-foot { margin-top: 6px; font-size: 12px; color: var(--ink-3); }

.tbl-tight th, .tbl-tight td { padding: 10px 16px; }
.nowrap { white-space: nowrap; }

/* ---------- alerts ---------- */

.alert {
  border-radius: var(--r-sm);
  padding: 12px 16px;
  font-size: 14px;
  margin-bottom: 20px;
}

.alert-bad {
  border: 1px solid #f3c7c7;
  background: #fef4f4;
  color: #a33a3a;
}

.alert-good {
  border: 1px solid #c3e0d1;
  background: var(--tint-2);
  color: var(--green-d);
}

.hint {
  margin-bottom: 20px;
  font-size: 13px;
  line-height: 1.8;
  color: var(--ink-3);
}

.panel {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: #fff;
  padding: 22px;
}

.block-top { margin-top: 8px; }

/* ---------- keys page ---------- */

.keys-wide { grid-template-columns: repeat(auto-fit, minmax(360px, 1fr)); }

.key-new {
  border-color: var(--green);
  box-shadow: 0 0 0 3px rgba(4, 120, 87, .08);
}

/* Card actions sit with the name they act on, in the same pill style as
   显示/复制 — they're secondary to the key itself and shouldn't outweigh it. */
.key-acts {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: none;
  margin-left: auto;
}

/* Opened on demand by the 重命名 button; rendered open when JS is off. */
.key-rename {
  display: flex;
  gap: 6px;
  margin-bottom: 12px;
}

.key-rename .input { flex: 1; min-width: 0; }

/* A class selector outranks the UA stylesheet's [hidden], so restate it. */
.key-rename[hidden] { display: none; }

.btn-sm { padding: 6px 12px; font-size: 13px; }

.btn-danger { color: #a33a3a; }
.btn-danger:hover { color: #8b2f2f; border-color: #e8c4c4; background: #fef4f4; }

.input-sm { padding: 7px 11px; font-size: 13px; }

.row-form {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 12px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: #fff;
  padding: 20px 22px;
}

.row-form .field { flex: 1; min-width: 180px; }

/* Key management borrows the overview dashboard: same topline, same panel
   chrome, restrained rectangular controls, and consistently aligned columns. */
.app[data-page="keys"] { padding-bottom: 24px; }
.keys-head { align-items: center; margin-bottom: 16px; }
.keys-topline { margin-bottom: 16px; }
.keys-topline .dash-greeting { font-size: 23px; }
.keys-topline-note {
  overflow: hidden;
  color: #8b9095;
  font-size: 13px;
  text-overflow: ellipsis;
  white-space: nowrap;
}
@media (max-width: 899.98px) {
  .keys-topline-note { display: none; }
  .keys-panel .dash-panel-head { flex-direction: row; align-items: center; }
}
.key-create-open { min-width: 96px; }
.key-modal {
  width: min(480px, calc(100vw - 32px));
  border: 0;
  border-radius: 15px;
  padding: 0;
  background: #fff;
  color: var(--ink);
  box-shadow: 0 24px 64px rgba(15, 23, 42, .22);
}
.key-modal::backdrop { background: rgba(15, 23, 42, .44); backdrop-filter: blur(2px); }
.key-modal-head {
  display: flex;
  min-height: 76px;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid #eceef0;
  padding: 0 22px;
}
.key-modal-head h2 { margin-top: 2px; font-size: 18px; }
.key-modal-head .eyebrow { margin: 0; }
.key-modal-close {
  display: grid;
  width: 34px;
  height: 34px;
  place-items: center;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: #7a7f84;
  font-size: 24px;
  cursor: pointer;
}
.key-modal-close:hover { background: #f3f4f6; color: #1f2328; }
.key-modal-body { display: grid; gap: 16px; padding: 22px; }
.key-modal-body .field { display: grid; gap: 7px; }
.key-modal-body .block-note { margin: 0; color: #7a7f84; font-size: 12px; }
.key-modal-foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  border-top: 1px solid #eceef0;
  padding: 14px 22px;
  background: #fafbfc;
}

.keys-panel {
  display: block;
  border: 1px solid #e5e7eb;
  border-radius: 15px;
  background: #fff;
  overflow: hidden;
}
.keys-panel .dash-panel-head small { color: #8b9095; font-size: 11px; font-weight: 400; }
.keys-toolbar {
  display: grid;
  grid-template-columns: minmax(220px, 1fr) 150px 150px auto auto;
  align-items: center;
  gap: 9px;
  min-height: 64px;
  border-bottom: 1px solid #eceef0;
  padding: 12px 15px;
}
.keys-toolbar .input { width: 100%; background-color: #fff; }
.keys-reset { padding: 7px 5px; color: #7a7f84; font-size: 13px; }
.keys-reset:hover { color: #2563eb; }
.keys-table-wrap { min-width: 0; background: #fff; overflow: visible; }
.keys-table { width: 100%; max-width: 100%; table-layout: fixed; border: 0; font-size: 12px; }
.keys-table th {
  height: 43px;
  padding: 0 12px;
  background: #fff;
  color: #777d83;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .02em;
  text-align: left;
}
.keys-table td { height: 72px; padding: 11px 12px; vertical-align: middle; }
.keys-table tbody tr:hover { background: #fafbfc; }
/* Tabular figures keep dates, counts, and amounts on a shared vertical rhythm,
   the same way the overview cards line their numbers up. */
.key-date, .key-expiry, .quota-line, .usage-link, .keys-table td:nth-child(5) {
  font-variant-numeric: tabular-nums;
}
.keys-table th:nth-child(1) { width: 16%; }
.keys-table th:nth-child(2) { width: 30%; }
.keys-table th:nth-child(3) { width: 14%; }
.keys-table th:nth-child(4) { width: 18%; }
.keys-table th:nth-child(5) { width: 12%; }
.keys-table th:nth-child(6) { width: 150px; }
.keys-table .key-name {
  display: block;
  overflow: hidden;
  margin-bottom: 7px;
  color: #31353a;
  font-size: 13px;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.key-meta-line { display: flex; min-width: 0; align-items: center; gap: 9px; color: #7a7f84; font-size: 11px; }
.key-status { display: inline-flex; align-items: center; gap: 5px; color: #238636; }
.key-status::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.key-status.is-off { color: #9a5b56; }
.key-group { overflow: hidden; min-width: 0; text-overflow: ellipsis; white-space: nowrap; }
.keys-table .key-val {
  min-width: 0;
  gap: 4px;
  overflow: hidden;
  border-color: #e4e7ea;
  border-radius: 7px;
  padding: 6px 7px 6px 9px;
  background: #f8f9fa;
}
.keys-table .key-text { overflow: hidden; color: #4b5563; white-space: nowrap; text-overflow: ellipsis; }
.keys-panel .key-btn {
  border-color: transparent;
  border-radius: 5px;
  padding: 4px 7px;
  background: transparent;
  color: #66707a;
  font-size: 11px;
}
.keys-panel .key-btn:hover { border-color: #d9dcdf; background: #fff; color: #2563eb; }
.key-date, .key-expiry, .quota-line { display: block; white-space: nowrap; }
.key-expiry, .quota-line { overflow: hidden; margin-top: 5px; color: #8b9095; font-size: 11px; text-overflow: ellipsis; }
.usage-link { color: #2563eb; font-weight: 600; white-space: nowrap; }
.key-actions { display: flex; max-width: 100%; min-width: 0; align-items: center; gap: 2px; }
.key-actions .key-btn-danger { color: #9a5b56; }
.key-editor { position: relative; }
.key-editor > summary { list-style: none; }
.key-editor > summary::-webkit-details-marker { display: none; }
.key-editor:not([open]) > .key-edit-form { display: none; }
.key-edit-form {
  position: fixed;
  z-index: 30;
  right: 24px;
  top: 96px;
  width: min(520px, calc(100vw - 32px));
  max-height: calc(100dvh - 120px);
  overflow-y: auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  padding: 16px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: #fff;
  box-shadow: 0 20px 55px rgba(15, 23, 42, .20);
}
.key-edit-form label { display: grid; gap: 5px; color: var(--ink-2); }
.key-edit-form label:has(> input[type="checkbox"]) { display: flex; align-items: center; }
.key-edit-form .edit-wide { grid-column: 1 / -1; }
.key-edit-form textarea { resize: vertical; }

.usage-estimated {
  margin-left: 5px;
  color: #b45309;
  font-size: 9px;
  font-weight: 500;
}

@media (max-width: 1279px) {
  .keys-toolbar { grid-template-columns: minmax(220px, 1fr) 140px 140px auto auto; }
  .keys-table-wrap { border: 0; background: transparent; }
  .keys-table { display: block; min-width: 0; }
  .keys-table thead { display: none; }
  .keys-table tbody { display: grid; gap: 12px; }
  .keys-table tr {
    position: relative;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px 14px;
    padding: 16px;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: #fff;
  }
  .keys-table td {
    display: block;
    width: auto !important;
    padding: 0;
    border: 0;
    text-align: left !important;
    min-width: 0;
  }
  .keys-table td::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 4px;
    color: var(--ink-3);
    font-size: 10px;
    text-transform: uppercase;
  }
  .keys-table td:nth-child(1),
  .keys-table td:nth-child(2),
  .keys-table td:last-child { grid-column: 1 / -1; }
  .keys-table .key-val { min-width: 0; max-width: none; }
  .key-actions { min-width: 0; }
  .key-edit-form { inset: 10vh 16px auto; max-height: 80vh; grid-template-columns: 1fr; }
  .key-edit-form .edit-wide { grid-column: auto; }
}

@media (max-width: 760px) {
  .keys-toolbar { grid-template-columns: 1fr 1fr; }
  .keys-toolbar .input[type="search"] { grid-column: 1 / -1; }
  .keys-toolbar .btn { width: 100%; }
  .keys-reset { text-align: center; }
}

@media (max-width: 520px) {
  .keys-head { align-items: flex-end; }
  .keys-panel { border: 0; border-radius: 0; background: transparent; overflow: visible; }
  .keys-toolbar { border: 1px solid #e5e7eb; border-radius: 12px; background: #fff; }
  .keys-table tbody { margin-top: 10px; }
  .key-modal-foot .btn { flex: 1; }
}

select.input {
  appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath fill='none' stroke='%236b7280' stroke-width='1.4' d='M3 4.5 6 7.5 9 4.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 12px;
  padding-right: 30px;
  cursor: pointer;
}

/* ---------- logs page ---------- */

.app[data-page="logs"] { padding-bottom: 16px; }
.logs-page { min-width: 0; }

/* One quiet filter rail above the table. The range is typed rather than picked
   from fixed windows, so an exact "what happened between these two hours" is
   answerable without repeating totals already available on the dashboard. */
.logs-head {
  margin-bottom: 10px;
  padding: 12px;
  border: 1px solid #edf0f2;
  border-radius: 12px;
  background: #fff;
}

.logs-filter { display: flex; flex-wrap: wrap; align-items: center; gap: 9px; }

.lf-range {
  display: grid;
  grid-template-columns: minmax(150px, 1fr) auto minmax(150px, 1fr);
  flex: 1 1 390px;
  max-width: 500px;
  min-height: 36px;
  border-radius: 10px;
  background: #f3f4f5;
  overflow: hidden;
}

.lf-range .input {
  width: 100%;
  min-width: 0;
  max-width: none;
  height: 36px;
  padding: 7px 12px;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  font-size: 13px;
}

.lf-range .input:focus { box-shadow: inset 0 0 0 1px #d9dde1; }
.lf-dash { align-self: center; color: #737b83; font-size: 12px; }

.lf-search {
  position: relative;
  flex: 1 1 150px;
  max-width: 240px;
}

.lf-search::before {
  content: "";
  position: absolute;
  z-index: 1;
  left: 12px;
  top: 50%;
  width: 12px;
  height: 12px;
  border: 2px solid #737b83;
  border-radius: 50%;
  transform: translateY(-58%);
  pointer-events: none;
}

.lf-search::after {
  content: "";
  position: absolute;
  z-index: 1;
  left: 23px;
  top: 22px;
  width: 6px;
  height: 2px;
  border-radius: 2px;
  background: #737b83;
  transform: rotate(45deg);
  transform-origin: left center;
  pointer-events: none;
}

.lf-search .input {
  width: 100%;
  min-width: 0;
  max-width: none;
  height: 36px;
  padding: 7px 12px 7px 36px;
  border: 0;
  border-radius: 10px;
  background: #f3f4f5;
  box-shadow: none;
  font-size: 13px;
}

.lf-search .input:focus { box-shadow: inset 0 0 0 1px #d9dde1; }
.lf-search .input::placeholder { color: #858c93; opacity: 1; }

.lf-actions {
  display: flex;
  flex: 0 0 auto;
  margin-left: auto;
  justify-content: flex-end;
  gap: 7px;
}
.logs-filter .btn {
  min-height: 36px;
  padding: 7px 16px;
  border: 0;
  border-radius: 10px;
  background: #f3f4f5;
  color: #3f464d;
  box-shadow: none;
  font-size: 13px;
  font-weight: 500;
}
.logs-filter .btn:hover { background: #e9ebed; color: #20252a; }

.seg {
  display: inline-flex;
  border: 1px solid var(--line-3);
  border-radius: var(--r-sm);
  background: #fff;
  overflow: hidden;
}

.seg-btn {
  display: block;
  border: 0;
  border-right: 1px solid var(--line-3);
  background: #fff;
  color: var(--ink-2);
  font-family: inherit;
  font-size: 13px;
  line-height: 1.3;
  padding: 7px 14px;
  text-decoration: none;
  cursor: pointer;
  transition: background-color .12s ease, color .12s ease;
}

.seg-btn:last-child { border-right: 0; }
.seg-btn:hover { background: var(--tint); color: var(--ink); }
.seg-btn.on { background: var(--green); color: #fff; }

.cache-hit {
  color: var(--green-d);
  font-weight: 500;
}

/* The page itself stays still: rows scroll inside this framed viewport, while
   the column labels and pagination remain visible at its two edges. */
.logs-frame {
  display: flex;
  flex-direction: column;
  border: 1px solid #edf0f2;
  border-radius: 12px;
  background: #fff;
  box-shadow: none;
  overflow: hidden;
}

.logs-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  transition: opacity .14s ease;
}

/* New API keeps the pager mounted while it loads another page. Only the rows
   soften briefly, so controls do not vanish or jump around during the request. */
.logs-frame.is-loading .logs-scroll { opacity: .48; }
.logs-frame.is-loading .logs-pager { cursor: progress; }
.logs-frame.is-loading .pager-info::after {
  content: "";
  display: inline-block;
  width: 12px;
  height: 12px;
  margin-left: 8px;
  border: 2px solid #dbe7fb;
  border-top-color: #2563eb;
  border-radius: 50%;
  vertical-align: -2px;
  animation: log-page-spin .65s linear infinite;
}

.logs-page-error {
  flex-basis: 100%;
  color: #b45309;
  font-size: 12px;
  text-align: left;
}

@keyframes log-page-spin { to { transform: rotate(360deg); } }

.logs-frame .tbl-logs thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  box-shadow: 0 1px 0 var(--line);
}

.logs-frame .pager {
  flex: 0 0 auto;
  margin: 0;
  padding: 15px 12px;
  border-top: 1px solid #edf0f2;
  background: #fff;
}

/* The frame has to end exactly at the viewport bottom, or the pager sits just
   below the fold: you scroll to click it, the click loads a new page at scroll
   top, and the bar appears to vanish until you scroll again. Sizing the frame
   as `100dvh - 196px` guessed the distance from the viewport top down to the
   frame, and the guess was 27px short — 75px once the filter row wrapped to two
   lines. Nothing here may assume that distance: the body takes the viewport
   height and each link in the chain hands the remainder down, so the frame
   ends where the padding does no matter how tall the header or filters grow. */
@media (min-width: 900px) {
  body:has(.app[data-page="logs"]) {
    height: 100dvh;
    /* auto rather than hidden: below the min-height floor the frame stops
       shrinking, and then the page still has to be able to scroll to the pager */
    overflow: auto;
  }

  /* `body > .app` ships flex: 1 0 auto; a shrink factor of 0 would let a
     hundred rows push the pager back off the bottom edge. */
  body:has(.app[data-page="logs"]) > .app { flex: 1 1 auto; min-height: 0; }

  /* Every other console page wants align-items: start. This grid row has to
     stretch instead, or the main column is only as tall as its content and the
     frame inherits no height to fill. */
  .app[data-page="logs"] { align-items: stretch; }
  .app[data-page="logs"] > .side { align-self: start; }

  .logs-page { display: flex; flex-direction: column; min-height: 0; }
  .logs-frame { flex: 1 1 auto; min-height: 300px; }
}

/* ---------- log table ---------- */

/* One dense line per call. The previous two-line cells fitted eight rows on a
   1440 screen and padded every row with filler text; a plain data table with
   one fact per column reads faster and shows roughly twice the history. */
.tbl-logs { table-layout: fixed; font-size: 13px; }

.tbl-logs th,
.tbl-logs td {
  padding: 12px 10px;
  white-space: nowrap;
  vertical-align: middle;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tbl-logs thead th {
  background: #fff;
  font-size: 13px;
  font-weight: 600;
  color: #70777e;
  border-bottom: 1px solid #edf0f2;
}

/* Match New API's eleven usage-log fields and order, including its rightmost
   detail action. Time, model and latency keep the largest shares. */
.tbl-logs .col-ts { width: 13%; }
.tbl-logs .col-key { width: 8%; }
.tbl-logs .col-group { width: 10%; }
.tbl-logs .col-type { width: 6%; }
.tbl-logs .col-model { width: 14%; }
.tbl-logs .col-lat { width: 14%; }
.tbl-logs .col-in { width: 6%; }
.tbl-logs .col-out { width: 6%; }
.tbl-logs .col-cache { width: 7%; }
.tbl-logs .col-cost { width: 8%; }
.tbl-logs .col-detail { width: 8%; }

/* Banding plus a hover tint keeps the eye on one row across all eleven fields. */
.tbl-logs tbody td { border-bottom: 1px solid #edf0f2; }
.tbl-logs tbody tr:last-child td { border-bottom: 0; }
.tbl-logs .log-row.alt > td { background: #fff; }
.tbl-logs .log-row:hover > td { background: #fafbfc; }
.tbl-logs .log-ts {
  font-family: inherit;
  font-size: 13px;
  color: #30363c;
}

.tbl-logs .log-in,
.tbl-logs .log-out,
.tbl-logs .log-cache,
.tbl-logs .log-cost { font-variant-numeric: tabular-nums; }

.tbl-logs .log-cost { font-weight: 600; }

.cost-zero { color: var(--ink-3); font-weight: 500; }

/* Prefix, not a badge: a rare estimate flag should not widen the column for
   every row. Cache is a first-class numeric field, matching New API. */
.tok-est { margin-right: 1px; color: var(--ink-3); font-weight: 400; cursor: help; }


.log-detail-btn {
  min-height: 28px;
  padding: 4px 11px;
  border: 1px solid #dbe7fb;
  border-radius: 8px;
  background: #f3f7ff;
  color: #2563eb;
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.log-detail-btn:hover { background: #eaf2ff; }
.log-detail-btn::after {
  content: "";
  display: inline-block;
  width: 5px;
  height: 5px;
  margin: 0 1px 2px 7px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
  transition: transform .15s ease;
}
.log-detail-btn[aria-expanded="true"]::after { transform: rotate(-135deg) translate(-2px, -1px); }

.log-expand > td { padding: 0 !important; background: #fafcff; }
.log-expand-panel { padding: 14px 18px 16px; border-bottom: 1px solid #edf0f2; }
.log-expand-head { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; font-size: 12px; }
.log-expand-head strong { color: #30363c; font-size: 13px; }
.note-calc { color: #727a82; font-variant-numeric: tabular-nums; }
.note-bad { color: #b45309; }

.calc-tbl { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.calc-tbl td { padding: 7px 0; border-bottom: 1px solid #edf0f2; }
.calc-tbl tr:last-child td { border-bottom: 0; }
.calc-saved td { color: #2f7137; }
.calc-total td { padding-top: 9px; border-top: 1px solid #e2e6e9; font-weight: 600; }
.calc-note { margin-top: 9px; color: #727a82; font-size: 12px; line-height: 1.65; }

/* ---------- label tags ---------- */

/* One pill for every categorical value, coloured by hashing the name. The hue
   assignment reproduces the legacy console's, so a model or group keeps the
   colour the users already associate with it. */
.ntag {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  padding: 1px 8px;
  border-radius: 999px;
  background: var(--line);
  color: var(--ink-2);
  font-size: 11.5px;
  line-height: 1.7;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: middle;
}

.ntag + .ntag { margin-left: 3px; }
.ntag-mono { font-family: var(--mono); font-size: 11.5px; }

/* Latency reads as a bare number behind a coloured bar: it is a measurement,
   not a category, so it does not get a pill of its own. */
.ntag-plain {
  padding: 1px 0 1px 7px;
  border-left: 3px solid var(--line-3);
  border-radius: 0;
  background: none;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}

.lat-fast { border-left-color: var(--green-l); }
.lat-mid { border-left-color: #f0a92a; }
.lat-slow { border-left-color: #e05656; }

/* The bar grades total duration; time to first token is graded on its own
   scale, so it carries the colour as text instead of a second bar. */
.lat-frt { color: var(--ink-3); }
.lat-frt.t-mid { color: #b5730f; }
.lat-frt.t-slow { color: #c0392b; }

.ntag-amber { background: #fff1db; color: #8f5100; }
.ntag-blue { background: #e6f1fe; color: #0b4fbf; }
.ntag-cyan { background: #dff4f7; color: #00707f; }
.ntag-green { background: #e4f7ec; color: #0a7a46; }
.ntag-grey { background: #f0f1f3; color: #575d66; }
.ntag-indigo { background: #e9ebfb; color: #303ba8; }
.ntag-light-blue { background: #e1f3fd; color: #00648f; }
.ntag-lime { background: #eef7da; color: #4f7a00; }
.ntag-orange { background: #ffeee0; color: #9c4a00; }
.ntag-pink { background: #fde9f3; color: #a82269; }
.ntag-purple { background: #f2e9fc; color: #6329b8; }
.ntag-red { background: #fde9e9; color: #b01b1b; }
.ntag-teal { background: #def4f1; color: #007566; }
.ntag-violet { background: #eee8fc; color: #5227c4; }
.ntag-yellow { background: #fff6d9; color: #7e6000; }

/* English labels run wider than the Chinese they replace, and the surplus can
   only come out of the columns. Shaving the pills is cheaper than clipping
   model names. */
html[lang="en"] .tbl-logs .ntag { padding: 1px 6px; font-size: 11px; }

/* New API-style compact chips: soft fills, modest radius and no heavy borders. */
.tbl-logs .ntag { padding: 2px 9px; border-radius: 8px; line-height: 1.65; }
.tbl-logs .log-model .ntag::before { content: "✺"; margin-right: 5px; color: #5366d6; }
.tbl-logs .ntag-lat { background: #e7f5e7; color: #2f7137; font-variant-numeric: tabular-nums; }
.tbl-logs .ntag-lat.lat-mid, .tbl-logs .ntag-lat.t-mid { background: #fff0df; color: #a35319; }
.tbl-logs .ntag-lat.lat-slow, .tbl-logs .ntag-lat.t-slow { background: #ffe9dc; color: #b54920; }

/* The phone layout for these rows lives at the end of the file: it has to
   outrank the shared `.tbl td` padding that a later media query sets. */

/* Exact timestamps in log tables: date above, time below, so full precision
   fits without widening the column. */
.ts { line-height: 1.35; }
.ts-d { display: block; font-size: 11.5px; color: var(--ink-3); }
.ts-t { display: block; font-variant-numeric: tabular-nums; color: var(--ink-2); }

.pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px 16px;
  margin-top: 16px;
}

.pager-info { font-size: 13px; color: var(--ink-3); }

.pages, .pager-size {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  font-size: 13px;
  color: var(--ink-3);
}

.page-btn {
  display: inline-block;
  min-width: 32px;
  padding: 6px 9px;
  border: 1px solid var(--line-3);
  border-radius: var(--r-sm);
  background: #fff;
  color: var(--ink-2);
  font-size: 13px;
  line-height: 1.3;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color .12s ease, color .12s ease, border-color .12s ease;
}

a.page-btn:hover { background: var(--tint); color: var(--ink); border-color: var(--line-2); }
.page-btn.on { background: var(--green); border-color: var(--green); color: #fff; }

/* Rendered as a span, not a link, so the row keeps its shape at either end. */
.page-btn.off { color: var(--ink-3); background: var(--tint); cursor: default; }

.page-gap { padding: 0 2px; color: var(--ink-3); }

.logs-frame .pager-info, .logs-frame .pager-size, .logs-frame .pager-total { color: #727a82; }
.logs-frame .page-btn { border-color: transparent; background: transparent; }
.logs-frame a.page-btn:hover { border-color: #dbe7fb; background: #f3f7ff; color: #2563eb; }
.logs-frame .page-btn.on { border-color: #dbe7fb; background: #eff6ff; color: #2563eb; }
.logs-frame .page-btn.off { border-color: transparent; background: transparent; color: #a1a7ad; }
.pager-total { margin-right: 4px; white-space: nowrap; }

@media (max-width: 899.98px) {
  .app[data-page="logs"] { padding-bottom: 40px; }
  .logs-filter { align-items: stretch; }
  .lf-range { flex: 1 1 100%; max-width: none; }
  .lf-search { flex: 1 1 calc(33.333% - 6px); max-width: none; }
  .lf-actions { margin-left: 0; }
  .logs-frame { overflow: visible; }
  .logs-scroll { overflow: visible; }
}

@media (max-width: 700px) {
  .lf-search { flex-basis: 100%; }
  .lf-actions { width: 100%; }
  .lf-actions .btn { flex: 1; text-align: center; }
  .pager { justify-content: flex-start; }
  .pager-info { width: 100%; }
}

/* ---------- wallet page ---------- */

/* Stripe settles in USD while the wallet stays CNY-denominated; keep that
   conversion rule visible beside the balance. */
.rate-note {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  margin: 0 0 22px;
  padding: 12px 16px;
  border: 1px solid #cfe7dc;
  border-radius: var(--r-sm);
  background: #f2fbf7;
  color: #14614a;
  font-size: 13px;
  line-height: 1.7;
}

.rate-eq {
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--green-d);
}

.rate-txt { flex: 1; min-width: 240px; }
.rate-note b { color: var(--green-d); }

.pay-pick { margin-bottom: 22px; }

.pay-pick-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 12px;
}

.pay-pick-step {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--ink);
  color: #fff;
  font-size: 12px;
  font-weight: 600;
}

.pay-pick-t { font-size: 14px; font-weight: 600; color: var(--ink); }
.pay-pick-note { font-size: 12px; color: var(--ink-3); }

/* Both methods are equal choices, so they get card-sized weight rather than the
   pair of 13px pills this used to be — nobody noticed a card option was there. */
.pay-choice {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
}

@media (max-width: 700px) {
  .pay-choice { grid-template-columns: 1fr; }
}

.pay-choice-card {
  /* Each method carries its own accent so the two cards stay distinguishable
     when one is selected; a shared green made them read as one control. */
  --pm: var(--green);
  --pm-glow: rgba(4, 120, 87, .10);
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 14px 46px 14px 16px;
  border: 1px solid var(--line-3);
  border-radius: var(--r-sm);
  background: #fff;
  text-align: left;
  cursor: pointer;
  transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}

.pay-choice-card[data-method="stripe"] {
  --pm: #635bff;
  --pm-glow: rgba(99, 91, 255, .14);
}

.pay-choice-card:hover { border-color: var(--pm); transform: translateY(-1px); }

.pay-choice-card.is-on {
  border-color: var(--pm);
  box-shadow: inset 0 0 0 1px var(--pm), 0 6px 18px var(--pm-glow);
}

/* With one method configured there is nothing to choose, so the card drops the
   radio affordances and reads as a statement of what checkout will do. */
.pay-choice-card.is-solo {
  padding-right: 16px;
  cursor: default;
}

.pay-choice-card.is-solo:hover { border-color: var(--line-3); transform: none; }
.pay-choice-card.is-solo .pay-choice-tick { display: none; }

.pay-choice-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 38px;
  height: 38px;
  border-radius: 10px;
}

.mark-wechat { background: #e6f7ed; color: #07c160; }
.mark-card { background: #ecebfd; color: #635bff; }

.pay-choice-body {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.pay-choice-t {
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
}

.pay-choice-d { font-size: 12px; line-height: 1.6; color: var(--ink-3); }
.pay-choice-d b { color: var(--ink-2); font-weight: 600; }

/* Which cards actually work is the first question this option raises, so the
   networks are named on the option itself rather than in the fine print. */
.pay-brands {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-top: 9px;
}

.pay-brand {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 26px;
  padding: 0 8px;
  border: 1px solid var(--line-2);
  border-radius: 6px;
  background: #fff;
}

.pay-brand-ico { display: block; flex: none; width: 25px; height: 15px; }

.pay-brand-wm {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .01em;
  color: var(--ink-2);
  white-space: nowrap;
}

/* Visa is distributed as a wordmark, so the styled text is the mark. */
.brand-visa {
  font-size: 14px;
  font-style: italic;
  font-weight: 800;
  letter-spacing: -.01em;
  color: #1434cb;
}

.pay-choice-tick {
  position: absolute;
  top: 50%;
  right: 16px;
  width: 18px;
  height: 18px;
  border: 1px solid var(--line-3);
  border-radius: 50%;
  background: #fff;
  transform: translateY(-50%);
}

.pay-choice-card.is-on .pay-choice-tick {
  border-color: var(--pm);
  background: var(--pm);
}

.pay-choice-card.is-on .pay-choice-tick::after {
  content: "";
  position: absolute;
  left: 5px;
  top: 2px;
  width: 5px;
  height: 9px;
  border: solid #fff;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

/* Three fixed columns so the six tiers read as 3 + 3. Sizing the tracks by
   min-width instead packed five into a wide panel and orphaned the sixth on a
   row of its own. */
.topup-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  margin-bottom: 16px;
}

/* Three 150px-plus cards don't fit a phone; two rows of three become three
   rows of two. */
@media (max-width: 560px) {
  .topup-grid { grid-template-columns: repeat(2, 1fr); }
}

.topup-grid form { margin: 0; }

.topup-opt {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  width: 100%;
  padding: 18px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: #fff;
  color: inherit;
  cursor: pointer;
  transition: border-color .15s ease, transform .15s ease;
}

.topup-opt:hover {
  border-color: var(--green);
  transform: translateY(-1px);
}

.topup-amt {
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}

.topup-sub {
  font-size: 12px;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}

/* Stripe Checkout rejects anything under $0.50 after conversion, so obviously
   tiny admin test amounts go flat instead of failing after a request. */
.topup-opt:disabled { cursor: not-allowed; opacity: .45; }
.topup-opt:disabled:hover { border-color: var(--line); transform: none; }

/* The 1-fen admin button sits in the same grid as the real amounts; dashed and
   muted so it reads as a tool rather than an offer. */
.topup-test {
  border-style: dashed;
  border-color: var(--line-3);
}

.topup-test .topup-amt { color: var(--ink-2); }

/* Sized to the QR rather than to the column: a checkout stretched across a
   1200px console reads as a broken embed. */
.pay-panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  max-width: 420px;
  margin: 0 auto;
}

/* The checkout is a third-party page, so its height is fixed here rather than
   negotiated — a cross-origin frame can't report its own content size. */
.pay-frame {
  width: 100%;
  max-width: 340px;
  height: 420px;
  border: 0;
}

.pay-tip { margin-top: 12px; text-align: center; }

.pay-done {
  padding: 48px 24px;
  text-align: center;
}

.pay-done-t {
  font-size: 20px;
  font-weight: 600;
  color: var(--green);
  margin-bottom: 8px;
}

.pay-done-d {
  font-size: 14px;
  color: var(--ink-3);
}

/* Picking an amount opens this straight away and swaps in the QR when the
   provider answers, so the wait happens over the amount grid instead of on a
   page that has nothing else on it. */
.pay-modal {
  width: min(420px, calc(100vw - 32px));
  border: 0;
  border-radius: 15px;
  padding: 0;
  background: #fff;
  color: var(--ink);
  box-shadow: 0 24px 64px rgba(15, 23, 42, .22);
}

.pay-modal::backdrop { background: rgba(15, 23, 42, .44); backdrop-filter: blur(2px); }

.pay-modal-body { padding: 18px 22px 22px; }

.pay-modal-sub {
  margin: 0 0 14px;
  text-align: center;
  font-size: 13px;
  color: var(--ink-2);
  min-height: 20px;
}

/* Fixed to the frame's height so swapping between spinner, QR and receipt
   doesn't resize the dialog under the pointer. */
.pay-modal-stage {
  display: grid;
  place-items: center;
  min-height: 420px;
}

.pay-modal-load {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  color: var(--ink-3);
}

.pay-spin {
  width: 20px;
  height: 20px;
  border: 2px solid var(--line-2);
  border-top-color: var(--green);
  border-radius: 50%;
  animation: pay-spin .7s linear infinite;
}

@keyframes pay-spin { to { transform: rotate(360deg); } }

.pay-modal-err {
  margin: 0;
  max-width: 280px;
  text-align: center;
  font-size: 13px;
  line-height: 1.7;
  color: #a33a3a;
}

/* Class selectors outrank the UA stylesheet's [hidden], so restate it for the
   stage's three swappable states. */
.pay-modal [hidden] { display: none; }

.pay-modal-tip { margin: 14px 0 0; text-align: center; }

@media (max-width: 480px) {
  .pay-modal-stage { min-height: 380px; }
}

/* ---------- settings page ---------- */

.field-hint {
  font-size: 12px;
  line-height: 1.7;
  color: var(--ink-3);
}

.deflist { display: grid; gap: 1px; background: var(--line); }

.deflist > div {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  background: #fff;
  padding: 10px 0;
}

.deflist dt { font-size: 13px; color: var(--ink-2); }

.deflist dd {
  font-size: 13px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.deflist + .field-hint { margin-top: 16px; }

/* ---------- auth page ---------- */

body.plain { background: var(--tint); }

.auth {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 22px;
  padding: 40px 20px;
}

.auth-brand { justify-content: center; }
.auth > .lang-switch { justify-content: center; margin-top: -24px; }

.auth-card {
  width: 100%;
  max-width: 400px;
  border: 1px solid var(--line-2);
  border-radius: var(--r-lg);
  background: #fff;
  padding: 32px;
  box-shadow: var(--sh-hero);
}

.auth-h { font-size: 22px; letter-spacing: -.02em; }

.auth-sub {
  margin-top: 8px;
  margin-bottom: 24px;
  font-size: 13px;
  line-height: 1.7;
  color: var(--ink-2);
}

.form { display: grid; gap: 16px; }

.oauth-entry { margin-bottom: 18px; }

/* The OAuth action is a form control, not one of the site's pill CTAs. Keep
   its geometry and interaction local so shared buttons remain untouched. */
.btn-google {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 48px;
  border: 1px solid #d6ddda;
  border-radius: 12px;
  background: #fff;
  color: var(--ink);
  padding: 0 48px;
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -.005em;
  cursor: pointer;
  box-shadow: 0 1px 2px rgba(15, 76, 92, .05);
  transition: border-color .15s ease, background-color .15s ease, box-shadow .15s ease;
}

.btn-google:hover {
  border-color: #b6c5bf;
  background: #fbfcfc;
  box-shadow: 0 3px 10px rgba(15, 76, 92, .08);
}

.btn-google:active {
  background: var(--tint);
  box-shadow: 0 1px 2px rgba(15, 76, 92, .04);
}

.btn-google:focus-visible {
  outline: none;
  border-color: var(--green);
  box-shadow: 0 0 0 3px rgba(4, 120, 87, .1);
}

.google-mark {
  position: absolute;
  left: 16px;
  width: 18px;
  height: 18px;
}

.google-mark svg { display: block; width: 100%; height: 100%; }
.google-label { overflow: hidden; text-overflow: ellipsis; }

.auth-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 16px;
  color: var(--ink-3);
  font-size: 12px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: .01em;
}

.auth-divider span { padding: 0 2px; white-space: nowrap; }

.auth-divider::before,
.auth-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--line-2));
}

.auth-divider::after { transform: scaleX(-1); }

/* Submit buttons size to their label; the auth page opts back into full
   width with .btn-block. */
.form > .btn { justify-self: start; }
.form > .btn-block { justify-self: stretch; }

.field { display: grid; gap: 7px; }

.field-code { display: flex; gap: 8px; }
.field-code .input { flex: 1; min-width: 0; }
.field-code .btn { flex-shrink: 0; white-space: nowrap; }

/* 阿里云验证码是弹窗模式，占位元素本身不占高；无痕验证时完全不可见 */
.captcha-slot { margin: 0; }

.field-edit { font-size: 12px; font-weight: 400; margin-left: 6px; }
.input[readonly] { background: var(--tint); color: var(--ink-2); }

.field-label { font-size: 13px; color: var(--ink-2); }

.input {
  width: 100%;
  border: 1px solid var(--line-3);
  border-radius: var(--r-sm);
  background: #fff;
  padding: 11px 13px;
  font-family: inherit;
  font-size: 15px;
  color: var(--ink);
  outline: none;
  transition: border-color .12s ease, box-shadow .12s ease;
}

.input:focus {
  border-color: var(--green);
  box-shadow: 0 0 0 3px rgba(4, 120, 87, .1);
}

.btn-block { width: 100%; padding: 12px; margin-top: 4px; }

/* Keep the two sign-in actions in one visual system without changing shared
   auth buttons on registration, password reset, or account pages. */
.auth-sign-in .form > .btn-block {
  min-height: 48px;
  border: 1px solid var(--green);
}

/* ---------- attributed signup landing ---------- */

/* The campaign strip is shared with the home page, where it spans the content
   width. On the auth page it has to track the card instead: 400px normally,
   and the wider two-column grid on an attributed visit. */
.signup-deal {
  width: 100%;
  max-width: 400px;
  margin: 0;
}

.auth-source .signup-deal { max-width: 980px; }

@media (max-width: 800px) {
  .auth-source .signup-deal { max-width: 520px; }
}

/* The ordinary /sign-up card stays exactly as-is. These rules only activate
   when utm_source/source turns the page into an acquisition landing surface. */
.auth-source {
  justify-content: flex-start;
  gap: 16px;
  padding-top: 28px;
  padding-bottom: 32px;
}

.auth-source > .lang-switch { margin-top: -16px; }

.signup-lead-gift,
.signup-lead-grid {
  width: min(100%, 980px);
}

.signup-lead-gift {
  display: flex;
  align-items: center;
  gap: 18px;
  border: 1px solid rgba(4, 120, 87, .2);
  border-radius: var(--r-lg);
  background: linear-gradient(115deg, var(--green), #066b52);
  color: #fff;
  padding: 16px 22px;
  box-shadow: 0 10px 30px rgba(4, 120, 87, .16);
}

.signup-gift-amount {
  flex: 0 0 auto;
  min-width: 84px;
  font-size: 36px;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -.04em;
  font-variant-numeric: tabular-nums;
}

.signup-gift-copy { display: grid; gap: 3px; }
.signup-gift-copy strong { font-size: 17px; letter-spacing: -.01em; }
.signup-gift-copy small { color: rgba(255, 255, 255, .82); font-size: 12px; line-height: 1.5; }

.signup-lead-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(360px, .75fr);
  grid-template-areas: "prices form";
  gap: 18px;
  align-items: start;
}

.signup-price-wall {
  grid-area: prices;
  min-width: 0;
  border: 1px solid var(--line-2);
  border-radius: var(--r-lg);
  background: #fff;
  padding: 24px;
  box-shadow: var(--sh-hero);
}

.signup-lead-form {
  grid-area: form;
  max-width: none;
  position: sticky;
  top: 18px;
}

.signup-price-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  margin-bottom: 18px;
}

.signup-price-kicker {
  display: inline-block;
  margin-bottom: 6px;
  color: var(--green);
  font-size: 11px;
  font-weight: 750;
  letter-spacing: .08em;
  text-transform: uppercase;
}

.signup-price-head h1 {
  font-size: 23px;
  line-height: 1.2;
  letter-spacing: -.025em;
}

.signup-price-head p {
  margin-top: 7px;
  color: var(--ink-2);
  font-size: 13px;
  line-height: 1.55;
}

.signup-price-count {
  flex: 0 0 auto;
  border: 1px solid var(--line-2);
  border-radius: 999px;
  background: var(--tint);
  color: var(--ink-2);
  padding: 6px 10px;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

.signup-price-list {
  display: grid;
  gap: 6px;
  max-height: 505px;
  overflow-y: auto;
  padding-right: 5px;
  scrollbar-width: thin;
  scrollbar-color: var(--line-3) transparent;
}

.signup-price-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: 50px;
  border: 1px solid transparent;
  border-radius: 11px;
  background: var(--tint);
  padding: 8px 11px;
}

.signup-price-row:hover { border-color: var(--line-2); }

.signup-price-model {
  display: grid;
  min-width: 0;
  gap: 2px;
}

.signup-price-model small {
  color: var(--ink-3);
  font-size: 10px;
  line-height: 1.2;
}

.signup-price-model code {
  overflow: hidden;
  color: var(--ink);
  font-size: 12px;
  font-weight: 650;
  line-height: 1.35;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.signup-price-value {
  display: flex;
  flex: 0 0 auto;
  align-items: baseline;
  gap: 4px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.signup-price-value strong { color: var(--green); font-size: 15px; }
.signup-price-value small { color: var(--ink-3); font-size: 9px; white-space: nowrap; }

.signup-price-foot,
.signup-price-empty {
  margin-top: 14px;
  color: var(--ink-3);
  font-size: 11px;
  line-height: 1.6;
}

@media (max-width: 800px) {
  .auth-source { padding-top: 24px; }

  .signup-lead-gift,
  .signup-lead-grid { max-width: 520px; }

  .signup-lead-grid {
    grid-template-columns: 1fr;
    grid-template-areas:
      "form"
      "prices";
  }

  .signup-lead-form { position: static; }
  .signup-price-list { max-height: none; }
}

@media (max-width: 520px) {
  .auth-source { padding-inline: 14px; }

  .signup-lead-gift {
    gap: 12px;
    padding: 14px 16px;
  }

  .signup-gift-amount { min-width: 67px; font-size: 29px; }
  .signup-gift-copy strong { font-size: 15px; }

  .signup-price-wall { padding: 18px 15px; }
  .signup-price-head h1 { font-size: 20px; }
  .signup-price-count { display: none; }
  .signup-price-row { gap: 10px; padding-inline: 9px; }
  .signup-price-value { display: grid; justify-items: end; gap: 0; }
}

.auth-sign-in .form > .btn-block {
  border-radius: 12px;
  padding: 0 16px;
  margin-top: 2px;
  font-family: inherit;
  font-size: 14px;
  line-height: 1.2;
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
}

.auth-sign-in .form > .btn-block:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(4, 120, 87, .14);
}

.auth-foot {
  margin-top: 20px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
  font-size: 13px;
  color: var(--ink-3);
  text-align: center;
}

/* The footer's action links (注册 / 找回密码) are the page's secondary
   conversions — plain muted links read as decoration, so give them weight,
   brand color, and an underline. */
.auth-act { display: block; }
.auth-act + .auth-act { margin-top: 10px; }
.auth-link {
  color: var(--green);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-color: var(--green-l);
  text-underline-offset: 3px;
}
.auth-link:hover { color: var(--green-d); text-decoration-color: var(--green-d); }

.auth-alt { font-size: 13px; color: var(--ink-3); }
.auth-alt a { color: var(--ink-2); }
.auth-alt a:hover { color: var(--ink); }
.auth-alt .sep { margin: 0 8px; }

/* ---------- error page ---------- */

.err { padding: 100px 0 120px; text-align: center; }

.err-code {
  font-size: 13px;
  font-weight: 500;
  color: var(--green);
  letter-spacing: .08em;
}

.err-h {
  margin-top: 12px;
  font-size: clamp(26px, 4vw, 38px);
  letter-spacing: -.02em;
}

.err-p {
  margin: 14px auto 28px;
  max-width: 440px;
  font-size: 15px;
  line-height: 1.8;
  color: var(--ink-2);
}

.err .cta-btns { justify-content: center; }

/* ---------- responsive ---------- */

@media (max-width: 899.98px) {
  :root { --pad: 14px; }

  .sec { padding: 60px 0; }
  .sec-head { margin-bottom: 32px; }
  /* Tighter than the desktop 48px: with the ad leading the hero, the default
     gap pushed the headline itself off a phone's first screen. */
  .hero { padding: 32px 0 60px; gap: 26px; }
  .hero-card { padding: 20px; }
  /* The signup credit is the primary phone CTA. Keep it before the campaign
     even if future template edits or translations make the row wrap. */
  .hero-gift { order: 0; }

  /* Full width rather than a pill: the announcement is longer than a 390px
     line, and a wrapped 999px capsule reads as a layout accident. As its own
     row it also cannot squeeze the gift off the first screen. */
  .hero-ad-compact {
    order: 1;
    width: 100%;
    border-radius: 14px;
    padding: 12px 14px;
    font-size: 13px;
    line-height: 1.45;
  }

  .hero-ad-compact .hero-gift-arw { margin-left: auto; padding-left: 8px; }
  .cta { padding: 28px 24px; }
  .only-lg { display: none; }
  .tbl th, .tbl td { padding: 12px 16px; }
  .tbl-head { padding: 16px; }
  .faq summary { padding: 16px 18px; }
  .faq p { padding: 0 18px 16px; }
  .steps li { padding: 18px; }

  /* The sidebar becomes a scrollable chip strip — a stacked seven-row nav
     would push the actual page content below the fold. */
  .side {
    margin: 0 calc(var(--pad) * -1);
    padding: 0 var(--pad);
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
    /* A chip cut flat by the viewport edge reads as a broken layout; fading it
       is what tells a touch user the strip keeps going. */
    -webkit-mask-image: var(--strip-fade);
    mask-image: var(--strip-fade);
  }

  .side::-webkit-scrollbar { display: none; }

  .side nav {
    grid-auto-flow: column;
    grid-auto-columns: max-content;
    gap: 7px;
  }

  .side-item {
    border: 1px solid var(--line-2);
    border-radius: 999px;
    padding: 9px 14px;
    font-size: 13px;
    white-space: nowrap;
  }

  .side-item.on { border-color: transparent; }
  .side-ico { display: none; }

  .page-head { margin-bottom: 22px; }
  .stat { padding: 16px 18px; }
  .stat-value { font-size: 26px; }
  .app { padding-top: 22px; gap: 24px; }

  /* The public nav has no hamburger menu; hiding it below the desktop
     breakpoint left phone users with no way to reach pricing/docs/contact.
     Wrap the header and lay the links out as a scrollable chip strip, the
     same pattern the console sidebar already uses on narrow screens. */
  .hdr-in { flex-wrap: wrap; row-gap: 10px; }

  .hdr .nav {
    display: flex;
    order: 3;
    width: calc(100% + (var(--pad) * 2));
    gap: 8px;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
    margin: 0 calc(var(--pad) * -1);
    padding: 0 var(--pad) 2px;
    -webkit-mask-image: var(--strip-fade);
    mask-image: var(--strip-fade);
  }

  .hdr .nav::-webkit-scrollbar { display: none; }

  .hdr .nav a {
    border: 1px solid var(--line-2);
    border-radius: 999px;
    padding: 8px 14px;
    font-size: 13px;
    white-space: nowrap;
  }

  /* 13x20 of glyph is not a touch target: pad the hit area out to the row
     height and pull the box back so the header keeps its measured height. */
  .lang-switch a {
    padding: 10px 3px;
    margin: -10px 0;
  }

  .code-name { font-size: 12px; }
  .copy { padding: 8px 14px; font-size: 12px; }

  /* `code { font-size: .92em }` compounds inside the block and lands the
     shell samples near 10px on a phone. */
  .code pre { font-size: 12.5px; }
  .code pre code { font-size: 1em; }
}

@media (max-width: 520px) {
  /* On a phone the rename note goes away — the domain under the logo already
     tells a returning user which site they're on, and the header needs the
     room. The secondary login action is available on the signup page, so the
     header can privilege the primary CTA without overflowing a 390px phone. */
  .hdr .brand { gap: 7px; }
  .hdr .brand-mark { width: 118px; height: 28px; }
  .hdr .brand-note { display: none; }
  /* Not zero: at gap 0 the language switch sits flush against the primary
     pill and the two read as one broken control. */
  .hdr-cta { gap: 9px; }
  .hdr-cta .btn { padding: 8px 14px; }
  /* Direct children only. The console's sign-out button is a ghost button
     nested in a form, and hiding it left phone users with no way out. */
  .hdr-cta > .btn-ghost { display: none; }
  .hdr-cta .console-pricing-btn { display: inline-flex; padding: 7px 10px; font-size: 12px; }
  /* Same exemption as the pricing pill: the blanket ghost-button hide above
     would take the QQ entry with it, leaving no group link on a phone. */
  .hdr-cta .hdr-qq { display: inline-flex; padding: 7px 9px; }
  /* space-between does nothing once the strip overflows, and spreads the chips
     unevenly when it doesn't — keep one rhythm at every width. */
  .hdr .nav { gap: 7px; }
  .hdr .nav a { padding: 8px 10px; font-size: 12.5px; }
  .console-pricing-btn .pricing-label-long { display: none; }
  .console-pricing-btn .pricing-label-short { display: inline; }
}

/* Below this the logo plus the CTA row no longer fit on one line, and the
   header spills onto a third row. The language separator is decorative, so
   it is the first thing to give up. */
@media (max-width: 360px) {
  .lang-switch { gap: 8px; }
  .lang-switch span[aria-hidden] { display: none; }
  .hdr-cta { gap: 8px; }
  .hdr-cta .btn { padding: 8px 12px; }
}

/* ---------- docs & pricing pages ---------- */

.page-hero {
  padding: 56px 0 8px;
  max-width: 760px;
}

.page-hero h1 {
  margin: 6px 0 14px;
  font-size: clamp(30px, 4vw, 44px);
  letter-spacing: -.02em;
}

.page-hero .lead {
  font-size: 16px;
  line-height: 1.85;
  color: var(--ink-2);
}

/* pricing catalogue: one card per model, one price row per group */

.pricing-hero .lead { max-width: 700px; }

.pf { display: grid; grid-template-columns: minmax(0, 1fr); gap: 22px; padding: 8px 0 4px; }
.pf-side { min-width: 0; }

.pf-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}

.pf-head h2 { font-size: 16px; letter-spacing: -.01em; }

.pf-reset {
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font: inherit;
  font-size: 12.5px;
  color: var(--ink-3);
}

.pf-reset:hover { color: var(--green-d); }
.pf-reset-m { display: none; }
.pf-facet + .pf-facet { margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--line); }

.pf-label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  padding: 0 8px;
  margin-bottom: 7px;
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-2);
}

.pf-label span { font-size: 10px; font-weight: 400; color: var(--ink-3); }
.pf-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }

.pf-opt {
  display: grid;
  grid-template-columns: 16px minmax(0, 1fr);
  align-items: center;
  column-gap: 8px;
  width: 100%;
  padding: 8px;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: none;
  cursor: pointer;
  font: inherit;
  font-size: 13px;
  text-align: left;
  color: var(--ink-2);
}

.pf-opt:hover:not(:disabled):not(.on) { background: var(--tint); color: var(--ink); }
.pf-opt.on { background: var(--tint-2); border-color: #c6f0da; color: var(--green-d); font-weight: 500; }
.pf-opt:disabled { color: var(--ink-3); opacity: .42; cursor: default; }

.pf-check {
  width: 15px;
  height: 15px;
  border: 1.5px solid var(--line-2);
  border-radius: 4px;
  background: #fff;
  position: relative;
}

.pf-facet[data-mode="single"] .pf-check { border-radius: 999px; }
.pf-opt.on .pf-check { border-color: var(--green); background: var(--green); }
.pf-opt.on .pf-check::after {
  content: "";
  position: absolute;
  left: 4px;
  top: 1px;
  width: 4px;
  height: 7px;
  border: solid #fff;
  border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}

.pf-facet[data-mode="single"] .pf-opt.on .pf-check::after {
  left: 3.5px;
  top: 3.5px;
  width: 5px;
  height: 5px;
  border: 0;
  border-radius: 999px;
  background: #fff;
  transform: none;
}

.pf-opt-main { min-width: 0; display: flex; align-items: center; gap: 6px; }
.pf-n { margin-left: auto; font-size: 11px; font-weight: 400; color: var(--ink-3); }
.pf-d { grid-column: 2; margin-top: 1px; font-size: 10.5px; font-weight: 500; color: var(--green-d); }
.pf-opt:disabled .pf-d { color: var(--ink-3); }

.pf-note {
  margin: 18px 8px 0;
  font-size: 11.5px;
  line-height: 1.75;
  color: var(--ink-3);
}

/* Tapping a 分组速查 row scrolls here, and the header is sticky, so the list
   needs the same offset anchored headings use or its toolbar lands behind it. */
.pf-main { min-width: 0; scroll-margin-top: 90px; }

.pf-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-bottom: 16px;
}

.pf-search {
  position: relative;
  display: flex;
  align-items: center;
  flex: 1 1 260px;
  max-width: 390px;
}

.pf-search svg { position: absolute; left: 12px; width: 16px; height: 16px; fill: var(--ink-3); pointer-events: none; }

.pf-q, .pf-sort, .pf-vendor {
  border: 1px solid var(--line);
  background: #fff;
  font: inherit;
  font-size: 13px;
  color: var(--ink);
}

.pf-q { width: 100%; padding: 10px 13px 10px 36px; border-radius: 999px; }
.pf-q:focus, .pf-sort:focus, .pf-vendor:focus { outline: none; border-color: var(--green-l); }
.pf-q::placeholder { color: var(--ink-3); }
.pf-q::-webkit-search-cancel-button { cursor: pointer; }

.pf-toggle {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 7px;
  min-height: 38px;
  padding: 0 11px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 12px;
  color: var(--ink-2);
  cursor: pointer;
  white-space: nowrap;
}

.pf-toggle input { position: absolute; opacity: 0; pointer-events: none; }
.pf-switch { flex: 0 0 auto; width: 28px; height: 16px; border-radius: 999px; background: var(--line-2); position: relative; transition: background .15s ease; }
.pf-switch::after { content: ""; position: absolute; width: 12px; height: 12px; left: 2px; top: 2px; border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.15); transition: transform .15s ease; }
.pf-toggle input:checked + .pf-switch { background: var(--green); }
.pf-toggle input:checked + .pf-switch::after { transform: translateX(12px); }
.pf-toggle:focus-within { border-color: var(--green-l); }

.pf-sort-wrap { display: inline-flex; flex: 0 0 auto; align-items: center; }
/* Phone-only vendor picker; the rail owns this choice on a wide screen. */
.pf-pick { display: none; flex: 0 0 auto; align-items: center; }
.pf-sort-wrap .sr-only, .pf-pick .sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.pf-sort, .pf-vendor { display: block; height: 38px; padding: 0 30px 0 12px; border-radius: 999px; cursor: pointer; }
.pf-count { margin-left: auto; font-size: 12px; color: var(--ink-3); white-space: nowrap; }
.pf-count strong { color: var(--ink); font-weight: 600; }
/* The campaign strip is shared with the home and signup pages, where it is a
   full-width banner with a big headline. In the filter bar it has to shrink to
   one control-height line: it takes the empty stretch between the sort control
   and the count (flex fills the gap), the count drops its own auto-margin so
   the two don't fight over the space, and everything stays on one line at a
   compact size. Below 800px the bar wraps and the strip spans its own row. */
.pricing-deal {
  flex: 1 1 240px;
  min-width: 0;
  margin: 0;
  gap: 8px;
  padding: 0 12px;
  min-height: 38px;
  border-radius: 999px;
  border-left-width: 4px;
}
.pricing-deal .deal-main { flex-wrap: nowrap; overflow: hidden; }
.pricing-deal .deal-title {
  font-size: 13px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pricing-deal .deal-note {
  font-size: 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pricing-deal .deal-flag { padding: 2px 7px; font-size: 10px; }
.pricing-deal .deal-off { margin-left: auto; padding: 3px 10px; font-size: 12px; }
.pricing-deal .deal-arw { font-size: 14px; }
.pricing-deal ~ .pf-count { margin-left: 0; }
@media (max-width: 800px) {
  .pricing-deal { flex-basis: 100%; order: 9; }
}

/* Cards in one grid row share a height, so a card with fewer groups used to
   leave a ragged hole under it. The card stretches to fill the row instead and
   the surplus collects inside it, above the footer: rows keep their natural
   height so the price lines stay aligned with the neighbouring card, which is
   what makes two cards comparable side by side. Reading order stays row-major
   (a masonry/column flow would reorder a ranked list into column-major). */
.price-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 430px), 1fr)); gap: 15px; align-items: stretch; }

.price-card {
  min-width: 0;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: #fff;
  overflow: hidden;
  transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}

.price-card:hover { border-color: #b7d9ce; box-shadow: 0 8px 24px rgba(6,95,70,.07); transform: translateY(-1px); }
.price-card[hidden] { display: none; }
.price-card.is-recommended { border-color: #c6e8dc; }

.pc-head { flex: 0 0 auto; min-height: 78px; padding: 16px 17px 13px; display: flex; align-items: flex-start; gap: 10px; }
.pc-title-wrap { min-width: 0; }
.pc-title { display: block; padding: 0; background: none; color: var(--ink); font-family: var(--mono); font-size: 14px; font-weight: 600; overflow-wrap: anywhere; }
.pc-tagline { margin-top: 5px; font-size: 11px; color: var(--ink-3); }

/* Rename note: louder than the tagline because it answers "where is the model
   I was looking for", not "what is this model good at". */
.pc-alias {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin-top: 6px;
  font-size: 11.5px;
  line-height: 1.5;
  color: #8a5a12;
}

.pc-alias-tag {
  flex: none;
  padding: 1px 7px;
  border-radius: 999px;
  background: #fdf0d8;
  border: 1px solid #f0c98a;
  color: #a4620d;
  font-size: 10px;
  font-weight: 700;
}
.pc-badges { margin-left: auto; display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 5px; }
.badge-vendor { background: var(--tint); color: var(--ink-2); }

.pc-prices { flex: 1 1 auto; display: flex; flex-direction: column; border-top: 1px solid var(--line); }
.price-vhead, .price-variant { display: grid; grid-template-columns: minmax(112px, 1.25fr) .62fr 1fr .62fr .82fr; column-gap: 8px; align-items: center; }
.price-vhead { flex: 0 0 auto; padding: 7px 16px; background: var(--tint); font-size: 9.5px; color: var(--ink-3); }
.price-variant { flex: 0 0 auto; min-height: 52px; padding: 10px 16px; border-top: 1px solid var(--line); font-size: 11.5px; }
.price-vhead + .price-variant { border-top: 0; }
.price-variant.is-best { background: #f5fbf8; }

.pv-group { min-width: 0; display: flex; align-items: center; flex-wrap: wrap; gap: 5px; }
.pv-group strong { min-width: 0; font-size: 11px; font-weight: 600; overflow-wrap: anywhere; }
.pv-dot { flex: none; width: 6px; height: 6px; border-radius: 50%; background: var(--green); }
.pv-val { min-width: 0; font-variant-numeric: tabular-nums; }
.pv-val strong { font-weight: 600; color: var(--ink); }
.pv-val small, .pv-cache small { display: block; margin-top: 2px; font-size: 9px; color: #b25a1b; }
.pv-cache span { color: var(--green-d); }
.pv-cache small { color: var(--ink-3); }
.pv-discount { color: var(--green-d); font-size: 10.5px; font-weight: 600; }
.pv-discount.muted { color: var(--ink-3); font-weight: 400; }
.pv-image { grid-column: 2 / 5; min-width: 0; color: var(--ink-2); font-size: 10.5px; }
.pv-image strong { color: var(--ink); font-weight: 600; }
.pv-unpriced { grid-column: 2 / 5; color: var(--ink-3); }

.pc-foot { flex: 0 0 auto; min-height: 34px; padding: 9px 16px; border-top: 1px solid var(--line); display: flex; justify-content: space-between; gap: 12px; font-size: 9.5px; color: var(--ink-3); }
.pc-foot strong { color: var(--green-d); font-weight: 500; text-align: right; }

.pf-empty { margin-top: 10px; padding: 42px 20px; border: 1px dashed var(--line-2); border-radius: var(--r); text-align: center; font-size: 13px; color: var(--ink-3); }

/* group quick reference: eleven rows that price every group, above the cards */

.pg { margin: 4px 0 26px; }
.pg-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 14px; margin-bottom: 12px; }
.pg-head h2 { font-size: 17px; letter-spacing: -.01em; }
.pg-head p { font-size: 12.5px; color: var(--ink-3); }

.pg-tbl { border: 1px solid var(--line); border-radius: var(--r); background: #fff; overflow: hidden; }

.pg-row {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) 54px minmax(0, .95fr) minmax(0, .95fr) minmax(0, .95fr);
  column-gap: 10px;
  align-items: center;
  width: 100%;
}

.pg-hd { padding: 8px 16px; background: var(--tint); font-size: 9.5px; color: var(--ink-3); }

button.pg-row {
  padding: 11px 16px;
  border: 0;
  border-top: 1px solid var(--line);
  background: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background .15s ease, box-shadow .15s ease;
}

button.pg-row:hover { background: var(--tint); }
/* The inset bar, not a border, so turning a row on never shifts its content. */
button.pg-row.is-on { background: var(--tint-2); box-shadow: inset 3px 0 0 var(--green); }

.pg-name { min-width: 0; }
.pg-name strong { display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px; font-size: 13px; font-weight: 600; color: var(--ink); overflow-wrap: anywhere; }
.pg-name small { display: block; margin-top: 3px; font-size: 10.5px; color: var(--ink-3); }
/* The raw group id: what the visitor's key is actually tagged with, so the
   readable label above it can stay a label. */
.pg-name code { padding: 1px 6px; border-radius: 5px; background: var(--tint); font-family: var(--mono); font-size: 10.5px; font-weight: 500; color: var(--ink-2); }
.pg-name .badge { margin: 0; font-size: 10px; font-weight: 500; }

/* Desktop: unwrap so 模型/输入/输出 still occupy the table columns. */
.pg-facts { display: contents; }
.pg-n { font-size: 12px; color: var(--ink-2); font-variant-numeric: tabular-nums; }
.pg-price, .pg-image { font-size: 12.5px; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
.pg-image { grid-column: 3 / 5; font-weight: 500; }
.pg-disc { font-size: 11.5px; font-weight: 600; color: var(--green-d); }
.pg-price.muted, .pg-disc.muted { font-weight: 400; color: var(--ink-3); }

.pg-note { margin-top: 9px; font-size: 11.5px; line-height: 1.7; color: var(--ink-3); }

/* Pricing visual hierarchy: this surface carries a commercial decision, so it
   intentionally uses stronger contrast than the editorial pages around it. */
.pricing-hero {
  position: relative;
  margin: 4px 0 26px;
  padding: 28px 32px 30px;
  border: 1px solid #c9ded6;
  border-radius: var(--r-lg);
  background:
    radial-gradient(circle at 92% 12%, rgba(52, 211, 153, .18), transparent 30%),
    linear-gradient(135deg, #f2fbf7 0%, #ffffff 62%);
  box-shadow: 0 12px 34px rgba(6, 95, 70, .08);
  overflow: hidden;
}

.pricing-hero::after { content: ""; position: absolute; inset: auto 0 0; height: 4px; background: linear-gradient(90deg, var(--green-d), var(--green-l), transparent 78%); }
.pricing-hero h1 { margin-bottom: 10px; font-size: clamp(34px, 4vw, 48px); font-weight: 760; letter-spacing: -.035em; }
.pricing-hero .eyebrow { color: var(--green-d); font-weight: 700; }
.pricing-hero .lead { color: #46534f; font-weight: 450; }

.pf-head h2 { color: #15251f; font-size: 17px; font-weight: 720; }
.pf-reset { color: #64736e; font-weight: 550; }
.pf-facet + .pf-facet { border-top-color: #d7e0dd; }
.pf-label { color: #34443f; font-weight: 700; }
.pf-label span { color: #71807b; font-weight: 500; }
.pf-opt { color: #485650; font-weight: 520; }
.pf-opt:hover:not(:disabled):not(.on) { background: #eef5f2; border-color: #d6e6df; }
.pf-opt.on { color: #fff; border-color: var(--green-d); background: linear-gradient(135deg, var(--green-d), var(--green)); box-shadow: 0 4px 12px rgba(6, 95, 70, .18); }
.pf-opt.on .pf-n { color: rgba(255, 255, 255, .82); }
.pf-opt.on .pf-d { color: #fff; }
.pf-opt.on .pf-check { border-color: rgba(255, 255, 255, .9); background: #fff; }
.pf-opt.on .pf-check::after { border-color: var(--green-d); }
.pf-facet[data-mode="single"] .pf-opt.on .pf-check::after { background: var(--green-d); }
.pf-d { color: #067153; font-weight: 650; }
.pf-note { color: #697872; }

.pf-q, .pf-sort, .pf-vendor, .pf-toggle { border-color: #d2dcd8; box-shadow: 0 2px 8px rgba(15, 23, 42, .035); }
.pf-q:hover, .pf-sort:hover, .pf-vendor:hover, .pf-toggle:hover { border-color: #9fbcb1; }
.pf-q:focus, .pf-sort:focus, .pf-vendor:focus, .pf-toggle:focus-within { border-color: var(--green); box-shadow: 0 0 0 3px rgba(4, 120, 87, .11); }
.pf-count { color: #6b7874; }
.pf-count strong { color: #17231f; font-size: 13px; font-weight: 750; }

.price-cards { gap: 18px; }
.price-card { border-color: #d3ded9; box-shadow: 0 5px 18px rgba(15, 23, 42, .065); }
.price-card:hover { border-color: #75ad9a; box-shadow: 0 12px 30px rgba(6, 95, 70, .13); transform: translateY(-2px); }
.price-card.is-recommended { border-color: #8fc8b5; box-shadow: 0 7px 22px rgba(6, 95, 70, .10); }
.price-card.is-recommended .pc-head { box-shadow: inset 0 3px 0 var(--green); }
.pc-head { min-height: 84px; padding: 18px 18px 14px; background: linear-gradient(135deg, #ffffff 35%, #f0f8f5 100%); }
.pc-title { color: #101b17; font-size: 15px; font-weight: 720; letter-spacing: -.015em; }
.pc-tagline { color: #5d6b66; font-size: 11.5px; font-weight: 500; }
.price-card .badge-vendor { border: 1px solid #cfd8d5; background: #eef2f1; color: #374640; font-weight: 650; }
.price-card .badge-grp { background: #e5eeff; color: #2455b5; font-weight: 650; }
.price-card .badge-on { background: #dff7ea; color: #036745; font-weight: 700; }

.pc-prices { border-top-color: #cbd7d2; }
.price-vhead { padding-top: 8px; padding-bottom: 8px; background: #173f34; color: rgba(255, 255, 255, .74); font-weight: 650; letter-spacing: .02em; }
.price-variant { min-height: 56px; border-top-color: #dce4e1; color: #26332e; font-size: 12px; }
.price-variant:nth-child(odd):not(.is-best) { background: #fbfcfc; }
.price-variant.is-best { background: #e8f7f0; box-shadow: inset 3px 0 0 var(--green); }
.pv-group strong { color: #16231e; font-size: 11.5px; font-weight: 700; }
.pv-dot { width: 7px; height: 7px; box-shadow: 0 0 0 3px rgba(4, 120, 87, .12); }
.pv-val { color: #36443f; }
.pv-val strong { color: #101915; font-size: 12.5px; font-weight: 750; }
.pv-cache span { color: #046c4e; font-weight: 650; }
.pv-cache small { color: #69766f; }
.pv-discount { color: #056c4e; font-size: 11px; font-weight: 750; }
.pc-foot { min-height: 37px; background: #f7f9f8; color: #697670; }
.pc-foot strong { color: #075f46; font-weight: 650; }

/* Group filtering hides the rows for groups the user filtered out, plus the
   two labels that only make sense while several groups are on screen.
   `hidden` alone loses to the grid/flex displays above, and the surviving top
   row must not keep a hairline that now sits directly under the column
   header. */
.price-variant[hidden] { display: none; }
.price-variant.is-first { border-top: 0; }
.price-card .badge[hidden],
.pc-foot strong[hidden] { display: none; }

@media (min-width: 900px) {
  .pf-side { border-color: #cfdad6; }
}

@media (max-width: 640px) {
  .pricing-hero { padding: 22px 20px 24px; border-radius: var(--r); }
  .price-variant.is-best { box-shadow: inset 3px 0 0 var(--green); }
}

.note-box {
  margin-top: 20px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--tint);
  padding: 22px 24px;
}

.note-box h3 { font-size: 15px; margin-bottom: 6px; }

.note-box p, .callout p {
  font-size: 14px;
  line-height: 1.85;
  color: var(--ink-2);
}

.note-box a, .callout a, .doc-body p a, .sec-foot a, .sec-note a {
  color: var(--green-d);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.tbl-price .cache-hit { color: var(--green-d); }

/* The header is sticky, so anchored sections need to clear it. */
.sec[id] { scroll-margin-top: 90px; }

/* Docs and pricing inside the console shell: the page already has a sidebar,
   so drop the second one and shrink the marketing-sized hero. */
.app-doc .page-hero { padding: 4px 0 0; }
.app-doc .page-hero h1 { font-size: 28px; margin: 4px 0 10px; }
.app-doc .page-hero .lead { font-size: 14.5px; }
.app-doc .doc { padding-top: 24px; }

/* docs page: sticky table of contents beside the article */

.doc { display: grid; gap: 32px; padding: 32px 0 72px; }

.doc-toc { display: none; }

.doc-toc-title {
  font-size: 12px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 10px;
}

.doc-toc a {
  display: block;
  padding: 6px 0 6px 12px;
  border-left: 2px solid var(--line-2);
  font-size: 13.5px;
  color: var(--ink-2);
  transition: color .12s ease, border-color .12s ease;
}

.doc-toc a:hover { color: var(--green-d); border-left-color: var(--green-l); }

.doc-toc-out {
  margin-top: 8px;
  border-left-color: transparent !important;
  color: var(--green-d) !important;
}

/* Editorial copy keeps a readable measure even though the site shell itself
   is full width. The table of contents remains anchored to the left. */
.doc-body { width: 100%; max-width: 900px; min-width: 0; display: grid; gap: 52px; }

/* Grid items default to min-width:auto, so a long URL or path in a <code>
   would widen the whole column instead of wrapping inside it. */
.doc-body section, .doc-body section > * { min-width: 0; }

.doc-body section { display: grid; gap: 14px; scroll-margin-top: 90px; }

/* Sits after the two 90px rules on purpose — same later-wins tiebreak as the
   640px docs block below. On a phone the header wraps to two rows (~122px), so
   the desktop offset would drop an anchored heading behind it. */
@media (max-width: 899.98px) {
  .sec[id], .doc-body section, .pf-main { scroll-margin-top: 130px; }
}

.doc-body h2 {
  font-size: 24px;
  letter-spacing: -.01em;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line);
}

.doc-body h3 { font-size: 16px; margin-top: 6px; }

.doc-body > section > p, .qa-body p {
  font-size: 14.5px;
  line-height: 1.9;
  color: var(--ink-2);
}

.doc-body code, .note-box code, .callout code, .deflist-doc code {
  font-family: var(--mono);
  font-size: .88em;
  background: var(--tint-2);
  border-radius: 5px;
  padding: 1px 5px;
  color: var(--green-d);
  /* model IDs and URLs have no break opportunities of their own */
  overflow-wrap: anywhere;
}

.doc-body .code code { background: none; padding: 0; color: inherit; }

.callout {
  border: 1px solid var(--line);
  border-left: 3px solid var(--green);
  border-radius: var(--r-sm);
  background: var(--tint);
  padding: 18px 22px;
  display: grid;
  gap: 6px;
}

.callout h3 { font-size: 15px; margin: 0; }
.callout-warn { border-left-color: #d97706; background: #fffbeb; }

.deflist-doc {
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  overflow: hidden;
}

/* The console's deflist is a right-aligned label/value row; in prose the
   value is a sentence, so it starts right after the term and wraps normally. */
.deflist-doc > div { justify-content: flex-start; padding: 12px 18px; }
.deflist-doc dt { flex: none; min-width: 104px; font-size: 13.5px; font-weight: 500; color: var(--ink); }

.deflist-doc dd {
  margin: 0;
  min-width: 0;
  font-size: 13.5px;
  line-height: 1.8;
  text-align: left;
  color: var(--ink-2);
}
.deflist-doc a { color: var(--green-d); text-decoration: underline; text-underline-offset: 2px; }

/* .steps is a flex row built around the .step-n badge; the docs variant
   numbers itself with a counter so the copy can stay plain markup. */
.steps-doc { counter-reset: dstep; gap: 14px; }

.steps-doc li {
  display: block;
  position: relative;
  padding: 20px 24px 22px 62px;
  counter-increment: dstep;
}

.steps-doc li::before {
  content: counter(dstep);
  position: absolute;
  left: 22px;
  top: 20px;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 999px;
  background: var(--tint-2);
  color: var(--green-d);
  font-size: 13px;
  font-weight: 600;
}

.steps-doc li > * + * { margin-top: 10px; }
.steps-doc p { margin-top: 8px; }
.steps-doc .muted { color: var(--ink-3); font-size: 13.5px; }

.qa-body { padding: 0 24px 20px; display: grid; gap: 12px; }
.qa-body p { padding: 0; }
.qa-body .code { margin: 0; }

/* Sits after the docs rules on purpose: an override in the shared
   max-width:900px block higher up would lose the later-wins tiebreak. */
@media (max-width: 640px) {
  .qa-body { padding: 0 18px 16px; }
  .steps-doc li { padding: 18px 18px 20px 52px; }
  .steps-doc li::before { left: 16px; top: 18px; }

  /* term over definition — 104px of label leaves too little for the value */
  .deflist-doc > div { display: block; padding: 12px 16px; }
  .deflist-doc dt { min-width: 0; margin-bottom: 2px; }
}

@media (max-width: 899.98px) {
  /* The rail used to become two horizontally-scrollable chip strips here. It
     was the worst control on the page: the group chips carry a 折 line that a
     nowrap pill clipped mid-word, and picking a group meant scrolling a strip
     whose options were named but not priced. The 分组速查 table above does the
     same job with the prices in view, so the rail steps aside on a phone —
     its buttons stay in the DOM as the filter state everything else drives.
     Vendor moves to a native picker in the toolbar; 只看多分组 is a
     comparison aid for the two-column desktop grid and goes with the rail. */
  /* A touch more than the 130px anchored headings use: those want the heading
     flush under the header, this wants the toolbar clear of it. */
  .pf-main { scroll-margin-top: 150px; }
  .pf-side, .pf-toggle { display: none; }
  .pf-pick { display: inline-flex; }
  .pf-reset-m:not([hidden]) { display: inline-block; order: 4; padding: 0 12px; height: 30px; border: 1px solid var(--line-2); border-radius: 999px; background: #fff; font-size: 12px; }
  .pf-bar { position: static; }
}

/* Five columns of figures do not fit a phone, so a group's row becomes two
   lines — name, then its numbers as labelled facts — and the overview still
   costs about a screen instead of the catalogue's twenty. */
@media (max-width: 760px) {
  .pg-hd { display: none; }

  button.pg-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) max-content;
    column-gap: 10px;
    row-gap: 6px;
    align-items: start;
    padding: 12px 14px;
  }

  .pg-name { grid-column: 1; grid-row: 1; min-width: 0; }
  .pg-disc { grid-column: 2; grid-row: 1; font-size: 12px; }
  .pg-facts {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 12px;
    grid-column: 1 / -1;
    grid-row: 2;
  }
  .pg-n, .pg-price, .pg-image { font-size: 12px; }
  .pg-image { grid-column: auto; }
  .pg-n::before, .pg-price::before, .pg-image::before { content: attr(data-label) " "; font-weight: 400; color: var(--ink-3); }
  /* The 折 figure is the commercial punchline on a phone; a red chip on the
     name row is the only thing that survives a glance down eleven groups. */
  .pg-disc:not(.muted) {
    padding: 3px 9px;
    border: 1px solid #fecaca;
    border-radius: 999px;
    background: #fef2f2;
    color: #b91c1c;
    font-size: 12.5px;
    font-weight: 750;
    line-height: 1.25;
    text-align: center;
    white-space: nowrap;
  }
}

@media (max-width: 640px) {
  .pricing-hero h1 { font-size: 30px; }
  .pricing-hero .lead { font-size: 14px; line-height: 1.7; }
  .pf-search { flex-basis: 100%; max-width: none; }
  .pf-pick { order: 2; }
  .pf-sort-wrap { order: 3; }
  .pf-count { order: 5; margin-left: auto; }

  .pc-head { display: block; min-height: 0; }
  .pc-badges { margin: 10px 0 0; justify-content: flex-start; }
  .price-vhead { display: none; }
  .price-variant { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 14px; padding: 13px 15px; }
  .pv-group { grid-column: 1 / -1; padding-bottom: 3px; }
  .pv-val::before, .pv-image::before, .pv-unpriced::before { content: attr(data-label); display: block; margin-bottom: 3px; font-size: 10.5px; font-weight: 400; color: var(--ink-3); }
  .pv-image, .pv-unpriced { grid-column: 1 / -1; }
  .pv-discount { text-align: left; }
  /* The desktop table can afford 9px asides; a phone cannot. */
  .pv-val small, .pv-cache small { font-size: 11px; }
  .pv-image { font-size: 11.5px; }
  .pc-foot { display: block; line-height: 1.6; font-size: 11px; }
  .pc-foot strong { display: block; margin-top: 2px; text-align: left; }
}

@media (min-width: 900px) {
  /* Wide enough for the rail and a two-column card grid, which already put a
     group's models and prices side by side — the overview would only repeat
     them. It exists for the phone, where that comparison costs a long scroll. */
  .pg { display: none; }

  .pf { grid-template-columns: 210px minmax(0, 1fr); gap: 26px; align-items: start; }

  /* The rail is fixed in place: no scroll port of its own, so a wheel over it
     never scrolls the facet list, and the cards keep the normal page scroll
     under a rail that stays put. */
  .pf-side {
    position: sticky;
    top: 92px;
    padding: 17px 13px;
    border: 1px solid #cfdad6;
    border-radius: var(--r);
    background: #fff;
    box-shadow: 0 8px 24px rgba(15, 23, 42, .055);
  }
}

/* Pinning a rail taller than the window would clip its last groups with no way
   to reach them, so on a short window it rides the page scroll instead. */
@media (min-width: 900px) and (max-height: 1160px) {
  .pf-side { position: static; }
}

@media (min-width: 900px) {
  .nav { display: flex; }

  .doc { grid-template-columns: 200px minmax(0, 1fr); gap: 56px; align-items: start; }

  .doc-toc {
    display: block;
    position: sticky;
    top: 92px;
  }

  /* One sidebar per screen — the console nav already fills that role — so the
     contents list lies down above the article instead of stacking beside it. */
  .app-doc .doc { grid-template-columns: minmax(0, 1fr); gap: 28px; }

  .app-doc .doc-toc {
    position: static;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    padding-bottom: 20px;
    border-bottom: 1px solid var(--line);
  }

  .app-doc .doc-toc-title { margin: 0 6px 0 0; }

  .app-doc .doc-toc a {
    padding: 5px 12px;
    border-left: 0;
    border: 1px solid var(--line);
    border-radius: 999px;
    font-size: 13px;
  }

  .app-doc .doc-toc a:hover { border-color: var(--green-l); }
  .app-doc .doc-toc-out { margin-top: 0; border-color: #bfe3d1 !important; }

  /* full-width prose runs ~120 characters a line inside the console */
  .app-doc .doc-body { max-width: 900px; }

  /* six feature cards read better as 3+3 than as the 4+2 auto-fit produces */
  .grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

  .app { grid-template-columns: 208px minmax(0, 1fr); gap: 44px; }
  .side { position: sticky; top: 84px; }
  .cols { grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); }

  .sec-head {
    grid-template-columns: minmax(0, 1.2fr) minmax(0, .8fr);
    align-items: end;
    gap: 48px;
  }

  .sec-note { justify-self: end; }

  .cta {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 40px;
  }

  .cta-act { align-items: flex-end; }
}

@media (min-width: 1024px) {
  .hero-split {
    grid-template-columns: 1.1fr .9fr;
    gap: 64px;
    align-items: start;
  }

  .hero-ad-compact { display: none; }

  .hero-ad {
    display: block;
    min-width: 0;
    margin-top: 8px;
  }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { transition: none !important; }
}

/* ---------- usage log rows on a phone ---------- */

/* Ten columns fit once the shell is at its full 1280px width; on a narrower
   window there is no room for them at any sane font size, so the same facts
   fold into a card instead of scrolling sideways. Last in the file because the
   shared `.tbl td` padding is itself set in a media query. */
@media (max-width: 1279px) {
  .tbl-logs, .tbl-logs tbody { display: block; }
  .tbl-logs thead { display: none; }

  .tbl-logs .log-row {
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-areas:
      "ts    cost"
      "model type"
      "lat   lat"
      "in    out"
      "cache cache"
      "key   group"
      "detail detail";
    align-items: center;
    gap: 7px 12px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--line);
  }

  .tbl-logs .log-row > td {
    display: block;
    padding: 0;
    border: 0;
    overflow: visible;
    white-space: normal;
  }

  .tbl-logs tbody tr:last-child { border-bottom: 0; }

  .tbl-logs .log-ts { grid-area: ts; font-size: 12.5px; }
  .tbl-logs .log-cost { grid-area: cost; text-align: right; font-size: 14px; }
  .tbl-logs .log-model { grid-area: model; }
  .tbl-logs .log-type { grid-area: type; text-align: right; }
  .tbl-logs .log-lat { grid-area: lat; }
  .tbl-logs .log-key { grid-area: key; }
  .tbl-logs .log-group { grid-area: group; text-align: right; }
  .tbl-logs .log-detail-action { grid-area: detail; text-align: right; }

  /* Token counts keep their headings here, since the column labels are gone. */
  .tbl-logs .log-in,
  .tbl-logs .log-out,
  .tbl-logs .log-cache { text-align: right; font-size: 12.5px; }
  .tbl-logs .log-in { grid-area: in; }
  .tbl-logs .log-out { grid-area: out; }
  .tbl-logs .log-cache { grid-area: cache; }
  .tbl-logs .log-in::before { content: "输入 "; color: var(--ink-3); }
  .tbl-logs .log-out::before { content: "输出 "; color: var(--ink-3); }
  .tbl-logs .log-cache::before { content: "缓存 "; color: var(--ink-3); }

  .tbl-logs .log-expand:not([hidden]) { display: block; }
  .tbl-logs .log-expand > td { display: block; }
  .tbl-logs .calc-tbl, .tbl-logs .calc-tbl tbody { display: block; }
  .tbl-logs .calc-tbl tr {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 2px 12px;
    padding: 8px 0;
    border-bottom: 1px solid #edf0f2;
  }
  .tbl-logs .calc-tbl td { display: block; padding: 0; border: 0; white-space: normal; }
  .tbl-logs .calc-tbl td:nth-child(2) { color: #727a82; font-size: 12px; }
  .tbl-logs .calc-tbl td:nth-child(3) { grid-row: 1; grid-column: 2; }
  .tbl-logs .calc-tbl td:nth-child(4) { grid-row: 2; grid-column: 2; }
  .tbl-logs .calc-total td:nth-child(2) { grid-row: 1; grid-column: 2; }
}

/* ---------- console overview dashboard ---------- */

.app[data-page="console-home"] { padding-bottom: 24px; }
.console-home { min-width: 0; }

.dash-topline {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 22px;
  margin-bottom: 18px;
}

.dash-greeting {
  font-size: 25px;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -.02em;
  white-space: nowrap;
}

.dash-ranges {
  display: inline-flex;
  justify-self: start;
  padding: 4px;
  border-radius: 8px;
  background: #f3f4f6;
}

.dash-range {
  min-width: 72px;
  border-radius: 5px;
  padding: 7px 14px;
  color: var(--ink-2);
  font-size: 13px;
  text-align: center;
}

.dash-range:hover { color: var(--ink); }
.dash-range.on { background: #fff; color: #2563eb; box-shadow: 0 1px 4px rgba(15, 23, 42, .08); }

.dash-refresh {
  display: grid;
  width: 36px;
  height: 36px;
  place-items: center;
  border-radius: 50%;
  background: #f1f3f4;
  color: #4b5563;
  font-size: 22px;
  line-height: 1;
}

.dash-refresh:hover { background: #e8ebed; color: var(--ink); }

.dash-cards {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px;
}

.dash-card,
.dash-analysis,
.dash-api {
  border: 1px solid #e5e7eb;
  border-radius: 15px;
  background: #fff;
  overflow: hidden;
}

.dash-card > header,
.dash-panel-head {
  display: flex;
  align-items: center;
  min-height: 47px;
  border-bottom: 1px solid #eceef0;
  padding: 0 15px;
}

.dash-card > header {
  gap: 9px;
  font-size: 14px;
  font-weight: 500;
}

.dash-head-ico { width: 18px; color: #24292f; font-size: 16px; text-align: center; }
.dash-card-body { padding: 3px 14px 9px; }

.dash-stat-row {
  position: relative;
  display: flex;
  min-height: 67px;
  align-items: center;
  gap: 13px;
}

.dash-stat-row + .dash-stat-row { border-top: 1px solid #f0f1f2; }

.dash-round {
  display: grid;
  width: 38px;
  height: 38px;
  flex: 0 0 auto;
  place-items: center;
  border-radius: 50%;
  color: #fff;
  font-size: 13px;
  font-weight: 700;
}

.dash-blue { background: #63b3ff; }
.dash-purple { background: #c56ed4; }
.dash-green { background: #71d280; }
.dash-cyan { background: #4bc3cf; }
.dash-yellow { background: #ffdd57; }
.dash-pink { background: #ef719b; }
.dash-indigo { background: #7d8bd7; }
.dash-orange { background: #ffbd5c; }

.dash-stat-copy { min-width: 0; }
.dash-stat-copy small { display: block; color: #72777d; font-size: 12px; }
.dash-stat-copy strong {
  display: block;
  overflow: hidden;
  margin-top: 2px;
  color: #4b4f54;
  font-size: 20px;
  line-height: 1.15;
  text-overflow: ellipsis;
  font-variant-numeric: tabular-nums;
}

.dash-mini-btn {
  margin-left: auto;
  border: 1px solid #d9dcdf;
  border-radius: 999px;
  padding: 4px 10px;
  color: #4b4f54;
  font-size: 12px;
}

.dash-mini-btn:hover { background: #f6f7f8; }

.dash-spark {
  width: 72px;
  height: 36px;
  margin-left: auto;
  overflow: visible;
  fill: none;
  stroke-width: 2;
}

.dash-spark-cyan { stroke: #08a5b5; }
.dash-spark-yellow { stroke: #f59e0b; }
.dash-spark-pink { stroke: #ec4899; }
.dash-spark-indigo { stroke: #6366f1; }
.dash-spark-orange { stroke: #f97316; }

.dash-lower {
  display: grid;
  grid-template-columns: minmax(0, 3.05fr) minmax(280px, .95fr);
  gap: 16px;
  margin-top: 16px;
}

.dash-panel-head { justify-content: space-between; gap: 18px; }
.dash-panel-head h2 { font-size: 15px; font-weight: 500; white-space: nowrap; }
.dash-panel-head h2 span { display: inline-block; width: 19px; text-align: center; }

.dash-view-tabs { display: flex; align-items: center; gap: 0; }
.dash-view-tabs a { color: #8b9095; font-size: 13px; white-space: nowrap; }
.dash-view-tabs a:hover,
.dash-view-tabs a.on { color: #1f2328; font-weight: 600; }
.dash-view-tabs a + a::before { content: "/"; margin: 0 14px; color: #aeb2b6; font-weight: 400; }

.dash-analysis-body { min-height: 390px; padding: 48px 40px 22px; }
.dash-analysis-body h3 { font-size: 16px; }
.dash-analysis-body > p { margin-top: 5px; color: #7a7f84; font-size: 13px; }

.dash-chart-wrap { position: relative; height: 250px; margin-top: 16px; padding: 0 0 26px 32px; }
.dash-grid-lines {
  position: absolute;
  inset: 0 0 26px 32px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  pointer-events: none;
}
.dash-grid-lines i { display: block; border-top: 1px solid #e8eaec; }

.dash-chart-bars {
  position: relative;
  z-index: 1;
  display: flex;
  height: 100%;
  align-items: flex-end;
  gap: 3px;
  border-bottom: 1px solid #60656a;
}

.dash-chart-col {
  display: flex;
  height: 100%;
  min-width: 0;
  flex: 1 1 0;
  flex-direction: column;
  justify-content: flex-end;
  align-items: stretch;
}

.dash-chart-stack {
  display: flex;
  height: calc(100% - 24px);
  flex-direction: column-reverse;
  justify-content: flex-start;
}
.dash-chart-stack > span { display: block; min-height: 0; }
.dash-chart-col > small {
  height: 22px;
  margin-top: 4px;
  color: #656b70;
  font-size: 8px;
  line-height: 1.15;
  text-align: center;
  white-space: normal;
}

.dash-legend { display: flex; flex-wrap: wrap; gap: 10px 18px; margin-top: 12px; color: #62686d; font-size: 11px; }
.dash-legend > span { max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dash-legend-dot { display: inline-block; width: 10px; height: 10px; margin-right: 6px; vertical-align: -1px; }

.c1 { background: #f4b400; }
.c2 { background: #a67fe0; }
.c3 { background: #0f9d84; }
.c4 { background: #a9e5c0; }
.c5 { background: #df72ca; }
.c6 { background: #2467eb; }
.c7 { background: #36b4c5; }
.c8 { background: #7040d8; }

.dash-chart-empty { display: grid; min-height: 235px; place-items: center; color: var(--ink-3); font-size: 13px; }

.dash-rank-list { display: grid; gap: 17px; margin-top: 25px; }
.dash-rank-row > div:first-child { display: flex; align-items: center; gap: 6px; }
.dash-rank-row code { overflow: hidden; flex: 1; text-overflow: ellipsis; white-space: nowrap; }
.dash-rank-row strong { font-size: 12px; font-variant-numeric: tabular-nums; }
.dash-rank-track { height: 8px; margin-top: 7px; border-radius: 99px; background: #f0f1f2; overflow: hidden; }
.dash-rank-track span { display: block; height: 100%; border-radius: inherit; }

.dash-api-body { padding: 0 13px; }
.dash-endpoint { position: relative; padding: 18px 46px 17px 47px; }
.dash-endpoint + .dash-endpoint { border-top: 1px solid #e8eaec; }
.dash-endpoint > div { display: flex; align-items: center; gap: 8px; }
.dash-endpoint-badge {
  position: absolute;
  left: 2px;
  top: 19px;
  display: grid;
  width: 34px;
  height: 34px;
  place-items: center;
  border-radius: 50%;
  background: #5ab6ff;
  color: #fff;
  font-size: 11px;
  font-weight: 700;
}
.dash-endpoint strong { font-size: 14px; }
.dash-endpoint code {
  display: block;
  overflow: hidden;
  margin-top: 7px;
  color: #165dff;
  font-family: Georgia, serif;
  font-size: 13px;
  font-style: italic;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dash-endpoint p { margin-top: 5px; color: #5f6368; font-size: 12px; }
.dash-endpoint > .key-btn { position: absolute; right: 0; top: 18px; padding: 3px 9px; }

.dash-notices .dash-panel-head small { color: #8b9095; font-size: 11px; font-weight: 400; }
.dash-notice-list {
  max-height: 390px;
  overflow-y: auto;
  padding: 0 15px;
}
.dash-notice-item {
  display: block;
  width: 100%;
  border: 0;
  padding: 17px 2px 16px;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.dash-notice-item + .dash-notice-item { border-top: 1px solid #eceef0; }
.dash-notice-item:hover h3,
.dash-notice-item:hover .dash-notice-more { color: #2563eb; }
.dash-notice-item:focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; }
.dash-notice-item[data-notice-unread="1"] h3 { font-weight: 700; }
.dash-notice-item[data-notice-unread="1"] h3::before {
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-right: 7px;
  border-radius: 50%;
  background: #2563eb;
  content: "";
  vertical-align: 1px;
}
.dash-notice-more { display: inline-block; margin-top: 9px; color: #9aa0a6; font-size: 11px; }
.dash-notice-more::after { content: " ›"; }
.dash-notice-item time { color: #9aa0a6; font-size: 10px; }
.dash-notice-item h3 {
  overflow: hidden;
  margin-top: 5px;
  color: #31353a;
  font-size: 13px;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dash-notice-item p {
  display: -webkit-box;
  overflow: hidden;
  margin-top: 7px;
  color: #6b7075;
  font-size: 12px;
  line-height: 1.65;
  white-space: pre-wrap;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
}
.dash-notice-empty { display: grid; min-height: 250px; place-items: center; align-content: center; gap: 8px; color: #a2a7ac; }
.dash-notice-empty span { font-size: 26px; }
.dash-notice-empty p { font-size: 12px; }

.announcement-modal {
  width: min(540px, calc(100vw - 32px));
  border: 0;
  border-radius: 16px;
  padding: 0;
  background: #fff;
  color: var(--ink);
  box-shadow: 0 28px 80px rgba(15, 23, 42, .25);
}
.announcement-modal::backdrop { background: rgba(15, 23, 42, .48); backdrop-filter: blur(2px); }
.announcement-modal-head {
  position: relative;
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr) 32px;
  align-items: center;
  gap: 12px;
  border-bottom: 1px solid #eceef0;
  padding: 20px 22px;
}
.announcement-icon {
  display: grid;
  width: 40px;
  height: 40px;
  place-items: center;
  border-radius: 50%;
  background: #e8f2ff;
  color: #2563eb;
  font-size: 18px;
  font-weight: 700;
}
.announcement-modal-head p { color: #2563eb; font-size: 10px; font-weight: 700; letter-spacing: .08em; }
.announcement-modal-head h2 { overflow: hidden; margin-top: 3px; font-size: 18px; text-overflow: ellipsis; white-space: nowrap; }
.announcement-modal-head button {
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: #7a7f84;
  font-size: 22px;
  cursor: pointer;
}
.announcement-modal-head button:hover { background: #f3f4f6; color: #1f2328; }
.announcement-modal-body {
  max-height: min(48vh, 420px);
  overflow-y: auto;
  padding: 24px 26px;
  color: #4b5055;
  font-size: 14px;
  line-height: 1.85;
  white-space: pre-wrap;
}
.announcement-modal-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  border-top: 1px solid #eceef0;
  padding: 14px 22px;
  background: #fafbfc;
}
.announcement-modal-meta { display: grid; min-width: 0; gap: 3px; }
.announcement-modal-foot time { color: #9aa0a6; font-size: 11px; }
.announcement-modal-meta [data-announcement-reminder] { color: #7a7f84; font-size: 11px; }
.announcement-modal-meta [data-announcement-error] { color: #c2413a; font-size: 12px; font-weight: 600; }
.announcement-modal-actions { display: flex; flex: 0 0 auto; align-items: center; gap: 8px; }
.announcement-modal-actions .btn { min-width: 82px; }
.announcement-modal-actions .btn:disabled { cursor: wait; opacity: .58; }

@media (max-width: 1250px) {
  .dash-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dash-lower { grid-template-columns: minmax(0, 2fr) minmax(270px, 1fr); }
}

@media (max-width: 899.98px) {
  .dash-topline { grid-template-columns: minmax(0, 1fr) auto; gap: 12px; }
  .dash-greeting { font-size: 22px; }
  .dash-ranges { grid-column: 1 / -1; grid-row: 2; max-width: 100%; overflow-x: auto; }
  .dash-range { min-width: 68px; }
  .dash-lower { grid-template-columns: minmax(0, 1fr); }
  .dash-analysis-body { min-height: 320px; padding: 28px 18px 20px; }
  .dash-panel-head { align-items: flex-start; flex-direction: column; padding-top: 13px; padding-bottom: 10px; }
  .dash-view-tabs { width: 100%; overflow-x: auto; }
}

@media (max-width: 560px) {
  .app[data-page="console-home"] { padding-bottom: 40px; }
  .dash-greeting { font-size: 20px; }
  .dash-cards { grid-template-columns: minmax(0, 1fr); gap: 10px; }
  .dash-card-body { padding-left: 13px; padding-right: 13px; }
  .dash-analysis-body { padding-left: 12px; padding-right: 12px; }
  .dash-chart-wrap { height: 220px; padding-left: 0; overflow-x: auto; }
  .dash-chart-bars { min-width: 720px; }
  .dash-grid-lines { left: 0; min-width: 720px; }
  .dash-legend { font-size: 10px; }
  .announcement-modal-head { padding: 17px 18px; }
  .announcement-modal-body { padding: 20px; }
  .announcement-modal-foot { align-items: stretch; flex-direction: column; gap: 10px; padding: 12px 18px; }
  .announcement-modal-actions { width: 100%; }
  .announcement-modal-actions .btn { flex: 1 1 0; min-width: 0; }
}

/* 推荐有礼 —— 邀请链接行在窄屏换行，复制按钮不被挤成一条竖线 */
/* 未开启态既不是成功也不是错误，.alert 基类本身没有边框和底色，
   不给它一个中性容器就会浮成一行无主的文字 */
.referral-terms-off {
  border: 1px solid var(--line);
  background: var(--bg-soft, #fafafa);
  color: var(--ink-2, #555);
}
.referral-terms { margin-bottom: 18px; }
.referral-link-card { margin-bottom: 18px; }
.referral-link-row {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
  margin: 10px 0 6px;
}
.referral-link-input {
  flex: 1 1 320px;
  min-width: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
}
.referral-link-row .btn { flex: 0 0 auto; }
/* 「已达上限」是解释，不是警告，所以用弱化的琥珀色而不是报错红 */
.referral-capped {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 999px;
  background: #fef3c7;
  color: #92400e;
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
}
