/* ═══════════════════════════════════════════════════════════════════════
   LiGMA — the panel's own styles.

   Import this once, anywhere. Every class is namespaced `gl-` so it can never
   collide with the page it is inspecting — and so the picker can filter its
   own classes out of the selectors it offers you.

   The one token it borrows from the host project is `--grain-img`, used by
   the grain plate. Define it yourself (see INSTALL.md §3) or delete the grain
   half of the tool; nothing else here depends on the host's design system.

   ── ONE LOOK ──────────────────────────────────────────────────────────────
   This file used to carry a base look plus three complete alternatives
   (`stream`, `quiet`, `term`) switched from the FAB. The base had accumulated
   rather than been designed — one type size doing the work of a hierarchy, a
   box round everything — and the three were written to find a better answer.
   This is that answer, folded in, and the switch is gone.

   What it settled on:

   · SANS for prose, MONO for identity. Selectors, paths, ids and the element's
     own words are the things you copy and compare, so they are set in the
     monospace face; everything you READ is set in the system sans. The old
     look was mono throughout and read as a log.
   · SOLID surfaces. Translucency plus blur over a grainy page was mud, and it
     is paint on every scroll frame of a tool that exists to make scrolling
     smooth. Ink, one raised step, hairlines.
   · ONE hue. Cyan is the tool's — no product on any host page owns it, which
     is the whole reason it was chosen — and it is spent on identity and on
     the one filled control per surface. Everything else is grey.
   · A type scale of three sizes and a caption, and hierarchy from weight and
     colour before size.
   ══════════════════════════════════════════════════════════════════════ */

:root {
  /* ── NO HUE ────────────────────────────────────────────────────────────
     The tool owns no colour, and that is the decision the rest of this file
     follows from.

     It used to be cyan on near-black. Every hue on the pages this sits over
     is spoken for — games violet, predict blue, sports green, gamification
     orange, lottery amber, affiliates pink, platform indigo, studio violet,
     the hub's ember — and Retail's teal had drifted close enough to the
     tool's cyan that the overlay read as one more product badge on the page
     it was inspecting. A tool that owns a hue on these pages will always be
     mistaken for something in them.

     So: near-black surfaces, white type, and WHITE as the live colour — the
     fill on a pin, an armed FAB, the one primary control per surface. State
     is said with fill and weight rather than with a second colour, which is
     also how it stays legible on a violet card and a white sheet alike.

     One exception, and it is not decoration: destructive is warm red.

     WHITE NEEDS AN EDGE. On the hub's white sheet a white pin is nothing, so
     everything white carries a dark ring — `--gl-halo` — in the same
     declaration. Anywhere that is missed, the control disappears on exactly
     one kind of page and looks fine everywhere else, which is the worst way
     to be wrong. */
  --gl-ink: #0e0e11;
  --gl-ink-2: #17171b;
  --gl-ink-3: #212127;
  --gl-line: rgba(255, 255, 255, 0.09);
  --gl-line-2: rgba(255, 255, 255, 0.16);
  --gl-text: #f4f4f7;
  --gl-dim: #9a9aa4;
  --gl-mute: #64646d;
  /* the live colour */
  --gl-accent: #ffffff;
  --gl-accent-ink: #0e0e11;
  --gl-accent-soft: rgba(255, 255, 255, 0.1);
  --gl-accent-line: rgba(255, 255, 255, 0.34);
  /* what keeps white readable on a white page */
  --gl-halo: rgba(6, 6, 9, 0.55);
  --gl-danger: #ff7a6b;
  --gl-ok: #dcdce2;
  --gl-warn: #ff7a6b;
  /* the bubble's width. `BUBBLE_W` in Lab.tsx is this number — it decides
     which side of a component the bubble will fit beside, so the two have to
     agree. */
  --gl-bw: 20rem;
  --gl-radius: 14px;
  --gl-radius-s: 8px;
  --gl-shadow: 0 24px 60px -20px rgba(0, 0, 0, 0.8), 0 0 0 1px var(--gl-line-2);
  --gl-sans: ui-sans-serif, -apple-system, 'Inter', 'Segoe UI', system-ui, sans-serif;
  --gl-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --gl-ease: cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* The LiGMA mark, cut to its own bounds and made transparent, inlined so an
   install stays what its doc promises: source files and no asset pipeline.
   The cut originals are in `src/assets/`, kept so they can be re-cut rather
   than re-traced.

   THE COUNTER IS TRANSPARENT, not white — the arch inside the letter is
   negative space, and a flood fill from the frame never reaches it because
   the letter walls it in. Left filled it arrives as a blob with a window
   punched in it.

   TWO CUTS, because the button it sits in inverts. At rest the FAB is ink and
   the mark has to be light; armed it fills white and the mark has to be dark.
   A single cut recoloured with a filter would take the orange drop with it,
   and the drop is the half of this mark that is not a letter. */
:root {
  --gl-mark: url('data:image/png;base64,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');
  --gl-mark-light: url('data:image/png;base64,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');
}

/* ══ the identity card ═══════════════════════════════════════════════════
   Drawn on the element under the pointer — while the picker is armed, and
   with peek on. It is the one per-element overlay that is not in document
   space, because it has to keep up with a pointer, so it is `fixed` and
   re-measured on scroll. */
.gl-hi {
  position: fixed;
  z-index: 9998;
  border: 1.5px solid var(--gl-accent);
  border-radius: 4px;
  background: var(--gl-accent-soft);
  /* the dark companion ring — a white outline on the hub's white sheet is
     nothing at all without it */
  box-shadow:
    0 0 0 1px var(--gl-halo),
    inset 0 0 0 1px var(--gl-halo);
  pointer-events: none;
}
/* the label: an ink plate, not a cyan one — cyan text on a cyan plate was
   the old look's one genuinely unreadable surface */
.gl-hi span {
  position: absolute;
  top: -1.55rem;
  left: -1.5px;
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  max-width: 24rem;
  border-radius: var(--gl-radius-s) var(--gl-radius-s) 0 0;
  background: var(--gl-ink);
  box-shadow: 0 0 0 1px var(--gl-line-2);
  color: var(--gl-text);
  padding: 0.2rem 0.55rem;
  font: 10.5px/1.4 var(--gl-mono);
  letter-spacing: 0.01em;
  white-space: nowrap;
}
/* how many share the selector — the number that says a note here lands on
   more than the thing under the cursor */
.gl-hi em {
  font-style: normal;
  color: var(--gl-warn);
}
/* the element's own words, which is what tells two `.btn`s apart */
.gl-hi b {
  overflow: hidden;
  text-overflow: ellipsis;
  font-weight: 400;
  color: var(--gl-text);
  opacity: 0.85;
}
/* peek says it is a look, not a pick: a dashed edge, no fill */
.gl-hi.is-peek {
  border-style: dashed;
  background: transparent;
}

/* ══ the panel ═══════════════════════════════════════════════════════════
   Two placements, one surface.

   FLOATING: a card, draggable by its bar and resizable by its own corner,
   both remembered — the geometry callback in the component. `resize` needs a
   non-visible `overflow`; the min/max keep the grip from producing a panel
   too small to read or larger than the screen it is reviewing.

   DRAWER (`.gl-drawer`): the right edge, full height, slid in and out. The
   card is right until it is over the thing you are reading; the drawer is
   right when you are working a list and want the page to hold still beside
   it. Placement is the stylesheet's alone here — the callback stands down. */
.gl-panel {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 9999;
  display: flex;
  flex-direction: column;
  width: 22rem;
  min-width: 16rem;
  max-width: 96vw;
  min-height: 9rem;
  max-height: 94vh;
  overflow: hidden;
  resize: both;
  border-radius: var(--gl-radius);
  background: var(--gl-ink);
  color: var(--gl-text);
  font: 12px/1.45 var(--gl-sans);
  box-shadow: var(--gl-shadow);
  -webkit-font-smoothing: antialiased;
}
/* the whole panel scrolls as one column past its own max height, so a long
   list never pushes the footer off the bottom of it */
.gl-panel > * {
  flex: none;
}
.gl-panel .gl-list {
  flex: 1 1 auto;
}
.gl-panel.gl-drawer {
  top: 0;
  right: 0;
  bottom: 0;
  width: min(24rem, 92vw);
  max-width: none;
  max-height: none;
  min-height: 0;
  resize: none;
  border-radius: 0;
  box-shadow:
    -12px 0 40px -20px rgba(0, 0, 0, 0.7),
    -1px 0 0 var(--gl-line-2);
  transition:
    translate 260ms var(--gl-ease),
    visibility 0s linear 0s;
}
/* Closed. The card is simply gone — the FAB's ≡ is its handle. The drawer
   slides off and leaves a tab; `visibility` waits for the slide so it does
   not vanish mid-travel, and `pointer-events` goes at once so nothing under
   the sliding edge is blocked. */
.gl-panel-off {
  display: none;
}
.gl-panel.gl-drawer.gl-panel-off {
  display: flex;
  translate: 100% 0;
  visibility: hidden;
  pointer-events: none;
  transition:
    translate 260ms var(--gl-ease),
    visibility 0s linear 260ms;
}
/* the tab a closed drawer leaves on the edge */
.gl-dock-tab {
  position: fixed;
  top: 50%;
  right: 0;
  z-index: 9999;
  display: grid;
  place-items: center;
  width: 1.6rem;
  height: 4.4rem;
  translate: 0 -50%;
  border: 1px solid var(--gl-line-2);
  border-right: 0;
  border-radius: var(--gl-radius-s) 0 0 var(--gl-radius-s);
  background: var(--gl-ink);
  color: var(--gl-accent);
  font: 600 14px/1 var(--gl-sans);
  cursor: pointer;
  padding: 0;
  box-shadow: -8px 0 24px -14px rgba(0, 0, 0, 0.7);
  transition:
    width 160ms var(--gl-ease),
    background-color 160ms ease;
}
.gl-dock-tab:hover {
  width: 2rem;
  background: var(--gl-ink-2);
}

/* ── the bar ────────────────────────────────────────────────────────────
   The drag handle when the panel floats. Nothing between the title and the
   controls on the right is interactive, so the strip is the grip rather than
   a dotted patch nobody finds. */
.gl-bar {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  min-height: 2.5rem;
  border-bottom: 1px solid var(--gl-line);
  padding: 0.45rem 0.55rem 0.45rem 0.8rem;
  cursor: grab;
  touch-action: none;
  user-select: none;
}
.gl-drawer .gl-bar {
  cursor: default;
}
.gl-bar.is-dragging {
  cursor: grabbing;
}
/* the name, with the tool's dot in front of it */
.gl-bar b {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  color: var(--gl-text);
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.gl-bar b::before {
  content: '';
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 999px 999px 999px 2px;
  background: var(--gl-accent);
}
/* bar controls are glyphs on no box */
/* ── THE SPECIFICITY TRAP IN THIS FILE ──────────────────────────────────
   The base chip is `.gl-panel button, .gl-thread button` — one class plus an
   ELEMENT, which scores (0,1,1). A bare `.gl-x` override scores (0,1,0) and
   LOSES to it, silently: the declarations are all there in the file, they
   simply never win. Three controls shipped wrong this way — an unboxed glyph
   that was a boxed chip, and the one filled commit button in the tool
   rendering as a plain one.

   So anything overriding the base chip is scoped under `.gl-panel` or
   `.gl-thread` and reaches (0,2,0). Rest and hover both, at matching weight,
   or the state that is not scoped gets undercut on its own. */
.gl-panel .gl-bar-btn,
.gl-panel .gl-bar-btn:hover:not(:disabled) {
  flex: none;
  width: 1.5rem;
  height: 1.5rem;
  border: 0;
  border-radius: var(--gl-radius-s);
  background: transparent;
  padding: 0;
  color: var(--gl-dim);
  font-size: 13px;
  line-height: 1;
}
.gl-panel .gl-bar-btn:hover:not(:disabled) {
  background: var(--gl-ink-2);
  color: var(--gl-text);
}
/* the shortcut, printed where the shortcut is — a key you have to be told
   about in a tooltip is a key nobody presses */
.gl-panel kbd {
  border: 1px solid var(--gl-line-2);
  border-radius: 4px;
  background: var(--gl-ink-2);
  padding: 0 0.3rem;
  font: 10.5px/1.5 var(--gl-mono);
  color: var(--gl-dim);
}
.gl-dim {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--gl-dim);
}

/* ── controls ───────────────────────────────────────────────────────────
   Scoped to BOTH surfaces. The bubbles carry the same controls the panel
   does — chips, a commit, a send — and two sets of rules would be two places
   to keep one look.

   Three weights, told apart by where they sit rather than by a class, since
   the markup passes bare <button>s: a quiet outlined chip by default, a
   filled one for the single control per surface that changes a FILE rather
   than a view, and unboxed glyphs inside rows. */
.gl-panel button,
.gl-panel select,
.gl-thread button,
.gl-draw button {
  border: 1px solid var(--gl-line-2);
  border-radius: var(--gl-radius-s);
  background: transparent;
  color: var(--gl-dim);
  padding: 0.22rem 0.55rem;
  font: inherit;
  font-size: 11.5px;
  line-height: 1.35;
  cursor: pointer;
  transition:
    background-color 120ms ease,
    color 120ms ease,
    border-color 120ms ease,
    opacity 120ms ease;
}
.gl-panel button:hover:not(:disabled),
.gl-panel select:hover,
.gl-thread button:hover:not(:disabled),
.gl-draw button:hover:not(:disabled) {
  border-color: var(--gl-line-2);
  background: var(--gl-ink-2);
  color: var(--gl-text);
}
.gl-panel button:disabled,
.gl-thread button:disabled,
.gl-draw button:disabled {
  opacity: 0.35;
  cursor: default;
}
.gl-panel button:focus-visible,
.gl-thread button:focus-visible,
.gl-draw button:focus-visible,
.gl-panel input:focus-visible,
.gl-thread input:focus-visible,
.gl-panel textarea:focus-visible {
  outline: 2px solid var(--gl-accent-line);
  outline-offset: 1px;
}
/* a live chip */
.gl-panel .gl-on,
.gl-thread .gl-on,
.gl-draw .gl-on {
  border-color: var(--gl-accent-line);
  background: var(--gl-accent-soft);
  color: var(--gl-accent);
}
/* the commit — the one filled control. `use this`, `send`, `open`. */
.gl-panel .gl-var-use,
.gl-panel .gl-var-use:hover:not(:disabled),
.gl-draw .gl-var-use,
.gl-draw .gl-var-use:hover:not(:disabled),
.gl-thread .gl-var-use,
.gl-thread .gl-var-use:hover:not(:disabled),
.gl-thread-say button[type='submit'],
.gl-thread-say button[type='submit']:hover:not(:disabled),
.gl-var-say button[type='submit'],
.gl-var-say button[type='submit']:hover:not(:disabled) {
  border-color: var(--gl-accent);
  background: var(--gl-accent);
  color: var(--gl-accent-ink);
  font-weight: 600;
}
.gl-panel .gl-var-use:hover:not(:disabled),
.gl-thread .gl-var-use:hover:not(:disabled),
.gl-thread-say button[type='submit']:hover:not(:disabled),
.gl-var-say button[type='submit']:hover:not(:disabled) {
  filter: brightness(1.08);
}
.gl-panel select {
  appearance: none;
  padding-right: 1.4rem;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--gl-dim) 50%),
    linear-gradient(135deg, var(--gl-dim) 50%, transparent 50%);
  background-position:
    calc(100% - 0.85rem) 55%,
    calc(100% - 0.55rem) 55%;
  background-size: 5px 5px;
  background-repeat: no-repeat;
}

