/* ═══════════════════════════════════════════════════════════════
   TLOM — Phase 5 (refocused): Timeline Insight Layer + restrained depth
   css/phase5.css

   Direction: interactive temporal intelligence, not decorative atmosphere.
   The previous cinematic atmosphere pass was removed. What remains here is:
     §1  Timeline Insight chip — calm, minimal relational-time readout
     §2  Restrained horizon edge — ~5-10% deeper, no spectacle
     §3  Mode hint styling
     §4  Mobile protection
     §5  Reduced-motion compliance
════════════════════════════════════════════════════════════════ */

/* ───────────────────────────────────────────────────────────────
   §1  TIMELINE INSIGHT CHIP
   A single calm line beneath the timeline that reads relational time
   on click / focus / marker-drag. Primary insight is relative distance
   ("7 years from now"); age and year are secondary context.
   No card chrome, no popup, no panel — an ambient readout.
─────────────────────────────────────────────────────────────── */
.tl-insight-chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-height: 32px;
  padding: 6px 14px 2px;
  text-align: center;
  user-select: none;
  -webkit-user-select: none;
  transition: opacity 0.25s ease;
}

.tl-insight-primary {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.005em;
  color: rgba(255, 255, 255, 0.95);
  line-height: 1.35;
}

.tl-insight-context {
  font-size: 10.5px;
  font-weight: 400;
  letter-spacing: 0.015em;
  color: rgba(255, 255, 255, 0.58);
  line-height: 1.35;
}

body.light-mode .tl-insight-primary {
  color: rgba(0, 0, 0, 0.78);
}
body.light-mode .tl-insight-context {
  color: rgba(0, 0, 0, 0.46);
}
/* Consequence line — what a focused event sets in motion. Faint cyan tint
   marks it as a different kind of information from the temporal context. */
.tl-insight-consequence {
  color: rgba(0, 207, 255, 0.72);
  font-weight: 500;
}
body.light-mode .tl-insight-consequence {
  color: rgba(0, 130, 165, 0.85);
}
/* "Add event in <year>" — placement affordance tied to the marker. Appears
   only when the inspected year is empty and no event is focused. */
.tl-add-here {
  display: inline-flex; align-items: center; gap: 5px;
  margin: 8px auto 2px; padding: 6px 13px;
  background: rgba(0, 207, 255, 0.08);
  border: 1px solid rgba(0, 207, 255, 0.24);
  border-radius: 999px;
  color: rgba(0, 207, 255, 0.92);
  font-size: 11px; font-weight: 700; letter-spacing: 0.01em;
  cursor: pointer;
  transition: background 0.18s ease, border-color 0.18s ease, transform 0.12s ease;
}
.tl-add-here:hover { background: rgba(0, 207, 255, 0.14); border-color: rgba(0, 207, 255, 0.4); }
.tl-add-here:active { transform: scale(0.97); }
body.light-mode .tl-add-here {
  background: rgba(0, 130, 165, 0.08);
  border-color: rgba(0, 130, 165, 0.3);
  color: rgba(0, 110, 140, 0.95);
}

/* When the chip is empty, collapse it so it never leaves a gap. */
.tl-insight-chip:empty {
  display: none;
}

/* Orientation readout — time behind / time ahead. Fainter than the chip;
   a quiet secondary line that reinforces "where am I" when zoomed. */
.tl-orientation {
  text-align: center;
  padding: 0 14px 4px;
  font-size: 9.5px;
  font-weight: 400;
  letter-spacing: 0.03em;
  color: rgba(255, 255, 255, 0.48);
  line-height: 1.3;
  user-select: none;
  -webkit-user-select: none;
}
body.light-mode .tl-orientation {
  color: rgba(0, 0, 0, 0.34);
}
.tl-orientation:empty {
  display: none;
}


/* ───────────────────────────────────────────────────────────────
   §2  RESTRAINED HORIZON EDGE
   A single, soft right-edge fade that implies time continuing beyond
   the viewport. ~5-10% deeper feel, no left/right asymmetry drama,
   no ambient gradients, no breathing. pointer-events:none preserves
   all interaction.
─────────────────────────────────────────────────────────────── */
#tl-drag-container {
  position: relative;
}

