/* The playground page.
 *
 * Everything site.css already gives us is used as-is: .wrap, .site-header,
 * .btn / .btn-ghost, .term and its .p/.m/.a/.c spans, .cmd + .copy, .label,
 * .code, .meta with .v, .flags, .caption, .site-footer. What is here is the
 * layout those components sit in and the few pieces the site has no component
 * for -- the file rail, the guided steps, the status pill and the editor.
 *
 * Three colours of its own, --pg-done, --pg-guide and --pg-install-bg, below for
 * both themes. Every other value is a site.css token, so the dark theme is
 * the site's own token swap and needs no rules of its own.
 *
 * Colour discipline. There are two surfaces, so the application can be told
 * from what it holds: the frame (the toolbar, the steps, the file list, the
 * tab strips over the panes) takes --code, and the documents (the editor, the
 * tables, the plan) keep --bg; a selected tab takes the document's surface.
 * Blue marks what is chosen or what runs. --accbg is the selection surface:
 * the step on screen and the selected file. --acc is links, the selected tab
 * and step, SQL keywords and the Run next button. Amber (--amber) is the only
 * attention colour, for waiting, drift, recovery and warnings. The site's
 * other buttons stay black. Green (--pg-done, below) is the tick on a step
 * the route has checked as done and a line that is new against the seeded
 * file, in the editor and in a patch's preview; a changed line is blue.
 * There is no red: a removed line is a grey triangle, or a grey minus.
 * --pg-guide (below) is the strip that says what the step on screen asks
 * for: a lighter step of the selection tint, so it belongs to the step
 * without competing with it.
 */

/* The colours site.css does not have. The three blocks follow
 * site.css's own: the system preference, unless a choice was stored, and the
 * stored choice.
 *
 * --pg-done sits at the lightness and chroma the site's blue and amber use
 * in each theme, on the green hue, so it reads as one of them.
 *
 * --pg-guide is #f0f7fc in the light theme: the accent's hue, about half way
 * from the page to --accbg in lightness and 40% of its chroma. The dark
 * value is the same rule applied to the dark page and the dark --accbg. */
:root {
  --pg-done: oklch(0.52 0.13 150);
  --pg-guide: #f0f7fc;
  --pg-install-bg: #fff7d6;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --pg-done: oklch(0.78 0.13 150);
    --pg-guide: oklch(0.228 0.02 235);
    --pg-install-bg: #39301e;
  }
}

:root[data-theme="dark"] {
  --pg-done: oklch(0.78 0.13 150);
  --pg-guide: oklch(0.228 0.02 235);
  --pg-install-bg: #39301e;
}

/* The UA's `[hidden] { display: none }` sits at user-agent origin, so any
 * author `display` below would silently win over it and show a hidden node. */
[hidden] {
  display: none !important;
}

/* ---------- Toolbar ---------- */

/* The page's title, the scenario and the workspace actions. As a page it is
 * the title block; in the full-window layout it is the application's top bar
 * (see "Full window" below). */
.pg-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 16px 24px;
  padding-top: 40px;
  padding-bottom: 20px;
}

.pg-toolbar-title {
  font: 500 28px/1.25 var(--sans);
  letter-spacing: -0.015em;
}

.pg-actions {
  margin-left: auto;
}

/* Opens the workspace actions as a menu, up to 900px; see "Phone" below. */
.pg-more-btn {
  display: none;
  place-items: center;
  flex: none;
  width: 36px;
  height: 32px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 2px;
  background: none;
  color: var(--fg2);
  cursor: pointer;
}

.pg-more-btn:hover,
.pg-more-btn[aria-expanded="true"] {
  border-color: var(--line);
  background: var(--bg);
  color: var(--fg);
}

.pg-more-btn:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 1px;
}

/* Amber, because a build the page cannot vouch for is something to read, not
 * an error. The default state is simply quiet. */
/* A sentence a workspace action needs in front of the visitor: an import that
 * was refused, an export that failed. Amber is the page's only attention
 * colour and this is one of the few places entitled to it. */
.pg-notice {
  margin: 0 0 4px;
  font: 13px var(--mono);
  color: var(--fg2);
}

.pg-notice[data-tone="attention"] {
  color: var(--amber);
}

.pg-build-warn {
  color: var(--amber);
}

/* ---------- CLI installation reminder ---------- */

.pg-install {
  display: grid;
  grid-template-rows: 1fr;
  flex: none;
  background: var(--pg-install-bg);
  color: var(--fg2);
}

.pg-install-clip {
  min-height: 0;
  overflow: hidden;
}

.pg-install-content {
  display: flex;
  align-items: center;
  gap: 12px 24px;
  padding: 2px 16px;
  border-bottom: 1px solid var(--line);
}

.pg-install-message {
  display: contents;
}

.pg-install-content p {
  margin: 0;
  font: 12px/1.5 var(--sans);
}

.pg-install-content strong {
  color: var(--fg);
  font-weight: 600;
}

.pg-install-action {
  flex: none;
  margin-left: auto;
  font: 600 12px/1.5 var(--sans);
  color: var(--acc);
  white-space: nowrap;
}

.pg-install-close {
  display: grid;
  place-items: center;
  flex: none;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  border-radius: 2px;
  color: var(--fg2);
  background: transparent;
  cursor: pointer;
}

.pg-install-close:hover {
  color: var(--fg);
  background: var(--bg);
}

.pg-install-link {
  flex: none;
  font: 600 12px/1.5 var(--sans);
  white-space: nowrap;
}

.pg-install-close:focus-visible,
.pg-install-action:focus-visible,
.pg-install-link:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: no-preference) {
  .pg-install:not([hidden]) {
    animation: pg-install-reveal 240ms ease-out;
  }
  .pg-install.is-closing {
    animation: pg-install-dismiss 200ms ease-in forwards;
  }
  @keyframes pg-install-reveal {
    from { grid-template-rows: 0fr; opacity: 0; transform: translateY(-8px); }
    to { grid-template-rows: 1fr; opacity: 1; transform: translateY(0); }
  }
  @keyframes pg-install-dismiss {
    from { grid-template-rows: 1fr; opacity: 1; transform: translateY(0); }
    to { grid-template-rows: 0fr; opacity: 0; transform: translateY(-8px); }
  }
}

@media (max-width: 900px) {
  /* The site header is the top menu on phones; precede the scenario row. */
  html.js .pg-install { order: -1; }
  /* Extend through the page wrapper's gutters at both mobile breakpoints. */
  .pg-install { margin-inline: -48px; }
  .pg-install-content {
    display: grid;
    grid-template-columns: 28px minmax(0, 1fr) 28px;
    gap: 0;
    padding: 0 10px;
  }
  .pg-install-message {
    display: flex;
    grid-column: 2;
    align-items: center;
    justify-content: center;
    gap: 8px;
  }
  .pg-install-content p { min-width: 0; }
  .pg-install-content p > span { display: none; }
  .pg-install-action { margin-left: 0; }
  .pg-install-close {
    grid-column: 3;
    width: 28px;
    height: 28px;
  }
}

@media (max-width: 720px) {
  .pg-install { margin-inline: -20px; }
}

/* ---------- Boot strip ---------- */

.pg-main {
  display: block;
}

.pg-boot {
  display: grid;
  gap: 12px;
  padding: 16px 18px;
  margin-bottom: 14px;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--code);
}

.pg-boot-static {
  font: 13px/1.5 var(--sans);
  color: var(--fg2);
}

.pg-boot-note {
  font: 13px/1.5 var(--sans);
  color: var(--mute);
}

.pg-boot-note em {
  font-style: normal;
  color: var(--fg2);
}

/* ---------- The frame ---------- */

.pg-bar {
  display: flex;
  align-items: center;
  min-width: 0;
}

.pg-scenario,
.pg-engine {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.pg-scenario-label,
.pg-engine-label {
  font: 500 13.5px var(--sans);
  color: var(--mute);
}

/* Opens a toolbar picker. It reads as the current choice with a
 * disclosure chevron, and shows its edge only when pointed at. */
/* 28px tall like the toolbar's other controls -- and the tour skips a target
 * under 24px, so at its text's own height the tour's first card vanished. */
.pg-scenario-btn,
.pg-engine-button {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  max-width: 100%;
  min-height: 28px;
  padding: 2px 6px;
  border: 1px solid transparent;
  border-radius: 2px;
  background: transparent;
  color: var(--fg);
  font: 500 13.5px var(--sans);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}

.pg-scenario-btn::after,
.pg-engine-button::after {
  content: "";
  flex: none;
  width: 5px;
  height: 5px;
  margin-top: -3px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
}

.pg-scenario-btn:hover,
.pg-engine-button:hover {
  border-color: var(--line);
}

.pg-scenario-btn:focus-visible,
.pg-engine-button:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 1px;
}