/* ── mode and tabs: underlined segments, not boxed ones ────────────────────
   Both rows size themselves to HOW MANY buttons they hold. They were
   `repeat(3, 1fr)`, which was true until a fourth mode arrived and `preview`
   wrapped onto a line of its own. `auto-flow: column` just asks the row. */
.gl-seg,
.gl-tabs {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  margin: 0;
  border-bottom: 1px solid var(--gl-line);
  padding: 0 0.55rem;
}
.gl-seg button,
.gl-tabs button,
.gl-seg button:hover:not(:disabled),
.gl-tabs button:hover:not(:disabled) {
  border: 0;
  border-radius: 0;
  background: transparent;
  padding: 0.55rem 0 0.5rem;
  color: var(--gl-dim);
  font-size: 11.5px;
  font-weight: 500;
  box-shadow: inset 0 -2px 0 transparent;
  transition:
    color 120ms ease,
    box-shadow 160ms ease;
}
.gl-seg button:hover:not(:disabled),
.gl-tabs button:hover:not(:disabled) {
  color: var(--gl-text);
}
/* the live one. Scoped under `.gl-panel` so the hover states clear the base
   chip's (0,3,1) — unscoped they sat at (0,3,0) and the ACTIVE tab turned
   grey under the pointer, which is the one tab that must not. */
.gl-panel .gl-seg .gl-on,
.gl-panel .gl-tabs .gl-on,
.gl-panel .gl-seg .gl-on:hover,
.gl-panel .gl-tabs .gl-on:hover {
  background: transparent;
  color: var(--gl-accent);
  box-shadow: inset 0 -2px 0 var(--gl-accent);
}

/* the line that says what a click on the page will do */
.gl-hint {
  margin: 0;
  padding: 0.45rem 0.8rem 0;
  color: var(--gl-dim);
  font-size: 11.5px;
}
.gl-quiet {
  padding-block: 0.7rem;
  color: var(--gl-mute);
  text-align: center;
}
/* a caption over a block, so the block is not left to explain itself */
.gl-cap {
  color: var(--gl-mute);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-size: 9.5px;
  font-weight: 600;
}

/* ── the target ─────────────────────────────────────────────────────────
   Captioned and on its own raised ground, because the thing that confuses
   people about this panel is which element the field is about. */
.gl-sel {
  display: grid;
  gap: 0.35rem;
  margin: 0.6rem 0.55rem 0.2rem;
  border-radius: var(--gl-radius-s);
  background: var(--gl-ink-2);
  padding: 0.55rem 0.65rem 0.6rem;
}
.gl-sel-head {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
.gl-sel-head code {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font: 11.5px/1.4 var(--gl-mono);
  color: var(--gl-accent);
}
.gl-sel-head button,
.gl-sel-head button:hover:not(:disabled) {
  flex: none;
  border: 0;
  background: none;
  padding: 0;
  color: var(--gl-dim);
  font-size: 13px;
}
.gl-panel .gl-sel-head button:hover:not(:disabled) {
  color: var(--gl-danger);
}
/* "1 of 9" — the shared-selector warning, stated rather than implied */
.gl-share {
  flex: none;
  border-radius: 4px;
  background: rgba(255, 122, 107, 0.14);
  padding: 0.05rem 0.35rem;
  color: var(--gl-warn);
  font: 600 10px/1.5 var(--gl-mono);
}
/* the element quoting itself */
.gl-said {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--gl-dim);
  font-style: italic;
}
.gl-route {
  margin-left: 0.4rem;
  color: var(--gl-mute);
  font: 10.5px/1.4 var(--gl-mono);
}
.gl-sel textarea {
  width: 100%;
  display: block;
  /* the JS sets an exact height from the content; both of these keep it from
     fighting back while it does */
  resize: none;
  overflow-y: auto;
  min-height: 2rem;
  border: 1px solid var(--gl-line-2);
  border-radius: var(--gl-radius-s);
  background: var(--gl-ink);
  color: inherit;
  padding: 0.4rem 0.5rem;
  font: inherit;
  line-height: 1.45;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.2) transparent;
}
.gl-sel textarea::placeholder {
  color: var(--gl-mute);
}
.gl-sel textarea:focus {
  border-color: var(--gl-accent-line);
  outline: none;
}

/* ── lists ──────────────────────────────────────────────────────────────
   A 6px thumb on no track, with a matching gutter, so nothing sits under
   the scrollbar either way. */
