/* ─── BUTTON COLOR VARIABLE ───
   Single knob for the whole app's button color, per Harding 2026-08-05:
   "create a variable with an easy-to-remember name... I will tell you a
   HEX number and you will change it in ALL appropriate places." Every
   button in the UNIFORM BUTTON SYSTEM (and everything folded into it —
   zoom stepper, Node IDs toggle, modal buttons, diff tabs, popups) reads
   its color from these three, never a hardcoded hex. To change the color:
   edit --btn-orange (solid fill) and --btn-orange-hover (its hover/darker
   shade) below — --btn-orange-rgb must stay the same numbers as
   --btn-orange, just comma-separated with no #, so it can be dropped into
   rgba(var(--btn-orange-rgb), 0.5) for the sim-widget's glow shadow. */
:root {
  --btn-orange: #75163F;
  --btn-orange-hover: #671337;
  --btn-orange-rgb: 117,22,63;
}
/* ─── STANDALONE HEADER (added 2026-08-12) ───
   app.gs4d.app no longer shares callgenix.com's style.css/sidebar.js —
   this is its own product page now, not a page inside the AI Decoded Lab
   site (removed the CallGenix sidebar + showroom nav entirely, per
   Harding). Everything the header/layout needs — previously supplied by
   the shared, 19-page style.css — lives here instead, self-contained. */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; overflow: hidden; }
body { display: flex; flex-direction: column; font-family: 'Inter', Arial, sans-serif; }

/* 2026-08-12: header/logo/title/lang-toggle values copied verbatim from
   callgenix.com's own style.css, per Harding — same color, font, sizing
   as the CallGenix site's header, just with GS4D's own logo/text. */
