/* ============================================================
   COMMS: the alert band, the channel rail and the console
   Sized by different rules, hence two elements. The band is bounded
   (MAX_ALERTS rows of one line), so its tallest state is known and cannot push
   the tracker beside it. The console is a modal that takes the screen for
   reading. Neither is a scrollback.
   ============================================================ */

:root{
  /* The band's reservation in three numbers. `--alert-n` is read back by
     maxAlerts() in engine/comms.js, so the DOM cap and the space agree.
     Restated per breakpoint in responsive.css only. */
  --alert-h:30px;
  --alert-gap:6px;
  --alert-n:3;
  --chip:34px;            /* the rail's tap target, and its own floor */
  /* The colour a channel is. Alerts, chips, tabs and rows all take their
     accent from these, so a text never looks like an email anywhere. */
  --ch-mail:#ff6f91;
  --ch-text:#5ad48a;
  --ch-chat:#b48cff;
  --ch-log:#8d9bb5;
  --ch-calls:#ffb347;
}

/* ---------- the band and the rail ---------- */
/* Bottom-left on a desktop, clear of #keyhints; above the canvas and below
   HUD (5), controls (6), dialogue (20), the call (25) and panels (26). */
#comms{position:absolute;bottom:calc(48px + env(safe-area-inset-bottom));
  left:calc(12px + env(safe-area-inset-left));z-index:3;
  width:min(392px,34vw);pointer-events:none;
  display:flex;flex-direction:column;align-items:flex-start;gap:8px;
  transition:top .25s ease,bottom .25s ease,left .25s ease,width .25s ease}

/* The alerts stand on their own, high and centred between the HUD's two cards:
   in sight, and off the player. Bare text on a soft shadow, not a capsule — a
   glance, with the record one tap away. The band hangs from the top and grows
   downward, newest last. If the player walks up under it, it drops to the
   foot of the screen (`.low`, Comms.dodge()) and comes back once they are
   clear. A retiring alert leaves the flow at once and fades where it stood
   (Comms.retire() pins `top` first), so the band's height is exactly the
   standing alerts. */
#alerts{position:absolute;z-index:3;left:50%;transform:translateX(-50%);
  top:calc(14px + env(safe-area-inset-top));
  width:min(560px,calc(100vw - 500px));
  display:flex;flex-direction:column;gap:var(--alert-gap);
  align-items:center;pointer-events:none}
#alerts.low{top:auto;bottom:calc(96px + env(safe-area-inset-bottom))}

.alert{pointer-events:auto;cursor:pointer;text-align:center;max-width:100%;
  display:inline-grid;grid-template-columns:auto auto;align-items:center;justify-content:center;gap:7px;
  min-height:var(--alert-h);padding:2px 14px;
  background:radial-gradient(closest-side,rgba(8,10,18,.42),rgba(8,10,18,0));border:0;border-radius:0;
  font:inherit;font-size:13px;font-weight:600;color:#fff;
  text-shadow:0 1px 2px rgba(0,0,0,.95),0 0 10px rgba(0,0,0,.7);
  animation:alertIn .26s ease}
.alert:hover{color:var(--acc,var(--brand))}
.alert:active{transform:translateY(1px)}
.alert:focus-visible{outline:2px solid var(--hold);outline-offset:2px}
.a-e{font-size:15px;line-height:1;filter:drop-shadow(0 1px 2px rgba(0,0,0,.8))}
/* ONE LINE, ALWAYS. The sentence is in the record; this is the glance. A wrap
   here is what turned the old band into a variable-height slab. */