.gl-list {
  display: grid;
  align-content: start;
  gap: 0.2rem;
  max-height: 11rem;
  overflow: auto;
  padding: 0.45rem 0.55rem;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.2) transparent;
}
.gl-drawer .gl-list {
  max-height: none;
}
.gl-list::-webkit-scrollbar {
  width: 6px;
}
.gl-list::-webkit-scrollbar-track {
  background: transparent;
}
.gl-list::-webkit-scrollbar-thumb {
  border-radius: 99px;
  background: rgba(255, 255, 255, 0.18);
}
.gl-list::-webkit-scrollbar-thumb:hover {
  background: rgba(255, 255, 255, 0.3);
}
/* row buttons are glyphs, not boxes */
.gl-row > button,
.gl-note > button {
  border: 0;
  border-radius: 4px;
  background: none;
  padding: 0;
}
.gl-row > button:hover:not(:disabled),
.gl-note > button:hover:not(:disabled) {
  border: 0;
  background: none;
  color: var(--gl-text);
}
/* the destructive one stays faint until the row is under the cursor */
.gl-row > button:last-child,
.gl-note > button:last-child {
  opacity: 0.3;
  color: var(--gl-dim);
}
.gl-row:hover > button:last-child,
.gl-note:hover > button:last-child {
  opacity: 1;
}
.gl-row > button:last-child:hover,
.gl-note > button:last-child:hover {
  color: var(--gl-danger);
}

.gl-row {
  display: grid;
  grid-template-columns: 1fr 2.4rem 0.9rem;
  align-items: center;
  gap: 0.4rem;
  border-radius: var(--gl-radius-s);
  padding: 0.2rem 0.35rem;
}
.gl-row:hover {
  background: var(--gl-ink-2);
}
.gl-row > button:first-child {
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--gl-dim);
  font: 11.5px/1.4 var(--gl-mono);
}
.gl-row i {
  color: var(--gl-ok);
  font: 11px/1.4 var(--gl-mono);
  text-align: right;
}
.gl-act > button:first-child {
  color: var(--gl-accent);
}

/* ── notes ─────────────────────────────────────────────────────────────── */
.gl-notes {
  max-height: 17rem;
  gap: 0;
}
.gl-note {
  display: grid;
  grid-template-columns: 1rem 1fr 0.9rem;
  align-items: start;
  gap: 0.5rem;
  border-top: 1px solid var(--gl-line);
  padding: 0.55rem 0.25rem;
}
.gl-note:first-child {
  border-top: 0;
  padding-top: 0.15rem;
}
.gl-note b {
  color: var(--gl-accent);
  font: 11.5px/1.4 var(--gl-mono);
}
.gl-note .gl-said {
  display: inline;
  margin-left: 0.3rem;
}
.gl-note p {
  margin: 0.2rem 0 0;
  color: var(--gl-text);
  overflow-wrap: anywhere;
  /* a note can have line breaks in it now, so show them */
  white-space: pre-wrap;
}
.gl-panel .gl-tick {
  color: var(--gl-mute);
  line-height: 1.35;
}

/* ── being worked on ────────────────────────────────────────────────────
   A third state between written and done, pushed here by the dev server
   when whoever is acting on the notes picks one up. A ring with a quarter
   missing, spun — not a bar, because there is no progress to report, only
   "someone has this". */
.gl-spin {
  display: block;
  height: 9px;
  width: 9px;
  border: 1.5px solid var(--gl-line-2);
  border-top-color: var(--gl-accent);
  border-radius: 999px;
  animation: gl-spin 700ms linear infinite;
}
@keyframes gl-spin {
  to {
    rotate: 1turn;
  }
}
/* the row itself, lit along its leading edge and breathing, so it is
   findable in a list of twenty without reading any of them */
.gl-working {
  margin-inline: -0.55rem;
  border-left: 2px solid var(--gl-accent);
  padding-inline: 0.7rem 0.55rem;
  background: rgba(255, 255, 255, 0.05);
  animation: gl-breathe 1.8s ease-in-out infinite;
}
@keyframes gl-breathe {
  50% {
    background-color: rgba(255, 255, 255, 0.1);
  }
}
/* the same thing said once in the bar, for when the notes tab is not open */
.gl-busy {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  color: var(--gl-accent);
}
.gl-busy i {
  height: 5px;
  width: 5px;
  flex: none;
  border-radius: 999px;
  background: currentColor;
  animation: gl-pulse 1.4s ease-in-out infinite;
}
@keyframes gl-pulse {
  0%,
  100% {
    opacity: 0.35;
    scale: 0.8;
  }
  50% {
    opacity: 1;
    scale: 1.25;
  }
}
.gl-done {
  opacity: 0.4;
}
.gl-done .gl-tick {
  color: var(--gl-ok);
  opacity: 1;
}
.gl-done p {
  text-decoration: line-through;
}

/* ── the footer ────────────────────────────────────────────────────────── */
.gl-acts {
  display: flex;
  gap: 0.4rem;
  border-top: 1px solid var(--gl-line);
  padding: 0.55rem;
}
.gl-acts button {
  flex: 1;
  padding-block: 0.3rem;
}

/* ── sliders ───────────────────────────────────────────────────────────── */
.gl-ctl {
  display: grid;
  gap: 0.4rem;
  border-top: 1px solid var(--gl-line);
  padding: 0.6rem 0.65rem;
}
.gl-ctl label {
  display: grid;
  grid-template-columns: 3.2rem 1fr 3rem;
  align-items: center;
  gap: 0.5rem;
  color: var(--gl-dim);
}
.gl-ctl input[type='range'] {
  width: 100%;
  height: 1rem;
  accent-color: var(--gl-accent);
}
.gl-ctl i {
  color: var(--gl-ok);
  font: 11px/1.4 var(--gl-mono);
  text-align: right;
}

/* preview: the panel is gone, this is all that is left */
.gl-pick-float {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 9999;
  border: 1px solid var(--gl-accent-line);
  border-radius: 999px;
  background: var(--gl-ink);
  color: var(--gl-accent);
  padding: 0.35rem 0.85rem;
  font: 600 11.5px/1.4 var(--gl-sans);
  cursor: pointer;
  box-shadow: var(--gl-shadow);
}
.gl-pick-float:hover {
  background: var(--gl-ink-2);
}

/* ── pasted images ──────────────────────────────────────────────────────
   Thumbnails are deliberately small and square-cropped: a receipt that the
   paste took, not a viewer. Click one in the list to open it full size;
   click one in the composer to remove it. */
.gl-shots {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}
/* the words, because three small tiles in a dark panel are easy to miss and
   "did that paste work?" is the question this has to answer unasked */
.gl-att {
  display: grid;
  gap: 0.35rem;
}
.gl-att-n {
  color: var(--gl-accent);
  font-size: 11px;
}
.gl-shot {
  position: relative;
  display: block;
  line-height: 0;
}
/* the remove control is a corner, not the whole tile */
.gl-shot-x {
  position: absolute;
  top: -5px;
  right: -5px;
  display: grid;
  height: 1.05rem;
  width: 1.05rem;
  place-items: center;
  border: 1px solid var(--gl-line-2) !important;
  border-radius: 999px !important;
  background: var(--gl-ink) !important;
  padding: 0 !important;
  font-size: 10px;
  line-height: 1;
  color: var(--gl-dim);
}
.gl-shot-x:hover {
  border-color: var(--gl-danger) !important;
  color: var(--gl-danger) !important;
}
.gl-shots a,
.gl-shots button {
  display: block;
  overflow: hidden;
  border: 1px solid var(--gl-line-2);
  border-radius: var(--gl-radius-s);
  background: none;
  padding: 0;
  line-height: 0;
  cursor: pointer;
}
/* CONTAIN, IN A TILE THAT TAKES THE IMAGE'S SHAPE. These were square and
   `cover`, which crops to the middle — and the middle of a 1200×780 sketch is
   a patch of blank paper, so an attached drawing showed up as an empty white
   square. A thumbnail whose whole job is "did that attach, and which one is
   it" cannot throw away the edges, which on a sketch is where the drawing
   usually is.

   Fixed height, free width, capped: a landscape shot comes out wide and a
   phone screenshot narrow, which is itself a thing you can recognise at a
   glance. The ground is there because `contain` letterboxes, and a
   transparent letterbox would show the panel through the picture. */
.gl-shots img {
  height: 3rem;
  width: auto;
  max-width: 6rem;
  display: block;
  background: var(--gl-ink-2);
  object-fit: contain;
}
.gl-shots-sm {
  margin-top: 0.4rem;
}
.gl-shots-sm img {
  height: 2.5rem;
  max-width: 5rem;
}
.gl-panel .gl-shots button:hover:not(:disabled),
.gl-thread .gl-shots button:hover:not(:disabled) {
  border-color: var(--gl-danger);
}
.gl-shots a:hover {
  border-color: var(--gl-accent);
}

/* Full size, over everything. Click anywhere or press Escape. */
.gl-zoom {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: grid;
  place-items: center;
  background: rgba(8, 8, 11, 0.93);
  padding: 2rem;
  cursor: zoom-out;
}
.gl-zoom-x {
  position: absolute;
  top: 1rem;
  right: 1rem;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  border: 1px solid var(--gl-line-2);
  border-radius: 999px;
  background: var(--gl-ink);
  padding: 0.35rem 0.8rem;
  font: 11.5px/1.4 var(--gl-sans);
  color: var(--gl-text);
  cursor: pointer;
}
.gl-zoom-x kbd {
  border: 1px solid var(--gl-line-2);
  border-radius: 4px;
  padding: 0 0.3rem;
  font: 10.5px/1.5 var(--gl-mono);
  color: var(--gl-dim);
}
.gl-zoom-x:hover {
  border-color: var(--gl-accent);
  color: var(--gl-accent);
}
.gl-zoom img {
  cursor: default;
  max-height: 100%;
  max-width: 100%;
  border-radius: var(--gl-radius-s);
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.6);
}

/* ── variants ───────────────────────────────────────────────────────────
   The list is deliberately plain: the thing you are judging is the PAGE, not
   this panel. The chips swap the live stylesheet and you look at the real
   element. `original` is always the first chip. */
