/*
 * Toast notifications
 * ------------------------------------------------------------------
 * STACKING POLICY lives in client/js/toast.js; this file paints what it
 * decides. Three things here exist only because of that policy:
 *   .toast__count      the "x3" badge on a coalesced card
 *   .toast-overflow    the single row standing in for everything the
 *                      visible cap is holding back. It is a BUTTON, and
 *                      it must never read as decoration - it is the only
 *                      on-screen evidence that suppressed toasts exist.
 *   [data-severity]    tier styling. 3 (PermissionRequest) is blocking
 *                      and is painted as such; it is also cap-exempt in
 *                      the JS, so it can never be behind the overflow row.
 * ------------------------------------------------------------------
 * THE CARD IS CHROME, THE STEM IS DATA. Everything structural here - the
 * ground, the hairline, the three text tiers, the kind glyphs - now reads
 * the --oc-* military terminal palette, the same one the shared modal
 * chassis in styles.css and terminal-carnivore.css read, so a toast looks
 * the same whatever session theme is on. The one thing that does NOT is
 * the left stem, which carries --toast-accent: the server bakes a hex onto
 * each Toast.color field from that session's own theme and the client
 * sets it inline as `style="--toast-accent: <color>"`, so it is not a
 * theme choice, it is which session this card is about. A card with
 * nothing baked falls back to --oc-orange. THE TITLE USED TO CARRY IT TOO
 * and was pulled back to --oc-text-primary (issue #652): a baked accent as
 * 13px bold TEXT is normal-size for WCAG, not the 3:1 non-text stem, and
 * failed 4.5:1 on 7 of 40 themes same-theme and as low as ~1:1 cross-theme
 * (a session pinned to theme X shown while the global theme is Y, so the
 * baked hex and the ground it sits on come from two different manifests).
 * No theme-data edit can fix a cross-theme pair; only reading a token that
 * resolves from whichever theme is actually on screen can.
 *
 * Kind-specific tint (Stop / PermissionRequest / Notification) layered
 * via data-kind selector. Mobile-responsive: full width on narrow
 * viewports, otherwise fixed-width column top-right.
 */

.toast-container {
  /* ABOVE THE HEADER, BELOW MODALS - and 900 was neither.
   *
   * The old value put toasts UNDER `.header` (z-index 1000), and the two
   * overlap: the header is `--header-h` tall (80px measured at desktop)
   * while this container started at a hardcoded 60px, so the header
   * painted over the top 20px of every toast. That strip is exactly
   * where `.toast__dismiss` sits, which made the close button
   * unclickable - `elementFromPoint` on its own centre returned the
   * header, not the button. A notification the user cannot dismiss is
   * worse than no notification.
   *
   * The original comment reasoned "site modals are in the 1000-range, so
   * pick 900 to sit under them". The premise was out of date: modals
   * here are 9998/9999, and 1000 is CHROME. A toast must outrank chrome
   * and yield to modals, which is what 1100 does.
   *
   * `top` STAYS 60px and is deliberately NOT derived from --header-h.
   * That was tried: the token resolves to 62px here while the rendered
   * header measures 80px, because the home view carries a subheader row
   * the token does not account for. Deriving from it would swap one
   * wrong number for another that merely looks principled. Once the
   * toast paints ABOVE the header the overlap is harmless, which is why
   * the z-index is the fix and the offset is not. */
  position: fixed;
  top: 60px;
  right: calc(16px + var(--oc-toast-clear-right, 0px));
  z-index: 1100;
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 360px;
  max-width: calc(100vw - 32px);
  pointer-events: none; /* the container itself doesn't catch clicks */
}

.toast {
  /* Each toast catches its own clicks (dismiss button etc.) */
  pointer-events: auto;

  /* The per-session accent. Set inline by ToastManager from toast.color,
     which the server bakes at record time from the SAME source
     client/js/theme-navigation.js reads for a session's pinned theme
     (src/core/session_manager.py resolve_project_theme() falls back to
     get_pinned_theme() whenever no project-level .cc.theme override
     exists, which is the ordinary case) - so this already IS "that
     session's theme colour", not a second theme concept. Falls back to
     --oc-orange for a session with no pin, which keeps the card in the
     same palette as the chrome around it. */
  --toast-accent: var(--oc-orange);

  background: var(--oc-ground-raised);
  color: var(--oc-text-primary);
  border: 1px solid var(--oc-line-panel);
  /* A STEM, NOT A BAR, AND THE CARD DID NOT MOVE. The 5px edge was the
     thickest accent in the app; 2px is what the sidebar's selected row
     already uses for the same job. Every box here is border-box, so the
     card's outer width is fixed by the container's 360px and only the
     CONTENT box would have grown - by the 3px the border gave up - which
     rewraps the body and therefore changes the height too. The 3px went
     into the left padding instead: 5 + 14 and 2 + 17 both leave a content
     box of 360 - 1 - 36 - 19 = 304px, so the card is identical to the
     pixel. Do not "tidy" 17px back to 14px. */
  border-left: 2px solid var(--toast-accent);
  border-radius: var(--oc-radius-sm);
  box-shadow: 0 4px 12px var(--oc-shadow-lift);
  padding: 12px 36px 12px 17px; /* extra right padding for the × */
  position: relative;
  font-family: var(--font-mono, 'SF Mono', monospace);
  font-size: 13px;
  line-height: 1.4;

  /* Slide-in animation. Replaced with .toast--dismissing for fade-out. */
  transform: translateX(0);
  opacity: 1;
  transition: transform 200ms ease-out, opacity 200ms ease-out;
}