#tl-drag-container::after {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  width: 44px;
  height: 100%;
  background: linear-gradient(
    270deg,
    rgba(5, 12, 22, 0.32) 0%,
    rgba(5, 12, 22, 0.08) 60%,
    transparent 100%
  );
  pointer-events: none;
  z-index: 2;
  border-radius: 0 16px 16px 0;
}

body.light-mode #tl-drag-container::after {
  background: linear-gradient(
    270deg,
    rgba(245, 243, 238, 0.30) 0%,
    rgba(245, 243, 238, 0.07) 60%,
    transparent 100%
  );
}


/* ───────────────────────────────────────────────────────────────
   §4  MOBILE PROTECTION
   Keep the insight chip (it's lightweight text and genuinely useful),
   but drop the horizon fade so nothing competes with readability.
─────────────────────────────────────────────────────────────── */
@media (max-width: 639px) {
  #tl-drag-container::after {
    display: none;
  }

  .tl-insight-chip {
    min-height: 30px;
    padding: 5px 12px 2px;
  }
  .tl-insight-primary { font-size: 12px; }
  .tl-insight-context { font-size: 10px; }
  .tl-orientation { font-size: 9px; padding: 0 12px 4px; }
}


/* ───────────────────────────────────────────────────────────────
   §5  REDUCED MOTION
   The chip transition is trivial, but disable it under reduced motion.
─────────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .tl-insight-chip {
    transition: none;
  }
}

/* ───────────────────────────────────────────────────────────────
   §6  TIMELINE FULLSCREEN
   Bulletproof model: when body.tl-fs-active, #main ITSELF becomes the fixed
   fullscreen layer with a solid background, and every child of its .view is
   hidden EXCEPT the timeline wrap. Everything stays in one stacking context
   (no cross-context z-index battles), and the solid #main background means
   nothing can bleed through. render() rebuilds inside #main, so the rules
   re-apply automatically every time.
─────────────────────────────────────────────────────────────── */

@keyframes tlFsFade { from { opacity: 0; } to { opacity: 1; } }

/* #main becomes the fullscreen surface. */
body.tl-fs-active #main {
  position: fixed !important;
  inset: 0 !important;
  max-width: none !important;
  margin: 0 !important;
  padding: 60px 4vw 5vh !important;
  z-index: 9990;
  background: radial-gradient(130% 100% at 50% -10%, #141c2a 0%, #0a0f18 55%, #05080e 100%) !important;
  overflow: hidden !important;
  animation: tlFsFade .2s ease;
}
/* The active timeline view fills the surface. */
body.tl-fs-active #main > .view {
  height: 100%;
  display: flex;
  flex-direction: column;
}
/* Hide every direct child of .view except the timeline wrap (controls+canvas).
   This removes the header, editor, and event list from the fullscreen surface
   so only the timeline shows. */
body.tl-fs-active #main > .view > * {
  display: none !important;
}
body.tl-fs-active #main > .view > #tl-fs-wrap {
  display: flex !important;
  flex: 1 1 auto;
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
  min-height: 0;
  overflow: hidden;
}
/* Cleaner fullscreen chrome: the date hint is config guidance, not the timeline,
   so it never shows in fullscreen. The view-controls card stays (tucked: just its
   header until tapped) but floats compactly at the top rather than as a heavy
   in-flow block, so the canvas claims the rest of the surface. */
body.tl-fs-active #tl-fs-wrap > div:first-child:not(#tl-drag-container) {
  display: none !important;
}
body.tl-fs-active #tl-fs-wrap > div:not(#tl-drag-container) {
  flex: 0 0 auto;
  max-width: 1100px;
  margin: 0 auto !important;
  width: 100%;
  opacity: .94;
}
/* Canvas fills the remaining height; short-aspect SVG is centred. */
body.tl-fs-active #tl-fs-wrap #tl-drag-container {
  flex: 1 1 auto;
  margin: 0 !important;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: stretch;
}
/* SVG keeps its aspect ratio (viewBox ~2.6:1), scaling to the available width;
   the flex container centres it vertically. Forcing height:100% would distort
   a fixed-aspect SVG, so we don't. */
body.tl-fs-active #tl-fs-wrap #tl-drag-container > svg {
  width: 100%;
  height: auto;
  max-height: 100%;
  display: block;
  margin: auto 0;
}
/* Drill panel (quarterly view) in fullscreen — fills the surface, scrolls if
   tall, and is never hidden by the generic child-hiding rules. */
