/*
 * A chat app that looks like it was made for people, not for traders.
 *
 * Layout is Teams/Slack-shaped: a sidebar with the channels and everyone in them, a
 * conversation beside it. On a phone the sidebar becomes a drawer and a tab bar takes over,
 * because a 240px rail on a 390px screen leaves no room for the thing you came for.
 *
 * The palette is butter yellow and pastels on warm dark-brown ink. Two rules keep it from
 * turning into a fisher-price toy: ink is #3B3220 (a warm brown) and never pure black, and
 * saturated colour only ever lands on small surfaces — a pill, an avatar, a button. Big areas
 * stay in the cream range, which is what stops a yellow page becoming a hazard sign.
 */

:root {
  color-scheme: light;

  /* ground */
  --bg:        #ffe9a3;   /* the page — butter */
  --bg-deep:   #ffd964;   /* the sidebar — one step warmer */
  --card:      #fffdf5;   /* anything you read words off */
  --card-2:    #fff8e0;

  /* ink */
  --ink:       #3b3220;
  --ink-2:     #6f6249;
  --ink-3:     #a2957c;

  /* the brand, and the crayon box */
  --brand:     #ffde59;
  --pink:      #ffb3c7;
  --mint:      #a8e6cf;
  --lav:       #c9b6f7;
  --peach:     #ffcba4;
  --sky:       #a8d8f0;
  --tomato:    #ff8f6b;

  --line:      rgba(59, 50, 32, 0.12);
  --line-soft: rgba(59, 50, 32, 0.07);

  /* Chunky radii and a soft dropped shadow are most of the "childish" — sharp corners and
     tight shadows read as a financial dashboard however pastel the colours are. */
  --r:    20px;
  --r-sm: 14px;
  --r-xs: 10px;
  --shadow:    0 2px 0 rgba(59,50,32,0.10), 0 8px 20px -10px rgba(59,50,32,0.35);
  --shadow-sm: 0 1px 0 rgba(59,50,32,0.10), 0 4px 12px -8px rgba(59,50,32,0.35);

  --safe-t: env(safe-area-inset-top, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --rail: 264px;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html { background: var(--bg); }
html, body {
  margin: 0; height: 100%;
  color: var(--ink);
  font: 15px/1.55 ui-rounded, "SF Pro Rounded", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  overscroll-behavior-y: none;
}

/* Soft blobs of colour so the page has some life without any of it landing under text. */
body::before {
  content: ''; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(38% 30% at 88% 4%,  color-mix(in oklab, var(--peach) 62%, transparent), transparent 70%),
    radial-gradient(34% 26% at 4% 92%,  color-mix(in oklab, var(--mint) 52%, transparent), transparent 70%),
    radial-gradient(30% 24% at 96% 86%, color-mix(in oklab, var(--pink) 46%, transparent), transparent 70%),
    var(--bg);
}

/* ── shell ───────────────────────────────────────────────────────────────────── */

#app { display: flex; height: 100dvh; overflow: hidden; }

/* ── sidebar ─────────────────────────────────────────────────────────────────── */

#sidebar {
  width: var(--rail); flex: none;
  display: flex; flex-direction: column;
  background: var(--bg-deep);
  border-right: 1px solid var(--line);
  padding: calc(var(--safe-t) + 14px) 12px calc(var(--safe-b) + 12px);
  overflow-y: auto;
}

.brand {
  display: flex; align-items: center; gap: 11px; padding: 4px 8px 16px;
  text-decoration: none; color: inherit; border-radius: var(--r-sm);
  transition: opacity .14s ease;
}
.brand:active { opacity: .6; }
.brand .mark {
  width: 40px; height: 40px; flex: none; border-radius: 14px;
  object-fit: cover; background: var(--card); box-shadow: var(--shadow-sm);
  transform: rotate(-3deg);
}
.brand b { font-size: 19px; font-weight: 800; letter-spacing: -0.02em; }

.side-h {
  font-size: 10.5px; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--ink-3); padding: 4px 10px 7px;
}
/* The gap is what separates "rooms you talk in" from "what each of them is doing" — two
   lists with no space between them read as one list of seven equivalent things. */
.side-h.spaced { padding-top: 22px; margin-top: 14px; border-top: 1px solid var(--line-soft); }

