/* @flatppl/web — gallery shell layout + theming. */

:root {
  --bg:        #1e1e1e;
  --bg-pane:   #252526;
  --bg-header: #2d2d30;
  --fg:        #cccccc;
  --fg-muted:  #858585;
  --border:    #3c3c3c;
  --accent:    #007acc;
  --mono: ui-monospace, 'Cascadia Code', 'JetBrains Mono', 'Source Code Pro',
          Menlo, Consolas, monospace;
}

html, body {
  margin: 0; padding: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  width: 100%; height: 100%;
  overflow: hidden;
}

/* Three-pane shell: header bar across the top, then file tree /
   source view / viewer side-by-side, separated by drag handles, then
   the footer (an `auto` row: it collapses to nothing while the footer
   is hidden). The grid-template-columns starting value is overridden
   by layout.js once it loads (which restores the user's persisted
   widths from localStorage) — the value here is the bootstrap
   default that applies during the brief window before layout.js
   runs, plus a no-JS fallback. */
#app {
  display: grid;
  grid-template-rows: 36px 1fr auto;
  grid-template-columns: 240px 5px 1fr 5px 1.5fr;
  width: 100vw;
  height: 100vh;
}

/* Pin every pane and handle to an explicit grid column so that
   `display: none` on the file pane (collapse) doesn't auto-shift
   the remaining items into the now-empty cells. */
#files-pane                                 { grid-column: 1; grid-row: 2; }
.resize-handle[data-resize="files-source"]  { grid-column: 2; grid-row: 2; }
#source-pane                                { grid-column: 3; grid-row: 2; }
.resize-handle[data-resize="source-viewer"] { grid-column: 4; grid-row: 2; }
#viewer-pane                                { grid-column: 5; grid-row: 2; }

#app-header {
  grid-column: 1 / -1;
  background: var(--bg-header);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 0.6em;
  padding: 0 1em;
  font-weight: 600;
  letter-spacing: 0.02em;
}

#toggle-files,
#nav-back,
#nav-forward {
  background: transparent;
  color: var(--fg-muted);
  border: none;
  font-size: 1.1em;
  line-height: 1;
  padding: 0.2em 0.4em;
  cursor: pointer;
  border-radius: 3px;
}
#toggle-files:hover,
#nav-back:hover,
#nav-forward:hover { color: var(--fg); background: rgba(255,255,255,0.05); }
#toggle-files:focus,
#nav-back:focus,
#nav-forward:focus { outline: 1px solid var(--accent); outline-offset: 1px; }
#nav-back, #nav-forward { font-size: 1.3em; padding: 0 0.35em; }

/* Footer: one thin row of links (Legal Notice, …) across the full
   width, rendered by footer.js from __FLATPPL_CONFIG__.footerLinks.
   Kept `hidden` (display: none, so the grid row collapses) when the
   deployment configures no links. */
#app-footer {
  grid-column: 1 / -1;
  grid-row: 3;
  background: var(--bg-header);
  border-top: 1px solid var(--border);
  padding: 0.3em 1em;
  font-size: 12px;
  line-height: 1;
  color: var(--fg-muted);
  display: flex;
  gap: 1.2em;
  white-space: nowrap;
  overflow: hidden;
}
#app-footer[hidden] { display: none; }
#app-footer a { color: inherit; text-decoration: none; }
#app-footer a:hover { color: var(--fg); text-decoration: underline; }

/* Drag handles between adjacent panes. Thin column inside the grid;
   colour-shift on hover so the user can see they're interactive. */
.resize-handle {
  background: var(--bg-pane);
  cursor: col-resize;
  user-select: none;
  /* Keep the visible band narrower than the actual hit-target so
     the user can grab without pixel-perfect aiming. */
  position: relative;
}
.resize-handle::before {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--border);
  transition: background 0.15s ease;
}
.resize-handle:hover::before {
  background: var(--accent);
}