.a-t{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.a-t b{color:var(--acc,var(--brand));font-weight:600}
/* The exception: a how-to tip (UI.toast(…, 'tip')) wraps, up to four lines. */
.alert.tip .a-t{white-space:normal;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:4;line-height:1.35}
.alert.tip{padding-top:4px;padding-bottom:4px;font-weight:500}
/* Out of the flex line the moment it starts going, so the band is the height of
   what is standing. `top` is written by Comms.retire() from the row it was in. */
/* A folded alert pulses in place: the same row saying something newer. */
.alert.folded{animation:alertFold .32s ease}
@keyframes alertFold{
  0%{transform:none}
  35%{transform:translateX(3px) scale(1.012)}
  100%{transform:none}
}
@media(prefers-reduced-motion:reduce){.alert.folded{animation:none}}
.alert.gone{position:absolute;left:0;right:0;margin:auto;
  opacity:0;transform:translateY(-6px);pointer-events:none;
  transition:opacity .22s ease,transform .24s ease}
/* Severity is the colour of the words, not a box round them. */
.alert.bad .a-t{color:#ffc2c8}
.alert.gold .a-t{color:#ffe7b0}
.alert.good .a-t{color:#c8f5d8}
@keyframes alertIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}

.alert.ch-mail{--acc:var(--ch-mail)} .alert.ch-text{--acc:var(--ch-text)}
.alert.ch-chat{--acc:var(--ch-chat)} .alert.ch-log{--acc:var(--ch-log)}
.alert.ch-calls{--acc:var(--ch-calls)}

/* The rail: what is waiting per channel, fixed size, always reachable. */
#chRail{display:flex;gap:6px;pointer-events:auto;
  padding:4px;border-radius:999px;
  background:rgba(13,17,23,.72);border:1px solid var(--line);
  backdrop-filter:blur(6px)}
.chip,#cmOne{position:relative;pointer-events:auto;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;gap:5px;
  min-width:var(--chip);min-height:var(--chip);padding:0 8px;
  background:transparent;border:1px solid transparent;border-radius:999px;
  color:var(--dim);font-family:var(--mono);font-size:11px;letter-spacing:.06em}
.chip:hover,#cmOne:hover{background:rgba(255,255,255,.06);color:var(--text)}
.chip:active,#cmOne:active{transform:scale(.94)}
.chip:focus-visible,#cmOne:focus-visible{outline:2px solid var(--hold);outline-offset:2px}
.chip-e{font-size:16px;line-height:1}
/* A count that is there is loud and a count that is not is absent — not a zero,
   which reads as a number you are meant to do something about. */
.chip-n{font-weight:700;color:var(--text)}
.chip-n:empty{display:none}
.chip[data-ch=mail]{--acc:var(--ch-mail)} .chip[data-ch=text]{--acc:var(--ch-text)}
.chip[data-ch=chat]{--acc:var(--ch-chat)} .chip[data-ch=log]{--acc:var(--ch-log)}
.chip[data-ch=calls]{--acc:var(--ch-calls)}
.chip.lit,#cmOne.lit{border-color:var(--acc,var(--brand));color:var(--text);
  background:rgba(255,255,255,.05)}
.chip.lit .chip-e,#cmOne.lit .chip-e{filter:none}
.chip:not(.lit) .chip-e,#cmOne:not(.lit) .chip-e{opacity:.62}
/* The phone's single chip. Always in the markup (sync() writes both, so a
   rotation shows a current number); shown only by the mobile query. */
#cmOne{display:none;background:rgba(13,17,23,.78);border-color:var(--line);
  box-shadow:0 10px 26px -14px #000}
#cmOne.lit{border-color:var(--hold)}

@media(prefers-reduced-motion:reduce){
  #comms{transition:none}
  .alert{animation:none}
  .chip:active,#cmOne:active{transform:none}
}

/* ---------- the console ---------- */
#inbox{position:absolute;inset:0;z-index:26;display:none;align-items:center;justify-content:center;
  background:rgba(6,9,14,.88);backdrop-filter:blur(3px)}
#inbox.on{display:flex;animation:fadeIn .2s ease}
.cm{width:min(1040px,97vw);height:min(760px,100%);display:flex;flex-direction:column;overflow:hidden}

/* The channel strip. One row that scrolls sideways on a phone and wraps
   nowhere — the selected tab is scrolled back into view by tabs(), for the
   same reason the portal's own strip does it. */
.cm-tabs{display:flex;gap:4px;padding:10px 12px;border-bottom:1px dashed var(--line);
  overflow-x:auto;scrollbar-width:none;flex:0 0 auto}
.cm-tabs::-webkit-scrollbar{height:0}
.cm-tab{display:inline-flex;align-items:center;gap:7px;flex:0 0 auto;
  background:transparent;border:1px solid transparent;border-radius:8px;
  padding:7px 11px;min-height:36px;cursor:pointer;
  font-family:var(--mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--dim)}