/* ---------- Scenario picker ----------
 *
 * The About dialog's frame at a list's width: a 1px rule, a 2px radius, no
 * shadow, the tour's scrim behind it. A row is a scenario's title, how many
 * steps it has and a sentence on what it is about; the one loaded is tinted
 * with the selection colour. The list scrolls inside the dialog, so the
 * scenarios still to come need no other change. */
.pg-picker {
  width: min(620px, calc(100vw - 32px));
  max-height: calc(100dvh - 32px);
  padding: 20px 24px 22px;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--bg);
  color: var(--fg);
}

.pg-picker[open] {
  display: grid;
  grid-template-rows: auto auto minmax(0, 1fr);
  gap: 14px;
}

.pg-picker::backdrop {
  background: var(--bg);
  opacity: 0.82;
}

.pg-picker-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.pg-picker-title {
  margin: 0;
  font: 600 17px/1.3 var(--sans);
}

.pg-picker-close {
  padding: 5px 12px;
  font-size: 13px;
}

.pg-picker-note {
  margin: 0;
  font: 13.5px/1.5 var(--sans);
  color: var(--fg2);
}

.pg-picker-list {
  display: grid;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-y: auto;
}

.pg-picker-option {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 4px 16px;
  width: 100%;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--bg);
  text-align: left;
  color: var(--fg);
  cursor: pointer;
}

.pg-picker-option:hover {
  border-color: var(--fg2);
}

.pg-picker-option:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 1px;
}

.pg-picker-option[aria-current="true"] {
  border-color: var(--acc);
  background: var(--accbg);
}

.pg-picker-name {
  font: 600 15px/1.35 var(--sans);
}

.pg-picker-meta {
  align-self: baseline;
  font: 12px var(--mono);
  color: var(--mute);
  white-space: nowrap;
}

.pg-picker-desc {
  grid-column: 1 / -1;
  font: 13.5px/1.5 var(--sans);
  color: var(--fg2);
}

/* ---------- Status bar ---------- */

/* What is running and in what state: the build the tab loaded, SQLite, the
 * runtime's status and where the workspace lives. Below the panes, the way an
 * editor keeps its own state out of the document. */
.pg-statusbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 6px 24px;
  padding: 10px 0;
}

.pg-statusbar .meta {
  margin: 0;
}

.pg-state {
  display: flex;
  align-items: center;
  gap: 20px;
  margin-left: auto;
}

/* The status bar's short form, for the phone's toolbar (see "Phone and
 * tablet portrait"). */
.pg-mini {
  display: none;
}

/* Opens the About dialog. Plain text at the status bar's size, underlined on
 * hover the way a link in running text is. */
.pg-about-open {
  appearance: none;
  padding: 0;
  border: 0;
  background: none;
  font: 500 12px var(--sans);
  color: var(--fg2);
  cursor: pointer;
}

.pg-about-open:hover {
  color: var(--fg);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.pg-about-open:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 2px;
}

/* ---------- Status pill ---------- */

.pg-status {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font: 500 12px var(--mono);
  color: var(--fg2);
  white-space: nowrap;
}

.pg-status[data-tone="quiet"] {
  color: var(--mute);
}

.pg-status[data-tone="attention"] {
  color: var(--amber);
}

.pg-storage {
  font: 12px var(--mono);
  color: var(--mute);
  white-space: nowrap;
}

/* ---------- Guided steps ---------- */

.pg-steps {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  background: var(--code);
  border: 1px solid var(--line);
  border-radius: 2px 2px 0 0;
}

.pg-step {
  display: flex;
  gap: 10px;
  align-items: baseline;
  text-align: left;
  padding: 10px 16px;
  min-height: 44px;
  border-right: 1px solid var(--line);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  color: var(--mute);
  font: 500 13.5px var(--sans);
}

.pg-step:last-child {
  border-right: 0;
}

/* Each step is a button that points the strip at it, and has to look like
 * one: a surface on hover, the step being shown tinted with the accent's own
 * background, and a focus ring. A done step keeps a darker title than one
 * still ahead, so the route reads as travelled up to here. */
.pg-step:hover {
  border-bottom-color: var(--mute);
}

.pg-step:hover .pg-step-title,
.pg-step[data-status="done"] .pg-step-title {
  color: var(--fg);
}

.pg-step-done {
  color: var(--pg-done);
  font-weight: 600;
}

/* Where a step's tick goes; the row keeps the slot and fills it when the
 * step is done. It lays out as if the tick were the step's own child. */
.pg-step-mark {
  display: contents;
}


.pg-step[aria-current="step"] {
  color: var(--fg);
  background: var(--accbg);
  border-bottom-color: var(--acc);
}

.pg-step:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: -2px;
}

.pg-step[aria-current="step"] .pg-step-n {
  color: var(--acc);
}

.pg-step-n {
  font: 500 12px var(--mono);
  color: var(--mute);
  flex-shrink: 0;
}

/* Only meaningful in the single-step narrow layout, where the strip has to say
 * how many steps there are. */
.pg-step-of {
  display: none;
  font: 500 12px var(--mono);
  color: var(--mute);
}

.pg-step-title {
  white-space: nowrap;
}

.pg-step-hint {
  font: 400 12px var(--sans);
  color: var(--mute);
  min-width: 0;
}

/* A scenario with no steps draws no row (see Guide.renderSteps). */
.pg-steps.is-empty {
  display: none;
}

/* ---------- Pane tabs (narrow layout only) ---------- */

.pg-panetabs {
  display: none;
  border-bottom: 1px solid var(--line);
}

.pg-panetab {
  font: 500 13.5px var(--sans);
  padding: 10px 8px;
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--mute);
}

.pg-panetab[aria-selected="true"] {
  color: var(--fg);
  box-shadow: inset 0 -2px 0 var(--acc);
}

.pg-panetab-icon {
  display: block;
  flex: none;
}

/* ---------- The three panes and the terminal ---------- */

/*
 * Three columns: the file list, the editor and the database pane. The rail
 * and the database pane default to fixed and fractional widths, and the
 * terminal to a share of the height; src/splitters.ts replaces each with the
 * size a reader drags it to, in --pg-rail, --pg-db and --pg-term. The rows
 * belong to the layout: "Full window" below arranges the children above
 * 900px, and the responsive queries lay the same children out as a page.
 */
.pg-grid {
  position: relative;
  display: grid;
  grid-template-columns: var(--pg-rail, 232px) minmax(0, 1fr) var(--pg-db, minmax(320px, 40%));
  border: 1px solid var(--line);
  border-top: 0;
}

.pg-rail {
  grid-column: 1;
  grid-row: 1;
  padding: 16px 0;
  background: var(--code);
  display: grid;
  gap: 20px;
  align-content: start;
  min-width: 0;
  overflow: auto;
  border-right: 1px solid var(--line);
}

.pg-rail-group {
  display: grid;
  gap: 4px;
  min-width: 0;
  /* In the two-column narrow rail the groups share a row; without this the
     shorter one spreads its rows to fill the taller one's height. */
  align-content: start;
}

/* 210px is not wide enough for a label and a note side by side, and a wrapped
 * two-column head reads as two broken labels. The note goes underneath. */
.pg-rail-head {
  display: grid;
  gap: 2px;
  padding: 0 16px 4px;
}

.pg-rail-note {
  font: 11.5px var(--mono);
  color: var(--mute);
}

.pg-files,
.pg-tables,
.pg-cols {
  margin: 0;
  padding: 0;
  list-style: none;
}

.pg-file,
.pg-table-head {
  display: flex;
  gap: 8px;
  align-items: center;
  padding: 5px 16px;
  font: 13px var(--mono);
  color: var(--fg2);
  min-width: 0;
}

.pg-table-head {
  color: var(--fg);
}

.pg-file-name,
.pg-table-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pg-file.is-current {
  color: var(--fg);
  background: var(--code);
  box-shadow: inset -2px 0 0 var(--acc);
}

.pg-file-icon {
  color: var(--mute);
  flex-shrink: 0;
}

.pg-file-note {
  margin-left: auto;
  padding-left: 8px;
  font-size: 11.5px;
  color: var(--mute);
  white-space: nowrap;
}

/* Unsaved work is something to act on, so it takes the attention colour. */
.pg-file-note[data-state="modified"] {
  color: var(--amber);
}