.gl-vars {
  gap: 0;
}
.gl-var {
  display: grid;
  gap: 0.4rem;
  border-top: 1px solid var(--gl-line);
  padding: 0.65rem 0.25rem;
}
.gl-var:first-child {
  border-top: 0;
}
.gl-var b {
  color: var(--gl-accent);
  font: 11.5px/1.4 var(--gl-mono);
}
.gl-var p {
  margin: 0;
  color: var(--gl-text);
  overflow-wrap: anywhere;
}
.gl-var-keys {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin-top: 0.1rem;
}
.gl-var-act {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.1rem;
}
.gl-var-act .gl-dim {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── a variant set's thread ───────────────────────────────────────────── */
.gl-var-thread {
  margin-top: 0.4rem;
  border-left: 2px solid var(--gl-accent-line);
  padding-left: 0.6rem;
  display: grid;
  gap: 0.35rem;
}
.gl-var-thread p {
  margin: 0;
  line-height: 1.45;
}
.gl-var-thread b {
  color: var(--gl-accent);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 9.5px;
}
.gl-say-claude {
  color: var(--gl-dim);
}
.gl-var-say {
  display: flex;
  gap: 0.4rem;
  margin-top: 0.45rem;
}
.gl-var-say input {
  flex: 1;
  min-width: 0;
  border: 1px solid var(--gl-line-2);
  border-radius: var(--gl-radius-s);
  background: var(--gl-ink);
  padding: 0.35rem 0.5rem;
  color: inherit;
  font: inherit;
}
.gl-var-say input::placeholder {
  color: var(--gl-mute);
}
.gl-var-say button[disabled] {
  opacity: 0.4;
  cursor: default;
}

/* ── armed: a ring round the viewport while pick mode is on ─────────────
   "Why do none of the links work" is the single most expensive question
   this tool has produced. A panel in the corner is easy to stop seeing. The
   whole frame is not. */
.gl-armed {
  position: fixed;
  inset: 0;
  z-index: 9997;
  border: 2px solid var(--gl-accent-line);
  pointer-events: none;
}
/* ── the panel, while the picker is armed ────────────────────────────────
   It fades out from under the pointer. The picker resolves hovers and clicks
   through it (`under()` in the picking effect), so the element beneath is
   the one being pointed at — this is the half that lets you SEE it.

   Plain `:hover`, which is why the panel keeps its pointer events rather than
   being switched to `pointer-events: none`: a surface that vanishes from
   hit-testing cannot notice the pointer that is over it.

   `.gl-hi` comes up above the panel for the same moment, because the
   highlight and its label are the only report of what is under there. */
html.gl-picking .gl-panel {
  transition: opacity 140ms var(--gl-ease);
}
html.gl-picking .gl-panel:hover {
  opacity: 0.1;
}
html.gl-picking .gl-hi {
  z-index: 10002;
}

/* Decorative artwork opts back into hit-testing while the picker is armed —
   see the walk in the picking effect. `!important` because the rule it has to
   beat is almost always an inline or utility `pointer-events: none`. */
.gl-pickable {
  pointer-events: auto !important;
}

/* ── the pick cursor ──────────────────────────────────────────────────────
   While the picker is armed the native cursor is hidden and this stands in
   for it. `cursor: none` has to be shouted at the whole tree — every link,
   button and text run on the site sets its own cursor, and an inherited
   `none` loses to all of them. The tool's own surfaces are exempt: that is
   the one place you are still operating a UI rather than picking. */
html.gl-picking,
html.gl-picking * {
  cursor: none !important;
  /* A drag across the page is a MARQUEE here, not a text selection — without
     this it does both, and you finish a pass with half the page highlighted
     blue under the boxes you just drew.

     On the whole tree while armed rather than only during the drag: a
     selection can start on the mousedown that becomes a drag, so switching it
     off at the moment the drag is recognised is already too late, and a class
     toggled per drag is a write to <html> on a pointer event that is trying
     to stay cheap. Nothing on the page is selectable while the picker is
     armed anyway — every click is being swallowed. */
  user-select: none !important;
  -webkit-user-select: none !important;
}
html.gl-picking .gl-panel,
html.gl-picking .gl-panel *,
html.gl-picking .gl-dock-tab,
html.gl-picking .gl-fab-set,
html.gl-picking .gl-fab-set *,
/* and the pins. These were missing, and it showed the moment a bubble was
   open while the picker was armed: the stand-in cursor hides itself over the
   tool's own surfaces, the real one was suppressed here, and the reply box
   had no pointer at all. */
html.gl-picking .gl-pins,
html.gl-picking .gl-pins * {
  cursor: auto !important;
  /* the tool's own text stays selectable — you type in these, and a reply box
     you cannot select a word in to correct it is worse than one that scrolls */
  user-select: text !important;
  -webkit-user-select: text !important;
}
html.gl-picking .gl-panel button,
html.gl-picking .gl-panel a,
html.gl-picking .gl-dock-tab,
html.gl-picking .gl-fab-set button,
html.gl-picking .gl-pins button {
  cursor: pointer !important;
}
html.gl-picking .gl-pins input,
html.gl-picking .gl-pins textarea {
  cursor: text !important;
}
html.gl-picking .gl-bar {
  cursor: grab !important;
}
html.gl-picking .gl-drawer .gl-bar {
  cursor: default !important;
}
html.gl-picking .gl-bar.is-dragging {
  cursor: grabbing !important;
}
/* Positioned by writing `translate` on the node itself, once per mouse
   frame, from the handler that was already running — no React render, no
   layout. The node sits at the origin and is moved to the pointer, which is
   why every child is placed relative to 0,0 here. */
.gl-cursor {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 10000;
  pointer-events: none;
  will-change: translate;
}
.gl-cursor.is-off {
  opacity: 0;
}
.gl-cursor-ring,
.gl-cursor-dot,
.gl-cursor-btn {
  position: absolute;
  display: block;
}

/* A RETICLE, drawn. It was a cyan ring with a photographed key cap riding
   beside it — 9KB of base64 PNG inlined in a stylesheet whose install doc
   promises source files and no asset pipeline, and a piece of skeuomorphism
   in a tool that is flat everywhere else.

   A ring, a crosshair and a word: what something for choosing a precise point
   looks like. White with a dark companion stroke, because one sweep of the
   pointer crosses a white sheet and a violet card. */
.gl-cursor-ring {
  top: -11px;
  left: -11px;
  width: 22px;
  height: 22px;
  border: 1.5px solid var(--gl-accent);
  border-radius: 999px;
  box-shadow:
    0 0 0 1px var(--gl-halo),
    inset 0 0 0 1px var(--gl-halo);
  animation: gl-cursor-breathe 2.1s ease-out infinite;
}
/* the crosshair: this span is the vertical rule, its pseudo the horizontal */
.gl-cursor-dot {
  top: -9px;
  left: 0;
  width: 1.5px;
  height: 18px;
  background: var(--gl-accent);
  box-shadow: 0 0 0 0.75px var(--gl-halo);
}
.gl-cursor-dot::after {
  content: '';
  position: absolute;
  top: 8.25px;
  left: -9px;
  width: 18px;
  height: 1.5px;
  background: var(--gl-accent);
  box-shadow: 0 0 0 0.75px var(--gl-halo);
}

/* The word, so what the pointer is FOR is readable without the panel. An ink
   chip rather than a drawn object: the tool says things in type now. */
.gl-cursor-btn {
  top: 13px;
  left: 15px;
  border-radius: 999px;
  background: var(--gl-ink);
  box-shadow: 0 0 0 1px var(--gl-line-2);
  padding: 0.2rem 0.5rem;
  color: var(--gl-text);
  font: 600 9.5px/1.35 var(--gl-sans);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  transition:
    translate 90ms ease,
    opacity 90ms ease;
}
.gl-cursor-btn::after {
  content: 'pick';
}
/* pressed: the ring closes on the point and the label steps back */
.gl-cursor.is-down .gl-cursor-ring {
  animation-play-state: paused;
  scale: 0.78;
}
.gl-cursor.is-down .gl-cursor-btn {
  translate: 0 2px;
  opacity: 0.55;
}

@keyframes gl-cursor-breathe {
  0% {
    opacity: 0.85;
    scale: 0.8;
  }
  70% {
    opacity: 0;
    scale: 1.3;
  }
  100% {
    opacity: 0;
    scale: 1.3;
  }
}

/* ── the marquee ─────────────────────────────────────────────────────────
   Drawn by writing four styles on the node from the mousemove that was
   already running, like the pointer above it — no React render per frame over
   a page the tool is meant to be helping you look at.

   Dashed, to match the outline a picked component gets, and carrying the same
   pair of strokes every white line here carries: one white, one dark, so the
   box reads over a cream section and a violet card alike. `is-on` rather than
   a width of zero, so it is not a 1px seam sitting on the page between
   drags. */
.gl-band {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 9996;
  display: none;
  border: 1.5px dashed var(--gl-accent);
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.08);
  box-shadow:
    0 0 0 1px var(--gl-halo),
    inset 0 0 0 1px var(--gl-halo);
  pointer-events: none;
}
.gl-band.is-on {
  display: block;
}

/* ══ pinned threads, on the page ═════════════════════════════════════════
   A comment lives under the thing it is about, not in a list in the corner.
   The layer is in DOCUMENT space — see the note on `Pins` — so it scrolls
   with the page without anything listening for scroll. It takes no pointer;
   the pins and bubbles opt back in, so the page between them stays live. */
.gl-pins {
  position: absolute;
  inset: 0 auto auto 0;
  z-index: 9990;
  pointer-events: none;
}
.gl-pin-at {
  position: absolute;
  /* the rect's TOP-left is the anchor; pull the pin back so it straddles the
     corner rather than sitting inside the element and covering its first
     word */
  translate: -0.55rem -0.55rem;
}
.gl-pin-at.is-on {
  /* an open thread has to paint over its neighbours' pins */
  z-index: 1;
}
/* the pin: a speech mark with one square corner for its tail, ringed in ink
   so it holds on a white sheet and a violet card alike */
