/* attachments-r1a (docs/PLAN-2026-10-20-attachments-1.md 3.1 and 4.1): the state of a file on its sent card.
   The card of a live turn is  [tile + state badge] name / type, size | state text.  Busy (sending, saving, reading) = a pulsing dot;
   indexing for search = a turning ring; ready = a check; sent = a quiet dot; partial / search not ready = a warning;
   failed = a cross. Theme tokens only (style.css defines them for dark and light), so both themes follow, and every
   state is also said in words, so colour and motion are never the only signal. Loaded after style.css and chat-align.css.

   attachments-ui-1: the composer card (the paste card's box with a 40 px type tile, the name cut in the middle, a meta line),
   the sent card (same anatomy, no date, the state mark is a badge on the tile) and the file viewer. The type colour sits on the
   glyph and never changes with state; the 3-letter label is in --text. */

/* ---- the type tile (composer card, sent card, viewer) ---- */
.att-tone-pdf   { --att-fg: var(--danger-text);    --att-bg: var(--danger-bg); }
.att-tone-doc   { --att-fg: var(--link);           --att-bg: var(--badge-blue-bg); }
.att-tone-sheet { --att-fg: var(--badge-green-text); --att-bg: var(--badge-green-bg); }
.att-tone-slide { --att-fg: var(--warn-fg);        --att-bg: var(--bg-hover); }
.att-tone-text  { --att-fg: var(--text);           --att-bg: var(--bg-hover); }
.att-tone-media { --att-fg: var(--warn-panel-text); --att-bg: var(--warn-panel-bg); }
.att-tone-other { --att-fg: var(--text-muted);     --att-bg: var(--bg-hover); }
@supports (color: color-mix(in srgb, currentColor 10%, transparent)) {
  .att-tone-slide { --att-bg: color-mix(in srgb, var(--warn-fg) 13%, transparent); }
}
.att-tile {
  position: relative;
  flex: none;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  width: 40px;
  height: 40px;
  border-radius: 10px;
  background: var(--att-bg);
  color: var(--att-fg);
}
.att-glyph { display: grid; place-items: center; line-height: 0; }
.att-glyph svg { width: 16px; height: 16px; }
.att-label {
  color: var(--text);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1;
}
.att-thumb { display: block; width: 100%; height: 100%; border-radius: inherit; object-fit: cover; }

/* a name cut in the middle: the head gives way, the tail keeps the extension */
.att-name-head { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: pre; }
.att-name-tail { flex: none; white-space: pre; }

/* ---- the composer card (.paste-card from chat-align.css is the base box) ---- */
.attachments.att-cards { gap: 10px; margin: 6px 0 10px; min-width: 0; }
.att-card { width: min(380px, 100%); }   /* wide enough for `PDF · 2.3 MB · Modified 20-10-2026 14:05` on one line */
.att-card .att-open { padding-right: 34px; }
.att-card .att-open:disabled { color: var(--text); cursor: progress; opacity: 1; }
.att-title { display: flex; min-width: 0; overflow: hidden; text-overflow: clip; }
.att-card .paste-card-meta { display: block; }
.att-card.is-adding .att-tile { animation: att-tile-pulse 0.9s ease-in-out infinite; }

/* ---- the sent card ---- */
.att-chip {
  max-width: 100%;
  transition: border-color 0.15s ease, background-color 0.15s ease;
  animation: att-chip-in 0.18s ease-out;
}
.att-chip.att-sent {
  --att-ring: var(--user-bubble);
  position: relative;
  box-sizing: border-box;
  display: inline-flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 12px;
  width: min(300px, 100%);
  min-width: 0;
  min-height: 60px;
  padding: 10px 14px 10px 10px;
  border: 1px solid transparent;
  border-radius: 14px;
  background: var(--user-bubble);
  color: var(--text);
  font-size: 0.86rem;
  text-align: left;
  cursor: pointer;
}
.att-chip.att-sent:hover { border-color: var(--border-strong); }
.att-chip.att-sent:focus-visible { outline: 2px solid var(--link); outline-offset: 2px; }
.att-body { display: flex; flex: 1; flex-direction: column; gap: 2px; min-width: 0; }
.att-name {
  display: flex;
  min-width: 0;
  overflow: hidden;
  color: var(--text);
  font-weight: 600;
  line-height: 1.3;
}
.att-meta {
  display: flex;
  flex-wrap: wrap;
  column-gap: 6px;
  min-width: 0;
  color: var(--text-muted);
  font-size: 0.76rem;
  line-height: 1.3;
}
.att-kind { white-space: nowrap; }
.att-state {
  display: inline-flex;
  flex-basis: 100%;
  align-items: center;
  min-width: 0;
  color: var(--text-muted);
}
.att-text { transition: opacity 0.15s ease; }
.att-text:empty { display: none; }
.att-state:has(.att-text:empty) { display: none; }
.msg.user .msg-media { justify-content: flex-end; }

