
/* ═══ THE DOCK RESERVES ITS OWN SPACE ═══
   ⚠️ THE RAILS WERE RUNNING UNDERNEATH HER.
   Both rails size themselves as
     calc(100vh - var(--topnav-h) - var(--bottom-h) - 36px)
   and `--bottom-h` shipped as 0px — but the dock is 82px of
   position:fixed sitting across the bottom of every app page. So the
   last 82px of each rail lived behind her: on Command that was Settings
   and Config, the two you reach for when something needs fixing.
   Invisible, and the rail hides its scrollbar, so nothing said so.

   The token exists for exactly this. Setting it here, next to the thing
   that occupies the space, means a change to the dock's height cannot
   leave the rails wrong — which is what a token is for. */
:root{ --bottom-h:82px; }
/* ═══════════════════════════════════════════════════════════
   187 DARK KNIGHTS — MORTICIA DOCK
   Fixed bottom. Present on both sides of the site.
   Loads AFTER forge.css.
   ═══════════════════════════════════════════════════════════ */

/* ── THE DOCK ── */
.mor-dock{
  position:fixed;left:0;right:0;bottom:0;z-index:400;
  display:flex;justify-content:center;pointer-events:none;
  padding:0 18px 18px;
}
.mor-dock-bar{
  pointer-events:auto;cursor:pointer;
  display:flex;align-items:center;gap:14px;
  padding:11px 18px 11px 12px;border-radius:14px;
  max-width:520px;width:100%;
  background:
    linear-gradient(180deg,rgba(226,232,240,.10) 0%,rgba(255,255,255,0) 48%),
    radial-gradient(ellipse 80% 90% at 50% 120%,
      rgba(224,32,32,.30) 0%, transparent 72%),
    linear-gradient(180deg,#1b1d21 0%,#111316 60%,#0a0b0d 100%);
  border:1px solid rgba(170,180,195,.2);
  box-shadow:
    0 1px 0 rgba(226,232,240,.14) inset,
    0 -1px 0 rgba(0,0,0,.9) inset,
    0 -8px 32px rgba(0,0,0,.7),
    0 0 26px rgba(224,32,32,.14);
  transition:transform .2s cubic-bezier(.22,1,.36,1),
             box-shadow .22s, border-color .22s;
}
.mor-dock-bar:hover{
  transform:translateY(-3px);
  border-color:rgba(255,91,74,.42);
  box-shadow:
    0 1px 0 rgba(226,232,240,.2) inset,
    0 -1px 0 rgba(0,0,0,.9) inset,
    0 -10px 38px rgba(0,0,0,.75),
    0 0 44px rgba(224,32,32,.34);
}
.mor-dock.open .mor-dock-bar{transform:translateY(140%);opacity:0;
  pointer-events:none}

.mor-dock-av{
  position:relative;width:40px;height:40px;border-radius:50%;flex-shrink:0;
  overflow:hidden;background:#0d0e11;
  border:1px solid rgba(178,34,34,.5)}
.mor-dock-av img{width:100%;height:100%;object-fit:cover;
  object-position:center 18%}
.mor-dock-pulse{
  position:absolute;right:1px;bottom:1px;width:9px;height:9px;border-radius:50%;
  background:#3fa86a;border:2px solid #0a0b0d;
  box-shadow:0 0 8px rgba(63,168,106,.9)}

.mor-dock-id{text-align:left;min-width:0;flex-shrink:0}
.mor-dock-id b{display:block;font-family:'Oswald',sans-serif;font-weight:600;
  font-size:14px;letter-spacing:.5px;line-height:1.2}
.mor-dock-id small{font-family:'Share Tech Mono',monospace;font-size:8.5px;
  letter-spacing:1.4px;color:#545c68;text-transform:uppercase}
.mor-dock-state{color:#3fa86a}

/* ═══ SHE IS OFF ═══
   The green word and the green dot are the two things people read as
   "she's there". Both have to go, or the label says Offline while the
   avatar still wears a live badge and the bar still reads as alive —
   which is worse than either state on its own. `.mor-off` is set
   server-side in morticia-dock.php from the same three conditions
   api/morticia.php checks. */
.mor-off .mor-dock-state{color:#6b7280}
.mor-off .mor-dock-pulse,
.mor-off .mor-dot{
  background:#3a3f47;box-shadow:none}
.mor-off .mor-dock-av img,
.mor-off .mor-head-av img{filter:grayscale(1);opacity:.55}

/* The portrait keeps its colour — greying a standing figure reads as a
   broken image, not a status. The signals that say "live" go instead:
   the pulsing flare on her shoulder and the glow behind her. */
.mor-off .flare-live{animation:none;opacity:.25}
.mor-off .mor-figure-glow{opacity:.3}

.mor-dock-prompt{
  flex:1;text-align:left;font-family:'Rajdhani',sans-serif;font-size:13.5px;
  color:#6b7280;padding-left:6px;
  border-left:1px solid rgba(154,154,159,.14);margin-left:4px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mor-dock-chev{color:#B22222;font-size:11px;flex-shrink:0;
  transition:transform .2s}
.mor-dock-bar:hover .mor-dock-chev{transform:translateY(-2px)}

/* ── SCRIM ── */
.mor-scrim{
  position:fixed;inset:0;z-index:398;opacity:0;visibility:hidden;
  background:rgba(0,0,0,.62);backdrop-filter:blur(3px);
  transition:opacity .24s, visibility .24s}
.mor-scrim.open{opacity:1;visibility:visible}

/* ── THE PANEL ──
   Two columns: her on the left, the conversation on the right.
   Wider than a normal chat widget on purpose — she is meant to read
   as a person in the room, not a support bubble in the corner. */
.mor-panel{
  position:fixed;left:50%;bottom:18px;transform:translate(-50%,120%);
  z-index:401;
  width:min(880px,calc(100vw - 36px));
  height:min(600px,80vh);
  display:grid;grid-template-columns:290px 1fr;
  overflow:hidden;
  opacity:0;visibility:hidden;
  transition:transform .3s cubic-bezier(.22,1,.36,1),
             opacity .24s, visibility .3s;
}
.mor-panel.open{transform:translate(-50%,0);opacity:1;visibility:visible}

/* ── LEFT: HER ── */
.mor-figure{
  position:relative;overflow:hidden;
  border-right:1px solid rgba(139,26,26,.28);
  background:
    radial-gradient(ellipse 80% 50% at 50% 110%,
      rgba(224,32,32,.26) 0%, transparent 70%),
    linear-gradient(180deg,#14161a 0%,#0b0c0e 60%,#050506 100%);
  display:flex;align-items:flex-end;justify-content:center;
}
.mor-figure-glow{
  position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(ellipse 60% 45% at 50% 38%,
    rgba(224,32,32,.16),transparent 70%)}
.mor-figure-img{
  position:relative;z-index:1;
  width:100%;height:100%;object-fit:cover;object-position:center top;
  filter:drop-shadow(0 0 40px rgba(224,32,32,.3));
  /* fade her into the panel at the bottom so she doesn't sit on a hard
     edge — she should feel lit from below, not pasted on */
  -webkit-mask-image:linear-gradient(180deg,#000 0%,#000 62%,transparent 100%);
  mask-image:linear-gradient(180deg,#000 0%,#000 62%,transparent 100%);
}
.mor-figure-id{
  position:absolute;left:0;right:0;bottom:0;z-index:2;
  padding:18px 20px 20px;text-align:left;
  background:linear-gradient(180deg,transparent,rgba(5,5,6,.9) 46%)}
.mor-figure-id b{display:block;font-family:'Oswald',sans-serif;font-weight:600;
  font-size:22px;letter-spacing:.5px;line-height:1.1}
.mor-figure-id small{display:flex;align-items:center;gap:6px;margin-top:4px;
  font-family:'Share Tech Mono',monospace;font-size:9px;letter-spacing:1.8px;
  color:#8e96a1;text-transform:uppercase}
.mor-figure-tag{display:block;margin-top:9px;font-family:'Rajdhani',sans-serif;
  font-size:12.5px;color:#545c68;font-style:italic}

/* ── RIGHT: CONVERSATION ── */
.mor-side{display:flex;flex-direction:column;min-width:0;overflow:hidden}

.mor-head{
  display:flex;align-items:center;gap:12px;padding:14px 16px;
  border-bottom:1px solid rgba(139,26,26,.28);flex-shrink:0;
  background:linear-gradient(180deg,rgba(30,20,20,.9),rgba(10,11,13,.6))}
/* the small avatar is redundant once the portrait is showing */
.mor-panel .mor-head-av{display:none}
.mor-head-av{width:38px;height:38px;border-radius:50%;overflow:hidden;
  flex-shrink:0;background:#0d0e11;border:1px solid rgba(178,34,34,.5)}
.mor-head-av img{width:100%;height:100%;object-fit:cover;object-position:center 18%}
.mor-head-id{flex:1;min-width:0}
.mor-head-id b{display:block;font-family:'Oswald',sans-serif;font-weight:600;
  font-size:15px}
.mor-head-id small{font-family:'Share Tech Mono',monospace;font-size:8.5px;
  letter-spacing:1.5px;color:#545c68;text-transform:uppercase;
  display:flex;align-items:center;gap:5px}
.mor-dot{width:6px;height:6px;border-radius:50%;background:#3fa86a;
  box-shadow:0 0 7px rgba(63,168,106,.9)}
.mor-x{background:none;border:none;color:#545c68;font-size:14px;cursor:pointer;
  padding:6px 8px;border-radius:6px;transition:.15s}
.mor-x:hover{color:#e2e8f0;background:rgba(139,26,26,.2)}

/* ── LOG ── */
.mor-log{flex:1;overflow-y:auto;padding:16px;display:flex;
  flex-direction:column;gap:12px;scrollbar-width:thin;
  scrollbar-color:rgba(139,26,26,.5) transparent}
.mor-log::-webkit-scrollbar{width:6px}
.mor-log::-webkit-scrollbar-thumb{background:rgba(139,26,26,.5);border-radius:3px}

.mor-msg{max-width:82%;padding:11px 14px;border-radius:12px;font-size:14px;
  line-height:1.6;font-family:'Rajdhani',sans-serif;white-space:pre-wrap;
  word-wrap:break-word}
.mor-her{align-self:flex-start;color:#c8cdd4;
  background:linear-gradient(160deg,rgba(28,30,34,.9),rgba(14,15,18,.8));
  border:1px solid rgba(154,154,159,.14);border-bottom-left-radius:4px}
.mor-you{align-self:flex-end;color:#fff0ee;
  background:linear-gradient(160deg,#8f1d1d,#5e1010);
  border:1px solid rgba(255,120,100,.3);border-bottom-right-radius:4px}
.mor-sys{align-self:center;font-family:'Share Tech Mono',monospace;
  font-size:10px;letter-spacing:1px;color:#454b54;text-transform:uppercase;
  background:none;border:none;padding:4px}

/* typing */
.mor-typing{align-self:flex-start;display:flex;gap:4px;padding:13px 16px;
  border-radius:12px;border-bottom-left-radius:4px;
  background:linear-gradient(160deg,rgba(28,30,34,.9),rgba(14,15,18,.8));
  border:1px solid rgba(154,154,159,.14)}
.mor-typing span{width:6px;height:6px;border-radius:50%;background:#B22222;
  animation:morType 1.3s ease-in-out infinite}
.mor-typing span:nth-child(2){animation-delay:.18s}
.mor-typing span:nth-child(3){animation-delay:.36s}
@keyframes morType{0%,60%,100%{opacity:.25;transform:translateY(0)}
                   30%{opacity:1;transform:translateY(-4px)}}

/* ── FOOT ── */
.mor-foot{display:flex;gap:9px;padding:13px 14px;flex-shrink:0;
  border-top:1px solid rgba(139,26,26,.22);
  background:linear-gradient(180deg,rgba(10,11,13,.5),rgba(5,5,6,.85))}
.mor-input{flex:1;background:#0d0e11;border:1px solid rgba(154,154,159,.18);
  color:#e2e8f0;font-family:'Rajdhani',sans-serif;font-size:14.5px;
  padding:11px 14px;border-radius:9px;outline:none;transition:border-color .15s}
.mor-input:focus{border-color:rgba(255,91,74,.5)}
.mor-send{padding:11px 20px !important;font-size:12px !important}
.mor-signin{flex:1;text-align:center;text-decoration:none;padding:12px !important}

/* ── NARROW: drop the portrait ──
   Below ~760px the portrait column costs more than it gives — it would
   squeeze the conversation into a sliver. Her small avatar comes back
   in the header so she still has a face. */
@media(max-width:760px){
  .mor-panel{grid-template-columns:1fr;width:min(520px,calc(100vw - 36px));
    height:min(620px,78vh)}
  .mor-figure{display:none}
  .mor-panel .mor-head-av{display:block}
}

/* ── MOBILE: collapse the dock to just the skull ── */
@media(max-width:620px){
  .mor-dock{justify-content:flex-end;padding:0 14px 14px}
  .mor-dock-bar{width:auto;padding:10px;border-radius:50%}
  .mor-dock-id,.mor-dock-prompt,.mor-dock-chev{display:none}
  .mor-panel{bottom:0;left:0;transform:translateY(100%);
    width:100vw;height:88vh;border-radius:16px 16px 0 0}
  .mor-panel.open{transform:translateY(0)}
}

/* Keep page content clear of the dock */
body{padding-bottom:84px}
@media(max-width:620px){body{padding-bottom:0}}


/* ═══════════════════════════════════════════════════════════
   DOCK PULSE — the count, and the lines she narrates
   Appended after the media queries above on purpose: these
   selectors are new, and the two overrides here (bar width,
   count on the collapsed dock) must land AFTER the rules they
   are widening.
   ═══════════════════════════════════════════════════════════ */

/* A little wider now it carries a count as well as the prompt. */
.mor-dock-bar{max-width:560px}

.mor-dock-count{
  flex-shrink:0;min-width:22px;height:22px;padding:0 7px;
  display:inline-flex;align-items:center;justify-content:center;
  font-family:'Oswald',sans-serif;font-weight:600;font-size:12px;
  letter-spacing:.4px;line-height:1;color:#fff0ee;
  border-radius:11px;
  background:linear-gradient(160deg,#b81f1f,#7a0f0f);
  border:1px solid rgba(255,91,74,.5);
  box-shadow:0 0 12px rgba(224,32,32,.55);
}
.mor-dock-count.pop{animation:mor-count-pop .34s cubic-bezier(.22,1,.36,1)}
@keyframes mor-count-pop{
  0%{transform:scale(.6)} 60%{transform:scale(1.18)} 100%{transform:scale(1)}
}
@media(prefers-reduced-motion:reduce){
  .mor-dock-count.pop{animation:none}
}

/* ⚠️ The count SURVIVES the mobile collapse. Everything else on the
   bar is hidden below 620px, but hiding the count would defeat the
   entire point of putting it on a minimised dock — on a phone it is
   the only unread indicator there is. It becomes a corner badge on
   the skull instead. */
@media(max-width:620px){
  .mor-dock-bar{position:relative}
  .mor-dock-count{
    position:absolute;top:-3px;right:-3px;
    min-width:20px;height:20px;padding:0 6px;font-size:11px;
  }
}

/* Lines she narrates. Still one of her bubbles — same voice, same
   side — with a crimson edge so an arrival is distinguishable from
   something she said in conversation. */
.mor-alert{border-left:2px solid rgba(224,32,32,.85)}
.mor-alert a{color:#ff8a7a;text-decoration:none;
  border-bottom:1px solid rgba(255,138,122,.35)}
.mor-alert a:hover{color:#ffb3a7;border-bottom-color:rgba(255,179,167,.6)}


/* ── THREADS STRIP ──
   Replaced the floating chat-tab bar. Horizontal, scrolls sideways
   rather than wrapping, so five threads never push the conversation
   down the panel. */
.mor-threads{
  display:flex;gap:8px;padding:10px 12px;overflow-x:auto;
  border-bottom:1px solid rgba(154,154,159,.14);
  scrollbar-width:none;
}
.mor-threads::-webkit-scrollbar{display:none}

.mor-thread{
  flex-shrink:0;display:flex;align-items:center;gap:7px;
  padding:5px 10px 5px 5px;border-radius:20px;text-decoration:none;
  background:linear-gradient(160deg,rgba(28,30,34,.9),rgba(14,15,18,.8));
  border:1px solid rgba(154,154,159,.16);
  transition:border-color .18s, transform .18s;
}
.mor-thread:hover{border-color:rgba(255,91,74,.42);transform:translateY(-1px)}
.mor-thread-av{
  width:24px;height:24px;border-radius:50%;flex-shrink:0;
  background:#0d0e11;background-size:cover;background-position:center;
  border:1px solid rgba(178,34,34,.45);
  display:flex;align-items:center;justify-content:center;
  font-family:'Oswald',sans-serif;font-size:10px;color:#8b93a1;
}
.mor-thread-name{
  font-family:'Rajdhani',sans-serif;font-size:13px;color:#c8cdd4;
  white-space:nowrap;max-width:110px;overflow:hidden;text-overflow:ellipsis;
}
.mor-thread-n{
  min-width:17px;height:17px;padding:0 5px;border-radius:9px;
  display:inline-flex;align-items:center;justify-content:center;
  font-family:'Oswald',sans-serif;font-size:10px;color:#fff0ee;
  background:linear-gradient(160deg,#b81f1f,#7a0f0f);
}


/* ═══════════════════════════════════════════════════════════
   THE DOCK RESERVES SPACE — and 100vh does not know that
   ═══════════════════════════════════════════════════════════
   `body{padding-bottom:84px}` above keeps normal page flow clear of
   the dock. A panel sized with `calc(100vh - N)` ignores that
   entirely — vh is the WHOLE viewport, padding included — so it
   runs its last 84px underneath her.

   Messages is where this bites: `.messages-wrap` in deck.css is
   `calc(100vh - 260px)`, which put the message composer 53px under
   the dock. You could see the Send button behind her bar and not
   click it. Reported 2026-09-09, measured, fixed here.

   ⚠️ Any NEW full-height panel must subtract var(--dock-h) too.
   The rails get away with it because they scroll internally; a
   composer pinned to the bottom of its own panel does not. */
:root{ --dock-h:84px }

.messages-wrap{
  height:calc(100vh - 260px - var(--dock-h));
  /* min-height has to come down with it, or a short viewport puts
     the composer straight back under her. */
  min-height:356px;
}

/* Below 620px the bar collapses to just the skull bottom-RIGHT, and
   body padding drops to 0 — but the composer's Send button is bottom
   right too, so they land on each other. Reserve the circle. */
@media(max-width:620px){
  :root{ --dock-h:72px }
}


/* ── DISMISSING AN ARRIVAL ──
   Notifications stack up in the log; without a way to clear one, a
   quiet morning leaves you scrolling past yesterday to reach her.
   Only .mor-alert gets this — a reply she actually said in
   conversation is not something you close. */
.mor-alert{ position:relative; padding-right:26px }

.mor-alert-x{
  position:absolute; top:6px; right:6px;
  width:18px; height:18px; padding:0; line-height:1;
  display:flex; align-items:center; justify-content:center;
  font-size:10px; cursor:pointer;
  color:#5b6472; background:transparent;
  border:1px solid transparent; border-radius:50%;
  /* Sits quiet until the bubble is hovered. A row of hard ✕ marks
     down the log reads as clutter and pulls the eye off the text
     they are attached to. */
  opacity:0; transition:opacity .15s, color .15s, background .15s;
}
.mor-alert:hover .mor-alert-x,
.mor-alert-x:focus{ opacity:1 }
.mor-alert-x:hover{ color:#ffb3a7; background:rgba(139,26,26,.35) }
.mor-alert-x:focus-visible{ outline:1px solid rgba(255,91,74,.6); opacity:1 }

/* Touch has no hover — on a phone the control has to be there. */
@media(hover:none){ .mor-alert-x{ opacity:.75 } }

.mor-msg.out{
  opacity:0; transform:translateX(8px);
  transition:opacity .18s ease, transform .18s ease;
}
@media(prefers-reduced-motion:reduce){
  .mor-msg.out{ transition:none }
}


/* ── HIDING A THREAD CHIP ──
   The chip is now a wrapper holding the link and its ✕ as siblings,
   so the flex sizing that used to sit on .mor-thread moves out here.
   The link keeps its own look; the wrapper only positions the ✕. */
.mor-thread-chip{ position:relative; flex-shrink:0; display:inline-flex }

.mor-thread-x{
  position:absolute; top:-4px; right:-4px;
  width:16px; height:16px; padding:0; line-height:1;
  display:flex; align-items:center; justify-content:center;
  font-size:9px; cursor:pointer;
  color:#8b93a1; background:#15171b;
  border:1px solid rgba(154,154,159,.28); border-radius:50%;
  opacity:0; transition:opacity .15s, color .15s, border-color .15s;
}
.mor-thread-chip:hover .mor-thread-x,
.mor-thread-x:focus{ opacity:1 }
.mor-thread-x:hover{ color:#ffb3a7; border-color:rgba(255,91,74,.55) }
.mor-thread-x:focus-visible{ outline:1px solid rgba(255,91,74,.6); opacity:1 }

/* No hover on touch — and the strip is tight there, so keep it faint
   rather than invisible. */
@media(hover:none){ .mor-thread-x{ opacity:.7 } }

/* The strip needs a little headroom now the ✕ overhangs the top. */
.mor-threads{ padding-top:14px }