body.tl-fs-active #tl-fs-wrap #tl-drill-panel {
  flex: 1 1 auto !important;
  display: block !important;
  width: 100%;
  max-width: 1100px;
  margin: 0 auto !important;
  min-height: 0;
  overflow-y: auto;
  opacity: 1 !important;
}

/* Floating close button — appended to <body>, above the fullscreen surface. */
.tl-fs-close {
  display: none;
  position: fixed;
  top: 18px;
  right: 24px;
  z-index: 9995;
  align-items: center;
  background: rgba(255,255,255,.07);
  border: 1px solid rgba(255,255,255,.15);
  color: rgba(255,255,255,.9);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 8px 15px;
  border-radius: 10px;
  cursor: pointer;
  transition: background .18s ease, border-color .18s ease;
}
.tl-fs-close:hover {
  background: rgba(255,255,255,.12);
  border-color: rgba(255,255,255,.24);
}

body.tl-fs-active { overflow: hidden; }

@media (max-width: 639px) {
  /* Pure-timeline mobile fullscreen: the phone has little vertical room, so the
     two collapsed header cards (settings + view controls) are hidden entirely
     and the canvas takes the whole surface. Desktop fullscreen keeps them. */
  body.tl-fs-active #main { padding: 44px 3vw 3vh !important; }
  body.tl-fs-active #main > .view > #tl-fs-wrap { gap: 0; }
  /* Hide every child of the wrap except the timeline canvas itself. */
  body.tl-fs-active #tl-fs-wrap > *:not(#tl-drag-container):not(#tl-drill-panel) { display: none !important; }
  /* Canvas fills the full reclaimed height. */
  body.tl-fs-active #tl-fs-wrap #tl-drag-container { flex: 1 1 auto; }
  body.tl-fs-active #tl-fs-wrap #tl-drill-panel { display: block !important; flex: 1 1 auto; }
  .tl-fs-close { top: 10px; right: 12px; padding: 7px 13px; font-size: 11px; }
}


/* ═══════════════════════════════════════════════════════
   §6b  MIND-MAP FULLSCREEN
   When body.map-fs-active, the existing #map-canvas-stage is promoted to a
   fixed, edge-to-edge immersive surface. The canvas keeps its inline handlers
   and node positions (no DOM is moved), and _mapSetupCanvas() recomputes the
   stage size to the viewport on the next setup pass. Darker, minimal chrome —
   this is thinking space, not data space.
   ═══════════════════════════════════════════════════════ */
@keyframes mapFsFade { from { opacity: 0; } to { opacity: 1; } }
body.map-fs-active { overflow: hidden; }
body.map-fs-active #map-canvas-stage {
  position: fixed !important;
  inset: 0 !important;
  z-index: 9990 !important;
  width: 100vw !important;
  height: 100vh !important;
  height: 100dvh !important;
  border-radius: 0 !important;
  margin: 0 !important;
  box-shadow: none !important;
  background: radial-gradient(125% 100% at 50% 30%, #0a0f1a 0%, #06090f 55%, #03050a 100%) !important;
  animation: mapFsFade .22s ease;
}
body.map-fs-active #map-canvas-stage > canvas#map-canvas {
  width: 100vw !important;
  height: 100vh !important;
  height: 100dvh !important;
}
/* Floating mind-map close + legend chrome, above the surface. */
.map-fs-close {
  display: none;
  position: fixed; top: 18px; right: 24px; z-index: 9995;
  align-items: center; gap: 6px;
  background: rgba(255,255,255,.07); border: 1px solid rgba(255,255,255,.15);
  color: rgba(255,255,255,.9); font-size: 12px; font-weight: 600; letter-spacing: .02em;
  padding: 8px 15px; border-radius: 10px; cursor: pointer;
  transition: background .18s ease, border-color .18s ease;
}
.map-fs-close:hover { background: rgba(255,255,255,.12); border-color: rgba(255,255,255,.24); }
body.map-fs-active .map-fs-close { display: inline-flex; }
.map-fs-legend {
  display: none;
  position: fixed; top: 18px; left: 24px; z-index: 9995;
  gap: 14px; align-items: center;
  background: rgba(8,12,18,.6); border: 1px solid rgba(255,255,255,.08);
  padding: 8px 14px; border-radius: 11px;
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
}
body.map-fs-active .map-fs-legend { display: inline-flex; }
.map-fs-legend .lg { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; color: rgba(255,255,255,.7); font-weight: 600; }
.map-fs-legend .dot { width: 9px; height: 9px; border-radius: 50%; }
.map-fs-hint {
  display: none;
  position: fixed; bottom: calc(20px + var(--safe-bottom, 0px)); left: 50%; transform: translateX(-50%);
  z-index: 9995; font-size: 11px; color: rgba(255,255,255,.42); letter-spacing: .02em;
  background: rgba(8,12,18,.5); padding: 6px 14px; border-radius: 999px;
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); pointer-events: none;
}
body.map-fs-active .map-fs-hint { display: block; }
@media (max-width: 639px) {
  .map-fs-close { top: 10px; right: 12px; padding: 7px 13px; font-size: 11px; }
  .map-fs-legend { top: 10px; left: 12px; padding: 6px 10px; gap: 9px; }
  .map-fs-legend .lg { font-size: 10px; }
}