.pg-cols {
  padding: 2px 16px 4px 40px;
  display: grid;
  gap: 2px;
  font: 12px var(--mono);
  color: var(--fg2);
}

.pg-col-note {
  color: var(--mute);
}

.pg-col-note[data-state="drift"] {
  color: var(--amber);
}

.pg-pane {
  display: grid;
  min-width: 0;
  min-height: 0;
}

.pg-editor {
  grid-column: 2;
  grid-row: 1;
  grid-template-rows: auto minmax(0, 1fr) auto;
  border-right: 1px solid var(--line);
}

.pg-db {
  grid-column: 3;
  grid-row: 1;
  grid-template-rows: auto minmax(0, 1fr);
}

/* ---------- Pane tabs ---------- */

.pg-tabs {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: var(--code);
  border-bottom: 1px solid var(--line);
  padding-right: 16px;
}

.pg-tablist {
  display: flex;
  gap: 2px;
  min-width: 0;
}

.pg-tab {
  font: 500 13.5px var(--sans);
  padding: 10px 16px;
  min-height: 44px;
  display: flex;
  align-items: center;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  color: var(--mute);
  white-space: nowrap;
}

.pg-tab:hover {
  color: var(--fg);
}

.pg-tab[aria-selected="true"] {
  color: var(--fg);
  background: var(--bg);
  border-bottom-color: var(--acc);
}

.pg-tabs-note {
  font: 12px var(--mono);
  color: var(--mute);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.pg-tabs-note[data-state="attention"] {
  color: var(--amber);
}

/* ---------- Editor ---------- */

.pg-editor-body {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  font: 13px/1.65 var(--mono);
  overflow: auto;
  min-height: 0;
}

.pg-gutter {
  padding: 14px 10px 14px 14px;
  border-right: 1px solid var(--line2);
  color: var(--mute);
  text-align: right;
  white-space: pre;
  user-select: none;
  font-variant-numeric: tabular-nums;
}

.pg-code {
  padding: 14px 16px;
  color: var(--fg);
  white-space: pre;
  overflow: visible;
  font: inherit;
}

/* Blue is SQL. It is the same blue as a link, deliberately: this is the
 * language Ptah reads and the language it writes. */
.pg-code .k {
  color: var(--acc);
}

.pg-code .c {
  color: var(--mute);
}

/* A line the desired state gained. The tint marks a moment, not a kind of
 * text, which is why it is the accent wash rather than a colour on the glyphs. */
.pg-code .n {
  display: inline-block;
  width: 100%;
  background: var(--accbg);
  box-shadow: -16px 0 0 var(--accbg), 16px 0 0 var(--accbg);
}

.pg-pane-foot {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 16px;
  border-top: 1px solid var(--line);
  font: 12px var(--mono);
  color: var(--mute);
}

/* ---------- Database pane ---------- */

.pg-db-body {
  padding: 16px;
  display: grid;
  gap: 14px;
  align-content: start;
  overflow: auto;
  min-height: 0;
}

.pg-empty {
  display: grid;
  gap: 10px;
  font: 13.5px/1.55 var(--sans);
  color: var(--fg2);
  max-width: 46ch;
}

.pg-empty strong {
  font-weight: 500;
  color: var(--fg);
}

.pg-empty em {
  font-style: normal;
  font-family: var(--mono);
  font-size: 0.92em;
}

/* ---------- Terminal ---------- */

.pg-terminal {
  grid-column: 1 / -1;
  grid-row: 3;
  /* .term brings the frame; inside the grid it is a band, not a card. */
  border: 0;
  border-top: 1px solid var(--line);
  border-radius: 0;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  min-height: 0;
}

.pg-terminal .term-bar {
  align-items: center;
  gap: 16px;
}

.pg-term-where,
.pg-term-right {
  display: flex;
  align-items: center;
  gap: 16px;
}

/* The exit code is the process's own number. Its colour comes off the code,
 * never off matching words in the output. */
#pg-term-exit {
  color: var(--termfg);
}

#pg-term-exit[data-tone="attention"] {
  color: var(--termamber);
}

.pg-transcript {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  overflow: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--termline) transparent;
}

.pg-prompt {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 22px;
  border-top: 1px solid var(--termline);
  font: 13.5px var(--mono);
  min-width: 0;
}

.pg-input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--termfg);
  font: inherit;
  caret-color: var(--termamber);
}

.pg-input:focus {
  outline: none;
}

.pg-input:focus-visible {
  outline: 2px solid var(--termacc);
  outline-offset: 3px;
}

.pg-input:disabled {
  color: var(--termmute);
  cursor: not-allowed;
}

.pg-input::placeholder {
  color: var(--termmute);
  opacity: 1;
}

.pg-prompt-help {
  flex-shrink: 0;
  margin-left: auto;
  padding-left: 16px;
  font-size: 12px;
  color: var(--termmute);
  white-space: nowrap;
}

.pg-prompt-help[data-tone="attention"] {
  color: var(--termamber);
}

/* ---------- Explain strip ---------- */

.pg-next {
  grid-column: 1 / -1;
  grid-row: 2;
  display: grid;
  /* fit-content rather than auto on the actions column. A grid `auto` track is
   * sized from max-content, and max-content is measured as if nothing wrapped,
   * so a step carrying a second command claimed the width of both rows laid
   * side by side and squeezed the prose to one word per line. The cap makes
   * that impossible rather than merely unlikely; .pg-next-run below removes
   * the reason it happened. */
  grid-template-columns: minmax(0, 1fr) fit-content(820px);
  gap: 24px;
  align-items: center;
  padding: 8px 16px;
  border-top: 1px solid var(--line);
  background: var(--pg-guide);
}

.pg-next .cmd {
  background: var(--bg);
}

/* The one button on the page that runs something takes the accent. */
.pg-next-run .btn:not(.btn-ghost):not(:disabled) {
  background: var(--acc);
  border-color: var(--acc);
  color: var(--bg);
}

.pg-next-run .btn:not(.btn-ghost):not(:disabled):hover {
  background: var(--fg);
  border-color: var(--fg);
}

/* The headline and the hint beside it. What the step means and what was
 * checked are in the hint's popover rather than under the headline: they are
 * worth reading once, and as a paragraph they held three lines of height
 * that every pane below had to give up. */
.pg-next-copy {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.pg-next-copy strong {
  font: 600 14px/1.4 var(--sans);
  text-wrap: balance;
}

.pg-next-info {
  flex: none;
  display: inline-grid;
  place-items: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: transparent;
  font: 600 11px var(--mono);
  color: var(--fg2);
  cursor: pointer;
}

.pg-next-info:hover,
.pg-next-info[aria-expanded="true"] {
  border-color: var(--fg2);
  color: var(--fg);
}

.pg-next-info:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 2px;
}

/* Off the route, the hint says so in amber on the strip itself: a note that
 * matters before carrying on cannot wait behind an icon. */
.pg-next-info[data-tone="attention"] {
  width: auto;
  padding: 0 8px;
  border-color: currentColor;
  border-radius: 2px;
  font: 500 12px/18px var(--sans);
  color: var(--amber);
  white-space: nowrap;
}

/* The popover. The top layer draws it over the panes without taking a line
 * from them; src/guide.ts places it under the hint when it opens. The help
 * beside Import is the same kind of popover. */
.pg-next-detail,
.pg-help-pop {
  inset: auto;
  margin: 0;
  width: max-content;
  max-width: min(460px, calc(100vw - 32px));
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--bg);
  color: var(--fg2);
  font: 13.5px/1.55 var(--sans);
}

/* Only the display waits for the popover to open. A closing popover is
 * still drawn while it fades, and a gap set only while open went first. */
.pg-next-detail,
.pg-help-pop {
  gap: 8px;
}

.pg-next-detail:popover-open,
.pg-help-pop:popover-open {
  display: grid;
}

.pg-next-detail p,
.pg-help-pop p {
  margin: 0;
  text-wrap: pretty;
}

.pg-help-pop .pg-help-title {
  font-weight: 600;
  color: var(--fg);
}

/* The round help button beside Import: the strip's hint, with a question. */
.pg-help-btn {
  flex: none;
  align-self: center;
  display: inline-grid;
  place-items: center;
  width: 20px;
  height: 20px;
  margin-left: -4px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: transparent;
  font: 600 11px var(--mono);
  color: var(--fg2);
  cursor: pointer;
}

.pg-help-btn:hover,
.pg-help-btn[aria-expanded="true"] {
  border-color: var(--fg2);
  color: var(--fg);
}

.pg-help-btn:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 2px;
}

/* A run the strip started is asking on stdin. Amber, the attention colour,
 * on the headline and around the question, which is quoted in a box shaped
 * like the command box it replaces. */
