/* blocfone® web chat — Telegram-like, mobile-first. No frameworks. */
:root {
  --bg: #ffffff;
  --chat-bg: #f2f4f7;          /* Telegram-ish light canvas */
  --bot-bubble: #ffffff;
  --user-bubble: #ffd84d;      /* blocfone yellow */
  --user-text: #040039;      /* navy on yellow — the logo pairing */
  --text: #1a1a1a;
  --hint: #7a8087;
  --accent: #f5c400;           /* brand yellow (buttons) */
  --accent-press: #e0b400;
  --chip-bg: #ffffff;
  --chip-border: #d9dee4;
  --best: #1e8e3e;
  --navy: #040039;            /* logo navy — wordmark, links, on-yellow text */
  --radius: 18px;
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
body {
  font: 16px/1.45 'Poppins', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  color: var(--text);
  background: var(--chat-bg);
  -webkit-font-smoothing: antialiased;
}

/* ── Layout: full-viewport chat, desktop gets a centered phone-width column ── */
#chat {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  max-width: 430px;
  margin: 0 auto;
  background: var(--chat-bg);
}

/* ── Flow mode (html.fit) ──
   Free-flowing document layout: the geo picker and every step sit in the page
   flow, the PAGE scrolls, no internal scrollbars anywhere. Applied in two
   contexts (widget.js): ?embed=fit (mobile hero — the parent iframe is sized
   to our content via postMessage) and TOP-LEVEL opens (/widget/ directly, the
   future blocfone.com sheet). Iframed without the param (hero desktop phone
   frame, og-card) stays viewport-bound with internal scrolling. */
html.fit, html.fit body { height: auto; }
html.fit #chat { height: auto; }
html.fit #messages { overflow-y: visible; }
html.fit #actionBar { max-height: none; overflow-y: visible; }
/* Top-level flow: keep the brand header (and the burger menu it anchors)
   pinned while the page scrolls. Inside the hero iframe sticky/fixed resolve
   against the iframe, which never scrolls internally — no behavior change. */
html.fit #chatHeader { position: sticky; top: 0; z-index: 30; }
/* Fixed = viewport-relative, but #chat is a CENTERED 430px column — anchor the
   menu to the column's right edge, not the window's (desktop top-level view).
   On narrow screens (100vw ≤ 430px) the max() falls back to the plain 8px. */
html.fit #menuPanel { position: fixed; right: max(8px, calc((100vw - 430px) / 2 + 8px)); }
html.fit #menuBackdrop { position: fixed; }

/* Legal line inside action-bar states (owner-placed, not a persistent
   footer). Full flex row, centered, comfortable spacing under the chips. */
.legalrow {
  flex-basis: 100%;
  text-align: center;
  font-size: 11.5px;
  color: var(--hint);
  margin-top: 12px;
  padding-bottom: 2px;
  line-height: 1.6;
}
.legalrow a { color: var(--hint); text-decoration: underline; }

#chatHeader {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  padding-top: calc(10px + env(safe-area-inset-top));
  background: var(--bg);
  border-bottom: 1px solid #e6e9ed;
  flex: 0 0 auto;
}
#brandLogo {
  width: 38px; height: 38px;
  border-radius: 50%;    /* circular yellow mark (logo B) */
  display: block;
  cursor: pointer;       /* tap = new search (same as the menu action) */
}
#brandName { font-weight: 700; font-size: 16px; color: var(--navy); }
#brandSub  { font-size: 12px; color: var(--hint); }

/* ── Messages ── */
#messages {
  flex: 1 1 auto;
  min-height: 0;             /* proper flex scroll — the actionBar cap below reserves
                                the room the intro bubbles need */
  overflow-y: auto;
  padding: 12px 12px 14px;   /* bottom breathing room so the last bubble clears the action bar */
  display: flex;
  flex-direction: column;
  gap: 6px;
  scroll-behavior: smooth;
}
.msg {
  max-width: 84%;
  padding: 9px 13px;
  border-radius: var(--radius);
  white-space: pre-line;
  overflow-wrap: break-word;
  animation: pop .18s ease-out;
}
@keyframes pop { from { transform: translateY(4px); opacity: 0; } to { transform: none; opacity: 1; } }
/* A step that opens a NEW section of the conversation (the plans list's intro
   line): its own top margin reads as the section break it is — and it keeps
   the anchored landing clean: the whitespace above the anchor (14px + the
   container's 6px gap) exceeds the 8px landing strip below the sticky header,
   so the PREVIOUS chat is always fully tucked under the header, never peeking
   (owner, 2026-08-20 — "they are technically separate chats"). */
