/* ============================================================
   The timeline canvas — the stage, its ruler and grid, and every event
   drawn on it: bars, moments, uncertainty, badges, link lines and notes.

   Shared by the editor (timeline.html, with app.css after it) and the
   live canvas on the home page (index.html), so neither page's layout
   rules reach the other: app.css sets the editor's html and body to a
   fixed, unscrolled height, which the home page must not inherit.
   ============================================================ */

.stage {
  flex: 1;
  position: relative;
  min-width: 0;
  overflow: hidden;
  background: var(--white);
  touch-action: none;
}

.ruler {
  position: absolute;
  inset: 0 0 auto 0;
  height: 54px;
  z-index: 20;
  background: var(--white);
  border-bottom: 1px solid var(--line);
  display: block;
  cursor: ew-resize;
}

.grid-canvas {
  position: absolute;
  inset: 54px 0 0 0;
  z-index: 1;
  display: block;
}

.events-layer {
  position: absolute;
  inset: 54px 0 0 0;
  z-index: 2;
  overflow: hidden;
}

.stage.panning { cursor: grabbing; }
.stage.can-pan { cursor: grab; }

/* ---------- event chips ---------- */

.ev {
  position: absolute;
  height: 28px;
  pointer-events: none;
  /* A drag that an anchor refuses leaves the pointer sweeping across
     the label, which would otherwise select its text. */
  user-select: none;
  -webkit-user-select: none;
  --ev: var(--accent);
  --ev-soft: var(--accent-soft);
  --ev-text: var(--ink);
  --grp: var(--accent);
}

/* Only the painted parts take pointer events, so a zero-width point
   event never blocks a background drag. */
.ev-bar, .ev-point, .ev-tail, .ev-handle { pointer-events: auto; }

.ev-bar, .ev-point, .ev-tail { cursor: grab; }
.ev.dragging .ev-bar, .ev.dragging .ev-point, .ev.dragging .ev-tail { cursor: grabbing; }
.ev.anchored .ev-bar, .ev.anchored .ev-point, .ev.anchored .ev-tail { cursor: default; }
.ev.readonly .ev-bar, .ev.readonly .ev-point, .ev.readonly .ev-tail { cursor: pointer; }

/* span bar. It clips, so the uncertain stretches inside it keep to its
   rounded corners. */
.ev-bar {
  position: absolute;
  left: 0; right: 0; top: 4px; bottom: 4px;
  background: var(--ev-soft);
  border: 1px solid var(--ev);
  border-radius: 5px;
  min-width: 3px;
  overflow: hidden;
}
.ev.selected .ev-bar { box-shadow: 0 0 0 2px var(--white), 0 0 0 4px var(--ev); }

/* Point marker. The element is deliberately larger than the dot it
   draws, so a single moment is still easy to grab with a mouse. */
.ev-point {
  position: absolute;
  left: 0; top: 50%;
  width: 22px; height: 22px;
  margin: -11px 0 0 -11px;
  border-radius: 50%;
}
.ev-point::after {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  width: 11px; height: 11px;
  margin: -5.5px 0 0 -5.5px;
  background: var(--ev);
  border-radius: 50%;
  border: 2px solid var(--white);
  box-shadow: 0 0 0 1px var(--ev);
}
.ev.selected .ev-point::after { box-shadow: 0 0 0 1px var(--ev), 0 0 0 4px var(--ev-soft); }

/* Uncertainty on a period. The best guess keeps its solid outline, and
   every uncertain stretch gets the same soft diagonal bands, whether the
   event could be longer (beyond the bar) or shorter (inside it). The bands
   are the bar's own fill a shade deeper, so they show on white.

   Each square tile holds two bands corner to corner, which is what lets
   tiles meet without a seam. The renderer sets background-position so every
   stretch shares one tile grid, and the bands run on unbroken from inside a
   bar to beyond it.

   A stretch beyond the bar tucks under its end and sits behind every bar
   (z-index -1 inside the events layer), so a neighbor's best guess always
   shows in front of it. A stretch inside the bar is clipped by the bar's
   corners and rounds its own. */
