{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "calendar",
  "title": "Calendar",
  "description": "A month drawn as time, after the \"28 December\" poster: a huge numeral, then a large disc for every day, ink for the days gone, the accent for today, rings for the days to come, and half-moons for the wait. The ruler draws the same month as one measured line; the ghost sets the days against the date huge and faint behind them; the parenthesis runs the month as a line of figures with the wait in parentheses.",
  "registryDependencies": [
    "https://thedirectors.agency/ui/r/0nlytype.json"
  ],
  "files": [
    {
      "path": "registry/0nlytype/ui/calendar.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { roll } from \"@/lib/0nlytype/roll\"\n\nconst DAY = 86_400_000\nconst startOfDay = (d: Date) => new Date(d.getFullYear(), d.getMonth(), d.getDate())\nconst startOfMonth = (d: Date) => new Date(d.getFullYear(), d.getMonth(), 1)\nconst addDays = (d: Date, n: number) => new Date(d.getFullYear(), d.getMonth(), d.getDate() + n)\nconst sameDay = (a?: Date | null, b?: Date | null) => !!a && !!b && +startOfDay(a) === +startOfDay(b)\nconst sameMonth = (a?: Date | null, b?: Date | null) => !!a && !!b && a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth()\nconst daysIn = (d: Date) => new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate()\n/** Whole days from a to b (a clock change can't make it 11.96). */\nconst between = (a: Date, b: Date) => Math.round((+startOfDay(b) - +startOfDay(a)) / DAY)\n/** Monday is 0. */\nconst weekday = (d: Date) => (d.getDay() + 6) % 7\nconst two = (n: number) => String(n).padStart(2, \"0\")\n\n/** Today's date after mount only, so the server and the first client render agree. */\nconst noop = () => () => {}\nconst useToday = () => {\n  const day = React.useSyncExternalStore(noop, () => startOfDay(new Date()).getTime(), () => null)\n  return day === null ? null : new Date(day)\n}\n\ntype CalendarProps = Omit<React.ComponentProps<\"div\">, \"defaultValue\" | \"onChange\"> & {\n  /** The chosen day. */\n  value?: Date\n  defaultValue?: Date\n  onValueChange?: (date: Date) => void\n  /** Any date in the month on show. */\n  month?: Date\n  defaultMonth?: Date\n  onMonthChange?: (month: Date) => void\n  /** Which day is today. Left out, it's the device's, known once the page has loaded. */\n  today?: Date\n  /** Days before today can't be chosen. */\n  disablePast?: boolean\n  locale?: string\n  /**\n   * How time is drawn. Dots, the poster: a large disc for every day, ink for the days gone, the accent\n   * for today, hairline rings for the days to come, and half-moons for the wait. Ruler: the month as one\n   * measured line, a tick a day, with the wait between today and your day drawn as a dimension.\n   * Ghost: the days as figures only, read through the chosen day and its month set huge and faint\n   * behind them. Parenthesis: the month set as one running line of figures, the time from today to your\n   * day enclosed in parentheses.\n   */\n  variant?: \"dots\" | \"ruler\" | \"ghost\" | \"parenthesis\"\n  /** The tracked line at the foot. Left out, it counts the days (in English); null leaves it out. */\n  caption?: React.ReactNode\n}\n\n/**\n * A month drawn as time: the days gone, today, the days to come, and the wait for the day you choose.\n * The big numeral in the head is the chosen day (else today); the day you choose is yours, in italic.\n * Arrow keys move by day and week, Page Up and Page Down by month, Home and End to the ends of the week.\n */\nfunction Calendar({\n  value: valueProp,\n  defaultValue,\n  onValueChange,\n  month: monthProp,\n  defaultMonth,\n  onMonthChange,\n  today: todayProp,\n  disablePast = false,\n  locale = \"en-GB\",\n  variant = \"dots\",\n  caption: captionProp,\n  className,\n  ...props\n}: CalendarProps) {\n  const id = React.useId()\n  const clock = useToday()\n  const today = todayProp ? startOfDay(todayProp) : clock\n  const ruler = variant === \"ruler\"\n  const ghost = variant === \"ghost\"\n\n  const [innerValue, setInnerValue] = React.useState(defaultValue)\n  const value = valueProp ?? innerValue\n  const [innerMonth, setInnerMonth] = React.useState(defaultMonth)\n  const shown = monthProp ?? innerMonth ?? value ?? today\n  const view = shown ? startOfMonth(shown) : null\n  const last = view ? daysIn(view) : 0\n\n  const [focus, setFocus] = React.useState<Date>()\n  const [turn, setTurn] = React.useState<0 | 1 | -1>(0)\n  const [acted, setActed] = React.useState(false)\n  const [scrub, setScrub] = React.useState(false)\n  const moved = React.useRef(false)\n  const grid = React.useRef<HTMLDivElement>(null)\n\n  const goTo = (to: Date, dir: 1 | -1) => {\n    if (monthProp === undefined) setInnerMonth(to)\n    onMonthChange?.(to)\n    setTurn(dir)\n  }\n  const step = (delta: number) => goTo(new Date(view!.getFullYear(), view!.getMonth() + delta, 1), delta > 0 ? 1 : -1)\n  const disabled = (date: Date) => disablePast && !!today && date < today\n  const pick = (date: Date) => {\n    if (disabled(date)) return\n    if (valueProp === undefined) setInnerValue(date)\n    onValueChange?.(date)\n    setFocus(date)\n    setTurn(0)\n    setActed(true)\n  }\n\n  // The one tab stop: where you were, else the chosen day, else today, else the 1st.\n  const stop = (view && [focus, value, today].find((d) => d && sameMonth(d, view))) || view\n\n  // After a key moves the stop (perhaps into another month), focus follows it.\n  React.useEffect(() => {\n    if (!moved.current) return\n    moved.current = false\n    grid.current?.querySelector<HTMLElement>('[tabindex=\"0\"]')?.focus()\n  })\n\n  // The head: the chosen day, else today, else the 1st, with its weekday. It rolls when it changes.\n  const headDay = view ? (value && sameMonth(value, view) ? value : today && sameMonth(today, view) ? today : view) : null\n  const yours = !!headDay && sameDay(headDay, value)\n  const short = new Intl.DateTimeFormat(locale, { weekday: \"short\" })\n  const target = headDay ? `${two(headDay.getDate())}|${short.format(headDay)}|${two(headDay.getMonth() + 1)}` : \"\"\n  const [seen, setSeen] = React.useState(target)\n  const [held, setHeld] = React.useState<string | null>(null)\n  if (target !== seen) {\n    setSeen(target)\n    if (seen && !ruler && !ghost) setHeld(seen) // keep the old numeral up while it rolls out; the ruler's glides, the ghost surfaces\n  }\n  const date = React.useRef<HTMLSpanElement>(null)\n  const name = React.useRef<HTMLSpanElement>(null)\n  React.useEffect(() => {\n    if (held === null) return\n    const dir = turn || (target > held ? 1 : -1)\n    if (!date.current || !name.current) return setHeld(null)\n    roll(date.current, () => setHeld(null), \"0.4em\", dir)\n    roll(name.current, () => {}, \"0.6em\", dir)\n  }, [held, target, turn])\n  const [numeral, dayName, monthNo] = (held ?? target).split(\"|\")\n\n  const narrow = new Intl.DateTimeFormat(locale, { weekday: \"narrow\" })\n  const long = new Intl.DateTimeFormat(locale, { weekday: \"long\" })\n  const dated = new Intl.DateTimeFormat(locale, { day: \"numeric\", month: \"long\", year: \"numeric\" })\n  const spoken = (day: Date) => `${long.format(day)} ${dated.format(day)}` // \"Wednesday 30 September 2026\"\n  const monthName = view?.toLocaleString(locale, { month: \"long\" }) ?? \"\"\n  const monday = new Date(2024, 0, 1)\n  const titleId = `${id}-title`\n\n  // Weeks as rows of seven, Monday first. Before the 1st there is only paper; after the last day, nothing.\n  const weeks: (Date | null)[][] = []\n  if (view) {\n    const cells = [...Array<null>(weekday(view)).fill(null), ...Array.from({ length: last }, (_, i) => addDays(view, i))]\n    for (let i = 0; i < cells.length; i += 7) weeks.push(cells.slice(i, i + 7))\n  }\n  // The wait: the days between today and the chosen day. Counted, so the moons wax in turn toward yours.\n  const waits = (day: Date) => !!value && !!today && day > today && day < startOfDay(value)\n  let wait = 0\n  // The ruler numbers the weeks' first days, but gives way to a neighbouring today or yours.\n  const near = (day: Date) => [today, value].some((d) => d && !sameDay(d, day) && Math.abs(between(d, day)) === 1)\n\n  // The foot says something true about the days on show.\n  const gap = value && today ? between(today, value) : null\n  const count = (n: number, one: string, many: string) => (n === 1 ? one : `${n} ${many}`)\n  const told =\n    !today || !view\n      ? null\n      : gap !== null\n        ? gap > 1 ? `${gap} days to go` : gap === 1 ? \"Tomorrow\" : gap === 0 ? \"Today is the day\" : gap === -1 ? \"Yesterday\" : `${-gap} days ago`\n        : sameMonth(today, view)\n          ? last === today.getDate() ? `The last day of ${monthName}` : `${count(last - today.getDate(), \"One day\", \"days\")} left in ${monthName}`\n          : view < today ? `All ${last} days gone` : `${last} days to come`\n\n  // The aside encloses the time from today to your day, whichever comes first, both ends included.\n  const [lo, hi] = variant === \"parenthesis\" && value && today ? (gap! >= 0 ? [today, startOfDay(value)] : [startOfDay(value), today]) : []\n  const paren = (day: Date) => [sameDay(day, lo) && \"open\", sameDay(day, hi) && \"close\"].filter(Boolean).join(\" \") || undefined\n\n  // The ruler measures the wait as a dimension, from today to your day. An end off this month stays open.\n  let span: { a: number; b: number; open: string } | null = null\n  if (ruler && view && value && today && gap) {\n    const [lo, hi] = gap > 0 ? [today, value] : [value, today]\n    const end = new Date(view.getFullYear(), view.getMonth(), last)\n    if (lo <= end && hi >= view) {\n      const a = sameMonth(lo, view) ? lo.getDate() - 0.5 : 0\n      const b = sameMonth(hi, view) ? hi.getDate() - 0.5 : last\n      span = { a, b, open: [a === 0 && \"start\", b === last && \"end\"].filter(Boolean).join(\" \") }\n    }\n  }\n\n  const onKeyDown = (e: React.KeyboardEvent) => {\n    if (!stop) return\n    // Right to left, the next day is on the left.\n    const rtl = getComputedStyle(e.currentTarget).direction === \"rtl\"\n    const to = {\n      ArrowLeft: addDays(stop, rtl ? 1 : -1),\n      ArrowRight: addDays(stop, rtl ? -1 : 1),\n      ArrowUp: addDays(stop, -7),\n      ArrowDown: addDays(stop, 7),\n      Home: addDays(stop, -weekday(stop)),\n      End: addDays(stop, 6 - weekday(stop)),\n      PageUp: new Date(stop.getFullYear(), stop.getMonth() - 1, Math.min(stop.getDate(), new Date(stop.getFullYear(), stop.getMonth(), 0).getDate())),\n      PageDown: new Date(stop.getFullYear(), stop.getMonth() + 1, Math.min(stop.getDate(), new Date(stop.getFullYear(), stop.getMonth() + 2, 0).getDate())),\n    }[e.key]\n    if (!to) return\n    e.preventDefault()\n    moved.current = true\n    setFocus(to)\n    if (!sameMonth(to, view)) goTo(startOfMonth(to), to > stop ? 1 : -1)\n  }\n\n  // The ruler's ticks are narrow, so the hand draws along it: pressing and moving sketches the day\n  // under the pointer, and letting go chooses it.\n  const dayAt = (e: React.PointerEvent<HTMLElement>) => {\n    const box = e.currentTarget.getBoundingClientRect()\n    let x = (e.clientX - box.left) / box.width\n    if (getComputedStyle(e.currentTarget).direction === \"rtl\") x = 1 - x\n    return new Date(view!.getFullYear(), view!.getMonth(), Math.min(last, Math.max(1, Math.ceil(x * last))))\n  }\n  const scrubbing = ruler && view\n    ? {\n        onPointerDown: (e: React.PointerEvent<HTMLDivElement>) => {\n          if (e.button !== 0) return\n          e.currentTarget.setPointerCapture(e.pointerId)\n          setScrub(true)\n          setFocus(dayAt(e))\n        },\n        onPointerMove: (e: React.PointerEvent<HTMLDivElement>) => scrub && setFocus(dayAt(e)),\n        onPointerUp: (e: React.PointerEvent<HTMLDivElement>) => {\n          if (!scrub) return\n          setScrub(false)\n          moved.current = true\n          pick(dayAt(e))\n        },\n        onPointerCancel: () => setScrub(false),\n      }\n    : {}\n\n  const p = headDay ? (headDay.getDate() - 0.5) / last : 0\n\n  return (\n    <div data-slot=\"calendar\" data-variant={variant} className={cn(\"ot-month\", className)} {...props}>\n      <div data-slot=\"calendar-head\" className=\"ot-month-head\">\n        <div\n          // The ghost is set afresh for each day, so it surfaces anew.\n          key={ghost ? target : undefined}\n          className=\"ot-month-at\"\n          aria-hidden=\"true\"\n          data-yours={yours ? \"\" : undefined}\n          data-acted={ghost && (acted || turn) ? \"\" : undefined}\n          data-side={p > 0.5 ? \"start\" : \"end\"}\n          style={{ \"--p\": p } as React.CSSProperties}\n        >\n          <span ref={date} className=\"ot-month-date\">{numeral}</span>\n          <span ref={name} className=\"ot-month-dayname\">{dayName}</span>\n          {ghost ? <span className=\"ot-month-mo\">{monthNo}</span> : null}\n        </div>\n        <p id={titleId} className=\"ot-month-title\" aria-live=\"polite\">\n          <span className=\"ot-month-name\">{monthName}</span>\n          <span className=\"ot-month-year\">{view ? ` ${view.getFullYear()}` : null}</span>\n        </p>\n        <div className=\"ot-month-nav\">\n          <button type=\"button\" aria-label=\"Previous month\" disabled={!view} onClick={() => step(-1)}>\n            <span aria-hidden=\"true\" />\n          </button>\n          <button type=\"button\" aria-label=\"Next month\" disabled={!view} onClick={() => step(1)}>\n            <span aria-hidden=\"true\" />\n          </button>\n        </div>\n      </div>\n      <div\n        ref={grid}\n        role=\"grid\"\n        aria-labelledby={titleId}\n        data-slot=\"calendar-grid\"\n        data-turn={turn ? (turn > 0 ? \"next\" : \"prev\") : undefined}\n        data-acted={acted ? \"\" : undefined}\n        data-scrub={scrub ? \"\" : undefined}\n        style={{ \"--days\": last } as React.CSSProperties}\n        className=\"ot-month-grid\"\n        onKeyDown={onKeyDown}\n        {...scrubbing}\n      >\n        <div role=\"row\" className=\"ot-month-week\">\n          {Array.from({ length: 7 }, (_, i) => {\n            const day = addDays(monday, i)\n            return (\n              <abbr key={i} role=\"columnheader\" title={long.format(day)}>\n                {narrow.format(day)}\n              </abbr>\n            )\n          })}\n        </div>\n        <React.Fragment key={view ? +view : \"unset\"}>\n          {weeks.map((week, w) => (\n            <div key={w} role=\"row\" className=\"ot-month-week\">\n              {week.map((day, i) => {\n                // --d is the cell's place in reading order: turning the month, the days sweep in by it.\n                const d = w * 7 + i\n                if (!day) return <span key={i} role=\"gridcell\" aria-hidden=\"true\" className=\"ot-month-gap\" />\n                const when = today ? (day < today ? \"past\" : sameDay(day, today) ? \"today\" : \"future\") : undefined\n                const inWait = waits(day)\n                const off = disabled(day)\n                return (\n                  <button\n                    key={i}\n                    type=\"button\"\n                    role=\"gridcell\"\n                    tabIndex={sameDay(day, stop) ? 0 : -1}\n                    aria-selected={sameDay(day, value)}\n                    aria-current={when === \"today\" ? \"date\" : undefined}\n                    aria-disabled={off || undefined}\n                    aria-label={`${spoken(day)}${when === \"today\" ? \", today\" : \"\"}`}\n                    data-slot=\"calendar-day\"\n                    data-when={when}\n                    data-between={inWait ? \"\" : undefined}\n                    data-paren={paren(day)}\n                    data-label={(i === 0 || day.getDate() === 1) && !near(day) ? \"\" : undefined}\n                    style={{ \"--d\": d, \"--k\": inWait ? wait++ : undefined } as React.CSSProperties}\n                    className=\"ot-month-day\"\n                    // A pointer on the ruler chooses by letting go (above); a key's click has no detail.\n                    onClick={(e) => (!ruler || e.detail === 0) && pick(day)}\n                    onFocus={() => setFocus(day)}\n                  >\n                    <span className=\"ot-month-disc\" aria-hidden=\"true\">\n                      <span className=\"ot-month-n\">{day.getDate()}</span>\n                    </span>\n                  </button>\n                )\n              })}\n            </div>\n          ))}\n        </React.Fragment>\n      </div>\n      {ruler ? (\n        <div className=\"ot-month-span\" aria-hidden=\"true\">\n          {span ? (\n            <span data-open={span.open || undefined} style={{ \"--a\": span.a / last, \"--b\": span.b / last } as React.CSSProperties}>\n              <span>{Math.abs(gap!)}</span>\n            </span>\n          ) : null}\n        </div>\n      ) : null}\n      {captionProp === null ? null : <p className=\"ot-month-foot\">{captionProp ?? told ?? \"\\u00a0\"}</p>}\n    </div>\n  )\n}\n\nexport { Calendar, type CalendarProps }\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/0nlytype/styles/calendar.css",
      "content": "@layer components {\n  /* ── ot-month ── a month drawn as time. Two faces: dots (the \"28 December\" poster: a huge\n     numeral over a grid of large discs) and ruler (the month as one measured line, after\n     Paul Rand's dimension lines and SPECTRA). Before the page knows the date (data-when unset)\n     every day is still to come. --big is the head numeral's size; a popover lowers it. */\n  @property --sweep { syntax: \"<angle>\"; inherits: false; initial-value: 0deg; }\n  .ot-month { --big: max(var(--ot-fff), min(24vw, var(--ot-ff) * 1.5)); --flip: 1; display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--ot-space-6); max-width: 32rem; }\n  .ot-month:is([dir=\"rtl\"], [dir=\"rtl\"] *) { --flip: -1; }\n\n  /* The head, as the poster sets it: the numeral alone on top; the month over the year at the\n     start, the weekday at the far side on the year's line; the turn, small, under them. */\n  .ot-month-head { display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: \"date date\" \"title day\" \"nav nav\"; align-items: last baseline; row-gap: var(--ot-space-6); }\n  .ot-month-at { display: contents; }\n  .ot-month-date { grid-area: date; justify-self: start; font-size: var(--big); font-weight: 700; line-height: 0.8; letter-spacing: var(--ot-fff-tr); color: var(--ot-ink); font-variant-numeric: tabular-nums; padding-inline-end: 0.04em; } /* the tight tracking lets the last figure's ink past its advance */\n  .ot-month-at[data-yours] .ot-month-date { font-family: var(--ot-expression); font-style: italic; font-weight: 400; font-size: calc(var(--big) * var(--ot-expression-scale)); letter-spacing: -0.02em; }\n  .ot-month-dayname { grid-area: day; font-size: var(--ot-mp); line-height: 1.15; color: var(--ot-ink); }\n  .ot-month-title { grid-area: title; display: grid; line-height: 1.15; font-size: var(--ot-mp); }\n  .ot-month-name { color: var(--ot-ink); text-transform: uppercase; letter-spacing: 0.02em; }\n  .ot-month-year { color: var(--ot-pencil); font-variant-numeric: tabular-nums; }\n\n  /* The turn is quiet: two hairline chevrons in pencil, each a corner mark turned to point. */\n  .ot-month-nav { grid-area: nav; display: flex; gap: var(--ot-space-4); margin-inline-start: -0.55em; }\n  .ot-month-nav > button { appearance: none; border: 0; background: none; padding: 0.55em; margin: 0; font-size: var(--ot-mp); color: var(--ot-pencil); cursor: pointer; transition: color var(--ot-allegro), translate var(--ot-allegro) var(--ot-exhale); }\n  .ot-month-nav > button > span { display: block; width: 0.42em; aspect-ratio: 1; border-top: var(--ot-hairline) solid currentColor; border-left: var(--ot-hairline) solid currentColor; rotate: -45deg; translate: 0.1em 0; }\n  .ot-month-nav > button:last-child > span { rotate: 135deg; translate: -0.1em 0; }\n  .ot-month-nav > button:is([dir=\"rtl\"], [dir=\"rtl\"] *) { scale: -1 1; } /* a chevron has a direction */\n  .ot-month-nav > button:hover { color: var(--ot-ink); }\n  .ot-month-nav > button:first-child:hover { translate: calc(-2px * var(--flip)) 0; }\n  .ot-month-nav > button:last-child:hover { translate: calc(2px * var(--flip)) 0; }\n  .ot-month-nav > button:disabled { color: var(--ot-rule-strong); cursor: default; translate: 0 0; }\n\n  .ot-month-grid { container-type: inline-size; display: grid; gap: var(--ot-space-2); --size: var(--ot-pp); }\n  @container (min-width: 26rem) { .ot-month-day { --size: var(--ot-p); } }\n  .ot-month-week { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--ot-space-1); }\n  .ot-month-week > abbr { text-align: center; text-decoration: none; font-size: var(--ot-pp); line-height: 2; color: var(--ot-pencil); }\n\n  /* A day is a disc, one per day of this month only: before the 1st there is paper.\n     Gone: solid ink. Today: the one accent disc. To come: a hairline ring. The number is\n     reversed out of whatever ink it sits on (paper on ink, paper on the accent), and set\n     in pencil inside a ring, so the days to come stay back as the poster's pale rings do. --f is how full of ink the disc is, from the bottom. */\n  .ot-month-day { --fill: var(--ot-ink); --f: 0%; --ring: var(--ot-rule-strong); --tone: var(--ot-pencil); position: relative; aspect-ratio: 1; min-width: 0; display: grid; place-items: center; appearance: none; border: 0; margin: 0; padding: 0; border-radius: 50%; background: none; cursor: pointer; color: var(--ot-ink); font: inherit; }\n  .ot-month-day[data-when=\"past\"] { --f: 100%; --ring: transparent; }\n  .ot-month-day[data-when=\"today\"] { --fill: var(--ot-accent); --f: 100%; --ring: transparent; }\n  .ot-month-day[aria-disabled=\"true\"] { cursor: default; }\n  .ot-month-disc { position: relative; width: 78%; aspect-ratio: 1; border-radius: 50%; background: linear-gradient(var(--fill), var(--fill)) no-repeat 50% 100% / 100% var(--f); box-shadow: inset 0 0 0 var(--ot-hairline) var(--ring); }\n  .ot-month[data-variant=\"dots\"] .ot-month-n {\n    position: absolute; inset: 0; display: grid; place-items: center; padding-top: 0.06em;\n    font-size: var(--size); font-weight: 600; line-height: 1; font-variant-numeric: tabular-nums;\n    color: transparent; background: linear-gradient(var(--ot-paper), var(--ot-paper)) no-repeat 50% 100% / 100% var(--f) var(--tone); -webkit-background-clip: text; background-clip: text;\n  }\n  .ot-month[data-variant=\"dots\"] .ot-month-day[data-when=\"today\"] .ot-month-n { font-weight: 800; }\n\n  /* The wait, after the eclipse poster: the days between today and yours are half-moons set\n     under one horizon, a hairline that runs on through the gaps from today to yours, with the\n     number in the sky above it. The moon is smaller than its day, as the poster's disc is\n     narrower than its horizon, so the wait stays lighter than the days gone. Choosing (or\n     turning the month), they wax in turn toward yours. */\n  .ot-month-day[data-between] { --f: 50%; --ring: transparent; --tone: var(--ot-ink); }\n  .ot-month[data-variant=\"dots\"] .ot-month-day[data-between] .ot-month-disc { width: 52%; }\n  .ot-month[data-variant=\"dots\"] .ot-month-day[data-between]::after { content: \"\"; position: absolute; inset-inline: calc(var(--ot-space-1) / -2); top: 50%; height: var(--ot-hairline); background: var(--ot-rule-strong); }\n  .ot-month[data-variant=\"dots\"] .ot-month-day[data-between] .ot-month-n { inset: -50% -40% 50%; place-items: end center; padding: 0 0 0.3em; line-height: 0.72; background-size: 100% 0%; }\n  .ot-month[data-variant=\"dots\"] .ot-month-grid:is([data-acted], [data-turn]) .ot-month-day[data-between] .ot-month-disc { animation: ot-wax var(--ot-andante) var(--ot-exhale) backwards; animation-delay: calc(var(--k, 0) * var(--ot-arpeggio)); }\n  @keyframes ot-wax { from { background-size: 100% 0%; } }\n\n  /* Pointing sketches a pencil ring round the disc, drawn from the top like a pencil going\n     round it; choosing inks the ring, circled like a date on a paper calendar, and it lands\n     with one small rebound. Yours is set in the italic. The accent stays with today. */\n  .ot-month[data-variant=\"dots\"] .ot-month-day::before { content: \"\"; position: absolute; inset: 1%; border-radius: 50%; box-shadow: inset 0 0 0 var(--ot-hairline) var(--ot-pencil); mask: conic-gradient(black var(--sweep), transparent 0); transition: --sweep var(--ot-moderato) var(--ot-breath), box-shadow var(--ot-allegro); }\n  .ot-month[data-variant=\"dots\"] .ot-month-day:not([aria-disabled=\"true\"]):is(:hover, [data-force~=\"hover\"] [tabindex=\"0\"])::before { --sweep: 360deg; }\n  .ot-month[data-variant=\"dots\"] .ot-month-day[aria-selected=\"true\"]::before { --sweep: 360deg; box-shadow: inset 0 0 0 var(--ot-stroke) var(--ot-ink); }\n  .ot-month[data-variant=\"dots\"] .ot-month-grid[data-acted] .ot-month-day[aria-selected=\"true\"]::before { animation: ot-land var(--ot-moderato) var(--ot-spiccato); }\n  @keyframes ot-land { from { scale: 0.84; } }\n  .ot-month[data-variant=\"dots\"] .ot-month-day[aria-selected=\"true\"] { --tone: var(--ot-ink); }\n  .ot-month[data-variant=\"dots\"] .ot-month-day[aria-selected=\"true\"] .ot-month-n { font-family: var(--ot-expression); font-style: italic; font-weight: 400; font-size: calc(var(--size) * 1.2 * var(--ot-expression-scale)); padding-top: 0; }\n\n  /* A pinned focus (docs) sits on the day that holds the tab stop, not on the whole month. */\n  .ot-month[data-force~=\"focus\"] { outline: 0; }\n  .ot-month[data-force~=\"focus\"] .ot-month-day[tabindex=\"0\"] { outline: var(--ot-stroke) solid var(--ot-accent); outline-offset: 4px; }\n  .ot-month[data-variant=\"parenthesis\"][data-force~=\"focus\"] .ot-month-day[tabindex=\"0\"] { outline-offset: 0; }\n\n  /* The tracked line at the foot, as the poster ends: something true about the days on show. */\n  .ot-month-foot { margin-top: var(--ot-space-2); font-size: var(--ot-pp); line-height: var(--ot-pp-lh); letter-spacing: 0.18em; text-transform: uppercase; color: var(--ot-pencil); text-align: center; }\n\n  /* Turning the month, the days sweep in, in reading order, from the side you turned to\n     (backwards when you go back). Right to left, that side is mirrored. */\n  .ot-month-grid[data-turn] .ot-month-day { animation: ot-turn var(--ot-moderato) var(--ot-exhale) backwards; animation-delay: calc(var(--d, 0) * var(--ot-arpeggio) / 4); }\n  .ot-month-grid[data-turn=\"prev\"] .ot-month-day { --from: -1; animation-delay: calc((41 - var(--d, 0)) * var(--ot-arpeggio) / 4); }\n  @keyframes ot-turn { from { opacity: 0; translate: calc(var(--from, 1) * var(--flip) * 12px) 0; } }\n\n  /* ── ruler: the month as one measured line. A tick a day on a baseline that is ink as far as\n     today; the weeks' first days stand taller and carry their number; today is the accent tick\n     with a dot at its head; yours is a full ink tick with a ring at its head. The big numeral\n     stands over its tick and glides along the line to the day you choose, and under the line\n     the wait is measured as a dimension from today to your day, open where it runs off the month. */\n  .ot-month[data-variant=\"ruler\"] { max-width: none; gap: var(--ot-space-5); }\n  .ot-month[data-variant=\"ruler\"] .ot-month-head { grid-template-areas: \"title nav\" \"at at\"; align-items: center; row-gap: var(--ot-space-6); }\n  .ot-month[data-variant=\"ruler\"] .ot-month-nav { margin-inline: 0 -0.55em; }\n  .ot-month[data-variant=\"ruler\"] .ot-month-at { --em: var(--big); --x: clamp(calc(var(--em) * 0.62), calc(var(--p) * 100%), calc(100% - var(--em) * 0.62)); grid-area: at; display: block; position: relative; height: calc(var(--big) * 0.8); margin-bottom: calc(var(--ot-space-2) * -1); }\n  .ot-month[data-variant=\"ruler\"] .ot-month-at[data-yours] { --em: calc(var(--big) * var(--ot-expression-scale)); }\n  .ot-month[data-variant=\"ruler\"] .ot-month-at[data-yours] .ot-month-dayname { bottom: calc(var(--big) * 0.02); } /* the italic sits lower in its line */\n  .ot-month[data-variant=\"ruler\"] .ot-month-date { position: absolute; bottom: 0; inset-inline-start: var(--x); translate: calc(-50% * var(--flip)) 0; padding: 0; transition: inset-inline-start var(--ot-andante) var(--ot-exhale); }\n  .ot-month[data-variant=\"ruler\"] .ot-month-dayname { position: absolute; bottom: calc(var(--big) * -0.015); inset-inline-start: calc(var(--x) + var(--em) * 0.62 + var(--ot-space-3)); white-space: nowrap; transition: inset-inline-start var(--ot-andante) var(--ot-exhale); }\n  .ot-month[data-variant=\"ruler\"] .ot-month-at[data-side=\"start\"] .ot-month-dayname { inset-inline-start: calc(var(--x) - var(--em) * 0.62 - var(--ot-space-3)); translate: calc(-100% * var(--flip)) 0; }\n\n  .ot-month[data-variant=\"ruler\"] .ot-month-grid { grid-template-columns: repeat(var(--days), minmax(0, 1fr)); gap: 0; touch-action: pan-y; user-select: none; -webkit-user-select: none; }\n  .ot-month[data-variant=\"ruler\"] .ot-month-week { display: contents; }\n  .ot-month[data-variant=\"ruler\"] .ot-month-week:first-child { display: block; position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }\n  .ot-month[data-variant=\"ruler\"] .ot-month-gap { display: none; }\n  .ot-month[data-variant=\"ruler\"] .ot-month-day { --tick: 3.25rem; --tick-c: var(--ot-rule-strong); --line: var(--ot-rule-strong); aspect-ratio: auto; height: calc(var(--tick) + 2.2em); border-radius: 0; font-size: var(--ot-pp); background: linear-gradient(var(--line), var(--line)) no-repeat 0 var(--tick) / 100% var(--ot-hairline); }\n  @container (min-width: 40rem) { .ot-month[data-variant=\"ruler\"] .ot-month-day { --tick: 4.5rem; } }\n  .ot-month[data-variant=\"ruler\"] .ot-month-day[data-when=\"past\"] { --tick-c: var(--ot-ink); --line: var(--ot-ink); }\n  .ot-month[data-variant=\"ruler\"] .ot-month-disc { position: absolute; top: auto; bottom: calc(100% - var(--tick)); inset-inline-start: 50%; width: var(--ot-hairline); height: calc(var(--tick) * 0.3); aspect-ratio: auto; translate: calc(-50% * var(--flip)) 0; border-radius: 0; background: var(--tick-c); box-shadow: none; transition: height var(--ot-moderato) var(--ot-exhale), background-color var(--ot-allegro); }\n  .ot-month[data-variant=\"ruler\"] .ot-month-day[data-label] .ot-month-disc { height: calc(var(--tick) * 0.55); }\n  .ot-month[data-variant=\"ruler\"] .ot-month-day:not([aria-disabled=\"true\"]):is(:hover, [data-force~=\"hover\"] [tabindex=\"0\"], [data-scrub] [tabindex=\"0\"]) { --tick-c: var(--ot-pencil); }\n  .ot-month[data-variant=\"ruler\"] .ot-month-day:is([data-when=\"today\"], [aria-selected=\"true\"]) .ot-month-disc,\n  .ot-month[data-variant=\"ruler\"] .ot-month-day:not([aria-disabled=\"true\"]):is(:hover, [data-force~=\"hover\"] [tabindex=\"0\"], [data-scrub] [tabindex=\"0\"]) .ot-month-disc { height: var(--tick); }\n  .ot-month[data-variant=\"ruler\"] .ot-month-day[data-when=\"today\"] { --tick-c: var(--ot-accent); }\n  .ot-month[data-variant=\"ruler\"] .ot-month-day[aria-selected=\"true\"] { --tick-c: var(--ot-ink); }\n  .ot-month[data-variant=\"ruler\"] .ot-month-day:is([data-when=\"today\"], [aria-selected=\"true\"]) .ot-month-disc { width: var(--ot-stroke); }\n  /* The heads: today's dot, your ring. Choosing lands the ring with one small rebound. */\n  .ot-month[data-variant=\"ruler\"] .ot-month-day::before { content: \"\"; position: absolute; top: 0; inset-inline-start: 50%; width: var(--ot-dot); aspect-ratio: 1; translate: calc(-50% * var(--flip)) -50%; border-radius: 50%; scale: 0; transition: scale var(--ot-moderato) var(--ot-spiccato); }\n  .ot-month[data-variant=\"ruler\"] .ot-month-day[data-when=\"today\"]::before { background: var(--ot-accent); scale: 1; }\n  .ot-month[data-variant=\"ruler\"] .ot-month-day[aria-selected=\"true\"]::before { width: calc(var(--ot-dot) * 1.7); box-shadow: inset 0 0 0 var(--ot-stroke) var(--ot-ink); scale: 1; }\n  /* The figures under the line: the weeks' first days (and the 1st) always; every day once the\n     line is long enough; the one you point at, in pencil. Today's in the accent, yours in italic. */\n  .ot-month[data-variant=\"ruler\"] .ot-month-n { position: absolute; top: calc(100% + 0.55em); inset-inline-start: 50%; translate: calc(-50% * var(--flip)) 0; line-height: 1; white-space: nowrap; font-variant-numeric: tabular-nums; color: var(--ot-pencil); opacity: 0; transition: opacity var(--ot-allegro); }\n  .ot-month[data-variant=\"ruler\"] .ot-month-day[data-label] .ot-month-n { color: var(--ot-graphite); }\n  .ot-month[data-variant=\"ruler\"] .ot-month-day:is([data-label], [data-when=\"today\"], [aria-selected=\"true\"], :hover, [data-force~=\"hover\"] [tabindex=\"0\"], [data-scrub] [tabindex=\"0\"]) .ot-month-n { opacity: 1; }\n  @container (min-width: 40rem) { .ot-month[data-variant=\"ruler\"] .ot-month-n { opacity: 1; } }\n  .ot-month[data-variant=\"ruler\"] .ot-month-day[data-when=\"today\"] .ot-month-n { color: var(--ot-accent); font-weight: 700; }\n  .ot-month[data-variant=\"ruler\"] .ot-month-day[aria-selected=\"true\"] .ot-month-n { color: var(--ot-ink); font-family: var(--ot-expression); font-style: italic; font-size: calc(1em * var(--ot-expression-scale)); }\n  /* The dimension: a hairline with a tick at each end (none where it runs off the month) and\n     the count of days under its middle. It stretches after the numeral. */\n  .ot-month-span { position: relative; height: 2.6em; margin-top: calc(var(--ot-space-3) * -1); font-size: var(--ot-pp); }\n  .ot-month-span > span { position: absolute; top: 0; inset-inline-start: calc(var(--a) * 100%); width: calc((var(--b) - var(--a)) * 100%); height: 0.8em; border-inline: var(--ot-hairline) solid var(--ot-graphite); background: linear-gradient(var(--ot-graphite), var(--ot-graphite)) no-repeat 0 50% / 100% var(--ot-hairline); transition: inset-inline-start var(--ot-andante) var(--ot-exhale), width var(--ot-andante) var(--ot-exhale); }\n  .ot-month-span > span[data-open~=\"start\"] { border-inline-start: 0; }\n  .ot-month-span > span[data-open~=\"end\"] { border-inline-end: 0; }\n  .ot-month-span > span > span { position: absolute; top: 100%; inset-inline-start: 50%; translate: calc(-50% * var(--flip)) 0; padding-top: 0.35em; line-height: 1; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--ot-graphite); }\n  .ot-month[data-variant=\"ruler\"] .ot-month-foot { margin-top: 0; text-align: start; }\n\n  /* ── ghost, after \"the silence that heals\": numbers as data, set against huge ghost numerals.\n     The days are figures only, no discs; behind them the chosen day (else today) stands over its\n     month's number, faint and cropped by the month's edge, and the grid is read through it.\n     Gone is pencil, to come is ink, today the accent figure, yours the italic, and the ghost turns\n     italic with it. Choosing (or turning the month), the new ghost surfaces. */\n  .ot-month[data-variant=\"ghost\"] { position: relative; overflow: clip; isolation: isolate; container-type: inline-size; }\n  .ot-month[data-variant=\"ghost\"] .ot-month-head { grid-template-areas: \"title nav\"; align-items: center; }\n  .ot-month[data-variant=\"ghost\"] .ot-month-nav { margin-inline: 0 -0.55em; }\n  .ot-month[data-variant=\"ghost\"] .ot-month-at { --em: 58cqi; position: absolute; z-index: -1; inset-block-end: calc(var(--em) * -0.06); inset-inline-end: calc(var(--em) * -0.12); display: grid; justify-items: end; pointer-events: none; font-size: var(--em); font-weight: 200; line-height: 0.76; letter-spacing: -0.06em; color: color-mix(in srgb, var(--ot-rule) 55%, transparent); font-variant-numeric: tabular-nums; }\n  .ot-month[data-variant=\"ghost\"] .ot-month-at .ot-month-date { all: unset; }\n  .ot-month[data-variant=\"ghost\"] .ot-month-dayname { display: none; }\n  .ot-month[data-variant=\"ghost\"] .ot-month-at[data-yours] { font-family: var(--ot-expression); font-style: italic; font-weight: 400; font-size: calc(var(--em) * 0.8); line-height: 0.95; letter-spacing: -0.02em; }\n  .ot-month[data-variant=\"ghost\"] .ot-month-at[data-acted] > span { animation: ot-surface var(--ot-andante) var(--ot-exhale) backwards; }\n  .ot-month[data-variant=\"ghost\"] .ot-month-at[data-acted] > span + span { animation-delay: var(--ot-arpeggio); }\n  @keyframes ot-surface { from { opacity: 0; translate: 0 0.04em; } }\n  .ot-month[data-variant=\"ghost\"] .ot-month-grid { gap: var(--ot-space-1); }\n  .ot-month[data-variant=\"ghost\"] .ot-month-day { --tone: var(--ot-ink); aspect-ratio: 1.25; border-radius: 50%; font-size: var(--size); } /* the round corner only shapes focus: a ring, not a box */\n  .ot-month[data-variant=\"ghost\"] .ot-month-day[data-when=\"past\"] { --tone: var(--ot-pencil); }\n  .ot-month[data-variant=\"ghost\"] .ot-month-disc { display: contents; }\n  .ot-month[data-variant=\"ghost\"] .ot-month-n { position: relative; line-height: 1; font-variant-numeric: tabular-nums; color: var(--tone); }\n  .ot-month[data-variant=\"ghost\"] .ot-month-day[data-when=\"today\"] .ot-month-n { color: var(--ot-accent); font-weight: 700; }\n  .ot-month[data-variant=\"ghost\"] .ot-month-day[aria-selected=\"true\"] .ot-month-n { color: var(--ot-ink); font-family: var(--ot-expression); font-style: italic; font-size: calc(1.35em * var(--ot-expression-scale)); }\n  .ot-month[data-variant=\"ghost\"] .ot-month-day[data-when=\"today\"][aria-selected=\"true\"] .ot-month-n { color: var(--ot-accent); font-weight: 400; }\n  /* Pointing sketches a pencil hairline under the figure, drawn out from its middle. */\n  .ot-month[data-variant=\"ghost\"] .ot-month-n::after { content: \"\"; position: absolute; inset-inline: -0.1em; bottom: -0.3em; height: var(--ot-hairline); background: var(--ot-pencil); scale: 0 1; transition: scale var(--ot-moderato) var(--ot-exhale); }\n  .ot-month[data-variant=\"ghost\"] .ot-month-day:not([aria-disabled=\"true\"]):is(:hover, [data-force~=\"hover\"] [tabindex=\"0\"]) .ot-month-n::after { scale: 1 1; }\n\n  /* ── parenthesis, after \"20(25)\": the month set as one running line of figures that wraps like a\n     sentence, a wider space closing each week. The time from today to your day is an aside:\n     a parenthesis opens before the first of the two and closes after the other, hung in the\n     space between figures so nothing reflows; the figures inside it are set heavier. An aside\n     that began last month only closes here. Gone is pencil, to come is ink, today the accent,\n     yours the italic. Pointing sketches pencil parentheses round a figure; choosing inks the\n     aside and its parentheses land. The weekday heads stay for screen readers only. */\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-grid { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--ot-space-2) 0; --size: var(--ot-mp); }\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-week { display: contents; }\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-week:first-child { display: block; position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-gap { display: none; }\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-day { --tone: var(--ot-ink); aspect-ratio: auto; min-inline-size: 1.25em; padding: 0.1em 0.36em; border-radius: 50%; outline-offset: 0; font-size: var(--size); line-height: 1.2; }\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-week > .ot-month-day:nth-child(7) { margin-inline-end: 0.6em; }\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-day[data-when=\"past\"] { --tone: var(--ot-pencil); }\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-disc { display: contents; }\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-n { font-variant-numeric: tabular-nums; color: var(--tone); }\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-day:is([data-between], [data-paren]) .ot-month-n { font-weight: 650; }\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-day[data-when=\"today\"] .ot-month-n { color: var(--ot-accent); font-weight: 700; }\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-day[aria-selected=\"true\"] .ot-month-n { color: var(--ot-ink); font-family: var(--ot-expression); font-style: italic; font-weight: 400; font-size: calc(1.1em * var(--ot-expression-scale)); }\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-day[data-when=\"today\"][aria-selected=\"true\"] .ot-month-n { color: var(--ot-accent); }\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-day::before,\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-day::after { position: absolute; top: 50%; font-family: var(--ot-voice); font-style: normal; font-weight: 200; font-size: 1.3em; line-height: 1; color: var(--ot-pencil); opacity: 0; translate: 0 -54%; transition: opacity var(--ot-allegro); }\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-day::before { content: \"(\"; inset-inline-end: calc(100% - 0.28em); }\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-day::after { content: \")\"; inset-inline-start: calc(100% - 0.28em); }\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-day:not([aria-disabled=\"true\"]):is(:hover, [data-force~=\"hover\"] [tabindex=\"0\"])::before,\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-day:not([aria-disabled=\"true\"]):is(:hover, [data-force~=\"hover\"] [tabindex=\"0\"])::after { opacity: 1; }\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-day[data-paren~=\"open\"]::before,\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-day[data-paren~=\"close\"]::after { color: var(--ot-ink); opacity: 1; transition: none; }\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-grid[data-acted] .ot-month-day[data-paren~=\"open\"]::before,\n  .ot-month[data-variant=\"parenthesis\"] .ot-month-grid[data-acted] .ot-month-day[data-paren~=\"close\"]::after { animation: ot-hang var(--ot-moderato) var(--ot-spiccato); }\n  @keyframes ot-hang { from { opacity: 0; scale: 0.6 1; } }\n\n  @media (forced-colors: active) {\n    .ot-month[data-variant=\"ghost\"] .ot-month-at { display: none; }\n    .ot-month { forced-color-adjust: none; --ot-paper: Canvas; --ot-ink: CanvasText; --ot-graphite: CanvasText; --ot-pencil: GrayText; --ot-rule-strong: GrayText; --ot-accent: Highlight; }\n  }\n}\n",
      "type": "registry:file",
      "target": "styles/0nlytype/calendar.css"
    }
  ],
  "css": {
    "@import \"../styles/0nlytype/calendar.css\"": {}
  },
  "meta": {
    "movement": "VIII",
    "underneath": "hook",
    "contract": "ot-month"
  },
  "type": "registry:ui"
}