/* the state mark: a badge on the tile's corner, drawn in CSS (no image, no emoji, so nothing to translate or to colour by hand) */
.att-badge {
  position: absolute;
  right: -6px;
  bottom: -6px;
  box-sizing: border-box;
  display: grid;
  place-items: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--att-ring, var(--bg));
}
.att-mark {
  position: relative;
  flex: none;
  box-sizing: border-box;
  width: 14px;
  height: 14px;
  border-radius: 50%;
}
/* busy: a dot that breathes */
.att-chip[data-state="sending"] .att-mark,
.att-chip[data-state="saving"] .att-mark,
.att-chip[data-state="reading"] .att-mark {
  width: 8px;
  height: 8px;
  background: var(--link);
  animation: att-pulse 0.9s ease-in-out infinite;
}
/* indexing: a ring with one quarter in the link colour, turning (no progress is known, so none is drawn) */
.att-chip[data-state="indexing"] .att-mark {
  border: 2px solid var(--border-strong);
  border-top-color: var(--link);
  animation: att-spin 0.9s linear infinite;
}
.att-chip[data-state="ready"] .att-mark { background: var(--success); }
.att-chip[data-state="ready"] .att-mark::after {
  content: "";
  position: absolute;
  left: 4.5px;
  top: 2px;
  width: 3px;
  height: 6px;
  border: solid var(--bg);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.att-chip[data-state="sent"] .att-mark {
  width: 8px;
  height: 8px;
  background: var(--text-muted);
  opacity: 0.55;
}
.att-chip[data-state="partial"] .att-mark,
.att-chip[data-state="unready"] .att-mark,
.att-chip[data-state="failed"] .att-mark {
  display: grid;
  place-items: center;
  color: var(--bg);
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
}
.att-chip[data-state="partial"] .att-mark,
.att-chip[data-state="unready"] .att-mark { background: var(--warn-fg); }
.att-chip[data-state="partial"] .att-mark::after,
.att-chip[data-state="unready"] .att-mark::after { content: "!"; }
.att-chip[data-state="failed"] .att-mark { background: var(--danger-text); }
.att-chip[data-state="failed"] .att-mark::after { content: "\00d7"; font-size: 12px; }

/* partial / search not ready: the amber is on the badge only (amber text on the light sent card is below AA); the words stay --text-muted */
.att-chip[data-state="failed"] .att-state { color: var(--danger-text); }
/* the neutral tiles on the sent card get an edge: on the light theme their fill is the card's own colour */
.att-chip.att-sent .att-tone-text,
.att-chip.att-sent .att-tone-other { --att-bg: var(--bg); box-shadow: inset 0 0 0 1px var(--border); }

/* an older (reloaded) chip and a sent picture open the viewer too */
.msg-media .media-chip[role="button"],
.msg-media img[role="button"] { cursor: pointer; }
.msg-media .media-chip[role="button"]:hover { border-color: var(--border-strong); }
.msg-media .media-chip[role="button"]:focus-visible,
.msg-media img[role="button"]:focus-visible { outline: 2px solid var(--link); outline-offset: 2px; }

/* ---- the viewer (a custom overlay on the paste preview's precedent; above the composer and the app modal) ---- */
.att-viewer-overlay { z-index: 330; }
.app-modal.att-viewer {
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  width: min(900px, 100%);
  height: min(86vh, 820px);
  max-height: none;
  overflow: hidden;
  padding: clamp(14px, 3vw, 20px);
}
.att-viewer-head { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.att-viewer-heading { flex: 1; min-width: 0; }
.att-viewer-heading h3 {
  display: -webkit-box;
  margin: 0;
  overflow: hidden;
  font-size: 1rem;
  overflow-wrap: anywhere;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
}
.att-viewer-meta { margin: 2px 0 0; color: var(--text-muted); font-size: 0.78rem; }
.att-viewer-close { width: 40px; height: 40px; }
.att-viewer-body { display: flex; flex: 1; flex-direction: column; min-height: 0; overflow: hidden; }
.att-viewer-text {
  flex: 1;
  min-height: 0;
  margin: 0;
  padding: 12px 14px;
  overflow: auto;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg);
  color: var(--text);
  font-family: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", monospace;
  font-size: 0.8rem;
  line-height: 1.55;
  overscroll-behavior: contain;
  tab-size: 4;
  white-space: pre;
}
.att-viewer-text.is-wrap { overflow-wrap: anywhere; white-space: pre-wrap; }
.att-viewer-text:focus-visible { outline: 2px solid var(--link); outline-offset: 2px; }
.att-viewer-body.is-media {
  display: grid;
  place-items: center;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg);
}
.att-viewer-media { max-width: 100%; max-height: 100%; object-fit: contain; }
audio.att-viewer-media { width: min(100%, 520px); }
.att-viewer-plain {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  margin: auto;
  padding: 24px 12px;
  text-align: center;
}
.att-viewer-plain .att-tile { width: 72px; height: 72px; border-radius: 18px; gap: 6px; }
.att-viewer-plain .att-glyph svg { width: 28px; height: 28px; }
.att-viewer-plain .att-label { font-size: 12px; }
.att-viewer-line { max-width: 38ch; margin: 0; color: var(--text); font-size: 0.9rem; line-height: 1.5; }
.att-viewer-note { margin: 10px 0 0; color: var(--warn-fg); font-size: 0.8rem; font-weight: 600; }
.att-viewer-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 10px; margin-top: 14px; }
.att-viewer-actions .btn { min-height: 40px; }
.att-viewer-actions .btn:hover:not(:disabled) { background: var(--bg-hover); border-color: var(--border-strong); }
.att-viewer-actions .btn:disabled { cursor: default; opacity: 0.6; }
.att-viewer-actions .btn:focus-visible { outline: 2px solid var(--link); outline-offset: 2px; }
.att-viewer-wrap[aria-pressed="true"] { background: var(--bg-hover); border-color: var(--border-strong); }