/* THE CARD, NOT JUST ITS EDGE - but ONLY when a session actually has its
   own theme. `[data-themed="1"]` is set by client/js/toast.js in the
   SAME branch that bakes --toast-accent from a real toast.color, which
   `.toast` above cannot key on by itself: that variable always resolves
   to something, a baked colour or the plain CSS fallback to whatever
   theme is currently on screen, so a rule reading the variable alone
   could not tell "this session is themed" apart from "nothing was ever
   baked here" - and would have painted the SAME wash on every unthemed
   card too, which breaks "a session with no pin keeps today's look".
   The mix is weak enough that --oc-text-primary / --oc-text-secondary
   stay legible over it - a tint, not a repaint. */
.toast[data-themed="1"] {
  background: color-mix(in srgb, var(--toast-accent) 7%, var(--oc-ground-raised));
  border-color: color-mix(in srgb, var(--toast-accent) 30%, var(--oc-line-rule));
}

/* Entry animation - start off-screen right, slide to rest. */
.toast--entering {
  transform: translateX(110%);
  opacity: 0;
}

/* Exit animation - fade + slide. */
.toast--dismissing {
  transform: translateX(20%);
  opacity: 0;
}

.toast__title {
  font-weight: bold;
  margin: 0 0 4px 0;
  color: var(--oc-text-primary);
}

/* WHICH SESSION THIS TOAST IS ABOUT.
   Quieter than the title and quieter than the body: it is context, not
   the message. It still has to be a legible box - a name nobody can see
   is not a name - so it gets a real font-size and its own line rather
   than being tucked into the title's row. */
.toast__session {
  display: block;
  margin: 0 0 4px 0;
  color: var(--oc-text-secondary);
  font-size: 11px;
  opacity: 0.85;
  word-break: break-word;
}

/* The could-not-name case reads as an admission rather than as a name.
   Italic is the only cue, deliberately: the text already says it. */
.toast__session[data-unknown="1"] {
  font-style: italic;
}

/* client/js/toast.js only renders <button> here when there is somewhere
   for it to switch TO, so this selector is exactly the clickable case -
   there is no bare `button {}` reset left anywhere in this codebase (see
   styles.css) to fight, but the user-agent stylesheet still is, hence
   the full reset. */