.ev-unc, .ev-unc-in {
  --band: color-mix(in srgb, var(--ev-soft), var(--ev) 15%);
  position: absolute;
  pointer-events: none;
  background-color: var(--white);
  background-image: linear-gradient(135deg,
    var(--band) 0 29.2%, transparent 29.2% 50%, var(--band) 50% 79.2%, transparent 79.2% 100%);
  background-size: 13px 13px;
}
.ev-unc { top: 4px; bottom: 4px; z-index: -1; }
.ev-unc.before { border-radius: 5px 0 0 5px; }
.ev-unc.after { border-radius: 0 5px 5px 0; }
.ev-unc-in { top: 0; bottom: 0; }
.ev-unc-in.start { left: 0; border-radius: 0 4px 4px 0; }
.ev-unc-in.end { right: 0; border-radius: 4px 0 0 4px; }

/* A moment's uncertainty is a whisker: a fine line either side of its dot,
   with a tick where it runs out. */
.ev-point-kind .ev-unc {
  top: 50%; bottom: auto;
  height: 0;
  background: none;
  border-top: 1px solid var(--ev);
  border-radius: 0;
  opacity: .6;
}
.ev-point-kind .ev-unc::after {
  content: "";
  position: absolute;
  top: -4px;
  width: 1px; height: 9px;
  background: var(--ev);
}
.ev-point-kind .ev-unc.before::after { left: 0; }
.ev-point-kind .ev-unc.after::after { right: 0; }

/* label and badges travel together in a shrink-wrapped row */
.ev-tail {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  gap: 4px;
  width: max-content;
  max-width: 340px;
  height: 20px;
  z-index: 2;
}
/* The label clips for its ellipsis, so its line box has to be tall enough
   for descenders: at line-height 1 the tails of g, p and y were cut off.
   --ev-text is the event's color, darkened where needed to stay readable
   (see colors.js), and it is the same inside a bar and beside one. */