/* a narrow screen: cards scroll sideways inside the composer (never the page), the date goes to a second line and then goes;
   the sent card puts its state on its own line; the viewer is a full-screen sheet (900 px and below) */
@media (max-width: 480px) {
  .attachments.att-cards {
    flex-wrap: nowrap;
    max-width: 100%;
    margin: -11px 0 8px;
    padding: 17px 17px 2px 0;   /* the remove button's 42 px target reaches 17 px past the card corner: room for all of it */
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    scrollbar-width: thin;
    scrollbar-color: var(--border-strong) transparent;
  }
  .att-card { flex: none; width: min(280px, 84vw); scroll-snap-align: start; }
  .att-sep { display: none; }
  .att-m2 { display: block; }
}
@media (pointer: coarse) {
  .attachments.att-cards { scrollbar-width: none; }   /* a finger scrolls the row; the native bar was a light strip with arrows on a dark page */
}
@media (max-width: 380px) {
  .att-m2 { display: none; }
}
@media (max-width: 900px) {
  .att-viewer-overlay { padding: 0; backdrop-filter: none; }
  .app-modal.att-viewer {
    width: 100vw;
    height: 100vh;
    height: 100dvh;
    border: 0;
    border-radius: 0;
    padding: max(14px, env(safe-area-inset-top)) 14px max(14px, env(safe-area-inset-bottom));
  }
}

@keyframes att-chip-in { from { opacity: 0; transform: translateY(4px); } }
@keyframes att-pulse { 50% { opacity: 0.35; transform: scale(0.8); } }
@keyframes att-spin { to { transform: rotate(360deg); } }
@keyframes att-tile-pulse { 50% { opacity: 0.55; } }
@media (prefers-reduced-motion: reduce) {
  .att-chip, .att-chip .att-mark, .att-chip[data-state] .att-mark, .att-card.is-adding .att-tile { animation: none !important; }
  .att-chip, .att-text { transition: none; }
  .attachments.att-cards { scroll-behavior: auto; }
}

/* attachments-r1b: the honest line above an answer that used only the inline text of a document whose search was not
   ready. It is a .stopped-chip (the existing quiet note); only the gap to the reply text is set here. */
.rag-index-note { margin: 0 0 8px; overflow-wrap: anywhere; }