.pg-next[data-state="asking"] .pg-next-copy strong {
  color: var(--amber);
}

.pg-next-ask {
  display: flex;
  align-items: baseline;
  gap: 10px;
  min-width: 0;
  max-width: 680px;
  padding: 7px 12px;
  border: 1px solid var(--amber);
  border-radius: 2px;
  background: color-mix(in oklab, var(--amber) 8%, var(--bg));
  font: 12.5px/1.5 var(--mono);
  color: var(--fg);
}

.pg-next-ask-sigil {
  color: var(--amber);
  font-weight: 600;
}

/* The strip put its SQL in the pane: where the button that runs it is, in
 * place of the one that put it there. */
.pg-next-offer {
  display: inline-flex;
  align-items: center;
  padding: 7px 12px;
  border: 1px solid var(--acc);
  border-radius: 2px;
  background: var(--bg);
  font: 500 13px var(--sans);
  color: var(--acc);
  white-space: nowrap;
}

/* What the route checked, in the words of the check. */
.pg-next-check {
  font: 12px/1.5 var(--mono);
  color: var(--mute);
  overflow-wrap: anywhere;
}

/* One column of rows, not a wrapping flex line. A single-column grid measures
 * its max-content as the widest row; a wrapping flex container measures its
 * own as every child on one line, which is what collapsed the prose beside it.
 * Each row lays its own box and button out horizontally. */
.pg-next-run {
  display: grid;
  grid-auto-flow: row;
  justify-items: start;
  gap: 10px;
  min-width: 0;
}

.pg-next-row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  max-width: 100%;
}

/* The parts of a step of more than one: numbered buttons that read as one
 * control, like the terminal-width pair. The part on screen wears the
 * selection colours, and a part seen done carries the step's green tick. */
.pg-next-parts {
  display: flex;
  flex: none;
}

.pg-next-part {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-width: 32px;
  height: 32px;
  padding: 0 9px;
  border: 1px solid var(--line);
  background: var(--bg);
  font: 500 12.5px var(--mono);
  color: var(--mute);
  cursor: pointer;
}

.pg-next-part:first-child {
  border-radius: 2px 0 0 2px;
}

.pg-next-part:last-child {
  border-radius: 0 2px 2px 0;
}

.pg-next-part + .pg-next-part {
  margin-left: -1px;
}

.pg-next-part:hover {
  color: var(--fg);
}

.pg-next-part[aria-current="step"] {
  position: relative;
  border-color: var(--acc);
  background: var(--accbg);
  color: var(--acc);
}

.pg-next-part:focus-visible {
  position: relative;
  z-index: 1;
  outline: 2px solid var(--acc);
  outline-offset: 1px;
}

.pg-next-run .cmd {
  max-width: 680px;
  padding: 7px 12px;
}

.pg-next-run .cmd pre {
  font-size: 12.5px;
}

.pg-next-run .btn {
  padding: 7px 14px;
  font-size: 13px;
}

.btn:disabled,
.btn[disabled] {
  background: var(--mute);
  border-color: var(--mute);
  color: var(--bg);
  cursor: not-allowed;
}

.btn-ghost:disabled,
.btn-ghost[disabled] {
  background: transparent;
  border-color: var(--line);
  color: var(--mute);
  cursor: not-allowed;
}

.copy:disabled {
  color: var(--mute);
  cursor: not-allowed;
}

/* ---------- About ----------
 *
 * What the playground is, what is running and what it cannot do, in a dialog
 * the status bar opens. The tour card's vocabulary at a reading width: a 1px
 * rule, a 2px radius, no shadow, and the tour's scrim behind it. */
.pg-about {
  width: min(680px, calc(100vw - 32px));
  max-height: calc(100dvh - 32px);
  padding: 20px 24px 22px;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--bg);
  color: var(--fg);
}

.pg-about[open] {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 20px;
}

.pg-about::backdrop {
  background: var(--bg);
  opacity: 0.82;
}

.pg-about-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.pg-about-title {
  margin: 0;
  font: 600 17px/1.3 var(--sans);
}

.pg-about-close {
  padding: 5px 12px;
  font-size: 13px;
}

.pg-about .lede {
  max-width: none;
  font-size: 16px;
}

.pg-about .flags {
  max-width: none;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}

.pg-about dd {
  min-width: 0;
  overflow-wrap: anywhere;
  text-wrap: pretty;
}

.pg-about-links {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 20px;
  margin: 0;
  padding: 14px 0 0;
  list-style: none;
  border-top: 1px solid var(--line);
  font: 13.5px var(--sans);
}

.pg-about-links a {
  color: var(--fg2);
}

/* Storage details replace About, with a fixed header and a scrolling body. */
.pg-storage-dialog {
  max-height: min(640px, 80dvh);
}

.pg-storage-dialog[open] {
  grid-template-rows: auto minmax(0, 1fr);
  overflow: hidden;
}

.pg-storage-dialog .pg-about-head {
  align-items: flex-start;
}

.pg-storage-dialog .pg-about-close {
  flex-shrink: 0;
}

.pg-storage-body {
  min-height: 0;
  overflow-y: auto;
  font: 13px/1.6 var(--sans);
  color: var(--fg2);
}

.pg-storage-body p {
  margin: 0;
}

.pg-storage-body .pg-storage-intro {
  margin-bottom: 20px;
  font-size: 14px;
  color: var(--fg);
}

.pg-storage-body h3 {
  margin: 0 0 4px;
  font: 600 13px/1.6 var(--sans);
  color: var(--fg);
}

.pg-storage-body code {
  display: block;
  margin-top: 4px;
  padding: 0;
  border: 0;
  background: none;
  font: 11.5px/1.6 var(--mono);
  color: var(--fg2);
  overflow-wrap: anywhere;
}

.pg-storage-body .pg-storage-inline {
  display: inline;
  margin: 0;
}

.pg-storage-list {
  margin: 14px 0 0;
}

.pg-storage-list > div {
  display: grid;
  grid-template-columns: 120px minmax(0, 1fr);
  gap: 4px 16px;
  padding: 10px 0;
  border-top: 1px solid var(--line);
}

.pg-storage-list dt {
  font-weight: 500;
  color: var(--fg);
}

.pg-storage-list dd {
  margin: 0;
}

.pg-storage-session {
  margin-top: 10px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}

.pg-storage-body .pg-storage-note {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  font-size: 12px;
}

