/* NimbleOS v2 shell · Sandbox styles
   ============================================================================
   Split out of operational.css on 2026-08-26. It had accumulated 83 rules in a
   1918-line file shared by every other operational screen, which meant every
   Sandbox tweak was a shared-file edit and therefore a branch and a PR.

   That is the opposite of what this surface is for. The agreed protocol
   (2026-08-25) lets Sandbox-only code deploy straight to main precisely
   BECAUSE it is isolated; the isolation had quietly eroded and taken the speed
   with it. Sandbox changes now touch operational-sandbox.js and this file, and
   nothing anyone else depends on.

   Every rule is prefixed .art- per the op- convention in BUILD-CONVENTIONS.
   Keep it that way: the prefix is what makes "isolated" checkable rather than
   asserted.
   ============================================================================ */

/* ── Sandbox (workspace:sandbox) ────────────────────────────────────────────
   All rules prefixed .art- so this file's other components cannot be reached
   by a Sandbox style, per the op- prefix convention in BUILD-CONVENTIONS. */
.art { max-width: 860px; }
.art-h1 { margin: 0 0 4px; font-size: 26px; }
.art-sub { margin: 0 0 16px; color: var(--text-secondary); font-size: 14px; }
.art-banner {
  padding: 10px 14px; border-radius: 8px; margin-bottom: 12px; font-size: 13px;
  background: var(--bg-surface); border: 1px solid var(--border, #e4e4e4); line-height: 1.5;
}
.art-banner strong { display: block; margin-bottom: 2px; }
.art-banner.info { background: #eef4f4; border-color: #cfe0e0; }
.art-banner.warn { background: #fdf3e8; border-color: #f0d8bb; }
.art-banner.err  { background: #fbeceb; border-color: #f0c4c1; }
.art-banner.ok   { background: #eef6ee; border-color: #cfe4cf; }
.art-create { margin-bottom: 14px; }
.art-create summary { cursor: pointer; font-weight: 600; font-size: 14px; padding: 6px 0; }
.art-create-body { display: flex; flex-direction: column; gap: 8px; padding: 8px 0 4px; }
.art-input {
  width: 100%; padding: 8px 10px; border: 1px solid var(--border, #d9d9d9);
  border-radius: 6px; font: inherit; font-size: 13px; background: #fff; box-sizing: border-box;
}
.art-filter { max-width: 280px; margin-bottom: 14px; }
.art-card {
  border: 1px solid var(--border, #e4e4e4); border-left: 3px solid var(--border, #e4e4e4);
  border-radius: 8px; padding: 12px 14px; margin-bottom: 10px; background: #fff;
}
.art-card.is-published { border-left-color: var(--accent, #004447); }
.art-head { display: flex; align-items: baseline; gap: 10px; justify-content: space-between; }
.art-title { font-weight: 600; font-size: 15px; }
.art-badge { font-size: 11px; padding: 2px 8px; border-radius: 10px; white-space: nowrap; }
.art-badge.on  { background: var(--accent, #004447); color: #fff; }
.art-badge.off { background: #ececec; color: #666; }
.art-meta { display: flex; gap: 8px; flex-wrap: wrap; margin: 6px 0 8px; align-items: center; }
.art-chip { font-size: 11px; background: #f2f1ee; padding: 2px 8px; border-radius: 4px; }
.art-chip.muted { color: #777; }
.art-key { font-size: 10px; color: #999; font-family: ui-monospace, Menlo, monospace; }
.art-desc { margin: 0 0 6px; font-size: 13px; line-height: 1.5; }
.art-note { margin: 0 0 6px; font-size: 12px; color: var(--text-secondary); font-style: italic; }
.art-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.art-btn {
  padding: 6px 12px; border-radius: 6px; border: 1px solid var(--border, #d9d9d9);
  background: #fff; cursor: pointer; font: inherit; font-size: 12px;
}
.art-btn:hover { background: #f6f5f2; }
.art-btn.primary { background: var(--accent, #004447); color: #fff; border-color: transparent; }
.art-btn.warn { background: #fdf3e8; border-color: #f0d8bb; }
.art-empty { color: var(--text-secondary); font-size: 13px; padding: 12px 0; }

/* First-run explanation, shown only while the Sandbox is empty. */
.art-firstrun {
  border: 1px dashed var(--border, #d9d9d9); border-radius: 8px;
  padding: 16px 18px; margin-bottom: 16px; background: #fbfaf7;
}
.art-firstrun h2 { margin: 0 0 8px; font-size: 16px; }
.art-firstrun p { margin: 0 0 8px; font-size: 13px; line-height: 1.55; color: var(--text-secondary); }
.art-firstrun-hint { font-style: italic; margin-bottom: 0 !important; }

/* Comments + history panels, and the document editor (Phase 2 + 3). */
.art-panel { margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--border, #e4e4e4); }
.art-panel-title { font-size: 12px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .04em; color: var(--text-secondary); margin-bottom: 8px; }
.art-ev { display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; padding: 4px 0; font-size: 13px; }
.art-ev-text { flex: 1; min-width: 200px; }
.art-ev-ago { font-size: 11px; color: #999; white-space: nowrap; }
.art-ev-why { margin: 0 0 8px 0; }
/* The summary carries the first SENTENCE of the note now, not the label
   "why", so it has to be readable prose rather than a 11px hint: it is the
   line Alex and Anna actually scan, and the rest of the note is what opens
   behind it. Sized to match .art-ev-why p, which is the text it expands into. */
.art-ev-why summary {
  font-size: 12px; line-height: 1.5; color: var(--text-primary, #111);
  cursor: pointer; list-style-position: outside;
}
.art-ev-why summary:hover { color: var(--accent, #004447); }
/* A note that is one sentence has nothing to reveal, so it renders as plain
   text rather than a disclosure that opens onto itself. Same type as the
   summary it stands in for, so a folded and an unfolded row read as one list. */
.art-why-line { display: block; font-size: 12px; line-height: 1.5; margin: 0 0 8px; }
.art-ev-why p { margin: 4px 0 0; font-size: 12px; line-height: 1.5; color: var(--text-secondary);
  border-left: 2px solid var(--border, #e4e4e4); padding-left: 8px; }
.art-cmt { border-left: 2px solid var(--accent, #004447); padding: 6px 0 6px 10px; margin-bottom: 8px; }
.art-cmt.resolved { border-left-color: #d9d9d9; opacity: .6; }

/* The resolved fold. One disclosure carrying every resolved thread, collapsed
   by default, so the open ones are the list rather than the survivors of it.
   The count lives on the summary, so it says how much is behind it.

   Inside the fold the rows drop the .resolved dimming back to full opacity:
   greying them was doing the "these are done" job when they sat in the same
   list, and behind a fold labelled "N resolved" it only makes them hard to
   read at the moment someone has deliberately gone looking for them. */
/* REPLIES SIT UNDER THE QUESTION. Indented and rebated so the eye reads a
   thread rather than a list: the answer belongs to the comment above it, which
   is the whole difference between replying and commenting again. Left border and
   indent mirror .inline-comment-reply in style.css, the Prototype's convention
   for the same relationship, so the two surfaces do not disagree. */
.art-cmt-replies { margin: 8px 0 0 0; }
.art-cmt.art-cmt-reply {
  border-left: 2px solid #ecf0f0;
  padding: 4px 0 4px 10px;
  margin: 0 0 4px 12px;
}
.art-cmt.art-cmt-reply .art-cmt-body { font-size: 12.5px; }
/* A resolved ROOT already dims the whole thread through .resolved on the
   parent; a reply must not dim a second time on top of that. */
.art-cmt.art-cmt-reply.resolved { opacity: 1; border-left-color: #ecf0f0; }

/* NOT A PERSON. Deliberately the accent colour rather than .muted grey: this
   chip answers "did Alex write this or did her Claude", and a reader scanning a
   thread has to catch it without looking for it. Muted is for facts you may
   ignore, and this is not one. */
.art-chip.agent {
  background: #eef2f2; color: var(--accent, #004447);
  font-weight: 600; letter-spacing: .01em;
}

.art-cmt-fold { margin: 4px 0 10px; }
.art-cmt-fold > summary {
  cursor: pointer;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .4px;
  color: var(--text-secondary, #555);
  padding: 4px 0;
  user-select: none;
}
.art-cmt-fold > summary:hover { color: var(--text-primary, #111); }
.art-cmt-fold-body { padding-top: 6px; }
.art-cmt-fold-body .art-cmt.resolved { opacity: 1; }
.art-cmt-body { margin: 0 0 4px; font-size: 13px; line-height: 1.5; white-space: pre-wrap; }
.art-cmt-foot { display: flex; gap: 8px; align-items: center; }
/* The author reads as the name, the timestamp as the footnote: same weight
   relationship as the card byline (.art-row-byline strong). */
.art-cmt-who { font-size: 11px; font-weight: 600; color: var(--text-secondary, #555); }
.art-btn.tiny { padding: 2px 8px; font-size: 11px; }
.art-doc-wrap { margin: 8px 0; }
.art-doc { font-family: ui-monospace, Menlo, monospace; font-size: 12px; line-height: 1.6; margin-bottom: 6px; }

/* HTML document view. The iframe is walled off (no allow-same-origin), so it
   cannot size itself against us: a fixed tall frame with its own scroll is the
   honest option rather than guessing at content height. */
.art-docbar { display:flex; gap:8px; align-items:center; margin-bottom:6px; }
.art-frame-host { border:1px solid var(--border,#e4e4e4); border-radius:6px; overflow:hidden; background:#fff; }
.art-frame { width:100%; height:70vh; min-height:420px; display:block; border:0; }

/* Compact list rows + the full reading view. */
.art-row {
  display:flex; align-items:center; gap:12px; justify-content:space-between;
  border:1px solid var(--border,#e4e4e4); border-left:3px solid var(--border,#e4e4e4);
  border-radius:8px; padding:12px 14px; margin-bottom:8px; background:#fff;
}
.art-row.is-published { border-left-color: var(--accent,#004447); }
.art-row-main { flex:1 1 auto; cursor:pointer; min-width:0; }
.art-row-main:hover .art-row-title { text-decoration:underline; }
.art-row-title { font-weight:600; font-size:15px; margin-bottom:4px; }
.art-row-meta { display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.art-row-side { display:flex; gap:8px; align-items:center; flex:0 0 auto; }
.art-back {
  appearance:none; border:0; background:transparent; font:inherit; font-size:12px;
  color:var(--text-secondary); cursor:pointer; padding:0 0 10px; display:block;
}
.art-back:hover { color:var(--accent,#004447); text-decoration:underline; }
.art-read-head { display:flex; align-items:baseline; gap:12px; justify-content:space-between; }
.art-read-title { font-size:24px; margin:0 0 6px; }
.art-read { max-width:none; }
.art-frame.tall { height:calc(100vh - 300px); min-height:520px; }

/* ── Full-bleed document view ──────────────────────────────────────────────
   Pinned beside the rail so an HTML document fills the screen the way it would
   if you opened the file, with the NimbleOS rail still there (which is also
   what it looks like as a portal tab). `left` comes from the shell's OWN
   --rail-w variable: never hardcode it, or this breaks when the rail changes. */
.art-doc-full {
  position: fixed; left: var(--rail-w); top: 0; right: 0; bottom: 0;
  display: flex; flex-direction: column; background: #fff; z-index: 30;
}
.art-full-bar {
  flex: 0 0 auto; display: flex; align-items: center; gap: 14px;
  justify-content: space-between; flex-wrap: wrap;
  padding: 10px 18px; border-bottom: 1px solid var(--border, #e4e4e4);
  background: var(--bg-surface, #f6f8f8);
}
.art-full-bar-left { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; min-width: 0; }
.art-full-title { font-weight: 600; font-size: 15px; }
.art-full-bar .art-back { padding: 0; }
.art-actions.compact { margin: 0; gap: 6px; }
.art-actions.compact .art-btn { padding: 5px 10px; font-size: 12px; }
/* min-height:0 is load-bearing: without it a flex child refuses to shrink and
   the iframe pushes the panel past the viewport, reintroducing outer scroll. */
.art-full-body { flex: 1 1 auto; display: flex; min-height: 0; }
.art-doc-canvas { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.art-doc-canvas .art-doc-wrap { flex: 1 1 auto; display: flex; flex-direction: column; margin: 0; min-height: 0; }
.art-doc-canvas .art-docbar { padding: 6px 12px; margin: 0; border-bottom: 1px solid var(--border, #eee); }
.art-doc-canvas .art-frame-host { flex: 1 1 auto; border: 0; border-radius: 0; min-height: 0; }
.art-doc-canvas .art-frame,
.art-doc-canvas .art-frame.tall { height: 100%; min-height: 0; }
/* Comments / history sit BESIDE the document, so you read and respond at once. */
/* The panel lives inside a SLOT that stays in the DOM across redraws. The slot
   is why the document iframe beside it is never detached, and a detached iframe
   reloads. Empty slot takes no space. */
.art-panel-slot { display: contents; }

.art-panel.as-side {
  flex: 0 0 360px; margin: 0; border-top: 0; border-left: 1px solid var(--border, #e4e4e4);
  padding: 14px 16px; overflow-y: auto; background: #fff;
}
@media (max-width: 900px) {
  /* The shell drops the side-by-side grid on narrow screens, so a fixed panel
     offset by --rail-w would sit off-screen. Fall back to normal flow. */
  .art-doc-full { position: static; height: auto; }
  .art-doc-canvas .art-frame { height: 70vh; }
  .art-full-body { flex-direction: column; }
  .art-panel.as-side { flex: 1 1 auto; border-left: 0; border-top: 1px solid var(--border, #e4e4e4); }
}

/* ── True full page · #/workspace/artifacts/<key>/full ─────────────────────
   The reading view keeps the rail on purpose. This is the other thing "full
   page" means: the document and nothing else. Collapsing --rail-w rather than
   overriding a left offset is what makes it one declaration instead of two
   that can drift, because the body grid and .art-doc-full both read it. */
/* grid-template-columns as well as the variable: #rail is display:none, so it
   leaves the grid entirely and #main would otherwise be placed in the 0px
   first track. Invisible while a document covers the screen, and a one-column
   page the moment one does not. */
body.doc-fullscreen { --rail-w: 0px; grid-template-columns: 1fr; }
body.doc-fullscreen #rail { display: none; }
/* Once the rail is gone the docbar holds only the button that opened this
   view, and pressing it again would open a third tab of the same document. */
body.doc-fullscreen .art-docbar { display: none; }
@media (max-width: 900px) {
  /* .art-doc-full falls back to static flow here, so the iframe needs a real
     height or it collapses. The full bar is about 44px, hence the allowance. */
  body.doc-fullscreen .art-doc-canvas .art-frame { height: calc(100vh - 60px); }
}

/* Status. Deliberately louder than a chip: the whole point is that you can see
   at a glance whether something is a rough cut or finished, without opening it. */
.art-status {
  font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  padding: 3px 8px; border-radius: 3px; white-space: nowrap;
}
/* Lifecycle, and the colours carry the meaning: amber while it is still being
   worked on, teal once it is finished, green once it has gone out, grey once
   it is not to be used. */
.art-status.draft     { background: #fdf3e8; color: #a35f31; border: 1px solid #f0d8bb; }
.art-status.ready     { background: #eef4f4; color: #0a6a6e; border: 1px solid #cfe0e0; }
.art-status.published { background: #eef6ee; color: #3d6b3d; border: 1px solid #cfe4cf; }
/* Dead is deliberately drained rather than red. It is not an error, it is a
   document nobody should pick up, and it should recede on a busy list. */
.art-status.dead      { background: #f2f2f1; color: #7a7a78; border: 1px solid #dedede; }
.art-row.is-dead .art-row-title { text-decoration: line-through; opacity: .65; }

/* The line under a title: who, when, how many versions. Quiet by design; it is
   reference, not the point of the card. */
.art-row-byline {
  display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: center;
  margin-top: 3px; font-size: 11px; color: var(--text-secondary, #6b6b6b);
}
.art-row-byline strong { font-weight: 600; color: inherit; }
.art-statussel {
  padding: 5px 8px; border: 1px solid var(--border, #d9d9d9); border-radius: 6px;
  font: inherit; font-size: 12px; background: #fff;
}
.art-filters { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 14px; }
.art-filters .art-filter { margin-bottom: 0; }

/* ── section comments (roadmap 1.2) ───────────────────────────────────────
   The bridge script inside the sandboxed document reports what was selected;
   these are the two places that shows up. Deliberately quiet: the document is
   the thing being read, and comment furniture that competes with it makes the
   page harder to read, not easier. */

/* What you are about to comment on, above the box. */
.art-selbar {
  display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
  padding: 6px 8px; margin: 6px 0;
  border-left: 2px solid var(--accent, #004447);
  background: var(--bg-surface, #f7f7f5);
  border-radius: 0 3px 3px 0;
}
.art-selbar-label {
  font-size: 10px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-secondary, #6b6b6b);
}
/* The quote is untrusted text from an uploaded document, so it is clamped
   rather than allowed to set the panel's width. */
.art-selbar-quote {
  flex: 1 1 200px; min-width: 0;
  font-size: 12px; font-style: italic; line-height: 1.45;
  overflow-wrap: anywhere;
}
.art-selbar-hint {
  margin: 6px 0; font-size: 11px; line-height: 1.5;
  color: var(--text-secondary, #6b6b6b);
}

/* The anchor shown on a comment that has one. */
.art-cmt-anchor {
  margin: 0 0 4px; padding: 3px 0 3px 8px;
  border-left: 2px solid #d9d9d9;
  font-size: 12px; font-style: italic; line-height: 1.45;
  color: var(--text-secondary, #6b6b6b);
  overflow-wrap: anywhere;
}
/* An anchor written against an older version. Marked, not hidden: the comment
   is still real, it is only the pointer that may have drifted. */
.art-cmt-anchor.stale { border-left-color: #d2804d; }
.art-cmt-quote { display: block; }

/* A comment anchor you can act on. Findable ones invite a click; ones whose
   passage has gone say so and stop pretending to be a link. */
.art-cmt-anchor.findable { cursor: pointer; }
.art-cmt-anchor.findable:hover {
  border-left-color: var(--accent, #004447);
  background: var(--bg-surface, #f7f7f5);
}
.art-cmt-anchor.missing { cursor: default; opacity: .7; text-decoration: line-through; }

/* ── new / changed, and what changed ──────────────────────────────────────
   Two different facts, so two different marks. "New" is a state; "3 changes"
   is a count against a position you already had. */
.art-row-titleline { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.art-new, .art-unread {
  font-size: 10px; letter-spacing: .04em; text-transform: uppercase;
  padding: 1px 6px; border-radius: 9px; white-space: nowrap;
}
.art-new    { background: var(--accent, #004447); color: #fff; }
.art-unread { background: #fdf3e8; color: #a35f31; border: 1px solid #f0d8bb; text-transform: none;
              letter-spacing: 0; font-size: 11px; }

/* The "since you last looked" strip. It sits above the document and is
   dismissible, because it is an interruption by design and should not become
   permanent furniture. */
.art-since-slot { display: contents; }
.art-since {
  border-bottom: 1px solid var(--border, #eee);
  background: var(--bg-surface, #f7f7f5);
  padding: 10px 14px;
}
.art-since-head { font-size: 12px; font-weight: 600; margin-bottom: 6px; }
/* THE CAP THAT KEEPS THE CHROME REACHABLE (2026-09-03).
   .art-doc-full is position:fixed, top:0 bottom:0, and a flex column whose
   children are, in order: the new-version banner, THIS strip, .art-full-bar
   (which holds the Back button) and .art-full-body. Nothing in this file
   scrolls except .art-panel.as-side, so a child that grows with content does
   not scroll, it PUSHES: with twenty changes the strip measured 1223px inside
   a 900px viewport and put Back at y=1236, off the bottom edge of a container
   the page cannot scroll. Alex, 2026-09-03: "there were a heap of changes and
   i couldn't scroll to close, had to zoom out." Zooming out worked because it
   shrank the content until it fit, which is the tell that the cause was
   height and not a missing control.

   THE LIST, NOT THE STRIP, is what is capped. The heading and the "Got it"
   button sit outside it and therefore stay put however long the log is, so
   the control that dismisses the strip is never the thing scrolled away.

   A viewport unit, not a pixel count: the guarantee has to hold on a laptop
   at 900px tall as well as on a large display, and vh is the only unit that
   knows. This is BELT AND BRACES against the fold in operational-artifacts-
   core.js, which is what makes the strip short in normal use. The fold is a
   display choice and could be changed; the cap is the invariant, and it holds
   at zero changes, at twenty and at a hundred. */
.art-since-list {
  list-style: none; margin: 0 0 6px; padding: 0;
  max-height: 34vh; overflow-y: auto;
}
.art-since-item {
  display: flex; gap: 10px; align-items: baseline;
  padding: 3px 0; font-size: 12px; line-height: 1.45;
}
/* Only lines that can actually take you somewhere look clickable. */
.art-since-item.jumpable { cursor: pointer; }
.art-since-item.jumpable:hover .art-since-text {
  text-decoration: underline; text-underline-offset: 2px;
}
.art-since-text { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }

/* A status this build does not recognise. Deliberately loud: it means the page
   is behind the database, and the reader should not trust the chip. */
.art-status.unknown {
  background: #fdecea; color: #a13b31; border: 1px solid #f2c4bd;
}

/* "Since you last looked": the REASONING is the line, the mechanical event is
   the footnote. Reversing these two is the difference between a change log and
   an activity feed. */
.art-since-body { flex: 1 1 auto; min-width: 0; }
.art-since-why {
  display: block; font-size: 12px; line-height: 1.5; overflow-wrap: anywhere;
}
.art-since-what {
  display: block; margin-top: 1px; font-size: 10.5px;
  color: var(--text-secondary, #6b6b6b);
}

/* Sharing panel. One row per team member: name on the left, what they can do on
   the right. Grid rather than flex so the level controls line up in a column
   however long the names are. */
.art-share-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 10px;
  align-items: center;
  padding: 5px 0;
  border-bottom: 1px solid var(--border, #e4e4e4);
}
.art-share-row:last-child { border-bottom: 0; }
.art-share-who { font-size: 13px; font-weight: 600; }
.art-share-note {
  margin: 10px 0 0;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--text-secondary, #555);
}