/* egress-notice-1: the private-egress notice bar above the composer was an unreadable cream block in the dark theme.
   style.css mixes var(--warning) with var(--surface) and colours the small print with var(--muted): none of the three
   is a theme token, so only their light fallbacks applied. Same layout, existing tokens only. */
.private-egress-panel {
  border-color: color-mix(in srgb, var(--warn-fg) 55%, transparent);
  background: color-mix(in srgb, var(--warn-fg) 9%, var(--bg));
  color: var(--text);
}
.private-egress-consent input[type="text"] { background: transparent; }   /* style.css: var(--surface), undefined = transparent today; stated so it stays */
.private-egress-consent small { color: var(--text-muted); }
@media (forced-colors: active) {
  .private-egress-panel { border-color: CanvasText; background: Canvas; color: CanvasText; }
}

/* attachments-ui-2: the copy icon under a sent prompt is hover-only again (and keyboard focus), as chat-align.css designed it.
   chat-align.css shows it on `.msg.user:focus-within`: since attachments-ui-1 the sent cards are focusable and the viewer gives the focus back
   to the card it was opened from, so a MOUSE click on a card left focus inside the message and the icon stayed on until the focus moved.
   Now only a keyboard focus (:focus-visible) inside the message reveals it. Touch screens (hover: none) keep it always visible
   (chat-align.css), and a browser without :has() keeps the old rule (the whole block is skipped there). */
@supports selector(:has(*)) {
  @media (hover: hover) {
    .msg.user:focus-within .msg-user-actions { opacity: 0; pointer-events: none; }
    .msg.user:hover .msg-user-actions { opacity: 1; pointer-events: auto; }
    .msg.user:has(:focus-visible) .msg-user-actions { opacity: 1; pointer-events: auto; }
  }
}

/* attachments-ui-3: on a device that has a mouse the copy icon is hover/keyboard-only even when the browser reports (hover: none).
   chat-align.css shows it always under (hover: none); attachments-ui-2's block above only applies under (hover: hover). A Windows touch laptop
   reports hover: none (and any-hover: hover false) but its touchpad sends real mouse pointer events, so app.js sets html.has-mouse on the first
   one (touch and pen never do; a touch-only phone keeps the always-visible icon). The html.has-mouse prefix gives these rules the specificity
   that beats chat-align's media rule. Hover and :focus-visible reveal; :focus-within does NOT (a mouse click on a sent card leaves the focus
   inside, the cause of attachments-ui-2). The transition and the reduced-motion rule of chat-align.css are not touched (no transition here). */
@supports selector(:has(*)) {
  html.has-mouse .msg.user .msg-user-actions { opacity: 0; pointer-events: none; }
  html.has-mouse .msg.user:hover .msg-user-actions { opacity: 1; pointer-events: auto; }
  html.has-mouse .msg.user:has(:focus-visible) .msg-user-actions { opacity: 1; pointer-events: auto; }
}

/* attachments-ui-4: the viewer's video was cut off at the bottom (its control bar hidden) when the window was short. The media body is a grid whose
   track was auto, so max-height: 100% on the video did not resolve and the clip kept its own aspect height; the body then clipped the bottom, where
   the native controls live. A definite track makes the percentage real: the whole element, controls included, always fits (picture and audio as before). */
.att-viewer-body.is-media {
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
}
video.att-viewer-media { min-width: 0; min-height: 0; }

/* chat-internet-ui-1 (owner 21-10): no Internet chip row. The globe carries the effective state as a small badge
   (shape AND colour: dot = on for this chat, slash = off, lock = blocked by the organization); the per-chat switch
   lives in a popover over the chat, and the once-per-chat ask is a small card over the chat. Both float
   (position: absolute above the globe), so the composer and the message list never move. Theme tokens only. */