@media (max-width: 520px) {
  .pg-storage-dialog {
    padding: 16px;
  }

  .pg-storage-list > div {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ---------- Leaving ptah.run ----------
 *
 * Asked before a link to another site opens (src/links.ts), in About's
 * frame at a narrower width. */
.pg-leave {
  width: min(460px, calc(100vw - 32px));
  padding: 20px 24px 22px;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--bg);
  color: var(--fg);
}

.pg-leave[open] {
  display: grid;
  gap: 12px;
}

.pg-leave::backdrop {
  background: var(--bg);
  opacity: 0.82;
}

.pg-leave-title {
  margin: 0;
  font: 600 17px/1.3 var(--sans);
}

.pg-leave-text {
  margin: 0;
  font: 14.5px/1.5 var(--sans);
  color: var(--fg2);
}

.pg-leave-url {
  margin: 0;
  font: 12.5px var(--mono);
  color: var(--mute);
  overflow-wrap: anywhere;
}

.pg-leave-buttons {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 4px;
}

.pg-leave-buttons .btn {
  padding: 7px 14px;
  font-size: 13.5px;
}

.pg-about-links a:hover {
  color: var(--fg);
}

/* ---------- The phone's header ----------
 *
 * Up to 900px the playground is the phone layout, and its header is the
 * phone's: the brand, the theme toggle and the menu button, with the page's
 * title and its short status moved in beside the brand by src/main.ts, so
 * the toolbar starts at the scenario. site.css switches its header at 720px.
 * Between 721 and 900px the links do not fit a row beside the title and the
 * status, and ran the brand into "Docs", so this page takes the drawer up to
 * 900px, restating site.css's rules for that range. */
@media (min-width: 721px) and (max-width: 900px) {
  html.js .site-header .wrap {
    height: 52px;
  }
  html.js .site-header .nav {
    gap: 0;
    margin-right: -12px;
  }
  html.js .site-header .icon-btn {
    width: 44px;
    height: 44px;
    border: 0;
  }
  html.js .site-header .menu-btn {
    display: grid;
  }
  html.js .site-header .menu-btn svg {
    width: 18px;
    height: 18px;
  }
  html.js .site-header .nav-links {
    display: none;
    position: absolute;
    right: 0;
    left: 0;
    top: 100%;
    z-index: 20;
    padding: 8px 48px 16px;
    border-bottom: 1px solid var(--line);
    background: var(--bg);
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    font-size: 16px;
  }
  html.js .site-header.is-open .nav-links {
    display: flex;
  }
  html.js .site-header .nav-links a {
    display: flex;
    align-items: center;
    min-height: 44px;
  }
}

@media (max-width: 900px) {
  html.js .site-header .wrap {
    justify-content: flex-start;
    gap: 10px;
  }

  html.js .site-header .pg-toolbar-title {
    min-width: 0;
    margin: 0;
    overflow: hidden;
    font: 600 17px/1.2 var(--sans);
    letter-spacing: 0;
    white-space: nowrap;
    text-overflow: ellipsis;
  }

  html.js .site-header .pg-mini {
    flex: none;
    margin-left: auto;
    padding: 3px 7px;
    font-size: 11.5px;
  }

  html.js .site-header .nav {
    flex: none;
  }
}

/* Where the brand's name, the title and the short status do not share a
 * row, the brand keeps its mark and drops its name from view; a screen
 * reader still reads it. */
@media (max-width: 440px) {
  html.js .site-header .brand-name {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

/* The narrowest phones keep the title whole by dropping the version from the
 * short status; About still names it. */
@media (max-width: 380px) {
  html.js .site-header .pg-mini .v {
    display: none;
  }
}

/* ---------- Dialogs and popovers coming and going ----------
 *
 * The scenario picker, About and the leaving dialog fade in and rise a
 * little, over a scrim that fades with them, and go the same way. The
 * popovers -- the step's hint, Import's help, the site menu, the phone's
 * workspace menu, the editor's peek at a change -- fade in and drop a little from the control they open
 * under. The start of each move is @starting-style's; the way out keeps the
 * element in the top layer until the fade ends (display and overlay,
 * allow-discrete). A browser without those opens and closes at once. */
@media (prefers-reduced-motion: no-preference) {
  .pg-next-detail,
  .pg-help-pop,
  .pg-sitemenu,
  .pg-actions[popover],
  .pgc-ed-peek {
    opacity: 0;
    transform: translateY(-4px);
    transition:
      opacity 150ms ease-out,
      transform 150ms ease-out,
      display 150ms allow-discrete,
      overlay 150ms allow-discrete;
  }

  .pg-next-detail:popover-open,
  .pg-help-pop:popover-open,
  .pg-sitemenu:popover-open,
  .pg-actions:popover-open,
  .pgc-ed-peek:popover-open {
    opacity: 1;
    transform: none;
  }

  @starting-style {
    .pg-next-detail:popover-open,
    .pg-help-pop:popover-open,
    .pg-sitemenu:popover-open,
    .pg-actions:popover-open,
    .pgc-ed-peek:popover-open {
      opacity: 0;
      transform: translateY(-4px);
    }
  }
}

@media (prefers-reduced-motion: no-preference) {
  .pg-picker,
  .pgc-overlay,
  .pg-about,
  .pg-leave,
  .pg-picker::backdrop,
  .pgc-overlay::backdrop,
  .pg-about::backdrop,
  .pg-leave::backdrop {
    transition:
      opacity 180ms ease-out,
      transform 180ms ease-out,
      display 180ms allow-discrete,
      overlay 180ms allow-discrete;
  }

  .pg-picker,
  .pgc-overlay,
  .pg-about,
  .pg-leave {
    opacity: 0;
    transform: translateY(8px) scale(0.98);
  }

  .pg-picker[open],
  .pgc-overlay[open],
  .pg-about[open],
  .pg-leave[open] {
    opacity: 1;
    transform: none;
  }

  .pg-picker::backdrop,
  .pgc-overlay::backdrop,
  .pg-about::backdrop,
  .pg-leave::backdrop {
    opacity: 0;
  }

  .pg-picker[open]::backdrop,
  .pg-about[open]::backdrop,
  .pg-leave[open]::backdrop {
    opacity: 0.82;
  }

  .pgc-overlay[open]::backdrop {
    opacity: 1;
  }

  @starting-style {
    .pg-picker[open],
    .pgc-overlay[open],
    .pg-about[open],
    .pg-leave[open] {
      opacity: 0;
      transform: translateY(8px) scale(0.98);
    }

    .pg-picker[open]::backdrop,
    .pgc-overlay[open]::backdrop,
    .pg-about[open]::backdrop,
    .pg-leave[open]::backdrop {
      opacity: 0;
    }
  }
}

/* ---------- Responsive ----------
 *
 * Two layouts, and nothing between them: above 900px the full-window editor
 * (see "Full window"), and up to 900px one column, which the phone block
 * below turns into panes behind a tab bar when the script runs. A width
 * between the two gets one of them, never a page of its own.
 */

@media (max-width: 900px) {
  .pg-grid {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(320px, auto) minmax(280px, auto) auto 320px;
  }
  .pg-rail {
    grid-column: 1;
    grid-row: 1;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    border-right: 0;
    border-bottom: 1px solid var(--line);
  }
  .pg-editor {
    grid-column: 1;
    grid-row: 2;
    border-right: 0;
    border-bottom: 1px solid var(--line);
  }
  .pg-db {
    grid-column: 1;
    grid-row: 3;
    border-bottom: 1px solid var(--line);
  }
  .pg-step-hint {
    display: none;
  }
  .pg-next {
    grid-row: 4;
  }
  .pg-terminal {
    grid-row: 5;
  }
  .pg-steps {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .pg-step {
    border-bottom: 1px solid var(--line);
  }
  .pg-step:nth-child(2n) {
    border-right: 0;
  }
  .pg-step[aria-current="step"] {
    border-bottom-color: var(--line);
    box-shadow: inset 0 -2px 0 var(--acc);
  }
  .pg-next {
    grid-template-columns: minmax(0, 1fr);
    gap: 16px;
  }
}

/* ---------- Phone and tablet portrait ----------
 *
 * Up to 900px: a phone, and a tablet held upright, where the stacked panes
 * gave the steps three rows and put the step's strip under the editor.
 * One column, and the page scrolls. What a visitor needs at every moment
 * stays in one place: the steps as a row of numbers, the step's strip under
 * them whichever pane is open, and the three panes behind a tab bar fixed to
 * the bottom of the screen, the way a phone app switches views. The file
 * list is the Database tab's, not repeated above the editor, and the
 * terminal takes most of a screen. */
@media (max-width: 900px) {
  .pg-toolbar {
    padding-top: 16px;
    padding-bottom: 12px;
    gap: 10px 12px;
  }

  /* Without the script, one row of compact buttons. site.css stacks .actions
   * into a column on a phone, which gave these four the first screen to
   * themselves. With it, they are a menu; see below. */
  .pg-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
  }

  .pg-actions .btn {
    padding: 8px 12px;
    font-size: 13.5px;
  }

  /* The scenario name and the status pill cannot share a 390px line, and a
     select does not shrink below its longest option on its own. */
  .pg-bar {
    flex-wrap: wrap;
    row-gap: 8px;
  }
  .pg-scenario {
    flex: 1 1 100%;
  }
  .pg-scenario-btn {
    flex: 1 1 auto;
    min-width: 0;
  }
  .pg-bar {
    flex: 1 1 100%;
  }
  .pg-actions {
    margin-left: 0;
  }

  /* The workspace actions as a menu, opened by the "⋯" at the end of the
   * scenario's line, which takes that line's last 36px. src/main.ts makes the
   * row the menu's popover at this width. Reset throws the workspace's edits
   * away, so a rule sets it apart from the other three. */
  html.js .pg-bar {
    flex: 1 1 0;
  }
  html.js .pg-more-btn {
    display: inline-grid;
  }
  /* The menu's look is on [popover], not on :popover-open: a closing menu
   * stays drawn while it fades, and without it the fade ran as a column of
   * buttons in the middle of the screen. */
  html.js .pg-actions[popover] {
    inset: auto;
    margin: 0;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 0;
    min-width: 220px;
    padding: 6px 0;
    border: 1px solid var(--line);
    border-radius: 2px;
    background: var(--bg);
    color: var(--fg);
  }
  html.js .pg-actions[popover] .btn {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    min-height: 44px;
    padding: 0 16px;
    border: 0;
    border-radius: 0;
    background: none;
    font: 500 14px var(--sans);
    color: var(--fg2);
    text-align: left;
  }
  html.js .pg-actions[popover] #pg-import {
    grid-column: 1;
  }
  html.js .pg-actions[popover] .btn:hover:not(:disabled) {
    background: var(--code);
    color: var(--fg);
  }
  html.js .pg-actions[popover] .btn:focus-visible {
    outline-offset: -2px;
  }
  html.js .pg-actions[popover] .pg-help-btn {
    grid-column: 2;
    margin: 0 16px 0 8px;
  }
  html.js .pg-actions[popover] #pg-reset {
    margin-top: 6px;
    border-top: 1px solid var(--line);
  }
  html.js .pg-actions[popover]:not(:popover-open) {
    display: none;
  }
  .pg-state {
    width: 100%;
    justify-content: space-between;
    gap: 12px;
  }
  .pg-toolbar-title {
    font-size: 20px;
  }

  /* The route as a row of numbers: every step can be reached, a done one
   * shows its tick, and the one on screen spells out its title. */
  .pg-steps:not(.is-empty) {
    display: flex;
  }
  /* Moving to another step widens it and narrows the one it leaves, and
   * the title shows and hides with the width. Only the grow factor changes
   * between the two, so the width animates. */
  .pg-step {
    flex: 0 1 auto;
    justify-content: flex-start;
    min-width: 44px;
    padding: 10px 12px;
    border-right: 1px solid var(--line);
    border-bottom: 0;
  }
  .pg-step-title {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    max-width: 24ch;
  }
  @media (prefers-reduced-motion: no-preference) {
    .pg-step {
      transition: flex-grow 260ms ease, background-color 260ms ease, box-shadow 260ms ease;
    }
    .pg-step-title {
      transition: max-width 260ms ease, opacity 200ms ease, margin-right 260ms ease;
    }
  }
  .pg-step:nth-child(2n) {
    border-right: 1px solid var(--line);
  }
  .pg-step:last-child {
    border-right: 0;
  }
  .pg-step:not([aria-current="step"]) .pg-step-title {
    max-width: 0;
    margin-right: -10px;
    opacity: 0;
  }
  .pg-step-hint,
  .pg-step-of {
    display: none;
  }
  .pg-step[aria-current="step"] {
    flex-grow: 1;
    min-width: 44px;
    border-bottom-color: transparent;
    box-shadow: inset 0 -2px 0 var(--acc);
  }

  /* The tabbed layout needs JavaScript to switch between panes, so without it
   * the panes simply stay stacked and everything is still reachable. With
   * it, the tabs are a bar fixed to the bottom of the screen, and the page
   * keeps that much room free under its last line. */
  html.js .pg-panetabs {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    position: fixed;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 20;
    border-top: 1px solid var(--line);
    border-bottom: 0;
    background: var(--code);
  }
  html.js body {
    padding-bottom: 53px;
  }
  html.js .pg-panetab {
    flex-direction: column;
    gap: 2px;
    min-height: 52px;
    padding: 6px 8px 7px;
    font-size: 11.5px;
  }
  html.js .pg-panetab[aria-selected="true"] {
    color: var(--acc);
    background: var(--bg);
    box-shadow: inset 0 2px 0 var(--acc);
  }
  html.js .pg-grid[data-pane="editor"] > .pg-rail,
  html.js .pg-grid[data-pane="editor"] > .pg-db,
  html.js .pg-grid[data-pane="editor"] > .pg-terminal,
  html.js .pg-grid[data-pane="console"] > .pg-rail,
  html.js .pg-grid[data-pane="console"] > .pg-editor,
  html.js .pg-grid[data-pane="console"] > .pg-db,
  html.js .pg-grid[data-pane="database"] > .pg-editor,
  html.js .pg-grid[data-pane="database"] > .pg-terminal,
  html.js .pg-grid[data-pane="database"] > .pg-rail {
    display: none;
  }
  /* The Database tab's own strip leads with Workspace, and that list is the
   * rail, under the strip. */
  html.js .pgc-tab-workspace {
    display: flex;
  }
  html.js .pg-grid[data-pane="database"]:has(> .pg-db[data-view="workspace"]) > .pg-rail {
    display: grid;
    order: 1;
  }
  html.js .pg-grid {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto;
  }
  html.js .pg-grid > * {
    grid-column: 1;
    grid-row: auto;
  }
  /* The step's strip first, whichever pane is open. */
  html.js .pg-grid > .pg-next {
    order: -1;
  }
  .pg-terminal {
    height: max(360px, 62dvh);
    min-height: 0;
  }

  /* The build details and the footer are not drawn: the build and the
   * runtime's state sit beside the title, and the rest -- the commit, SQLite,
   * what the session keeps, the footer's links -- is in About, which that
   * short form opens. Without the script there is no About to open, so both
   * stay. */
  html.js .pg-statusbar > :not(.pg-install-link),
  html.js .site-footer {
    display: none;
  }
  html.js .pg-statusbar {
    justify-content: center;
    padding: 4px 12px;
    border-top: 1px solid var(--line);
    background: var(--code);
  }
  html.js .pg-mini {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
    padding: 4px 8px;
    border: 1px solid var(--line);
    border-radius: 2px;
    background: none;
    font: 12px var(--mono);
    color: var(--fg2);
    white-space: nowrap;
    cursor: pointer;
  }
  .pg-mini-status[data-tone="quiet"] {
    color: var(--mute);
  }
  .pg-mini-status[data-tone="attention"] {
    color: var(--amber);
  }

  /* Each pane is the page: the page is the screen's height, and the pane
   * takes what the toolbar, the steps and the strip leave above the tab
   * bar, scrolling inside itself, so the page never scrolls away.
   *
   * The pane never gets less than 160px, though: on a phone held sideways
   * the screen leaves it 74px, and the terminal's bar and prompt sat on top
   * of each other. There the page is taller than the screen and scrolls by
   * what the pane lacks, the tab bar's room included. */
  html.js body:has(.pg-grid[data-pane]) {
    display: flex;
    flex-direction: column;
    min-height: 100dvh;
  }
  html.js body:has(.pg-grid[data-pane]) .site-header {
    flex: none;
  }
  html.js body:has(.pg-grid[data-pane]) .pg-main,
  html.js body:has(.pg-grid[data-pane]) .pg-app {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }
  html.js body:has(.pg-grid[data-pane]) .pg-app > :not(.pg-grid) {
    flex: none;
  }
  html.js .pg-grid[data-pane] {
    flex: 1 1 auto;
    grid-template-rows: auto minmax(160px, 1fr);
  }
  /* What a pane holds does not size it: a long schema or transcript scrolls
   * inside the pane rather than lengthening the page. */
  html.js .pg-grid[data-pane] > :is(.pg-terminal, .pg-editor, .pg-rail),
  html.js .pg-grid[data-pane] > .pg-db:not([data-view="workspace"]) {
    contain: size;
  }
  /* Workspace: the strip, the results' tab row, and the list under it. */
  html.js .pg-grid[data-pane="database"]:has(> .pg-db[data-view="workspace"]) {
    grid-template-rows: auto auto minmax(160px, 1fr);
  }
  html.js .pg-grid[data-pane] > .pg-terminal {
    height: auto;
  }
  html.js .pg-grid[data-pane] > .pg-editor,
  html.js .pg-grid[data-pane] > .pg-db,
  html.js .pg-grid[data-pane] > .pg-rail {
    min-height: 0;
  }

  /* The editor can take the whole screen (Editor.setFull): a schema is read
   * and written more than a pane at a time, and here the pane is what the
   * steps and the strip leave. src/main.ts sets the two variables to the part
   * of the screen the keyboard leaves, and makes the rest of the page inert
   * while the editor covers it. */
  html.js .pgc-ed-full {
    display: inline-grid;
  }
  html.js .pg-editor[data-full] {
    position: fixed;
    top: var(--pg-vv-top, 0px);
    right: 0;
    left: 0;
    z-index: 40;
    height: var(--pg-vv-h, 100dvh);
    border: 0;
    background: var(--bg);
  }
  html.js .pg-editor[data-full] .pgc-ed-input {
    overscroll-behavior: contain;
  }
  .pg-rail {
    grid-template-columns: minmax(0, 1fr);
  }
  .pg-transcript {
    padding: 14px 16px;
    font-size: 12.5px;
    line-height: 1.65;
  }
  .pg-prompt {
    padding: 10px 16px;
  }
  /* There is no room for the syntax note beside the input; the same sentence
   * is in the guide. */
  .pg-prompt-help {
    display: none;
  }
  .pg-editor-body {
    font-size: 12.5px;
  }
  /* The strip: its headline, the command in full across the width -- a
   * phone has no hover to show a tooltip, so nothing is cut off -- and under
   * it the parts and the button. */
  .pg-next {
    gap: 10px;
    padding: 12px 16px;
  }
  .pg-next-run {
    display: grid;
    gap: 8px;
  }
  .pg-next-row {
    flex-wrap: wrap;
  }
  .pg-next-run .cmd {
    flex: 1 1 100%;
    order: -1;
    min-width: 0;
    max-width: none;
    padding: 7px 10px;
  }
  .pg-next-run .btn {
    flex: none;
    margin-left: auto;
    padding: 9px 14px;
    text-align: center;
  }
  .pg-next-offer {
    white-space: normal;
  }
  .pg-boot {
    padding: 14px 16px;
  }
}

/* ---------- Guided route ---------- */

/* The steps are buttons: clicking one points the strip at it, and a step can
 * be re-run at any time. Only the user-agent chrome is removed; the geometry
 * and the colours stay in the .pg-step rule above. */
.pg-step {
  appearance: none;
  background: transparent;
  border-top: 0;
  border-left: 0;
  cursor: pointer;
}

/* Where the steps are one row they read as a route. Each step ends in a
 * chevron pointing at the next, and the step on screen is filled as an arrow
 * along those chevrons: notched where the step before it points in, pointed
 * where its own chevron is, with the underline along the arrow's lower edge.
 * A hovered step gets the same arrow as an underline only.
 *
 * The arrow and the chevron have to meet exactly, and clip-path cannot take
 * a length from the element's height, so the step's height is fixed here
 * and the chevron's depth is half of it. The chevron is a square turned 45
 * degrees whose diagonal is that height, drawn inside the step it ends with
 * its tip on the boundary; the arrow reaches back by the same depth into the
 * step before it.
 *
 * The chevrons stay drawn over the fill, so the arrow has an edge rather than
 * a colour that stops. That is a matter of layers: the row is the one
 * stacking context, the fill sits at 0 under the step's text, and every
 * chevron at 1 over both -- including the one belonging to the step before,
 * whose line is the arrow's notch. */
@media (min-width: 901px) {
  .pg-steps {
    --pg-step-h: 44px;
    position: relative;
    isolation: isolate;
  }

  .pg-step {
    --pg-chev: calc(var(--pg-step-h) / 2);
    position: relative;
    height: var(--pg-step-h);
    min-height: 0;
    border-right: 0;
    border-bottom: 0;
  }

  .pg-step + .pg-step {
    padding-left: 24px;
  }

  .pg-step:not(:last-child) {
    padding-right: 28px;
  }

  .pg-step:hover,
  .pg-step[aria-current="step"] {
    background: transparent;
  }

  /* Positioned, so the text paints after the fill in tree order. */
  .pg-step > * {
    position: relative;
  }

  /* Every step carries its arrow, invisible until it is the step on screen
   * or under the pointer, so moving from one step to another is a fade
   * rather than a jump. It takes no clicks: its notch reaches into the step
   * before it, and a click there belongs to that step. */
  .pg-step::before {
    content: "";
    position: absolute;
    z-index: 0;
    top: 0;
    right: 0;
    bottom: 0;
    left: calc(-1 * var(--pg-chev));
    clip-path: polygon(
      0 0,
      calc(100% - var(--pg-chev)) 0,
      100% 50%,
      calc(100% - var(--pg-chev)) 100%,
      0 100%,
      var(--pg-chev) 50%
    );
    background: linear-gradient(to top, var(--mute) 2px, transparent 2px);
    opacity: 0;
    pointer-events: none;
  }

  @media (prefers-reduced-motion: no-preference) {
    .pg-step::before {
      transition: opacity 220ms ease;
    }
  }

  .pg-step:hover::before,
  .pg-step[aria-current="step"]::before {
    opacity: 1;
  }

  .pg-step[aria-current="step"]::before {
    background: linear-gradient(to top, var(--acc) 2px, var(--accbg) 2px);
  }

  /* The first step has no chevron before it, and the last none after it. */
  .pg-step:first-child::before {
    left: 0;
    clip-path: polygon(
      0 0,
      calc(100% - var(--pg-chev)) 0,
      100% 50%,
      calc(100% - var(--pg-chev)) 100%,
      0 100%
    );
  }

  .pg-step:last-child::before {
    clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, var(--pg-chev) 50%);
  }

  .pg-step:not(:last-child)::after {
    content: "";
    position: absolute;
    top: 50%;
    right: 0;
    z-index: 1;
    width: calc(var(--pg-step-h) * 0.7071);
    height: calc(var(--pg-step-h) * 0.7071);
    border-top: 1px solid var(--line);
    border-right: 1px solid var(--line);
    /* The tip of a square turned 45 degrees lies half its diagonal from the
     * centre: 20.7% of its side beyond the right edge it had unturned. */
    transform: translate(-20.7%, -50%) rotate(45deg);
    pointer-events: none;
  }

}

