/* semgit — the desk: the workspace (section 4 minus the sheet), the history
   panel (6), the timeline (6b), zoom, toasts and skip (6d), the responsive
   rules (9) and the desk's own print rules (#70). Linked after topbar.css. */

/* ------------------------------------------ 4. the workspace: desk + sheet */

/* The document is a sheet of paper lying on a desk. The desk is the full width
   of the window and nothing permanently occupies part of it — the history
   floats above it and folds away, so opening the panel never narrows the page
   you are reading. That is the whole reason the panel is not a grid column. */

.shell { display: grid; grid-template-rows: var(--topbar-h) 1fr; height: 100dvh; }

/* The paper is centred on the desk and stays there. The history is anchored to
   the top-left of the desk, out of the flow, in the empty margin beside the
   document — so opening it costs the reader nothing: the document does not
   move, it just stops being alone on the desk. */
.desk {
  position: relative;
  min-height: 0;
  overflow: auto;
  /* The canvas (ui-pass §1): the warm off-white under a faint square grid,
     --grid cells drawn in --grid-line. Two gradients, one per axis. Prints
     white (below). */
  background-color: var(--canvas);
  background-image:
    repeating-linear-gradient(0deg, var(--grid-line) 0 1px, transparent 1px var(--grid)),
    repeating-linear-gradient(90deg, var(--grid-line) 0 1px, transparent 1px var(--grid));
  /* Edit mode has a bar under the topbar (#68); everything on the desk
     that is anchored to its top — the padding, the panel — moves down by
     it, through this one variable. */
  --ribbon-on: 0px;
  /* The panel's pill sits --s5 under the topbar (mock y 60), is --pill-h
     tall and has --s3 to the card (y 92). The paper starts level with the
     card, so the desk's top padding is that whole row. Lanes 1 and 3 were
     told the number (#71, #73). */
  --pill-h: 22px;
  --desk-top: calc(var(--s5) + var(--pill-h) + var(--s3));
  padding: calc(var(--desk-top) + var(--ribbon-on)) var(--s4) var(--s8);
  transition: padding-top var(--mid) var(--ease);
}
.desk:has(.editing[data-mode="edit"]) { --ribbon-on: var(--ribbon-h); }

/* The paper. The flow layout below the breakpoint has exactly two children
   to lay out: history, then this. Relative because the annotation margin is
   positioned against it. */
.paper { min-width: 0; position: relative; }

/* The document page's frame (#58): the editor's root and form wrap #mode;
   #mode wraps a mode's sheet, pagefoot and margin. All of them are
   display:contents so the paper's layout is untouched and the margin stays
   positioned against the paper. A parked mode (the editor, kept alive while
   another mode shows) is hidden outright. */
.editing, .editing-form, .mode { display: contents; }

.rail-empty { padding: var(--s3); font-size: var(--t-sm); color: var(--ink-3); }

/* Screens that are lists, not documents, want the desk without the paper. */
.spread { max-width: var(--spread-w); margin: 0 auto; }

/* ------------------------------------------ 6. the history — pill and card

   Not a column. A pill at the desk's top left says whether the history is
   showing; the card under it holds the timeline. Toggled by a checkbox — no
   JS — so it works with the script off, and shell.js only remembers it. */

.rail {
  position: fixed;
  top: calc(var(--topbar-h) + var(--ribbon-on) + var(--s5));
  left: var(--s3);
  width: var(--rail-w);
  z-index: 1;
  max-height: calc(100dvh - var(--topbar-h) - var(--ribbon-on) - var(--s5) - var(--s4));
  display: flex; flex-direction: column; align-items: flex-start;
  gap: var(--s3);
  transition: top var(--mid) var(--ease);
}

/* The toggle lives in the shell so a plain sibling selector reaches the panel;
   `:has()` is not needed and support is not a question. */
.rail-toggle { position: absolute; opacity: 0; pointer-events: none; }

/* The pill: the checkbox's label, white, hairline, fully round. Its text is
   the state — Hide history while the card shows, Show history while it does
   not — so the button says what it will do. */
.rail-handle {
  flex: none;
  display: inline-flex; align-items: center;
  height: var(--pill-h);
  padding: 0 var(--s3);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 999px;
  box-shadow: var(--e1);
  font-size: var(--t-sm); font-weight: 600;
  line-height: 1;
  color: var(--ink-2);
  cursor: pointer;
  white-space: nowrap;
  user-select: none;
}
.rail-handle:hover { color: var(--ink); background: var(--sunken); }
.rail-handle .rail-show { display: none; }
.rail-toggle:not(:checked) ~ .desk .rail-handle .rail-hide { display: none; }
.rail-toggle:not(:checked) ~ .desk .rail-handle .rail-show { display: inline; }