.chan {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 9px 10px; margin-bottom: 3px;
  border: 0; background: none; border-radius: var(--r-sm);
  font: inherit; font-weight: 700; color: var(--ink); text-align: left;
  cursor: pointer; transition: background .14s ease, transform .1s ease;
}
.chan:hover { background: rgba(255,255,255,0.55); }
.chan:active { transform: scale(0.98); }
.chan[aria-current="true"] { background: var(--card); box-shadow: var(--shadow-sm); }
.chan .ico { width: 30px; height: 30px; flex: none; display: grid; place-items: center; font-size: 15px; }
.chan .ico svg { width: 100%; height: 100%; display: block; }
.chan .nm { min-width: 0; }
.chan .nm b { display: block; font-size: 14px; font-weight: 750; }
.chan .nm span {
  display: block; font-size: 11px; font-weight: 600; color: var(--ink-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.chan .st { margin-left: auto; flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--ink-3); opacity: .3; }
.chan .st.working { background: #34c759; opacity: 1; animation: pulse 1.3s ease-in-out infinite; }
@keyframes pulse { 50% { opacity: .3; } }
@media (prefers-reduced-motion: reduce) { .chan .st.working { animation: none; } }
.chan .badge {
  margin-left: auto; font-size: 11px; font-weight: 800;
  background: var(--tomato); color: #fff; border-radius: 999px; padding: 1px 7px;
}

/* ── main column ─────────────────────────────────────────────────────────────── */

#main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.topbar {
  flex: none; display: flex; align-items: center; gap: 10px;
  padding: calc(var(--safe-t) + 12px) 16px 12px;
  background: color-mix(in oklab, var(--bg) 80%, white);
  border-bottom: 1px solid var(--line-soft);
}
.topbar .burger { display: none; }
.topbar .head-txt { min-width: 0; }
.topbar h1 { margin: 0; font-size: 17px; font-weight: 800; letter-spacing: -0.02em; }

/* The channel's owner, at a size where a badly drawn face reads as a face. */
.face-lg { width: 40px; height: 40px; flex: none; border-radius: 14px; display: none; place-items: center; overflow: hidden; box-shadow: var(--shadow-sm); }
.face-lg.on { display: grid; }
.face-lg svg { width: 100%; height: 100%; display: block; }

/* the contract address, and the two places people go to check us */
.ca {
  display: inline-flex; align-items: center; gap: 7px; padding: 7px 12px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px; font-weight: 700;
}
.ca .ca-ico { opacity: .45; font-size: 12px; }
.ca.copied { background: var(--mint); }
.ico-btn {
  width: 38px; height: 38px; flex: none; display: grid; place-items: center;
  border-radius: 12px; color: var(--ink); text-decoration: none;
  background: var(--card); border: 1px solid var(--line); box-shadow: var(--shadow-sm);
  transition: transform .1s ease;
}
.ico-btn:active { transform: translateY(1px); }
.ico-btn svg { width: 17px; height: 17px; }
.ico-btn.pf { background: var(--card); }
.ico-btn[aria-disabled="true"] { opacity: .4; pointer-events: none; }
/* Two lines at most, and a fixed height either way. A description that wraps freely makes the
   header grow and shrink as you move between channels, which reads as the page jumping. */
.topbar .sub {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  font-size: 12px; font-weight: 600; color: var(--ink-2); letter-spacing: 0;
  max-width: 78ch; line-height: 1.35;
}
.topbar .spacer { flex: 1; }

/* ── buttons ─────────────────────────────────────────────────────────────────── */

button, .btn {
  font: inherit; font-weight: 750; color: var(--ink);
  background: var(--card); border: 1px solid var(--line);
  border-radius: 999px; padding: 8px 15px; cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: transform .1s ease, box-shadow .14s ease;
}
button:active { transform: translateY(1px) scale(0.98); box-shadow: none; }
button.primary { background: var(--brand); border-color: rgba(59,50,32,0.18); }
button.ghost { background: transparent; box-shadow: none; }
button:disabled { opacity: .45; }

/* ── the wallet menu ─────────────────────────────────────────────────────────── */

.wallet-wrap { position: relative; }
.wallet-menu {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 40;
  min-width: 210px; padding: 10px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-sm);
  box-shadow: var(--shadow);
  display: flex; flex-direction: column; gap: 6px;
}
.wallet-menu .addr {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px;
  color: var(--ink-2); padding: 4px 8px 8px; border-bottom: 1px solid var(--line-soft);
  overflow-wrap: anywhere;
}
.wallet-menu button { text-align: left; border-radius: var(--r-xs); box-shadow: none; border-color: transparent; }
.wallet-menu button:hover { background: var(--card-2); }
.wallet-menu button.danger { color: #b23c17; }

/* ── messages ────────────────────────────────────────────────────────────────── */

#feed {
  flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch;
  padding: 16px; display: flex; flex-direction: column; gap: 12px;
}