/* ═══════════════════════════════════════════════════════
   §7  REUSABLE CHART FULLSCREEN
   Any chart wrapped in fsChartWrap() can go fullscreen. When S.chartFsId is set,
   the matching .chart-fs-on host is promoted to a fixed overlay; the render()
   body-class sync keeps this consistent across re-renders.
   ═══════════════════════════════════════════════════════ */

/* The expand affordance — corner button in normal flow */
.chart-fs-host { position: relative; }
.chart-fs-corner {
  position: absolute; top: 8px; right: 8px; z-index: 2;
  opacity: 0; transition: opacity .15s ease;
}
.chart-fs-host:hover .chart-fs-corner { opacity: 1; }
.chart-fs-btn {
  display: inline-flex; align-items: center; gap: 5px;
  background: rgba(10,15,24,.7); border: 1px solid rgba(255,255,255,.14);
  color: var(--dim, #9aa7b8); cursor: pointer;
  font-size: 10px; font-weight: 600; letter-spacing: .02em;
  padding: 5px 10px; border-radius: 8px;
  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
}
.chart-fs-btn:hover { color: #fff; border-color: rgba(255,255,255,.28); }

/* On touch devices the corner button is always visible (no hover) */
@media (hover: none) {
  .chart-fs-corner { opacity: .8; }
}

/* The fullscreen surface */
body.chart-fs-active { overflow: hidden; }
.chart-fs-host.chart-fs-on {
  position: fixed; inset: 0; z-index: 9980;
  display: flex; flex-direction: column;
  background: radial-gradient(ellipse at 50% 0%, #141c2a 0%, #0a0f18 55%, #05080e 100%);
  padding: 0; margin: 0; border-radius: 0;
}
.chart-fs-host.chart-fs-on .chart-fs-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 14px 20px;
  border-bottom: 1px solid rgba(255,255,255,.07);
  flex-shrink: 0;
}
.chart-fs-host.chart-fs-on .chart-fs-title {
  font-size: 13px; font-weight: 700; color: #fff; letter-spacing: .02em;
}
.chart-fs-host.chart-fs-on .chart-fs-body {
  flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column; justify-content: center;
  padding: 16px 24px; overflow: auto;
}
/* The chart SVG keeps its aspect ratio (≈2.75:1), so in fullscreen it scales to
   fill the full width and centres vertically. We cap height so a very wide
   viewport can't overscale it, and minimise padding so it uses as much of the
   surface as the aspect allows — closing most of the gap without distorting the
   carefully-placed labels and the NOW marker. */
.chart-fs-host.chart-fs-on .chart-fs-body svg {
  width: 100% !important;
  height: auto !important;
  max-height: 100% !important;
  display: block;
  margin: auto;
}

@media (max-width: 639px) {
  .chart-fs-host.chart-fs-on .chart-fs-bar { padding: 12px 14px; }
  .chart-fs-host.chart-fs-on .chart-fs-body { padding: 12px; }
  .chart-fs-corner { opacity: .8; }
}

/* ═══════════════════════════════════════════════════════
   §8  MAP OF TLOM (orientation layer)
   Shared by the compact in-panel map and the standalone view.
   ═══════════════════════════════════════════════════════ */
.map-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:12px; }
.map-grid.compact { gap:8px; }
@media (max-width:680px){ .map-grid{ grid-template-columns:1fr; } }

/* Standalone Map view fills the available width (consistent with full-width views) */
.map-view { width:100%; }
@media (min-width:1100px){ .map-view .map-grid:not(.compact){ gap:16px; } }

/* ── The weekly loop: the visual spine / journey ───────────────────────── */
.map-loop {
  margin-bottom:18px; border:1px solid rgba(255,255,255,.08); border-radius:16px;
  padding:16px 18px 18px;
  background:linear-gradient(160deg,rgba(0,207,255,.05),rgba(255,255,255,.012));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.05);
}
.map-loop-head { margin-bottom:14px; }
.map-loop-kicker {
  font-size:10px; font-weight:700; letter-spacing:.14em; text-transform:uppercase;
  color:var(--cyan); margin-right:10px;
}
.map-loop-sub { font-size:11px; color:var(--dim); }
.map-loop-track {
  display:flex; align-items:stretch; gap:0; flex-wrap:nowrap;
  position:relative; overflow-x:auto; padding-bottom:2px;
}
.loop-node {
  display:flex; align-items:center; gap:10px; flex:1 1 0; min-width:150px;
  text-align:left; cursor:pointer;
  background:rgba(255,255,255,.02);
  border:1px solid rgba(255,255,255,.08); border-top:2px solid var(--node);
  border-radius:12px; padding:11px 13px;
  transition:background .15s, border-color .15s, transform .12s;
}
.loop-node:hover { background:rgba(255,255,255,.06); transform:translateY(-1px); }
.loop-node-n {
  width:24px; height:24px; border-radius:50%; flex-shrink:0;
  background:var(--node-bg, rgba(0,207,255,.14));
  border:1px solid var(--node-bd, rgba(0,207,255,.4));
  color:var(--node); font-size:11px; font-weight:700;
  display:flex; align-items:center; justify-content:center;
}
.loop-node-body { display:flex; flex-direction:column; gap:2px; }
.loop-node-t { font-size:12px; font-weight:700; color:var(--white); line-height:1.2; }
.loop-node-s { font-size:10px; color:var(--dim); line-height:1.3; }
/* The flowing connector between steps */
.loop-flow {
  flex:0 0 26px; align-self:center; height:2px; margin:0 2px;
  background:linear-gradient(90deg, rgba(0,207,255,.5), rgba(255,255,255,.12));
  position:relative;
}
.loop-flow::after {
  content:''; position:absolute; right:-1px; top:50%; transform:translateY(-50%);
  border-left:5px solid rgba(0,207,255,.55);
  border-top:4px solid transparent; border-bottom:4px solid transparent;
}
/* The return arc — signals the loop closing back to the start */
.loop-return {
  flex:0 0 30px; align-self:stretch; margin-left:4px; position:relative;
  border:2px solid rgba(0,207,255,.28); border-left:none;
  border-radius:0 12px 12px 0;
}
.loop-return::before {
  content:'↩'; position:absolute; left:-7px; bottom:-9px;
  color:rgba(0,207,255,.55); font-size:13px; line-height:1;
}
@media (max-width:760px){
  .map-loop-track { flex-wrap:wrap; overflow-x:visible; gap:8px; }
  .loop-node { flex:1 1 100%; }
  .loop-flow, .loop-return { display:none; }
}