/* The card: white, soft corners, lifted off the desk. Shared with the
   Changes page's submissions column (notes.css places that one; this is
   the look). */
.history-card {
  display: flex; flex-direction: column;
  min-height: 0;
  padding: var(--s3) var(--s2);
  background: var(--surface);
  border-radius: 10px;
  box-shadow: var(--e3);
}

/* Folded, the card is gone and the pill stays. Visibility, not display, so
   the fold can fade; the delay keeps it hit-testable until it has. */
.rail-card {
  align-self: stretch;
  transition: opacity 130ms var(--ease), transform 160ms var(--ease), visibility 0s;
}
.rail-toggle:not(:checked) ~ .desk .rail-card {
  visibility: hidden;
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity 130ms var(--ease), transform 130ms var(--ease), visibility 0s 130ms;
}

/* A head, for a card that needs one (the submissions column). A word, not
   a tracked-out eyebrow (UI-BRIEF §3). */
.rail-head {
  display: flex; align-items: center; gap: var(--s2);
  padding: 0 var(--s2) var(--s2);
}
.rail-label {
  font-family: var(--ui);
  font-size: var(--t-xs); font-weight: 600;
  color: var(--ink-3);
}

/* ----------------------------------------------- 6b. the timeline itself ---

   One row per commit: a dot on a line, a title line and a meta line. The
   line is drawn with a pseudo-element rather than a border so the last dot
   can end it instead of the line running past. Only the list scrolls; the
   way out to the map stays put at the foot. */

.history {
  display: flex; flex-direction: column;
  min-height: 0;
  overflow-y: auto;
}

.commit {
  position: relative;
  display: grid;
  grid-template-columns: 12px minmax(0, 1fr);
  column-gap: var(--s2);
  padding: var(--s2) var(--s2) var(--s2) var(--s1);
  border-radius: var(--r2);
  color: inherit;
}
.commit:hover { background: var(--sunken); text-decoration: none; }
.commit[aria-current="page"] { background: var(--sunken); }

/* The graph. align-content, not align-items: the cell still stretches the
   full height of the row — the connector is measured off its bottom edge —
   but the dot packs to the top so it sits on the title's first line. */
.commit-node {
  position: relative;
  display: grid;
  align-content: start;
  justify-items: center;
  padding-top: 4px;
}
.commit-node::before {
  content: "";
  box-sizing: border-box;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--line);
  z-index: 1;
}
.commit-node::after {
  content: "";
  position: absolute;
  top: 12px; bottom: calc(-1 * var(--s2) - 4px);
  width: 1px;
  background: var(--line);
}
.commit:last-child .commit-node::after { display: none; }

/* The row being shown: a filled dot. */
.commit[aria-current="page"] .commit-node::before { background: var(--ink); }

/* Under review: the meaning colour (UI-BRIEF §2.1 — a submission is a
   meaning question until it is merged), and a hollow ring for a version
   that is not one yet. After the current rule so a reviewing row stays a
   ring when it is the one shown; the row's background marks it then. */
.commit[data-state="reviewing"] .commit-node::before {
  width: 8px; height: 8px;
  margin: -1px;
  background: var(--surface);
  border: 1.5px solid var(--meaning);
}
.commit[data-state="reviewing"] .commit-title { color: var(--meaning); }

/* The first draft: one faded line. */
.commit[data-state="first"] { opacity: .6; }

.commit-body { display: grid; row-gap: 1px; min-width: 0; }

/* The title line is the label — the version and when, grey and semibold —
   and the meta line is the content: who, and what they called it. Both
   clamp at two lines so a long title cannot stretch the card. */
.commit-title, .commit-meta {
  font-size: var(--t-xs);
  line-height: 1.35;
  overflow: hidden;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
}
.commit-title { font-weight: 600; color: var(--ink-3); }
.commit-meta { color: var(--ink); }

/* Pinned to the foot: the way out to the whole graph. A link, not a button —
   it goes somewhere, it does not do something — drawn as a light pill. */