.pane {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  background: var(--bg-pane);
  border-right: 1px solid var(--border);
}
.pane:last-child { border-right: none; }

/* All three pane headers (Files / Source / Visualization) lock to
   the same pixel height regardless of which icon buttons they
   carry. Emoji glyphs in particular render larger than text at
   the same em size, so without an explicit height + button
   font-size cap, a header with more emoji buttons would push
   taller than a text-only one. flex: 0 0 32px stops a flex
   parent (.pane is `flex-direction: column`) from collapsing the
   header to fit smaller content either. */
.pane-header {
  background: var(--bg-header);
  border-bottom: 1px solid var(--border);
  flex: 0 0 32px;
  height: 32px;
  min-height: 32px;
  max-height: 32px;
  box-sizing: border-box;
  padding: 0 1em;
  display: flex;
  align-items: center;
  font-size: 13px;
  line-height: 1;
  color: var(--fg-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pane-header--row {
  gap: 0.6em;
}
.pane-header-text {
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
}


/* Buttons inside a pane header. Pixel-pinned font + line-height
   so the icon glyph (emoji, text, img) stays exactly 14 px tall;
   the .pane-header height (32 px) then has 9 px of breathing
   room above and below regardless of which icons live in the
   header. */
.pane-header button {
  flex: 0 0 auto;
  background: transparent;
  border: none;
  padding: 2px 4px;
  border-radius: 3px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--fg);
  opacity: 1;
  font-size: 14px;
  line-height: 1;
}
.pane-header button:hover { background: rgba(255,255,255,0.07); }
.pane-header button:focus { outline: 1px solid var(--accent); outline-offset: 1px; }
.pane-header button img { display: block; width: 14px; height: 14px; }

/* Desaturate the colour-emoji icons in the pane-header buttons
   (📤 📥 💾 🗑 — anything in <span aria-hidden="true">) so they
   render as grayscale instead of the system emoji font's vivid
   colours. Tonal shading + glyph shape are preserved, but the
   icons no longer clash with the surrounding monochrome chrome.
   The SVG <img> children are unaffected (already monochrome). */
.pane-header button > span[aria-hidden="true"] {
  filter: grayscale(100%);
}
.pane-header button[aria-pressed="true"] {
  background: rgba(13, 113, 199, 0.55);
  color: #fff;
}
.pane-header button[hidden] { display: none; }

.pane-body {
  flex: 1 1 auto;
  overflow: auto;
  position: relative;
}

.pane-placeholder {
  color: var(--fg-muted);
  font-style: italic;
  font-size: 0.9em;
  padding: 0.8em;
}

#file-tree { padding: 0.5em 0; }

.file-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.file-list-item {
  padding: 0.4em 1em;
  cursor: pointer;
  font-size: 0.9em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  user-select: none;
}
.file-list-item:hover    { background: #2a2d2e; }
.file-list-item.selected {
  background: #094771;
  color: #ffffff;
}

/* "Unsaved" section header above ephemeral entries. */
.file-list-header {
  padding: 0.8em 1em 0.2em;
  font-size: 0.85em;
  opacity: 0.6;
  user-select: none;
}

/* Ephemeral entries get italicised + a soft accent so they stand out
   from on-disk manifest entries. The "Unsaved" section label and the
   per-entry save/revert icons (see .file-list-action below) signal
   the unsaved-to-disk state; italic alone keeps the row's typography
   distinct without a redundant leading `* ` prefix. */
.file-list-item--ephemeral {
  font-style: italic;
}

/* Inline save (💾) / revert (↺) icons that appear on file-tree
   entries with unsaved work — every ephemeral row, plus the
   currently-loaded read-only-or-user file when its editor buffer
   differs from the last-persisted source. Floated right so they
   sit at the row's trailing edge regardless of filename length;
   the filename truncates first via the existing
   .file-list-item text-overflow rule. The icons are clickable
   (with click-stop in JS so the row navigation doesn't fire); the
   row's own click target still works on the rest of the row. */
.file-list-item--dirty {
  padding-right: 0.4em;          /* tighten so the icons don't fall off */
}
.file-list-action {
  float: right;
  margin-left: 0.4em;
  padding: 0 2px;
  font-size: 12px;
  line-height: 1.4;
  cursor: pointer;
  filter: grayscale(100%);
  opacity: 0.7;
  border-radius: 2px;
}
.file-list-action:hover {
  opacity: 1;
  background: rgba(255, 255, 255, 0.08);
}

/* Folder grouping in the file pane (Examples / Test cases / User).
   Each folder has a clickable header with a chevron + name + count
   badge; click toggles open/closed (persisted via the gallery's
   FOLDER_STATE_KEY localStorage key). When open, the entries
   render inside a normal .file-list with a small left indent so
   the hierarchy reads at a glance. */
.file-folder {
  user-select: none;
}
.file-folder-header {
  display: flex;
  align-items: center;
  gap: 0.4em;
  padding: 0.45em 1em 0.3em 0.6em;
  cursor: pointer;
  font-size: 0.85em;
  color: var(--fg-muted);
}
.file-folder-header:hover { color: var(--fg); }
.file-folder-chev {
  display: inline-block;
  width: 0.9em;
  font-size: 0.85em;
  text-align: center;
  line-height: 1;
}
.file-folder-name {
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
}
.file-folder-count {
  opacity: 0.6;
  font-size: 0.9em;
}
.file-folder .file-list-item {
  padding-left: 1.6em;     /* indent under the folder header */
}
.file-folder-empty {
  padding: 0.35em 1em 0.6em 1.6em;
  font-size: 0.85em;
  font-style: italic;
  opacity: 0.5;
}

/* Right-click context menu on a sidebar entry. Single floating
   div reused across right-clicks; positioned absolutely at the
   mouse, click-outside closes (handled by app.ts). Compact list
   of one action per row; rows highlight on hover. */
#tree-context-menu {
  position: fixed;
  z-index: 1000;
  min-width: 160px;
  background: var(--bg-pane);
  border: 1px solid var(--border);
  border-radius: 3px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
  padding: 0.25em 0;
  font-size: 0.9em;
  user-select: none;
}
.tree-ctx-item {
  padding: 0.35em 1em;
  cursor: pointer;
  color: var(--fg);
}
.tree-ctx-item:hover { background: #094771; color: #fff; }

/* Drop-target highlight on the file tree while a drag is in
   progress. Inset dashed border so the cue is unambiguous but
   doesn't shift layout. */
.file-tree--drop-target {
  box-shadow: inset 0 0 0 2px var(--accent);
  background: rgba(0, 122, 204, 0.07);
}

/* Transient bottom-of-pane notification for upload summaries
   ("Uploaded 3 files; skipped 1 non-.flatppl"). Single reused
   element; auto-fades after ~2.5 s via app.ts showToast. */
#gallery-toast {
  position: fixed;
  bottom: 1em;
  left: 50%;
  transform: translateX(-50%);
  background: var(--bg-pane);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 0.5em 1em;
  font-size: 0.9em;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
  z-index: 1100;
  opacity: 0;
  transition: opacity 0.3s ease;
  pointer-events: none;
}

/* CodeMirror line-flash decoration — re-installed by editor.ts on
   every revealLine call, cleared by its own timer after 1.5 s.
   The Decoration applies this class to the target line wrapper
   (`<div class="cm-line cm-line-flash">`); animation fades the
   highlight out. Same timing + colour as the old `.src-line-flash`
   rule the pre-CodeMirror viewer used, so the visual feedback is
   unchanged. */
.cm-line-flash {
  animation: cm-line-flash 1.5s ease-out;
  border-radius: 2px;
}
@keyframes cm-line-flash {
  0%   { background: rgba(255, 235, 100, 0.45); }
  100% { background: transparent; }
}

/* Lint (error/warning) gutter. CodeMirror's base theme gives the
   diagnostic pip a wide column with generous side padding, leaving the
   "error ball" floating well off the source. Tighten it so the pip sits
   snug between the line-number and fold gutters. Scoped under
   #source-editor so the id selector outranks CodeMirror's
   runtime-injected base theme (equal class specificity, later in head). */
#source-editor .cm-gutter-lint { width: 0.9em; }
#source-editor .cm-gutter-lint .cm-gutterElement { padding: 0; }
#source-editor .cm-lint-marker { width: 0.8em; height: 0.8em; }

/* Token palette. Roughly tracks VS Code's Dark+ for familiarity, with
   one FlatPPL-specific addition — `tok-ident-binding` (identifiers
   that resolve to a binding in the current module) is rendered with
   its own colour and a hover affordance, so users can tell which
   names in the source map to nodes in the DAG view. CodeMirror's
   highlight ViewPlugin applies these same class names to its
   Decoration ranges, so the palette is shared between syntax
   styling in view and edit modes. */
.tok-comment        { color: #6a9955; font-style: italic; }
.tok-string         { color: #ce9178; }
.tok-number         { color: #b5cea8; }
.tok-op             { color: #d4d4d4; }
.tok-placeholder    { color: #9cdcfe; }
.tok-hole           { color: #9cdcfe; }
.tok-ident          { color: #9cdcfe; }
.tok-ident-binding  { color: #4ec9b0; }
.tok-ident-binding:hover {
  text-decoration: underline;
  cursor: pointer;
}
.tok-keyword   { color: #c586c0; }   /* and, or, not, in, true/false (variant kwds) */
.tok-special   { color: #c586c0; }   /* draw, lawof, fn, …  */
.tok-mop       { color: #c586c0; }   /* weighted, joint, … */
.tok-dist      { color: #4fc1ff; }   /* Normal, Poisson, … */
.tok-set       { color: #569cd6; }   /* reals, interval,    */
.tok-const     { color: #569cd6; }   /* true, pi, inf, im   */
.tok-func      { color: #dcdcaa; }   /* exp, log, sum, …    */
.tok-reserved  { color: #c586c0; }   /* self, base, flatppl_compat */

/* The viewer manages its own internal scroll and resize, so the
   right pane's body must not introduce a second scrollbar. */
#viewer-pane > .pane-body { overflow: hidden; }

/* Source pane hosts CodeMirror in both view and edit modes. The
   editor handles its own scrolling, so the pane-body shouldn't
   add a second scrollbar. */
#source-pane > .pane-body { overflow: hidden; }
#source-editor { width: 100%; height: 100%; }
.cm-editor { height: 100%; }

/* Hover-tooltip emitted by editor.ts's hoverTooltip extension when
   the cursor hovers an identifier resolving to a defined binding.
   Same visual treatment as the DAG-view tooltip (see the viewer's
   own #tooltip rule) so doc rendering stays consistent across
   surfaces. The outer `.cm-tooltip` class is CodeMirror's own
   tooltip wrapper; our custom content lives inside `.cm-source-
   hover` and re-uses the same expr / doc CSS classes. */
.cm-tooltip.cm-tooltip-hover .cm-source-hover {
  max-width: 480px;
  padding: 4px 8px;
  font-family: var(--mono);
  font-size: 13px;
  white-space: normal;
}
.cm-source-hover .cm-source-hover-expr {
  font-family: var(--mono);
  white-space: pre;
}
.cm-source-hover .cm-source-hover-doc {
  margin-top: 4px;
  padding-top: 4px;
  border-top: 1px solid #454545;
  opacity: 0.95;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.cm-source-hover .cm-source-hover-doc p,
.cm-source-hover .cm-source-hover-doc ul,
.cm-source-hover .cm-source-hover-doc ol,
.cm-source-hover .cm-source-hover-doc pre {
  margin: 0.25em 0;
}
.cm-source-hover .cm-source-hover-doc h1,
.cm-source-hover .cm-source-hover-doc h2,
.cm-source-hover .cm-source-hover-doc h3,
.cm-source-hover .cm-source-hover-doc h4 {
  margin: 0.4em 0 0.2em;
  font-size: 1.05em;
  font-weight: 600;
}
.cm-source-hover .cm-source-hover-doc code {
  font-family: var(--mono);
  font-size: 0.95em;
  background: rgba(255, 255, 255, 0.07);
  padding: 0 3px;
  border-radius: 2px;
}
.cm-source-hover .cm-source-hover-doc math[display="block"] {
  display: block;
  margin: 0.4em 0;
  text-align: center;
}

#surface-root {
  width: 100%;
  height: 100%;
  /* The viewer was originally written for the VS Code webview, where
     it occupies the full panel — its outermost #main child uses
     viewport units (100vw, calc(100vh - 86px)) to size itself.
     Embedded inside the gallery's right pane, those viewport values
     overflow the column, pushing the DAG and plot content off to the
     right. The rules below override the viewer's outer layout
     (#header / #main / #info) into a flex column that fills
     #surface-root instead of the viewport. Selector
     specificity (0,2,0,0 vs the viewer's 0,1,0,0) makes the override
     win without !important. */
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
#surface-root > #header,
#surface-root > #info {
  flex: 0 0 auto;
}
#surface-root > #main {
  flex: 1 1 auto;
  width: 100%;     /* override viewer's 100vw */
  height: auto;    /* override viewer's calc(100vh - 86px) */
  min-height: 0;
  min-width: 0;
}

/* DAG-view hover tooltip — gallery-specific override.
   The viewer injects its own `<div id="tooltip">` (inside
   `#surface-root`) and ships matching CSS with `position:
   absolute`. In the VS Code webview that's fine because the viewer
   fills the viewport, so the tooltip's viewport-coord math (from
   `cy.getBoundingClientRect()` + cytoscape's renderedPosition)
   aligns with the absolute-positioning origin.
   In the gallery the viewer is embedded inside `.pane-body`, which
   has `position: relative` — that makes it the containing block for
   the absolutely-positioned tooltip, and the viewport-coord left/top
   get applied additively to the pane-body's offset, pushing the
   tooltip off-screen to the right. Switching to `position: fixed`
   (always viewport-relative) restores the original math without
   patching the viewer. */
#surface-root #tooltip { position: fixed; }

/* Non-FlatPPL surfaces mounted into #surface-root by the PaneController
   (surfaces.ts). #surface-root is a flex column; these fill it. */
.surface-markdown {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 1rem 1.25rem;
  line-height: 1.5;
}
.surface-markdown > :first-child { margin-top: 0; }
.surface-markdown pre,
.surface-markdown code {
  font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  font-size: 0.92em;
}
.surface-markdown pre {
  overflow: auto;
  padding: 0.6em 0.8em;
  background: rgba(127, 127, 127, 0.12);
  border-radius: 4px;
}
.surface-markdown table { border-collapse: collapse; }
.surface-markdown th,
.surface-markdown td { border: 1px solid rgba(127, 127, 127, 0.35); padding: 0.3em 0.6em; }
.surface-placeholder {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  text-align: center;
  opacity: 0.7;          /* normal font, just a quiet muted tone */
}

/* Inline rename of the source-pane header filename (user/ files only).
   #source-header gets `.renamable` from updateHeaderButtons; clicking it (or
   F2) swaps the text for `.rename-input` with the `user/` prefix kept static. */
#source-header.renamable { cursor: text; }
#source-header.renamable:hover { text-decoration: underline dotted; text-underline-offset: 2px; }
.rename-prefix { opacity: 0.6; }
.rename-input {
  font: inherit;
  color: inherit;
  padding: 0 3px;
  min-width: 8em;
  border: 1px solid rgba(127, 127, 127, 0.6);
  border-radius: 3px;
  background: rgba(127, 127, 127, 0.12);
}
