{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "week-agenda",
  "type": "registry:component",
  "title": "Week Agenda",
  "description": "Your week on one strip and your day on a timeline, where you can drag a meeting to a new time and see straight away if it clashes with another.",
  "categories": [
    "component",
    "Workspace"
  ],
  "dependencies": [
    "lucide-react",
    "motion"
  ],
  "registryDependencies": [
    "utils",
    "https://coss.com/ui/r/button.json"
  ],
  "files": [
    {
      "path": "registry/devl/week-agenda.tsx",
      "type": "registry:component",
      "content": "\"use client\";\n\nimport { CalendarOffIcon, ChevronLeftIcon, ChevronRightIcon, MapPinIcon, TriangleAlertIcon } from \"lucide-react\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport {\n  type KeyboardEvent,\n  type PointerEvent,\n  useCallback,\n  useEffect,\n  useId,\n  useMemo,\n  useRef,\n  useState,\n} from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\n\nexport type CalendarTone = \"info\" | \"success\" | \"warning\" | \"destructive\" | \"neutral\";\n\nexport interface AgendaCalendar {\n  id: string;\n  name: string;\n  tone: CalendarTone;\n}\n\nexport interface AgendaEvent {\n  id: string;\n  title: string;\n  calendarId: string;\n  start: Date;\n  end: Date;\n  location?: string;\n}\n\nexport interface WeekAgendaProps {\n  calendars: AgendaCalendar[];\n  events: AgendaEvent[];\n  now?: Date;\n  startHour?: number;\n  endHour?: number;\n  snap?: number;\n  onEventChange?: (event: AgendaEvent) => void;\n  className?: string;\n}\n\nconst tones: Record<CalendarTone, { dot: string; soft: string }> = {\n  info: { dot: \"bg-info\", soft: \"bg-info/10 group-hover/event:bg-info/16 dark:bg-info/16 dark:group-hover/event:bg-info/24\" },\n  success: {\n    dot: \"bg-success\",\n    soft: \"bg-success/10 group-hover/event:bg-success/16 dark:bg-success/16 dark:group-hover/event:bg-success/24\",\n  },\n  warning: {\n    dot: \"bg-warning\",\n    soft: \"bg-warning/12 group-hover/event:bg-warning/18 dark:bg-warning/16 dark:group-hover/event:bg-warning/24\",\n  },\n  destructive: {\n    dot: \"bg-destructive\",\n    soft: \"bg-destructive/10 group-hover/event:bg-destructive/16 dark:bg-destructive/16 dark:group-hover/event:bg-destructive/24\",\n  },\n  neutral: {\n    dot: \"bg-muted-foreground\",\n    soft: \"bg-muted-foreground/10 group-hover/event:bg-muted-foreground/16 dark:bg-muted-foreground/16 dark:group-hover/event:bg-muted-foreground/24\",\n  },\n};\n\nconst WEEKDAYS = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\nconst WEEKDAYS_LONG = [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"];\nconst MONTHS = [\n  \"January\",\n  \"February\",\n  \"March\",\n  \"April\",\n  \"May\",\n  \"June\",\n  \"July\",\n  \"August\",\n  \"September\",\n  \"October\",\n  \"November\",\n  \"December\",\n];\nconst PX_PER_MIN = 0.9;\nconst iconHit = \"after:absolute after:-inset-1 transition-[scale,background-color] duration-150 ease-out active:scale-[0.94]\";\n\nconst dayKey = (d: Date) => `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`;\nconst startOfDay = (d: Date) => new Date(d.getFullYear(), d.getMonth(), d.getDate());\nconst addDays = (d: Date, n: number) => new Date(d.getFullYear(), d.getMonth(), d.getDate() + n);\nconst minutesOf = (d: Date) => d.getHours() * 60 + d.getMinutes();\nconst atMinutes = (day: Date, m: number) => new Date(day.getFullYear(), day.getMonth(), day.getDate(), 0, m);\n\nfunction startOfWeek(d: Date) {\n  const day = startOfDay(d);\n  const offset = (day.getDay() + 6) % 7;\n  return addDays(day, -offset);\n}\n\nfunction formatTime(m: number, withPeriod = true) {\n  const h = Math.floor(m / 60) % 24;\n  const mm = m % 60;\n  const h12 = h % 12 === 0 ? 12 : h % 12;\n  const period = h < 12 ? \"AM\" : \"PM\";\n  return `${h12}${mm ? `:${String(mm).padStart(2, \"0\")}` : \"\"}${withPeriod ? ` ${period}` : \"\"}`;\n}\n\nfunction formatRange(a: number, b: number) {\n  const samePeriod = a < 720 === b < 720;\n  return `${formatTime(a, !samePeriod)} – ${formatTime(b)}`;\n}\n\nfunction formatDuration(m: number) {\n  const h = Math.floor(m / 60);\n  const mm = m % 60;\n  return h ? `${h}h${mm ? ` ${mm}m` : \"\"}` : `${mm}m`;\n}\n\ninterface Placed {\n  event: AgendaEvent;\n  col: number;\n  cols: number;\n  conflict: boolean;\n}\n\nfunction layoutDay(events: AgendaEvent[]): Placed[] {\n  const sorted = [...events].sort((a, b) => +a.start - +b.start || +b.end - +a.end);\n  const placed: Placed[] = [];\n  let cluster: Placed[] = [];\n  let clusterEnd = 0;\n  const flush = () => {\n    const cols = Math.max(1, ...cluster.map((p) => p.col + 1));\n    for (const p of cluster) p.cols = cols;\n    placed.push(...cluster);\n    cluster = [];\n  };\n  for (const event of sorted) {\n    if (cluster.length && +event.start >= clusterEnd) flush();\n    const taken = new Set(cluster.filter((p) => +p.event.end > +event.start).map((p) => p.col));\n    let col = 0;\n    while (taken.has(col)) col++;\n    const overlaps = cluster.filter((p) => +p.event.end > +event.start);\n    for (const p of overlaps) p.conflict = true;\n    cluster.push({ event, col, cols: 1, conflict: overlaps.length > 0 });\n    clusterEnd = Math.max(clusterEnd, +event.end);\n  }\n  flush();\n  return placed;\n}\n\nfunction findConflicts(events: AgendaEvent[]) {\n  const sorted = [...events].sort((a, b) => +a.start - +b.start);\n  const pairs: { a: AgendaEvent; b: AgendaEvent; minutes: number }[] = [];\n  for (let i = 0; i < sorted.length; i++) {\n    for (let j = i + 1; j < sorted.length; j++) {\n      const a = sorted[i];\n      const b = sorted[j];\n      if (+b.start >= +a.end) continue;\n      pairs.push({ a, b, minutes: Math.round((Math.min(+a.end, +b.end) - +b.start) / 60000) });\n    }\n  }\n  return pairs;\n}\n\nexport function WeekAgenda({\n  calendars,\n  events: initialEvents,\n  now: nowProp,\n  startHour = 8,\n  endHour = 19,\n  snap = 15,\n  onEventChange,\n  className,\n}: WeekAgendaProps) {\n  const reduce = useReducedMotion();\n  const uid = useId();\n  const [events, setEvents] = useState(initialEvents);\n  const [liveNow, setLiveNow] = useState<Date | null>(null);\n  const now = nowProp ?? liveNow;\n  const [selected, setSelected] = useState<Date | null>(() => (nowProp ? startOfDay(nowProp) : null));\n  const [hidden, setHidden] = useState<Set<string>>(() => new Set());\n  const [dragging, setDragging] = useState<string | null>(null);\n  const [announce, setAnnounce] = useState(\"\");\n  const scrollRef = useRef<HTMLDivElement>(null);\n  const drag = useRef<{ id: string; y: number; start: number; length: number; moved: boolean } | null>(null);\n\n  useEffect(() => {\n    if (nowProp) return;\n    const tick = () => setLiveNow(new Date());\n    tick();\n    const id = setInterval(tick, 30_000);\n    return () => clearInterval(id);\n  }, [nowProp]);\n\n  useEffect(() => {\n    if (!selected && now) setSelected(startOfDay(now));\n  }, [now, selected]);\n\n  const day = selected ?? (now ? startOfDay(now) : startOfWeek(new Date(0)));\n  const week = useMemo(() => {\n    const first = startOfWeek(day);\n    return Array.from({ length: 7 }, (_, i) => addDays(first, i));\n  }, [day]);\n\n  const calendarById = useMemo(() => new Map(calendars.map((c) => [c.id, c])), [calendars]);\n  const visible = useMemo(() => events.filter((e) => !hidden.has(e.calendarId)), [events, hidden]);\n  const byDay = useMemo(() => {\n    const map = new Map<string, AgendaEvent[]>();\n    for (const e of visible) {\n      const k = dayKey(e.start);\n      map.set(k, [...(map.get(k) ?? []), e]);\n    }\n    return map;\n  }, [visible]);\n\n  const dayEvents = byDay.get(dayKey(day)) ?? [];\n  const placed = useMemo(() => layoutDay(dayEvents), [dayEvents]);\n  const conflicts = useMemo(() => findConflicts(dayEvents), [dayEvents]);\n  const booked = dayEvents.reduce((sum, e) => sum + (+e.end - +e.start) / 60000, 0);\n\n  const rangeStart = startHour * 60;\n  const rangeEnd = endHour * 60;\n  const height = (rangeEnd - rangeStart) * PX_PER_MIN;\n  const isToday = now ? dayKey(now) === dayKey(day) : false;\n  const nowMinutes = now ? minutesOf(now) : 0;\n  const showNow = isToday && nowMinutes >= rangeStart && nowMinutes <= rangeEnd;\n\n  const dayStamp = dayKey(day);\n  useEffect(() => {\n    const el = scrollRef.current;\n    if (!el) return;\n    const first = dayEvents.length ? Math.min(...dayEvents.map((e) => minutesOf(e.start))) : rangeStart + 60;\n    const anchor = (isToday ? Math.min(nowMinutes - 45, first) : first) - 20;\n    el.scrollTo({ top: Math.max(0, (anchor - rangeStart) * PX_PER_MIN), behavior: reduce ? \"auto\" : \"smooth\" });\n  }, [dayStamp]);\n\n  const reschedule = useCallback(\n    (id: string, startMin: number, commit: boolean) => {\n      setEvents((prev) =>\n        prev.map((e) => {\n          if (e.id !== id) return e;\n          const length = (+e.end - +e.start) / 60000;\n          const clamped = Math.max(rangeStart, Math.min(rangeEnd - length, startMin));\n          const next = { ...e, start: atMinutes(e.start, clamped), end: atMinutes(e.start, clamped + length) };\n          if (commit) {\n            onEventChange?.(next);\n            setAnnounce(`${e.title} moved to ${formatRange(clamped, clamped + length)}`);\n          }\n          return next;\n        }),\n      );\n    },\n    [onEventChange, rangeEnd, rangeStart],\n  );\n\n  const onPointerDown = (e: PointerEvent<HTMLDivElement>, ev: AgendaEvent) => {\n    if (e.button !== 0) return;\n    e.currentTarget.setPointerCapture(e.pointerId);\n    drag.current = {\n      id: ev.id,\n      y: e.clientY,\n      start: minutesOf(ev.start),\n      length: (+ev.end - +ev.start) / 60000,\n      moved: false,\n    };\n  };\n\n  const onPointerMove = (e: PointerEvent<HTMLDivElement>) => {\n    const d = drag.current;\n    if (!d) return;\n    const dy = e.clientY - d.y;\n    if (!d.moved && Math.abs(dy) < 4) return;\n    if (!d.moved) {\n      d.moved = true;\n      setDragging(d.id);\n    }\n    const raw = d.start + dy / PX_PER_MIN;\n    reschedule(d.id, Math.round(raw / snap) * snap, false);\n  };\n\n  const onPointerUp = (e: PointerEvent<HTMLDivElement>) => {\n    const d = drag.current;\n    drag.current = null;\n    if (e.currentTarget.hasPointerCapture(e.pointerId)) e.currentTarget.releasePointerCapture(e.pointerId);\n    if (!d?.moved) return;\n    setDragging(null);\n    const ev = events.find((x) => x.id === d.id);\n    if (ev) reschedule(d.id, minutesOf(ev.start), true);\n  };\n\n  const onEventKey = (e: KeyboardEvent<HTMLDivElement>, ev: AgendaEvent) => {\n    if (e.key !== \"ArrowUp\" && e.key !== \"ArrowDown\") return;\n    e.preventDefault();\n    reschedule(ev.id, minutesOf(ev.start) + (e.key === \"ArrowUp\" ? -snap : snap), true);\n  };\n\n  const toggleCalendar = (id: string) =>\n    setHidden((prev) => {\n      const next = new Set(prev);\n      if (next.has(id)) next.delete(id);\n      else next.add(id);\n      return next;\n    });\n\n  const spring = reduce ? { duration: 0 } : { type: \"spring\" as const, stiffness: 420, damping: 36 };\n  const hours = Array.from({ length: endHour - startHour + 1 }, (_, i) => startHour + i);\n  const firstConflict = conflicts[0];\n\n  return (\n    <div\n      className={cn(\n        \"flex w-full max-w-[420px] flex-col overflow-hidden rounded-2xl border bg-card text-card-foreground shadow-xs/5\",\n        className,\n      )}\n    >\n      <div className=\"flex items-center gap-2 px-4 pt-3.5 pb-2\">\n        <div className=\"min-w-0 flex-1\">\n          <p className=\"font-medium text-sm\">\n            {MONTHS[week[3].getMonth()]} <span className=\"text-muted-foreground\">{week[3].getFullYear()}</span>\n          </p>\n        </div>\n        <Button\n          className=\"transition-[scale,box-shadow,background-color] duration-150 ease-out active:scale-[0.97]\"\n          disabled={!now || isToday}\n          onClick={() => now && setSelected(startOfDay(now))}\n          size=\"xs\"\n          variant=\"outline\"\n        >\n          Today\n        </Button>\n        <div className=\"flex items-center\">\n          <Button aria-label=\"Previous week\" className={iconHit} onClick={() => setSelected(addDays(day, -7))} size=\"icon-xs\" variant=\"ghost\">\n            <ChevronLeftIcon />\n          </Button>\n          <Button aria-label=\"Next week\" className={iconHit} onClick={() => setSelected(addDays(day, 7))} size=\"icon-xs\" variant=\"ghost\">\n            <ChevronRightIcon />\n          </Button>\n        </div>\n      </div>\n\n      <div aria-label=\"Week\" className=\"grid grid-cols-7 gap-1 px-3 pb-3\" role=\"group\">\n        {week.map((d) => {\n          const active = dayKey(d) === dayKey(day);\n          const today = now ? dayKey(d) === dayKey(now) : false;\n          const dots = [...new Set((byDay.get(dayKey(d)) ?? []).map((e) => calendarById.get(e.calendarId)?.tone))]\n            .filter(Boolean)\n            .slice(0, 3) as CalendarTone[];\n          return (\n            <button\n              aria-label={`${WEEKDAYS_LONG[d.getDay()]} ${MONTHS[d.getMonth()]} ${d.getDate()}${today ? \", today\" : \"\"}`}\n              aria-current={today ? \"date\" : undefined}\n              aria-pressed={active}\n              className=\"relative flex cursor-pointer flex-col items-center gap-1 rounded-xl py-1.5 outline-none transition-[background-color,scale] duration-150 ease-out hover:bg-accent focus-visible:ring-2 focus-visible:ring-ring active:scale-[0.97]\"\n              key={dayKey(d)}\n              onClick={() => setSelected(d)}\n              type=\"button\"\n            >\n              {active && (\n                <motion.span\n                  aria-hidden\n                  className=\"absolute inset-0 rounded-xl bg-primary\"\n                  layoutId={`${uid}-day`}\n                  transition={spring}\n                />\n              )}\n              <span\n                className={cn(\n                  \"relative text-[11px] uppercase tracking-wide\",\n                  active ? \"text-primary-foreground/72\" : \"text-muted-foreground\",\n                )}\n              >\n                {WEEKDAYS[d.getDay()]}\n              </span>\n              <span\n                className={cn(\n                  \"relative font-medium text-sm tabular-nums\",\n                  active ? \"text-primary-foreground\" : today ? \"text-destructive-foreground\" : \"text-foreground\",\n                )}\n              >\n                {d.getDate()}\n              </span>\n              <span className=\"relative flex h-1 items-center gap-0.5\">\n                {dots.map((t) => (\n                  <span className={cn(\"size-1 rounded-full\", active ? \"bg-primary-foreground/80\" : tones[t].dot)} key={t} />\n                ))}\n              </span>\n            </button>\n          );\n        })}\n      </div>\n\n      <div className=\"flex flex-wrap items-center gap-1.5 border-t px-4 py-2.5\">\n        {calendars.map((c) => {\n          const off = hidden.has(c.id);\n          return (\n            <button\n              aria-pressed={!off}\n              className={cn(\n                \"relative inline-flex h-6 cursor-pointer items-center gap-1.5 rounded-full border px-2 text-xs outline-none transition-[color,background-color,border-color,scale] duration-150 ease-out after:absolute after:inset-x-0 after:-inset-y-1 focus-visible:ring-2 focus-visible:ring-ring active:scale-[0.97]\",\n                off ? \"border-dashed text-muted-foreground\" : \"bg-background text-foreground dark:bg-input/32\",\n              )}\n              key={c.id}\n              onClick={() => toggleCalendar(c.id)}\n              type=\"button\"\n            >\n              <span\n                className={cn(\"size-2 rounded-full transition-opacity\", tones[c.tone].dot, off && \"opacity-32\")}\n              />\n              {c.name}\n            </button>\n          );\n        })}\n      </div>\n\n      <div className=\"flex items-baseline justify-between gap-3 border-t px-4 pt-3 pb-2\">\n        <p className=\"font-medium text-sm\">\n          {isToday ? \"Today\" : WEEKDAYS_LONG[day.getDay()]}\n          <span className=\"font-normal text-muted-foreground\">\n            , {MONTHS[day.getMonth()].slice(0, 3)} {day.getDate()}\n          </span>\n        </p>\n        <p className=\"text-muted-foreground text-xs tabular-nums\">\n          {dayEvents.length} {dayEvents.length === 1 ? \"event\" : \"events\"}\n          {booked > 0 && ` · ${formatDuration(booked)} booked`}\n        </p>\n      </div>\n\n      <AnimatePresence initial={false}>\n        {firstConflict && (\n          <motion.div\n            animate={{ height: \"auto\", opacity: 1 }}\n            className=\"overflow-hidden\"\n            exit={{ height: 0, opacity: 0 }}\n            initial={{ height: 0, opacity: 0 }}\n            transition={reduce ? { duration: 0 } : { type: \"spring\", stiffness: 380, damping: 34 }}\n          >\n            <div\n              className=\"mx-3 mb-2 flex items-start gap-2 rounded-xl bg-warning/10 px-3 py-2 text-warning-foreground text-xs dark:bg-warning/16\"\n              role=\"status\"\n            >\n              <TriangleAlertIcon aria-hidden className=\"mt-px size-3.5 shrink-0\" />\n              <p className=\"min-w-0\">\n                <span className=\"font-medium\">{firstConflict.a.title}</span> overlaps{\" \"}\n                <span className=\"font-medium\">{firstConflict.b.title}</span> by {formatDuration(firstConflict.minutes)}\n                {conflicts.length > 1 && ` · ${conflicts.length - 1} more`}\n              </p>\n            </div>\n          </motion.div>\n        )}\n      </AnimatePresence>\n\n      <div\n        className=\"relative h-[300px] overflow-y-auto overscroll-contain border-t [mask-image:linear-gradient(to_bottom,black_calc(100%-28px),transparent)] [scrollbar-width:thin]\"\n        ref={scrollRef}\n      >\n        <div className=\"relative\" style={{ height: height + 16 }}>\n          {hours.map((h) => (\n            <div\n              aria-hidden\n              className={cn(\n                \"absolute inset-x-0 flex -translate-y-1/2 items-center transition-opacity\",\n                showNow && Math.abs(h * 60 - nowMinutes) < 14 && \"[&>span:first-child]:opacity-0\",\n              )}\n              key={h}\n              style={{ top: 8 + (h * 60 - rangeStart) * PX_PER_MIN }}\n            >\n              <span className=\"w-14 pe-2 text-right text-[11px] text-muted-foreground tabular-nums\">\n                {formatTime(h * 60)}\n              </span>\n              <span className=\"h-px flex-1 bg-border\" />\n            </div>\n          ))}\n\n          <div className=\"absolute inset-y-2 right-2 left-14\">\n            {dayEvents.length === 0 && (\n              <div className=\"absolute inset-x-0 top-12 flex flex-col items-center gap-1.5 text-center text-muted-foreground text-xs\">\n                <CalendarOffIcon aria-hidden className=\"size-4\" />\n                Nothing scheduled\n              </div>\n            )}\n            {placed.map(({ event: ev, col, cols, conflict }) => {\n              const cal = calendarById.get(ev.calendarId);\n              const tone = tones[cal?.tone ?? \"neutral\"];\n              const s = minutesOf(ev.start);\n              const length = (+ev.end - +ev.start) / 60000;\n              const top = (s - rangeStart) * PX_PER_MIN;\n              const h = Math.max(20, length * PX_PER_MIN - 2);\n              const compact = h < 40;\n              const isDragging = dragging === ev.id;\n              return (\n                <motion.div\n                  animate={{\n                    top,\n                    height: h,\n                    left: `calc(${(col / cols) * 100}% + ${col ? 2 : 0}px)`,\n                    width: `calc(${100 / cols}% - ${cols > 1 ? 2 : 0}px)`,\n                    scale: isDragging ? 1.02 : 1,\n                  }}\n                  aria-label={`${ev.title}, ${formatRange(s, s + length)}${cal ? `, ${cal.name}` : \"\"}${conflict ? \", conflicts with another event\" : \"\"}. Use arrow keys to move.`}\n                  className={cn(\n                    \"group/event absolute flex cursor-grab touch-none select-none overflow-hidden rounded-lg bg-card py-1 ps-2.5 pe-2 outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n                    isDragging && \"z-10 cursor-grabbing shadow-lg/10 ring-1 ring-border\",\n                    conflict && !isDragging && \"ring-1 ring-warning/60\",\n                    compact ? \"items-center\" : \"flex-col\",\n                  )}\n                  initial={false}\n                  key={ev.id}\n                  onKeyDown={(e) => onEventKey(e, ev)}\n                  onPointerCancel={onPointerUp}\n                  onPointerDown={(e) => onPointerDown(e, ev)}\n                  onPointerMove={onPointerMove}\n                  onPointerUp={onPointerUp}\n                  role=\"button\"\n                  tabIndex={0}\n                  transition={isDragging ? { top: { duration: 0 }, scale: spring } : spring}\n                >\n                  <span aria-hidden className={cn(\"absolute inset-0 transition-colors\", tone.soft)} />\n                  <span aria-hidden className={cn(\"absolute inset-y-1 left-1 w-0.75 rounded-full\", tone.dot)} />\n                  <span className=\"relative flex min-w-0 items-center gap-1\">\n                    {conflict && <TriangleAlertIcon aria-hidden className=\"size-3 shrink-0 text-warning-foreground\" />}\n                    <span className=\"truncate font-medium text-foreground text-xs\">{ev.title}</span>\n                    {compact && (\n                      <span className=\"shrink-0 text-foreground/70 text-xs tabular-nums\">{formatTime(s)}</span>\n                    )}\n                  </span>\n                  {!compact && (\n                    <span className=\"relative truncate text-foreground/70 text-xs tabular-nums\">\n                      {formatRange(s, s + length)}\n                    </span>\n                  )}\n                  {!compact && h > 58 && ev.location && (\n                    <span className=\"relative mt-auto flex min-w-0 items-center gap-1 text-muted-foreground text-xs\">\n                      <MapPinIcon aria-hidden className=\"size-3 shrink-0\" />\n                      <span className=\"truncate\">{ev.location}</span>\n                    </span>\n                  )}\n                </motion.div>\n              );\n            })}\n          </div>\n\n          {showNow && (\n            <div\n              aria-label={`Now, ${formatTime(nowMinutes)}`}\n              className=\"pointer-events-none absolute inset-x-0 z-20 flex -translate-y-1/2 items-center\"\n              role=\"img\"\n              style={{ top: 8 + (nowMinutes - rangeStart) * PX_PER_MIN }}\n            >\n              <span className=\"w-14 pe-1.5 text-right\">\n                <span className=\"rounded-full bg-red-600 px-1.5 py-px font-medium text-[10px] text-white tabular-nums\">\n                  {formatTime(nowMinutes, false)}\n                </span>\n              </span>\n              <span className=\"relative h-px flex-1 bg-destructive\">\n                <span className=\"absolute -top-[3px] -left-[3px] size-[7px] rounded-full bg-destructive\" />\n              </span>\n            </div>\n          )}\n        </div>\n      </div>\n      <p aria-live=\"polite\" className=\"sr-only\">\n        {announce}\n      </p>\n    </div>\n  );\n}\n"
    }
  ]
}