:root{
  --bg:#0f1620; --card:#172230; --line:#26344a; --text:#e8eef5; --muted:#8aa0b6;
  --accent:#1fd18e; --accent-d:#15a872; --accent2:#2f6bff; --danger:#ef4444; --r:14px;
  /* Accent-derived glow tints. server.js overrides these (and the vars above)
     per-brand via an injected :root block, so a non-green accent loses the
     green halos. These literals are just the neutral-default fallback. */
  --accent-glow-45:rgba(31,209,142,.45); --accent-glow-18:rgba(31,209,142,.18);
  --accent-glow-22:rgba(31,209,142,.22); --accent-glow-50:rgba(31,209,142,.5);
}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{background:var(--bg);color:var(--text);font-family:-apple-system,"Segoe UI",Roboto,Inter,system-ui,sans-serif;font-size:15px;line-height:1.45;-webkit-tap-highlight-color:transparent}
#hdr{display:flex;align-items:center;padding:13px 16px;border-bottom:1px solid var(--line);position:sticky;top:0;background:var(--bg);z-index:5}
.logo{display:flex;align-items:center;gap:9px;font-weight:800;letter-spacing:.6px;font-size:17px;
  text-decoration:none;color:inherit}
.logo-mark{width:20px;height:20px;border-radius:6px;background:linear-gradient(135deg,var(--accent),var(--accent2));box-shadow:0 0 14px var(--accent-glow-45);flex:none}
.logo-img{width:26px;height:26px;border-radius:7px;object-fit:cover;flex:none}
.hero-logo{display:block;width:128px;height:128px;border-radius:20px;object-fit:cover;margin:8px auto 18px;box-shadow:0 0 30px var(--accent-glow-18)}
.logo-text b{color:var(--accent);font-weight:800}
#app{padding:16px;max-width:600px;margin:0 auto}
.center{text-align:center}.muted{color:var(--muted)}
h2{font-size:19px;margin:2px 0 14px}
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--r);padding:16px;margin-bottom:14px}
.contact-card{padding:2px 0 8px}
.contact-card .ct-title{font-size:10.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.08em;text-align:center;margin-bottom:10px}
.contact-row{display:flex;flex-wrap:wrap;gap:8px;justify-content:center}
.cpill{display:inline-flex;align-items:center;gap:6px;padding:8px 13px;border:1px solid var(--line);border-radius:99px;background:var(--inset);color:var(--text);font-size:13px;font-weight:600;cursor:pointer;text-decoration:none;white-space:nowrap;transition:border-color .15s,background .15s,transform .1s}
.cpill:hover{border-color:var(--accent)}
.cpill:active{border-color:var(--accent);background:var(--card);transform:scale(.97)}
.balance{font-size:36px;font-weight:800;letter-spacing:-.5px}
.btn{display:block;width:100%;border:none;border-radius:12px;padding:15px;font-size:15px;font-weight:700;cursor:pointer;font-family:inherit;margin-top:10px}
.btn-primary{background:var(--accent);color:var(--on-accent)}
.btn-primary:active{background:var(--accent-d)}
.btn-ghost{background:var(--card);color:var(--text);border:1px solid var(--line)}
.btn:disabled{opacity:.5}
label{display:block;font-size:11px;color:var(--muted);margin:11px 0 4px;text-transform:uppercase;letter-spacing:.05em}
input,select,textarea{width:100%;background:var(--inset);border:1px solid var(--line);border-radius:10px;padding:12px;color:var(--text);font-size:16px;font-family:inherit;outline:none}
input:focus,select:focus,textarea:focus{border-color:var(--accent)}
textarea{resize:vertical;min-height:118px;box-sizing:border-box;line-height:1.45}
.row{display:flex;gap:8px}.row>*{flex:1;min-width:0}
.rate{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:13px;border:1px solid var(--line);border-radius:12px;margin-bottom:9px;cursor:pointer;background:var(--card)}
.rate:active{border-color:var(--accent)}
.rate .price{font-weight:800;color:var(--accent);white-space:nowrap}
.rate .carrier{font-weight:700}.rate .name{font-size:12.5px}
/* Pickup-point picker: a search box above a height-capped scroll box so a long
   point list scrolls inside itself instead of the whole page. dvh tracks the
   Telegram WebView's live viewport; vh is the fallback for older webviews. */