.msg { max-width: min(660px, 86%); }
.msg .row { display: flex; gap: 10px; align-items: flex-start; }
.msg .av {
  width: 36px; height: 36px; flex: none; display: grid; place-items: center;
  border-radius: 13px; overflow: hidden; box-shadow: var(--shadow-sm); background: var(--card);
}
.msg .av svg { width: 100%; height: 100%; display: block; }
/* White by default, which is what the agents keep. Holders get a per-wallet tint applied
   inline — see walletTint() — so staff and strangers are distinguishable at a glance. */
.msg .bubble {
  background: var(--card); border: 1px solid transparent; border-radius: var(--r);
  padding: 10px 14px; box-shadow: var(--shadow); min-width: 0;
}
.msg .who { font-size: 12px; font-weight: 800; margin-bottom: 2px; }
.msg .who time { font-weight: 600; color: var(--ink-3); margin-left: 6px; font-size: 11px; }
.msg .body { overflow-wrap: anywhere; white-space: pre-wrap; }
.msg a { color: #b6710f; font-weight: 700; }

/* Yours: on the right, in the brand colour, no avatar — the usual chat grammar. */
.msg.holder { align-self: flex-end; }
.msg.holder .bubble { background: var(--card-2); border: 1px solid var(--line-soft); }
.msg.mine .bubble { background: var(--brand); }
.msg.mine .av { display: none; }

/* A receipt is not a message and must not be able to look like one. */
.msg.receipt { align-self: stretch; max-width: 100%; }
.msg.receipt .bubble {
  background: linear-gradient(170deg, #fffdf5, var(--card-2));
  border: 2px solid var(--brand);
}
.msg.receipt .who { color: #a8760a; letter-spacing: 0.06em; text-transform: uppercase; }
.msg.receipt .proof {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 10px;
  font-size: 12.5px; font-weight: 800; color: var(--ink); text-decoration: none;
  background: var(--brand); border-radius: 999px; padding: 6px 13px;
  box-shadow: var(--shadow-sm);
}

.msg.system { align-self: center; font-size: 12px; color: var(--ink-2); }

.typing { display: inline-flex; gap: 3px; margin-left: 4px; }
.typing i { width: 5px; height: 5px; border-radius: 50%; background: var(--ink-3); animation: blink 1.2s infinite; }
.typing i:nth-child(2) { animation-delay: .18s } .typing i:nth-child(3) { animation-delay: .36s }
@keyframes blink { 0%,60%,100% { opacity:.25 } 30% { opacity:1 } }

/* ── the work log ────────────────────────────────────────────────────────────── */

/*
 * An action is a disclosure. Closed it is a quiet line saying what someone ran; open it shows
 * exactly what went in and what came back. That is the whole point — a message claiming work
 * happened is a claim, and this is the evidence, one tap away.
 */
.msg.action { align-self: stretch; max-width: 100%; }
.msg.action .head {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 12px; border-radius: 999px; width: fit-content; max-width: 100%;
  background: rgba(255,255,255,0.5); border: 1px dashed var(--line);
  font-size: 12.5px; color: var(--ink-2); cursor: pointer; user-select: none;
  transition: background .14s ease;
}
.msg.action .head:hover { background: var(--card); }
.msg.action .head b { font-weight: 800; color: var(--ink); }
.msg.action .head .tw {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px;
  background: var(--lav); color: var(--ink); padding: 1px 8px; border-radius: 999px; font-weight: 700;
}
.msg.action .head .chev { transition: transform .18s ease; }
.msg.action[data-open="true"] .head .chev { transform: rotate(90deg); }

.msg.action .work {
  margin: 8px 0 2px; padding: 14px;
  background: var(--card); border-radius: var(--r-sm); box-shadow: var(--shadow-sm);
}
.msg.action .work h4 {
  margin: 0 0 6px; font-size: 10px; font-weight: 800; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-3);
}
.msg.action .work pre {
  margin: 0 0 14px; padding: 11px 13px;
  background: #fdf6df; border: 1px solid var(--line-soft); border-radius: var(--r-xs);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11.5px; line-height: 1.6; color: var(--ink-2);
  /* Wide output scrolls in its own box. The page itself must never scroll sideways. */
  overflow-x: auto; white-space: pre; max-height: 340px; overflow-y: auto;
}
.msg.action .work pre:last-child { margin-bottom: 0; }
.msg.action .work .failed { color: #c2410c; }
.msg.action .work .pending { color: var(--ink-3); font-style: italic; font-size: 12.5px; }

/* ── the reply that is coming ────────────────────────────────────────────────── */

/*
 * Rendered as a message from Goobs, in the flow, where his answer is about to appear — not as
 * a status bar somewhere else on the page. That placement is the whole point: you watch the
 * reply being written in the spot it will land, and your own tag joins it as you speak.
 *
 * It always sits last, so new messages arriving push it down rather than appearing under it.
 */
.msg.pending .bubble {
  background: var(--card); border: 1px dashed var(--line);
  display: flex; align-items: center; gap: 10px;
}
.msg.pending .who { margin: 0; }
.msg.pending .to { font-size: 13.5px; color: var(--ink-2); }
.msg.pending .to b { color: var(--ink); font-weight: 800; }
.msg.pending .dots { display: inline-flex; gap: 3px; flex: none; }
.msg.pending .dots i { width: 6px; height: 6px; border-radius: 50%; background: var(--ink-3); animation: blink 1.1s infinite; }
.msg.pending .dots i:nth-child(2) { animation-delay: .16s }
.msg.pending .dots i:nth-child(3) { animation-delay: .32s }
@media (prefers-reduced-motion: reduce) { .msg.pending .dots i { animation: none; } }

/* ── composer ────────────────────────────────────────────────────────────────── */

.composer { flex: none; padding: 10px 16px calc(var(--safe-b) + 12px); display: flex; gap: 10px; align-items: flex-end; }
.composer .field {
  flex: 1; display: flex; align-items: flex-end; min-width: 0;
  background: var(--card); border: 1px solid var(--line); border-radius: 22px;
  padding: 4px 6px 4px 16px; box-shadow: var(--shadow-sm);
}
.composer textarea {
  flex: 1; border: 0; background: none; color: var(--ink); font: inherit;
  resize: none; outline: none; max-height: 130px; padding: 9px 0;
}
.composer textarea::placeholder { color: var(--ink-3); }
.composer textarea:disabled { opacity: 1; }

/* Locked, and obviously about to unlock. Dimmed rather than greyed out, so it reads as a
   pause instead of a permission problem. */
.composer .field.locked {
  background: var(--card-2); border-style: dashed; border-color: var(--line);
}
.composer .field.locked textarea::placeholder { color: var(--ink-2); font-weight: 700; }
.composer .send .count { font-size: 14px; font-weight: 800; font-variant-numeric: tabular-nums; }
.composer .send:disabled { opacity: 1; background: var(--card-2); border-color: var(--line); color: var(--ink-2); }
.composer .send { width: 40px; height: 40px; padding: 0; flex: none; display: grid; place-items: center; border-radius: 50%; font-size: 17px; }

.gatebar {
  flex: none; margin: 0 16px calc(var(--safe-b) + 14px); padding: 16px;
  background: var(--card); border-radius: var(--r); box-shadow: var(--shadow); text-align: center;
}
.gatebar p { margin: 0 0 12px; font-size: 13.5px; color: var(--ink-2); }

/* ── sheets ──────────────────────────────────────────────────────────────────── */

.sheet {
  position: fixed; inset: 0; z-index: 60; display: flex; flex-direction: column;
  background: var(--bg);
  animation: rise .24s cubic-bezier(.22,1,.36,1);
}
@keyframes rise { from { opacity: 0; transform: translateY(16px); } }
/* `display: flex` outranks the hidden attribute's UA `display: none`. Without this every
   sheet renders on top of the app at load — and so does the composer. */
[hidden] { display: none !important; }

.sheet header {
  flex: none; display: flex; align-items: center; gap: 10px;
  padding: calc(var(--safe-t) + 14px) 16px 14px; border-bottom: 1px solid var(--line-soft);
}
.sheet header h2 { margin: 0; font-size: 17px; font-weight: 800; flex: 1; }
.sheet .list { flex: 1; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 12px; }
.sheet .empty { color: var(--ink-2); text-align: center; padding: 56px 24px; font-size: 14px; }
.sheet .empty .big { font-size: 40px; display: block; margin-bottom: 12px; }

.pill { font-size: 12px; font-weight: 800; background: var(--mint); border-radius: 999px; padding: 3px 10px; }
.pill.off { background: var(--line-soft); color: var(--ink-2); }

/* ── the tape ────────────────────────────────────────────────────────────────── */

.trade { display: flex; align-items: center; gap: 12px; padding: 11px 14px; background: var(--card); border-radius: var(--r-sm); box-shadow: var(--shadow-sm); }
.trade .side { font-size: 11px; font-weight: 800; border-radius: 999px; padding: 2px 9px; }
.trade .side.buy { background: var(--mint); }
.trade .side.sell { background: var(--peach); }
.trade .amt { font-weight: 800; }
.trade .who { margin-left: auto; font-size: 12px; color: var(--ink-2); font-family: ui-monospace, Menlo, monospace; }

/* ── phone ───────────────────────────────────────────────────────────────────── */

.tabbar { display: none; }
.scrim { display: none; }

@media (max-width: 860px) {
  /* The sidebar becomes a drawer. At 390px a fixed rail leaves nothing for the conversation,
     which is the only thing anyone opened the app for. */
  #sidebar {
    position: fixed; inset: 0 auto 0 0; z-index: 50; width: min(80vw, var(--rail));
    transform: translateX(-102%); transition: transform .24s cubic-bezier(.22,1,.36,1);
    box-shadow: 12px 0 32px -18px rgba(59,50,32,.6);
  }
  body.drawer #sidebar { transform: none; }
  body.drawer .scrim { display: block; position: fixed; inset: 0; z-index: 45; background: rgba(59,50,32,.32); }

  .topbar .burger { display: grid; place-items: center; width: 38px; height: 38px; padding: 0; border-radius: 12px; }

  #feed { padding: 14px 12px; }
  .msg { max-width: 92%; }
  .composer { padding: 8px 12px calc(var(--safe-b) + 8px); }
  .about { display: none; }
  .gatebar { margin: 0 12px calc(var(--safe-b) + 10px); }

  .tabbar {
    display: flex; flex: none; border-top: 1px solid var(--line);
    background: color-mix(in oklab, var(--bg) 76%, white);
    padding-bottom: var(--safe-b);
  }
  .tabbar button {
    flex: 1; background: none; border: 0; box-shadow: none; border-radius: 0;
    padding: 9px 4px 8px; display: grid; gap: 2px; justify-items: center;
    font-size: 10.5px; font-weight: 750; color: var(--ink-2);
  }
  .tabbar button .ico { font-size: 19px; }
  .tabbar button[aria-current="true"] { color: var(--ink); }

  .topbar { padding: calc(var(--safe-t) + 10px) 12px 10px; gap: 8px; }
  .topbar .sub { -webkit-line-clamp: 2; font-size: 11.5px; }
  /* On a phone the header can hold a face, a name and one action. The links move to the
     channel header card in the feed instead of being crushed into 8px each. */
  .ca, .ico-btn { display: none; }
  .face-lg { width: 34px; height: 34px; border-radius: 12px; }
  /* The gate bar is a prompt, not a page. On a phone it was eating a third of the screen. */
  .gatebar { padding: 12px; margin-bottom: 8px; }
  .gatebar p { margin-bottom: 9px; font-size: 12.5px; }
}

/* Pre-launch. A stated condition, not an empty page. */
.prelaunch {
  flex: none; margin: 10px 16px 0; padding: 11px 15px;
  background: var(--card); border: 1px dashed var(--line); border-radius: var(--r-sm);
  font-size: 13px; color: var(--ink-2); box-shadow: var(--shadow-sm);
}
.prelaunch b { color: var(--ink); }
@media (max-width: 860px) { .prelaunch { margin: 8px 12px 0; font-size: 12.5px; } }

/* ── the numbers that matter, above everything ───────────────────────────────── */

/*
 * Market cap and what the treasury actually holds. These sit at the top of every channel
 * because they are the two things a holder opens the app to check, and burying them behind a
 * tab means the answer to "is this thing alive" costs a tap.
 *
 * A value the server could not read renders as an em dash, never as zero. "0 SOL in the dev
 * wallet" and "we could not reach the RPC" look identical to a reader and mean opposite things.
 */
.stats {
  flex: none; display: flex; gap: 10px; padding: 10px 16px 4px;
  overflow-x: auto; scrollbar-width: none;
}
.stats::-webkit-scrollbar { display: none; }
.stat {
  flex: none; min-width: 116px;
  background: var(--card); border-radius: var(--r-sm);
  padding: 9px 13px; box-shadow: var(--shadow-sm);
}
.stat .k {
  font-size: 10px; font-weight: 800; letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--ink-3); display: block; margin-bottom: 1px;
}
.stat .v { font-size: 17px; font-weight: 800; letter-spacing: -0.02em; }
.stat .v small { font-size: 11px; font-weight: 700; color: var(--ink-2); margin-left: 3px; }
.stat .d { font-size: 11px; font-weight: 700; }
.stat .d.down { color: #c2410c; }
.stat .d.up { color: #15803d; }
.stat.live .v { color: #15803d; }

@media (max-width: 860px) { .stats { padding: 8px 12px 2px; } .stat { min-width: 104px; padding: 8px 11px; } }