/* A step whose state the page cannot observe keeps its number and says why on
 * hover. There is no third glyph, because anything softer than a tick would
 * still read as a quieter kind of success. */
.pg-step[data-status="unverifiable"] .pg-step-n,
.pg-step[data-status="unknown"] .pg-step-n {
  opacity: 0.55;
}

/* Amber: going off the route is something to read before carrying on. It is
 * not an error, so it gets the attention colour and not an error treatment. */
.pg-next-note {
  font: 13.5px/1.5 var(--sans);
  color: var(--amber);
}

/* ---------- Full window ----------
 *
 * Above 900px the playground is an application rather than a page: it takes
 * the window under the site header, 56px and its rule, and the panes share the
 * height between them instead of each asking for its own. The toolbar and
 * the status bar take --code with the rest of the frame.
 *
 * The page is the window and does not scroll: the application and nothing
 * else. The site header folds into the toolbar -- its links behind the Ptah
 * mark, its theme toggle with the controls -- and the introduction, "What is
 * running" and the footer's links are in About.
 *
 * Inside the grid the step's strip runs across the top, under the steps it
 * belongs to. Below it the file list and the database pane take the whole
 * height at the sides, and the editor and the terminal share the middle, the
 * way an editor docks its panel under the document. data-dock="full" on the
 * grid, which src/dock.ts sets from the toolbar, gives the terminal the whole
 * width instead and stops the side panes above it.
 */