.gl-pin {
  display: grid;
  place-items: center;
  width: 1.45rem;
  height: 1.45rem;
  border: 0;
  border-radius: 999px 999px 999px 3px;
  background: var(--gl-accent);
  color: var(--gl-accent-ink);
  font: 700 10.5px/1 var(--gl-mono);
  cursor: pointer;
  pointer-events: auto;
  box-shadow:
    0 0 0 2px var(--gl-halo),
    0 4px 14px rgba(0, 0, 0, 0.45);
  transition: scale 120ms var(--gl-ease);
}
.gl-pin:hover {
  scale: 1.1;
}
/* claimed by an agent: the fill says it; nobody needed a second colour */
.gl-pin.is-working {
  background: var(--gl-accent);
  color: var(--gl-accent-ink);
}
/* a composer not yet sent: ink with a cyan edge, so "new" and "taken" are
   told apart by fill rather than by hue */
.gl-pin.is-new {
  background: var(--gl-ink);
  color: var(--gl-accent);
  box-shadow:
    0 0 0 1.5px var(--gl-accent),
    0 0 0 2.5px var(--gl-halo),
    0 4px 14px rgba(0, 0, 0, 0.45);
}

/* ── the treatment's name, on the component ──────────────────────────────
   A set plays itself when nobody is steering it, and the chips that say which
   option is up are in the bubble — which can be most of a screen from the
   element that just changed. So the name sits on the element.

   It arrives rather than appearing: a short rise and fade, replayed on every
   change because the node is keyed on the label. A tag whose text silently
   swaps is a tag you stop reading. */
.gl-now {
  position: absolute;
  top: 0;
  left: 1.95rem;
  z-index: 2;
  display: block;
  white-space: nowrap;
  border-radius: 999px;
  background: var(--gl-ink);
  box-shadow:
    0 0 0 1px var(--gl-line-2),
    0 3px 12px rgba(0, 0, 0, 0.4);
  padding: 0.24rem 0.6rem;
  color: var(--gl-text);
  font: 600 10.5px/1.3 var(--gl-sans);
  letter-spacing: 0.02em;
  pointer-events: none;
  animation: gl-now-in 420ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
@keyframes gl-now-in {
  from {
    opacity: 0;
    translate: -0.35rem 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  .gl-now {
    animation: none;
  }
}

/* ── which way the bubble opens ────────────────────────────────────────────
   Down and to the right by default, and the other way when the element is
   near an edge — `is-left` and `is-up`, set in `Pins` from where the element
   actually is on screen, ONCE, when the bubble first appears. */
.gl-thread {
  position: absolute;
  top: 1.9rem;
  left: 0;
  width: var(--gl-bw);
  max-width: 78vw;
  border-radius: var(--gl-radius);
  background: var(--gl-ink);
  color: var(--gl-text);
  padding: 0.65rem 0.7rem 0.7rem;
  font: 12px/1.45 var(--gl-sans);
  box-shadow: var(--gl-shadow);
  pointer-events: auto;
  -webkit-font-smoothing: antialiased;
  /* three panes stack up: history, what the agent is doing, the chips. Each
     caps itself, and this catches the case where all three are long. */
  max-height: 80vh;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.2) transparent;
}
/* ── the bubble goes OUTSIDE the component ────────────────────────────────
   The pin sits on the element's top-left corner, and the bubble used to hang
   down-and-right off it — which is *inside* anything bigger than the bubble.
   Pick a headline and the box landed on the headline.

   `Pins` measures the room on each side of the element and writes the winner
   as `at-right` / `at-left` / `at-up` / `at-down`, once, when the bubble first
   appears. Every offset below is expressed off the element's own box, which
   arrives as `--gl-w` / `--gl-h` on the pin. The pin itself is translated
   -0.55rem so it straddles the corner, which is where the extra 0.55rem in
   each of these comes from. */
.gl-pin-at.at-right .gl-thread {
  top: 0;
  left: calc(var(--gl-w) + 1.1rem);
}
.gl-pin-at.at-left .gl-thread {
  top: 0;
  left: calc(-1 * var(--gl-bw) - 0.55rem);
}
.gl-pin-at.at-down .gl-thread {
  top: calc(var(--gl-h) + 1.1rem);
  left: 0;
}
.gl-pin-at.at-up .gl-thread {
  top: auto;
  bottom: 1.9rem;
  left: 0;
}

/* ── no outside to go to ──────────────────────────────────────────────────
   An element the size of the screen has no side with room on it, and the
   honest answer is to stop pretending the bubble can be anchored to it: it
   parks in the viewport's bottom-left, out of the way of the FAB and of a
   docked drawer, and the DASHED OUTLINE on the component is what keeps the
   two connected. That outline is why this is possible at all — before it
   there was nothing but position to say which element a bubble belonged to,
   so the bubble had to sit on it. */
.gl-pin-at.at-over .gl-thread {
  position: fixed;
  top: auto;
  right: auto;
  bottom: 1rem;
  left: 1rem;
  max-height: 60vh;
}
/* The header is the drag handle — the bubble opens ON the thing you are
   talking about, so it covers it, and no placement rule can know which side
   you actually want it on. Double-click puts it back. */
.gl-thread-top {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 0.5rem;
  border-bottom: 1px solid var(--gl-line);
  padding-bottom: 0.55rem;
  margin-bottom: 0.55rem;
  cursor: grab;
  touch-action: none;
  user-select: none;
}
.gl-thread-top:active {
  cursor: grabbing;
}
.gl-thread-top b {
  color: var(--gl-accent);
  font: 11.5px/1.4 var(--gl-mono);
}
.gl-thread-top .gl-said {
  flex: 1 1 100%;
  font-size: 11px;
}
/* the status, as a pill rather than a run of dim text: it is the one thing
   in the header that CHANGES, and a change reads better in a shape */
.gl-thread-top .gl-dim {
  flex: none;
  border-radius: 999px;
  background: var(--gl-ink-2);
  padding: 0.05rem 0.5rem;
  color: var(--gl-dim);
  font-size: 10.5px;
  font-weight: 500;
}
.gl-thread-top button {
  cursor: pointer;
}
/* ── the dismiss ─────────────────────────────────────────────────────────
   TOP-RIGHT of the bubble, where a dismiss lives. It was in the flow, so the
   header's wrapping put it on the third line beside the status pill — nowhere
   anyone looks for a close, and next to the one thing on the header that
   changes.

   And it is DRAWN, not typed. `×` is a multiplication sign: at body weight on
   a dark ground it is a grey smudge, which is exactly what it had become
   after being unboxed to fix a heavy red one. Two bars at 1.5px hold their
   weight at any size and any contrast.

   A resting plate, because the complaint was that it did not read as
   something you could press. The danger colour is kept for the hover, where
   it is a warning rather than a decoration. */
.gl-thread-top {
  position: relative;
  /* room for the button, so the quote and the pill do not run under it */
  padding-right: 2.1rem;
}
.gl-thread .gl-thread-x,
.gl-thread .gl-thread-x:hover:not(:disabled) {
  position: absolute;
  top: -0.2rem;
  right: -0.2rem;
  margin: 0;
  display: grid;
  place-items: center;
  flex: none;
  width: 1.65rem;
  height: 1.65rem;
  border: 0;
  border-radius: var(--gl-radius-s);
  background: var(--gl-ink-3);
  padding: 0;
  /* the text node is not the glyph — the pseudo-elements below are */
  font-size: 0;
  line-height: 0;
  color: var(--gl-text);
  transition:
    background-color 120ms ease,
    color 120ms ease;
}
.gl-thread .gl-thread-x::before,
.gl-thread .gl-thread-x::after {
  content: '';
  position: absolute;
  width: 11px;
  height: 1.5px;
  border-radius: 1px;
  background: currentColor;
}
.gl-thread .gl-thread-x::before {
  rotate: 45deg;
}
.gl-thread .gl-thread-x::after {
  rotate: -45deg;
}
.gl-thread .gl-thread-x:hover:not(:disabled) {
  background: rgba(255, 122, 107, 0.18);
  color: var(--gl-danger);
}

/* ── history ────────────────────────────────────────────────────────────
   Each turn under a rule on its leading edge: grey for you, cyan for the
   agent — whose turn it is, readable from across the room. */
.gl-thread-msgs {
  display: grid;
  gap: 0.45rem;
  max-height: 15rem;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.2) transparent;
}
.gl-thread-msgs p {
  margin: 0;
  border-left: 2px solid var(--gl-line-2);
  padding-left: 0.6rem;
  line-height: 1.45;
  overflow-wrap: anywhere;
}
.gl-thread-msgs p.gl-say-claude {
  border-left-color: var(--gl-accent-line);
}
.gl-thread-msgs b {
  display: block;
  margin-bottom: 0.1rem;
  color: var(--gl-dim);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 9.5px;
}
.gl-thread-msgs p.gl-say-claude b {
  color: var(--gl-accent);
}

/* ── the process viewer ────────────────────────────────────────────────
   What the agent has actually DONE, written by the agent as it goes. A list
   of sentences, oldest first, the newest picked out. Nothing animates: a
   pulsing element on a page you are trying to LOOK at is the thing this
   whole tool is for. */
.gl-thread-steps {
  margin: 0.5rem 0 0;
  border-top: 1px solid var(--gl-line);
  padding-top: 0.45rem;
}
.gl-thread-steps summary {
  cursor: pointer;
  color: var(--gl-dim);
  font-size: 11px;
  font-weight: 500;
  list-style: none;
}
.gl-thread-steps summary::-webkit-details-marker {
  display: none;
}
.gl-thread-steps summary::before {
  content: '▸';
  display: inline-block;
  margin-right: 0.35rem;
  transition: rotate 120ms ease;
}
.gl-thread-steps[open] summary::before {
  rotate: 90deg;
}
.gl-thread-steps ol {
  list-style: none;
  display: grid;
  gap: 0.25rem;
  margin: 0.4rem 0 0;
  padding: 0.1rem 0 0.1rem 0.6rem;
  border-left: 2px solid var(--gl-accent-line);
  color: var(--gl-dim);
  font-size: 11px;
  max-height: 9rem;
  overflow-y: auto;
}
.gl-thread-steps li {
  overflow-wrap: anywhere;
}
/* the one it is on now */
.gl-thread-steps li:last-child {
  color: var(--gl-text);
}

/* ── the history scrubber ────────────────────────────────────────────────
   Drag left to walk the element back through the states the page has actually
   painted it in. The right-hand end is live, and is where it sits unless you
   have moved it.

   A native range input, styled down rather than rebuilt: it is already
   keyboard-operable, already draggable, and a hand-built slider in a tool
   this small would be a week of edge cases for a control the browser ships. */