.composer-web-wrap { position: relative; display: inline-flex; align-self: flex-end; }
.composer-web-search-btn { position: relative; }
.composer-web-search-btn::before { content: ""; position: absolute; inset: -3px; border-radius: 50%; }   /* 38px target */
.composer-web-search-btn .internet-badge { display: none; }
.composer-web-search-btn:is([data-internet="off"], [data-internet="private-off"], [data-internet="admin"])::after {
  content: ""; position: absolute; left: 50%; top: 50%; width: 2px; height: 19px; border-radius: 1px;
  background: currentColor; box-shadow: 0 0 0 1.5px var(--bg-elevated);
  transform: translate(-50%, -50%) rotate(-45deg); pointer-events: none;
}
.composer-web-search-btn.search-down::after { display: none; }
.composer-web-search-btn[data-internet="chat-on"] .internet-badge {
  display: block; position: absolute; right: -1px; bottom: -1px; width: 9px; height: 9px; border-radius: 50%;
  background: var(--success); box-shadow: 0 0 0 2px var(--bg-elevated);
}
.composer-web-search-btn[data-internet="blocked"] .internet-badge {
  display: block; position: absolute; right: -3px; bottom: -2px; width: 9px; height: 7px; border-radius: 2px;
  background: var(--text-muted); box-shadow: 0 0 0 2px var(--bg-elevated);
}
.composer-web-search-btn[data-internet="blocked"] .internet-badge::before {
  content: ""; position: absolute; left: 1.5px; bottom: 6px; width: 6px; height: 5px; box-sizing: border-box;
  border: 1.5px solid var(--text-muted); border-bottom: 0; border-radius: 3px 3px 0 0;
}

.internet-popover,
.private-egress-panel.is-ask {
  position: absolute; z-index: 40; right: -48px; bottom: calc(100% + 18px);
  max-width: calc(100vw - 32px); box-sizing: border-box;
  background: var(--bg-elevated); color: var(--text); border: 1px solid var(--border);
  border-radius: 10px; box-shadow: 0 8px 24px var(--shadow-strong);
  animation: internet-pop 140ms ease-out;
}
.internet-popover[hidden], .private-egress-panel.is-ask[hidden] { display: none; }
@keyframes internet-pop { from { opacity: 0; transform: translateY(4px) scale(0.98); } }
@media (prefers-reduced-motion: reduce) {
  .internet-popover, .private-egress-panel.is-ask { animation: none; }
}
.internet-popover { width: 284px; padding: 6px; font-size: 0.84rem; }
.internet-popover-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 8px 8px; border-radius: 8px;
}
.internet-popover-row[hidden] { display: none; }
.internet-popover-row:hover { background: var(--bg-hover); }
.internet-popover-note { margin: 2px 8px 6px; font-size: 0.76rem; line-height: 1.35; color: var(--text-muted); }
.internet-popover-note:empty { display: none; }
.internet-switch {
  position: relative; flex: none; width: 34px; height: 20px; padding: 0; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--border-strong); background: var(--bg-hover); transition: background 0.12s, border-color 0.12s;
}
.internet-switch::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 14px; height: 14px; border-radius: 50%;
  background: var(--text-muted); transition: transform 0.12s, background 0.12s;
}
.internet-switch[aria-checked="true"] { background: var(--link); border-color: var(--link); }
.internet-switch[aria-checked="true"]::after { transform: translateX(14px); background: var(--bg-elevated); }
.internet-switch:focus-visible, .private-egress-allow:focus-visible, .private-egress-close:focus-visible {
  outline: 2px solid var(--link); outline-offset: 2px;
}
.internet-switch:disabled { cursor: not-allowed; opacity: 0.5; }
@media (prefers-reduced-motion: reduce) { .internet-switch, .internet-switch::after { transition: none; } }

/* the ask card sits in .composer-wrap (position: relative), over the end of the message list, right-aligned with the
   composer; it never covers the message box */
.private-egress-panel.is-ask {
  display: flex; align-items: center; gap: 6px; width: max-content; margin: 0; padding: 6px 6px 6px 12px;
  right: 20px; bottom: calc(100% + 8px); max-width: calc(100% - 40px); font-size: 0.8rem;
}
@media (max-width: 860px) { .private-egress-panel.is-ask { right: 12px; max-width: calc(100% - 24px); } }
/* a phone: the card stays ONE line (owner rule): smaller type, tighter spacing, closer to the edge */
@media (max-width: 480px) {
  .private-egress-panel.is-ask { right: 8px; max-width: calc(100% - 16px); gap: 2px; padding: 4px 4px 4px 10px; font-size: 0.74rem; }
  .private-egress-panel.is-ask .private-egress-status { white-space: nowrap; }
  .private-egress-allow { padding: 4px 6px; }
  .private-egress-close { width: 22px; }
}
.private-egress-panel.is-ask .private-egress-status { font-weight: 500; }
.private-egress-allow {
  flex: none; border: 0; background: none; color: var(--link); font: inherit; font-weight: 600; cursor: pointer;
  padding: 4px 8px; border-radius: 6px; white-space: nowrap;
}
.private-egress-allow:hover { background: var(--accent-blue-soft); }
.private-egress-close {
  flex: none; width: 24px; height: 24px; padding: 0; border: 0; border-radius: 6px; background: none;
  color: var(--text-muted); font-size: 1rem; line-height: 1; cursor: pointer;
}
.private-egress-close:hover { background: var(--bg-hover); color: var(--text); }
.private-egress-allow[hidden], .private-egress-close[hidden] { display: none; }
@media (forced-colors: active) {
  .internet-popover, .private-egress-panel.is-ask { border-color: CanvasText; background: Canvas; color: CanvasText; }
  .internet-switch[aria-checked="true"] { background: Highlight; }
}