.msg.step-break { margin-top: 14px; }
.msg.bot  { align-self: flex-start; background: var(--bot-bubble); border-bottom-left-radius: 6px; box-shadow: 0 1px 1px rgba(0,0,0,.05); }
.msg.user { align-self: flex-end;   background: var(--user-bubble); color: var(--user-text); border-bottom-right-radius: 6px; }

/* typing indicator */
.typing { display: inline-flex; gap: 4px; padding: 13px 15px; }
.typing i {
  width: 7px; height: 7px; border-radius: 50%;
  background: #b9c0c7;
  animation: blink 1.1s infinite both;
}
.typing i:nth-child(2) { animation-delay: .18s; }
.typing i:nth-child(3) { animation-delay: .36s; }
@keyframes blink { 0%, 80%, 100% { opacity: .25; } 40% { opacity: 1; } }

/* ── Plan cards (inside chat) ── */
.plan {
  align-self: stretch;
  background: var(--bot-bubble);
  border: 1px solid var(--chip-border);
  border-radius: 14px;
  padding: 11px 13px;
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  animation: pop .18s ease-out;
}
.plan:active { background: #fdf6dc; }
.plan .flag { font-size: 26px; }
.plan .mid  { flex: 1 1 auto; min-width: 0; }
.plan .line1 { font-weight: 600; font-size: 15px; }
.plan .line2 { font-size: 12.5px; color: var(--hint); margin-top: 1px; }
.plan .price { text-align: right; }
.plan .usd   { font-weight: 700; font-size: 15px; }
.plan .local { font-size: 12px; color: var(--hint); }
.plan .badge {
  display: inline-block;
  font-size: 11px;
  color: var(--best);
  border: 1px solid currentColor;
  border-radius: 6px;
  padding: 0 5px;
  margin-bottom: 2px;
}

/* ── Bottom action bar (Telegram reply-keyboard feel) ── */
#actionBar {
  flex: 0 1 auto;
  /* Adaptive cap: tall pickers scroll instead of crushing the chat. 320px =
     header + overhead + both EN intro bubbles (~230px at 400px width) — the
     picker takes everything else and scrolls. (Mobile hero uses embed=fit and
     ignores this; it matters for the desktop phone frame + og-card.
     Re-measure if intro copy changes, then regenerate the og-image under a
     NEW versioned name.) */
  max-height: max(90px, calc(100% - 320px));
  overflow-y: auto;
  padding: 8px 10px;
  padding-bottom: calc(10px + env(safe-area-inset-bottom));
  background: var(--bg);
  border-top: 1px solid #e6e9ed;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
  min-height: 58px;
}
#actionBar:empty { display: none; }
.chip {
  border: 1px solid var(--chip-border);
  background: var(--chip-bg);
  color: var(--text);
  border-radius: 12px;
  padding: 11px 14px;         /* ≥44px touch target with line-height */
  font-size: 15px;
  cursor: pointer;
  flex: 1 1 calc(33% - 8px);
  min-width: 96px;
  max-width: 100%;
  text-align: center;
}
.chip:active { background: #f0f2f5; }
.chip.primary { background: var(--accent); border-color: var(--accent); font-weight: 600; color: var(--navy); }
.chip.primary:active { background: var(--accent-press); }
.chip.wide { flex-basis: 100%; }
/* Brand chips (pay step): leading mark + Phantom's own purple. */
/* Icon chips are FLEX (2026-07-26, owner): every mark vertically centered by
   layout, not by per-logo baseline nudges. Zero vertical padding + min-height
   keeps every button the same height no matter the icon size — oversized
   marks grow into the padding zone, never the button. Heights are tuned per
   logo so the visible INK matches, not the bounding box: Phantom's and
   Solflare's tiles are the same colour as their chips, so only the inner
   glyph shows (~60% of the tile) and they need a larger box to read equal
   to Apple's bare glyph (the reference at 19px). Tuned by side-by-side
   render, variant C. */
.chip.has-ic { display: flex; align-items: center; justify-content: center; gap: 9px; padding-top: 0; padding-bottom: 0; min-height: 45px; }
/* The QR sheet's brand switcher (2026-07-26) */
.payqr-switch { display: flex; gap: 6px; justify-content: center; flex-wrap: wrap; margin-top: 10px; }
.qswitch { border: 1px solid var(--chip-border); background: var(--chip-bg); color: var(--text); border-radius: 9px; padding: 6px 12px; font-size: 13px; cursor: pointer; }
.qswitch.active { border-color: var(--navy); font-weight: 600; }
.chip .chip-ic { width: auto; height: 18px; }
.chip .chip-dot { display: inline-block; width: 12px; height: 12px; border-radius: 50%; vertical-align: -1px; margin-right: 8px; }
.chip.phantom { background: #AB9FF2; border-color: #AB9FF2; font-weight: 600; color: #1C1633; }
.chip.phantom:active { background: #998BEF; }
/* Wallet-first buttons (2026-07-23) — brand values from AUTHORITATIVE sources
   only (see the connect-session spec): Solflare = their wallet-adapter icon's
   own fills; Backpack = their icon's pixels (#E33E3F glyph on a neutral chip —
   they publish no button background); Base = brand.base.org exact (#0000FF,
   white passes AA per their guide). Active shades are interaction affordance,
   not brand spec. */
.chip.solflare { background: #FFEF46; border-color: #FFEF46; font-weight: 600; color: #02050A; }
.chip.solflare:active { background: #F2E23B; }
.chip.backpack { background: var(--chip-bg); border-color: var(--chip-border); font-weight: 600; color: #0A0B0D; }
.chip.backpack:active { background: #f0f2f5; }
.chip.base { background: #0000FF; border-color: #0000FF; font-weight: 600; color: #ffffff; }
.chip.base:active { background: #0000DB; }
.chip.gpay { background: #ECCB45; border-color: #ECCB45; font-weight: 600; color: #0C003C; }  /* exact logo yellow */
.chip.gpay:active { background: #E0BC33; }
.chip.qr { background: #E9EDF1; border-color: #E9EDF1; }
.chip.qr:active { background: #DEE3E9; }

/* Start over (2026-07-18) — blocfone yellow with navy type. */
.chip.bfstart { background: var(--accent); border-color: var(--accent); color: var(--navy); font-weight: 600; }
.chip.bfstart:active { background: var(--accent-press); }
.chip .chip-badge {
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto;
  width: 20px; height: 20px; border-radius: 6px; margin-right: 9px;
  font-weight: 700; font-size: 12px; line-height: 1;
}
/* Every "Continue with Google / Apple" button, everywhere (owner, 2026-07-19).
   ONE class, one look: blocfone navy, white type, Google's four-colour G and
   Apple's mark in white. Previously the pay step gave Apple a white button to
   rank it second to Google — that inconsistency is gone deliberately, so the
   pair reads as two equal ways in rather than a preferred one and an
   afterthought.

   Marks center by flex (.has-ic) and size per logo — see the icon-chip
   block above. Apple's 19px glyph is the visual reference all others are
   tuned against. */
.chip.bfconnect {
  display: flex; align-items: center; justify-content: center;
  background: var(--navy); border-color: var(--navy); color: #fff; font-weight: 600;
}
.chip.bfconnect:active { background: #0a0a5c; }
.chip.bfconnect.apple .chip-ic { height: 19px; }
.chip.phantom .chip-ic { height: 27px; }
.chip.solflare .chip-ic { height: 25px; }
.chip.backpack .chip-ic { height: 20px; }
.chip.base .chip-ic { height: 17px; }
/* Live payment status dot — three beats (owner, 2026-07-19):
     AMBER pulse  we need something from you: pick a payment method
     GREEN pulse  a method was chosen; payment in flight, don't leave
     GREEN solid  payment received on-chain; nothing left to wait on
   Supersedes the 2026-07-14 scheme (blue pulse = waiting, green pulse =
   provisioning). Pulsing now means "waiting on something" full stop, so a
   solid dot always reads as secured. */
/* "Need help?" inside the delivered-eSIM status — a link, not a chip: it opens
   the How-blocfone-works read in place (owner, 2026-07-14). */
.msg .statlink { color: var(--navy); text-decoration: underline; font-weight: 600; cursor: pointer; }
.msg .statdot { display: inline-block; width: 12px; height: 12px; border-radius: 50%; background: #E9A200; box-shadow: 0 0 0 0 #E9A200; animation: bfpulse 1.2s ease-in-out infinite; margin-right: 9px; vertical-align: -1px; flex: 0 0 auto; }
/* Green = the payment is ours. SOLID once it has landed on-chain (and for the
   terminal states: eSIM ready, top-up complete, refunded). It PULSES only while
   the payment is in flight — a method has been tapped but nothing has confirmed
   yet — which is the one moment the buyer must not click away. */
.msg.stat-ok .statdot { background: #22c55e; box-shadow: none; animation: none; }
.msg.stat-ok.stat-working .statdot { animation: bfpulse-ok 1.2s ease-in-out infinite; }
@keyframes bfpulse {
  0%   { transform: scale(1);    box-shadow: 0 0 0 0 rgba(233,162,0,.55); opacity: 1 }
  70%  { transform: scale(1.25); box-shadow: 0 0 0 10px rgba(233,162,0,0); opacity: .85 }
  100% { transform: scale(1);    box-shadow: 0 0 0 0 rgba(233,162,0,0);   opacity: 1 }
}
@keyframes bfpulse-ok {
  0%   { transform: scale(1);    box-shadow: 0 0 0 0 rgba(34,197,94,.55); opacity: 1 }
  70%  { transform: scale(1.25); box-shadow: 0 0 0 10px rgba(34,197,94,0); opacity: .85 }
  100% { transform: scale(1);    box-shadow: 0 0 0 0 rgba(34,197,94,0);   opacity: 1 }
}

/* search input mode */
#searchWrap { display: flex; gap: 8px; width: 100%; }
#searchInput {
  flex: 1 1 auto;
  border: 1px solid var(--chip-border);
  border-radius: 12px;
  padding: 11px 14px;
  font-size: 16px;             /* 16px prevents iOS zoom-on-focus */
  outline: none;
  background: var(--chip-bg);
}
#searchInput:focus { border-color: var(--accent); }
#searchResults { display: flex; flex-direction: column; gap: 6px; width: 100%; max-height: 34dvh; overflow-y: auto; }
#searchResults .chip { text-align: left; flex-basis: 100%; }

/* ── Checkout: Solana Pay QR card ── */
.payqr {
  align-self: center;
  max-width: 100%;          /* the nowrap intro must never widen the card past the column */
  background: #fff;
  border: 1px solid var(--chip-border);
  border-radius: 14px;
  padding: 14px;
  text-align: center;
  animation: pop .18s ease-out;
}
/* QR intro line (owner, 2026-07-14): prominent, and ALWAYS one line — it must
   never wrap. Because it can't wrap, the type has to fit the narrowest column
   it will ever sit in, so the size steps down with the viewport (min(): 13px in
   the 430px desktop column, ~12px at 375px, smaller still on a 320px phone).
   Prominence therefore comes from weight + full-strength ink rather than size.
   Margin-bottom matches .payqr-warn's margin-top → equal air above and below
   the code. Longest string is Greek; all 9 are measured to fit on one line. */
.payqr-intro {
  /* Was `white-space: nowrap` + `font-size: min(13px, 3.2vw)` — held to one line
     by SHRINKING, which put the card's key instruction at ~10px on a 320px
     phone. It now wraps instead and keeps a readable size. The 9 translations
     run 42–50 chars, so it is one line on most screens anyway and only wraps
     where it genuinely has to (2026-07-19). */
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  line-height: 1.35;
  margin-bottom: 8px;
}
.payqr-code { display: inline-block; background: #fff; }
.payqr-code img, .payqr-code canvas { display: block; }
.payqr-cap { font-size: 12px; color: var(--hint); margin-top: 8px; }

/* ── eSIM delivery card ── */
.esim {
  align-self: stretch;
  background: #fff;
  border: 1px solid var(--chip-border);
  border-radius: 14px;
  padding: 14px;
  animation: pop .18s ease-out;
}
.esim-qr { display: flex; justify-content: center; padding: 4px 0 10px; }
.esim-row {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 0; border-top: 1px solid #eef1f4;
}
.esim-lbl { flex: 0 0 auto; font-size: 12px; color: var(--hint); min-width: 92px; }
.esim-val { flex: 1 1 auto; font-size: 13px; word-break: break-all; }
.esim-copy {
  flex: 0 0 auto; border: 1px solid var(--chip-border); background: var(--chip-bg);
  border-radius: 8px; padding: 6px 10px; font-size: 12px; cursor: pointer;
}
.esim-note { font-size: 12px; color: var(--hint); margin-top: 10px; }

/* Paying wallet (2026-07-18). Owner wants the FULL 44-char address, on ONE
   line — so it stacks (label above, address below) to claim the whole card
   width, and the address scales with the viewport instead of wrapping.
   clamp: 2.35vw fits 44 monospace chars inside a 375px phone card
   (44 x ~0.6em x 9px ~= 235px), floored at 7.5px so it never becomes
   unreadable, and capped at 12px so it doesn't balloon on desktop.
   nowrap + the flex-basis reset are what actually hold it to one line;
   .esim-val's word-break would otherwise wrap it. Nobody types an address by
   hand — the copy button is the real affordance — so small is acceptable here
   in a way it wouldn't be for body text. */
/* The funding card holds ONE row and nothing above it, so .esim-row's divider
   border reads as a stray rule. On the eSIM card the QR is the first child, so
   this doesn't match there and the dividers stay. */
.esim > .esim-row:first-child { border-top: none; }
.esim-row.wallet { flex-wrap: wrap; align-items: flex-start; row-gap: 4px; }
.esim-row.wallet .esim-lbl { flex: 1 1 100%; min-width: 0; }
.esim-row.wallet .esim-val {
  flex: 1 1 auto; min-width: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: clamp(7.5px, 2.35vw, 12px);
  white-space: nowrap; word-break: normal; overflow: hidden;
}

/* ── "Show all N plans" link (below the top 5) ── */
.showmore {
  align-self: center;
  background: none;
  border: none;
  color: var(--navy);          /* logo navy — reads as a brand link */
  font-size: 14px;
  font-weight: 600;
  padding: 6px 12px;
  margin: 2px 0 4px;
  cursor: pointer;
}
.showmore:active { opacity: .6; }

/* Alternative badge (↓/↑) — muted so the ⭐ best still stands out */
.plan .badge.alt { color: #7a8087; }

/* Keep USDC prices on one line (longer than a $ figure) */
.plan .price { flex: 0 0 auto; white-space: nowrap; padding-left: 6px; }
.plan .usd, .plan .local { white-space: nowrap; }

/* Plan-list footer: guidance caption + Search-again (secondary) */
.plansfooter {
  align-self: center;
  max-width: 90%;
  text-align: center;
  font-size: 13px;
  color: var(--hint);
  margin: 6px 0 2px;
}
.showmore.secondary { color: var(--hint); font-weight: 500; }

/* Footer container — centres caption + buttons; re-rendered below the full list on expand */
.plansfoot { align-self: stretch; display: flex; flex-direction: column; align-items: center; gap: 2px; }

/* Country buttons (region 3-up grid): uniform size even when a long name wraps.
   Fixed height fits two lines; content is centred so single-line names match. */
.chip.country {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 52px;
  padding: 6px 8px;
  font-size: 13.5px;
  line-height: 1.15;
}

/* Data-tier buttons: 2-up so 'Standard · 5 GB' etc. fit on one line (no wrap) */
.chip.tier { flex: 1 1 calc(50% - 8px); white-space: nowrap; }

/* Terms/Privacy consent line under the pay QR */
/* margin-bottom matches margin-top so the notice sits in even space, rather
   than crowding whatever follows it (2026-07-18). */
.consent { font-size: 11.5px; color: var(--hint); margin-top: 10px; margin-bottom: 10px; line-height: 1.35; }
.consent a { color: var(--navy); text-decoration: underline; }

/* ── Header burger menu ── */
#chat { position: relative; }   /* anchor for the dropdown */
#menuBtn {
  margin-left: auto;
  border: none;
  background: none;
  font-size: 22px;
  line-height: 1;
  color: var(--text);
  cursor: pointer;
  padding: 6px 4px;
  width: 40px; height: 40px;
}
#menuBtn:active { opacity: .55; }

#menuBackdrop {
  position: absolute;
  inset: 0;
  background: rgba(0,0,0,.18);
  z-index: 40;
}
#menuBackdrop[hidden] { display: none; }

#menuPanel {
  position: absolute;
  top: calc(58px + env(safe-area-inset-top));
  right: 8px;
  min-width: 220px;
  max-width: 82%;
  background: var(--bg);
  border: 1px solid var(--chip-border);
  border-radius: 14px;
  box-shadow: 0 8px 28px rgba(0,0,0,.18);
  z-index: 41;
  overflow: hidden;
  animation: pop .16s ease-out;
}
#menuPanel[hidden] { display: none; }
.menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  border: none;
  background: none;
  text-align: left;
  font-size: 15px;
  color: var(--text);
  padding: 13px 16px;
  cursor: pointer;
}
.menu-item + .menu-item { border-top: 1px solid #eef1f4; }
.menu-item:active { background: #f0f2f5; }
.menu-item .mi-ic { font-size: 17px; width: 22px; text-align: center; }
.menu-item.current { color: var(--best); font-weight: 600; }  /* selected language */
.menu-head {
  display: flex; align-items: center; gap: 8px;
  padding: 11px 14px; font-size: 13px; color: var(--hint);
  border-bottom: 1px solid #eef1f4; cursor: pointer;
}

/* Links inside chat bubbles (help / legal / support) */
.msg a { color: var(--navy); text-decoration: underline; }

/* Full "How it works" help bubble (verbatim Telegram content) */
.msg.help { max-width: 92%; }
.msg.help code { background: #eef1f4; border-radius: 4px; padding: 1px 5px; font-size: 13px; }
.msg.help strong { font-weight: 700; }

/* ⚠️ safety warning inside the pay QR card */
/* The whole warning is bold (owner, 2026-07-14) — it's the one line on this
   card a buyer must not skim. Bolding lives here rather than in *markers* so
   the emphasis can't fall out of a translation. */
.payqr-warn { font-size: 11.5px; font-weight: 700; color: #b26a00; margin-top: 8px; line-height: 1.35; }

/* Compare-all divider: rule under the original 5-card window, label above the
   full lowest→highest price list (owner spec 2026-07-08). */
.plansdivider {
  border-top: 1px solid var(--chip-border);
  color: var(--hint);
  font-size: 12.5px;
  font-weight: 600;
  text-align: center;
  letter-spacing: .02em;
  margin: 14px 2px 4px;
  padding-top: 10px;
  align-self: stretch;
}

/* Compact select pill on plan cards (Mini App parity, chat-card height).
   Brand-primary styling matches .chip.primary — navy on yellow. */
.plansel {
  display: inline-block;
  margin-top: 5px;
  border: 1px solid var(--accent);
  background: var(--accent);
  color: var(--navy);
  font-weight: 600;
  font-size: 11.5px;
  border-radius: 8px;
  padding: 3px 9px;
  cursor: pointer;
}
.plansel:active { background: var(--accent-press); }

/* Thin rule inside a bot bubble — richBubble renders a bare '---' line as
   this (the "Also works in:" section under the summary's price). */
.msg hr.msg-rule {
  border: 0;
  border-top: 1px solid var(--chip-border);
  margin: 8px 0;
}