.gl-trail {
  display: grid;
  gap: 0.3rem;
  border-top: 1px solid var(--gl-line);
  margin-top: 0.55rem;
  padding-top: 0.55rem;
}
.gl-trail-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  color: var(--gl-dim);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.gl-trail-top b {
  color: var(--gl-text);
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: none;
}
.gl-trail input[type='range'] {
  width: 100%;
  height: 1rem;
  margin: 0;
  accent-color: var(--gl-accent);
  cursor: grab;
}
.gl-trail input[type='range']:active {
  cursor: grabbing;
}
/* Undo takes the base chip from `.gl-thread button` and only needs placing —
   deliberately, because the one thing this control must not look like is the
   filled commit. It sends a REQUEST; the file changes when an agent acts on
   it, and dressing it as the write would promise something the lab cannot
   do. Left-aligned under the slider, not floated beside the reading, so the
   reading stays the thing your eye lands on while dragging. */
.gl-trail .gl-trail-do {
  justify-self: start;
  margin-top: 0.15rem;
}

/* ── the variant picker, in the bubble ─────────────────────────────────── */
.gl-thread-vars {
  display: grid;
  gap: 0.35rem;
  border-top: 1px solid var(--gl-line);
  margin-top: 0.55rem;
  padding-top: 0.55rem;
}
.gl-thread-vars p {
  margin: 0;
  color: var(--gl-text);
  overflow-wrap: anywhere;
}
.gl-thread-vars .gl-var-use {
  justify-self: start;
}

/* ── the standing asks ───────────────────────────────────────────────────
   A row of one-click sentences above the composer's box. Each one SENDS: the
   thread opens with that sentence as its first message.

   Outlined rather than filled even so. They write a file, which is what the
   filled treatment is reserved for — but three filled controls stacked over
   a fourth would make the surface all buttons, and `open` stays the one that
   sends what YOU wrote.

   They wrap rather than scroll: three chips in a 20rem bubble is close, and a
   row that scrolls sideways hides the third one on exactly the narrow bubble
   where it is hardest to notice it is missing. */
.gl-asks {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin-top: 0.55rem;
}
.gl-thread .gl-asks button,
.gl-thread .gl-asks button:hover:not(:disabled) {
  border-radius: 999px;
  padding: 0.18rem 0.55rem;
  font-size: 10.5px;
  font-weight: 500;
  line-height: 1.4;
}
.gl-thread .gl-asks button:hover:not(:disabled) {
  border-color: var(--gl-accent-line);
  background: var(--gl-accent-soft);
  color: var(--gl-text);
}

/* ── the reply row ───────────────────────────────────────────────────────
   TWO LINES: the field, then the controls under it. It was one line — field,
   then buttons beside it — which worked while `send` was the only button and
   stopped working the moment `draw` joined it. Three things competing for
   290px left the field about 165px wide, which is six or seven words before
   it wraps, in the box where you are describing a component.

   `flex: 1 1 100%` on the field is what splits them: it claims a full line,
   the buttons wrap under it, and `margin-left: auto` on the send throws it to
   the far end so the two controls sit at opposite ends of their row rather
   than huddling. Costs about 28px of bubble height, which the field wins back
   the first time a sentence does not wrap three times. */
.gl-thread-say {
  display: flex;
  flex-wrap: wrap;
  /* the box grows downward, so the send sits at its FOOT rather than floating
     against the middle of three lines of text */
  align-items: flex-end;
  gap: 0.35rem;
  margin-top: 0.55rem;
}
.gl-thread-say button[type='submit'] {
  margin-left: auto;
}
/* A textarea sized from its content by the JS — it was an `<input>`, so
   anything longer than the bubble scrolled sideways out of sight, and a
   preset drops a whole sentence in at once. Both of these keep it from
   fighting the height the script sets. */
.gl-thread-say textarea {
  flex: 1 1 100%;
  min-width: 0;
  display: block;
  resize: none;
  overflow-y: auto;
  min-height: 1.95rem;
  border: 1px solid var(--gl-line-2);
  border-radius: var(--gl-radius-s);
  background: var(--gl-ink-2);
  padding: 0.4rem 0.5rem;
  color: inherit;
  font: inherit;
  line-height: 1.45;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.2) transparent;
}
.gl-thread-say textarea::placeholder {
  color: var(--gl-mute);
}
.gl-thread-say textarea:focus {
  border-color: var(--gl-accent-line);
  outline: none;
}
.gl-thread-say button[disabled] {
  opacity: 0.4;
  cursor: default;
}
/* Remove, armed. Red only in the armed state: a permanently red control in a
   row with `send` is a thing you learn to avoid rather than a thing you use. */
.gl-thread-kill.is-armed,
.gl-thread .gl-thread-kill.is-armed:hover:not(:disabled) {
  border-color: rgba(255, 122, 107, 0.55);
  background: rgba(255, 122, 107, 0.14);
  color: var(--gl-danger);
  font-size: 0.92em;
}

/* ── the component, being worked ───────────────────────────────────────
   An outline round the component itself, not a mark near it. It appears the
   moment the thread exists — "did that land on the right element" — and
   starts MOVING when an agent claims it — "is anything happening". Two
   questions, two answers. */
/* ── the outline takes its colour from the ground ────────────────────────
   `Pins` measures what is painted under the component (`groundIsLight`) and
   the line is the opposite of it: white on a dark card, ink on a pale sheet.
   It was white always, which is invisible on the hub's own cream sections —
   a border drawn on somebody's design that they cannot see is worse than no
   border, because the pin then points at nothing.

   Both colours travel together as a pair. Each line carries a COMPANION ring
   in the opposite value, so neither depends on the measurement being right:
   get the ground wrong and it is still legible, just the other way round. */
.gl-busy-box {
  --box-ink: var(--gl-accent);
  --box-halo: var(--gl-halo);
  position: absolute;
  top: 0;
  left: 0;
  /* the pin is translated off the corner; put the box back on it */
  translate: 0.55rem 0.55rem;
  border: 3px solid var(--box-ink);
  border-radius: 5px;
  pointer-events: none;
}
.gl-busy-box.on-light {
  --box-ink: #0e0e11;
  --box-halo: rgba(255, 255, 255, 0.7);
}
.gl-busy-box::after {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 8px;
  border: 1px solid var(--box-halo);
}
/* ── showing a set: the mark stands down ─────────────────────────────────
   A 3px box in the ground's own ink is the right weight for "an agent has
   this" — it has to be found on a busy page. It is the wrong weight entirely
   while a set is playing itself, because then you are judging the DESIGN and
   the loudest thing on the screen is the tool's own frame around it.

   Down to a hairline at a third strength, and the companion ring goes: a
   second stroke exists to make a line survive any ground, and this one is
   meant to be barely survivable. The pin and the name tag still say which
   component it is, which is why the box can afford to nearly vanish. */
.gl-busy-box.is-showcase {
  border-width: 1px;
  border-color: color-mix(in srgb, var(--box-ink) 32%, transparent);
}
.gl-busy-box.is-showcase::after {
  display: none;
}

/* The component a composer is about — picked, nothing said about it yet.
   Dashed and unfilled: a quieter relative of the working ring, because "you
   picked this" and "an agent is changing this" arriving in the same shape
   would make the one that matters unreadable.

   IT KEEPS ITS COMPANION RING. It was dropped from this one on the reasoning
   that a dashed line has its own gaps to be read by — which is not what a
   companion is for. The gaps break the line up; they do nothing about a white
   line on a cream page, which is exactly where this was invisible. A dashed
   line needs the second value MORE than a solid one, not less: there is half
   as much of it to find. */
.gl-busy-box.is-pick {
  border-width: 2px;
  border-style: dashed;
}
.gl-busy-box.is-pick::after {
  inset: -3px;
  border-radius: 7px;
  border-style: dashed;
}

/* claimed: a Bayer-dithered field over the element (`PixelBlast` in
   Lab.tsx). No border and no radius while it runs — the field fades into its
   own edges, and a stroke round it would be a second frame. */
/* ── claimed: a GRAINY BLUR, not dots on type ────────────────────────────
   The dither sat directly on the words. At a glance you could not tell
   whether the component was being worked on or the page had broken — which is
   the one confusion this tool exists to prevent.

   A frosted backdrop under the same dither reads as an overlay instead: the
   component goes soft, the grain sits on the softness rather than on the
   letterforms, and it is obvious that something is ON this rather than wrong
   WITH it.

   THE EDGE BECOMES CRISP, reversing an earlier decision deliberately. The
   field used to fade into its own edges precisely so it would not draw a box
   round somebody's component. That is right for a bare dither and wrong for a
   blur: a frosted panel with a soft edge looks like a rendering fault, and
   the job here is to say exactly which element is taken. So it keeps a
   hairline in the ground's own ink with the companion outside it, like every
   other line the tool draws. */
.gl-busy-box.is-live {
  border: 0;
  border-radius: 4px;
  animation: none;
  overflow: hidden;
  backdrop-filter: blur(11px) saturate(0.8);
  box-shadow:
    0 0 0 1px var(--box-ink),
    0 0 0 2px var(--box-halo);
}
.gl-busy-box.is-live::after {
  display: none;
}
.gl-blast {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
}

/* ── the marker, with the pins off ──────────────────────────────────────
   A chip that says what it means: `queued`, `your call`, or the agent's
   name. An ink plate with cyan spent on the dot and the text, because
   light-on-light is why the first versions vanished. */
.gl-pin.is-quiet {
  width: auto;
  height: auto;
  min-width: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.3rem 0.65rem 0.3rem 0.55rem;
  border-radius: 999px;
  background: var(--gl-ink);
  color: var(--gl-accent);
  font: 600 10.5px/1 var(--gl-sans);
  letter-spacing: 0.02em;
  white-space: nowrap;
  box-shadow:
    0 0 0 1px var(--gl-line-2),
    0 0 0 2px var(--gl-halo),
    0 4px 16px rgba(0, 0, 0, 0.45);
  position: relative;
}
.gl-pin-dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 999px;
  background: var(--gl-accent);
  flex: none;
}
/* only the dot moves, so the WORD stays readable */
.gl-pin.is-quiet.is-working .gl-pin-dot,
.gl-pin.is-quiet.is-think .gl-pin-dot {
  animation: gl-alive 1.9s ease-in-out infinite;
}