/* chat-files-3: the viewer shows the stored original from the server. The page images of an Office file are one vertical stack
   inside the viewer (a focusable scroll region, fitted to the panel width; the chat behind never moves: the viewer is an
   overlay). Skeletons while a page loads; the loading bar is still under reduced motion. Theme tokens only. */
.att-viewer-body.is-pages { overflow: hidden; }
.att-pages {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 14px;
  min-height: 0;
  padding: 14px;
  overflow-y: auto;
  overscroll-behavior: contain;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg);
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
.att-pages:focus-visible { outline: 2px solid var(--link); outline-offset: 2px; }
.att-page { display: flex; flex-direction: column; align-items: center; gap: 6px; margin: 0; }
.att-page img {
  display: block;
  width: 100%;
  max-width: 860px;
  height: auto;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg-elevated);
  box-shadow: 0 1px 4px var(--shadow-strong);
}
.att-page.is-loading img,
.att-page.is-broken img { aspect-ratio: 1 / 1.294; max-height: 70vh; background: var(--bg-hover); }
.att-page.is-loading img { animation: att-tile-pulse 1.4s ease-in-out infinite; }
.att-page figcaption { color: var(--text-muted); font-size: 0.72rem; font-variant-numeric: tabular-nums; }
.att-viewer-note.is-quiet { color: var(--text-muted); font-weight: 500; }
.att-skel-bar {
  display: block;
  width: 140px;
  height: 4px;
  overflow: hidden;
  border-radius: 2px;
  background: var(--bg-hover);
}
.att-skel-bar::after {
  content: "";
  display: block;
  width: 40%;
  height: 100%;
  border-radius: 2px;
  background: var(--link);
  animation: att-skel-move 1.2s ease-in-out infinite;
}
@keyframes att-skel-move { from { transform: translateX(-100%); } to { transform: translateX(250%); } }
@media (max-width: 480px) {
  .att-pages { gap: 10px; padding: 8px; }
}
@media (prefers-reduced-motion: reduce) {
  .att-page.is-loading img, .att-skel-bar::after { animation: none; }
}
@media (forced-colors: active) {
  .att-pages, .att-page img { border-color: CanvasText; }
}

/* storage-s4a: the one retention line at the foot of the attach popover (shown only where the store is on). */
.composer-attach-note {
  margin: 4px 0 0;
  padding: 7px 11px 5px;
  border-top: 1px solid var(--border);
  max-width: 210px;
  color: var(--text-muted);
  font-size: 0.75rem;
  line-height: 1.35;
  overflow-wrap: anywhere;
}

/* composer-preview-1: the browser-side preview of an attached Office file (before Send). Same viewer shell; only the body differs.
   The label line sits under the header; tables, slides and flowing text scroll inside focusable regions. Theme tokens only. */