.rail-map {
  flex: none;
  display: flex; align-items: center; justify-content: center;
  margin: var(--s3) var(--s2) 0;
  height: 24px;
  border-radius: 999px;
  background: var(--sunken);
  font-size: var(--t-xs); font-weight: 600;
  color: var(--ink-2);
}
.rail-map:hover { background: var(--line); color: var(--ink); text-decoration: none; }

/* ------------------------------------------------- 6d. zoom, toasts, skip ---

   Zoom exists because the page does not stretch. With --sheet-w fixed at A4, a
   narrow window scrolls sideways and the reader has no way out; scaling the
   page is the only escape that keeps the invariant, since reflowing it is the
   very thing we forbade. It scales — the line breaks do not move.

   It lives in the View menu rather than floating on the desk. Zoom is set once
   and left; Ctrl +, Ctrl - and Ctrl 0 serve the reader who does move it often,
   and cost no pixels. The menu does not close when a zoom row is used, so
   stepping twice is two clicks and the reading stays put. */

.zoom { display: block; }
.zoom output {
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  color: var(--ink-3);
}

/* An action that changed something on the server says so, once, and goes. The
   error box is for things that stay wrong; this is for things that went right. */
.toasts {
  position: fixed;
  left: 50%; bottom: var(--s5);
  z-index: 9;
  display: flex; flex-direction: column; gap: var(--s2);
  align-items: center;
  transform: translateX(-50%);
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  display: flex; align-items: center; gap: var(--s2);
  max-width: 60ch;
  padding: var(--s2) var(--s4);
  background: var(--ink);
  border-radius: calc(var(--r3) + 2px);
  box-shadow: var(--e2);
  font-size: var(--t-sm);
  color: var(--surface);
  animation: toast-in 160ms var(--ease);
}
.toast[data-kind="error"] { background: var(--danger); }
.toast.is-going { opacity: 0; transition: opacity 200ms var(--ease); }
.toast .mono { color: inherit; opacity: .7; }
@keyframes toast-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* Keyboard operability (G.4). Off-screen until focused, which is the first Tab
   on the page. */
.skip {
  position: absolute;
  left: var(--s4); top: var(--s4);
  z-index: 20;
  padding: var(--s2) var(--s3);
  background: var(--surface);
  border: 1px solid var(--ink);
  border-radius: var(--r2);
  font-size: var(--t-sm);
  color: var(--ink);
  transform: translateY(-200%);
  transition: transform var(--fast) var(--ease);
}
.skip:focus { transform: none; text-decoration: none; }

/* Section 9 sits here, after the rules it overrides, and not in tokens.css:
   a media query on .rail that comes before the base rule loses. */
/* ------------------------------------------------------------ 9. responsive */

/* The panel may sit in the margin only while the margin can hold it:
   794 + 2 * (185 + 14 + 16) = 1224px — --rail-w's arithmetic, in tokens.css;
   move this number with the token. Below that it rejoins the flow and pushes
   the paper across rather than lying on top of it. Folded, only the pill is
   left in the flow, so the paper gets the room straight back. */
@media (max-width: 1224px) {
  /* Same channel as the fixed layout, so the spacing does not change when the
     panel crosses the breakpoint into the flow. */
  .desk { display: flex; align-items: flex-start; gap: var(--channel); }
  /* In the flow the panel starts level with the paper's top, pill first;
     sticky against the desk's content box, so it stays there. */
  .rail {
    position: sticky;
    top: 0; left: auto;
    flex: none;
    width: var(--rail-w);
  }
  .paper { flex: 1 1 auto; }
  /* No fade in the flow: a hidden card would still hold its column. */
  .rail-toggle:not(:checked) ~ .desk .rail { width: auto; }
  .rail-toggle:not(:checked) ~ .desk .rail-card { display: none; }
}

/* Laptop and below. The sheet is deliberately NOT touched here — it used to go
   full-bleed and drop its margins, which is exactly the stretching the invariant
   forbids. A narrow window scrolls to the page instead of reshaping it. */
@media (max-width: 900px) {
  .spread { padding: 0 var(--s3); }
  .rail { max-width: calc(100vw - var(--s4) * 2); }
  .dock { margin-bottom: var(--s3); }
}

@media (max-width: 600px) { .crumb a, .crumb-sep { display: none; } }

/* The desk's share of the print rules; the base is in tokens.css. The grid
   goes with the canvas: `background: none` clears both layers. */
@media print {
  html, body, .shell { height: auto; }
  .shell { display: block; }
  .desk { overflow: visible; padding: 0; background: none; }
  .paper { padding: 0 !important; }
}