/* ── thinking ────────────────────────────────────────────────────────────
   Claimed, and the field is not running yet: the agent has the thread and is
   reading before it changes anything. That is a real moment, and it used to
   say `queued` — which describes the QUEUE rather than the agent, and reads
   as though nothing had started.

   A sheen crossing the chip, plus the dot the working state already pulses.
   On the chip rather than on the word, for the reason directly above — and
   slow, because this state can last a minute and anything quick here would
   be a tic rather than a signal. */
.gl-pin.is-quiet.is-think::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(
    100deg,
    transparent 35%,
    rgba(255, 255, 255, 0.16) 50%,
    transparent 65%
  );
  background-size: 260% 100%;
  animation: gl-think 2.4s linear infinite;
  pointer-events: none;
}
@keyframes gl-think {
  from {
    background-position: 160% 0;
  }
  to {
    background-position: -60% 0;
  }
}
/* the hit area: an invisible pad on a pseudo-element rather than a bigger
   button, because everything that makes this readable is sized off the
   button itself and would grow with it */
.gl-pin.is-quiet::before {
  content: '';
  position: absolute;
  inset: -0.8rem;
  border-radius: 999px;
}
.gl-pin.is-quiet:hover,
.gl-pin.is-quiet:focus-visible {
  background: var(--gl-ink-2);
  scale: 1.12;
  animation: none;
}
@keyframes gl-alive {
  50% {
    opacity: 0.25;
  }
}

/* ══ the FAB: the tool's front door ══════════════════════════════════════
   One button, fixed bottom right, dragged anywhere by the whole set. It sits
   ABOVE the panel's z-index, because the one control that must always be
   reachable is the one that turns picking off. */
.gl-fab-set {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 10001;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  touch-action: none;
  user-select: none;
}
.gl-fab-set.is-dragging {
  cursor: grabbing;
}
.gl-fab-set.is-dragging .gl-fab {
  scale: 1;
}
.gl-fab {
  position: relative;
  display: grid;
  place-items: center;
  width: 2.8rem;
  height: 2.8rem;
  border: 0;
  border-radius: 999px;
  background: var(--gl-ink);
  cursor: pointer;
  padding: 0;
  box-shadow:
    0 0 0 1px var(--gl-line-2),
    0 0 0 2px var(--gl-halo),
    0 12px 30px -10px rgba(0, 0, 0, 0.75);
  transition:
    background-color 200ms ease,
    box-shadow 200ms ease,
    scale 160ms var(--gl-ease);
}
.gl-fab:hover {
  scale: 1.06;
}
/* Armed reads as FILLED, not as a tint. The picker swallows clicks, and a
   control that is quietly eating every click on the site has to be
   unmissable. */
.gl-fab.is-armed {
  background: var(--gl-accent);
  box-shadow:
    0 0 0 1px var(--gl-accent),
    0 10px 28px -8px rgba(255, 255, 255, 0.25);
}

/* ── armed also PULSES ───────────────────────────────────────────────────
   A fill is a state you have to have noticed the before-and-after of. An
   armed picker is eating every click on the site, so it has to announce
   itself to someone who just looked over — and movement is the only thing on
   a static page that does that.

   Two rings a half-cycle apart, expanding and fading: the same breath the
   reticle uses, so the button and the pointer it produces read as one state
   rather than two signals that happen to coincide.

   Each ring carries a dark companion, like every other white line here — the
   FAB is dragged wherever you want it, including onto the hub's cream
   sections, and a white pulse there would be a pulse nobody can see.

   On the pseudo-elements rather than the element's own `box-shadow`, which is
   already carrying the rim and the drop shadow and would be clobbered by an
   animation on the same property. */
.gl-fab.is-armed::before,
.gl-fab.is-armed::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 999px;
  border: 1.5px solid var(--gl-accent);
  box-shadow: 0 0 0 1px var(--gl-halo);
  pointer-events: none;
  animation: gl-fab-pulse 1.9s ease-out infinite;
}
.gl-fab.is-armed::after {
  animation-delay: 0.95s;
}
@keyframes gl-fab-pulse {
  0% {
    opacity: 0.75;
    scale: 1;
  }
  70% {
    opacity: 0;
    scale: 1.85;
  }
  100% {
    opacity: 0;
    scale: 1.85;
  }
}
/* The LiGMA mark. It was a drawn ring with a bite out of its lower left —
   the pin shape the threads use — which said "this makes pins" and nothing
   about whose tool it is. The mark says both: the drop in it IS the pin
   shape.

   It swaps cut rather than being recoloured. At rest the button is ink and
   the mark is the light cut; armed the button fills white and it takes the
   dark one. A `filter: invert()` would have done it in one line and taken the
   orange drop with it. */
.gl-fab-mark {
  width: 1.4rem;
  height: 1.4rem;
  border: 0;
  border-radius: 0;
  background-image: var(--gl-mark-light);
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  transition: scale 160ms var(--gl-ease);
}
.gl-fab.is-armed .gl-fab-mark {
  background-image: var(--gl-mark);
  border-color: transparent;
  background-color: transparent;
  scale: 1.06;
}
.gl-fab-mini {
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  border: 1px solid var(--gl-line-2);
  border-radius: 999px;
  background: var(--gl-ink);
  color: var(--gl-dim);
  font: 600 11px/1 var(--gl-sans);
  cursor: pointer;
  padding: 0;
  transition:
    color 120ms ease,
    border-color 120ms ease,
    background-color 120ms ease;
}
.gl-fab-mini:hover {
  background: var(--gl-ink-2);
  color: var(--gl-text);
}
.gl-fab-mini.is-on {
  border-color: var(--gl-accent-line);
  color: var(--gl-accent);
}
/* the peek glyph: an eye drawn as a ring with a pupil, so it needs no font */
.gl-glyph-peek {
  position: relative;
  display: block;
  width: 0.85rem;
  height: 0.85rem;
  border: 1.5px solid currentColor;
  border-radius: 999px;
}
.gl-glyph-peek::after {
  content: '';
  position: absolute;
  inset: 3px;
  border-radius: 999px;
  background: currentColor;
}

/* ══ motion off ══════════════════════════════════════════════════════════
   The canvas handles its own case — it draws one frame and stops. The BLUR
   under the grain stays: it is not motion, and a still grain on a frosted
   panel is exactly as legible as a moving one. */
@media (prefers-reduced-motion: reduce) {
  .gl-spin,
  .gl-working,
  .gl-busy i,
  .gl-pin.is-quiet.is-think::after,
  .gl-pin-dot,
  .gl-busy-box,
  .gl-busy-box::after,
  .gl-cursor-ring {
    animation: none;
  }
  .gl-panel.gl-drawer,
  .gl-panel.gl-drawer.gl-panel-off,
  .gl-dock-tab,
  .gl-fab,
  .gl-pin {
    transition: none;
  }
  /* the armed ring stops travelling but stays drawn — it is a STATE, and the
     one state on this page that must not go quiet */
  .gl-fab.is-armed::before,
  .gl-fab.is-armed::after {
    animation: none;
    opacity: 0.55;
    scale: 1.3;
  }
}

/* ══ the drawing pad ══════════════════════════════════════════════════════
   A modal, which nothing else in this tool is — every other surface sits
   beside the page so you can keep looking at what you are talking about. The
   pad is the exception because drawing needs the room and a pen that leaves
   the surface mid-stroke is a pen that draws on the page underneath.

   THE SHELL IS AN EDITOR'S, NOT A FORM'S. A dark desk, a light sheet on it,
   and the controls as islands floating over the desk where every drawing app
   puts them: tools along the top, properties down the left, history at the
   bottom left, the way out at the bottom right. It was a stack of chip rows
   above a canvas, which is the layout of a settings dialog, and it read as
   one however the chips were styled.

   It portals to the body, so it is outside `.gl-panel` and `.gl-thread` and
   reaches none of their rules by inheritance. Text buttons take the base
   chip by `.gl-draw` being in the lists above; the icon buttons in the
   islands are restyled below under `.gl-draw .gl-isle`, which out-specifies
   the base without depending on file order.

   NO BACKDROP CLOSE AND NO ESCAPE, deliberately. Both are reflexes, and the
   thing behind them is minutes of drawing with nothing to restore it from. */
.gl-draw {
  position: fixed;
  z-index: 2147483000;
  inset: 0;
  display: grid;
  place-items: center;
  padding: 1.25rem;
  background: rgba(6, 6, 9, 0.66);
  backdrop-filter: blur(8px) saturate(0.9);
}
.gl-draw-app {
  display: grid;
  grid-template-rows: auto 1fr;
  width: min(100%, 92rem);
  height: min(100%, 62rem);
  overflow: hidden;
  border: 1px solid var(--gl-line-2);
  border-radius: var(--gl-radius);
  background: var(--gl-ink);
  box-shadow:
    0 0 0 1px var(--gl-halo),
    0 30px 90px rgba(0, 0, 0, 0.6);
  color: var(--gl-text);
}