.map-pillar {
  border:1px solid rgba(255,255,255,.08); border-left:3px solid var(--accent);
  border-radius:12px; padding:12px 14px; background:rgba(255,255,255,.02);
}
.map-pillar-head { margin-bottom:10px; }
.map-pillar-name { font-size:13px; font-weight:700; color:var(--white); }
.map-pillar-purpose { font-size:11px; color:var(--accent); margin-top:2px; }
.map-pillar-mods { display:flex; flex-direction:column; gap:5px; }
.map-pillar-mods.compact { flex-flow:row wrap; gap:6px; }

.map-mod {
  display:flex; flex-direction:column; gap:2px; text-align:left;
  background:rgba(255,255,255,.03); border:1px solid rgba(255,255,255,.06);
  border-radius:8px; padding:7px 10px; cursor:pointer; color:var(--light);
  transition:background .15s,border-color .15s;
}
.map-pillar-mods.compact .map-mod { padding:5px 9px; }
.map-mod:hover { background:rgba(255,255,255,.07); border-color:rgba(255,255,255,.16); }
.map-mod.active { border-color:var(--accent); background:rgba(0,207,255,.06); }
.map-mod-label { font-size:12px; font-weight:600; line-height:1.2; }
.map-mod-desc { font-size:10px; color:var(--dim); line-height:1.3; }

