/* ------------------------------
   Design system preview
   ------------------------------
   The Preview page PreviewPanel.js renders into .token-list — a peer of the
   group pages, reached from its own sidebar entry.

   One HTML file, shown two ways: the file itself in a textarea, and the file
   rendered in a sandboxed frame. Both live inside a drawn window, because
   what is in the frame is somebody else's PAGE at somebody else's scale, and
   without a frame a dark hero bleeds into the app while a white one reads as
   a broken panel.

   Everything here is the APP's furniture and uses --pf-* tokens. Nothing
   styles the preview itself, and nothing can: the file is a separate document
   in an opaque origin, so this stylesheet does not reach it and the project's
   own tokens are injected there instead. That isolation is the point — the
   preview's promise is "this is what YOUR system looks like", and the app's
   palette leaking in would quietly break it. See previewDocument.mjs. */

/* The sidebar entry. It inherits the group rows' anatomy from
   .token-sidebar button (including .active); all it needs of its own is a
   left-packed label — the group rows justify space-between to push their
   count to the right edge, and this row has no count to push. Scoped under
   .token-sidebar to outweigh that base rule honestly rather than with
   !important. The Explore page's twin is .explore-groups .sidebar-preview. */
.token-sidebar .sidebar-preview {
  justify-content: flex-start;
  margin-bottom: var(--pf-space-2);
}

/* A column, so the window can take whatever height the head above it leaves.
   min-height rather than height: the token list is the scroll container and
   has a definite height, so 100% fills the visible area exactly — and `min-`
   is what lets a tall page grow past it and scroll normally. */
.preview {
  display: flex;
  flex-direction: column;
  min-height: 100%;
}

/* In the EDITOR the panel is exactly the height of the pane, which is what
   bounds the window below it: `flex: 1` can only hand a child a share of the
   space if this box has a definite height to share, and `min-height: 100%`
   alone is not one — the panel would grow with its content and a long file
   would take the chrome bar off the top of the screen with it.

   Scoped to .token-list rather than set on .preview, because the published
   page has no definite ancestor to resolve a percentage against (see the note
   in CLAUDE.md) and a bare `height: 100%` there would collapse to nothing.
   That page has no code field and needs no bounding: the frame is a fixed
   height and scrolls the document inside itself. */
.token-list .preview {
  height: 100%;
}

.preview__where {
  margin: 0 0 var(--pf-space-4);
  max-width: 62ch;
  font-size: var(--pf-text-xs);
  color: var(--pf-color-muted);
}

/* ------ the window ------ */

/* Two rows: the toolbar across the top and the document under it. The
   settings share the document's cell rather than holding a track of their
   own, so opening them lays a panel over the page instead of resizing it.

   `auto 1fr`, not two auto rows: with both auto the leftover height is spread
   across BOTH tracks and the toolbar grows into a half-height band. The page
   takes the slack, the toolbar takes its content. The column is
   minmax(0, 1fr) so a wide preview cannot stretch the grid past the frame.

   Always the dark chrome color as its border, not --pf-color-border: that
   token follows the theme, and in light mode it drew a pale halo around an
   otherwise dark toolbar. */
.preview__window {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto 1fr;
  grid-template-areas:
    "chrome"
    "page";
  /* Takes the space below the head and stays there. Neither face needs the
     window to grow: the frame scrolls the page inside itself, and the code
     field scrolls its own text. Letting it grow instead (`flex: 1 0 auto`,
     which is what the old template stage used) means a 200-line file makes a
     4,500px window, the chrome bar scrolls off the top, and the tabs that
     switch back go with it — the frame stops framing anything.

     min-height is the floor for the other direction: a short viewport should
     scroll the token list rather than squeeze the window to nothing. */
  flex: 1;
  min-height: 32rem;
  overflow: hidden;
  border: 1px solid #1A1D22;
  border-radius: var(--pf-radius-sm);
  /* The document inside has a z-index scale of its own. One stacking context
     here keeps the preview's depth entirely inside the frame, and lets the
     settings panel out-stack the page without joining the app's scale. */
  isolation: isolate;
}

/* ------ the window facade ------ */