.att-viewer-quick { margin: -6px 0 10px; color: var(--text-muted); font-size: 0.78rem; line-height: 1.4; }
.att-viewer-body.is-quick { overflow: hidden; }
.cp-holder { display: flex; flex: 1; flex-direction: column; min-height: 0; }
.cp-doc, .cp-slides, .cp-panel {
  flex: 1;
  min-height: 0;
  overflow: auto;
  overscroll-behavior: contain;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg);
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
.cp-doc:focus-visible, .cp-slides:focus-visible, .cp-panel:focus-visible, .cp-tab:focus-visible { outline: 2px solid var(--link); outline-offset: 2px; }
.cp-doc { padding: 18px 22px; color: var(--text); font-size: 0.92rem; line-height: 1.6; }
.cp-p, .cp-h { margin: 0 0 0.7em; white-space: pre-wrap; overflow-wrap: anywhere; }
.cp-h { font-weight: 700; line-height: 1.3; margin-top: 0.9em; }
.cp-h1 { font-size: 1.35rem; }
.cp-h2 { font-size: 1.15rem; }
.cp-h3 { font-size: 1.02rem; }
.cp-h4 { font-size: 0.95rem; }
.cp-p.is-li { display: flex; gap: 8px; margin-bottom: 0.3em; white-space: normal; }
.cp-l1 { padding-inline-start: 20px; }
.cp-l2 { padding-inline-start: 40px; }
.cp-l3, .cp-l4 { padding-inline-start: 60px; }
.cp-mark { flex: none; min-width: 1.4em; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.cp-run { white-space: pre-wrap; }
.cp-run.is-b { font-weight: 700; }
.cp-run.is-i { font-style: italic; }
.cp-run.is-u { text-decoration: underline; }
.cp-run.is-s { text-decoration: line-through; }
.cp-img { display: block; max-width: 100%; max-height: 50vh; height: auto; margin: 6px 0; border-radius: 4px; }
.cp-tablewrap { max-width: 100%; margin: 0 0 1em; overflow-x: auto; }
.cp-table { border-collapse: collapse; font-size: 0.86rem; }
.cp-table td { padding: 5px 10px; border: 1px solid var(--border); vertical-align: top; overflow-wrap: anywhere; }
.cp-empty { margin: 14px; color: var(--text-muted); font-size: 0.86rem; }
.cp-sheet { display: flex; flex: 1; flex-direction: column; min-height: 0; }
.cp-info { margin: 0 0 8px; color: var(--text-muted); font-size: 0.8rem; font-variant-numeric: tabular-nums; }
.cp-tabs { display: flex; gap: 4px; margin-bottom: 8px; overflow-x: auto; scrollbar-width: thin; }
.cp-tab {
  flex: none;
  max-width: 200px;
  min-height: 34px;
  padding: 4px 14px;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: 0.82rem;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
}
.cp-tab:hover { background: var(--bg-hover); }
.cp-tab.is-on { border-color: var(--border-strong); background: var(--bg-hover); color: var(--text); font-weight: 600; }
.cp-grid { border-collapse: separate; border-spacing: 0; font-size: 0.82rem; font-variant-numeric: tabular-nums; }
.cp-grid th, .cp-grid td { padding: 4px 10px; border-right: 1px solid var(--border); border-bottom: 1px solid var(--border); white-space: nowrap; }
.cp-grid td { max-width: 320px; overflow: hidden; color: var(--text); text-overflow: ellipsis; }
.cp-grid td.is-num { text-align: right; }
.cp-grid thead th { position: sticky; top: 0; z-index: 2; background: var(--bg-elevated); color: var(--text-muted); font-weight: 600; text-align: center; }
.cp-grid tbody th { position: sticky; left: 0; z-index: 1; background: var(--bg-elevated); color: var(--text-muted); font-weight: 500; text-align: right; }
.cp-grid thead th.cp-corner { left: 0; z-index: 3; }
.cp-slides { display: flex; flex-direction: column; gap: 12px; padding: 14px; }
.cp-slide { flex: none; padding: 14px 18px; border: 1px solid var(--border); border-radius: 10px; background: var(--bg-elevated); }
.cp-slide-no { margin: 0 0 4px; color: var(--text-muted); font-size: 0.72rem; font-variant-numeric: tabular-nums; }
.cp-slide-title { margin: 0 0 8px; font-size: 1.02rem; line-height: 1.3; overflow-wrap: anywhere; }
.cp-slide-line { margin: 0 0 4px; font-size: 0.88rem; line-height: 1.5; overflow-wrap: anywhere; }
.cp-p.is-box { margin-inline-start: 14px; padding-inline-start: 10px; border-inline-start: 2px solid var(--border-strong); color: var(--text-muted); }
.cp-foot { margin: 8px 0 0; color: var(--text-muted); font-size: 0.78rem; }
.cp-badge { margin-inline-start: 8px; padding: 1px 7px; border: 1px solid var(--border-strong); border-radius: 999px; font-size: 0.68rem; }
@media (max-width: 480px) {
  .cp-doc { padding: 12px; }
  .cp-slides { padding: 8px; }
}
@media (forced-colors: active) {
  .cp-doc, .cp-slides, .cp-panel, .cp-slide, .cp-tab { border-color: CanvasText; }
}