.ev-label {
  font-size: 12px;
  line-height: 16px;
  font-weight: 550;
  color: var(--ev-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ev-label.untitled { color: var(--faint); font-style: italic; font-weight: 400; }

/* A name with no room inside or beside its bar hangs under it, in the 22px
   between this row's bar and the next row's, from a tick at the bar's start
   (a moment's dot) that shows whose name it is even where it runs on under
   later bars. render.js places it: HANG_LEFT, HANG_MID. */
.ev-tail.hang { top: 25px; transform: none; }
.ev-tail.hang::before {
  content: "";
  position: absolute;
  left: -5px; top: -1px;
  width: 1px; height: 11px;
  background: var(--ev);
  opacity: .6;
}
.ev-point-kind .ev-tail.hang::before { left: -6.5px; top: -4px; height: 14px; }

/* A dark corner marks a period whose name has no room anywhere near its bar.
   Pointing at the bar shows the name. --flag is set to suit the bar's width. */
.ev-flag {
  position: absolute;
  left: 0; top: 4px;
  width: 0; height: 0;
  border-top: var(--flag, 7px) solid var(--ev);
  border-right: var(--flag, 7px) solid transparent;
  border-top-left-radius: 5px;
  pointer-events: none;
}

/* resize handles */
.ev-handle {
  position: absolute;
  top: 0; bottom: 0;
  width: 10px;
  cursor: ew-resize;
  z-index: 3;
}
.ev-handle.l { left: -5px; }
.ev-handle.r { right: -5px; }

/* Every badge is a solid box with a white icon. A note or anchor badge wears
   its event's label color, inside a bar or beyond it: colors.js keeps that
   color readable on white, so white is readable on it.

   12px: about the height of a capital letter in the 12px label, a touch
   more, so a badge reads as part of the name rather than as a button stuck
   on the end of it. It was 16px until October 2026. Keep it even, which is
   what centers it exactly in the 18px inside a bar's border, 3px clear of it
   at top and bottom; an odd size sits on half pixels and touches the border
   on one side. No shadow either, which made a badge look lower than it is.

   It is smaller to see but not to hit: ::after reaches 2px past it all
   round, so the note and link badges are as easy to click as the 16px ones
   were, and with the tail's 4px gap two neighbors' targets just meet. */
.ev-badge {
  position: relative;
  width: 12px; height: 12px;
  border-radius: 3px;
  border: 1px solid var(--ev-text);
  background: var(--ev-text);
  color: #fff;
  display: grid;
  place-items: center;
  cursor: pointer;
  padding: 0;
  flex: none;
  transition: box-shadow .12s ease;
}
.ev-badge::after { content: ''; position: absolute; inset: -2px; }
.ev-badge svg { display: block; width: 9px; height: 9px; }
.ev-badge.anchor { cursor: default; }
/* The ring means the note is showing. */
.ev-badge.note.on { box-shadow: 0 0 0 2px var(--white), 0 0 0 3.5px var(--ev-text); }

/* A ring reaches 3.5px beyond its badge, so the first badge sits 2px further
   from the label than the tail's gap, and a ring never covers the text.
   render.js counts those 2px as BADGE_LEAD. Nothing moves when a ring
   appears: a badge that shifted or grew as it was pointed at or clicked
   jiggled its icon. */
.ev-label + .ev-badge { margin-left: 2px; }

/* Link badges wear their group's color instead, so which events move
   together can be read straight off the canvas. */
.ev-badge.link { background: var(--grp); border-color: var(--grp); }
.ev-badge.link:hover, .ev-badge.link:focus-visible, .ev-badge.link.open {
  box-shadow: 0 0 0 2px var(--white), 0 0 0 3.5px var(--grp);
  outline: none;
}

/* Every member of a group, while one of its badges is hovered or open. */
.ev.grp-lit .ev-bar { box-shadow: 0 0 0 2px var(--white), 0 0 0 3.5px var(--grp); }
.ev.grp-lit .ev-point::after { box-shadow: 0 0 0 1px var(--ev), 0 0 0 3px var(--white), 0 0 0 4.5px var(--grp); }
.ev.grp-lit .ev-badge.link { box-shadow: 0 0 0 2px var(--white), 0 0 0 3.5px var(--grp); }

/* The lines joining a hovered event to whatever it is linked to. */
.link-lines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  z-index: 1;
}
/* An event's own links are a fine solid line; the rest of its group is
   finely dotted. Dashes were too coarse to read between events close
   together. Each dot is a round cap on a dash of no length, so its
   diameter is the stroke width. */
.link-lines .link-line { fill: none; stroke-linecap: round; }
.link-lines .link-line.direct { stroke-width: 1; }
.link-lines .link-line.further { stroke-width: 1.3; stroke-dasharray: 0 2.6; opacity: .8; }
.link-lines .link-end { stroke: var(--white); stroke-width: 1.5; }

/* layered events from someone else's public timeline */
.ev.overlay { opacity: .82; }
.ev.overlay .ev-bar { border-style: dashed; background: transparent; }
.ev.overlay .ev-unc, .ev.overlay .ev-unc-in { background-color: transparent; }
.ev.overlay .ev-label { color: var(--muted); font-weight: 500; }
.ev.overlay .ev-bar, .ev.overlay .ev-point, .ev.overlay .ev-tail { cursor: default; }

/* ---------- note popovers ---------- */

.note-pop {
  position: absolute;
  z-index: 12;
  width: 250px;
  background: var(--white);
  border: 1px solid var(--line);
  border-left: 3px solid var(--gold);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
  padding: .6rem .75rem;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-2);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  max-height: 180px;
  overflow-y: auto;
}
.note-pop .np-title {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: .3rem;
}