/* A chrome bar with the three dots every reader already knows, so what is
   below it reads as a PAGE at a glance rather than as one more panel of the
   editor. Its anatomy is .mock-chrome's from home.css — same 10px dots, same
   8px gap, same 16px inset — deliberately: two window facades in one product
   that disagreed about their controls would read as two different windows.

   Always dark, whatever mode the preview is in. Real browser chrome does not
   flip with the page; a white toolbar over a light marketing hero also bleeds
   into the page and stops reading as a window frame. Literals match the app's
   dark surface (#1A1D22) rather than --pf-* light-dark(), which would follow
   the theme instead. */
.preview__chrome {
  grid-area: chrome;
  display: flex;
  align-items: center;
  gap: var(--pf-space-2);
  padding: var(--pf-space-2) var(--pf-space-4);
  color-scheme: dark;
  border-bottom: 1px solid rgb(255 255 255 / 0.1);
  background: #1A1D22;
  color: #F2F4F8;
}

/* The traffic lights. The values are shared with the homepage's editor mock
   and live in tokens.css, where the note on them explains why they are fixed
   literals rather than themed. */
.preview__chrome i {
  width: 10px;
  height: 10px;
  flex: none;
  border-radius: var(--pf-radius-full);
}

.preview__chrome i:nth-child(1) { background: var(--pf-window-red); }
.preview__chrome i:nth-child(2) { background: var(--pf-window-yellow); }
.preview__chrome i:nth-child(3) { background: var(--pf-window-green); }

/* The first control pushes, whichever it turns out to be: an owner's bar opens
   with the cog, a viewer's with Expand, and either way the run of nothing sits
   between the tabs and the controls so the controls stay flush right. `i` is
   not a button, so nth-of-type counts only the controls. */
.preview__chrome > button:nth-of-type(1) {
  margin-left: auto;
}

/* ------ the tabs ------ */

/* HTML, CSS and Preview are three views of the ONE document in this window,
   so they belong to the window rather than to the page around it — which is
   what a browser's own tab strip already says.

   Drawn as tabs rather than as buttons that happen to sit in a bar: the
   selected one has no bottom edge and sits on the bar's bottom border with a
   negative margin, so it overlaps rather than stacks. That overlap is the
   whole grammar of a tab, and the reason a reader knows without being told
   that the pane below belongs to the strip above.

   Left, immediately after the traffic lights — the tabs and the facade are
   both the window's own furniture, so proximity says so; the controls are
   what gets the empty run. */
.preview__tabs {
  display: flex;
  align-items: flex-end;
  align-self: stretch;
  gap: 2px;
  margin-bottom: calc(-1 * var(--pf-space-2));
}

.preview__tab {
  display: flex;
  align-items: center;
  gap: var(--pf-space-1);
  padding: var(--pf-space-1) var(--pf-space-4);
  border: 1px solid transparent;
  border-bottom: none;
  border-radius: var(--pf-radius-sm) var(--pf-radius-sm) 0 0;
  background: none;
  color: #9AA1AC;
  font-family: var(--pf-font-body);
  font-size: var(--pf-text-xs);
  line-height: 1.9;
  cursor: pointer;
  transition: color var(--pf-duration-fast) var(--pf-ease-out),
              background-color var(--pf-duration-fast) var(--pf-ease-out);
}

.preview__tab:hover {
  color: #F2F4F8;
  background: rgb(255 255 255 / 0.05);
}

/* The selected tab, in ONE ground for all three. Tying it to the pane below
   meant the Preview tab went white while the code tabs stayed dark, and three
   controls that do the same job must look like each other — a tab that changes
   colour depending on which one it is claims a difference that is not there.
   What marks the selection is the shape and the contrast, which every tab
   gets equally. */
.preview__tab[aria-selected="true"] {
  background: var(--pf-color-surface);
  border-color: rgb(255 255 255 / 0.12);
  color: var(--pf-color-ink);
  z-index: 1;
}

/* ------ the bar's controls ------ */

/* The two controls a browser toolbar would actually have on that side, styled
   as one pair because that is what they are — settings for the page, and an
   Expand that reads as the window's own maximise.

   Icon only: a word here would compete with the tabs beside them on a bar
   whose whole job is to frame something else. Both names are on them for a
   screen reader and as a title for a pointer. `aria-expanded` doubles as the
   cog's pressed state. */
.preview__cog,
.preview__expand {
  display: inline-flex;
  align-items: center;
  flex: none;
  padding: var(--pf-space-1);
  border: none;
  border-radius: var(--pf-radius-sm);
  background: none;
  /* Pinned to the chrome's own ramp: inside the always-dark bar the app
     tokens would resolve light whenever the app is in light mode. */
  color: #9AA1AC;
  cursor: pointer;
  transition: color var(--pf-duration-fast) var(--pf-ease-out),
              background-color var(--pf-duration-fast) var(--pf-ease-out);
}