button.toast__session {
  width: 100%;
  background: transparent;
  border: none;
  padding: 0;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

button.toast__session:hover,
button.toast__session:focus-visible {
  color: var(--oc-text-primary);
  text-decoration: underline;
}

.toast__body {
  margin: 0;
  color: var(--oc-text-secondary);
  font-size: 12px;
  white-space: pre-wrap;
  word-break: break-word;
}

.toast__action {
  align-self: flex-start;
  margin-top: 6px;
  padding: 4px 12px;
  font-size: 12px;
}

.toast__dismiss {
  position: absolute;
  top: 6px;
  right: 8px;
  background: transparent;
  border: none;
  color: var(--oc-text-secondary);
  cursor: pointer;
  /* styles.css pins every button to a var(--control-size) square, which
     turned this 2px/6px glyph into a 36px box on desktop and a 44px one
     at the 768px breakpoint - wider than the toast is tall, overhanging
     the title row and painting the glyph off-centre. Size it from its
     own padding instead. */
  width: auto;
  height: auto;
  padding: 2px 6px;
  /* The button opts into the base layer (data-btn, which only the raw-controls ratchet asks for), whose zero-specificity type
     rules would give the x the base font. `revert` rolls the face back to the browser's own button font, the look this card
     had before the base, so the glyph is drawn exactly as it was. */
  font-family: revert;
  font-size: 16px;
  line-height: 1;
  border-radius: var(--oc-radius-sm);
}

.toast__dismiss:hover {
  background: var(--oc-ground-hover);
  color: var(--oc-text-primary);
}

/* Kind-specific subtle tint on the title row + a small leading glyph. */
.toast[data-kind="Stop"] .toast__title::before {
  content: "■ ";
  color: var(--oc-green);
}

.toast[data-kind="PermissionRequest"] .toast__title::before {
  content: "⚠ ";
  color: var(--oc-yellow);
}

.toast[data-kind="Notification"] .toast__title::before {
  content: "ⓘ ";
  color: var(--oc-cyan);
}

/* THE ALERT CENTER'S CARD IS PAINTED BY ITS ALERT'S SEVERITY
 * (`data-alert-severity`, web/src/lib/app-alerts/alerts.svelte.ts): the
 * same leading square as the panel's severity mark, and an error's edge in
 * the error token, so a failing app reads apart from a session's news. */
.toast[data-alert-severity] .toast__title::before {
  content: "■ ";
}
.toast[data-alert-severity="error"] {
  border-color: var(--oc-coral);
}
.toast[data-alert-severity="error"] .toast__title::before {
  color: var(--oc-coral);
}
.toast[data-alert-severity="warn"] .toast__title::before {
  color: var(--oc-yellow);
}
.toast[data-alert-severity="info"] .toast__title::before {
  color: var(--oc-cyan);
}

/* ------------------------------------------------------------------
   Coalesced-repeat count badge.
   A separate ELEMENT, not text appended to the title, for two reasons:
   it needs its own colour and shape, and a title that literally
   contained "x3" would be indistinguishable from this at the
   .textContent level - which is precisely how a `~~claude` badge
   shipped through a green suite in this codebase.
   ------------------------------------------------------------------ */
.toast__title {
  display: flex;
  align-items: baseline;
  gap: 6px;
}

.toast__title-text {
  min-width: 0;
  overflow-wrap: anywhere;
}

/* THE AUTO-SLEEP TOAST'S MOON (issue #690): the one title icon that is a
   real SVG node (client/js/toast-render.js), not a `content:` glyph, so it
   can paint with `currentColor` and follow the title's own
   --oc-text-primary rather than carrying a hardcoded color the way the
   Stop/PermissionRequest/Notification/Attachment kind glyphs below do.
   `align-self: center` overrides the row's `align-items: baseline` just
   for this child - an SVG has no text baseline to align to, so baseline
   alignment reads as "stuck to the bottom" instead of centered against
   the title's line height. The gap to the text is the title row's own
   6px flex gap above; nothing new is added for it. */
.toast__title-icon {
  display: inline-flex;
  flex: 0 0 auto;
  align-self: center;
  line-height: 0;
}

.toast__title-icon svg {
  display: block;
}

.toast__count {
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: bold;
  line-height: 1;
  padding: 2px 6px;
  /* A CHIP, NOT A CAPSULE. This used to be a pill radius; the military
     terminal palette has one small radius and nothing round, which is the
     same call the server status panel and the settings screen already
     made. A square-ish chip is what a pill degraded to under the themes
     that zero every radius anyway, so nothing here is new. */
  border-radius: var(--oc-radius-sm);
  background: var(--toast-accent);
  /* The badge sits ON the accent, so it needs the darkest ground as its
     ink rather than the foreground colour - the same pairing
     .modal-btn-alert uses in text-prompt-modal.css. */
  color: var(--oc-ground-chrome);
}

/* ------------------------------------------------------------------
   Severity tiers.
   3 = PermissionRequest: blocking, user must answer. 2 = Notification.
   1 = Stop, the chatty one. Only the top tier gets extra weight; a
   fleet of equally-shouty cards is the problem, not the fix.
   ------------------------------------------------------------------ */
/* ONE YELLOW EDGE, NOT TWO. This used to paint the border AND a 1px ring
   immediately outside it, which in this palette reads as exactly the kind
   of thick accent frame the re-skin is removing. The hairline alone still
   tells the top tier apart from the grey-edged cards beside it, and a
   box-shadow costs no layout either way. */
.toast[data-severity="3"] {
  border-color: var(--oc-yellow);
  box-shadow: 0 4px 12px var(--oc-shadow-lift);
}

/* ------------------------------------------------------------------
   Overflow row. The cap's receipt.
   ------------------------------------------------------------------ */
.toast-overflow {
  pointer-events: auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  /* styles.css pins every button to a var(--control-size) square; this
     is a full-width row, so it sizes from its own padding - the same
     escape .toast__dismiss needs, for the same reason. */
  width: 100%;
  height: auto;
  padding: 7px 12px;
  background: var(--oc-ground-raised);
  color: var(--oc-text-secondary);
  border: 1px dashed var(--oc-line-rule);
  border-radius: var(--oc-radius-sm);
  box-shadow: 0 4px 12px var(--oc-shadow-lift);
  font-family: var(--font-mono, 'SF Mono', monospace);
  font-size: 12px;
  line-height: 1.4;
  cursor: pointer;
  text-align: center;
}

.toast-overflow:hover {
  background: var(--oc-ground-hover);
  color: var(--oc-text-primary);
}

.toast-overflow__count {
  font-weight: bold;
  color: var(--oc-text-primary);
}

/* What is in there, in words. A count alone tells the user something is
   hidden but not whether it matters. */
.toast-overflow__worst {
  color: var(--oc-yellow);
}

/* The row itself takes the colour of the worst thing it holds, so a
   glance at the edge of the screen carries the same fact as the text. */
.toast-overflow[data-severity="2"] {
  border-color: var(--oc-yellow);
}

/* CLEAR OF AN OPEN DETAIL PANE (web/src/opscenter/ui/detail-clearance.ts). A page's detail pane publishes
   its box on the root: "side" steps the stack in by --oc-toast-clear-right so it ends where the pane
   begins; "full" (a phone, or a pane with no room beside it) drops the stack to the bottom edge, away
   from the pane's title, switch and review strip. One rule for every page, no page named here. */
:root[data-toast-clear="full"] .toast-container {
  top: auto;
  bottom: 16px;
  right: 16px;
}

/* Mobile responsive: full-width, anchored just under the header. */
@media (max-width: 640px) {
  .toast-container {
    top: 56px;
    right: 8px;
    left: 8px;
    width: auto;
    max-width: none;
  }

  :root[data-toast-clear="full"] .toast-container {
    top: auto;
    bottom: 8px;
    right: 8px;
  }

  /* A phone is mostly toast at three cards, so the JS caps at two here.
     That makes the overflow row load-bearing rather than an edge case:
     give it a real 44px touch target. */
  .toast-overflow {
    padding: 12px;
    min-height: 44px;
  }
}

/* "Dismiss all" - the explicit whole-stack control.
 *
 * Rendered by client/js/toast.js only when two or more toasts are on
 * screen (with one, the card's own x is already a single click). It sits
 * at the HEAD of the container, above every card, because it governs the
 * whole list.
 *
 * Shaped after .toast-overflow rather than after a card: both are
 * full-width utility rows that act on the stack rather than carrying a
 * message. Solid border instead of the overflow row's dashed one - this
 * one performs an action, that one reveals hidden content, and a user
 * must be able to tell them apart at a glance without reading. Same
 * width/height escape from styles.css's square-button rule, for the same
 * reason .toast-overflow needs it.
 */
.toast-dismiss-all {
  pointer-events: auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  height: auto;
  padding: 7px 12px;
  background: var(--oc-ground-raised);
  color: var(--oc-text-secondary);
  border: 1px solid var(--oc-line-rule);
  border-radius: var(--oc-radius-sm);
  box-shadow: 0 4px 12px var(--oc-shadow-lift);
  font-family: var(--font-mono, 'SF Mono', monospace);
  font-size: 12px;
  line-height: 1.4;
  cursor: pointer;
  text-align: center;
}

.toast-dismiss-all:hover {
  background: var(--oc-ground-hover);
  color: var(--oc-text-primary);
}

/* "N sessions waiting" (issue #750), the detailed toast mode's aggregate
   line. A status label, not a control: the Military Terminal title-row
   label (small, uppercase, tracked, in the accent) with a hairline under
   it rather than a card border - the pattern of .pl-title in
   web/src/lib/ledger/ledger-chrome.css - on the SAME opaque raised ground
   the cards and the dismiss-all control use, because it floats over
   whatever screen is underneath (the title-row ground is translucent). */
.toast-waiting {
  pointer-events: none;
  width: 100%;
  padding: 6px 12px;
  background: var(--oc-ground-raised);
  color: var(--oc-orange);
  box-shadow: inset 0 -1px 0 var(--oc-line-rule), 0 4px 12px var(--oc-shadow-lift);
  border-radius: var(--oc-radius-sm);
  font-family: var(--font-mono, 'SF Mono', monospace);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  line-height: 1.4;
  text-align: left;
}

/* A stack holding a blocking permission prompt is about to be cleared of
   it too, so the control says so with the same colour the blocking tier
   uses elsewhere. The full sentence is in its aria-label/title. */
.toast-dismiss-all:not([data-blocking="0"]) {
  border-color: var(--oc-coral);
  color: var(--oc-text-primary);
}

/* ------------------------------------------------------------------
   Attachment receipt: the thumbnail strip.

   Built by client/js/attachment-toast.js and appended to a card that
   client/js/toast.js owns, so everything here is scoped under the
   strip's own class and adds no rule that a notification card can pick
   up. The card's background, border, shadow and accent are inherited
   untouched - which is the point of reusing the component, and is also
   what makes this readable over live terminal output: the opaque card
   is already between the text and the scrollback.

   EVERY COLOUR IS A TOKEN. The per-session pinned theme
   (client/js/theme-navigation.js) works by swapping these same custom
   properties, so a hardcoded hex here would be the one element on the
   card that ignored the user's theme, in exactly the way the low
   contrast overlay this replaced ignored it.
   ------------------------------------------------------------------ */
.toast__thumbs {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 10px;
  /* A PILE IS CAPPED, NOT ALLOWED TO GROW. Attachments coalesce onto one
     card, so six files is six rows; past four the card would own the
     screen on a phone. Scrolling the strip keeps the card a fixed size
     and still reaches every row. */
  max-height: 200px;
  overflow-y: auto;
}

.toast__thumb-row {
  display: flex;
  align-items: center;
  gap: 10px;
  /* The touch-target floor this app uses everywhere. The row is not
     itself tappable, but it sets the rhythm the thumb is sized to and
     keeps two rows from colliding under a fingertip on the scroll. */
  min-height: 44px;
}

/* The picture, or the chip standing in for one. A fixed square either
   way, so a card with one of each does not have a ragged left edge. */
.toast__thumb {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: var(--oc-ground-hover);
  border: 1px solid var(--oc-line-rule);
  border-radius: var(--oc-radius-sm);
}

/* The thumbnail as a button (issue #1208): fills the frame, no chrome of
   its own; focus is the shared own-border rule from focus.css. */
.toast__thumb-btn {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
  border: 0;
  background: var(--oc-ground-hover);
  cursor: zoom-in;
}

.toast__thumb-img {
  width: 100%;
  height: 100%;
  /* COVER, not contain: the frame is square and a phone photo is not,
     so contain would letterbox a 44px square down to a stripe. The
     preview is generated at 2x (attachment-toast.js THUMB_PX) so the
     crop still resolves. */
  object-fit: cover;
  display: block;
}

/* The typed chip for a file with no preview. A .pdf reads as a PDF
   rather than as a picture that failed to load, which is the outcome
   this whole branch exists to avoid. */
.toast__thumb-ext {
  font-size: 10px;
  font-weight: bold;
  letter-spacing: 0.06em;
  color: var(--oc-text-secondary);
  text-transform: uppercase;
}

.toast__thumb-name {
  min-width: 0;
  font-size: 12px;
  color: var(--oc-text-primary);
  /* Clamped to two lines. A single line with an ellipsis hides the
     extension, which is the half of a filename that says what the file
     IS; two lines carry almost every real name whole. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
}

/* The kind glyph, matching the set above. A geometric block, not an
   emoji - the house voice bans them, and the three cards beside this
   one already use plain symbols. */
.toast[data-kind="Attachment"] .toast__title::before {
  content: "▤ ";
  color: var(--oc-cyan);
}

@media (max-width: 640px) {
  /* A phone card is full-width, so the thumb can afford to be bigger
     than the touch floor and the name gets the room it needs. */
  .toast__thumb {
    width: 48px;
    height: 48px;
  }

  .toast__thumb-name {
    font-size: 13px;
  }

  /* THE ATTACHMENT RECEIPT IS JUST THE PICTURE ON A PHONE (Adam
     (Adoom666), 2026-10-02: "on mobile the attachment notifications
     shouldnt be as wide and just show the attached image thumbnail").
     The card shrinks to its content, pinned to the right edge, the
     title, session line, name and fold toggle drop out, and the strip
     is always shown (the :not() lifts it over the collapse rule in
     toast-collapse.css by specificity). The card's own click
     still opens the session and the x still dismisses. */
  .toast[data-kind="Attachment"] {
    align-self: flex-end;
    padding: 6px 28px 6px 6px;
  }

  .toast[data-kind="Attachment"] .toast__title,
  .toast[data-kind="Attachment"] .toast__session,
  .toast[data-kind="Attachment"] .toast__thumb-name {
    display: none;
  }

  .toast[data-kind="Attachment"]:not([data-expanded="1"]) .toast__thumbs {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    margin-top: 0;
  }

  .toast[data-kind="Attachment"] .toast__thumb-row {
    min-height: 0;
  }
}