@media (min-width: 901px) {
  body {
    display: flex;
    flex-direction: column;
    height: 100dvh;
    min-height: 540px;
  }

  /* With the script running, the header's links and theme toggle are in the
   * toolbar; without it they stay in the header, which then stays drawn. */
  html.js .site-header {
    display: none;
  }

  html.js .pg-sitemenu-btn {
    display: inline-flex;
  }

  html.js .pg-toolbar-theme {
    display: grid;
  }

  .site-header {
    flex: none;
  }

  .site-footer {
    display: none;
  }

  .pg-main {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }

  /* No auto margins: in the column above they would center the application
   * at its content's width instead of stretching it across the window. */
  .pg-app.wrap {
    max-width: none;
    margin-inline: 0;
    padding-inline: 0;
  }

  .pg-app {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }

  .pg-toolbar {
    flex: none;
    flex-wrap: nowrap;
    gap: 20px;
    padding: 7px 16px;
    background: var(--code);
    border-bottom: 1px solid var(--line);
  }

  .pg-toolbar-title {
    font: 600 15px/1.2 var(--sans);
    letter-spacing: 0;
  }

  .pg-toolbar .btn {
    padding: 5px 12px;
    font-size: 13px;
  }

  .pg-notice {
    flex: none;
    margin: 0;
    padding: 6px 16px;
    border-bottom: 1px solid var(--line);
  }

  .pg-boot {
    flex: none;
    margin: 0;
    border: 0;
    border-bottom: 1px solid var(--line);
    border-radius: 0;
  }

  .pg-steps {
    flex: none;
    border: 0;
    border-bottom: 1px solid var(--line);
    border-radius: 0;
  }

  .pg-steps {
    --pg-step-h: 38px;
  }

  .pg-step {
    align-items: center;
    min-height: 38px;
    padding: 8px 16px;
  }

  /* One line per step: a hint that wrapped made the whole strip taller, and
   * the strip's height comes out of the terminal's. */
  .pg-step-hint {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .pg-grid {
    flex: 1 1 auto;
    min-height: 0;
    grid-template-rows: auto minmax(0, 1fr) var(--pg-term, minmax(240px, 44%));
    border: 0;
  }

  /* Down to 901px the side panes start narrower, so the editor and the
   * terminal between them keep a width to work in. A width dragged to is
   * still the reader's, clamped as it always is. */
  @media (max-width: 1100px) {
    .pg-grid {
      grid-template-columns: var(--pg-rail, 200px) minmax(0, 1fr) var(--pg-db, minmax(280px, 34%));
    }

    /* The strip's headline gets a column two lines can hold, and the strip
     * holds two lines' height whatever the step says, so it still does not
     * move; the command gives way, ending in its ellipsis. */
    .pg-next {
      grid-template-columns: minmax(300px, 1fr) fit-content(55%);
    }
    .pg-next-copy {
      min-height: 40px;
    }

    /* The scenario's name needs the room more than its label does. */
    .pg-scenario-label {
      display: none;
    }
  }

  .pg-next {
    grid-column: 1 / -1;
    grid-row: 1;
    border-top: 0;
    border-bottom: 1px solid var(--line);
  }

  /* The strip keeps the height of a row holding a command box whether or
   * not the step has one -- a finished route, free exploration, a patch's
   * lone button -- so the panes under it do not move as the route does. */
  .pg-next-run {
    min-height: 35px;
    align-content: center;
  }

  /* And one line for the command in it. The site's rule is that a command
   * never hides its tail; here a long one -- `migrations generate` with all
   * its flags -- would take a second line and move every pane under it. So
   * it ends in an ellipsis where it runs out of room, with the whole command
   * in its tooltip, in what Copy takes, and in what the terminal echoes when
   * it runs. */
  .pg-next-run .cmd pre {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .pg-rail {
    grid-row: 2 / -1;
  }

  .pg-editor {
    grid-row: 2;
    border-right: 0;
  }

  .pg-db {
    grid-row: 2 / -1;
    border-left: 1px solid var(--line);
  }

  .pg-terminal {
    grid-column: 2;
    grid-row: 3;
  }

  .pg-grid[data-dock="full"] > .pg-rail,
  .pg-grid[data-dock="full"] > .pg-db {
    grid-row: 2;
  }

  .pg-grid[data-dock="full"] > .pg-terminal {
    grid-column: 1 / -1;
  }

  /* The terminal-width pair: two icon buttons that read as one control, the
   * chosen one raised onto the page's own background. */
  html.js .pg-dock {
    display: flex;
    margin-left: auto;
  }

  html.js .pg-toolbar .pg-actions {
    margin-left: 0;
  }

  .pg-statusbar {
    flex: none;
    flex-wrap: nowrap;
    padding: 4px 16px;
    background: var(--code);
    border-top: 1px solid var(--line);
    font-size: 12px;
  }

  .pg-statusbar .meta {
    font-size: 12px;
    min-width: 0;
    flex: 1 1 auto;
    overflow: hidden;
  }
}

/* ---------- Site menu and theme ----------
 *
 * The full-window layout does not draw the site header, so the toolbar
 * carries what it held: the Ptah mark opens the site's links in a popover,
 * and the theme toggle sits with the other controls. Both are hidden
 * elsewhere, where the header is on screen. */
.pg-sitemenu-btn,
.pg-toolbar-theme {
  display: none;
}

.pg-sitemenu-btn {
  align-items: center;
  gap: 5px;
  height: 30px;
  margin: 0 -8px 0 -8px;
  padding: 0 7px;
  border: 1px solid transparent;
  border-radius: 2px;
  background: none;
  color: var(--mute);
  cursor: pointer;
}

.pg-sitemenu-btn:hover,
.pg-sitemenu-btn[aria-expanded="true"] {
  border-color: var(--line);
  background: var(--bg);
  color: var(--fg);
}

.pg-sitemenu-btn:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 1px;
}

.pg-sitemenu {
  inset: auto;
  margin: 0;
  min-width: 220px;
  padding: 6px 0;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--bg);
  color: var(--fg);
}