.cm-tab:hover{background:rgba(255,255,255,.05);color:var(--text)}
.cm-tab.on{background:var(--panel2);border-color:var(--acc,var(--brand));color:var(--text)}
.cm-tab:focus-visible{outline:2px solid var(--hold);outline-offset:2px}
.cm-tab .t-i{font-size:15px}
.cm-tab .t-c{font-family:var(--mono);font-size:10px;font-weight:700;
  padding:1px 6px;border-radius:999px;background:var(--acc,var(--brand));color:#1a1026}
#inbox.ch-mail{--acc:var(--ch-mail)} #inbox.ch-text{--acc:var(--ch-text)}
#inbox.ch-chat{--acc:var(--ch-chat)} #inbox.ch-log{--acc:var(--ch-log)}
#inbox.ch-calls{--acc:var(--ch-calls)}
.cm-tabs .cm-tab[class*=ch-]{}
.cm-tab:nth-child(1){--acc:var(--ch-mail)} .cm-tab:nth-child(2){--acc:var(--ch-text)}
.cm-tab:nth-child(3){--acc:var(--ch-chat)} .cm-tab:nth-child(4){--acc:var(--ch-log)}
.cm-tab:nth-child(5){--acc:var(--ch-calls)}

/* Two panes on a desktop. The list is a third of it, because a subject line is
   short and a body is not. */
.cm-panes{flex:1;display:grid;grid-template-columns:minmax(220px,32%) 1fr;min-height:0}
.cm-list{border-right:1px dashed var(--line);padding:8px;display:flex;flex-direction:column;gap:4px}
.cm-read{padding:16px 18px;min-width:0}
/* A ledger has nothing to choose between, so it has no list. Said in CSS
   rather than by emptying the pane, or the border and the padding stay. */
#inbox.ledger .cm-list{display:none}
#inbox.ledger .cm-panes{grid-template-columns:1fr}
.cm-empty{color:var(--dim);font-size:13px;padding:18px 4px;line-height:1.6}

/* a row in the list */
.cm-row{display:grid;grid-template-columns:auto 1fr auto;align-items:start;gap:9px;
  width:100%;text-align:left;cursor:pointer;
  background:transparent;border:1px solid transparent;border-radius:9px;
  padding:9px 10px;min-height:52px;color:var(--text);font:inherit}
.cm-row:hover{background:rgba(255,255,255,.04)}
.cm-row.on{background:var(--panel2);border-color:var(--acc,var(--brand))}
.cm-row:focus-visible{outline:2px solid var(--hold);outline-offset:2px}
.cm-row .r-e{font-size:18px;line-height:1.2}
.cm-row .r-b{min-width:0;display:block}
.cm-row .r-h{display:flex;gap:8px;align-items:baseline;justify-content:space-between}
.cm-row .r-h b{font-size:12.5px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cm-row .r-h i{font-family:var(--mono);font-size:10px;color:var(--dim);font-style:normal;flex:0 0 auto}
/* Two lines of gist and no more: this is a list, and a row that grows to fit a
   quoted reply chain is a list with one thing in it. */
.cm-row .r-s{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
  overflow:hidden;font-size:11.5px;color:var(--dim);line-height:1.45;margin-top:2px}
.cm-row .r-n{font-family:var(--mono);font-size:10px;color:var(--dim);
  border:1px solid var(--line);border-radius:999px;padding:1px 6px}
/* Unread is a filled bar down the left edge, not a tint: at this weight a tint
   reads as "slightly less transparent" rather than as "you have not read
   this" — the same lesson the job tracker's pin learned. */
.cm-row.new{border-left-color:var(--acc,var(--brand));
  box-shadow:inset 3px 0 0 var(--acc,var(--brand))}
.cm-row.new .r-h b{color:var(--text)}
.cm-row:not(.new) .r-h b{color:var(--dim)}

/* mail */
.cm-mail{border-bottom:1px dashed var(--line);padding-bottom:16px;margin-bottom:16px}
.cm-mail:last-child{border-bottom:0;margin-bottom:0}
.cm-mail h4{font-size:15px;font-weight:600;line-height:1.35}
.cm-mail .m-from{font-family:var(--mono);font-size:11px;color:var(--ch-mail);margin-top:4px;word-break:break-word}
.cm-mail .m-when,.cm-call .m-when{font-family:var(--mono);font-size:10px;color:var(--dim);
  letter-spacing:.1em;text-transform:uppercase;margin-top:3px}
/* Mail in this building is paragraphs, quoted replies and a signature. It is
   read as written, which is the entire joke in about half of it. */
.cm-mail .m-body{font-size:13px;line-height:1.72;margin-top:12px;color:var(--text);
  white-space:normal;word-break:break-word}

/* texts and chat: bubbles, because a conversation has two sides */
.cm-talk{display:flex;flex-direction:column;gap:9px}
.bub{max-width:min(78%,520px);align-self:flex-start}
.bub.me{align-self:flex-end;text-align:right}
.b-who{font-family:var(--mono);font-size:10px;color:var(--dim);letter-spacing:.08em;
  margin-bottom:3px;display:flex;gap:6px;align-items:center}
.bub.me .b-who{justify-content:flex-end}
.b-who i{font-style:normal;opacity:.7}
.b-e{font-size:14px}
.b-t{display:inline-block;text-align:left;
  background:var(--panel2);border:1px solid var(--line);border-radius:14px 14px 14px 4px;
  padding:9px 13px;font-size:13px;line-height:1.6;word-break:break-word}
.bub.me .b-t{background:rgba(255,111,145,.14);border-color:var(--ch-mail);
  border-radius:14px 14px 4px 14px}
#inbox.ch-text .b-t{border-radius:16px 16px 16px 5px}
#inbox.ch-chat .b-t{border-radius:4px 12px 12px 12px}

/* the call transcript */
.cm-call{border-bottom:1px dashed var(--line);padding-bottom:14px;margin-bottom:14px}
.cm-call:last-child{border-bottom:0;margin-bottom:0}
.cm-call h4{font-size:14px;font-weight:600}
.cm-call.good h4{color:var(--good)} .cm-call.bad h4{color:var(--bad)}
.c-lines{list-style:none;margin-top:10px;display:flex;flex-direction:column;gap:3px}
.c-lines li{font-family:var(--mono);font-size:11px;line-height:1.6;color:var(--dim);
  padding-left:12px;border-left:2px solid var(--line)}

/* the ledger */
.cm-ledger{display:flex;flex-direction:column}
.l-hour{font-family:var(--mono);font-size:10px;letter-spacing:.2em;color:var(--dim);
  text-transform:uppercase;padding:12px 0 6px;border-bottom:1px dashed var(--line);margin-bottom:6px}
.cm-ledger .l-hour:first-child{padding-top:0}
.l-row{display:grid;grid-template-columns:44px 20px 1fr;gap:8px;align-items:baseline;
  padding:5px 6px;border-radius:7px;font-size:12.5px;line-height:1.55}
.l-row:hover{background:rgba(255,255,255,.03)}
.l-t{font-family:var(--mono);font-size:10.5px;color:var(--dim)}
.l-e{font-size:14px;line-height:1.2}
.l-b b{color:var(--text);font-weight:600}
.l-row.bad{color:#ffc8c4} .l-row.bad .l-b b{color:var(--bad)}
.l-row.good{color:#c6f0d8} .l-row.good .l-b b{color:var(--good)}
.l-row.gold{color:#ffe3b8} .l-row.gold .l-b b{color:var(--hold)}

/* the answer button, where a message is waiting on one */
.cm-answer{margin-top:14px;display:inline-flex}
.bub .cm-answer{margin-top:8px}

/* the foot: a count, and on a phone the way back to the list */
.cm-foot{flex:0 0 auto;display:flex;align-items:center;gap:10px;
  padding:9px 12px;border-top:1px dashed var(--line);min-height:44px}
.cm-count{font-family:var(--mono);font-size:10px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--dim)}
/* Nothing to go back FROM on a desktop, where both panes are on screen. */
.cm-back{display:none}
/* A tip never sits over a conversation, and never runs the width of a
   landscape phone. */
body.talking .alert.tip{visibility:hidden;opacity:0!important;pointer-events:none}
@media(orientation:landscape) and (max-height:520px){
  /* A landscape phone: the tip keeps to the left column, clear of the player
     in the middle of the screen, and stops at three lines (the whole of it is
     in the Log, one tap away). */
  #comms .alert.tip{max-width:min(40vw,320px);font-size:11px}
  .alert.tip .a-t{-webkit-line-clamp:3;line-height:1.3}
}
@media(orientation:portrait) and (max-width:600px){
  /* An upright phone: four lines of how-to across the whole width was a slab of
     text over the world. Two short lines, on a darker ground so they read; the
     rest of it is in the Log, one tap away. */
  #alerts .alert.tip{max-width:min(86vw,330px);font-size:11px;padding:4px 10px;
    background:radial-gradient(closest-side,rgba(8,10,18,.62),rgba(8,10,18,.2))}
  .alert.tip .a-t{-webkit-line-clamp:2;line-height:1.3}
}