.map-spine {
  margin-top:18px; border:1px solid rgba(255,255,255,.08); border-radius:14px;
  padding:14px 16px; background:rgba(255,255,255,.02);
}
.map-spine-label {
  font-size:10px; font-weight:700; letter-spacing:.12em; text-transform:uppercase;
  color:var(--dim); margin-bottom:12px;
}
.map-spine-row { display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.map-spine-step { display:flex; align-items:center; gap:8px; }
.map-spine-n {
  width:22px; height:22px; border-radius:50%; flex-shrink:0;
  background:rgba(0,207,255,.12); border:1px solid rgba(0,207,255,.3);
  color:var(--cyan); font-size:11px; font-weight:700;
  display:flex; align-items:center; justify-content:center;
}
.map-spine-t { font-size:12px; font-weight:600; color:var(--white); line-height:1.2; }
.map-spine-s { font-size:10px; color:var(--dim); line-height:1.3; }
.map-spine-arrow { color:var(--dim); font-size:13px; }
@media (max-width:680px){ .map-spine-arrow{ display:none; } .map-spine-step{ width:100%; } }

.map-q-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:10px; margin-top:10px; }
@media (max-width:680px){ .map-q-grid{ grid-template-columns:1fr; } }
.map-q { border:1px solid rgba(255,255,255,.07); border-radius:10px; padding:11px 13px; background:rgba(255,255,255,.02); }
.map-q-q { font-size:12px; font-weight:700; color:var(--white); margin-bottom:4px; }
.map-q-a { font-size:11px; color:var(--muted); line-height:1.5; }

/* ── Timeline event interactivity ─────────────────────────
   Events were already clickable; these add the tactile feedback that makes
   that obvious — a hover lift on the dot, and a one-shot ring that expands
   and fades when an event becomes focused (confirming the tap landed). */
.tl-event { transition: opacity .15s ease; }
/* When one event is focused, recede the others so the selection and its
   causal links read clearly — directly cuts visual clutter. */
svg:has(.tl-event-focused) .tl-event:not(.tl-event-focused) { opacity: .38; }
svg:has(.tl-event-focused) .tl-event:not(.tl-event-focused):hover { opacity: .7; }
/* Hover affordance — only on devices with a real pointer, so touch users
   don't get a stuck hover state. The dot and label lift slightly. */
@media (hover: hover) and (pointer: fine) {
  .tl-event:hover circle,
  .tl-event:hover rect { filter: brightness(1.35); }
  .tl-event:hover text { fill-opacity: 1; }
  .tl-event:hover { opacity: 1 !important; }
}
/* Focus ring — plays once when an event is selected. Expands from the dot
   and fades, like a tap ripple. Calm, single pulse, no looping. */
.tl-focus-ring {
  transform-box: fill-box;
  transform-origin: center;
  animation: tlFocusRing .55s cubic-bezier(.2,.7,.3,1) 1;
  pointer-events: none;
}
@keyframes tlFocusRing {
  0%   { opacity: 0;   stroke-width: 3;   transform: scale(.55); }
  35%  { opacity: .9;  stroke-width: 1.5; transform: scale(1.05); }
  100% { opacity: .55; stroke-width: 1.2; transform: scale(1); }
}
/* A gentle settle on the focused dot itself, so the selection reads as
   "this one" even after the ring finishes. */
.tl-event-focused circle,
.tl-event-focused rect { animation: tlFocusSettle .4s ease 1; }
@keyframes tlFocusSettle {
  0%   { transform: scale(.7); }
  60%  { transform: scale(1.18); }
  100% { transform: scale(1); }
}
.tl-event-focused circle,
.tl-event-focused rect { transform-box: fill-box; transform-origin: center; }

/* Respect reduced-motion preferences — no ripple, just the static ring. */
@media (prefers-reduced-motion: reduce) {
  .tl-focus-ring { animation: none; opacity: .55; }
  .tl-event-focused circle, .tl-event-focused rect { animation: none; }
}

/* On-chart event detail popover */
@keyframes tlPopIn {
  from { opacity: 0; transform: translateX(-50%) translateY(-4px) scale(.96); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
}