.preview__cog:hover,
.preview__cog:focus-visible,
.preview__cog[aria-expanded="true"],
.preview__expand:hover,
.preview__expand:focus-visible {
  color: #F2F4F8;
  background: #0D0F12;
}

/* ------ the settings ------ */

/* One setting, laid over the page rather than pushing it down: opening a
   panel that resized the document would change the thing being previewed at
   the moment somebody looked at it. */
.preview__settings {
  grid-area: page;
  z-index: 2;
  align-self: start;
  justify-self: end;
  margin: var(--pf-space-3);
  padding: var(--pf-space-4);
  width: min(22rem, calc(100% - var(--pf-space-6)));
  border: 1px solid var(--pf-color-border);
  border-radius: var(--pf-radius-sm);
  background: var(--pf-color-surface);
  box-shadow: var(--pf-shadow-lg);
}

.preview__field {
  display: flex;
  flex-direction: column;
  gap: var(--pf-space-2);
  font-size: var(--pf-text-sm);
  color: var(--pf-color-muted);
}

.preview__segment {
  display: flex;
  gap: 2px;
  padding: 2px;
  border: 1px solid var(--pf-color-border-strong);
  border-radius: var(--pf-radius-sm);
}

.preview__segment button {
  flex: 1;
  padding: var(--pf-space-1) 0;
  border: 0;
  border-radius: calc(var(--pf-radius-sm) - 2px);
  background: none;
  font-family: var(--pf-font-body);
  font-size: var(--pf-text-sm);
  color: var(--pf-color-muted);
  cursor: pointer;
}

.preview__segment button[aria-pressed="true"] {
  background: var(--pf-color-ink);
  color: var(--pf-color-bg);
}

.preview__note {
  margin: var(--pf-space-3) 0 0;
  font-size: var(--pf-text-xs);
  line-height: 1.5;
  color: var(--pf-color-faint);
}

.preview__note code {
  font-family: var(--pf-font-mono);
  font-size: 0.95em;
}

/* Below the note and separated from it, because it is not another setting:
   the field above changes how the preview looks, and this throws it away.
   The rule sits on the note rather than on the button, so the button keeps
   its own shape — a danger button with a borrowed top border and squared
   corners stops reading as a button at all.

   Full width so it reads as its own act rather than as something tucked
   beside the Mode control. */
.preview__settings .preview__note {
  padding-bottom: var(--pf-space-4);
  border-bottom: 1px solid var(--pf-color-border);
}

.preview__delete {
  width: 100%;
  justify-content: center;
  margin-top: var(--pf-space-4);
}

/* ------ the document ------ */

/* Both faces share this cell, and only one is in flow at a time — the editor
   is removed from the layout in preview mode rather than hidden under the
   frame, so it cannot take a click meant for the page. */
.preview__body {
  grid-area: page;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  min-height: 0;
}

/* One pane at a time, keyed off the panel's own data-view. Removed from the
   layout rather than hidden behind the frame: a textarea still in flow would
   take a click meant for the page.

   Written so ABSENCE means the preview. The published page builds this window
   itself and sets no data-view — it has no tabs to set one with — and a rule
   phrased as :not([data-view="preview"]) hid the stage there, leaving a 0x0
   frame and a window with nothing in it. Only a source tab hides the page. */
.preview__editor {
  display: none;
}

.preview[data-view="html"] .preview__editor[data-pane="html"],
.preview[data-view="css"] .preview__editor[data-pane="css"] {
  display: grid;
}

.preview[data-view="html"] .preview__stage,
.preview[data-view="css"] .preview__stage {
  display: none;
}

.preview__stage {
  min-height: 0;
  /* White, not a themed surface. The preview is somebody's page and its own
     CSS decides its background; what shows through before that is the
     browser default, and a dark app surface behind a light page would read
     as a bug in their design rather than in ours. */
  background: #fff;
}

/* The frame fills the stage and scrolls INSIDE it. There is no height
   negotiation and there cannot be: measuring the content would need script in
   the frame, and the frame is precisely where script never runs. So the
   preview is given a page-shaped box and behaves like a page in it — and the
   document itself carries `html, body { min-height: 100% }` from
   previewDocument.mjs, which is what stops a short page reading as a cut-off
   one.

   `display: block` because an iframe is inline by default and the descender
   gap under it would show as a stripe along the bottom edge. */