.pg-sitemenu ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

.pg-sitemenu li:first-child {
  margin-bottom: 6px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--line);
}

.pg-sitemenu a {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 16px;
  font: 500 14px var(--sans);
  color: var(--fg2);
  text-decoration: none;
}

.pg-sitemenu a:hover {
  background: var(--code);
  color: var(--fg);
}

.pg-sitemenu a:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: -2px;
}

.pg-sitemenu a.pg-sitemenu-home {
  font-weight: 600;
  color: var(--fg);
}

/* ---------- Terminal width ---------- */

.pg-dock {
  display: none;
}

.pg-dock-btn {
  display: grid;
  place-items: center;
  width: 32px;
  height: 28px;
  padding: 0;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--mute);
  cursor: pointer;
}

.pg-dock-btn:first-child {
  border-radius: 2px 0 0 2px;
}

.pg-dock-btn + .pg-dock-btn {
  margin-left: -1px;
  border-radius: 0 2px 2px 0;
}

.pg-dock-btn:hover {
  color: var(--fg);
}

.pg-dock-btn[aria-pressed="true"] {
  position: relative;
  border-color: var(--fg2);
  background: var(--bg);
  color: var(--fg);
}

.pg-dock-btn:focus-visible {
  position: relative;
  z-index: 1;
  outline: 2px solid var(--acc);
  outline-offset: 1px;
}

/* ---------- Splitters ----------
 *
 * The lines between the panes, made draggable. Each is a grid item laid over
 * the border it moves, seven pixels wide so a pointer can find it, with the
 * one-pixel line drawn in its middle only while it is hovered, dragged or
 * focused. They exist only in the full-window layout. */
.pg-split {
  display: none;
}

@media (min-width: 901px) {
  .pg-split {
    display: block;
    position: relative;
    z-index: 3;
    padding: 0;
    border: 0;
    background: none;
    touch-action: none;
  }

  .pg-split[aria-orientation="vertical"] {
    grid-row: 2 / -1;
    justify-self: start;
    width: 7px;
    margin-left: -4px;
    cursor: col-resize;
  }

  .pg-split[data-split="rail"] {
    grid-column: 2;
  }

  .pg-split[data-split="db"] {
    grid-column: 3;
  }

  .pg-grid[data-dock="full"] > .pg-split[aria-orientation="vertical"] {
    grid-row: 2;
  }

  .pg-split[aria-orientation="horizontal"] {
    grid-column: 2;
    grid-row: 3;
    align-self: start;
    height: 7px;
    margin-top: -4px;
    cursor: row-resize;
  }

  .pg-split::after {
    content: "";
    position: absolute;
    background: transparent;
  }

  .pg-split[aria-orientation="vertical"]::after {
    top: 0;
    bottom: 0;
    left: 3px;
    width: 1px;
  }

  .pg-grid[data-dock="full"] > .pg-split[aria-orientation="horizontal"] {
    grid-column: 1 / -1;
  }

  .pg-split[aria-orientation="horizontal"]::after {
    left: 0;
    right: 0;
    top: 3px;
    height: 1px;
  }

  .pg-split:hover::after,
  .pg-split.is-dragging::after {
    background: var(--fg2);
  }

  .pg-split:focus {
    outline: none;
  }

  .pg-split:focus-visible::after {
    background: var(--acc);
  }

  .pg-split[aria-orientation="vertical"]:focus-visible::after {
    left: 2px;
    width: 3px;
  }

  .pg-split[aria-orientation="horizontal"]:focus-visible::after {
    top: 2px;
    height: 3px;
  }

  /* While a line is dragged the page must not select the text under the
   * pointer, and the whole window keeps the resize cursor. */
  .pg-app.is-resizing {
    user-select: none;
    cursor: inherit;
  }
}

.pg-engine { margin-left: 16px; }
.pg-engine-button:disabled { cursor: wait; opacity: .6; }
.pg-engine-note { color: var(--fg2); font: 14px/1.6 var(--sans); margin: 0 0 18px; }
.pg-engine-option { width: 100%; display: flex; flex-direction: column; align-items: start; gap: 6px; padding: 18px; margin: 12px 0 0; text-align: left; border: 1px solid var(--line); border-radius: 3px; color: var(--fg); background: var(--bg); cursor: pointer; }
.pg-engine-option strong { font: 500 18px var(--sans); }
.pg-engine-tool { font: 13px var(--mono); color: var(--fg2); }
.pg-engine-description { font: 14px/1.5 var(--sans); color: var(--fg2); }
.pg-engine-choice-status { font: 500 12px var(--sans); color: var(--acc); margin-top: 8px; }
.pg-engine-option:hover:not(:disabled), .pg-engine-option:focus-visible, .pg-engine-option[aria-pressed="true"] { border-color: var(--acc); }
.pg-engine-option:disabled { cursor: default; opacity: .5; }
#pg-import[hidden], #pg-import-help[hidden] { display: none; }
@media (max-width: 900px) { .pg-engine { margin-left: 0; } }