.site-header {
  position: relative; /* containing block for .app-header-title's centering */
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 58px;
  padding: 0 24px;
  background: #1c3461;
  border-bottom: 2px solid #e8a020;
}
.logo-container { display: flex; align-items: center; gap: 12px; }
.site-logo { width: 48px; height: 48px; border-radius: 8px; object-fit: contain; }
.site-title { font-family: 'Monda', monospace; font-size: 16px; font-weight: 700; color: #ffffff; letter-spacing: 0.01em; }
.header-user { display: flex; align-items: center; gap: 12px; }

.lang-toggle { display: inline-flex; align-items: center; gap: 6px; margin-left: 12px; padding-bottom: 10px; }
.lang-flag {
  font-size: 11px; font-weight: 700; line-height: 1; width: 32px; height: 22px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 5px; cursor: pointer; border: 1px solid rgba(245,244,240,0.35);
  background: #f5f4f0; color: #1a1a18; opacity: .85; padding: 0; position: relative;
  transition: opacity .15s, border-color .15s, box-shadow .15s;
}
.lang-flag:hover { opacity: 1; }
.lang-flag.active { opacity: 1; border-color: #185FA5; box-shadow: 0 0 0 2px rgba(24,95,165,0.35); }
.lang-flag.active::after {
  content: ''; display: block; position: absolute; bottom: -8px; left: 50%;
  transform: translateX(-50%); width: 6px; height: 6px; border-radius: 50%;
  background: #00c04b; box-shadow: 0 0 2px rgba(0,192,75,0.6);
}
html.cg-lang-pt #langFlagEN.active { opacity: .85; border-color: rgba(245,244,240,0.35); box-shadow: none; }
html.cg-lang-pt #langFlagEN.active::after { content: none; }
html.cg-lang-pt #langFlagBR { opacity: 1; border-color: #185FA5; box-shadow: 0 0 0 2px rgba(24,95,165,0.35); }
html.cg-lang-pt #langFlagBR::after {
  content: ''; display: block; position: absolute; bottom: -8px; left: 50%;
  transform: translateX(-50%); width: 6px; height: 6px; border-radius: 50%;
  background: #00c04b; box-shadow: 0 0 2px rgba(0,192,75,0.6);
}
.lang-status { display: inline-flex; align-items: center; gap: 6px; margin-left: 4px; font-family: 'JetBrains Mono', monospace; font-size: 11px; color: #6b6a63; }
.lang-status .status-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: #6b6a63; }
.lang-status .status-dot.processing { background: #BA7517; animation: langPulseAmber 1.2s ease-in-out infinite; }
.lang-status .status-dot.done { background: #1D9E75; }
@keyframes langPulseAmber { 0%, 100% { opacity: 1; } 50% { opacity: .5; } }

@media (max-width: 900px) {
  .site-header { padding: 0 12px; }
  .site-title { font-size: 13px; }
}

.content-wrap { flex: 1; min-height: 0; display: flex; }

/* Centered app title in the header (2026-08-10, per Harding's rearrangement
   mockup). .site-header (above) establishes the positioning context. */
.app-header-title {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  font-family: 'Inter', sans-serif;
  font-size: 14px;
  font-weight: 700;
  color: #ffffff;
  white-space: nowrap;
  pointer-events: none;
}
@media (max-width: 900px) {
  /* Narrow viewports don't have room for logo + centered title + lang
     controls all at once — hide the centered title rather than let it
     overlap the logo or the language toggle. */
  .app-header-title { display: none; }
}
/* ─── RESET ── */
html { background: #0f1117; }
.app-viewport {
  font-family: 'Inter', Arial, sans-serif;
  background: #0f1117;
  color: #1a1a18;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.content-wrap--app {
  overflow: hidden !important;
  min-height: 0;
}
/* ─── MAIN PANELS ── */
.panels { display: flex; flex: 1; overflow: hidden; }
/* ─── LEFT PANEL ── */
.left-panel {
  width: 420px;
  min-width: 460px;
  max-width: calc(100% - 240px);
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  background: #0f1117;
  overflow: hidden;
}
.panel-header {
  padding: 9px 14px;
  display: flex; align-items: center; gap: 10px;
  border-bottom: 1px solid rgba(255,255,255,0.10);
  flex-shrink: 0;
  background: #161b26;
  /* Long logged-in emails at the left-panel's narrowest allowed width
     (460px, see .left-panel min-width) could still exceed the row's
     total width even with the 130px email cap below, since left-panel
     also has overflow:hidden — wrap is a safety net so Log out drops to
     its own line instead of getting silently clipped off-screen. */
  flex-wrap: wrap;
  row-gap: 6px;
}
/* ============================================================================
   UNIFORM BUTTON SYSTEM
   ============================================================================
   Every action button in the app (reference: Apply) shares one fixed height,
   one background (orange, var(--btn-orange) — was blue #185FA5 until 2026-08-05, per
   Harding: "ALL buttons" orange with white font), one font, and one border
   treatment so the whole interface reads as a single consistent design
   instead of each button having drifted its own color/size over successive
   edits. Icon-only buttons (copy buttons, pan mode) get a second rule
   further down that turns them into a fixed-width square instead of a
   padded pill. The zoom stepper (−/100%/+), Node IDs checkbox, and pan-mode
   active state are folded into the same orange scheme further down this
   file even though they keep their own compact pill shapes.
   2026-08-05, second pass: modal buttons (fp-close, fp-btn-cancel,
   login-submit/cancel, share-btn-cancel/send, the sim-widget LOAD button,
   diff tabs, guardrail/AI-notice popups) were folded into the same
   orange/white color+font system too — each keeps whatever shape its
   context needs (tab top-corners, icon square, full-width modal CTA), but
   none of them use their own bespoke color anymore. A "Claude Design"
   proposal for a different, off-white/tiered slate-blue palette was
   reviewed and explicitly rejected by Harding in favor of staying orange
   and radically uniform. */
.btn-open-file, .btn-logout, .btn-reset-prompt, .btn-apply-prompt,
.btn-github, .btn-slack, .btn-share, .btn-diff, .btn-mmd, .btn-drawio, .btn-vsdx, .btn-bpmn,
.btn-copy-code, .btn-copy-diagram, .btn-copy-code-quick, .btn-copy-diagram-quick,
.btn-pan-mode, .btn-fullscreen {
  height: 30px;
  box-sizing: border-box;
  background: var(--btn-orange);
  border: none;
  border-radius: 6px;
  color: #ffffff;
  font-family: 'Inter', sans-serif;
  font-size: 11.5px;
  font-weight: 700;
  padding: 0 14px;
  cursor: pointer;
  transition: background 0.15s;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  white-space: nowrap;
  flex-shrink: 0;
}
.btn-open-file:hover:not(:disabled), .btn-logout:hover:not(:disabled),
.btn-reset-prompt:hover:not(:disabled), .btn-apply-prompt:hover:not(:disabled),
.btn-github:hover:not(:disabled), .btn-slack:hover:not(:disabled), .btn-share:hover:not(:disabled),
.btn-diff:hover:not(:disabled), .btn-mmd:hover:not(:disabled), .btn-drawio:hover:not(:disabled), .btn-vsdx:hover:not(:disabled),
.btn-bpmn:hover:not(:disabled), .btn-copy-code:hover:not(:disabled), .btn-copy-diagram:hover:not(:disabled),
.btn-copy-code-quick:hover:not(:disabled), .btn-copy-diagram-quick:hover:not(:disabled),
.btn-pan-mode:hover:not(.active), .btn-fullscreen:hover {
  background: var(--btn-orange-hover);
}
/* 2026-08-13, per Harding: these were rendering below the project's own
   contrast floor (CLAUDE-UI-STANDARDS.md says never drop white opacity
   below 0.55 on dark panels) -- 0.35 was actually a standing violation of
   that rule, not just a stylistic choice. Bumped so the labels stay
   readable while still clearly inactive. */
.btn-open-file:disabled, .btn-logout:disabled, .btn-reset-prompt:disabled, .btn-apply-prompt:disabled,
.btn-github:disabled, .btn-slack:disabled, .btn-share:disabled, .btn-diff:disabled,
.btn-mmd:disabled, .btn-drawio:disabled, .btn-vsdx:disabled, .btn-bpmn:disabled,
.btn-copy-code:disabled, .btn-copy-diagram:disabled,
.btn-copy-code-quick:disabled, .btn-copy-diagram-quick:disabled {
  background: #333f56;
  color: rgba(255,255,255,0.62);
  cursor: not-allowed;
}

/* 2026-08-11, per Harding: Ingestao Glossario mode locks these three too
   (pan mode, zoom stepper, fullscreen), which had no :disabled styling
   before since they were never gated by anything until now.
   2026-08-13: opacity floor raised, same reasoning as the block above. */
.btn-pan-mode:disabled, .zoom-btn:disabled, .btn-fullscreen:disabled {
  opacity: 0.65;
  cursor: not-allowed;
  pointer-events: none;
}
/* 2026-08-13, per Harding: was crushing the placeholder text via a
   blanket `opacity`, which dims background/border/text all together --
   the exact same "USER CHANGE REQUEST" box read noticeably dimmer than
   "CLAUDE'S SUMMARY" right next to it, even though the latter is never
   disabled and so never got this treatment. Restyled to match
   .claude-response's own pattern instead: keep the text bright, only
   fade the chrome (background/border) to signal "locked." */
.prompt-input:disabled {
  opacity: 1;
  cursor: not-allowed;
  background: rgba(255,255,255,0.06);
  border-color: rgba(255,255,255,0.20);
}
.prompt-input:disabled::placeholder { color: rgba(255,255,255,0.85); }
/* Icon-only square variant — same fixed height, width matches it 1:1 */
.btn-copy-code, .btn-copy-diagram, .btn-copy-code-quick, .btn-copy-diagram-quick, .btn-pan-mode, .btn-fullscreen {
  width: 30px;
  padding: 0;
}
/* 2026-08-10, per Harding's rearrangement mockup: Open File and Log out/in
   no longer spell themselves out — same fixed-square treatment as the
   copy/pan/fullscreen buttons above, applied as an add-on modifier class
   so .btn-open-file/.btn-logout keep their base orange/white look from the
   UNIFORM BUTTON SYSTEM block, just without the wide text padding. */
.btn-open-file.btn-icon-only, .btn-logout.btn-icon-only {
  width: 30px;
  padding: 0;
}
/* 2026-08-11, per Harding: these read visibly smaller than the fullscreen
   button next to them even though the BUTTON itself is already the same
   30px square (see the icon-only square variant rule above) -- the gap is
   the glyph, not the box. Fullscreen uses a hand-authored SVG via
   .icon-inline, sized 16px and drawn to fill nearly all of that box.
   These are Tabler webfont glyphs instead, which typically ink only
   ~70-80% of their own em box, so a same-number font-size reads smaller
   next to a tightly-drawn SVG. Bumped 15px -> 19px (not just to 16px) to
   compensate for that ink-coverage gap and land at genuine visual parity,
   not just numeric parity. */
.btn-open-file.btn-icon-only i, .btn-logout.btn-icon-only i {
  font-size: 19px;
}
/* Pan mode is a toggle, not a one-shot action — invert colors when active so
   "on" stays visually distinct even though it shares the same orange family.
   Fullscreen (2026-08-07) is the same kind of toggle, same convention. */
.btn-pan-mode.active, .btn-fullscreen.active {
  background: #ffffff;
  color: var(--btn-orange);
  border: 2px solid var(--btn-orange);
}

/* Pan button icon, v3 (2026-08-09, per Harding): v1 (emoji + corner badge)
   and v2 (bigger emoji + centered badge) both mixed a colored emoji glyph
   with a thin stroke-icon badge -- Harding didn't like either. v3 drops
   the hand entirely per his explicit ask: "get rid of the hand altogether
   and leave only 4 large thick arrows." Now a genuine hand-authored SVG
   (same .icon-inline convention every other toolbar button already uses,
   e.g. #fullscreenIcon, the copy buttons) instead of a webfont glyph or
   emoji -- 4 arrowheads on a centered cross, stroke-width 2.5 (vs. the
   site's usual 2) and a bigger box for "large thick." No separate active-
   state override needed anymore: being a real stroke SVG, it already
   follows currentColor via .icon-inline, so it automatically goes white
   on orange / orange on white through .btn-pan-mode.active's existing
   color swap, same as every other icon button on the page. */
.pan-mode-arrows { width: 20px; height: 20px; }
/* Success/confirmation flashes (kept distinct from the base blue on purpose —
   these signal "it worked", not "here is an available action"). */
.btn-github.saved,
.btn-mmd.copied, .btn-drawio.copied, .btn-vsdx.copied, .btn-bpmn.copied,
.btn-copy-code.copied, .btn-copy-diagram.copied,
.btn-copy-code-quick.copied, .btn-copy-diagram-quick.copied {
  background: #1D9E75 !important;
  color: #ffffff !important;
}
.icon-inline { color: currentColor; }
/* Slack's mark is a real 4-color logo (explicit SVG fills), not currentColor —
   the rule above must never try to recolor it. */
.icon-slack-logo { color: initial; }
.logged-in-email {
  font-family: 'JetBrains Mono', monospace;
  font-size: 10.5px;
  color: rgba(255,255,255,0.55);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* Explicit reset, not redundant: .role-label (the new parent as of
     2026-08-07, see below) sets text-transform:uppercase for its own
     "USER:" text, which an email address inherits by default — confirmed
     live, it rendered the whole address in ugly, semantically-wrong ALL
     CAPS. Emails are case-sensitive-looking to a human reader even when
     the mailbox itself isn't, so this must stay explicit here. */
  text-transform: none;
  /* 2026-08-05: moved out of the panel-header's margin-left:auto right-flush
     group (per Harding: "shift the email left") to sit right after Open
     File instead of jammed against Log out at the panel's far edge.
     2026-08-07: moved AGAIN, this time out of .panel-header entirely and
     into the "User:" role-label in .prompt-area (per Harding) — the header
     row was the recurring cause of "Log out" wrapping to its own line
     (worse once Bare/Rich and the ⓘ button were added, and again once
     PT-BR labels run longer than their English source), and the
     role-label row has room to spare that the header never did. Widened
     220px → 380px same day, live-tested: .prompt-area is the full left-panel
     width, not a crowded button row, so the old header-tuned cap was
     truncating addresses far earlier than the space available actually
     required. */
  max-width: 380px;
  min-width: 0;
  cursor: default;
  margin-left: 10px;
}
.btn-copy-code-quick {
  flex-shrink: 0;
}
/* 2026-08-11, per Harding: the Mermaid copy button moved OUT of the
   panel-header row entirely, so it costs zero horizontal space there —
   floats instead as an overlay in the top-right corner of the code editor
   itself (.code-scroll-wrap is already position:relative, see its own
   rule further down, so no new positioning context needed). z-index above
   both the textarea (1) and the highlight overlay (0) so it always sits
   on top of the code, not behind it. */
.btn-copy-code-quick--overlay {
  position: absolute;
  top: 10px; right: 10px;
  z-index: 6;
}
.current-filename {
  display: none;
}
.code-editor { flex: 1; display: flex; overflow: hidden; }
.line-numbers {
  padding: 16px 10px 16px 12px;
  text-align: right;
  color: rgba(255,255,255,0.20);
  font-family: 'JetBrains Mono', monospace;
  font-size: 11.5px; line-height: 1.7;
  user-select: none;
  overflow: hidden;
  flex-shrink: 0;
  min-width: 38px;
  background: rgba(255,255,255,0.02);
  border-right: 1px solid rgba(255,255,255,0.06);
  pointer-events: none;
}
.line-numbers div { line-height: 1.7; }
/* 2026-08-10, per Harding's rearrangement mockup: he flagged a "dark thick
   vertical scroll that seems useless" and asked me to double-check before
   removing it, rather than just trusting his read — confirmed. The real,
   working scroll for the code text is the <textarea> below, which is a
   native replaced element with its own independent internal scrollbar
   (see the historical scroll-sync note in CLAUDE.md: "a fixed-height
   <textarea> manages its own independent native scroll"). This wrap's OWN
   overflow:auto rarely does anything useful — the textarea is sized to
   fill the wrap's padding box exactly (100%/100%), so it never overflows
   the wrap on its own. But #codeHighlightOverlay is position:absolute
   with no explicit height/bottom, sized only by its .code-highlight-band
   children's JS-computed `top` offsets — on a long file those bands can
   extend past the visible fold, and since this wrap is their containing
   block (position:relative), that DOES generate scrollable overflow on
   THIS element too. The result: a second, visually-adjacent scrollbar
   that inherits the page's dark ::-webkit-scrollbar theme further down
   this file, sits pixel-close to the textarea's real one, and does
   nothing when dragged — nothing in the JS reads this wrap's own
   scrollTop (syncCodeHighlightOverlayScroll() follows the TEXTAREA's
   scroll event, translateY()-ing the overlay to match). overflow:hidden
   removes that dead scrollbar; position:relative stays, since the overlay
   still needs this element as its positioning anchor, and nothing that
   actually needs to be seen ever exceeds the wrap's own box (the textarea
   doesn't; the overlay's own visible content — the highlight bands — are
   already positioned correctly via the translateY sync regardless of
   whether the overflow beyond them is clipped or scrollable). */
.code-scroll-wrap { flex: 1; overflow: hidden; padding: 16px; position: relative; }
.code-editor textarea {
  width: 100%; height: 100%;
  background: transparent;
  border: none; outline: none; resize: none;
  font-family: 'JetBrains Mono', monospace;
  font-size: 12px; line-height: 1.7;
  color: #e8edf8;
  caret-color: #6ab0e0;
  white-space: pre;
  position: relative;
  z-index: 1;
}
.code-highlight-overlay {
  position: absolute;
  top: 16px; left: 16px; right: 16px;
  pointer-events: none;
  z-index: 0;
}
.code-highlight-band {
  position: absolute;
  left: 0; right: 0;
  height: 20.4px;
  background: rgba(255,230,0,0.28);
  border-left: 3px solid #FFE600;
  box-sizing: border-box;
}
.diagram-highlight-cue {
  position: fixed;
  display: none;
  pointer-events: none;
  background: rgba(24,95,165,0.18);
  border: 2px solid #185FA5;
  border-radius: 4px;
  z-index: 50;
  transition: top 0.08s ease-out, left 0.08s ease-out, width 0.08s ease-out, height 0.08s ease-out;
}
.toggle-version {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  color: #a8c0d8;
  display: none;
}
.toggle-version input[type="checkbox"] {
  width: 16px;
  height: 16px;
  cursor: pointer;
  accent-color: #1D9E75;
}
.toggle-version input[type="checkbox"]:disabled {
  cursor: not-allowed;
  opacity: 0.4;
}
.version-label {
  font-weight: 600;
  display: inline-block;
  width: 65px;
  word-break: break-word;
  line-height: 1.15;
}
.prompt-area {
  border-top: 2px solid #1D9E75;
  padding: 11px 14px 12px;
  flex-shrink: 0;
  background: linear-gradient(180deg, #0e1622 0%, #1a2230 45%, #333d52 75%, #6b7488 100%);
}
.prompt-input {
  width: 100%;
  background: rgba(255,255,255,0.10);
  border: 1.5px solid rgba(255,255,255,0.28);
  border-radius: 7px;
  color: #f0f0ee;
  font-family: 'Inter', sans-serif;
  /* 2026-08-14, per Harding: font-size/line-height/weight/style now match
     .claude-response exactly (was 12.5px/1.55, .claude-response is
     12px/1.6/400/normal) -- the two content areas read as visibly
     different type before this, caught side by side. */
  font-size: 12px;
  font-weight: 400;
  font-style: normal;
  padding: 10px 13px;
  outline: none;
  resize: none;
  min-height: 140px;
  max-height: 220px;
  line-height: 1.6;
  transition: border-color 0.2s;
}
.prompt-input::placeholder { color: rgba(255,255,255,0.30); }
.prompt-input.prompt-input-warning {
  background: #FFE600 !important;
  color: #1a1a18 !important;
  font-weight: 700 !important;
  border: 2px solid #BA7517 !important;
}
.prompt-input.prompt-input-warning::placeholder { color: rgba(26,26,24,0.55); }
.prompt-input.prompt-input-guidance {
  background: #E6F1FB !important;
  color: #1a1a18 !important;
  font-weight: 600 !important;
  border: 2px solid #185FA5 !important;
}
.prompt-input.prompt-input-guidance::placeholder { color: rgba(26,26,24,0.55); }
.demo-lock {
  font-size: 10px;
  color: rgba(255,255,255,0.32);
  display: flex; align-items: center; gap: 5px;
  letter-spacing: 0.03em;
}
.prompt-footer {
  display: flex; align-items: center; justify-content: flex-start;
  margin-top: 7px;
}
/* 2026-08-07, per Harding (live-caught, called it "disgruntled" UI): the
   Apply/Reset row visibly jumped from flush-left (idle) to flush-right
   (while Processing/Done). Root cause was `justify-content: space-between`
   above combined with #statusIndicator toggling `display:none` — with the
   indicator display:none it's removed from the flex layout entirely, so
   the actions div was the ONLY flex child and space-between collapses to
   flex-start for a single item; the moment showStatusIndicator() flips it
   to display:flex, there are two items again and space-between shoves them
   to opposite edges. Fixed by anchoring the actions row to the right with
   its OWN margin instead of relying on sibling count — margin-left:auto on
   a flex item always claims the rest of the row for itself regardless of
   whether #statusIndicator exists in the layout at that moment, so Apply/
   Reset now sit in the same place (flush right) whether the indicator is
   there or not. Matches the position they were already in during
   Processing/Done, since that's the more informative state for two items
   to visually separate (status readable on the left, actions reachable on
   the right) — idle now starts there too instead of drifting into it. */
.prompt-footer-actions {
  display: flex; gap: 8px;
  margin-left: auto;
}
/* Base look for .btn-apply-prompt, .btn-reset-prompt, .btn-github, .btn-slack
   comes from the UNIFORM BUTTON SYSTEM block above. Only true one-offs live
   here now (icon sizing for GitHub/Slack's webfont <i> glyphs, which aren't
   .icon-inline SVGs). */
.btn-github i, .btn-slack i {
  font-size: 16px;
}
.btn-github.saved {
  background: #1D9E75 !important;
  color: #ffffff !important;
  font-weight: 700;
  opacity: 1 !important;
}
.status-indicator {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  font-weight: 600;
  color: #a8c0d8;
}
.status-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  transition: background-color 0.3s;
}
.status-dot.waiting {
  background-color: #6b6a63;
}
.status-dot.processing {
  background-color: #f5a623;
  animation: pulse-yellow 1.2s ease-in-out infinite;
}
.status-dot.done {
  background-color: #1D9E75;
}
@keyframes pulse-yellow {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}
.status-timer {
  font-family: 'JetBrains Mono', monospace;
  font-size: 10px;
  color: rgba(255,255,255,0.55);
}
.status-cost {
  font-family: 'JetBrains Mono', monospace;
  font-size: 10px;
  color: #e0a94d;
  cursor: help;
}
.role-label {
  font-size: 9.5px; font-weight: 800; letter-spacing: 0.14em;
  text-transform: uppercase; margin-bottom: 5px;
  display: flex; align-items: center; gap: 6px;
}
.role-label.user-label   { color: #6ab0e0; }
.role-label.claude-label { color: #2ed4a0; margin-top: 10px; }
.role-label::before {
  content: ''; display: inline-block;
  width: 6px; height: 6px; border-radius: 50%;
  background: currentColor;
}
.claude-response {
  width: 100%;
  background: rgba(46,212,160,0.05);
  border: 1.5px solid rgba(46,212,160,0.25);
  border-left: 3px solid #1D9E75;
  border-radius: 7px;
  color: #ffffff;
  font-family: 'Inter', sans-serif;
  font-size: 12px;
  padding: 9px 12px;
  min-height: 62px;
  max-height: 170px;
  overflow-y: auto;
  line-height: 1.6;
  font-style: normal;
  font-weight: 400;
}
.claude-response::-webkit-scrollbar       { width: 8px; }
.claude-response::-webkit-scrollbar-track { background: rgba(255,255,255,0.04); border-radius: 4px; }
.claude-response::-webkit-scrollbar-thumb { background: rgba(46,212,160,0.35); border-radius: 4px; }
.claude-response::-webkit-scrollbar-thumb:hover { background: rgba(46,212,160,0.55); }
.claude-response.active {
  border-color: rgba(46,212,160,0.55);
  border-left-color: #1D9E75;
}
.claude-response-footer {
  display: flex; align-items: center; justify-content: flex-end;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 6px;
}
.btn-speak {
  background: #1D9E75;
  border: 1px solid #16825c;
  border-radius: 6px;
  color: #ffffff;
  font-size: 15px;
  padding: 4px 10px;
  cursor: pointer; transition: all 0.15s;
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  line-height: 1;
}
.btn-speak:hover:not(:disabled) { background: #23b586; }
.btn-speak.speaking { animation: btn-speak-pulse 1s ease-in-out infinite; }
@keyframes btn-speak-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
.btn-speak:disabled { background: #1e3c32; border-color: #2c5346; color: #ffffff; cursor: default; }
.panel-divider {
  width: 8px; flex-shrink: 0;
  background: rgba(255,255,255,0.04);
  border-left:  1px solid rgba(255,255,255,0.07);
  border-right: 1px solid rgba(255,255,255,0.07);
  cursor: col-resize;
  display: flex; align-items: center; justify-content: center;
  transition: background 0.15s; position: relative; z-index: 10;
}
.panel-divider:hover  { background: rgba(24,95,165,0.25); }
.panel-divider:active { background: rgba(24,95,165,0.40); }
.divider-grip {
  color: rgba(255,255,255,0.35); font-size: 12px; writing-mode: vertical-rl;
  letter-spacing: 3px; user-select: none; line-height: 1; pointer-events: none;
}
.panel-divider:hover .divider-grip { color: rgba(255,255,255,0.70); }
.right-panel {
  flex: 1; min-width: 200px;
  display: flex; flex-direction: column;
  background: #ffffff; overflow: hidden;
  position: relative;
}
/* Diagram panel's own header row (2026-08-10, per Harding's rearrangement
   mockup) — the old .page-topbar strip (shared style.css, sat ABOVE both
   panels) is gone; its filename + pan/copy/fullscreen/Node IDs/zoom
   controls moved down into this row instead, so both panels' header rows
   now start at the same height on screen, mirroring the left panel's own
   .panel-header. Reuses .diagram-filename/.diagram-actions/.zoom-controls/
   .node-id-toggle/.btn-pan-mode/.btn-copy-diagram-quick/.btn-fullscreen
   completely unchanged — all of those were already styled for a WHITE
   background (this panel's own bg, and the old page-topbar's bg in
   style.css was white too), so no color changes were needed, only the
   move itself. */
.panel-header--diagram {
  background: #f5f4f0;
  border-bottom: 1px solid rgba(0,0,0,0.10);
}
.sim-widget {
  display: none;
  position: absolute;
  left: 14px; top: 14px;
  z-index: 20;
  width: 200px;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  pointer-events: none;
}
.sim-widget.open { display: flex; animation: simWidgetIn 0.35s ease-out; }
@keyframes simWidgetIn {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}
.sim-caption-row { display: flex; align-items: flex-start; gap: 8px; width: 100%; }
.sim-character { width: 50px; flex-shrink: 0; margin-left: 4px; }
.sim-character svg { width: 100%; display: block; }
.sim-caption {
  flex: 1; padding-top: 4px;
  font-family: 'Inter', sans-serif; font-size: 10.5px; line-height: 1.45;
}
.sim-callout { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; pointer-events: auto; width: 100%; }
.sim-load-btn {
  align-self: flex-start;
  background: var(--btn-orange); color: #fff;
  border: 2px solid #ffffff; border-radius: 20px;
  font-family: 'Inter', sans-serif; font-size: 12px; font-weight: 800;
  letter-spacing: 0.04em; padding: 6px 16px; cursor: pointer;
  box-shadow: 0 3px 10px rgba(var(--btn-orange-rgb),0.5), 0 0 0 2px var(--btn-orange);
  animation: simLoadPulse 1.8s ease-in-out infinite;
  transition: background 0.15s, transform 0.1s;
}
.sim-load-btn:hover  { background: var(--btn-orange-hover); }
.sim-load-btn:active { transform: scale(0.96); }
@keyframes simLoadPulse {
  0%, 100% { box-shadow: 0 3px 10px rgba(var(--btn-orange-rgb),0.5), 0 0 0 2px var(--btn-orange), 0 0 0 0 rgba(var(--btn-orange-rgb),0.45); }
  50%      { box-shadow: 0 3px 14px rgba(var(--btn-orange-rgb),0.6), 0 0 0 2px var(--btn-orange), 0 0 0 7px rgba(var(--btn-orange-rgb),0); }
}
.sim-bubble {
  position: relative;
  background: #185FA5;
  border: 2.5px solid #ffffff;
  border-radius: 10px;
  padding: 10px 12px;
  font-family: 'Inter', sans-serif;
  font-size: 12px; line-height: 1.5; font-weight: 500; color: #ffffff;
  box-shadow: 0 10px 28px rgba(0,0,0,0.24), 0 0 0 4px rgba(24,95,165,0.18);
}
.sim-bubble::after {
  content: ''; position: absolute; left: 22px; bottom: -8px;
  width: 14px; height: 14px; background: #185FA5;
  border-right: 2.5px solid #ffffff;
  border-bottom: 2.5px solid #ffffff;
  transform: rotate(45deg);
}
.sim-persona {
  font-weight: 800; color: #185FA5;
}
.sim-caption-action {
  color: #6b6a63; font-weight: 500;
}
.topbar-diagram-controls {
  display: flex;
  align-items: center;
  gap: 16px;
}
.format-info  { display: flex; align-items: center; gap: 8px; min-width: 0; }
.diagram-filename {
  font-family: 'JetBrains Mono', monospace;
  font-size: 13px; font-weight: 700;
  color: #1a1a18;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.format-badge {
  font-size: 10px; font-weight: 700; padding: 3px 9px; border-radius: 20px;
  background: #E6F1FB; color: #185FA5;
  text-transform: uppercase; letter-spacing: 0.06em;
}
.quality-badge {
  font-size: 10px; font-weight: 700; padding: 3px 9px; border-radius: 20px;
  background: #E1F5EE; color: #1D9E75;
}
.division-badge {
  font-size: 10px; font-weight: 700; padding: 3px 9px; border-radius: 20px;
  background: #FFF3E0; color: #BA7517;
  text-transform: uppercase; letter-spacing: 0.06em;
}
.step-indicator { display: flex; gap: 5px; align-items: center; }
.step-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: #d0cfc8; transition: background 0.3s;
}
.step-dot.done   { background: #1D9E75; }
.step-dot.active { background: #185FA5; animation: dotPulse 0.8s infinite; }
@keyframes dotPulse {
  0%,100% { opacity:1; transform:scale(1); }
  50%      { opacity:0.4; transform:scale(0.75); }
}
.diagram-actions { display: flex; align-items: center; gap: 8px; }
/* Zoom stepper and Node IDs toggle — folded into the orange scheme
   2026-08-05 alongside the rest of the UNIFORM BUTTON SYSTEM block above,
   per Harding's explicit "ALL, I mean ALL buttons" instruction. Kept as
   their own compact pill shape rather than forced to the 30px button
   height, since they're stepper/toggle controls, not one-shot actions. */
.zoom-controls {
  display: flex; align-items: center;
  height: 34px; box-sizing: border-box;
  background: var(--btn-orange); border: 1px solid var(--btn-orange-hover);
  border-radius: 7px; padding: 3px; gap: 1px;
}
.zoom-btn {
  width: 26px; height: 26px; background: transparent;
  border: none; border-radius: 5px;
  cursor: pointer; font-size: 17px; font-weight: 700;
  color: #ffffff; display: flex; align-items: center; justify-content: center;
  transition: all 0.12s; line-height: 1;
}
.zoom-btn:hover { background: rgba(255,255,255,0.22); color: #ffffff; }
.zoom-pct {
  font-size: 11px; font-weight: 700; color: #ffffff;
  min-width: 40px; text-align: center;
  font-family: 'JetBrains Mono', monospace;
  background: transparent; border: none; cursor: pointer; padding: 0 4px;
}
.zoom-pct:hover { color: #ffffff; opacity: 0.85; }
/* .btn-pan-mode base/active look comes from the UNIFORM BUTTON SYSTEM block. */
.diagram-output.pan-mode-active { cursor: grab; }
.diagram-output.pan-mode-active.pan-mode-grabbing { cursor: grabbing; }
.diagram-output.pan-mode-active .diagram-wrap,
.diagram-output.pan-mode-active svg { user-select: none; }
.node-id-toggle {
  display: flex; align-items: center; gap: 5px;
  height: 34px; box-sizing: border-box;
  background: var(--btn-orange); border: 1px solid var(--btn-orange-hover);
  border-radius: 7px; padding: 0 8px;
  font-size: 11px; font-weight: 600; color: #ffffff;
  cursor: pointer; user-select: none;
}
.node-id-toggle input[type="checkbox"] {
  width: 14px; height: 14px; cursor: pointer; accent-color: #ffffff;
}
/* Lo-Fi/Hi-Fi ingestion toggle (2026-08-07, per Harding — cost-optimization
   pass). Originally: Rich/Hi-Fi (unchecked) sat in a neutral gray outline
   with an empty checkbox, Bare/Lo-Fi (checked) turned solid teal with a
   checkmark. Per Harding (2026-08-07, live feedback): the unchecked state
   read as "off"/disabled rather than as a real, deliberately-chosen mode —
   Hi-Fi (Pro) is the DEFAULT, not a lesser option, and shouldn't look like
   one. Redesigned so BOTH states are solid, affirmative, and always show a
   checkmark — the control communicates "here is which of two modes is
   active" rather than "here is a box you haven't checked yet." Hi-Fi (Pro)
   gets brand blue (matches its own "this is the trustworthy/full-fidelity
   choice" role); Lo-Fi (Saver) keeps the existing teal "this saves you
   something" color already used for success/saved states elsewhere. The
   real `<input type="checkbox">` stays in the DOM (keyboard/Tab/Space and
   screen-reader semantics all keep working exactly as before — clicking
   anywhere in the label still toggles it), just visually hidden via the
   standard accessible-custom-checkbox technique, in favor of the always-
   visible ✓ glyph doing the "checked" signaling instead. */
.ingest-mode-toggle {
  position: relative;
  display: flex; align-items: center; gap: 5px;
  height: 30px; box-sizing: border-box;
  background: #185FA5; border: 1px solid #185FA5;
  border-radius: 6px; padding: 0 10px;
  font-size: 11px; font-weight: 700; color: #ffffff;
  cursor: pointer; user-select: none; flex-shrink: 0;
  transition: background 0.15s, border-color 0.15s, filter 0.15s, box-shadow 0.15s;
  /* 2026-08-15, per Harding: extends the soft white halo already used on
     PRODUCTION (see .obfuscation-option.active below) to every pill-style
     control in this row, for one consistent "finish". Value hardcoded
     (not a CSS variable) per this project's standing no-CSS-variables-in-
     shipped-HTML rule -- kept identical, by hand, everywhere it's used:
     0 0 5px rgba(255,255,255,0.30). */
  box-shadow: 0 0 5px rgba(255,255,255,0.30);
}
.ingest-mode-toggle:hover { filter: brightness(1.08); }
.ingest-mode-toggle input[type="checkbox"] {
  /* Standard accessible-hidden-checkbox pattern: not display:none (which
     would drop it from the accessibility tree and break Tab/Space), just
     visually removed. The label wrapping it still delegates clicks/taps to
     it natively, no JS needed for that part. */
  position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none;
}
.ingest-mode-check { font-weight: 800; }
.ingest-mode-toggle:has(input:checked),
.ingest-mode-toggle.active {
  background: #1D9E75; border-color: #1D9E75;
}
/* ⓘ info-button look, shared by the toggle's own info button and the
   cache-pill's (2026-08-07, per Harding): a plain transparent glyph read as
   invisible/not-really-there against this dark header. Both now get a
   real, visible circular fill instead — the "I" always sits OUTSIDE
   whatever status/control it explains, as its own separate click target,
   never embedded inside another element's own text or background. */
/* 2026-08-10, per Harding's rearrangement mockup: the circular ⓘ took too
   much horizontal room in the row — replaced with a tiny, mouse-pointer-
   size "?" that's still its own separate click target (same placement
   rule as before: OUTSIDE whatever it explains, never embedded in another
   element's own text/background). Renamed from .ingest-mode-info-btn/
   .cache-status-info-btn — ids on the buttons are unchanged, only the
   class + glyph changed. */
.tiny-info-btn {
  height: 15px; width: 15px; box-sizing: border-box;
  background: transparent; border: none; border-radius: 50%;
  color: rgba(255,255,255,0.55);
  font-size: 11px; font-weight: 700; line-height: 1;
  cursor: pointer; flex-shrink: 0; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background 0.15s, color 0.15s;
}
.tiny-info-btn:hover {
  background: rgba(255,255,255,0.20); color: #ffffff;
}
/* 2026-08-11, per Harding: even at 15px the "?" was still its own flex
   item in the panel-header row, claiming that 15px + the row's own gap
   every time -- exactly what the circular ⓘ was already criticized for.
   Genuinely zero horizontal footprint means taking it OUT of flex flow
   entirely, not just shrinking it further. .info-btn-anchor wraps the
   toggle/pill + its info button together (see HTML), gives that pair one
   positioning context, and the button becomes position:absolute inside
   it -- so the row only ever reserves space for the toggle/pill's own
   width, never the button's. Perched on the outer top-right corner (same
   "OUTSIDE whatever it explains" placement rule as before, just achieved
   with negative offsets instead of row order), still its own real,
   separately clickable <button>, not a decorative pseudo-element. */
.info-btn-anchor {
  position: relative;
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
}
.tiny-info-btn--corner {
  position: absolute;
  top: -6px;
  right: -6px;
  z-index: 5;
  height: 13px; width: 13px;
  font-size: 9px;
  background: rgba(15,17,23,0.85);
  border: 1px solid rgba(255,255,255,0.40);
  color: rgba(255,255,255,0.75);
}
.tiny-info-btn--corner:hover {
  background: #0f1117; color: #ffffff; border-color: #ffffff;
}
/* Redaction Glossary eye / eye-off pair (2026-08-10, per Harding's
   rearrangement mockup) — replaces the single 🔒 button. Two separate,
   always-visible icons shown together (not one button whose icon swaps by
   state, per Harding's explicit answer when asked) — both open the same
   glossary modal. .active (toggled on both by updateRedactionBadge() in
   redaction.js when the glossary has at least one real->placeholder pair)
   is a value-add status cue, not a behavior change — same green-highlight
   convention #redactionGlossaryBtn.active already used before this pass. */
.redaction-icon-btn {
  height: 30px; width: 30px; box-sizing: border-box;
  background: rgba(255,255,255,0.10); border: none; border-radius: 6px;
  color: rgba(255,255,255,0.85);
  font-size: 15px; cursor: pointer; flex-shrink: 0; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background 0.15s, color 0.15s;
}
.redaction-icon-btn:hover { background: rgba(255,255,255,0.24); color: #ffffff; }
.redaction-icon-btn.active { background: #1D9E75; color: #ffffff; }
/* Obfuscation ON/OFF (2026-08-11, per Harding — replaces the eye/eye-off
   pair above, .redaction-icon-btn kept only because .opt-info-modal/etc.
   reference nothing here that breaks by leaving it defined unused). Two
   real, mutually-exclusive radio pills (native <input type="radio">,
   shared name) stacked in one compact orange pill, matching the
   UNIFORM BUTTON SYSTEM's 30px row height. Selecting one directly
   deselects the other — see onObfuscationToggle() in redaction.js.
   Default is ON (checked in the HTML). The pills only ever flip the
   enabled/disabled state; adding/editing real→placeholder pairs moved to
   the separate corner .obfuscation-edit-btn, same corner-badge pattern as
   .tiny-info-btn--corner — clicking a radio pill toggling state shouldn't
   also yank the glossary modal open every time. */
/* 2026-08-11, per Harding: split from one shared pill into two full,
   independently-colored buttons stacked vertically with a small gap
   (was a single dark-orange "double-decker" pill, hard to tell which
   half was active). Green = Glossary Ingestion (matches the teal used
   everywhere else for this feature -- the Obfuscation modal's tagline,
   the Included-formats checkmark icon, etc.), black = Production (the
   "nothing special happening" normal-work state). The tiny glasses SVG
   icon was dropped per Harding's follow-up -- at 11x11px it was too
   small to read as anything, and now that each state is its own
   full-width, solid-colored button, the color + text alone is
   unambiguous without it.
   2026-08-13, second pass, per Harding: the two stacked 15px pills from
   the note above are now laid out as a ROW instead of a column, each
   grown to the full 30px UNIFORM BUTTON SYSTEM height ("the TRAINING
   button, now, finally, has its own full-fledged-sized button... the
   midget PRODUCTION button has expanded and now will measure the same
   height as all other buttons"). The vacated stacked-pill shape didn't
   go to waste -- see .glossary-storage-toggle right below, which reuses
   it for the new Local/GitHub Glossary-storage choice sandwiched
   between TRAINING and PRODUCTION. */
.obfuscation-toggle {
  display: flex; flex-direction: row; align-items: stretch; gap: 10px;
}
/* 2026-08-15, per Harding: groups the either-or pairs (Production/Training
   here, Local/GitHub below) inside their own rounded-rectangle border so the
   grouping reads visually, not just through shared radio-input semantics.
   3px padding keeps the group border from touching the inner pills' own
   borders. Purely visual -- no behavior change, radiogroup semantics stay
   on the existing #obfuscationToggle/#glossaryStorageToggle elements. */
.mode-group {
  display: flex; flex-direction: row; align-items: stretch; gap: 6px;
  border: 1.5px solid rgba(255,255,255,0.30);
  border-radius: 9px;
  padding: 3px;
}
.obfuscation-option {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  height: 30px; box-sizing: border-box;
  padding: 0 10px; border-radius: 6px;
  /* 2026-08-14, per Harding's ruler screenshot: added a real, always-on
     border so both TRAINING and PRODUCTION read as buttons even when
     inactive (was invisible/borderless until the .active box-shadow hit),
     matching the outline treatment given to .glossary-storage-option
     below. box-sizing:border-box keeps the total height locked at 30px
     with this border added, same as .ingest-mode-toggle's own border. */
  border: 1.5px solid rgba(255,255,255,0.30);
  font-size: 11px; font-weight: 700; letter-spacing: 0.02em;
  line-height: 1; white-space: nowrap;
  color: rgba(255,255,255,0.75); cursor: pointer;
  opacity: 0.62;
  transition: opacity 0.15s, border-color 0.15s, box-shadow 0.15s;
  /* 2026-08-15, per Harding: soft white halo, always on (not gated by
     .active) -- this used to be PRODUCTION-only by accident of it always
     being the active/default pill; TRAINING gets the identical finish now
     too, same hardcoded value used on every other pill in this row. */
  box-shadow: 0 0 5px rgba(255,255,255,0.30);
}
.obfuscation-option input[type="radio"] {
  position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none;
}
.obfuscation-option.active {
  color: #ffffff;
  opacity: 1;
  border-color: rgba(255,255,255,0.55);
  /* 2026-08-14, per Harding's ruler screenshot: this used to be an outward
     box-shadow ring (0 0 0 1.5px), which visibly grew the active pill past
     its own 30px box -- that's exactly why the active TRAINING button read
     taller than the Hi-Fi/cache siblings next to it. Switched to inset so
     the "active" cue stays inside the fixed 30px footprint instead of
     bleeding outside it. 2026-08-15: layered on top of the always-on
     outer halo above (base rule) so active still reads brighter than
     inactive, instead of losing that distinction now that both have a
     halo. */
  box-shadow: 0 0 5px rgba(255,255,255,0.30), inset 0 0 0 1.5px rgba(255,255,255,0.85);
}
#obfuscationOnLabel  { background: #1D9E75; }
#obfuscationOffLabel { background: #1a1a18; }
/* Glossary storage: Local vs. GitHub (2026-08-13, per Harding) -- which
   backend holds THIS diagram's Glossary. Sits between the now full-height
   TRAINING/PRODUCTION buttons, styled as two stacked 13px pills (13+4+13
   = 30px) so the group lines up exactly with the 30px height on either
   side of it. Blue = GitHub, matching the blue already used for the
   Hi-Fi/Lo-Fi toggle's own inactive state elsewhere in this same row --
   this is a data-destination choice, not a pass/fail state, so it
   deliberately doesn't borrow the green/black obfuscation-option colors.
   See onGlossaryStorageToggle() in js/redaction.js and
   api/glossary-proxy.php for the actual read/write calls. */
/* 2026-08-15, per Harding: upgraded from the stacked 13+4+13=30px "midget"
   pills to full 30px buttons laid out as a row, matching the
   PRODUCTION/TRAINING pair and the rest of the UNIFORM BUTTON SYSTEM --
   the same treatment PRODUCTION/TRAINING itself got in the 2026-08-13
   second pass (see the comment above .obfuscation-option). Wrapped in the
   same rounded-rectangle group border as .mode-group above so Local/GitHub
   reads as its own either-or pair. */
.glossary-storage-toggle {
  display: flex; flex-direction: row; align-items: stretch; gap: 6px;
  border: 1.5px solid rgba(255,255,255,0.30);
  border-radius: 9px;
  padding: 3px;
  flex-shrink: 0;
}
.glossary-storage-option {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  height: 30px; box-sizing: border-box;
  padding: 0 10px; border-radius: 6px;
  border: 1.5px solid rgba(255,255,255,0.30);
  font-size: 11px; font-weight: 700; letter-spacing: 0.02em;
  line-height: 1; white-space: nowrap;
  background: rgba(255,255,255,0.12);
  color: rgba(255,255,255,0.75); cursor: pointer;
  opacity: 0.62;
  transition: opacity 0.15s, border-color 0.15s, box-shadow 0.15s, background 0.15s;
  /* 2026-08-15, per Harding: same always-on halo as .obfuscation-option
     above -- one consistent finish across every pill in this row. */
  box-shadow: 0 0 5px rgba(255,255,255,0.30);
}
.glossary-storage-option input[type="radio"] {
  position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none;
}
.glossary-storage-option.active {
  color: #ffffff;
  opacity: 1;
  background: #185FA5;
  border-color: rgba(255,255,255,0.55);
  /* Same outward-ring-to-inset fix as .obfuscation-option.active above,
     for the same reason and on the same day. 2026-08-15: layered on the
     always-on halo (base rule) same as .obfuscation-option.active. */
  box-shadow: 0 0 5px rgba(255,255,255,0.30), inset 0 0 0 1.5px rgba(255,255,255,0.85);
}
/* Owner-only gate, same .guest-restricted convention used by githubBtn/
   slackBtn/speakBtn (see applyRoleRestrictions() in js/api-client.js) --
   GitHub Glossary storage writes into the same shared repo as "Save to
   GitHub", so it gets the same restriction for the same reason. */
.glossary-storage-option.guest-restricted {
  opacity: 0.3; cursor: not-allowed;
}
/* 2026-08-13, per Harding: was a pencil glyph -- replaced with the live
   Glossary item count instead, so the badge tells you something (how much
   you've trained) rather than just "click to edit." Text is always black
   per his explicit ask, so the badge needed a light background instead of
   the old dark circle (black-on-dark would fail the project's own
   contrast rule). Pill-shaped with a min-width instead of a fixed 15px
   circle so a 2-digit count doesn't clip. */
.obfuscation-edit-btn {
  position: absolute; top: -6px; right: -6px; z-index: 5;
  height: 15px; min-width: 15px; box-sizing: border-box;
  background: #ffffff; border: 1px solid rgba(0,0,0,0.30);
  border-radius: 999px; color: #1a1a18;
  font-size: 9px; font-weight: 800; cursor: pointer; padding: 0 3px;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background 0.15s, border-color 0.15s;
}
.obfuscation-edit-btn:hover { background: #f0efe9; border-color: rgba(0,0,0,0.45); }
.obfuscation-edit-btn.active { background: #E1F5EE; border-color: #1D9E75; }
/* Cache-warmth pill (2026-08-07, per Harding) — tells a visitor whether
   their NEXT import will land inside the import endpoint's 1-hour prompt-
   cache window (warm, cheap cache-read rate) or outside it (cold, a small
   one-time cache-write premium). Deliberately NOT part of the Lo-Fi/Hi-Fi
   toggle itself — which mode is picked and whether the cache is warm are
   two independent things, and folding them together would wrongly imply
   the toggle controls cache timing. Pure status display, not a control —
   the separate .cache-status-info-btn right after it (same pattern as the
   toggle + its own ⓘ) is the actual click target for the explanation.
   Colors deliberately bold and saturated rather than a subtle tint: an
   earlier low-opacity version was nearly invisible against this panel's
   own dark background, defeating the point of an at-a-glance indicator. */
.cache-status-pill {
  display: none; /* JS shows it once a timestamp exists to evaluate */
  align-items: center; height: 30px; box-sizing: border-box;
  border-radius: 6px; padding: 0 10px;
  font-family: 'JetBrains Mono', monospace;
  font-size: 10.5px; font-weight: 700;
  flex-shrink: 0; white-space: nowrap;
  border: none;
  /* 2026-08-15, per Harding: same always-on halo as the toggles above --
     one consistent finish, applied here to both the warm and cold variant
     rather than just one, since the halo isn't a status cue on its own. */
  box-shadow: 0 0 5px rgba(255,255,255,0.30);
  /* 2026-08-15, per Harding: room for the countdown span at the edge,
     still inside the pill's own 30px/border-radius box -- a real child
     element in normal flex flow, not an absolutely-positioned overlay
     that could spill past the border. */
  gap: 8px;
}
.cache-status-pill.warm { background: #C1440E; color: #ffffff; }
.cache-status-pill.cold { background: #DCEBFA; color: #0f3a5f; }
/* 2026-08-15, per Harding: live 59:00 -> 0:00 countdown to the next cache
   expiry, sitting at the trailing edge of the pill (JS: updateCacheStatusCountdown()
   in js/ui-handlers.js). Only rendered while warm -- see updateCacheStatusPill().
   Slightly dimmer + smaller than the "cache: WARM" label so the label stays
   the primary read and the countdown is a secondary, at-a-glance detail. */
.cache-status-countdown {
  font-variant-numeric: tabular-nums;
  font-size: 9.5px; font-weight: 700;
  opacity: 0.80;
  margin-left: auto;
}
.diagram-output {
  flex: 1; overflow: auto; padding: 22px;
  position: relative;
}
.role-legend {
  display: none;
  position: absolute;
  bottom: 42px; right: 14px;
  background: rgba(245,244,240,0.96);
  border: 1px solid rgba(0,0,0,0.12);
  border-radius: 6px;
  padding: 8px 10px;
  font-size: 11px;
  max-width: 160px;
  z-index: 40;
  box-shadow: 0 2px 10px rgba(0,0,0,0.10);
}
.role-legend.show { display: block; }
/* Drag handle (2026-08-07, per Harding: the legend was blocking the
   diagram underneath it with no way to move it). Sits flush at the top of
   the box, its own row, so it never gets confused with the role rows below
   it — same "···" grip glyph .panel-divider already uses elsewhere. */
.role-legend-drag-handle {
  cursor: move;
  display: flex; align-items: center; justify-content: center;
  margin: -8px -10px 6px -10px;
  padding: 2px 0 3px;
  color: rgba(0,0,0,0.30);
  font-size: 12px;
  letter-spacing: 3px;
  border-bottom: 1px solid rgba(0,0,0,0.08);
  border-radius: 6px 6px 0 0;
  user-select: none;
  touch-action: none;
}
.role-legend-drag-handle:hover { color: rgba(0,0,0,0.55); background: rgba(0,0,0,0.04); }
.role-legend.dragging { opacity: 0.88; box-shadow: 0 6px 18px rgba(0,0,0,0.22); }
.role-legend-title {
  font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  font-size: 10px; color: #6b6a63; margin-bottom: 4px;
}
.role-legend-caveat {
  font-weight: 400; text-transform: none; letter-spacing: normal;
  font-size: 9px; color: #6b6a63; font-style: italic;
}
.role-legend-row { display: flex; gap: 6px; padding: 1px 0; }
.role-legend-code { font-weight: 700; color: #185FA5; min-width: 24px; flex-shrink: 0; }
.role-legend-name { color: #1a1a18; }
.diagram-wrap {
  min-width: 100%; min-height: 100%;
  display: flex; align-items: flex-start; justify-content: center;
}
.diagram-wrap svg { max-width: 100%; height: auto; user-select: text; }
.diagram-placeholder {
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; min-height: 200px; width: 100%;
  color: #9a9990; gap: 10px; text-align: center;
}
.diagram-placeholder .icon { font-size: 32px; opacity: 0.4; }
.diagram-placeholder p     { font-size: 12.5px; line-height: 1.5; }
.diagram-placeholder .icon.loading {
  font-size: 64px;
  color: #BA7517;
  opacity: 1;
  filter: drop-shadow(0 1px 3px rgba(186,117,23,0.35));
  animation: icon-spin-360 1.1s linear infinite;
}
@keyframes icon-spin-360 {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
.diagram-error {
  color: #b03030; font-size: 11.5px;
  background: #fde8e8; border-radius: 8px;
  padding: 14px; font-family: 'JetBrains Mono', monospace; width: 100%;
}
.mermaid g.node.changed rect,
.mermaid g.node.changed polygon,
.mermaid g.node.changed ellipse,
.mermaid g.node.changed circle {
  fill: #FAEEDA !important;
  stroke: #BA7517 !important;
  stroke-width: 2.5 !important;
}
.mermaid g.node.changed text {
  fill: #7a4d0f !important;
}
.modal-overlay {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.52);
  z-index: 500;
  display: none; align-items: center; justify-content: center;
}
.modal-overlay.open { display: flex; }
/* 2026-08-11, per Harding: the shared 52%-black wash above reads as
   near-invisible on THIS page specifically -- the app's own base is
   already a near-black dark theme (#0f1117), so a semi-transparent black
   overlay barely changes its brightness, and every toolbar control is a
   saturated, high-contrast color (orange buttons, white text, colored
   mode pills) that stays fully legible straight through a 52% wash. The
   practical effect, confirmed live even in a fresh Private/Incognito tab
   with zero session state: an unauthenticated visitor hitting
   ?mode=signin sees the entire toolbar -- Ingestion/Production mode
   pills, Hi-Fi/Lo-Fi toggle, Node IDs, zoom, the .drawio/.vsdx/.bpmn/.mmd
   export row, the version badge -- in full color behind the login box,
   which discloses in-development feature surface to anyone with the
   URL, logged in or not. #loginOverlay specifically (not the other
   modals on this page, which only ever show to an already-authenticated
   visitor) gets a near-opaque wash plus a real blur, so the toolbar
   behind it is genuinely unreadable, not just slightly dimmed. */
#loginOverlay {
  background: rgba(10,12,16,0.94);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
.file-picker {
  background: #f0efe8;
  border: 1.5px solid #a8a7a0;
  border-radius: 5px;
  width: 500px;
  box-shadow: 0 12px 40px rgba(0,0,0,0.40);
  font-family: 'Inter', sans-serif;
}
.fp-titlebar {
  background: #185FA5;
  color: #fff;
  padding: 8px 14px;
  font-size: 12px; font-weight: 700;
  display: flex; align-items: center; justify-content: space-between;
  border-radius: 3px 3px 0 0;
}
.fp-close {
  background: var(--btn-orange); border: none; color: #ffffff;
  font-size: 15px; cursor: pointer; line-height: 1;
  width: 30px; height: 30px; display: flex; align-items: center; justify-content: center;
  border-radius: 6px; transition: background 0.12s; flex-shrink: 0;
}
.fp-close:hover { background: var(--btn-orange-hover); color: #fff; }
.fp-location {
  background: #e4e3dc;
  border-bottom: 1px solid #c8c7c0;
  padding: 6px 14px;
  font-size: 11px; color: #5a5a52;
  display: flex; align-items: center; gap: 6px;
}
.fp-body {
  margin: 10px;
  background: #ffffff;
  border: 2px inset #d0cfc8;
  border-radius: 2px;
  min-height: 200px;
  padding: 4px;
}
.file-item {
  display: flex; align-items: center; padding: 8px 10px;
  gap: 12px; border-radius: 3px; cursor: pointer;
  transition: background 0.08s;
}
.file-item:hover  { background: #cce4ff; }
.file-item:active { background: #99ccff; }
.file-icon  { font-size: 18px; flex-shrink: 0; }
.file-name  { flex: 1; font-size: 12.5px; font-weight: 500; color: #1a1a18; }
.file-ext   {
  font-size: 9.5px; font-weight: 700; padding: 2px 7px; border-radius: 3px;
  background: #E6F1FB; color: #185FA5; letter-spacing: 0.06em; white-space: nowrap;
}
.file-ext.teal   { background: #E1F5EE; color: #1D9E75; }
.file-ext.purple { background: #EDE9FE; color: #6d28d9; }
.file-ext.amber  { background: #FEF3C7; color: #b45309; }
.file-ext.green  { background: #D1FAE5; color: #059669; }
.fp-footer {
  padding: 10px 14px;
  display: flex; justify-content: flex-end; gap: 6px;
  border-top: 1px solid #d0cfc8;
  background: #f0efe8;
  border-radius: 0 0 3px 3px;
}
.fp-btn-cancel {
  height: 30px; padding: 0 16px;
  background: var(--btn-orange); color: #ffffff;
  border: none; border-radius: 6px;
  font-size: 11.5px; font-weight: 700; cursor: pointer;
  font-family: 'Inter', sans-serif;
}
.fp-btn-cancel:hover { background: var(--btn-orange-hover); }
::-webkit-scrollbar       { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: rgba(0,0,0,0.25); border-radius: 4px; }
::-webkit-scrollbar-thumb { background: #3a4a5e; border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: #4e627e; }
.diagram-output::-webkit-scrollbar-track { background: #dddcd6; border-radius: 4px; }
.diagram-output::-webkit-scrollbar-thumb { background: #7a7a6c; border-radius: 4px; }
.diagram-output::-webkit-scrollbar-thumb:hover { background: #555550; }
@media (max-width: 768px) {
  .panels { flex-direction: column; }
  .left-panel  { width:100%!important; max-width:100%; height:42vh; min-width:unset; flex-shrink:0; }
  .panel-divider { width:100%; height:8px; cursor:row-resize; border-left:none; border-right:none;
                   border-top:1px solid rgba(255,255,255,0.07); border-bottom:1px solid rgba(255,255,255,0.07); }
  .divider-grip { writing-mode:horizontal-tb; letter-spacing:3px; }
  .right-panel  { min-width:unset; }
  .file-picker  { width: 95vw; }
  .diagram-filename { display: none; }
  /* Phone widths are narrower than the 130px base cap allows; wrap is
     already handled globally by .panel-header, this just tightens the
     truncation further so less wrapping is needed in the first place. */
  .logged-in-email { max-width: 80px; }
}
/* --- Login gate --- */
.login-box {
  background: #f5f4f0;
  border: 1.5px solid rgba(0,0,0,0.12);
  border-radius: 5px;
  width: 380px;
  box-shadow: 0 12px 40px rgba(0,0,0,0.40);
  font-family: "Segoe UI", Arial, sans-serif;
  color: #1a1a18;
}
.login-account-type {
  display: flex;
  gap: 8px;
  margin-bottom: 14px;
}
.login-radio-option {
  flex: 1;
  display: flex;
  align-items: flex-start;
  gap: 7px;
  padding: 10px 10px;
  border: 1.5px solid rgba(0,0,0,0.14);
  border-radius: 6px;
  background: #ffffff;
  cursor: pointer;
  font-size: 11.5px;
  font-weight: 600;
  color: #6b6a63;
  line-height: 1.35;
  transition: border-color 0.12s, background 0.12s;
}
.login-radio-option input[type="radio"] { margin-top: 2px; accent-color: #185FA5; flex-shrink: 0; }
.login-radio-option small { display: block; font-weight: 500; color: #9a968c; font-size: 10.5px; }
.login-radio-option:has(input:checked), .login-radio-option.checked {
  border-color: #185FA5;
  background: #E6F1FB;
  color: #0f3d6b;
}
.login-radio-option:has(input:checked) small, .login-radio-option.checked small { color: #185FA5; }
.login-business-fields { display: none; }
.login-business-fields.show { display: block; }
.login-titlebar {
  background: #185FA5;
  color: #fff;
  padding: 10px 14px;
  font-size: 13px;
  font-weight: 700;
  border-radius: 3px 3px 0 0;
}
.login-body { padding: 18px 16px; }
.login-intro { font-size: 12.5px; line-height: 1.5; color: #6b6a63; margin: 0 0 14px; }
.login-privacy { font-size: 11px; line-height: 1.4; color: #9a968c; margin: 10px 0 0; text-align: center; }
.login-mode-switch { font-size: 11.5px; line-height: 1.4; color: #9a968c; margin: 8px 0 0; text-align: center; }
.login-mode-switch a { color: #185FA5; font-weight: 600; text-decoration: none; }
.login-mode-switch a:hover { text-decoration: underline; }
.login-body label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  margin-bottom: 5px;
  color: #6b6a63;
}
.login-body input[type="password"], .login-body input[type="email"], .login-body input[type="text"] {
  width: 100%;
  padding: 9px 10px;
  margin-bottom: 12px;
  box-sizing: border-box;
  border: 1px solid rgba(0,0,0,0.12);
  border-radius: 5px;
  font-size: 14px;
  background: #fff;
  color: #1a1a18;
}
.login-error {
  background: #FAEEDA;
  color: #BA7517;
  border-radius: 5px;
  padding: 8px 10px;
  font-size: 12px;
  margin-bottom: 12px;
  display: none;
  line-height: 1.5;
}
.login-error.show { display: block; }
.login-business-warning {
  background: #FBE7E5;
  color: #B0281A;
  border: 1px solid rgba(176,40,26,0.35);
  border-radius: 5px;
  padding: 8px 10px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.5;
  text-align: center;
  margin: 10px 0 14px;
}
.login-error a { color: #185FA5; text-decoration: underline; font-weight: 600; }
.login-error a:hover { color: #134b85; }
.login-pending { display: none; }
.login-pending.show { display: block; }
.login-pending-msg {
  background: #E1F5EE;
  color: #1D9E75;
  border-radius: 5px;
  padding: 12px 10px;
  font-size: 13px;
  line-height: 1.4;
  margin-bottom: 12px;
  text-align: center;
}
.login-submit {
  width: 100%;
  height: 30px; padding: 0 14px;
  background: var(--btn-orange);
  color: #fff;
  border: none;
  border-radius: 6px;
  font-family: 'Inter', sans-serif;
  font-size: 11.5px;
  font-weight: 700;
  cursor: pointer;
}
.login-submit:hover { background: var(--btn-orange-hover); }
.login-submit:disabled { opacity: 0.5; cursor: default; }
.login-btn-row { display: flex; gap: 8px; }
.login-btn-row .login-submit { width: auto; flex: 1; }
.login-cancel {
  flex: 1;
  height: 30px; padding: 0 14px;
  background: var(--btn-orange);
  color: #ffffff;
  border: none;
  border-radius: 6px;
  font-family: 'Inter', sans-serif;
  font-size: 11.5px;
  font-weight: 700;
  cursor: pointer;
}
.login-cancel:hover { background: var(--btn-orange-hover); }
/* .btn-logout, .btn-diff, .btn-drawio, .btn-vsdx, .btn-bpmn, .btn-share base
   look comes from the UNIFORM BUTTON SYSTEM block. Only the success flash
   (.copied) is button-specific and already handled there too. */

/* ─── Inline SVG icon system ───
   Used instead of webfont <i> classes wherever the exact glyph or brand
   color matters (Save/floppy, Share, Diff, copy-to-clipboard) or where a
   real multi-color mark is required (Slack). Colored via currentColor (see
   `.icon-inline { color: currentColor }` in the UNIFORM BUTTON SYSTEM block)
   so it always matches whatever text color its parent button has. */
.icon-inline {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  vertical-align: -3px;
}
.btn-copy-code-quick, .btn-copy-diagram-quick {
  flex-shrink: 0;
}
.version-badge {
  position: fixed;
  bottom: 10px;
  right: 14px;
  background: #1a1a18;
  color: #e8a33d;
  /* 2026-08-14, per Harding: at the old 11px, "v2.90" got misread as
     "v2.98" from a screenshot -- JetBrains Mono's slashed/dotted zero is
     meant to make 0 unmistakable from 8, but that detail was too small to
     survive screen-capture compression at this size. Bumped size, weight,
     and letter-spacing so every digit -- especially 0 vs 8 vs 9 -- stays
     legible even scaled down in a screenshot. Font-family unchanged,
     JetBrains Mono is still the right choice, it just needed more room. */
  font-family: 'JetBrains Mono', monospace;
  font-size: 13px;
  font-weight: 700;
  padding: 4px 11px;
  border-radius: 5px;
  border: 1px solid rgba(232,163,61,0.45);
  letter-spacing: 0.06em;
  z-index: 9999;
  pointer-events: none;
}
.diff-box {
  background: #f0efe8;
  border: 1.5px solid #a8a7a0;
  border-radius: 5px;
  width: min(1600px, 92vw);
  max-height: min(1100px, 92vh);
  display: flex;
  flex-direction: column;
  box-shadow: 0 12px 40px rgba(0,0,0,0.40);
  font-family: 'Inter', sans-serif;
}
.diff-titlebar {
  background: #185FA5;
  color: #fff;
  padding: 8px 14px;
  font-size: 12px; font-weight: 700;
  display: flex; align-items: center; justify-content: space-between;
  border-radius: 3px 3px 0 0;
  flex-shrink: 0;
}
.diff-summary {
  padding: 14px 18px 0 18px;
  flex-shrink: 0;
}
.diff-objective {
  font-size: 13.5px; font-weight: 700; color: #1a1a18; margin-bottom: 4px;
}
.diff-explanation {
  font-size: 12px; color: #6b6a63; line-height: 1.5;
}
.diff-tabs {
  display: flex; gap: 6px;
  padding: 14px 18px 0 18px;
  flex-shrink: 0;
}
/* Tabs keep their attached-to-panel top-rounded shape (like the icon-only
   square buttons keep their own shape in the UNIFORM BUTTON SYSTEM block),
   but share the same orange/white color and font family/weight as every
   other button. Active tab inverts, same convention as .btn-pan-mode.active. */
.diff-tab {
  background: var(--btn-orange);
  border: none;
  border-radius: 6px 6px 0 0;
  padding: 7px 16px;
  font-family: 'Inter', sans-serif;
  font-size: 11.5px; font-weight: 700;
  color: #ffffff;
  cursor: pointer;
}
.diff-tab:hover:not(.active) { background: var(--btn-orange-hover); }
.diff-tab.active { background: #fff; color: var(--btn-orange); border: 1px solid var(--btn-orange); border-bottom: none; }
.diff-tab-panel { display: none; }
.diff-tab-panel.active { display: block; }
.diff-content {
  background: #fff;
  border-top: 1px solid rgba(0,0,0,0.12);
  flex: 1;
  overflow: auto;
  padding: 18px;
}
.diff-visual-panes {
  display: flex; gap: 22px;
}
.diff-pane { flex: 1; min-width: 0; }
.diff-pane-label {
  font-size: 12px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.03em;
  color: #6b6a63;
  margin-bottom: 8px;
  display: flex; align-items: center; gap: 6px;
}
.diff-pane-label.before::before { content: '●'; color: #BA7517; }
.diff-pane-label.after::before { content: '●'; color: #1D9E75; }
.diff-pane-svg {
  border: 1px solid rgba(0,0,0,0.12);
  border-radius: 5px;
  padding: 14px;
  overflow: auto;
  max-height: 68vh;
  background: #f5f4f0;
}
.diff-pane-svg svg { max-width: 100%; height: auto; }
.diff-empty-note {
  font-size: 11.5px; color: #6b6a63; margin-top: 8px; line-height: 1.5;
}
.diff-code-view {
  font-family: 'JetBrains Mono', monospace;
  font-size: 12px;
  line-height: 1.65;
  color: #000000;
}
.diff-line { padding: 1px 8px; border-radius: 2px; white-space: pre-wrap; word-break: break-word; }
.diff-line.add    { background: #E1F5EE; color: #000000; }
.diff-line.remove { background: #FAEEDA; color: #000000; text-decoration: line-through; opacity: 0.9; }
.diff-line.same   { color: #000000; }
.share-box {
  background: #f0efe8;
  border: 1.5px solid #a8a7a0;
  border-radius: 5px;
  width: 420px;
  box-shadow: 0 12px 40px rgba(0,0,0,0.40);
  font-family: 'Inter', sans-serif;
}
.share-titlebar {
  background: #1D9E75;
  color: #fff;
  padding: 8px 14px;
  font-size: 12px; font-weight: 700;
  display: flex; align-items: center; gap: 6px; justify-content: space-between;
  border-radius: 3px 3px 0 0;
}
.share-body { padding: 16px 18px; }
.share-intro { font-size: 12px; color: #4a4a44; line-height: 1.5; margin-bottom: 12px; }
.share-body label { display: block; font-size: 11px; font-weight: 700; color: #1a1a18; margin-bottom: 5px; }
.share-body input[type="email"] {
  width: 100%; padding: 8px 10px;
  border: 1px solid #b0afa8; border-radius: 4px;
  font-size: 13px; font-family: 'Inter', sans-serif;
  background: #ffffff; color: #1a1a18;
}
.share-body input[type="email"]:focus { outline: 2px solid #1D9E75; outline-offset: 1px; border-color: #1D9E75; }
.share-error { font-size: 11.5px; color: #b03030; margin-top: 8px; }
.share-btn-row { display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px; }
.share-btn-cancel {
  height: 30px; padding: 0 16px; background: var(--btn-orange); color: #ffffff;
  border: none; border-radius: 6px;
  font-family: 'Inter', sans-serif;
  font-size: 11.5px; font-weight: 700; cursor: pointer;
}
.share-btn-cancel:hover { background: var(--btn-orange-hover); }
.share-btn-send {
  height: 30px; padding: 0 18px; background: var(--btn-orange); color: #ffffff;
  border: none; border-radius: 6px;
  font-family: 'Inter', sans-serif;
  font-size: 11.5px; font-weight: 700; cursor: pointer;
}
.share-btn-send:hover:not(:disabled) { background: var(--btn-orange-hover); }
.share-btn-send:disabled { opacity: 0.6; cursor: default; }
.guest-restricted {
  opacity: 0.4 !important;
  cursor: not-allowed !important;
  filter: grayscale(40%);
}
.guardrail-popup-overlay {
  display: none; position: fixed; inset: 0; background: rgba(26,26,24,0.6);
  align-items: center; justify-content: center; z-index: 9999;
}
.guardrail-popup-overlay.open { display: flex; }
.guardrail-popup {
  background: #ffffff; border-radius: 10px; max-width: 420px; width: 90%;
  padding: 30px 28px; text-align: center; box-shadow: 0 20px 60px rgba(0,0,0,0.28);
  border-top: 4px solid #BA7517;
  font-family: "Segoe UI", Arial, sans-serif;
}
.guardrail-popup.tone-info { border-top-color: #1D9E75; }
.guardrail-popup-icon { font-size: 30px; margin-bottom: 10px; }
.guardrail-popup-title { font-size: 17px; font-weight: 700; color: #1a1a18; margin: 0 0 10px; }
.guardrail-popup-message { font-size: 14px; line-height: 1.55; color: #4a4a45; margin: 0 0 22px; }
.guardrail-popup-btn {
  background: var(--btn-orange); color: #fff; border: none; border-radius: 6px;
  height: 30px; padding: 0 14px;
  font-family: 'Inter', sans-serif; font-size: 11.5px; font-weight: 700; cursor: pointer;
}
.guardrail-popup-btn:hover { background: var(--btn-orange-hover); }
.ai-notice-overlay {
  display: none; position: fixed; inset: 0; background: rgba(26,26,24,0.6);
  align-items: center; justify-content: center; z-index: 10000;
}
.ai-notice-overlay.open { display: flex; }
.ai-notice-modal {
  background: #ffffff; border-radius: 10px; max-width: 460px; width: 90%;
  padding: 30px 28px; box-shadow: 0 20px 60px rgba(0,0,0,0.28);
  border-top: 4px solid #185FA5;
  font-family: "Segoe UI", Arial, sans-serif;
}
.ai-notice-icon { font-size: 26px; margin-bottom: 10px; }
.ai-notice-title { font-size: 17px; font-weight: 700; color: #1a1a18; margin: 0 0 12px; }
.ai-notice-body { font-size: 13.5px; line-height: 1.6; color: #4a4a45; margin: 0 0 10px; }
.ai-notice-body strong { color: #1a1a18; }
.ai-notice-footer { display: flex; justify-content: flex-end; gap: 10px; margin-top: 18px; }
.ai-notice-btn {
  background: var(--btn-orange); color: #fff; border: none; border-radius: 6px;
  height: 30px; padding: 0 14px;
  font-family: 'Inter', sans-serif; font-size: 11.5px; font-weight: 700; cursor: pointer;
}
.ai-notice-btn:hover { background: var(--btn-orange-hover); }
.ai-notice-citation {
  font-size: 10.5px; letter-spacing: 0.03em; text-transform: uppercase;
  font-weight: 700; color: #185FA5; background: #E6F1FB;
  border-radius: 4px; padding: 5px 10px; display: inline-block;
  margin-bottom: 14px;
}
/* Bare/Rich explainer splash (2026-08-07, per Harding) — same visual
   language as .ai-notice-modal (white card, colored top border, Segoe UI)
   but wider, since this one walks through three separate benefit
   categories rather than one or two short paragraphs. */
.opt-info-overlay {
  display: none; position: fixed; inset: 0; background: rgba(26,26,24,0.6);
  align-items: center; justify-content: center; z-index: 10000;
}
.opt-info-overlay.open { display: flex; }
.opt-info-modal {
  background: #ffffff; border-radius: 10px; max-width: 540px; width: 92%;
  padding: 28px 30px; box-shadow: 0 20px 60px rgba(0,0,0,0.28);
  border-top: 4px solid #1D9E75;
  font-family: "Segoe UI", Arial, sans-serif;
  max-height: 85vh; overflow-y: auto;
}
.opt-info-title { font-size: 18px; font-weight: 700; color: #1a1a18; margin: 0 0 4px; }
.opt-info-subtitle { font-size: 13px; color: #6b6a63; margin: 0 0 18px; }
.opt-info-section {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 12px 0; border-top: 1px solid rgba(0,0,0,0.08);
}
.opt-info-section:first-of-type { border-top: none; padding-top: 0; }
.opt-info-icon {
  font-size: 20px; flex-shrink: 0; width: 30px; height: 30px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 7px; background: #E1F5EE;
}
.opt-info-body h4 { font-size: 13.5px; font-weight: 700; color: #1a1a18; margin: 0 0 4px; }
.opt-info-body p { font-size: 13px; line-height: 1.55; color: #4a4a45; margin: 0; }
.opt-info-body p + p { margin-top: 6px; }
.opt-info-caveat {
  margin-top: 16px; padding: 12px 14px; background: #FAEEDA;
  border-left: 3px solid #BA7517; border-radius: 4px;
  font-size: 12.5px; line-height: 1.5; color: #4a4a45;
}
/* No footer/button anymore (2026-08-07, per Harding) — both popups close
   on a click anywhere outside the modal box instead (see the overlay's own
   onclick in the HTML, checking event.target === the overlay itself). */

/* Redaction Glossary (2026-08-10) — reuses .opt-info-modal/.opt-info-title/
   .opt-info-subtitle/.opt-info-caveat, this file only adds the row list. */
/* .redaction-icon-btn.active (eye/eye-off pair) is defined earlier in this
   file alongside .redaction-icon-btn itself — this old single-button #id
   rule is gone now that there are two buttons, not one. */
.redaction-row { display: flex; align-items: center; gap: 8px; }
.redaction-row input[type="text"] {
  flex: 1 1 0; min-width: 0; padding: 7px 10px; font-size: 13px;
  border: 1.5px solid rgba(0,0,0,0.18); border-radius: 5px;
  background: #ffffff; color: #1a1a18; font-family: inherit;
}
.redaction-row input[type="text"]:focus { outline: none; border-color: #1D9E75; }
.redaction-arrow { color: #6b6a63; font-size: 14px; flex: 0 0 auto; }
.redaction-row-remove {
  flex: 0 0 auto; width: 26px; height: 26px; border-radius: 5px;
  border: 1.5px solid rgba(0,0,0,0.18); background: #ffffff; color: #a33;
  cursor: pointer; font-size: 13px; line-height: 1;
}
.redaction-row-remove:hover { background: #FEE2E2; border-color: #dc2626; }
.redaction-add-row {
  margin-top: 4px; align-self: flex-start; padding: 7px 12px; font-size: 12.5px;
  font-weight: 700; color: #0f6b4c; background: #E1F5EE;
  border: 1.5px dashed #1D9E75; border-radius: 5px; cursor: pointer;
}
.redaction-add-row:hover { background: #cdeee1; }

/* 2026-08-11, per Harding: link/icon button that opens the dedicated
   Glossary table modal, replacing the rows that used to sit inline in
   the Obfuscation explainer modal. */
.glossary-link-btn {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 14px;
  padding: 8px 14px; font-size: 12.5px; font-weight: 700; color: #0f6b4c;
  background: #E1F5EE; border: 1.5px solid #1D9E75; border-radius: 6px;
  cursor: pointer; font-family: inherit;
}
.glossary-link-btn:hover { background: #cdeee1; }
.glossary-link-btn i { font-size: 15px; }

/* Column headers above the Glossary table rows in #glossaryTableOverlay */
.redaction-table-headers {
  display: flex; gap: 8px; margin-top: 12px;
  font-size: 11px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase;
  color: #6b6a63;
}
.redaction-table-headers span:nth-child(1) { flex: 1 1 0; }
.redaction-table-headers span:nth-child(2) { flex: 1 1 0; }
.redaction-table-headers span:nth-child(3) { flex: 0 0 26px; }

/* Trailing "+" row, right-aligned under the X column, replacing the old
   dashed "+ Add pair" full-width button. */
.redaction-row-add {
  justify-content: flex-end;
}
.redaction-row-add-btn {
  flex: 0 0 auto; width: 26px; height: 26px; border-radius: 5px;
  border: 1.5px dashed #1D9E75; background: #E1F5EE; color: #0f6b4c;
  cursor: pointer; font-size: 15px; font-weight: 700; line-height: 1;
}
.redaction-row-add-btn:hover { background: #cdeee1; }

/* 2026-08-11, per Harding: "never deface, only inactivate" -- used on
   things that aren't <button>/<input> and so have no native :disabled
   look, plus the Lo-Fi/Hi-Fi wrapper label, while Ingestao Glossario mode
   is on. Stays visible, just dimmed and unclickable.
   2026-08-13, per Harding: lit up further -- the dimmed content (e.g.
   "Claude's Summary" and its response box) is still useful for a visitor
   trying to understand what the app does, so it needs to stay legible,
   not just present. */
.gs4d-inactive {
  opacity: 0.72;
  pointer-events: none;
  filter: grayscale(15%);
}

/* 2026-08-11, per Harding: lets a visitor wipe the whole Glossary and
   start over, from inside the table modal. */
.redaction-reset-btn {
  height: 30px; padding: 0 14px;
  background: #ffffff; color: #a33;
  border: 1.5px solid rgba(163,51,51,0.4); border-radius: 6px;
  font-size: 11.5px; font-weight: 700; cursor: pointer;
  font-family: 'Inter', sans-serif;
}
.redaction-reset-btn:hover:not(:disabled) { background: #FEE2E2; border-color: #dc2626; }
.redaction-reset-btn:disabled {
  background: #E1F5EE; color: #0f6b4c; border-color: #1D9E75;
  opacity: 1; cursor: default;
}

/* 2026-08-11, per Harding: a forewarning notice pinned right under the
   title of the Obfuscation modal, explaining the current-version format
   limitation (no LLM call during Ingestion, so only locally-readable
   formats are scanned). Same amber warning palette as .opt-info-caveat,
   just louder (icon + placed at the very top, not the bottom). */
.opt-info-notice {
  display: flex; gap: 10px; align-items: flex-start;
  background: #FAEEDA; border-left: 4px solid #BA7517; border-radius: 6px;
  padding: 14px 16px; margin: 4px 0 16px;
  font-size: 12.5px; line-height: 1.55; color: #4a4a45;
}
.opt-info-notice-icon { font-size: 17px; flex: 0 0 auto; line-height: 1.3; }
.opt-info-notice strong { color: #1a1a18; }
.opt-info-notice ul { margin: 0; }
.opt-info-notice li { margin-bottom: 2px; }
/* 2026-08-11, per Harding: rebuilt as an explicit two-column comparison
   card (real CSS grid, not free-flowing text) after the original
   single-paragraph markup rendered as a broken, near-single-word-per-line
   4-way split in the live app -- a flex child with no min-width:0/flex
   basis sizing against nested block <ul>s inside a narrow 480px modal.
   Giving each column its own bounded grid track removes the ambiguity
   that caused it, regardless of root cause. */
.opt-info-notice-body { flex: 1 1 0; min-width: 0; }
.opt-info-notice-lead { margin: 0 0 12px; }
.opt-info-notice-cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}
@media (max-width: 520px) {
  .opt-info-notice-cols { grid-template-columns: 1fr; }
}
.opt-info-notice-col {
  min-width: 0;
  background: #ffffff;
  border-radius: 5px;
  border: 1px solid rgba(0,0,0,0.10);
  padding: 10px 12px;
}
.opt-info-notice-col--yes { border-left: 3px solid #1D9E75; }
.opt-info-notice-col--no { border-left: 3px solid #6b6a63; }
.opt-info-notice-col-title {
  display: flex; align-items: center; gap: 5px;
  font-size: 10.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .03em; color: #1a1a18; margin-bottom: 7px;
}
.opt-info-notice-col--yes .opt-info-notice-col-title { color: #0f6b4c; }
.opt-info-notice-col--yes .opt-info-notice-col-title i { color: #1D9E75; }
.opt-info-notice-col--no .opt-info-notice-col-title i { color: #6b6a63; }
.opt-info-notice-col ul { list-style: disc; padding-left: 16px; }
.opt-info-notice-col li {
  font-size: 12px; line-height: 1.5; color: #4a4a45; margin-bottom: 4px;
}
.opt-info-notice-hint { color: #6b6a63; font-size: 11.5px; }
.opt-info-notice-col--no .opt-info-notice-hint {
  display: block; margin-top: 6px; font-style: italic;
}