/* ── title bar ─────────────────────────────────────────────────────────── */
.gl-draw-bar {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  border-bottom: 1px solid var(--gl-line);
  background: var(--gl-ink-2);
  padding: 0.45rem 0.55rem 0.45rem 0.8rem;
  font-size: 11.5px;
}
.gl-draw-name {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}
.gl-draw-name svg {
  width: 14px;
  height: 14px;
  color: var(--gl-dim);
}
.gl-draw-name b {
  letter-spacing: 0.02em;
}
.gl-draw-name i {
  overflow: hidden;
  color: var(--gl-dim);
  font-family: var(--gl-mono, ui-monospace, monospace);
  font-style: normal;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* the only way out that does not attach — at the far end, and worded */
.gl-draw .gl-draw-x {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin-left: auto;
}
.gl-draw .gl-draw-x svg {
  width: 12px;
  height: 12px;
}

/* ── the desk ──────────────────────────────────────────────────────────────
   A size container so the sheet can be fitted to it in `cq` units without a
   measurement: the desk is the `1fr` row of a fixed-height grid, so both its
   dimensions are definite and asking for them is not a cycle. */
.gl-draw-desk {
  display: grid;
  grid-template-columns: auto 1fr;
  min-height: 0;
  overflow: hidden;
}
/* The properties column. In flow, so the stage beside it is exactly the room
   that is left — an island over the desk sat on the sheet's left edge on
   every wide window, because a height-bound sheet is centred and its margins
   are whatever the height leaves over, which is not a number a panel can be
   told to respect. */
.gl-draw-side {
  display: grid;
  align-content: start;
  gap: 0.7rem;
  border-right: 1px solid var(--gl-line);
  background: var(--gl-ink-2);
  padding: 0.8rem 0.85rem;
}
.gl-draw-stage {
  container-type: size;
  position: relative;
  display: grid;
  place-items: center;
  min-height: 0;
  overflow: hidden;
  background:
    radial-gradient(circle at 50% 40%, rgba(255, 255, 255, 0.035), transparent 70%),
    var(--gl-ink);
}

/* ── the sheet ─────────────────────────────────────────────────────────────
   Fitted like `object-fit: contain`: as wide as the desk allows, or as wide
   as its height allows at 1200:780, whichever is smaller — with room kept
   above and below for the islands so the paper never slides under them.

   An inline-size container of its own, so the text caret can be sized in
   `cqw` of the SHEET and land at the size the letters will paint at. */
.gl-sheet {
  container-type: inline-size;
  position: relative;
  width: min(calc(100cqw - 2.5rem), calc((100cqh - 7.5rem) * 1.5385));
  aspect-ratio: 1200 / 780;
  border-radius: 3px;
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.5),
    0 18px 50px rgba(0, 0, 0, 0.55);
}
/* The paper. Its `background-color` and `color` are read back at paint time
   and become the ground and the default ink, so the pair is decided once here
   and cannot drift apart.

   LIGHT IN BOTH THEMES, unlike everything else in this tool. The export is
   the point: the PNG goes to an agent and gets looked at outside this page
   entirely, and dark paper would send a picture that is harder to read for
   the sake of matching a surround it will never be seen against again.

   `touch-action: none` or the first drag on a trackpad scrolls the page out
   from under the stroke. */
.gl-draw-pad {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: inherit;
  background: #fbfaf8;
  color: #16161a;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}
.gl-pad-text {
  position: absolute;
  z-index: 1;
  min-width: 6rem;
  border: 0;
  border-bottom: 1px dashed rgba(0, 0, 0, 0.45);
  background: transparent;
  padding: 0;
  color: #16161a;
  font-family: ui-sans-serif, system-ui, sans-serif;
  font-weight: 600;
  line-height: 1;
  outline: none;
}
.gl-pad-text::placeholder {
  color: rgba(0, 0, 0, 0.3);
  font-weight: 400;
}

/* ── islands ───────────────────────────────────────────────────────────────
   Every control group is one of these: a small raised plate over the desk.
   Same plate, four places, told apart by position — which is the thing that
   makes a set of controls read as an application rather than a form. */
.gl-isle {
  position: absolute;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 0.2rem;
  border: 1px solid var(--gl-line-2);
  border-radius: 10px;
  background: var(--gl-ink-2);
  padding: 0.3rem;
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.35),
    0 10px 30px rgba(0, 0, 0, 0.45);
}
.gl-isle-tools {
  top: 0.75rem;
  left: 50%;
  transform: translateX(-50%);
}
.gl-isle-hist {
  bottom: 0.75rem;
  left: 0.75rem;
}
.gl-isle-go {
  right: 0.75rem;
  bottom: 0.75rem;
  gap: 0.7rem;
  padding: 0.3rem 0.3rem 0.3rem 0.75rem;
}
/* a hairline between groups inside one island */
.gl-isle-gap {
  width: 1px;
  height: 1.2rem;
  margin: 0 0.2rem;
  background: var(--gl-line-2);
}

/* Icon buttons. Square, unboxed, filled white when live — the lab's own
   language for the one live thing — with the shortcut printed small in the
   corner. Out-specifies the base chip by the extra class rather than by
   order, so it wins wherever it lands in the file. */
.gl-draw .gl-isle button,
.gl-draw .gl-draw-side button {
  position: relative;
  display: grid;
  place-items: center;
  width: 2.05rem;
  height: 2.05rem;
  border: 0;
  border-radius: 7px;
  background: transparent;
  padding: 0;
  color: var(--gl-text);
}
.gl-draw .gl-isle button svg,
.gl-draw .gl-draw-side button svg {
  width: 16px;
  height: 16px;
}
.gl-draw .gl-isle button:hover:not(:disabled),
.gl-draw .gl-draw-side button:hover:not(:disabled) {
  border: 0;
  background: var(--gl-ink-3);
  color: var(--gl-text);
}
.gl-draw .gl-isle button:disabled {
  opacity: 0.3;
}
.gl-draw .gl-isle .gl-on,
.gl-draw .gl-isle .gl-on:hover:not(:disabled),
.gl-draw .gl-draw-side .gl-on,
.gl-draw .gl-draw-side .gl-on:hover:not(:disabled) {
  border: 0;
  background: var(--gl-accent);
  color: var(--gl-accent-ink);
}
.gl-draw .gl-isle kbd {
  position: absolute;
  right: 3px;
  bottom: 1px;
  font-family: inherit;
  font-size: 8.5px;
  font-weight: 600;
  line-height: 1;
  opacity: 0.55;
}
.gl-draw .gl-isle .gl-on kbd {
  opacity: 0.7;
}

/* ── the shapes flyout ─────────────────────────────────────────────────────
   Opened by the stylesheet on hover and on focus-within, not by state — so
   it is there the instant the pointer arrives, stays while the pointer
   crosses the gap to it, and opens for the keyboard without a second code
   path. The `::before` is that gap, made hoverable: without it the menu
   closes as the pointer leaves the button, before it can reach the menu. */
.gl-fly {
  position: relative;
  display: inline-flex;
}
.gl-fly-menu {
  position: absolute;
  z-index: 3;
  top: calc(100% + 0.45rem);
  left: 50%;
  display: none;
  grid-template-columns: repeat(4, auto);
  gap: 0.2rem;
  transform: translateX(-50%);
  border: 1px solid var(--gl-line-2);
  border-radius: 10px;
  background: var(--gl-ink-2);
  padding: 0.3rem;
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.35),
    0 10px 30px rgba(0, 0, 0, 0.45);
}
.gl-fly-menu::before {
  content: '';
  position: absolute;
  top: -0.55rem;
  right: 0;
  left: 0;
  height: 0.55rem;
}
.gl-fly:hover .gl-fly-menu,
.gl-fly:focus-within .gl-fly-menu {
  display: grid;
}
/* the corner mark that says "there is more under this" */
.gl-draw .gl-fly-mark {
  position: absolute;
  bottom: 3px;
  left: 3px;
  border-style: solid;
  border-width: 0 0 4px 4px;
  border-color: transparent transparent currentColor transparent;
  opacity: 0.5;
}

/* ── properties rows ───────────────────────────────────────────────────── */
.gl-prop {
  display: grid;
  gap: 0.25rem;
}
.gl-prop-l {
  color: var(--gl-dim);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.gl-prop-r {
  display: flex;
  gap: 0.15rem;
}
/* the width buttons show the width */
.gl-prop-r svg path {
  stroke: currentColor;
}

/* A swatch is its colour, so it is filled rather than outlined — the one
   place in this tool where colour is the content and not the state. The
   default ink has no `--c` and takes the PAD's ink, not the panel's
   foreground: the swatch has to show what the pen will lay down, and the pen
   draws on paper, not on this near-black plate. Which is why it carries a
   ring. */
.gl-draw .gl-draw-side .gl-ink-dot {
  width: 1.35rem;
  height: 1.35rem;
  margin: 0.35rem 0.2rem;
  border-radius: 50%;
  border: 1px solid var(--gl-line-2);
  background: var(--c, #16161a);
}
.gl-draw .gl-draw-side .gl-ink-dot:hover:not(:disabled) {
  background: var(--c, #16161a);
  transform: scale(1.12);
}
.gl-draw .gl-draw-side .gl-ink-dot.gl-on,
.gl-draw .gl-draw-side .gl-ink-dot.gl-on:hover:not(:disabled) {
  background: var(--c, #16161a);
  box-shadow:
    0 0 0 2px var(--gl-ink-2),
    0 0 0 3.5px var(--gl-text);
}

/* ── the way out ───────────────────────────────────────────────────────── */
.gl-draw-tip {
  color: var(--gl-text);
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
}
.gl-draw-tip em {
  color: var(--gl-dim);
  font-style: normal;
  font-weight: 400;
}
/* the commit keeps the filled treatment and its chip shape; only the island
   rule above must not turn it into a square */
.gl-draw .gl-isle .gl-var-use,
.gl-draw .gl-isle .gl-var-use:hover:not(:disabled) {
  width: auto;
  height: auto;
  display: inline-block;
  border: 1px solid var(--gl-accent);
  border-radius: var(--gl-radius-s);
  background: var(--gl-accent);
  padding: 0.38rem 0.7rem;
  color: var(--gl-accent-ink);
}

/* narrow windows: the props island would cover the sheet, so it becomes a
   row under the tools, and the sheet is fitted to what is left */
@media (width < 52rem) {
  .gl-draw-desk {
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr;
  }
  .gl-draw-side {
    grid-auto-flow: column;
    justify-content: start;
    gap: 1rem;
    border-right: 0;
    border-bottom: 1px solid var(--gl-line);
    overflow-x: auto;
  }
}

/* The draw chip on the composer's action row. It keeps the base outlined chip
   from `.gl-thread button` and adds only the glyph — it is a peer of `send`,
   not a decoration on it, and the one control that looks different on this
   surface is the filled one that writes.

   The pencil stays even though the word is there: a row of two word-chips is
   two things to read, and the glyph is what your eye finds when you already
   know which one you want. */
.gl-thread .gl-draw-open {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
}
.gl-draw-open svg {
  width: 13px;
  height: 13px;
  flex: none;
}
