{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"date-picker","type":"registry:ui","title":"Date Picker","description":"Pick one day in the Text Field's look – a month in a small card, today marked, days outside min and max or ruled out stay visible, full keyboard control and the locale's own week","dependencies":[],"files":[{"path":"registry/date-picker/date-picker.tsx","type":"registry:ui","content":"\"use client\";\n\nimport { useEffect, useId, useLayoutEffect, useMemo, useRef, useState, type KeyboardEvent, type ReactNode, type Ref } from \"react\";\n\n/*\n * Date Picker – a single self-contained file.\n * One day: a field in the Text Field's look that opens a month in a small white card – under the field, or above it\n * when there is no room. Today is marked with a small dot, the chosen day is a blue chip; days outside min / max or ones\n * you rule out stay visible but can't be picked. Arrow keys move day by day, PageUp / PageDown month by month (with\n * Shift a year), Home / End to the start or end of the week, Enter picks, Esc closes. Month names, weekdays and the\n * first day of the week follow the locale. Always six weeks high, so the card never jumps between months.\n * The card sits in the top layer, so a card's overflow never cuts it. The styles are injected below, so no extra\n * files are needed.\n */\n\nexport type DatePickerProps = {\n  /** The chosen day – makes it controlled; null shows the placeholder */\n  value?: Date | null;\n  /** Starting day when uncontrolled */\n  defaultValue?: Date | null;\n  /** Called with the picked day (local midnight) */\n  onChange?: (date: Date) => void;\n  /** Days before this can't be picked */\n  min?: Date;\n  /** Days after this can't be picked */\n  max?: Date;\n  /** Rules out single days, e.g. weekends – they stay visible but can't be picked */\n  isDateDisabled?: (date: Date) => boolean;\n  /** BCP 47 locale for month names, weekdays and the shown value – defaults to the browser's */\n  locale?: string;\n  /** 0 = Sunday … 6 = Saturday – defaults to the locale's first day of the week */\n  weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;\n  /** Shown while nothing is chosen */\n  placeholder?: string;\n  /** Shown above the field and tied to it */\n  label?: ReactNode;\n  /** A quiet line under the field – the error takes its place while there is one */\n  hint?: ReactNode;\n  /** Marks the field invalid and shows this under it, in place of the hint */\n  error?: ReactNode;\n  /** \"Optional\" next to the label */\n  optional?: boolean;\n  /** sm 28 · md 32 · lg 36 high */\n  size?: \"sm\" | \"md\" | \"lg\";\n  disabled?: boolean;\n  /** Sends the day as YYYY-MM-DD with a form, through a hidden input */\n  name?: string;\n  required?: boolean;\n  /** \"Today\" for the marker – defaults to the real today */\n  today?: Date;\n  /** Open the calendar right away (it doesn't take the focus) – nice for previews */\n  defaultOpen?: boolean;\n  id?: string;\n  className?: string;\n  \"aria-label\"?: string;\n  ref?: Ref<HTMLButtonElement>;\n};\n\n/* ---------- Days (local, no library) ---------- */\n\nconst valid = (d?: Date | null): d is Date => d instanceof Date && !Number.isNaN(d.getTime());\nconst day = (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 startOfMonth = (d: Date) => new Date(d.getFullYear(), d.getMonth(), 1);\nconst daysIn = (y: number, m: number) => new Date(y, m + 1, 0).getDate();\n/** The same day n months on – Jan 31 + 1 month is Feb 28, not Mar 3 */\nconst addMonths = (d: Date, n: number) => new Date(d.getFullYear(), d.getMonth() + n, Math.min(d.getDate(), daysIn(d.getFullYear(), d.getMonth() + n)));\nconst same = (a?: Date | null, b?: Date | null) => !!a && !!b && a.getTime() === b.getTime();\nconst sameMonth = (a: Date, b: Date) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();\nconst clamp = (d: Date, min?: Date, max?: Date) => (min && d < min ? min : max && d > max ? max : d);\nconst pad = (n: number) => String(n).padStart(2, \"0\");\n/** YYYY-MM-DD in local time, what a form sends */\nconst iso = (d: Date) => `${String(d.getFullYear()).padStart(4, \"0\")}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;\n\n/** Six weeks from the start of the week the month begins in – always 42 days, so the card never changes height */\nfunction weeks(month: Date, weekStart: number) {\n  const first = startOfMonth(month);\n  const start = addDays(first, -((first.getDay() - weekStart + 7) % 7));\n  return Array.from({ length: 6 }, (_, w) => Array.from({ length: 7 }, (_, i) => addDays(start, w * 7 + i)));\n}\n\n/** A locale the browser knows, or undefined (the browser's own) */\nfunction safeLocale(locale?: string) {\n  if (!locale) return undefined;\n  try {\n    return Intl.getCanonicalLocales(locale)[0];\n  } catch {\n    return undefined;\n  }\n}\n\n/** Regions whose week starts on Sunday – only used where the browser can't tell (no Intl week info) */\nconst SUNDAY_FIRST = new Set([\"US\", \"CA\", \"MX\", \"BR\", \"JP\", \"KR\", \"TW\", \"HK\", \"IL\", \"PH\", \"IN\", \"ZA\", \"SA\", \"PE\", \"CO\", \"VE\", \"GT\", \"PR\"]);\n\n/** 0 = Sunday … 6 = Saturday, from the locale's own week */\nfunction localeWeekStart(locale?: string): number {\n  try {\n    type WeekLocale = Intl.Locale & { getWeekInfo?: () => { firstDay: number }; weekInfo?: { firstDay: number } };\n    const loc = new Intl.Locale(locale ?? new Intl.DateTimeFormat().resolvedOptions().locale) as WeekLocale;\n    const info = loc.getWeekInfo?.() ?? loc.weekInfo;\n    if (info && info.firstDay >= 1 && info.firstDay <= 7) return info.firstDay % 7;\n    const region = loc.maximize().region;\n    return region && SUNDAY_FIRST.has(region) ? 0 : 1;\n  } catch {\n    return 1;\n  }\n}\n\n/* ---------- Picker ---------- */\n\nexport function DatePicker({\n  value: valueProp,\n  defaultValue = null,\n  onChange,\n  min,\n  max,\n  isDateDisabled,\n  locale,\n  weekStartsOn,\n  placeholder = \"Pick a date\",\n  label,\n  hint,\n  error,\n  optional = false,\n  size = \"md\",\n  disabled = false,\n  name,\n  required,\n  today: todayProp,\n  defaultOpen = false,\n  id,\n  className,\n  \"aria-label\": ariaLabel,\n  ref,\n}: DatePickerProps) {\n  const autoId = useId();\n  const triggerId = id ?? `${autoId}-trigger`;\n  const panelId = `${autoId}-panel`;\n  const labelId = `${autoId}-label`;\n  const titleId = `${autoId}-title`;\n  const messageId = `${autoId}-message`;\n\n  const loc = safeLocale(locale);\n  const fmt = useMemo(() => {\n    // Always the Gregorian calendar the grid is built on – fa-IR or th-TH would otherwise name other months\n    const f = (o: Intl.DateTimeFormatOptions) => new Intl.DateTimeFormat(loc, { ...o, calendar: \"gregory\" });\n    return {\n      value: f({ weekday: \"short\", month: \"short\", day: \"numeric\", year: \"numeric\" }),\n      month: f({ month: \"long\", year: \"numeric\" }),\n      long: f({ weekday: \"long\", month: \"long\", day: \"numeric\", year: \"numeric\" }),\n      /** Only the number – \"9\", not \"9日\" – in the locale's digits */\n      day: (d: Date) => f({ day: \"numeric\" }).formatToParts(d).find((p) => p.type === \"day\")?.value ?? String(d.getDate()),\n      wdShort: f({ weekday: \"short\" }),\n      wdNarrow: f({ weekday: \"narrow\" }),\n      wdLong: f({ weekday: \"long\" }),\n    };\n  }, [loc]);\n  const wso = weekStartsOn != null && weekStartsOn >= 0 && weekStartsOn <= 6 ? Math.floor(weekStartsOn) : localeWeekStart(loc);\n\n  const todayTime = valid(todayProp) ? day(todayProp).getTime() : undefined;\n  const today = useMemo(() => (todayTime != null ? new Date(todayTime) : day(new Date())), [todayTime]);\n  const minDay = valid(min) ? day(min) : undefined;\n  const maxDay = valid(max) && (!minDay || day(max) >= minDay) ? day(max) : undefined;\n  const blocked = (d: Date) => (!!minDay && d < minDay) || (!!maxDay && d > maxDay) || !!isDateDisabled?.(d);\n\n  const [own, setOwn] = useState<Date | null>(() => (valid(defaultValue) ? day(defaultValue) : null));\n  const value = valueProp !== undefined ? (valid(valueProp) ? day(valueProp) : null) : own;\n\n  const startDay = () => clamp(value ?? today, minDay, maxDay);\n  const [open, setOpen] = useState(defaultOpen && !disabled);\n  const [focus, setFocus] = useState<Date>(startDay);\n  const [month, setMonth] = useState<Date>(() => startOfMonth(startDay()));\n  const [dir, setDir] = useState<1 | -1>(1);\n\n  const trigger = useRef<HTMLButtonElement | null>(null);\n  const panel = useRef<HTMLDivElement | null>(null);\n  // Only an opening or the keys move the focus onto a day – not a press on the month arrows, not a preview\n  const moveFocus = useRef(false);\n\n  const invalid = Boolean(error);\n  const message = shows(error) ? error : shows(hint) ? hint : null;\n  const scale = SIZES.includes(size) ? size : \"md\";\n\n  const show = () => {\n    if (disabled) return;\n    const d = startDay();\n    setFocus(d);\n    setMonth(startOfMonth(d));\n    moveFocus.current = true;\n    setOpen(true);\n  };\n  const hide = (focusBack = true) => {\n    setOpen(false);\n    moveFocus.current = false;\n    if (focusBack) trigger.current?.focus({ preventScroll: true });\n  };\n  const pick = (d: Date) => {\n    if (blocked(d)) return;\n    if (valueProp === undefined) setOwn(d);\n    if (!same(d, value)) onChange?.(d);\n    hide();\n  };\n  const goMonth = (next: Date) => {\n    const m = startOfMonth(next);\n    if (m.getTime() !== month.getTime()) {\n      setDir(m > month ? 1 : -1);\n      setMonth(m);\n    }\n  };\n  // The month arrows: the focus stays on the arrow, the roving day follows into the new month\n  const shift = (n: number) => {\n    moveFocus.current = false;\n    const m = new Date(month.getFullYear(), month.getMonth() + n, 1);\n    setFocus(clamp(addMonths(focus, n), minDay, maxDay));\n    setDir(n > 0 ? 1 : -1);\n    setMonth(m);\n  };\n  const canPrev = !minDay || startOfMonth(minDay) < month;\n  const canNext = !maxDay || startOfMonth(maxDay) > month;\n\n  // Into the top layer, next to the field – under it, or above when there is more room there; it follows on scroll\n  useLayoutEffect(() => {\n    const el = panel.current;\n    if (!open || !el) return;\n    if (\"showPopover\" in el && !el.matches(\":popover-open\")) el.showPopover();\n    const update = () => {\n      const t = trigger.current;\n      if (t) place(el, t);\n    };\n    update();\n    window.addEventListener(\"resize\", update);\n    window.addEventListener(\"scroll\", update, true);\n    return () => {\n      window.removeEventListener(\"resize\", update);\n      window.removeEventListener(\"scroll\", update, true);\n      if (\"hidePopover\" in el && el.isConnected && el.matches(\":popover-open\")) el.hidePopover();\n    };\n  }, [open]);\n\n  // The focus follows the focused day – after an opening or a key, before paint\n  useLayoutEffect(() => {\n    if (!open || !moveFocus.current) return;\n    panel.current?.querySelector<HTMLButtonElement>(`[data-day=\"${focus.getTime()}\"]`)?.focus({ preventScroll: true });\n  }, [open, focus, month]);\n\n  // A press anywhere else closes it, without taking the focus back\n  useEffect(() => {\n    if (!open) return;\n    const down = (e: PointerEvent) => {\n      const target = e.target as Node;\n      if (trigger.current?.contains(target) || panel.current?.contains(target)) return;\n      hide(false);\n    };\n    document.addEventListener(\"pointerdown\", down, true);\n    return () => document.removeEventListener(\"pointerdown\", down, true);\n  }, [open]); // eslint-disable-line react-hooks/exhaustive-deps\n\n  const onGridKey = (e: KeyboardEvent<HTMLDivElement>) => {\n    const rtl = getComputedStyle(e.currentTarget).direction === \"rtl\";\n    const side = rtl ? -1 : 1;\n    let next: Date | null = null;\n    switch (e.key) {\n      case \"ArrowLeft\": next = addDays(focus, -side); break;\n      case \"ArrowRight\": next = addDays(focus, side); break;\n      case \"ArrowUp\": next = addDays(focus, -7); break;\n      case \"ArrowDown\": next = addDays(focus, 7); break;\n      case \"PageUp\": next = addMonths(focus, e.shiftKey ? -12 : -1); break;\n      case \"PageDown\": next = addMonths(focus, e.shiftKey ? 12 : 1); break;\n      case \"Home\": next = addDays(focus, -((focus.getDay() - wso + 7) % 7)); break;\n      case \"End\": next = addDays(focus, 6 - ((focus.getDay() - wso + 7) % 7)); break;\n      case \"Enter\":\n      case \" \":\n        e.preventDefault();\n        pick(focus);\n        return;\n      default:\n        return;\n    }\n    e.preventDefault();\n    next = clamp(next, minDay, maxDay);\n    moveFocus.current = true;\n    setFocus(next);\n    goMonth(next);\n  };\n\n  const rows = weeks(month, wso);\n  const visible = rows.flat();\n  // The day that Tab lands on: the focused one, or the first in the month when it scrolled out of view\n  const tabDay = visible.some((d) => same(d, focus)) ? focus : (visible.find((d) => sameMonth(d, month) && !blocked(d)) ?? visible.find((d) => sameMonth(d, month))!);\n  const heads = rows[0].map((d) => {\n    const short = fmt.wdShort.format(d).replace(/\\.$/, \"\");\n    // Latin scripts: two letters (Mo, Tu); others their short name, or the narrow one when that is long\n    const text = /^\\p{Script=Latin}/u.test(short) ? short.slice(0, 2) : short.length > 3 ? fmt.wdNarrow.format(d) : short;\n    return { key: d.getDay(), text, long: fmt.wdLong.format(d) };\n  });\n\n  return (\n    <div\n      className={[\"ui-dpk\", className].filter(Boolean).join(\" \")}\n      data-size={scale}\n      data-open={open || undefined}\n      data-invalid={invalid || undefined}\n      data-disabled={disabled || undefined}\n    >\n      {/* React 19 hoists this into <head> once, no matter how many pickers render */}\n      <style href=\"ui-date-picker\" precedence=\"default\">\n        {css}\n      </style>\n      {label != null && (\n        <div className=\"ui-dpk__head\">\n          <label className=\"ui-dpk__label\" id={labelId} htmlFor={triggerId}>\n            {label}\n          </label>\n          {optional && <span className=\"ui-dpk__optional\">Optional</span>}\n        </div>\n      )}\n      <button\n        ref={(el) => {\n          trigger.current = el;\n          if (typeof ref === \"function\") ref(el);\n          else if (ref) ref.current = el;\n        }}\n        id={triggerId}\n        type=\"button\"\n        className=\"ui-dpk__trigger\"\n        aria-haspopup=\"dialog\"\n        aria-expanded={open}\n        aria-controls={open ? panelId : undefined}\n        aria-label={ariaLabel ? `${ariaLabel}, ${value ? fmt.long.format(value) : placeholder}` : undefined}\n        aria-labelledby={ariaLabel ? undefined : label != null ? `${labelId} ${triggerId}` : undefined}\n        aria-describedby={message ? messageId : undefined}\n        aria-invalid={invalid || undefined}\n        aria-required={required || undefined}\n        disabled={disabled}\n        onClick={() => (open ? hide() : show())}\n        onKeyDown={(e) => {\n          if (!open && (e.key === \"ArrowDown\" || e.key === \"ArrowUp\")) {\n            e.preventDefault();\n            show();\n          } else if (open && e.key === \"Escape\") {\n            e.preventDefault();\n            hide();\n          }\n        }}\n      >\n        <span className=\"ui-dpk__icon\" aria-hidden=\"true\">\n          <CalendarIcon />\n        </span>\n        <span className=\"ui-dpk__value\" data-empty={value ? undefined : \"\"} key={value ? value.getTime() : \"empty\"}>\n          {value ? fmt.value.format(value) : placeholder}\n        </span>\n        <span className=\"ui-dpk__chevron\" aria-hidden=\"true\">\n          <ChevronIcon />\n        </span>\n      </button>\n      {name && <input type=\"hidden\" name={name} value={value ? iso(value) : \"\"} />}\n\n      {open && (\n        <div\n          ref={panel}\n          id={panelId}\n          className=\"ui-dpk__panel\"\n          role=\"dialog\"\n          aria-modal=\"false\"\n          aria-labelledby={titleId}\n          popover=\"manual\"\n          onKeyDown={(e) => {\n            if (e.key === \"Escape\") {\n              e.preventDefault();\n              e.stopPropagation();\n              hide();\n            }\n          }}\n          onBlur={(e) => {\n            // Tabbing out of the card closes it (not into the field)\n            const to = e.relatedTarget as Node | null;\n            if (to && !panel.current?.contains(to) && !trigger.current?.contains(to)) hide(false);\n          }}\n        >\n          <div className=\"ui-dpk__nav\">\n            <button type=\"button\" className=\"ui-dpk__arrow\" aria-label=\"Previous month\" disabled={!canPrev} onClick={() => shift(-1)}>\n              <ArrowIcon d=\"M15 6C15 6 9 10.42 9 12C9 13.58 15 18 15 18\" />\n            </button>\n            <div className=\"ui-dpk__title\" id={titleId} aria-live=\"polite\">\n              <span key={month.getTime()} data-dir={dir}>\n                {fmt.month.format(month)}\n              </span>\n            </div>\n            <button type=\"button\" className=\"ui-dpk__arrow\" aria-label=\"Next month\" disabled={!canNext} onClick={() => shift(1)}>\n              <ArrowIcon d=\"M9 6C9 6 15 10.42 15 12C15 13.58 9 18 9 18\" />\n            </button>\n          </div>\n\n          <div role=\"grid\" aria-labelledby={titleId} className=\"ui-dpk__grid\" onKeyDown={onGridKey} onPointerDown={() => (moveFocus.current = false)}>\n            <div role=\"row\" className=\"ui-dpk__weekdays\">\n              {heads.map((h) => (\n                <span key={h.key} role=\"columnheader\" aria-label={h.long} className=\"ui-dpk__weekday\">\n                  <abbr title={h.long}>{h.text}</abbr>\n                </span>\n              ))}\n            </div>\n            <div className=\"ui-dpk__weeks\" key={month.getTime()} data-dir={dir}>\n              {rows.map((week, wi) => (\n                <div role=\"row\" key={wi} className=\"ui-dpk__week\">\n                  {week.map((d) => {\n                    const chosen = same(d, value);\n                    const off = blocked(d);\n                    return (\n                      <span key={d.getTime()} role=\"gridcell\" aria-selected={chosen} className=\"ui-dpk__cell\">\n                        <button\n                          type=\"button\"\n                          className=\"ui-dpk__day\"\n                          data-day={d.getTime()}\n                          data-outside={!sameMonth(d, month) || undefined}\n                          data-chosen={chosen || undefined}\n                          tabIndex={same(d, tabDay) ? 0 : -1}\n                          aria-label={fmt.long.format(d)}\n                          aria-current={same(d, today) ? \"date\" : undefined}\n                          aria-disabled={off || undefined}\n                          onClick={() => pick(d)}\n                          onFocus={() => same(d, focus) || setFocus(d)}\n                        >\n                          {fmt.day(d)}\n                        </button>\n                      </span>\n                    );\n                  })}\n                </div>\n              ))}\n            </div>\n          </div>\n        </div>\n      )}\n\n      {message && (\n        <p className=\"ui-dpk__message\" id={messageId} key={shows(error) ? \"error\" : \"hint\"} aria-live={shows(error) ? \"polite\" : undefined}>\n          {message}\n        </p>\n      )}\n    </div>\n  );\n}\n\nconst SIZES = [\"sm\", \"md\", \"lg\"];\n/** Text or an element – not true, false, null or an empty string */\nconst shows = (v: ReactNode) => v != null && typeof v !== \"boolean\" && v !== \"\";\n\n/** Under the field (or above it when there is more room there), at its start edge, always inside the window */\nfunction place(el: HTMLElement, trigger: HTMLElement) {\n  const r = trigger.getBoundingClientRect();\n  const gap = 6;\n  const edge = 8;\n  const vw = document.documentElement.clientWidth || window.innerWidth;\n  const vh = window.innerHeight;\n  const w = el.offsetWidth;\n  const h = el.offsetHeight;\n  const below = vh - r.bottom - gap - edge;\n  const above = r.top - gap - edge;\n  const up = below < h && above > below;\n  const rtl = getComputedStyle(trigger).direction === \"rtl\";\n  const left = Math.min(Math.max(edge, rtl ? r.right - w : r.left), Math.max(edge, vw - edge - w));\n  const top = up ? Math.max(edge, r.top - gap - h) : r.bottom + gap;\n  el.style.left = `${Math.round(left)}px`;\n  el.style.top = `${Math.round(top)}px`;\n  el.toggleAttribute(\"data-up\", up);\n}\n\n/* Icons: Hugeicons Free (MIT, © Hugeicons, hugeicons.com) – Calendar 03, Arrow Left / Right / Down 01 */\nfunction CalendarIcon() {\n  return (\n    <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n      <path d=\"M16 2V6M8 2V6\" />\n      <path d=\"M13 4H11C7.23 4 5.34 4 4.17 5.17C3 6.34 3 8.23 3 12V14C3 17.77 3 19.66 4.17 20.83C5.34 22 7.23 22 11 22H13C16.77 22 18.66 22 19.83 20.83C21 19.66 21 17.77 21 14V12C21 8.23 21 6.34 19.83 5.17C18.66 4 16.77 4 13 4Z\" />\n      <path d=\"M3 10H21\" />\n      <path d=\"M12 14H12.01M12 18H12.01M7.5 14H7.51M7.5 18H7.51M16.5 14H16.51\" strokeWidth=\"2\" />\n    </svg>\n  );\n}\nfunction ArrowIcon({ d }: { d: string }) {\n  return (\n    <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n      <path d={d} />\n    </svg>\n  );\n}\nfunction ChevronIcon() {\n  return (\n    <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n      <path d=\"M18 9C18 9 13.58 15 12 15C10.42 15 6 9 6 9\" />\n    </svg>\n  );\n}\n\n/* ---------- Styles ---------- */\n\nconst css = /* css */ `\n/* Colors and motion as variables – override them on .ui-dpk (see Theming in the docs) */\n.ui-dpk {\n  --ui-dpk-title: var(--aui-ink, #171717);\n  --ui-dpk-strong: var(--aui-ink, #171717);\n  --ui-dpk-ink: var(--aui-ink, #171717);\n  --ui-dpk-subtle: var(--aui-muted, #8f8f8f);\n  --ui-dpk-faint: var(--aui-placeholder, #c4c4c7);\n  --ui-dpk-icon: var(--aui-muted, #8f8f8f);\n  --ui-dpk-surface: var(--aui-surface, #ffffff);\n  --ui-dpk-card: var(--aui-overlay, var(--aui-surface, #ffffff));\n  --ui-dpk-line: var(--aui-border, #e6e6e8);\n  --ui-dpk-edge: var(--aui-border-strong, #d4d4d8);\n  --ui-dpk-edge-open: var(--aui-border-strong, #b4b4bb);\n  --ui-dpk-hover: var(--aui-subtle, #f2f2f3);\n  --ui-dpk-readonly: #fafafa;\n  /* The chosen day: a blue chip */\n  --ui-dpk-accent: var(--aui-blue, #4fb0fe);\n  --ui-dpk-on-accent: #ffffff;\n  --ui-dpk-shadow-pop: var(--aui-shadow-popover, 0 0 0 1px rgba(0, 0, 0, 0.06), 0 18px 40px -12px rgba(0, 0, 0, 0.18), 0 4px 12px -4px rgba(0, 0, 0, 0.06));\n  --ui-dpk-danger: var(--aui-danger, #c4320a);\n  --ui-dpk-danger-edge: #ff8a7a;\n  --ui-dpk-danger-wash: color-mix(in srgb, var(--aui-red, #f04438) 4%, transparent);\n  --ui-dpk-focus: var(--aui-focus, rgba(0, 96, 226, 0.7));\n  --ui-dpk-ease: var(--aui-ease, cubic-bezier(0.22, 1, 0.36, 1));\n  --ui-dpk-spring: cubic-bezier(0.34, 1.56, 0.64, 1);\n\n  --h: 36px;\n  --px: 12px;\n  --fs: 13.5px;\n  --radius: var(--aui-radius, 10px);\n  --cell: 34px;\n\n  display: flex;\n  flex-direction: column;\n  gap: 7px;\n  min-width: 0;\n  font-family: var(--aui-font, \"Inter\", \"InterVariable\", ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif);\n  font-size: 13.5px;\n  line-height: 20px;\n  color: var(--ui-dpk-ink);\n  -webkit-font-smoothing: antialiased;\n}\n.ui-dpk[data-size=\"sm\"] { --h: 32px; --px: 10px; --fs: 13px; --radius: calc(var(--aui-radius, 10px) - 1px); }\n.ui-dpk[data-size=\"lg\"] { --h: 40px; --px: 14px; --fs: 14px; --radius: calc(var(--aui-radius, 10px) + 2px); }\n/* Touch screens: a comfortable tap height, 16px text and bigger days */\n@media (pointer: coarse) {\n  .ui-dpk { --fs: 16px; --cell: 40px; }\n  .ui-dpk[data-size=\"sm\"] { --h: 36px; }\n  .ui-dpk[data-size=\"md\"] { --h: 40px; }\n  .ui-dpk[data-size=\"lg\"] { --h: 44px; }\n}\n.ui-dpk *, .ui-dpk *::before, .ui-dpk *::after { box-sizing: border-box; }\n:where(.ui-dpk) button { margin: 0; padding: 0; border: 0; background: none; font: inherit; color: inherit; cursor: pointer; -webkit-tap-highlight-color: transparent; }\n:where(.ui-dpk) svg { display: block; flex: none; }\n.ui-dpk button:focus-visible { outline: 2px solid var(--ui-dpk-focus); outline-offset: 2px; }\n\n.ui-dpk__head { display: flex; align-items: baseline; gap: 6px; min-width: 0; padding: 0 2px; }\n.ui-dpk__label { min-width: 0; font-size: 13px; font-weight: 500; color: var(--ui-dpk-title); overflow-wrap: anywhere; cursor: default; }\n.ui-dpk__optional { flex: none; font-size: 12px; line-height: 16px; color: var(--ui-dpk-subtle); }\n\n/* The field: the Text Field's look – white with a 1px gray edge that darkens on hover and while open */\n.ui-dpk__trigger {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  width: 100%;\n  height: var(--h);\n  min-width: 0;\n  padding: 0 calc(var(--px) - 2px) 0 var(--px);\n  border-radius: var(--radius);\n  background: var(--ui-dpk-surface);\n  box-shadow: 0 0 0 1px var(--ui-dpk-line), 0 1px 2px rgba(0, 0, 0, 0.04);\n  font-size: var(--fs);\n  color: var(--ui-dpk-ink);\n  text-align: start;\n  transition: box-shadow 140ms ease, background-color 140ms ease;\n}\n.ui-dpk__trigger:hover { box-shadow: 0 0 0 1px var(--ui-dpk-edge), 0 1px 2px rgba(0, 0, 0, 0.04); }\n.ui-dpk[data-open] .ui-dpk__trigger { box-shadow: 0 0 0 1px var(--ui-dpk-edge-open), 0 1px 2px rgba(0, 0, 0, 0.04); }\n.ui-dpk[data-invalid] .ui-dpk__trigger { background: var(--ui-dpk-danger-wash); box-shadow: 0 0 0 1px var(--ui-dpk-danger-edge), 0 1px 2px rgba(0, 0, 0, 0.04); }\n.ui-dpk__trigger:disabled { background: var(--ui-dpk-readonly); box-shadow: 0 0 0 1px var(--ui-dpk-line); cursor: not-allowed; }\n.ui-dpk[data-disabled] { opacity: 0.55; }\n.ui-dpk__icon, .ui-dpk__chevron { display: grid; flex: none; place-items: center; width: 16px; height: 16px; color: var(--ui-dpk-icon); }\n.ui-dpk__chevron { transition: transform 240ms var(--ui-dpk-ease); }\n.ui-dpk[data-open] .ui-dpk__chevron { transform: rotate(180deg); }\n.ui-dpk__value { flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-variant-numeric: tabular-nums; animation: ui-dpk-value 260ms var(--ui-dpk-ease) both; }\n.ui-dpk__value[data-empty] { color: var(--ui-dpk-faint); animation: none; }\n\n/* The card: white in the top layer, a 1px edge and a deeper drop */\n.ui-dpk__panel {\n  position: fixed;\n  inset: auto;\n  z-index: 1000;\n  width: calc(var(--cell) * 7 + 20px);\n  max-width: calc(100vw - 16px);\n  margin: 0;\n  padding: 10px;\n  border: 0;\n  border-radius: 16px;\n  background: var(--ui-dpk-card);\n  box-shadow: var(--ui-dpk-shadow-pop);\n  color: var(--ui-dpk-ink);\n  font: inherit;\n  font-size: 13px;\n  overflow: hidden;\n  transform-origin: top left;\n  animation: ui-dpk-open 220ms var(--ui-dpk-ease) both;\n}\n.ui-dpk__panel:dir(rtl) { transform-origin: top right; }\n.ui-dpk__panel[data-up] { transform-origin: bottom left; animation-name: ui-dpk-open-up; }\n.ui-dpk__panel::backdrop { display: none; }\n\n/* Month name between the two arrows */\n.ui-dpk__nav { display: flex; align-items: center; gap: 4px; height: 30px; margin-bottom: 4px; }\n.ui-dpk__title { flex: 1; min-width: 0; overflow: hidden; text-align: center; font-size: 13.5px; font-weight: 600; white-space: nowrap; color: var(--ui-dpk-strong); }\n.ui-dpk__title > span { display: block; overflow: hidden; text-overflow: ellipsis; animation: ui-dpk-fade 240ms var(--ui-dpk-ease) both; }\n.ui-dpk__arrow { display: grid; flex: none; place-items: center; width: 30px; height: 30px; border-radius: 9px; color: var(--ui-dpk-icon); transition: background-color 140ms ease, color 140ms ease; }\n.ui-dpk__arrow:hover:not(:disabled) { background: var(--ui-dpk-hover); color: var(--ui-dpk-ink); }\n.ui-dpk__arrow:disabled { opacity: 0.35; cursor: default; }\n.ui-dpk__arrow:dir(rtl) svg { transform: scaleX(-1); }\n\n/* The month: weekday heads and six weeks of days that touch */\n.ui-dpk__grid { display: grid; }\n.ui-dpk__weekdays, .ui-dpk__week { display: grid; grid-template-columns: repeat(7, minmax(0, var(--cell))); justify-content: center; }\n.ui-dpk__weekday { height: 26px; overflow: hidden; font-size: 11px; font-weight: 500; line-height: 26px; text-align: center; white-space: nowrap; color: var(--ui-dpk-subtle); }\n.ui-dpk__weekday abbr { text-decoration: none; cursor: default; }\n.ui-dpk__weeks { animation: ui-dpk-slide 300ms var(--ui-dpk-ease) both; }\n.ui-dpk__weeks[data-dir=\"-1\"] { animation-name: ui-dpk-slide-back; }\n.ui-dpk__cell { display: grid; height: var(--cell); }\n.ui-dpk__day {\n  position: relative;\n  margin: 2px;\n  border-radius: 9px;\n  font-size: 12.5px;\n  line-height: 20px;\n  font-variant-numeric: tabular-nums;\n  color: var(--ui-dpk-ink);\n  transition: background-color 200ms ease, color 200ms ease, transform 160ms var(--ui-dpk-ease);\n}\n.ui-dpk__day:hover:not([aria-disabled]):not([data-chosen]) { background: var(--ui-dpk-hover); }\n.ui-dpk__day:active:not([aria-disabled]) { transform: scale(0.94); }\n/* Days of the months around: quieter, still a press away */\n.ui-dpk__day[data-outside] { color: var(--ui-dpk-subtle); }\n.ui-dpk__day[aria-disabled] { color: var(--ui-dpk-faint); cursor: default; }\n/* The days touch each other: their ring sits inside */\n.ui-dpk .ui-dpk__day:focus-visible { outline-offset: -2px; }\n/* Today: a small dot under the number */\n.ui-dpk__day[aria-current=\"date\"] { font-weight: 600; }\n.ui-dpk__day[aria-current=\"date\"]::after { content: \"\"; position: absolute; left: 50%; bottom: 4px; width: 3px; height: 3px; margin-left: -1.5px; border-radius: 50%; background: currentColor; opacity: 0.55; }\n/* The chosen day: a blue chip that pops in */\n.ui-dpk__day[data-chosen] { background: var(--ui-dpk-accent); color: var(--ui-dpk-on-accent); font-weight: 600; animation: ui-dpk-pick 380ms var(--ui-dpk-spring); }\n.ui-dpk__day[data-chosen][aria-current=\"date\"]::after { opacity: 0.85; }\n\n.ui-dpk__message { margin: 0; padding: 0 2px; font-size: 12.5px; line-height: 17px; color: var(--ui-dpk-subtle); overflow-wrap: anywhere; animation: ui-dpk-message 260ms var(--ui-dpk-ease); }\n.ui-dpk[data-invalid] .ui-dpk__message { color: var(--ui-dpk-danger); }\n\n@keyframes ui-dpk-open { from { opacity: 0; transform: translateY(-4px) scale(0.98); } }\n@keyframes ui-dpk-open-up { from { opacity: 0; transform: translateY(4px) scale(0.98); } }\n@keyframes ui-dpk-slide { from { opacity: 0; transform: translateX(14px); } }\n@keyframes ui-dpk-slide-back { from { opacity: 0; transform: translateX(-14px); } }\n@keyframes ui-dpk-fade { from { opacity: 0; } }\n@keyframes ui-dpk-value { from { opacity: 0; transform: translateY(4px); filter: blur(2px); } }\n@keyframes ui-dpk-pick { 0% { transform: scale(0.82); } 60% { transform: scale(1.06); } 100% { transform: scale(1); } }\n@keyframes ui-dpk-message { from { opacity: 0; transform: translateY(-3px); filter: blur(2px); } }\n\n@media (prefers-reduced-motion: reduce) {\n  .ui-dpk *, .ui-dpk *::before, .ui-dpk *::after { animation-duration: 1ms !important; transition-duration: 1ms !important; }\n}\n\n/* Dark mode: follows a .dark class or data-theme=\"dark\" on any ancestor, e.g. <html> */\n:where(.dark, [data-theme=\"dark\"]) .ui-dpk {\n  --ui-dpk-title: var(--aui-ink, #ededed);\n  --ui-dpk-strong: var(--aui-ink, #ededed);\n  --ui-dpk-ink: var(--aui-ink, #ededed);\n  --ui-dpk-subtle: var(--aui-muted, #8b8b92);\n  --ui-dpk-faint: var(--aui-placeholder, #4a4a50);\n  --ui-dpk-icon: var(--aui-muted, #8b8b92);\n  --ui-dpk-surface: var(--aui-surface, #19191b);\n  --ui-dpk-card: var(--aui-overlay, var(--aui-surface, #19191b));\n  --ui-dpk-line: var(--aui-border, #2a2a2e);\n  --ui-dpk-edge: var(--aui-border-strong, #3a3a40);\n  --ui-dpk-edge-open: var(--aui-border-strong, #4e4e55);\n  --ui-dpk-hover: var(--aui-subtle, #232326);\n  --ui-dpk-readonly: #151517;\n  --ui-dpk-accent: var(--aui-blue, #4fb0fe);\n  --ui-dpk-on-accent: #0d0d10;\n  --ui-dpk-shadow-pop: var(--aui-shadow-popover, 0 0 0 1px rgba(255, 255, 255, 0.06), 0 18px 40px -12px rgba(0, 0, 0, 0.6));\n  --ui-dpk-danger: var(--aui-danger, #ff8a7a);\n  --ui-dpk-danger-edge: color-mix(in srgb, var(--aui-red, #ff6b5f) 45%, transparent);\n  --ui-dpk-danger-wash: color-mix(in srgb, var(--aui-red, #ff6b5f) 6%, transparent);\n  --ui-dpk-focus: var(--aui-focus, rgba(77, 141, 255, 0.8));\n  color-scheme: dark;\n}\n`;\n"}]}