.ptsearch{margin-bottom:11px}
.ptscroll{max-height:calc(100vh - 190px);max-height:calc(100dvh - 190px);overflow-y:auto;-webkit-overflow-scrolling:touch;padding-right:3px}
.ptscroll::-webkit-scrollbar{width:7px}
.ptscroll::-webkit-scrollbar-thumb{background:var(--line);border-radius:99px}
.ptscroll::-webkit-scrollbar-track{background:transparent}
.pill{display:inline-block;font-size:10.5px;padding:2px 8px;border-radius:99px;background:var(--inset);border:1px solid var(--line);color:var(--muted);margin-left:6px;white-space:nowrap}
.pill-home{background:rgba(31,209,142,.12);border-color:rgba(31,209,142,.38);color:#6ee7a8}
.pill-point{background:rgba(59,130,246,.12);border-color:rgba(59,130,246,.38);color:#93b4f8}
.pill-locker{background:rgba(168,85,247,.12);border-color:rgba(168,85,247,.38);color:#c4a3f5}
.err{background:rgba(239,68,68,.12);border:1px solid rgba(239,68,68,.3);color:#fca5a5;padding:11px 13px;border-radius:10px;margin:10px 0;font-size:14px}
.ok{background:rgba(31,209,142,.12);border:1px solid rgba(31,209,142,.3);color:var(--accent);padding:11px 13px;border-radius:10px;margin:10px 0}
.mono{font-family:ui-monospace,Menlo,Consolas,monospace;word-break:break-all;font-size:13px}
.lbl{padding:13px;border:1px solid var(--line);border-radius:12px;margin-bottom:9px;background:var(--card)}
.spin{display:inline-block;width:18px;height:18px;border:2px solid var(--line);border-top-color:var(--accent);border-radius:50%;animation:sp .8s linear infinite;vertical-align:-3px}
@keyframes sp{to{transform:rotate(360deg)}}
.seg{display:flex;gap:6px;margin-top:6px}
.seg button{flex:1;padding:11px;border-radius:10px;border:1px solid var(--line);background:var(--inset);color:var(--text);font-weight:700;cursor:pointer;font-family:inherit}
.seg button.on{background:var(--accent);color:var(--on-accent);border-color:var(--accent)}
.sugg{padding:11px 12px;border:1px solid var(--line);border-top:none;background:var(--inset);cursor:pointer;font-size:14px}
.sugg:first-child{border-top:1px solid var(--line);border-radius:8px 8px 0 0}
.sugg:last-child{border-radius:0 0 8px 8px}
.sugg:active{background:var(--card)}
/* Dozens of localities can share one postal code (PL 87-600 has 49), so the
   list scrolls instead of being cut short. overscroll-behavior keeps the drag
   inside the box — otherwise Telegram reads it as swipe-to-close the app. */
.sugglist{max-height:320px;max-height:min(320px,52dvh);overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain}
.sugglist::-webkit-scrollbar{width:7px}
.sugglist::-webkit-scrollbar-thumb{background:var(--line);border-radius:99px}
.sugglist::-webkit-scrollbar-track{background:transparent}
.sel{font-size:13px;margin-top:6px;color:var(--accent);min-height:16px}
.kv{display:flex;justify-content:space-between;gap:10px;padding:6px 0;border-bottom:1px solid var(--line)}
.kv:last-child{border:none}
.big{font-size:22px;font-weight:800;color:var(--accent)}
small{color:var(--muted)}
#lang{margin-left:auto;position:relative}
#langbtn{display:flex;align-items:center;gap:5px;background:var(--card);border:1px solid var(--line);border-radius:10px;padding:5px 8px;cursor:pointer;font-family:inherit}
#langbtn img{width:24px;height:16px;object-fit:cover;border-radius:3px;display:block}
#langbtn .chev{color:var(--muted);font-size:11px;line-height:1}
#langdd{display:none;position:absolute;top:calc(100% + 6px);right:0;background:var(--card);border:1px solid var(--line);border-radius:12px;padding:5px;min-width:158px;box-shadow:0 10px 30px rgba(0,0,0,.5);z-index:20}
.langopt{display:flex;align-items:center;gap:10px;padding:10px;border-radius:8px;cursor:pointer;font-weight:600}
.langopt:active{background:var(--inset)}
.langopt img{width:24px;height:16px;object-fit:cover;border-radius:3px}

/* ─── Rate-search loading overlay (glassmorphism) ───
   A translucent rate-search overlay themed from the brand palette (accent +
   accent2, dark glass). Markup lives in index.html
   outside #app; driven by MRLoader in app.js. */
.search-loader{
  position:fixed; inset:0; z-index:9999;
  display:flex; align-items:center; justify-content:center;
  background:rgba(15,22,32,.66);
  -webkit-backdrop-filter:blur(8px); backdrop-filter:blur(8px);
  opacity:0; visibility:hidden; pointer-events:none;
  transition:opacity .3s ease, visibility .3s ease;
}
.search-loader.open{opacity:1; visibility:visible; pointer-events:auto}
.sl-card{
  width:min(90vw,380px); padding:2rem 1.8rem 1.6rem; text-align:center;
  border-radius:18px; background:rgba(23,34,48,.62);
  -webkit-backdrop-filter:blur(14px); backdrop-filter:blur(14px);
  box-shadow:0 20px 60px rgba(0,0,0,.5); position:relative;
  transform:translateY(8px) scale(.98);
  transition:transform .3s cubic-bezier(.2,.8,.2,1);
}
.search-loader.open .sl-card{transform:translateY(0) scale(1)}
/* Gradient hairline border via a masked pseudo-element so the card body stays
   translucent (a normal border would sit over the blur). */
.sl-card::before{
  content:''; position:absolute; inset:0; border-radius:18px; padding:1px;
  background:linear-gradient(135deg,var(--accent),var(--accent2));
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite:xor; mask-composite:exclude; pointer-events:none; opacity:.7;
}
.sl-icon{position:relative; width:56px; height:56px; margin:0 auto 1rem; display:flex; align-items:center; justify-content:center}
.sl-logo{width:34px; height:34px; border-radius:9px; object-fit:cover; display:block; animation:sl-bob 1.6s ease-in-out infinite; -webkit-user-select:none; user-select:none}
.sl-ring{position:absolute; inset:0; border-radius:50%; border:2px solid var(--accent-glow-22); border-top-color:var(--accent); animation:sp .9s linear infinite}
.sl-title{font-size:18px; font-weight:800; color:var(--text); letter-spacing:-.01em; margin-bottom:5px}
.sl-msg{font-size:13.5px; color:var(--muted); min-height:1.2em; margin-bottom:16px; transition:opacity .22s ease; font-family:ui-monospace,Menlo,Consolas,monospace}
.sl-track{height:6px; border-radius:100px; overflow:hidden; background:rgba(255,255,255,.07); margin-bottom:8px}
.sl-bar{height:100%; width:0%; border-radius:100px; background:linear-gradient(90deg,var(--accent),var(--accent2)); box-shadow:0 0 10px var(--accent-glow-50); transition:width .18s ease-out}
.sl-pct{font-size:11.5px; color:var(--muted); font-family:ui-monospace,Menlo,Consolas,monospace; letter-spacing:.05em}
@keyframes sl-bob{0%,100%{transform:translateY(0)}50%{transform:translateY(-4px)}}
@media (prefers-reduced-motion:reduce){
  .sl-logo,.sl-ring{animation:none}
  .sl-card{transition:none; transform:none}
  .sl-bar{transition:none}
  .search-loader{transition:opacity .1s linear}
}

/* ─── Customer surveys (.sv-*) ───────────────────────────────────────────────
   The Home prompt card + the one-question-per-screen sheet. Deliberately
   HEX-FREE: every colour is one of the nine :root vars every fork declares, or
   a neutral rgba(). Inset surfaces use rgba(0,0,0,.25) rather than a literal so
   they darken whatever --card the fork ships instead of painting this brand's
   palette onto a white-label. Buttons, glyphs and rings all key off --accent,
   so a fork gets its own colour for free. */
.sv-card{background:var(--card);border:1px solid var(--line);border-left:3px solid var(--accent);border-radius:var(--r);padding:14px;margin-bottom:14px;box-shadow:0 0 18px var(--accent-glow-18,transparent)}
.sv-eyebrow{font-size:10.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:5px}
.sv-c-t{font-weight:700;font-size:15px;line-height:1.35}
.sv-c-m{font-size:12.5px;color:var(--muted);margin-top:3px}
.sv-c-a{display:flex;gap:8px;margin-top:12px}
.sv-c-a .btn{flex:1;margin-top:0;padding:12px;font-size:14px}
.sv-top{display:flex;align-items:center;gap:10px;margin-bottom:14px}
.sv-prog{flex:1;height:6px;border-radius:99px;background:rgba(0,0,0,.25);overflow:hidden}
.sv-bar{height:100%;width:0;border-radius:99px;background:var(--accent2,var(--accent));transition:width .25s ease}
.sv-step{font-size:12px;color:var(--muted);font-variant-numeric:tabular-nums;white-space:nowrap}
.sv-q{font-size:18px;font-weight:700;line-height:1.35;margin-bottom:6px}
.sv-help,.sv-intro{font-size:13px;color:var(--muted);line-height:1.5;margin-bottom:12px;white-space:pre-line}
.sv-opts{display:flex;flex-direction:column;gap:8px}
/* Real <button> rows: keyboard-reachable, >=44px tall, state in aria-pressed
   and mirrored by a TEXT glyph (Tor "Safest" blocks SVG). */
.sv-opt{display:flex;align-items:center;gap:10px;width:100%;min-height:52px;padding:12px 14px;text-align:left;
  background:rgba(0,0,0,.25);border:1px solid var(--line);border-radius:12px;color:var(--text);
  font-family:inherit;font-size:15px;line-height:1.35;cursor:pointer}
.sv-box{flex:none;width:20px;color:var(--muted);font-size:16px;line-height:1}
.sv-opt[aria-pressed="true"]{border-color:var(--accent)}
.sv-opt[aria-pressed="true"] .sv-box{color:var(--accent)}
.sv-opt:disabled{opacity:.45;cursor:default}
.sv-stars{display:flex;gap:4px;justify-content:center;margin:4px 0 2px}
.sv-star{min-width:44px;min-height:44px;padding:4px;background:none;border:none;border-radius:10px;
  color:var(--muted);font-family:inherit;font-size:26px;line-height:1;cursor:pointer}
.sv-star.on{color:var(--accent)}
.sv-ends{display:flex;justify-content:space-between;gap:10px;font-size:11.5px;color:var(--muted)}
/* 11 pills: two rows of 6+5 on a 360px phone, one row once there is room for
   each pill to stay a comfortable target. */
.sv-nps{display:grid;grid-template-columns:repeat(6,1fr);gap:6px;margin:4px 0 6px}
@media (min-width:560px){.sv-nps{grid-template-columns:repeat(11,1fr)}}
.sv-pill{min-height:44px;background:rgba(0,0,0,.25);border:1px solid var(--line);border-radius:10px;
  color:var(--text);font-family:inherit;font-size:15px;font-weight:700;cursor:pointer}
.sv-pill[aria-pressed="true"]{border-color:var(--accent);color:var(--accent)}
.sv-ex{margin-top:12px}
.sv-ex-p{font-size:13px;color:var(--muted);line-height:1.45;margin-bottom:6px}
.sv-cnt{font-size:11.5px;color:var(--muted);text-align:right;margin-top:4px;font-variant-numeric:tabular-nums}
.sv-cnt.hot{color:var(--danger)}
/* The Telegram keyboard eats half the viewport, so the forward control rides
   the bottom edge instead of scrolling away under it. */
.sv-foot{position:sticky;bottom:0;z-index:2;padding:10px 0 4px;background:var(--bg)}
.sv-foot .btn{margin-top:0}
.sv-foot:empty{display:none}
.sv-thx{text-align:center;padding:26px 6px}
.sv-thx-i{font-size:34px;line-height:1;margin-bottom:10px;color:var(--accent)}
.sv-thx-t{font-size:19px;font-weight:800;margin-bottom:6px}
.sv-opt:focus-visible,.sv-pill:focus-visible,.sv-star:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
@media (prefers-reduced-motion:reduce){.sv-bar{transition:none}}

/* The prompt as a floating panel. Anchored high rather than dead-centre: on a
   phone the thumb sits low and a centred panel lands under it, and a card
   further down the page is simply never seen — a storefront that stacks a hero
   logo, six contact buttons and the balance above it pushed the inline card
   below the fold. This is NOT a modal: the backdrop dismisses on tap, the page keeps
   scrolling behind it, and the inline card stays put so the survey is still
   findable afterwards. z-index sits BELOW .smsg (9500) so a support message
   always wins if the two ever coincide. */
.sv-pop{position:fixed; inset:0; z-index:9000; display:flex; align-items:flex-start;
  justify-content:center; padding:16px; background:rgba(0,0,0,.55);
  opacity:0; visibility:hidden; pointer-events:none; transition:opacity .18s ease, visibility .18s ease}
.sv-pop.on{opacity:1; visibility:visible; pointer-events:auto}
.sv-pop-in{position:relative; width:100%; max-width:380px; margin-top:13vh;
  background:var(--card); border:1px solid var(--line); border-left:3px solid var(--accent);
  border-radius:var(--r); padding:16px 16px 14px;
  box-shadow:0 18px 50px rgba(0,0,0,.55), 0 0 18px var(--accent-glow-18,transparent);
  transform:translateY(-10px); transition:transform .18s ease}
.sv-pop.on .sv-pop-in{transform:none}
.sv-pop-x{position:absolute; top:8px; right:10px; background:none; border:0; color:var(--muted);
  font-size:20px; line-height:1; padding:6px 8px; cursor:pointer; font-family:inherit}
.sv-pop-x:hover{color:var(--text)}
.sv-pop-x:focus-visible{outline:2px solid var(--accent); outline-offset:2px}
@media (prefers-reduced-motion:reduce){.sv-pop,.sv-pop-in{transition:none}}

/* ── Caution "kraft" row ─────────────────────────────────────────────────────
   The cheapest rates on the board, from a source whose issuing genuinely fails
   more often than the rest of the catalogue. upstream flags the row with a
   NEUTRAL `caution` boolean (never the provider key — the mini-app token hides
   it), exactly like `signature`.

   The DELIBERATE INVERSE of .rate--signature: not a dimmer highlight but a
   different MATERIAL. Signature is machined metal (an animated bezel); this is
   cardboard — matte brown plane, 3% diagonal fibre, sepia frame, no motion, no
   brand accent. A dimmer bezel would read as "signature, but broken"; a different
   material reads as a different thing, which is the message: cheap, honest, plain.
   Brand-agnostic on purpose, so this block is identical in every fork. Dark-only,
   like the rest of the storefront. ── */
.rate--caution{
  border-color:#4a3a26;
  background:
    repeating-linear-gradient(46deg,rgba(201,168,119,.030) 0 1.5px,transparent 1.5px 5px),
    linear-gradient(180deg,rgba(201,168,119,.055) 0%,rgba(201,168,119,.012) 100%),
    #16120c;
}
.rate--caution:active{border-color:#7a613e}
/* Price → flat champagne. `background:none` + text-fill reset is REQUIRED: some
   forks paint the price as a brand gradient clipped to the text, and a bare
   `color` would be overridden by `-webkit-text-fill-color:transparent`. Flat,
   never the pack/brand gradient — a premium-looking price fights the message. */
.rate--caution .price{background:none;-webkit-text-fill-color:#d9b877;color:#d9b877}
/* The advisory as a FOOTNOTE not a chip: the refund promise is the half that
   turns "the shop is broken" into "I picked the cheap one", and two words cannot
   carry it. The one full sentence in the rate list, on the row with most cause. */
.rate-caution-note{margin-top:6px;font-size:12px;line-height:1.45;color:#b99a6b;display:flex;gap:5px;align-items:flex-start}
.rate-caution-note .rcn-mark{flex:none;opacity:.85}
/* Blue clock — a measured ETA ("~10 min") or the generic deferred-issuing chip.
   Same .pill family as the delivery badges (shape inherited per fork); explicit
   margin because some forks' .pill carries none. The storefront rendered NEITHER
   timing chip before, so locker/card-checkout rates showed no ETA at all. */
.pill-clock{background:rgba(59,130,246,.14);border-color:rgba(59,130,246,.42);color:#93c5fd;margin-left:6px}

/* ── Low-reliability tariff filter (Settings › Shipping) ─────────────────────
   The card wears the same kraft material as the rows it governs — matte brown
   plane, 3% diagonal fibre, sepia frame — so the switch reads as belonging to
   THOSE rows and not to the shipping defaults under it. Brand-agnostic on
   purpose, exactly like .rate--caution: this block is identical in every fork. */
.card--kraft{
  border-color:#4a3a26;
  background:
    repeating-linear-gradient(46deg,rgba(201,168,119,.030) 0 1.5px,transparent 1.5px 5px),
    linear-gradient(180deg,rgba(201,168,119,.055) 0%,rgba(201,168,119,.012) 100%),
    #16120c;
}
.kr-row{display:flex;align-items:center;gap:12px}
.kr-row .kr-txt{flex:1;min-width:0}
.kr-row .kr-txt b{display:block;font-size:14.5px;color:#e8d9b8}
.kr-row .pill{margin:4px 0 0;display:inline-block;background:#0d0b07;border-color:#4a3a26;color:#b99a6b}
.kr-help{font-size:12.5px;line-height:1.45;margin-top:8px;color:#b99a6b}
.kr-saved{font-size:12px;font-weight:700;color:#d9b877;margin-top:9px;min-height:15px}
.kr-saved.err{color:#fca5a5}
/* The switch — the one component the storefront did not have. Pure CSS (no
   image, nothing to load) on a <button role="switch">, so it is keyboard- and
   screen-reader-operable and the state lives in aria-checked, not in a class. */
.switch{flex:none;width:46px;height:27px;border-radius:99px;cursor:pointer;border:1px solid #4a3a26;background:#0d0b07;padding:0;position:relative;transition:background .18s,border-color .18s}
.switch::after{content:'';position:absolute;top:2px;left:2px;width:21px;height:21px;border-radius:50%;background:#6b5a3c;transition:transform .18s,background .18s}
.switch[aria-checked="true"]{background:rgba(217,184,119,.20);border-color:#d9b877}
.switch[aria-checked="true"]::after{transform:translateX(19px);background:#d9b877}
.switch:disabled{opacity:.55;cursor:default}
/* "N hidden · Show" footer under a filtered rate list, and the same block as
   the whole-screen message when EVERY rate on the route was filtered out. */
.rate-hidden-line{display:flex;justify-content:space-between;align-items:center;gap:10px;padding:10px 12px;border:1px dashed #4a3a26;border-radius:12px;color:#b99a6b;font-size:12.5px;background:rgba(201,168,119,.03)}
.rate-hidden-line b{color:#d9b877;cursor:pointer;font-weight:700;white-space:nowrap}
@media (prefers-reduced-motion: reduce){.switch,.switch::after{transition:none}}

/* ---------- support-message popup ---------- */
/* Blocking dialog: a one-shot support message, or an unread ticket reply. Sits
   at z-index 9500, above the search loader, and outside #app so a repaint can't
   wipe it. Tapping the button is what POSTs the read receipt. */
.smsg{
  position:fixed; inset:0; z-index:9500;
  display:flex; align-items:center; justify-content:center; padding:18px;
  background:rgba(0,0,0,.7);
  -webkit-backdrop-filter:blur(8px); backdrop-filter:blur(8px);
  opacity:0; visibility:hidden; pointer-events:none;
  transition:opacity .25s ease, visibility .25s ease;
}
.smsg.open{opacity:1; visibility:visible; pointer-events:auto}
.smsg-card{
  width:min(92vw,420px); padding:18px;
  border-radius:16px; background:var(--card);
  border:1px solid var(--accent-glow-45);
  box-shadow:0 18px 60px rgba(0,0,0,.55), 0 0 24px var(--accent-glow-18);
}
.smsg-head{display:flex; align-items:center; gap:10px; margin-bottom:8px}
.smsg-head img{width:34px; height:34px; border-radius:9px; object-fit:cover; flex:none}
.smsg-brand{font-weight:800; letter-spacing:.6px; font-size:14px}
.smsg-brand b{color:var(--accent)}
.smsg-title{font-size:12.5px; color:var(--muted); font-weight:700}
.smsg-date{font-size:11px; color:var(--muted); margin-bottom:8px}
.smsg-body{
  white-space:pre-wrap; overflow-wrap:anywhere; font-size:14.5px; line-height:1.5;
  max-height:48vh; overflow-y:auto; -webkit-overflow-scrolling:touch;
  background:var(--bg); border:1px solid var(--line); border-radius:12px; padding:12px;
}

/* ---------- support tickets ---------- */
.tk-badge{display:inline-block;background:var(--danger);color:#fff;font-size:11px;font-weight:700;line-height:1;padding:2px 6px;border-radius:999px;margin-left:6px;vertical-align:middle}
.tk-inc{display:block;text-decoration:none;color:var(--text);background:var(--bg);border:1px solid var(--line);border-radius:12px;padding:11px 12px;margin-top:8px}
.tk-inc:active{border-color:var(--accent)}
.tk-inc-t{font-weight:700;font-size:13.5px}
.tk-inc-s{font-size:12.5px;margin-top:3px}
.tk-row{display:block;text-decoration:none;color:var(--text);background:var(--card);border:1px solid var(--line);border-radius:12px;padding:11px 12px;margin-bottom:10px}
.tk-row:active{border-color:var(--accent)}
.tk-chat{display:flex;flex-direction:column;gap:10px}
.tk-msg{max-width:88%;align-self:flex-start}
.tk-msg.me{align-self:flex-end;text-align:right}
.tk-from{font-size:11.5px;font-weight:700;margin-bottom:3px}
.tk-bubble{display:inline-block;text-align:left;background:var(--bg);border:1px solid var(--line);border-radius:12px;padding:9px 12px;font-size:14px;line-height:1.45;white-space:pre-wrap;overflow-wrap:anywhere}
.tk-msg.me .tk-bubble{background:var(--accent-glow-18);border-color:var(--accent-glow-45)}
.tk-when{font-size:10.5px;margin-top:3px}

/* ══════════════════════════════════════════════════════════════════════════
   AGORA — the app, dressed as the site.

   Everything above this line is the shared storefront: a 600px phone column
   built for a Telegram Mini App. Agora's customers arrive in a browser, from a
   public page built out of Anton caps, hard blue shadows, mono micro-labels,
   square corners and 1px rules. This block is not a patch on that column — it
   restyles the PRIMITIVES every screen is made of (.card, .btn, input, .rate,
   .lbl, .pill, .kv, headings) so all thirteen screens change together, and
   then lays each one out for a real screen.

   Two rules for anyone editing this:

   1. THEME SELECTOR. brand.json's palette is injected by server.js into
      <head> as a :root block (0,1,0). Anything that must beat it lives under
      html[data-theme="dark"] (0,1,1). Do not "simplify" these into :root.

   2. NO SHADOW ON FIGURES. The hard offset shadow is the brand's signature and
      it belongs on headings and buttons — words, read at a glance. Behind a
      price, a balance or a tracking number it stops reading as a shadow and
      starts reading as a misprinted second number.
   ══════════════════════════════════════════════════════════════════════════ */

:root{
  --inset:#f7f5ef;      /* input / pill / segment surfaces — was a hardcoded navy */
  --on-accent:#ffffff;  /* text sitting on --accent */
  --rule:#0A0A0A;
  --rule-w:2px;
  --sh:#0A0A0A;         /* hard shadow, no blur, offset down-left: the signature */
  --shade:rgba(10,10,10,.04);
}
html[data-theme="dark"]{
  --bg:#06070b; --card:#0b0d15; --line:#171b28; --text:#eef1f7; --muted:#666f85;
  --accent:#3B72FF; --accent-d:#2f5ede; --accent2:#3B72FF;
  --accent-glow-45:rgba(59,114,255,.45); --accent-glow-18:rgba(59,114,255,.18);
  --accent-glow-22:rgba(59,114,255,.22); --accent-glow-50:rgba(59,114,255,.5);
  --inset:#0f1220; --on-accent:#ffffff;
  --rule:#171b28; --rule-w:1px;
  --sh:#3B72FF;         /* on black a black shadow does not exist: it turns blue */
  --shade:rgba(255,255,255,.02);
}

/* The display face, shared with the public page. The app had NO @font-face at
   all — Anton existed only inside landing.html, so the two halves of one brand
   were set in different type. Same file and same ?v=, so a customer arriving
   from the landing already has it cached. */
@font-face{
  font-family:'Anton';font-style:normal;font-weight:400;font-display:swap;
  src:url('/fonts/anton-latin.woff2?v=1') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

/* Everything is square. The template hardcodes a radius in a few dozen places,
   so they are flattened in one list rather than edited one by one. */
.card,.btn,input,select,textarea,.rate,.lbl,.seg button,.sugg,.sugg:first-child,
.sugg:last-child,.tk-row,.tk-inc,.tk-bubble,.err,.ok,#langbtn,#langdd,.cpill,
.pill,.smsg-card,.sl-card,.sv-card,.sv-opt,.sv-pill,.dtile,.drow,.dempty,
.ptsearch,.sugglist{border-radius:0}

/* ── type ───────────────────────────────────────────────────────────────── */
.eyebrow{display:block;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:10px;
  letter-spacing:.22em;text-transform:uppercase;color:var(--muted)}
h2{font-family:'Anton',Impact,sans-serif;font-weight:400;text-transform:uppercase;
  letter-spacing:.02em;font-size:26px;line-height:1.08;margin:0 0 18px}
/* Field labels are the site's micro-label, not sentence-case form text. */
label{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:10px;
  letter-spacing:.18em;margin:14px 0 6px}
small{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:10px;
  letter-spacing:.18em;text-transform:uppercase}
.big{font-family:'Anton',Impact,sans-serif;font-weight:400;font-size:30px;letter-spacing:.01em}

/* ── surfaces ───────────────────────────────────────────────────────────── */
#hdr{border-bottom-width:var(--rule-w);border-bottom-color:var(--rule)}
.logo-text{color:var(--accent);text-shadow:-2px 2px 0 var(--sh);font-weight:800}
html[data-theme="dark"] .logo-text{color:var(--text)}
.card{border-width:var(--rule-w);border-color:var(--rule);padding:18px}
#lang{margin-left:auto}   /* the push used to live on the deleted theme switch */

/* ── account + log out, in the app bar ──
   The same pair the public page carries, so the top-right corner means the same
   thing on both surfaces: where you are signed in, and how to leave. Leaving
   used to be buried at the bottom of the Account screen — three taps and a
   scroll from anywhere else in the app. */
#acct{display:flex;align-items:center;gap:8px;margin-left:8px}
#acct:empty{display:none}   /* signed out: no gap left behind */
.acctbtn{display:inline-flex;align-items:center;gap:7px;background:var(--card);
  border:1px solid var(--line);cursor:pointer;font-family:ui-monospace,Menlo,Consolas,monospace;
  font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--muted);
  padding:9px 12px;line-height:1}
.acctbtn:hover{border-color:var(--accent);color:var(--text)}
.acctbtn.on{border-color:var(--accent);color:var(--text)}
/* Under 560px the name goes and its initial stands in: at that width the brand,
   a language flag, a whole username and a door do not fit on one bar. Swapped
   rather than dropped — an empty button beside a door says nothing about whose
   account it is. */
/* 14ch fits the usernames people actually pick; past that it truncates rather
   than pushing the door off the bar. */
.acctbtn .who{max-width:14ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.acctbtn .whoin{display:none;font-weight:700;letter-spacing:0}
@media (max-width:559px){
  .acctbtn .who{display:none}
  .acctbtn .whoin{display:inline}
  .acctbtn{padding:9px 11px}
}

/* A door, drawn in CSS: a frame, and the leaf leaving it. Same shape as the one
   on the public page — copied deliberately rather than shared, because the two
   sheets have different colour tokens and one of them would have resolved to
   nothing. Not an SVG: Tor's "Safest" refuses inline SVG and the icon would
   vanish with no error and no gap. */
.exitbtn{display:inline-flex;align-items:center;justify-content:center;
  background:transparent;border:1px solid var(--line);cursor:pointer;
  padding:10px 11px;line-height:0;color:var(--muted)}
.exitbtn:hover{border-color:var(--accent);color:var(--text)}
.exitbtn i{display:inline-block;position:relative;width:13px;height:14px;
  border:2px solid currentColor;border-right:0}
.exitbtn i::before{content:"";position:absolute;top:50%;right:-8px;width:8px;height:2px;
  background:currentColor;transform:translateY(-1px)}
.exitbtn i::after{content:"";position:absolute;top:50%;right:-8px;width:5px;height:5px;
  border:2px solid currentColor;border-left:0;border-bottom:0;transform:translateY(-3.5px) rotate(45deg)}

/* ── controls ───────────────────────────────────────────────────────────── */
.btn{font-family:'Anton',Impact,sans-serif;font-weight:400;letter-spacing:.09em;
  text-transform:uppercase;font-size:16px;padding:16px}
/* The primary action is BLUE, everywhere, on every screen. The template's dark
   theme made it a white slab — right for one lone call to action on a marketing
   page, but inside the app it lands among dark cards and reads as something
   that broke rather than as the thing to press. One rule, so the dashboard, the
   quote form and the checkout cannot drift apart. */
html[data-theme="dark"] .btn-primary{background:var(--accent);color:#fff;box-shadow:none}
html[data-theme="dark"] .btn-primary:hover{background:#4d80ff}
html[data-theme="dark"] .btn-primary:active{background:var(--accent-d)}
.btn-ghost{border-width:var(--rule-w);border-color:var(--rule)}
.btn-ghost:hover{border-color:var(--accent)}
input,select,textarea{background:var(--inset);border-color:var(--line);padding:13px 14px}
input:focus,select:focus,textarea:focus{border-color:var(--accent);box-shadow:-3px 3px 0 var(--sh)}
.seg button{font-family:'Anton',Impact,sans-serif;font-weight:400;letter-spacing:.06em;
  text-transform:uppercase}

/* ── data rows: a rate, a saved label, a pickup point ───────────────────── */
/* The public page shows a mock of exactly this row — carrier mark, service
   line, price — so it is the one component customers have already seen. */
.rate,.lbl{border-width:1px;border-color:var(--line);padding:15px 16px;margin-bottom:8px;
  transition:border-color .12s,box-shadow .12s,background .12s}
.rate:hover{border-color:var(--accent);box-shadow:-4px 4px 0 var(--sh)}
.rate .price{font-family:'Anton',Impact,sans-serif;font-weight:400;font-size:24px;
  letter-spacing:.01em;color:var(--text)}
.rate .carrier{font-weight:700;font-size:15.5px;letter-spacing:-.01em}
.rate .name{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:11.5px}
/* "InPost by Vinted" — the carrier, then who carries it.
   A lockup, not two words: "by" is the site's micro-label (mono, tracked,
   muted) so it stays subordinate to the carrier name it follows, and the
   official wordmark rides beside it at its own aspect ratio. inline-flex keeps
   the three parts on one baseline no matter how the row wraps; without it the
   image sits on the text baseline and hangs below the line.
   `translateY` and not `vertical-align`: the wordmark's own artwork has the
   letterforms slightly high in the box, so it reads a hair above centre. */
.cb{display:inline-flex;align-items:center;gap:5px;margin-left:7px;
  vertical-align:baseline;white-space:nowrap}
.cb-by{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:9px;
  letter-spacing:.16em;text-transform:uppercase;color:var(--muted);font-weight:400}
.cb img{display:block;transform:translateY(.5px);opacity:.92}
/* Delivery / timing chips.
   The template painted these mint, pink and violet — three saturated hues that
   belong to no part of this brand, on a page whose entire palette is one blue
   against near-black. Here they are the site's micro-label: mono, tracked,
   bordered, and colour used ONCE, on the one chip that changes a decision
   (where the parcel ends up). Timing is information, not an alarm. */
.pill{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:9.5px;
  letter-spacing:.12em;text-transform:uppercase;padding:3px 7px;
  background:transparent;border-color:var(--line);color:var(--muted)}
html[data-theme="dark"] .pill-home,
html[data-theme="dark"] .pill-point,
html[data-theme="dark"] .pill-locker{background:rgba(59,114,255,.10);
  border-color:rgba(59,114,255,.42);color:#9dbaff}
html[data-theme="dark"] .pill-clock,
html[data-theme="dark"] .pill-instant{background:transparent;
  border-color:var(--line);color:var(--muted)}
.kv{border-bottom-color:var(--line);padding:8px 0}
.sel{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px}
.err,.ok{border-width:1px;border-left-width:3px}
.cpill{background:transparent;border-color:var(--line);font-weight:600}
.cpill:hover{border-color:var(--accent)}

/* ══ dashboard ═════════════════════════════════════════════════════════════
   A dashboard is not a menu. This one leads with the money, then the actions,
   then the customer's actual shipments — without those it is six buttons and
   an empty page, which is what it was. */
.dbal{border:var(--rule-w) solid var(--rule);background:var(--card);padding:20px 18px;margin-bottom:14px}
.dbal .balance{font-family:'Anton',Impact,sans-serif;font-weight:400;font-size:40px;
  letter-spacing:.01em;line-height:1.06;margin-top:8px;text-shadow:none}
.dstat{margin-top:16px}
.dstat b{display:block;font-family:'Anton',Impact,sans-serif;font-weight:400;
  font-size:26px;letter-spacing:.01em;margin-top:6px}
.dbal-b{width:auto;margin-top:16px;padding:12px 22px;font-size:13px}

.dtile{position:relative}
.dtile::before{content:"";position:absolute;left:-1px;top:-1px;width:34px;height:3px;
  background:var(--accent);opacity:0;transition:opacity .14s}
.dtile:hover::before{opacity:1}
/* The primary action is the blue block, not the white one: white is right for a
   lone call to action on the public page, but dropped into a grid of dark tiles
   it reads as a slab that broke rather than as the thing to press. */
html[data-theme="dark"] .dtile--pri{background:var(--accent);color:#fff;box-shadow:none}
html[data-theme="dark"] .dtile--pri:hover{background:#4d80ff}
html[data-theme="dark"] .dtile--pri:active{background:var(--accent-d)}
.dtile--pri::before{display:none}
/* Top up is the other thing customers come here to do, so it shares the top
   row — but it is not the same act as buying a label, and painting them the
   same blue would make the page ask two questions at once. Outlined in the
   accent: clearly of the same rank, clearly a different verb. */
.dtile--alt{border-color:var(--accent);color:var(--text)}
.dtile--alt:hover{background:rgba(59,114,255,.10)}
.dtile--alt::before{opacity:1}

.drec{margin-top:26px}
.drec-h{display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  padding-bottom:10px;border-bottom:var(--rule-w) solid var(--rule);margin-bottom:12px}
.dlink{background:none;border:0;padding:0;cursor:pointer;color:var(--accent);
  font-family:ui-monospace,Menlo,Consolas,monospace;font-size:11px;letter-spacing:.14em;
  text-transform:uppercase}
.drow{display:flex;align-items:center;gap:12px;padding:13px 14px;border:1px solid var(--line);
  background:var(--card);margin-bottom:8px}
.drow-c{display:flex;align-items:center;gap:9px;min-width:0;flex:1}
.drow-c b{font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.drow-m{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:11.5px;color:var(--muted);
  white-space:nowrap}
.drow-p{font-family:'Anton',Impact,sans-serif;font-weight:400;font-size:19px;
  letter-spacing:.01em;margin-left:auto;white-space:nowrap}
/* Empty state, because "no shipments yet" and "this screen is broken" must not
   look the same. */
.dempty{border:1px dashed var(--line);background:var(--card);padding:30px 22px;text-align:center}
.dempty b{display:block;font-family:'Anton',Impact,sans-serif;font-weight:400;font-size:20px;
  text-transform:uppercase;letter-spacing:.03em;margin-bottom:8px}
.dempty span{color:var(--muted);font-size:13.5px}

/* ══ signing in ════════════════════════════════════════════════════════════
   A window, not a page: a card that floats on the dark, carrying the brand's
   hard shadow. The customer arrives here from a marketing page and is being
   asked for a password — the smaller and more contained the surface, the more
   it reads as a deliberate step and the less as "the site turned into a form". */
.authwin{width:min(100%,440px);margin:0 auto;background:var(--card);
  border:var(--rule-w) solid var(--rule);box-shadow:-8px 8px 0 var(--sh);padding:28px 26px 24px}
.authwin-t{font-size:22px;margin:0 0 18px}
.authwin .btn{margin-top:16px}
/* The window has its own padding, so a .card inside it would be a box in a box. */
.authwin .card{background:transparent;border:0;padding:0;margin:0}

.contact-card{padding:16px 0 2px;margin-top:26px;border-top:1px solid var(--line)}
.contact-card .ct-title{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:10px;
  letter-spacing:.22em;margin-bottom:9px;text-align:left}
/* justify-content, not text-align: .contact-row is a flex container, so the
   text-align that looks like it should do this does nothing at all. */
.contact-card .contact-row{justify-content:flex-start}

/* ══ a real screen ═════════════════════════════════════════════════════════
   Under 1000px nothing here applies and the phone column stands exactly as the
   Mini App needs it. Above it, each screen gets the width it can actually use
   — which is NOT the same width for all of them: a login form 1180px wide is
   worse than one at 460px. */
@media (min-width:1000px){
  /* No wrapper element exists inside #hdr, so the bar is centred with padding
     instead of markup — max() keeps a sane gutter on a narrow desktop. */
  #hdr{padding:16px max(24px,calc((100% - 1180px) / 2));height:76px}
  .logo{font-size:22px;gap:11px}
  .logo-img{width:34px;height:34px}

  #app{max-width:620px;padding:38px 24px 80px}
  h2{font-size:34px;margin-bottom:26px}

  /* The sign-in window floats in the middle of the page instead of sitting at
     the top of a column. 76px is the sticky header. */
  body[data-screen="auth"] #app{max-width:none;padding:0;
    min-height:calc(100vh - 76px);display:flex;align-items:center;justify-content:center}
  body[data-screen="auth"] .authwin{padding:34px 32px 28px}
  body[data-screen="auth"] .authwin-t{font-size:26px}

  /* Screens that earn the full page. */
  body[data-screen="home"] #app,
  body[data-screen="labelsScreen"] #app,
  body[data-screen="pointsScreen"] #app{max-width:1180px;padding-left:34px;padding-right:34px}
  /* Rates sits just inside the others. A rate row is one short line of text and
     one price: at the full 1180 the carrier name and the amount end up at
     opposite edges of the screen and the eye has to travel the whole width to
     pair them. Narrower reads as a list of comparable things. */
  body[data-screen="ratesScreen"] #app{max-width:1040px;padding-left:34px;padding-right:34px}
  /* Forms are wide enough for two columns, and no wider. */
  body[data-screen="shipFrom"] #app,
  body[data-screen="details"] #app,
  body[data-screen="smartLabel"] #app,
  body[data-screen="settings"] #app{max-width:940px;padding-left:34px;padding-right:34px}

  /* ── home ── */
  .dash-mark{display:none}          /* the 104px hero mark is a phone device */
  #svhost:empty{display:none}
  .dbal{display:grid;grid-template-columns:1.5fr 1fr 1fr;align-items:end;gap:28px;
    padding:26px 30px;margin-bottom:16px}
  .dbal .balance{font-size:52px}
  .dstat{margin-top:0}

  /* Twelve tracks, so the two rows can have different rhythms.
     Row one is what people come for: buy a label, put money in. Row two is
     everything a customer touches once a month at most. Equal tiles would have
     claimed all six were equally likely. Sized by padding and never by a
     min-height — forcing them taller only put empty floor under one word. */
  .dgrid{display:grid;grid-template-columns:repeat(12,1fr);gap:14px}
  .dgrid .dtile{margin-top:0;padding:26px 24px;text-align:left;font-size:19px;grid-column:span 3}
  .dgrid .dtile--pri{grid-column:span 8;font-size:26px;padding:34px 28px}
  .dgrid .dtile--alt{grid-column:span 4;font-size:26px;padding:34px 28px}
  .dgrid .dtile--sm{font-size:17px;padding:22px 22px}
  .drow{padding:15px 20px;gap:20px}

  /* ── the quote form ──
     From and To are the same question asked twice, so they sit side by side
     and the parcel spans both. Stacked, the customer scrolls past the origin
     to reach the destination and cannot see the corridor they are pricing. */
  body[data-screen="shipFrom"] #app > .card:nth-of-type(1),
  body[data-screen="shipFrom"] #app > .card:nth-of-type(2){
    display:inline-block;width:calc(50% - 7px);vertical-align:top;margin-bottom:14px}
  body[data-screen="shipFrom"] #app > .card:nth-of-type(1){margin-right:12px}

  /* ── rates ──
     The price set like the prices on the public page, in a row kept deliberately
     tight: a rate list is scanned, not read, and every millimetre of row height
     is one fewer option visible without scrolling. */
  body[data-screen="ratesScreen"] .rate{padding:13px 20px;margin-bottom:7px}
  body[data-screen="ratesScreen"] .rate .price{font-size:26px}

  /* ── details ──
     Sender and recipient in two columns: they are filled in one after the
     other and constantly compared. */
  body[data-screen="details"] #app{column-gap:0}
  body[data-screen="details"] .dcols{display:grid;grid-template-columns:1fr 1fr;gap:14px;align-items:start}

  /* ── saved labels ── */
  body[data-screen="labelsScreen"] .lbl{padding:16px 20px}
}