.preview-frame {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  background: #fff;
  color-scheme: light;
}

/* ------ the code ------ */

/* A field for a file, not a code editor — we are not building one. The
   highlighted copy sits UNDER a transparent textarea, both in the same grid
   cell so they are laid out by the same rules and cannot disagree about
   width. Everything that decides where a glyph lands — font, size,
   line-height, padding, tab-size, white-space — is asserted on both below,
   and any change to one is a change to the other. */
/* No `display` here: which pane is showing is decided by the toggle above,
   and a `display: grid` restated at this point would beat it and show both. */
.preview__editor {
  min-height: 0;
  background: var(--pf-color-surface);
}

.preview__paint,
.preview__code {
  grid-area: 1 / 1;
  margin: 0;
  padding: var(--pf-space-4);
  border: 0;
  font-family: var(--pf-font-mono);
  font-size: var(--pf-text-sm);
  line-height: 1.6;
  tab-size: 2;
  /* A page of markup has long lines, and wrapping them hides the structure —
     which is most of what somebody reading a file like this is after. */
  white-space: pre;
  overflow: auto;
}

/* The paint layer is scrolled by script to follow the field, so it must not
   take a wheel or a pointer of its own. */
.preview__paint {
  pointer-events: none;
  overflow: hidden;
  color: var(--pf-color-ink);
}

/* The same six kinds every grammar in ui/code speaks, painted from the same
   properties. Restated here rather than shared with the shadow sheet because
   this copy lives in the light DOM — see the note on the import in
   PreviewPanel.js, and .rules__paint in rules.css, which does the same. */
.preview__paint .syn--comment     { color: var(--pf-syntax-comment); }
.preview__paint .syn--string      { color: var(--pf-syntax-string); }
.preview__paint .syn--number      { color: var(--pf-syntax-number); }
.preview__paint .syn--keyword     { color: var(--pf-syntax-keyword); }
.preview__paint .syn--property    { color: var(--pf-syntax-property); }
.preview__paint .syn--punctuation { color: var(--pf-syntax-punctuation); }

/* The text itself is transparent and the CARET is not, which is the whole
   trick: what a reader sees is the highlighted copy underneath, in register
   with a field they are typing into. */
.preview__code {
  background: none;
  color: transparent;
  caret-color: var(--pf-color-ink);
  resize: none;
}

.preview__code::placeholder {
  color: var(--pf-color-faint);
}

.preview__code:focus-visible {
  outline: 2px solid var(--pf-color-primary);
  outline-offset: -2px;
}

.preview__count {
  margin: var(--pf-space-2) 0 0;
  font-size: var(--pf-text-xs);
  color: var(--pf-color-muted);
  font-variant-numeric: tabular-nums;
}

.preview__count--over {
  color: var(--pf-color-danger);
}

/* ------ expanded ------ */

/* The whole panel lifts out of the token list and fills the window, so
   presenting keeps every control in the place it already was. Real dialogs
   sit in the top layer and still win. */
.preview.preview--expanded {
  position: fixed;
  inset: 0;
  z-index: 70;
  overflow-y: auto;
  padding: 0;
  background: var(--pf-color-bg);
}

/* The head and the blurb are editor furniture; expanded, the preview is the
   only thing on screen. */
.preview.preview--expanded .token-list-head,
.preview.preview--expanded .preview__where,
.preview.preview--expanded .preview__count {
  display: none;
}

.preview.preview--expanded .preview__window {
  min-height: 100dvh;
  border: 0;
  border-radius: 0;
}

/* ------ the published page ------ */

/* No head, no tabs, nothing to edit: the window is the whole of it, and it
   fills the screen the way the editor's does — see the flex chain from `body`
   down to .explore-tokens in explore-project.css.

   A LOWER floor than the editor's 32rem, not a higher one, which is the
   opposite of the obvious move. The stretch only has room to work while the
   page is shorter than the viewport, so a tall floor is self-defeating: at
   40rem the window made the page overflow, the flex line had nothing spare to
   hand out, and the window stayed at exactly 40rem on a screen half as tall
   again. 24rem is the floor for the case where the page really is long, and
   the growth does the rest. */
.explore-tokens .preview__window {
  min-height: 24rem;
}
