diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b46522..07fba4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,13 @@ Versioning follows [SemVer](https://semver.org/). - **Visual regression** — Playwright gallery screenshots (`npm run test:visual`) + CI step - Gallery **Welcome** feature cards (Theme Builder + App shell) · README gallery map - GETTING-STARTED refresh for v1.8 languages + gallery hashes +- **`Calendar`** — inline day/month/year grids (single · range); shared by DatePicker +- **`TimePicker`** — trigger + panel with TimeInput, presets, Apply +- Studio: `#library/calendar` · `#library/time-picker` + +### Changed + +- **DatePicker** panel body now composes **`Calendar`** (behavior preserved) ## [1.8.0] — 2026-07-31 diff --git a/apps/web/public/r/calendar.json b/apps/web/public/r/calendar.json new file mode 100644 index 0000000..a2bcec6 --- /dev/null +++ b/apps/web/public/r/calendar.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "calendar", + "title": "Calendar", + "description": "Inline day/month/year grid — single or range ISO dates.", + "registryDependencies": [ + "tokens" + ], + "files": [ + { + "path": "packages/ui/src/atoms/calendar.tsx", + "content": "\"use client\";\n\nimport { cn } from \"../lib/cn\";\nimport {\n useEffect,\n useId,\n useMemo,\n useState,\n type HTMLAttributes,\n type KeyboardEvent,\n} from \"react\";\n\n/**\n * ISO calendar date `YYYY-MM-DD` (local calendar parts — no timezone engine).\n */\nexport type DateRangeValue = {\n start: string;\n end: string;\n};\n\nexport type CalendarMode = \"single\" | \"range\";\n\nexport type CalendarProps = Omit, \"onChange\"> & {\n mode?: CalendarMode;\n /** Single: controlled ISO date. */\n value?: string;\n defaultValue?: string;\n onValueChange?: (value: string) => void;\n /** Range: controlled `{ start, end }` (empty = unset). */\n rangeValue?: DateRangeValue;\n defaultRangeValue?: DateRangeValue;\n onRangeValueChange?: (value: DateRangeValue) => void;\n min?: string;\n max?: string;\n /**\n * Called after a complete selection (single day, or range start+end).\n * DatePicker uses this to close the popover.\n */\n onSelectComplete?: () => void;\n \"aria-label\"?: string;\n};\n\nconst WEEKDAYS = [\"Su\", \"Mo\", \"Tu\", \"We\", \"Th\", \"Fr\", \"Sa\"] as const;\nconst YEAR_PAGE = 12;\nconst ISO_RE = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nconst EMPTY_RANGE: DateRangeValue = { start: \"\", end: \"\" };\n\ntype Ymd = { y: number; m: number; d: number };\ntype PanelMode = \"day\" | \"month\" | \"year\";\ntype Cell = { iso: string; day: number; inMonth: boolean };\n\nexport function parseISODate(iso: string | undefined): Ymd | null {\n if (!iso) return null;\n const match = ISO_RE.exec(iso);\n if (!match) return null;\n const y = Number(match[1]);\n const m = Number(match[2]) - 1;\n const d = Number(match[3]);\n if (!Number.isFinite(y) || m < 0 || m > 11 || d < 1 || d > 31) return null;\n const dt = new Date(y, m, d);\n if (dt.getFullYear() !== y || dt.getMonth() !== m || dt.getDate() !== d) {\n return null;\n }\n return { y, m, d };\n}\n\nexport function toISODate(y: number, m: number, d: number): string {\n return `${y}-${String(m + 1).padStart(2, \"0\")}-${String(d).padStart(2, \"0\")}`;\n}\n\nfunction ymdToDate({ y, m, d }: Ymd): Date {\n return new Date(y, m, d);\n}\n\nexport function compareISODate(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0;\n}\n\nfunction isDisabledISO(iso: string, min?: string, max?: string): boolean {\n if (min && compareISODate(iso, min) < 0) return true;\n if (max && compareISODate(iso, max) > 0) return true;\n return false;\n}\n\nfunction isMonthDisabled(y: number, m: number, min?: string, max?: string): boolean {\n const lastDay = new Date(y, m + 1, 0).getDate();\n const first = toISODate(y, m, 1);\n const last = toISODate(y, m, lastDay);\n if (max && compareISODate(first, max) > 0) return true;\n if (min && compareISODate(last, min) < 0) return true;\n return false;\n}\n\nfunction isYearDisabled(y: number, min?: string, max?: string): boolean {\n const first = toISODate(y, 0, 1);\n const last = toISODate(y, 11, 31);\n if (max && compareISODate(first, max) > 0) return true;\n if (min && compareISODate(last, min) < 0) return true;\n return false;\n}\n\nfunction addDays(ymd: Ymd, delta: number): Ymd {\n const dt = ymdToDate(ymd);\n dt.setDate(dt.getDate() + delta);\n return { y: dt.getFullYear(), m: dt.getMonth(), d: dt.getDate() };\n}\n\nfunction addMonths(ymd: Ymd, delta: number): Ymd {\n const dt = new Date(ymd.y, ymd.m + delta, 1);\n const last = new Date(dt.getFullYear(), dt.getMonth() + 1, 0).getDate();\n return { y: dt.getFullYear(), m: dt.getMonth(), d: Math.min(ymd.d, last) };\n}\n\nfunction clampDayInMonth(y: number, m: number, d: number): number {\n return Math.min(d, new Date(y, m + 1, 0).getDate());\n}\n\nfunction formatMonthName(m: number): string {\n try {\n return new Date(2000, m, 1).toLocaleDateString(undefined, { month: \"long\" });\n } catch {\n return String(m + 1);\n }\n}\n\nfunction formatMonthShort(m: number): string {\n try {\n return new Date(2000, m, 1).toLocaleDateString(undefined, { month: \"short\" });\n } catch {\n return String(m + 1);\n }\n}\n\nfunction buildMonthGrid(viewY: number, viewM: number): Cell[] {\n const first = new Date(viewY, viewM, 1);\n const startPad = first.getDay();\n const daysInMonth = new Date(viewY, viewM + 1, 0).getDate();\n const cells: Cell[] = [];\n const prevLast = new Date(viewY, viewM, 0).getDate();\n\n for (let i = startPad - 1; i >= 0; i -= 1) {\n const d = prevLast - i;\n const dt = new Date(viewY, viewM - 1, d);\n cells.push({\n iso: toISODate(dt.getFullYear(), dt.getMonth(), dt.getDate()),\n day: d,\n inMonth: false,\n });\n }\n for (let d = 1; d <= daysInMonth; d += 1) {\n cells.push({ iso: toISODate(viewY, viewM, d), day: d, inMonth: true });\n }\n while (cells.length % 7 !== 0 || cells.length < 42) {\n const last = cells[cells.length - 1]!;\n const next = addDays(parseISODate(last.iso)!, 1);\n cells.push({ iso: toISODate(next.y, next.m, next.d), day: next.d, inMonth: false });\n }\n return cells;\n}\n\nfunction isInRangeISO(iso: string, start: string, end: string): boolean {\n if (!start || !end) return false;\n return compareISODate(iso, start) >= 0 && compareISODate(iso, end) <= 0;\n}\n\nfunction ChevronLeftIcon() {\n return (\n \n \n \n );\n}\n\nfunction ChevronRightIcon() {\n return (\n \n \n \n );\n}\n\n/**\n * Atom — Calendar\n * Inline day / month / year grids (shared by DatePicker panel).\n * Values: ISO `YYYY-MM-DD`. No timezone engine.\n */\nexport function Calendar({\n className,\n mode = \"single\",\n value,\n defaultValue = \"\",\n onValueChange,\n rangeValue,\n defaultRangeValue = EMPTY_RANGE,\n onRangeValueChange,\n min,\n max,\n onSelectComplete,\n \"aria-label\": ariaLabel,\n ...props\n}: CalendarProps) {\n const isRange = mode === \"range\";\n const reactId = useId();\n const today = useMemo(() => {\n const n = new Date();\n return toISODate(n.getFullYear(), n.getMonth(), n.getDate());\n }, []);\n const todayYmd = parseISODate(today)!;\n\n const singleControlled = value !== undefined;\n const [singleUncontrolled, setSingleUncontrolled] = useState(defaultValue);\n const current = singleControlled ? value! : singleUncontrolled;\n\n const rangeControlled = rangeValue !== undefined;\n const [rangeUncontrolled, setRangeUncontrolled] =\n useState(defaultRangeValue);\n const activeRange = rangeControlled ? rangeValue! : rangeUncontrolled;\n\n const seed =\n parseISODate(isRange ? activeRange.start || activeRange.end : current) ??\n todayYmd;\n\n const [view, setView] = useState({ y: seed.y, m: seed.m });\n const [focusISO, setFocusISO] = useState(\n isRange\n ? activeRange.end || activeRange.start || today\n : current || today,\n );\n const [panelMode, setPanelMode] = useState(\"day\");\n const [yearPageStart, setYearPageStart] = useState(\n () => Math.floor(seed.y / YEAR_PAGE) * YEAR_PAGE,\n );\n const [rangeDraft, setRangeDraft] = useState(null);\n\n // Keep view in sync when controlled value jumps months\n useEffect(() => {\n const next = parseISODate(\n isRange ? activeRange.start || activeRange.end : current,\n );\n if (next) setView({ y: next.y, m: next.m });\n }, [current, activeRange.start, activeRange.end, isRange]);\n\n const selected = parseISODate(current);\n const cells = useMemo(\n () => buildMonthGrid(view.y, view.m),\n [view.y, view.m],\n );\n const years = useMemo(\n () => Array.from({ length: YEAR_PAGE }, (_, i) => yearPageStart + i),\n [yearPageStart],\n );\n\n function commitSingle(iso: string) {\n if (!singleControlled) setSingleUncontrolled(iso);\n onValueChange?.(iso);\n }\n\n function commitRange(next: DateRangeValue) {\n let { start, end } = next;\n if (start && end && compareISODate(start, end) > 0) {\n [start, end] = [end, start];\n }\n const ordered = { start, end };\n if (!rangeControlled) setRangeUncontrolled(ordered);\n onRangeValueChange?.(ordered);\n return ordered;\n }\n\n function selectISO(iso: string) {\n if (isDisabledISO(iso, min, max)) return;\n\n if (!isRange) {\n commitSingle(iso);\n setFocusISO(iso);\n onSelectComplete?.();\n return;\n }\n\n if (!rangeDraft || !rangeDraft.start || rangeDraft.end) {\n setRangeDraft({ start: iso, end: \"\" });\n setFocusISO(iso);\n return;\n }\n\n const completed = commitRange({ start: rangeDraft.start, end: iso });\n setRangeDraft(null);\n setFocusISO(completed.end || completed.start || iso);\n onSelectComplete?.();\n }\n\n function jumpTo(y: number, m: number) {\n const focus = parseISODate(focusISO) ?? todayYmd;\n const d = clampDayInMonth(y, m, focus.d);\n let iso = toISODate(y, m, d);\n if (isDisabledISO(iso, min, max)) {\n const last = new Date(y, m + 1, 0).getDate();\n let found = false;\n for (let day = 1; day <= last; day += 1) {\n const candidate = toISODate(y, m, day);\n if (!isDisabledISO(candidate, min, max)) {\n iso = candidate;\n found = true;\n break;\n }\n }\n if (!found) return;\n }\n setView({ y, m });\n setFocusISO(iso);\n }\n\n function selectMonth(m: number) {\n if (isMonthDisabled(view.y, m, min, max)) return;\n jumpTo(view.y, m);\n setPanelMode(\"day\");\n }\n\n function selectYear(y: number) {\n if (isYearDisabled(y, min, max)) return;\n if (!isMonthDisabled(y, view.m, min, max)) {\n jumpTo(y, view.m);\n } else {\n for (let month = 0; month < 12; month += 1) {\n if (!isMonthDisabled(y, month, min, max)) {\n jumpTo(y, month);\n break;\n }\n }\n setView((v) => ({ ...v, y }));\n }\n setPanelMode(\"month\");\n }\n\n function moveFocus(deltaDays: number) {\n const cur = parseISODate(focusISO) ?? todayYmd;\n let next = addDays(cur, deltaDays);\n let iso = toISODate(next.y, next.m, next.d);\n let guard = 0;\n while (isDisabledISO(iso, min, max) && guard < 400) {\n next = addDays(next, deltaDays > 0 ? 1 : -1);\n iso = toISODate(next.y, next.m, next.d);\n guard += 1;\n }\n if (isDisabledISO(iso, min, max)) return;\n setFocusISO(iso);\n setView({ y: next.y, m: next.m });\n }\n\n function shiftByNav(delta: number) {\n if (panelMode === \"year\") {\n setYearPageStart((s) => s + delta * YEAR_PAGE);\n return;\n }\n if (panelMode === \"month\") {\n const y = view.y + delta;\n if (isYearDisabled(y, min, max)) return;\n setView((v) => ({ ...v, y }));\n setYearPageStart(Math.floor(y / YEAR_PAGE) * YEAR_PAGE);\n return;\n }\n const focus = parseISODate(focusISO) ?? todayYmd;\n const shifted = addMonths(focus, delta);\n jumpTo(shifted.y, shifted.m);\n }\n\n function onDayKeyDown(event: KeyboardEvent, iso: string) {\n if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n moveFocus(-1);\n } else if (event.key === \"ArrowRight\") {\n event.preventDefault();\n moveFocus(1);\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n moveFocus(-7);\n } else if (event.key === \"ArrowDown\") {\n event.preventDefault();\n moveFocus(7);\n } else if (event.key === \"PageUp\") {\n event.preventDefault();\n shiftByNav(event.shiftKey ? -12 : -1);\n } else if (event.key === \"PageDown\") {\n event.preventDefault();\n shiftByNav(event.shiftKey ? 12 : 1);\n } else if (event.key === \"Home\") {\n event.preventDefault();\n const start = toISODate(view.y, view.m, 1);\n if (!isDisabledISO(start, min, max)) setFocusISO(start);\n } else if (event.key === \"End\") {\n event.preventDefault();\n const lastDay = new Date(view.y, view.m + 1, 0).getDate();\n const end = toISODate(view.y, view.m, lastDay);\n if (!isDisabledISO(end, min, max)) setFocusISO(end);\n } else if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n selectISO(iso);\n }\n }\n\n const navPrevLabel =\n panelMode === \"year\"\n ? \"Previous years\"\n : panelMode === \"month\"\n ? \"Previous year\"\n : \"Previous month\";\n const navNextLabel =\n panelMode === \"year\"\n ? \"Next years\"\n : panelMode === \"month\"\n ? \"Next year\"\n : \"Next month\";\n const headerTitle =\n panelMode === \"year\"\n ? `${yearPageStart} – ${yearPageStart + YEAR_PAGE - 1}`\n : panelMode === \"month\"\n ? String(view.y)\n : null;\n\n const displayRange = rangeDraft ?? activeRange;\n\n return (\n \n
\n shiftByNav(-1)}\n >\n \n \n\n {panelMode === \"day\" ? (\n
\n setPanelMode(\"month\")}\n >\n {formatMonthName(view.m)}\n \n {\n setYearPageStart(Math.floor(view.y / YEAR_PAGE) * YEAR_PAGE);\n setPanelMode(\"year\");\n }}\n >\n {view.y}\n \n
\n ) : (\n \n setPanelMode(panelMode === \"year\" ? \"month\" : \"day\")\n }\n >\n {headerTitle}\n \n )}\n\n shiftByNav(1)}\n >\n \n \n
\n\n {panelMode === \"day\" ? (\n <>\n {isRange ? (\n

\n {displayRange.start && !displayRange.end\n ? \"Select end date\"\n : \"Select start date\"}\n

\n ) : null}\n
\n {WEEKDAYS.map((wd) => (\n \n {wd}\n \n ))}\n
\n
\n {Array.from({ length: cells.length / 7 }, (_, week) => (\n
\n {cells.slice(week * 7, week * 7 + 7).map((cell) => {\n const rangeStart =\n isRange &&\n Boolean(displayRange.start) &&\n cell.iso === displayRange.start;\n const rangeEnd =\n isRange &&\n Boolean(displayRange.end) &&\n cell.iso === displayRange.end;\n const inRange =\n isRange &&\n Boolean(displayRange.start && displayRange.end) &&\n isInRangeISO(\n cell.iso,\n displayRange.start,\n displayRange.end,\n );\n const draftStart = rangeDraft?.start;\n const inDraftPreview =\n isRange &&\n Boolean(draftStart) &&\n !rangeDraft?.end &&\n Boolean(focusISO) &&\n isInRangeISO(\n cell.iso,\n compareISODate(draftStart!, focusISO) <= 0\n ? draftStart!\n : focusISO,\n compareISODate(draftStart!, focusISO) <= 0\n ? focusISO\n : draftStart!,\n );\n const selectedDay = isRange\n ? rangeStart || rangeEnd\n : cell.iso === current;\n const isToday = cell.iso === today;\n const focused = cell.iso === focusISO;\n const dayDisabled = isDisabledISO(cell.iso, min, max);\n\n return (\n selectISO(cell.iso)}\n onKeyDown={(event) => onDayKeyDown(event, cell.iso)}\n onFocus={() => setFocusISO(cell.iso)}\n >\n {cell.day}\n \n );\n })}\n
\n ))}\n
\n \n ) : null}\n\n {panelMode === \"month\" ? (\n \n {Array.from({ length: 12 }, (_, m) => {\n const monthDisabled = isMonthDisabled(view.y, m, min, max);\n const isSelected = Boolean(\n isRange\n ? (activeRange.start &&\n parseISODate(activeRange.start)?.y === view.y &&\n parseISODate(activeRange.start)?.m === m) ||\n (activeRange.end &&\n parseISODate(activeRange.end)?.y === view.y &&\n parseISODate(activeRange.end)?.m === m)\n : selected?.y === view.y && selected?.m === m,\n );\n const isCurrent = todayYmd.y === view.y && todayYmd.m === m;\n return (\n selectMonth(m)}\n >\n {formatMonthShort(m)}\n \n );\n })}\n \n ) : null}\n\n {panelMode === \"year\" ? (\n \n {years.map((y) => {\n const yearDisabled = isYearDisabled(y, min, max);\n const isSelected = Boolean(\n isRange\n ? parseISODate(activeRange.start)?.y === y ||\n parseISODate(activeRange.end)?.y === y\n : selected?.y === y,\n );\n const isCurrent = todayYmd.y === y;\n return (\n selectYear(y)}\n >\n {y}\n \n );\n })}\n \n ) : null}\n \n \n );\n}\n", + "type": "registry:ui", + "target": "components/softglass/atoms/calendar.tsx" + }, + { + "path": "packages/ui/src/lib/cn.ts", + "content": "/** Values allowed in `cn(...)` — only strings survive. */\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\ntype ClassValue = string | false | null | undefined | 0 | 0n | boolean;\r\n\r\n/**\r\n * Tiny class merger — no runtime deps.\r\n * Prefer this over pulling tailwind-merge until we need conflict resolution.\r\n */\r\nexport function cn(...parts: ClassValue[]): string {\r\n return parts.filter((part): part is string => typeof part === \"string\" && part.length > 0).join(\" \");\r\n}\r\n", + "type": "registry:lib", + "target": "components/softglass/lib/cn.ts" + }, + { + "path": "packages/ui/src/lib/looks.ts", + "content": "/** Shared visual “look” languages across controls. */\r\n\r\nexport type ButtonLook = \"solid\" | \"soft\" | \"glass\" | \"gradient\" | \"neon\";\r\n\r\nexport type RadioLook = \"dot\" | \"card\" | \"chip\";\r\n\r\nexport type CheckboxLook = \"box\" | \"card\" | \"pill\";\r\n\r\nexport type SwitchLook = \"track\" | \"ios\" | \"minimal\";\r\n\r\nexport type BadgeLook = \"soft\" | \"solid\" | \"outline\" | \"glow\";\r\n\r\nexport type FieldLook = \"solid\" | \"underline\" | \"filled\" | \"ghost\";\r\n\r\nexport type SelectLook = \"solid\" | \"soft\" | \"glass\" | \"gradient\";\r\n\r\nexport type TabsLook = \"pill\" | \"underline\" | \"segmented\";\r\n\r\nexport type AvatarLook = \"circle\" | \"rounded\" | \"soft\";\r\n\r\nexport type TooltipLook = \"frost\" | \"solid\" | \"accent\";\r\n\r\nexport type CardLook = \"flat\" | \"raised\" | \"outline\" | \"glow\";\r\n\r\n/** NumberInput shell + stepper language */\r\nexport type NumberInputLook = \"soft\" | \"solid\" | \"outline\" | \"ghost\";\r\n\r\n/** Slider track / thumb language */\r\nexport type SliderLook = \"soft\" | \"solid\" | \"glass\" | \"accent\";\r\n\r\n/** FileField trigger surface */\r\nexport type FileFieldLook = \"solid\" | \"soft\" | \"dashed\" | \"ghost\";\r\n\r\n/** Progress track / fill language */\r\nexport type ProgressLook = \"soft\" | \"solid\" | \"glass\" | \"accent\" | \"striped\";\r\n\r\n/** StatusDot chrome language (color still from status / color) */\r\nexport type StatusDotLook = \"soft\" | \"solid\" | \"outline\" | \"glow\";\r\n\r\n/** Chip surface language */\r\nexport type ChipLook = \"soft\" | \"solid\" | \"outline\" | \"glass\" | \"glow\";\r\n\r\n/** CloseButton chrome */\r\nexport type CloseButtonLook = \"ghost\" | \"soft\" | \"solid\" | \"danger\";\r\n\r\n/**\r\n * Collapsible / Accordion disclosure surface\r\n * soft = frost card · solid = long content · glass = chrome · outline = border only · ghost = flush list\r\n */\r\nexport type CollapsibleLook = \"soft\" | \"solid\" | \"glass\" | \"outline\" | \"ghost\";\r\n\r\nexport type AccordionLook = CollapsibleLook;\r\n\r\n/** Breadcrumb chrome: plain path · soft tray · pill chips */\r\nexport type BreadcrumbLook = \"plain\" | \"soft\" | \"pill\";\r\n\r\n/** Pagination control chrome */\r\nexport type PaginationLook = \"soft\" | \"solid\" | \"ghost\" | \"glass\";\r\n\r\n/** EmptyState surface */\r\nexport type EmptyStateLook = \"solid\" | \"soft\" | \"outline\";\r\n\r\n/**\r\n * Stepper language\r\n * soft = frost track · solid = solid rail · outline = line only\r\n * dots = progress line + large dots · pills = segmented chip row\r\n */\r\nexport type StepperLook = \"soft\" | \"solid\" | \"outline\" | \"dots\" | \"pills\";\r\n\r\n/**\r\n * Toolbar strip\r\n * soft | solid | glass | ghost | accent (accent edge rail)\r\n */\r\nexport type ToolbarLook = \"soft\" | \"solid\" | \"glass\" | \"ghost\" | \"accent\";\r\n\r\n/** List host surface */\r\nexport type ListLook = \"soft\" | \"solid\" | \"outline\" | \"ghost\" | \"inset\";\r\n\r\n/**\r\n * Stat metric tile\r\n * solid | soft | glass | outline | accent (left rail)\r\n */\r\nexport type StatLook = \"solid\" | \"soft\" | \"glass\" | \"outline\" | \"accent\";\r\n\r\n/** PageHeader chrome: plain flush · soft frost · solid panel · glass chrome */\r\nexport type PageHeaderLook = \"plain\" | \"soft\" | \"solid\" | \"glass\";\r\n\r\n/** SettingsSection panel chrome */\r\nexport type SettingsSectionLook = \"soft\" | \"solid\" | \"glass\" | \"plain\";\r\n\r\n/**\r\n * DataTable surface language\r\n * soft = frost · solid = panel · glass = chrome · outline = border · ghost = flush\r\n */\r\nexport type DataTableLook = \"soft\" | \"solid\" | \"glass\" | \"outline\" | \"ghost\";\r\n", + "type": "registry:lib", + "target": "components/softglass/lib/looks.ts" + }, + { + "path": "packages/ui/src/lib/motion.ts", + "content": "/**\r\n * Motion recipes — component-specific animation menus.\r\n * Parallel to `look` (visual surface). Always pair with `prefers-reduced-motion`.\r\n *\r\n * Defaults match the pre-motion global micro-interactions so existing UIs\r\n * keep the same feel when no prop is passed.\r\n */\r\n\r\n/** Shared “off” token used on every control. */\r\nexport type MotionNone = \"none\";\r\n\r\nexport type ButtonMotion = MotionNone | \"lift\" | \"press\" | \"sheen\" | \"ripple\";\r\n\r\nexport type CheckboxMotion = MotionNone | \"pop\" | \"draw\" | \"fade-in\" | \"bounce\";\r\n\r\nexport type RadioMotion = MotionNone | \"pop\" | \"dot-scale\" | \"ring-expand\";\r\n\r\nexport type SwitchMotion = MotionNone | \"snap\" | \"spring\" | \"elastic\";\r\n\r\nexport type FieldMotion = MotionNone | \"ring\" | \"underline-grow\" | \"glow\";\r\n\r\nexport type SelectMotion = MotionNone | \"fade\" | \"scale\" | \"slide-down\";\r\n\r\nexport type TooltipMotion = MotionNone | \"fade\" | \"scale\" | \"slide\";\r\n\r\nexport type BadgeMotion = MotionNone | \"pulse\" | \"pop-in\" | \"shimmer\";\r\n\r\nexport type AvatarMotion = MotionNone | \"lift\" | \"ring-pulse\" | \"status-ping\";\r\n\r\n/**\r\n * Tabs indicator + content enter.\r\n * - none: instant active swap\r\n * - fade: soft cross-fade on active chrome / content\r\n * - slide: moving pill or underline indicator (content still fades)\r\n */\r\nexport type TabsMotion = MotionNone | \"fade\" | \"slide\";\r\n\r\nexport type ModalMotion = MotionNone | \"scale\" | \"fade\" | \"slide-up\";\r\n\r\nexport type ToastMotion = MotionNone | \"slide-in\" | \"pop\";\r\n\r\nexport type CardMotion = MotionNone | \"lift\" | \"glow-pulse\";\r\n\r\n/** Popover / DropdownMenu panel enter (same recipe set as Select menu). */\r\nexport type PopoverMotion = MotionNone | \"fade\" | \"scale\" | \"slide-down\";\r\n\r\nexport type DropdownMenuMotion = MotionNone | \"fade\" | \"scale\" | \"slide-down\";\r\n\r\n/** ContextMenu panel — same recipes as DropdownMenu / Popover. */\r\nexport type ContextMenuMotion = MotionNone | \"fade\" | \"scale\" | \"slide-down\";\r\n\r\n/** DatePicker calendar panel — same recipes as Select. */\r\nexport type DatePickerMotion = MotionNone | \"fade\" | \"scale\" | \"slide-down\";\r\n\r\n/** Collapsible / Accordion panel open motion. */\r\nexport type CollapsibleMotion = MotionNone | \"fade\" | \"height\";\r\n\r\nexport type AccordionMotion = CollapsibleMotion;\r\n\r\n/** Sheet edge panel enter/exit. */\r\nexport type SheetMotion = MotionNone | \"slide\" | \"fade\";\r\n\r\n/** HoverCard panel (Popover family). */\r\nexport type HoverCardMotion = MotionNone | \"fade\" | \"scale\" | \"slide-down\";\r\n\r\n/** Defaults — preserve current product feel. */\r\nexport const MOTION_DEFAULTS = {\r\n button: \"lift\",\r\n checkbox: \"pop\",\r\n radio: \"pop\",\r\n switch: \"spring\",\r\n field: \"ring\",\r\n select: \"scale\",\r\n tooltip: \"scale\",\r\n badge: \"none\",\r\n avatar: \"lift\",\r\n tabs: \"slide\",\r\n modal: \"scale\",\r\n toast: \"slide-in\",\r\n card: \"lift\",\r\n popover: \"scale\",\r\n dropdownMenu: \"scale\",\r\n contextMenu: \"scale\",\r\n datePicker: \"scale\",\r\n collapsible: \"height\",\r\n accordion: \"height\",\r\n sheet: \"slide\",\r\n hoverCard: \"scale\",\r\n} as const satisfies Record;\r\n", + "type": "registry:lib", + "target": "components/softglass/lib/motion.ts" + } + ], + "type": "registry:ui" +} \ No newline at end of file diff --git a/apps/web/public/r/data-table.json b/apps/web/public/r/data-table.json index 32e92bf..2205093 100644 --- a/apps/web/public/r/data-table.json +++ b/apps/web/public/r/data-table.json @@ -11,7 +11,7 @@ "files": [ { "path": "packages/ui/src/molecules/data-table.tsx", - "content": "\"use client\";\n\nimport { cn } from \"../lib/cn\";\nimport type { DataTableLook } from \"../lib/looks\";\nimport { Checkbox } from \"../atoms/checkbox\";\nimport { EmptyState } from \"./empty-state\";\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n type ReactNode,\n type ThHTMLAttributes,\n type TdHTMLAttributes,\n type UIEvent,\n} from \"react\";\n\nexport type { DataTableLook };\n\nexport type DataTableDensity = \"comfortable\" | \"compact\";\n\nexport type DataTableSortDirection = \"asc\" | \"desc\";\n\nexport type DataTableSortState = {\n columnId: string;\n direction: DataTableSortDirection;\n} | null;\n\nexport type DataTableSelectionMode = \"none\" | \"single\" | \"multiple\";\n\nexport type DataTableColumnAlign = \"start\" | \"center\" | \"end\";\n\nexport type DataTableColumn = {\n id: string;\n header: ReactNode;\n /** Simple field key — used for cell text and default sort. */\n accessor?: keyof T & string;\n cell?: (row: T, index: number) => ReactNode;\n sortable?: boolean;\n /** Custom sort key (preferred over raw accessor when sorting). */\n sortValue?: (row: T) => string | number | boolean | null | undefined;\n align?: DataTableColumnAlign;\n width?: string | number;\n};\n\nexport type DataTableProps = Omit<\n HTMLAttributes,\n \"children\"\n> & {\n data: T[];\n columns: DataTableColumn[];\n getRowId?: (row: T, index: number) => string;\n /**\n * Accessible name for a row (selection checkbox / row label).\n * Default: row id.\n */\n getRowLabel?: (row: T, index: number) => string;\n /** soft | solid | glass | outline | ghost */\n look?: DataTableLook;\n density?: DataTableDensity;\n /** Sticky thead. Default true. */\n stickyHeader?: boolean;\n loading?: boolean;\n /** Full empty override (replaces default EmptyState). */\n empty?: ReactNode;\n emptyTitle?: ReactNode;\n emptyDescription?: ReactNode;\n selectionMode?: DataTableSelectionMode;\n selectedIds?: string[];\n defaultSelectedIds?: string[];\n onSelectionChange?: (ids: string[]) => void;\n sort?: DataTableSortState;\n defaultSort?: DataTableSortState;\n onSortChange?: (sort: DataTableSortState) => void;\n /**\n * When true (default), reorder `data` client-side for active sort.\n * Set false for server-ordered rows (still shows sort UI if controlled).\n */\n clientSort?: boolean;\n /** Skeleton row count while loading. Default 5. */\n loadingRowCount?: number;\n /**\n * Window only visible rows (native spacer virtualization).\n * Use for long lists (hundreds+). Needs a bounded viewport height\n * (`maxHeight` or style height / maxHeight on the root).\n */\n virtualized?: boolean;\n /**\n * Fixed row height estimate in px for windowing math.\n * Default: 44 (comfortable) / 36 (compact).\n */\n estimateRowHeight?: number;\n /**\n * Scroll viewport max height when virtualized (or any scrollable table).\n * Number = px. Also accept CSS length strings.\n * Default when virtualized: 320.\n */\n maxHeight?: number | string;\n /** Extra rows rendered above/below the viewport. Default 6. */\n overscan?: number;\n \"aria-label\"?: string;\n};\n\n/* —— Low-level table compounds —— */\n\nexport type TableProps = HTMLAttributes;\nexport function Table({ className, ...props }: TableProps) {\n return ;\n}\n\nexport type TableHeaderProps = HTMLAttributes;\nexport function TableHeader({ className, ...props }: TableHeaderProps) {\n return ;\n}\n\nexport type TableBodyProps = HTMLAttributes;\nexport function TableBody({ className, ...props }: TableBodyProps) {\n return ;\n}\n\nexport type TableRowProps = HTMLAttributes & {\n selected?: boolean;\n};\nexport function TableRow({\n className,\n selected,\n ...props\n}: TableRowProps) {\n return (\n \n );\n}\n\nexport type TableHeadProps = Omit<\n ThHTMLAttributes,\n \"align\"\n> & {\n align?: DataTableColumnAlign;\n sortable?: boolean;\n sorted?: DataTableSortDirection | false;\n};\nexport function TableHead({\n className,\n align = \"start\",\n sortable,\n sorted,\n children,\n ...props\n}: TableHeadProps) {\n const ariaSort =\n sorted === \"asc\"\n ? \"ascending\"\n : sorted === \"desc\"\n ? \"descending\"\n : sortable\n ? \"none\"\n : undefined;\n\n return (\n \n {children}\n \n );\n}\n\nexport type TableCellProps = Omit<\n TdHTMLAttributes,\n \"align\"\n> & {\n align?: DataTableColumnAlign;\n};\nexport function TableCell({\n className,\n align = \"start\",\n ...props\n}: TableCellProps) {\n return (\n \n );\n}\n\n/* —— Helpers —— */\n\nfunction defaultRowId(row: T, index: number): string {\n if (row && typeof row === \"object\") {\n const rec = row as Record;\n if (typeof rec.id === \"string\" || typeof rec.id === \"number\") {\n return String(rec.id);\n }\n }\n return String(index);\n}\n\nfunction compareSortValues(\n a: string | number | boolean | null | undefined,\n b: string | number | boolean | null | undefined,\n): number {\n if (a == null && b == null) return 0;\n if (a == null) return 1;\n if (b == null) return -1;\n if (typeof a === \"number\" && typeof b === \"number\") return a - b;\n if (typeof a === \"boolean\" && typeof b === \"boolean\") {\n return Number(a) - Number(b);\n }\n return String(a).localeCompare(String(b), undefined, {\n numeric: true,\n sensitivity: \"base\",\n });\n}\n\nfunction cellContent(\n column: DataTableColumn,\n row: T,\n index: number,\n): ReactNode {\n if (column.cell) return column.cell(row, index);\n if (column.accessor) {\n const value = (row as Record)[column.accessor];\n if (value == null) return null;\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n return String(value);\n }\n return null;\n }\n return null;\n}\n\nfunction columnWidthStyle(width?: string | number): CSSProperties | undefined {\n if (width == null) return undefined;\n return { width: typeof width === \"number\" ? `${width}px` : width };\n}\n\nfunction headerText(header: ReactNode): string {\n if (typeof header === \"string\" || typeof header === \"number\") {\n return String(header);\n }\n return \"Column\";\n}\n\nfunction sortButtonLabel(\n header: ReactNode,\n sorted: DataTableSortDirection | false,\n): string {\n const name = headerText(header);\n if (sorted === \"asc\") return `Sort ${name}, currently ascending`;\n if (sorted === \"desc\") return `Sort ${name}, currently descending`;\n return `Sort ${name}`;\n}\n\nfunction defaultEstimateRowHeight(density: DataTableDensity): number {\n return density === \"compact\" ? 36 : 44;\n}\n\n/** Parse CSS max-height / height to a px number when possible (for window math). */\nfunction parseCssLengthToPx(value: number | string | undefined): number | null {\n if (value == null) return null;\n if (typeof value === \"number\" && Number.isFinite(value) && value > 0) {\n return value;\n }\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (!trimmed) return null;\n if (trimmed.endsWith(\"px\")) {\n const n = Number.parseFloat(trimmed);\n return Number.isFinite(n) && n > 0 ? n : null;\n }\n const bare = Number.parseFloat(trimmed);\n if (Number.isFinite(bare) && bare > 0 && /^\\d+(\\.\\d+)?$/.test(trimmed)) {\n return bare;\n }\n }\n return null;\n}\n\nfunction resolveMaxHeightStyle(\n maxHeight: number | string | undefined,\n virtualized: boolean,\n styleMaxHeight: CSSProperties[\"maxHeight\"],\n): number | string | undefined {\n if (maxHeight != null) {\n return typeof maxHeight === \"number\" ? maxHeight : maxHeight;\n }\n if (styleMaxHeight != null) return styleMaxHeight;\n if (virtualized) return 320;\n return undefined;\n}\n\nfunction SortGlyph({ direction }: { direction?: DataTableSortDirection | false }) {\n if (direction === \"asc\") {\n return (\n \n ↑\n \n );\n }\n if (direction === \"desc\") {\n return (\n \n ↓\n \n );\n }\n return (\n \n ↕\n \n );\n}\n\n/** Vary skeleton widths so loading rows feel like real content. */\nconst SKELETON_WIDTHS = [\"68%\", \"52%\", \"78%\", \"44%\", \"60%\", \"72%\"] as const;\n\n/**\n * Molecule — DataTable\n * Product-list table: sort, selection, sticky header, density, look, empty/loading,\n * optional native row windowing (`virtualized`).\n */\nexport function DataTable({\n className,\n data,\n columns,\n getRowId = defaultRowId,\n getRowLabel,\n look = \"soft\",\n density = \"comfortable\",\n stickyHeader = true,\n loading = false,\n empty,\n emptyTitle = \"No results\",\n emptyDescription = \"Nothing to show yet.\",\n selectionMode = \"none\",\n selectedIds: selectedIdsProp,\n defaultSelectedIds,\n onSelectionChange,\n sort: sortProp,\n defaultSort = null,\n onSortChange,\n clientSort = true,\n loadingRowCount = 5,\n virtualized = false,\n estimateRowHeight,\n maxHeight,\n overscan = 6,\n \"aria-label\": ariaLabel = \"Data table\",\n style,\n ...props\n}: DataTableProps) {\n const selectable = selectionMode !== \"none\";\n const multi = selectionMode === \"multiple\";\n const rowHeight =\n estimateRowHeight ?? defaultEstimateRowHeight(density);\n const resolvedMaxHeight = resolveMaxHeightStyle(\n maxHeight,\n virtualized,\n style?.maxHeight,\n );\n\n const sortControlled = sortProp !== undefined;\n const [sortUncontrolled, setSortUncontrolled] =\n useState(defaultSort);\n const sort = sortControlled ? sortProp : sortUncontrolled;\n\n const selectionControlled = selectedIdsProp !== undefined;\n const [selectedUncontrolled, setSelectedUncontrolled] = useState(\n () => defaultSelectedIds ?? [],\n );\n const selectedIds = selectionControlled\n ? selectedIdsProp\n : selectedUncontrolled;\n const selectedSet = useMemo(() => new Set(selectedIds), [selectedIds]);\n\n const setSelected = useCallback(\n (next: string[]) => {\n if (!selectionControlled) setSelectedUncontrolled(next);\n onSelectionChange?.(next);\n },\n [onSelectionChange, selectionControlled],\n );\n\n const setSort = useCallback(\n (next: DataTableSortState) => {\n if (!sortControlled) setSortUncontrolled(next);\n onSortChange?.(next);\n },\n [onSortChange, sortControlled],\n );\n\n const toggleSort = useCallback(\n (columnId: string) => {\n if (!sort || sort.columnId !== columnId) {\n setSort({ columnId, direction: \"asc\" });\n return;\n }\n if (sort.direction === \"asc\") {\n setSort({ columnId, direction: \"desc\" });\n return;\n }\n setSort(null);\n },\n [setSort, sort],\n );\n\n const sortedData = useMemo(() => {\n if (!clientSort || !sort) return data;\n const column = columns.find((c) => c.id === sort.columnId);\n if (!column?.sortable) return data;\n\n const indexed = data.map((row, index) => ({ row, index }));\n indexed.sort((a, b) => {\n const av = column.sortValue\n ? column.sortValue(a.row)\n : column.accessor\n ? ((a.row as Record)[\n column.accessor\n ] as string | number | boolean | null | undefined)\n : undefined;\n const bv = column.sortValue\n ? column.sortValue(b.row)\n : column.accessor\n ? ((b.row as Record)[\n column.accessor\n ] as string | number | boolean | null | undefined)\n : undefined;\n const cmp = compareSortValues(av, bv);\n // Stable tie-break on original index\n if (cmp === 0) return a.index - b.index;\n return sort.direction === \"asc\" ? cmp : -cmp;\n });\n return indexed.map((x) => x.row);\n }, [clientSort, columns, data, sort]);\n\n const rowIds = useMemo(\n () => sortedData.map((row, index) => getRowId(row, index)),\n [getRowId, sortedData],\n );\n\n const allSelected =\n multi && rowIds.length > 0 && rowIds.every((id) => selectedSet.has(id));\n const someSelected =\n multi &&\n !allSelected &&\n rowIds.some((id) => selectedSet.has(id));\n\n const toggleAll = useCallback(() => {\n if (!multi) return;\n if (allSelected) setSelected([]);\n else setSelected([...rowIds]);\n }, [allSelected, multi, rowIds, setSelected]);\n\n const toggleRow = useCallback(\n (id: string) => {\n if (!selectable) return;\n if (multi) {\n if (selectedSet.has(id)) {\n setSelected(selectedIds.filter((x) => x !== id));\n } else {\n setSelected([...selectedIds, id]);\n }\n return;\n }\n // single\n if (selectedSet.has(id)) setSelected([]);\n else setSelected([id]);\n },\n [multi, selectable, selectedIds, selectedSet, setSelected],\n );\n\n const colSpan = columns.length + (selectable ? 1 : 0);\n const showEmpty = !loading && data.length === 0;\n const selectAllLabel = allSelected\n ? \"Deselect all rows\"\n : someSelected\n ? \"Select all rows (some selected)\"\n : \"Select all rows\";\n\n /* —— Virtualization (native spacer window) —— */\n const scrollRef = useRef(null);\n const [scrollTop, setScrollTop] = useState(0);\n const [viewportHeight, setViewportHeight] = useState(0);\n const windowActive =\n virtualized && !loading && !showEmpty && sortedData.length > 0;\n\n const measureViewport = useCallback(() => {\n const el = scrollRef.current;\n if (!el) return;\n const next = el.clientHeight;\n setViewportHeight((prev) => (prev === next ? prev : next));\n }, []);\n\n useEffect(() => {\n if (!windowActive) return;\n measureViewport();\n const el = scrollRef.current;\n if (!el || typeof ResizeObserver === \"undefined\") return;\n const ro = new ResizeObserver(() => measureViewport());\n ro.observe(el);\n return () => ro.disconnect();\n }, [measureViewport, windowActive, resolvedMaxHeight, density, rowHeight]);\n\n // Reset scroll when data identity / sort changes so window stays valid\n useEffect(() => {\n if (!windowActive) return;\n const el = scrollRef.current;\n if (el) el.scrollTop = 0;\n setScrollTop(0);\n }, [windowActive, sortedData, sort]);\n\n const onScroll = useCallback(\n (event: UIEvent) => {\n if (!windowActive) return;\n const next = event.currentTarget.scrollTop;\n setScrollTop((prev) => (Math.abs(prev - next) < 1 ? prev : next));\n },\n [windowActive],\n );\n\n const fallbackViewport =\n parseCssLengthToPx(resolvedMaxHeight) ??\n parseCssLengthToPx(style?.height) ??\n 320;\n const effectiveViewport =\n viewportHeight > 0 ? viewportHeight : fallbackViewport;\n\n const { startIndex, endIndex, padTop, padBottom } = useMemo(() => {\n const total = sortedData.length;\n if (!windowActive || total === 0) {\n return {\n startIndex: 0,\n endIndex: total,\n padTop: 0,\n padBottom: 0,\n };\n }\n const safeHeight = Math.max(1, rowHeight);\n const rawStart = Math.floor(scrollTop / safeHeight) - overscan;\n const start = Math.max(0, rawStart);\n const visible = Math.ceil(effectiveViewport / safeHeight) + overscan * 2;\n const end = Math.min(total, start + Math.max(visible, 1));\n return {\n startIndex: start,\n endIndex: end,\n padTop: start * safeHeight,\n padBottom: Math.max(0, (total - end) * safeHeight),\n };\n }, [\n effectiveViewport,\n overscan,\n rowHeight,\n scrollTop,\n sortedData.length,\n windowActive,\n ]);\n\n const visibleRows = windowActive\n ? sortedData.slice(startIndex, endIndex)\n : sortedData;\n\n const rootStyle: CSSProperties = {\n ...style,\n ...(resolvedMaxHeight != null && style?.maxHeight == null\n ? {\n maxHeight:\n typeof resolvedMaxHeight === \"number\"\n ? resolvedMaxHeight\n : resolvedMaxHeight,\n }\n : null),\n };\n\n return (\n \n \n
\n \n \n {selectable ? (\n \n {multi ? (\n toggleAll()}\n />\n ) : (\n \n )}\n \n ) : null}\n {columns.map((column) => {\n const isSorted =\n sort?.columnId === column.id ? sort.direction : false;\n return (\n \n {column.sortable ? (\n toggleSort(column.id)}\n >\n \n {column.header}\n \n \n \n ) : (\n {column.header}\n )}\n \n );\n })}\n \n \n \n {loading\n ? Array.from({ length: Math.max(1, loadingRowCount) }, (_, i) => (\n \n {selectable ? (\n \n \n \n ) : null}\n {columns.map((column, colIndex) => (\n \n \n \n ))}\n \n ))\n : null}\n\n {showEmpty ? (\n \n \n {empty ?? (\n \n )}\n \n \n ) : null}\n\n {!loading && !showEmpty && padTop > 0 ? (\n \n \n \n ) : null}\n\n {!loading && !showEmpty\n ? visibleRows.map((row, localIndex) => {\n const index = windowActive\n ? startIndex + localIndex\n : localIndex;\n const id = getRowId(row, index);\n const isSelected = selectedSet.has(id);\n const rowLabel = getRowLabel?.(row, index) ?? id;\n return (\n \n {selectable ? (\n \n toggleRow(id)}\n />\n \n ) : null}\n {columns.map((column) => (\n \n {cellContent(column, row, index)}\n \n ))}\n \n );\n })\n : null}\n\n {!loading && !showEmpty && padBottom > 0 ? (\n \n \n \n ) : null}\n \n
\n \n \n );\n}\n", + "content": "\"use client\";\r\n\r\nimport { cn } from \"../lib/cn\";\r\nimport type { DataTableLook } from \"../lib/looks\";\r\nimport { Checkbox } from \"../atoms/checkbox\";\r\nimport { EmptyState } from \"./empty-state\";\r\nimport {\r\n useCallback,\r\n useEffect,\r\n useMemo,\r\n useRef,\r\n useState,\r\n type CSSProperties,\r\n type HTMLAttributes,\r\n type ReactNode,\r\n type ThHTMLAttributes,\r\n type TdHTMLAttributes,\r\n type UIEvent,\r\n} from \"react\";\r\n\r\nexport type { DataTableLook };\r\n\r\nexport type DataTableDensity = \"comfortable\" | \"compact\";\r\n\r\nexport type DataTableSortDirection = \"asc\" | \"desc\";\r\n\r\nexport type DataTableSortState = {\r\n columnId: string;\r\n direction: DataTableSortDirection;\r\n} | null;\r\n\r\nexport type DataTableSelectionMode = \"none\" | \"single\" | \"multiple\";\r\n\r\nexport type DataTableColumnAlign = \"start\" | \"center\" | \"end\";\r\n\r\nexport type DataTableColumn = {\r\n id: string;\r\n header: ReactNode;\r\n /** Simple field key — used for cell text and default sort. */\r\n accessor?: keyof T & string;\r\n cell?: (row: T, index: number) => ReactNode;\r\n sortable?: boolean;\r\n /** Custom sort key (preferred over raw accessor when sorting). */\r\n sortValue?: (row: T) => string | number | boolean | null | undefined;\r\n align?: DataTableColumnAlign;\r\n width?: string | number;\r\n};\r\n\r\nexport type DataTableProps = Omit<\r\n HTMLAttributes,\r\n \"children\"\r\n> & {\r\n data: T[];\r\n columns: DataTableColumn[];\r\n getRowId?: (row: T, index: number) => string;\r\n /**\r\n * Accessible name for a row (selection checkbox / row label).\r\n * Default: row id.\r\n */\r\n getRowLabel?: (row: T, index: number) => string;\r\n /** soft | solid | glass | outline | ghost */\r\n look?: DataTableLook;\r\n density?: DataTableDensity;\r\n /** Sticky thead. Default true. */\r\n stickyHeader?: boolean;\r\n loading?: boolean;\r\n /** Full empty override (replaces default EmptyState). */\r\n empty?: ReactNode;\r\n emptyTitle?: ReactNode;\r\n emptyDescription?: ReactNode;\r\n selectionMode?: DataTableSelectionMode;\r\n selectedIds?: string[];\r\n defaultSelectedIds?: string[];\r\n onSelectionChange?: (ids: string[]) => void;\r\n sort?: DataTableSortState;\r\n defaultSort?: DataTableSortState;\r\n onSortChange?: (sort: DataTableSortState) => void;\r\n /**\r\n * When true (default), reorder `data` client-side for active sort.\r\n * Set false for server-ordered rows (still shows sort UI if controlled).\r\n */\r\n clientSort?: boolean;\r\n /** Skeleton row count while loading. Default 5. */\r\n loadingRowCount?: number;\r\n /**\r\n * Window only visible rows (native spacer virtualization).\r\n * Use for long lists (hundreds+). Needs a bounded viewport height\r\n * (`maxHeight` or style height / maxHeight on the root).\r\n */\r\n virtualized?: boolean;\r\n /**\r\n * Fixed row height estimate in px for windowing math.\r\n * Default: 44 (comfortable) / 36 (compact).\r\n */\r\n estimateRowHeight?: number;\r\n /**\r\n * Scroll viewport max height when virtualized (or any scrollable table).\r\n * Number = px. Also accept CSS length strings.\r\n * Default when virtualized: 320.\r\n */\r\n maxHeight?: number | string;\r\n /** Extra rows rendered above/below the viewport. Default 6. */\r\n overscan?: number;\r\n \"aria-label\"?: string;\r\n};\r\n\r\n/* —— Low-level table compounds —— */\r\n\r\nexport type TableProps = HTMLAttributes;\r\nexport function Table({ className, ...props }: TableProps) {\r\n return ;\r\n}\r\n\r\nexport type TableHeaderProps = HTMLAttributes;\r\nexport function TableHeader({ className, ...props }: TableHeaderProps) {\r\n return ;\r\n}\r\n\r\nexport type TableBodyProps = HTMLAttributes;\r\nexport function TableBody({ className, ...props }: TableBodyProps) {\r\n return ;\r\n}\r\n\r\nexport type TableRowProps = HTMLAttributes & {\r\n selected?: boolean;\r\n};\r\nexport function TableRow({\r\n className,\r\n selected,\r\n ...props\r\n}: TableRowProps) {\r\n return (\r\n \r\n );\r\n}\r\n\r\nexport type TableHeadProps = Omit<\r\n ThHTMLAttributes,\r\n \"align\"\r\n> & {\r\n align?: DataTableColumnAlign;\r\n sortable?: boolean;\r\n sorted?: DataTableSortDirection | false;\r\n};\r\nexport function TableHead({\r\n className,\r\n align = \"start\",\r\n sortable,\r\n sorted,\r\n children,\r\n ...props\r\n}: TableHeadProps) {\r\n const ariaSort =\r\n sorted === \"asc\"\r\n ? \"ascending\"\r\n : sorted === \"desc\"\r\n ? \"descending\"\r\n : sortable\r\n ? \"none\"\r\n : undefined;\r\n\r\n return (\r\n \r\n {children}\r\n \r\n );\r\n}\r\n\r\nexport type TableCellProps = Omit<\r\n TdHTMLAttributes,\r\n \"align\"\r\n> & {\r\n align?: DataTableColumnAlign;\r\n};\r\nexport function TableCell({\r\n className,\r\n align = \"start\",\r\n ...props\r\n}: TableCellProps) {\r\n return (\r\n \r\n );\r\n}\r\n\r\n/* —— Helpers —— */\r\n\r\nfunction defaultRowId(row: T, index: number): string {\r\n if (row && typeof row === \"object\") {\r\n const rec = row as Record;\r\n if (typeof rec.id === \"string\" || typeof rec.id === \"number\") {\r\n return String(rec.id);\r\n }\r\n }\r\n return String(index);\r\n}\r\n\r\nfunction compareSortValues(\r\n a: string | number | boolean | null | undefined,\r\n b: string | number | boolean | null | undefined,\r\n): number {\r\n if (a == null && b == null) return 0;\r\n if (a == null) return 1;\r\n if (b == null) return -1;\r\n if (typeof a === \"number\" && typeof b === \"number\") return a - b;\r\n if (typeof a === \"boolean\" && typeof b === \"boolean\") {\r\n return Number(a) - Number(b);\r\n }\r\n return String(a).localeCompare(String(b), undefined, {\r\n numeric: true,\r\n sensitivity: \"base\",\r\n });\r\n}\r\n\r\nfunction cellContent(\r\n column: DataTableColumn,\r\n row: T,\r\n index: number,\r\n): ReactNode {\r\n if (column.cell) return column.cell(row, index);\r\n if (column.accessor) {\r\n const value = (row as Record)[column.accessor];\r\n if (value == null) return null;\r\n if (\r\n typeof value === \"string\" ||\r\n typeof value === \"number\" ||\r\n typeof value === \"boolean\"\r\n ) {\r\n return String(value);\r\n }\r\n return null;\r\n }\r\n return null;\r\n}\r\n\r\nfunction columnWidthStyle(width?: string | number): CSSProperties | undefined {\r\n if (width == null) return undefined;\r\n return { width: typeof width === \"number\" ? `${width}px` : width };\r\n}\r\n\r\nfunction headerText(header: ReactNode): string {\r\n if (typeof header === \"string\" || typeof header === \"number\") {\r\n return String(header);\r\n }\r\n return \"Column\";\r\n}\r\n\r\nfunction sortButtonLabel(\r\n header: ReactNode,\r\n sorted: DataTableSortDirection | false,\r\n): string {\r\n const name = headerText(header);\r\n if (sorted === \"asc\") return `Sort ${name}, currently ascending`;\r\n if (sorted === \"desc\") return `Sort ${name}, currently descending`;\r\n return `Sort ${name}`;\r\n}\r\n\r\nfunction defaultEstimateRowHeight(density: DataTableDensity): number {\r\n return density === \"compact\" ? 36 : 44;\r\n}\r\n\r\n/** Parse CSS max-height / height to a px number when possible (for window math). */\r\nfunction parseCssLengthToPx(value: number | string | undefined): number | null {\r\n if (value == null) return null;\r\n if (typeof value === \"number\" && Number.isFinite(value) && value > 0) {\r\n return value;\r\n }\r\n if (typeof value === \"string\") {\r\n const trimmed = value.trim();\r\n if (!trimmed) return null;\r\n if (trimmed.endsWith(\"px\")) {\r\n const n = Number.parseFloat(trimmed);\r\n return Number.isFinite(n) && n > 0 ? n : null;\r\n }\r\n const bare = Number.parseFloat(trimmed);\r\n if (Number.isFinite(bare) && bare > 0 && /^\\d+(\\.\\d+)?$/.test(trimmed)) {\r\n return bare;\r\n }\r\n }\r\n return null;\r\n}\r\n\r\nfunction resolveMaxHeightStyle(\r\n maxHeight: number | string | undefined,\r\n virtualized: boolean,\r\n styleMaxHeight: CSSProperties[\"maxHeight\"],\r\n): number | string | undefined {\r\n if (maxHeight != null) {\r\n return typeof maxHeight === \"number\" ? maxHeight : maxHeight;\r\n }\r\n if (styleMaxHeight != null) return styleMaxHeight;\r\n if (virtualized) return 320;\r\n return undefined;\r\n}\r\n\r\nfunction SortGlyph({ direction }: { direction?: DataTableSortDirection | false }) {\r\n if (direction === \"asc\") {\r\n return (\r\n \r\n ↑\r\n \r\n );\r\n }\r\n if (direction === \"desc\") {\r\n return (\r\n \r\n ↓\r\n \r\n );\r\n }\r\n return (\r\n \r\n ↕\r\n \r\n );\r\n}\r\n\r\n/** Vary skeleton widths so loading rows feel like real content. */\r\nconst SKELETON_WIDTHS = [\"68%\", \"52%\", \"78%\", \"44%\", \"60%\", \"72%\"] as const;\r\n\r\n/**\r\n * Molecule — DataTable\r\n * Product-list table: sort, selection, sticky header, density, look, empty/loading,\r\n * optional native row windowing (`virtualized`).\r\n */\r\nexport function DataTable({\r\n className,\r\n data,\r\n columns,\r\n getRowId = defaultRowId,\r\n getRowLabel,\r\n look = \"soft\",\r\n density = \"comfortable\",\r\n stickyHeader = true,\r\n loading = false,\r\n empty,\r\n emptyTitle = \"No results\",\r\n emptyDescription = \"Nothing to show yet.\",\r\n selectionMode = \"none\",\r\n selectedIds: selectedIdsProp,\r\n defaultSelectedIds,\r\n onSelectionChange,\r\n sort: sortProp,\r\n defaultSort = null,\r\n onSortChange,\r\n clientSort = true,\r\n loadingRowCount = 5,\r\n virtualized = false,\r\n estimateRowHeight,\r\n maxHeight,\r\n overscan = 6,\r\n \"aria-label\": ariaLabel = \"Data table\",\r\n style,\r\n ...props\r\n}: DataTableProps) {\r\n const selectable = selectionMode !== \"none\";\r\n const multi = selectionMode === \"multiple\";\r\n const rowHeight =\r\n estimateRowHeight ?? defaultEstimateRowHeight(density);\r\n const resolvedMaxHeight = resolveMaxHeightStyle(\r\n maxHeight,\r\n virtualized,\r\n style?.maxHeight,\r\n );\r\n\r\n const sortControlled = sortProp !== undefined;\r\n const [sortUncontrolled, setSortUncontrolled] =\r\n useState(defaultSort);\r\n const sort = sortControlled ? sortProp : sortUncontrolled;\r\n\r\n const selectionControlled = selectedIdsProp !== undefined;\r\n const [selectedUncontrolled, setSelectedUncontrolled] = useState(\r\n () => defaultSelectedIds ?? [],\r\n );\r\n const selectedIds = selectionControlled\r\n ? selectedIdsProp\r\n : selectedUncontrolled;\r\n const selectedSet = useMemo(() => new Set(selectedIds), [selectedIds]);\r\n\r\n const setSelected = useCallback(\r\n (next: string[]) => {\r\n if (!selectionControlled) setSelectedUncontrolled(next);\r\n onSelectionChange?.(next);\r\n },\r\n [onSelectionChange, selectionControlled],\r\n );\r\n\r\n const setSort = useCallback(\r\n (next: DataTableSortState) => {\r\n if (!sortControlled) setSortUncontrolled(next);\r\n onSortChange?.(next);\r\n },\r\n [onSortChange, sortControlled],\r\n );\r\n\r\n const toggleSort = useCallback(\r\n (columnId: string) => {\r\n if (!sort || sort.columnId !== columnId) {\r\n setSort({ columnId, direction: \"asc\" });\r\n return;\r\n }\r\n if (sort.direction === \"asc\") {\r\n setSort({ columnId, direction: \"desc\" });\r\n return;\r\n }\r\n setSort(null);\r\n },\r\n [setSort, sort],\r\n );\r\n\r\n const sortedData = useMemo(() => {\r\n if (!clientSort || !sort) return data;\r\n const column = columns.find((c) => c.id === sort.columnId);\r\n if (!column?.sortable) return data;\r\n\r\n const indexed = data.map((row, index) => ({ row, index }));\r\n indexed.sort((a, b) => {\r\n const av = column.sortValue\r\n ? column.sortValue(a.row)\r\n : column.accessor\r\n ? ((a.row as Record)[\r\n column.accessor\r\n ] as string | number | boolean | null | undefined)\r\n : undefined;\r\n const bv = column.sortValue\r\n ? column.sortValue(b.row)\r\n : column.accessor\r\n ? ((b.row as Record)[\r\n column.accessor\r\n ] as string | number | boolean | null | undefined)\r\n : undefined;\r\n const cmp = compareSortValues(av, bv);\r\n // Stable tie-break on original index\r\n if (cmp === 0) return a.index - b.index;\r\n return sort.direction === \"asc\" ? cmp : -cmp;\r\n });\r\n return indexed.map((x) => x.row);\r\n }, [clientSort, columns, data, sort]);\r\n\r\n const rowIds = useMemo(\r\n () => sortedData.map((row, index) => getRowId(row, index)),\r\n [getRowId, sortedData],\r\n );\r\n\r\n const allSelected =\r\n multi && rowIds.length > 0 && rowIds.every((id) => selectedSet.has(id));\r\n const someSelected =\r\n multi &&\r\n !allSelected &&\r\n rowIds.some((id) => selectedSet.has(id));\r\n\r\n const toggleAll = useCallback(() => {\r\n if (!multi) return;\r\n if (allSelected) setSelected([]);\r\n else setSelected([...rowIds]);\r\n }, [allSelected, multi, rowIds, setSelected]);\r\n\r\n const toggleRow = useCallback(\r\n (id: string) => {\r\n if (!selectable) return;\r\n if (multi) {\r\n if (selectedSet.has(id)) {\r\n setSelected(selectedIds.filter((x) => x !== id));\r\n } else {\r\n setSelected([...selectedIds, id]);\r\n }\r\n return;\r\n }\r\n // single\r\n if (selectedSet.has(id)) setSelected([]);\r\n else setSelected([id]);\r\n },\r\n [multi, selectable, selectedIds, selectedSet, setSelected],\r\n );\r\n\r\n const colSpan = columns.length + (selectable ? 1 : 0);\r\n const showEmpty = !loading && data.length === 0;\r\n const selectAllLabel = allSelected\r\n ? \"Deselect all rows\"\r\n : someSelected\r\n ? \"Select all rows (some selected)\"\r\n : \"Select all rows\";\r\n\r\n /* —— Virtualization (native spacer window) —— */\r\n const scrollRef = useRef(null);\r\n const [scrollTop, setScrollTop] = useState(0);\r\n const [viewportHeight, setViewportHeight] = useState(0);\r\n const windowActive =\r\n virtualized && !loading && !showEmpty && sortedData.length > 0;\r\n\r\n const measureViewport = useCallback(() => {\r\n const el = scrollRef.current;\r\n if (!el) return;\r\n const next = el.clientHeight;\r\n setViewportHeight((prev) => (prev === next ? prev : next));\r\n }, []);\r\n\r\n useEffect(() => {\r\n if (!windowActive) return;\r\n measureViewport();\r\n const el = scrollRef.current;\r\n if (!el || typeof ResizeObserver === \"undefined\") return;\r\n const ro = new ResizeObserver(() => measureViewport());\r\n ro.observe(el);\r\n return () => ro.disconnect();\r\n }, [measureViewport, windowActive, resolvedMaxHeight, density, rowHeight]);\r\n\r\n // Reset scroll when data identity / sort changes so window stays valid\r\n useEffect(() => {\r\n if (!windowActive) return;\r\n const el = scrollRef.current;\r\n if (el) el.scrollTop = 0;\r\n setScrollTop(0);\r\n }, [windowActive, sortedData, sort]);\r\n\r\n const onScroll = useCallback(\r\n (event: UIEvent) => {\r\n if (!windowActive) return;\r\n const next = event.currentTarget.scrollTop;\r\n setScrollTop((prev) => (Math.abs(prev - next) < 1 ? prev : next));\r\n },\r\n [windowActive],\r\n );\r\n\r\n const fallbackViewport =\r\n parseCssLengthToPx(resolvedMaxHeight) ??\r\n parseCssLengthToPx(style?.height) ??\r\n 320;\r\n const effectiveViewport =\r\n viewportHeight > 0 ? viewportHeight : fallbackViewport;\r\n\r\n const { startIndex, endIndex, padTop, padBottom } = useMemo(() => {\r\n const total = sortedData.length;\r\n if (!windowActive || total === 0) {\r\n return {\r\n startIndex: 0,\r\n endIndex: total,\r\n padTop: 0,\r\n padBottom: 0,\r\n };\r\n }\r\n const safeHeight = Math.max(1, rowHeight);\r\n const rawStart = Math.floor(scrollTop / safeHeight) - overscan;\r\n const start = Math.max(0, rawStart);\r\n const visible = Math.ceil(effectiveViewport / safeHeight) + overscan * 2;\r\n const end = Math.min(total, start + Math.max(visible, 1));\r\n return {\r\n startIndex: start,\r\n endIndex: end,\r\n padTop: start * safeHeight,\r\n padBottom: Math.max(0, (total - end) * safeHeight),\r\n };\r\n }, [\r\n effectiveViewport,\r\n overscan,\r\n rowHeight,\r\n scrollTop,\r\n sortedData.length,\r\n windowActive,\r\n ]);\r\n\r\n const visibleRows = windowActive\r\n ? sortedData.slice(startIndex, endIndex)\r\n : sortedData;\r\n\r\n const rootStyle: CSSProperties = {\r\n ...style,\r\n ...(resolvedMaxHeight != null && style?.maxHeight == null\r\n ? {\r\n maxHeight:\r\n typeof resolvedMaxHeight === \"number\"\r\n ? resolvedMaxHeight\r\n : resolvedMaxHeight,\r\n }\r\n : null),\r\n };\r\n\r\n return (\r\n \r\n \r\n
\r\n \r\n \r\n {selectable ? (\r\n \r\n {multi ? (\r\n toggleAll()}\r\n />\r\n ) : (\r\n \r\n )}\r\n \r\n ) : null}\r\n {columns.map((column) => {\r\n const isSorted =\r\n sort?.columnId === column.id ? sort.direction : false;\r\n return (\r\n \r\n {column.sortable ? (\r\n toggleSort(column.id)}\r\n >\r\n \r\n {column.header}\r\n \r\n \r\n \r\n ) : (\r\n {column.header}\r\n )}\r\n \r\n );\r\n })}\r\n \r\n \r\n \r\n {loading\r\n ? Array.from({ length: Math.max(1, loadingRowCount) }, (_, i) => (\r\n \r\n {selectable ? (\r\n \r\n \r\n \r\n ) : null}\r\n {columns.map((column, colIndex) => (\r\n \r\n \r\n \r\n ))}\r\n \r\n ))\r\n : null}\r\n\r\n {showEmpty ? (\r\n \r\n \r\n {empty ?? (\r\n \r\n )}\r\n \r\n \r\n ) : null}\r\n\r\n {!loading && !showEmpty && padTop > 0 ? (\r\n \r\n \r\n \r\n ) : null}\r\n\r\n {!loading && !showEmpty\r\n ? visibleRows.map((row, localIndex) => {\r\n const index = windowActive\r\n ? startIndex + localIndex\r\n : localIndex;\r\n const id = getRowId(row, index);\r\n const isSelected = selectedSet.has(id);\r\n const rowLabel = getRowLabel?.(row, index) ?? id;\r\n return (\r\n \r\n {selectable ? (\r\n \r\n toggleRow(id)}\r\n />\r\n \r\n ) : null}\r\n {columns.map((column) => (\r\n \r\n {cellContent(column, row, index)}\r\n \r\n ))}\r\n \r\n );\r\n })\r\n : null}\r\n\r\n {!loading && !showEmpty && padBottom > 0 ? (\r\n \r\n \r\n \r\n ) : null}\r\n \r\n
\r\n \r\n \r\n );\r\n}\r\n", "type": "registry:ui", "target": "components/softglass/molecules/data-table.tsx" }, diff --git a/apps/web/public/r/date-picker.json b/apps/web/public/r/date-picker.json index c08d411..8813be9 100644 --- a/apps/web/public/r/date-picker.json +++ b/apps/web/public/r/date-picker.json @@ -2,14 +2,15 @@ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "date-picker", "title": "Date Picker", - "description": "Single ISO date calendar — body portal + flip/clamp panel.", + "description": "Single or range ISO dates — portal panel over Calendar.", "registryDependencies": [ - "tokens" + "tokens", + "calendar" ], "files": [ { "path": "packages/ui/src/atoms/date-picker.tsx", - "content": "\"use client\";\r\n\r\nimport { cn } from \"../lib/cn\";\r\nimport { MOTION_DEFAULTS, type DatePickerMotion } from \"../lib/motion\";\r\nimport { exitDurationForMotion, usePresence } from \"../lib/presence\";\r\nimport {\r\n eventInside,\r\n useFloatingPortal,\r\n} from \"../lib/use-floating-portal\";\r\nimport {\r\n useCallback,\r\n useEffect,\r\n useId,\r\n useMemo,\r\n useRef,\r\n useState,\r\n type KeyboardEvent,\r\n type ReactNode,\r\n} from \"react\";\r\nimport { createPortal } from \"react-dom\";\r\n\r\nexport type DatePickerSize = \"sm\" | \"md\" | \"lg\";\r\nexport type { DatePickerMotion };\r\n\r\n/**\r\n * ISO calendar date `YYYY-MM-DD` (local calendar parts — no timezone engine).\r\n */\r\nexport type DateRangeValue = {\r\n start: string;\r\n end: string;\r\n};\r\n\r\nexport type DatePickerMode = \"single\" | \"range\";\r\n\r\nexport type DatePickerProps = {\r\n /** single (default) | range */\r\n mode?: DatePickerMode;\r\n /** Single mode: controlled ISO date. */\r\n value?: string;\r\n defaultValue?: string;\r\n onValueChange?: (value: string) => void;\r\n /** Range mode: controlled `{ start, end }` ISO strings (empty string = unset). */\r\n rangeValue?: DateRangeValue;\r\n defaultRangeValue?: DateRangeValue;\r\n onRangeValueChange?: (value: DateRangeValue) => void;\r\n placeholder?: string;\r\n size?: DatePickerSize;\r\n /** Panel enter/exit motion (same recipes as Select). */\r\n motion?: DatePickerMotion;\r\n label?: ReactNode;\r\n hint?: ReactNode;\r\n error?: ReactNode;\r\n disabled?: boolean;\r\n /** Inclusive min date `YYYY-MM-DD`. */\r\n min?: string;\r\n /** Inclusive max date `YYYY-MM-DD`. */\r\n max?: string;\r\n name?: string;\r\n /** Range mode: optional second hidden input name for end date. */\r\n nameEnd?: string;\r\n id?: string;\r\n className?: string;\r\n placement?: \"auto\" | \"bottom\" | \"top\";\r\n \"aria-label\"?: string;\r\n};\r\n\r\nconst sizeClass: Record = {\r\n sm: \"sg-datepicker-trigger-sm\",\r\n md: \"\",\r\n lg: \"sg-datepicker-trigger-lg\",\r\n};\r\n\r\nconst WEEKDAYS = [\"Su\", \"Mo\", \"Tu\", \"We\", \"Th\", \"Fr\", \"Sa\"] as const;\r\n\r\n/** How many years in the year grid page. */\r\nconst YEAR_PAGE = 12;\r\n\r\nconst ISO_RE = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\r\n\r\ntype Ymd = { y: number; m: number; d: number };\r\ntype PanelMode = \"day\" | \"month\" | \"year\";\r\n\r\nfunction parseISO(iso: string | undefined): Ymd | null {\r\n if (!iso) return null;\r\n const match = ISO_RE.exec(iso);\r\n if (!match) return null;\r\n const y = Number(match[1]);\r\n const m = Number(match[2]) - 1;\r\n const d = Number(match[3]);\r\n if (!Number.isFinite(y) || m < 0 || m > 11 || d < 1 || d > 31) return null;\r\n const dt = new Date(y, m, d);\r\n if (dt.getFullYear() !== y || dt.getMonth() !== m || dt.getDate() !== d) {\r\n return null;\r\n }\r\n return { y, m, d };\r\n}\r\n\r\nfunction toISO(y: number, m: number, d: number): string {\r\n const mm = String(m + 1).padStart(2, \"0\");\r\n const dd = String(d).padStart(2, \"0\");\r\n return `${y}-${mm}-${dd}`;\r\n}\r\n\r\nfunction ymdToDate({ y, m, d }: Ymd): Date {\r\n return new Date(y, m, d);\r\n}\r\n\r\nfunction compareISO(a: string, b: string): number {\r\n return a < b ? -1 : a > b ? 1 : 0;\r\n}\r\n\r\nfunction clampISO(iso: string, min?: string, max?: string): string {\r\n let next = iso;\r\n if (min && compareISO(next, min) < 0) next = min;\r\n if (max && compareISO(next, max) > 0) next = max;\r\n return next;\r\n}\r\n\r\nfunction isDisabledISO(iso: string, min?: string, max?: string): boolean {\r\n if (min && compareISO(iso, min) < 0) return true;\r\n if (max && compareISO(iso, max) > 0) return true;\r\n return false;\r\n}\r\n\r\n/** Month has no selectable day inside min/max. */\r\nfunction isMonthDisabled(\r\n y: number,\r\n m: number,\r\n min?: string,\r\n max?: string,\r\n): boolean {\r\n const lastDay = new Date(y, m + 1, 0).getDate();\r\n const first = toISO(y, m, 1);\r\n const last = toISO(y, m, lastDay);\r\n if (max && compareISO(first, max) > 0) return true;\r\n if (min && compareISO(last, min) < 0) return true;\r\n return false;\r\n}\r\n\r\n/** Year has no selectable day inside min/max. */\r\nfunction isYearDisabled(y: number, min?: string, max?: string): boolean {\r\n const first = toISO(y, 0, 1);\r\n const last = toISO(y, 11, 31);\r\n if (max && compareISO(first, max) > 0) return true;\r\n if (min && compareISO(last, min) < 0) return true;\r\n return false;\r\n}\r\n\r\nfunction addDays(ymd: Ymd, delta: number): Ymd {\r\n const dt = ymdToDate(ymd);\r\n dt.setDate(dt.getDate() + delta);\r\n return { y: dt.getFullYear(), m: dt.getMonth(), d: dt.getDate() };\r\n}\r\n\r\nfunction addMonths(ymd: Ymd, delta: number): Ymd {\r\n const dt = new Date(ymd.y, ymd.m + delta, 1);\r\n const last = new Date(dt.getFullYear(), dt.getMonth() + 1, 0).getDate();\r\n const d = Math.min(ymd.d, last);\r\n return { y: dt.getFullYear(), m: dt.getMonth(), d };\r\n}\r\n\r\nfunction clampDayInMonth(y: number, m: number, d: number): number {\r\n const last = new Date(y, m + 1, 0).getDate();\r\n return Math.min(d, last);\r\n}\r\n\r\nfunction formatDisplay(iso: string): string {\r\n const ymd = parseISO(iso);\r\n if (!ymd) return iso;\r\n try {\r\n return ymdToDate(ymd).toLocaleDateString(undefined, {\r\n year: \"numeric\",\r\n month: \"short\",\r\n day: \"numeric\",\r\n });\r\n } catch {\r\n return iso;\r\n }\r\n}\r\n\r\nfunction formatMonthName(m: number): string {\r\n try {\r\n return new Date(2000, m, 1).toLocaleDateString(undefined, {\r\n month: \"long\",\r\n });\r\n } catch {\r\n return String(m + 1);\r\n }\r\n}\r\n\r\nfunction formatMonthShort(m: number): string {\r\n try {\r\n return new Date(2000, m, 1).toLocaleDateString(undefined, {\r\n month: \"short\",\r\n });\r\n } catch {\r\n return String(m + 1);\r\n }\r\n}\r\n\r\ntype Cell = {\r\n iso: string;\r\n day: number;\r\n inMonth: boolean;\r\n};\r\n\r\nfunction buildMonthGrid(viewY: number, viewM: number): Cell[] {\r\n const first = new Date(viewY, viewM, 1);\r\n const startPad = first.getDay(); // 0 = Sunday\r\n const daysInMonth = new Date(viewY, viewM + 1, 0).getDate();\r\n const cells: Cell[] = [];\r\n\r\n const prevLast = new Date(viewY, viewM, 0).getDate();\r\n for (let i = startPad - 1; i >= 0; i -= 1) {\r\n const d = prevLast - i;\r\n const dt = new Date(viewY, viewM - 1, d);\r\n cells.push({\r\n iso: toISO(dt.getFullYear(), dt.getMonth(), dt.getDate()),\r\n day: d,\r\n inMonth: false,\r\n });\r\n }\r\n\r\n for (let d = 1; d <= daysInMonth; d += 1) {\r\n cells.push({\r\n iso: toISO(viewY, viewM, d),\r\n day: d,\r\n inMonth: true,\r\n });\r\n }\r\n\r\n while (cells.length % 7 !== 0) {\r\n const last = cells[cells.length - 1]!;\r\n const next = addDays(parseISO(last.iso)!, 1);\r\n cells.push({\r\n iso: toISO(next.y, next.m, next.d),\r\n day: next.d,\r\n inMonth: false,\r\n });\r\n }\r\n while (cells.length < 42) {\r\n const last = cells[cells.length - 1]!;\r\n const next = addDays(parseISO(last.iso)!, 1);\r\n cells.push({\r\n iso: toISO(next.y, next.m, next.d),\r\n day: next.d,\r\n inMonth: false,\r\n });\r\n }\r\n\r\n return cells;\r\n}\r\n\r\nfunction CalendarIcon() {\r\n return (\r\n \r\n \r\n \r\n \r\n \r\n );\r\n}\r\n\r\nfunction ChevronLeftIcon() {\r\n return (\r\n \r\n \r\n \r\n );\r\n}\r\n\r\nfunction ChevronRightIcon() {\r\n return (\r\n \r\n \r\n \r\n );\r\n}\r\n\r\nconst EMPTY_RANGE: DateRangeValue = { start: \"\", end: \"\" };\r\n\r\nfunction formatRangeDisplay(range: DateRangeValue): string | null {\r\n const startOk = Boolean(range.start && parseISO(range.start));\r\n const endOk = Boolean(range.end && parseISO(range.end));\r\n if (startOk && endOk) {\r\n return `${formatDisplay(range.start)} – ${formatDisplay(range.end)}`;\r\n }\r\n if (startOk) {\r\n return `${formatDisplay(range.start)} – …`;\r\n }\r\n return null;\r\n}\r\n\r\nfunction isInRangeISO(iso: string, start: string, end: string): boolean {\r\n if (!start || !end) return false;\r\n return compareISO(iso, start) >= 0 && compareISO(iso, end) <= 0;\r\n}\r\n\r\n/**\r\n * Atom — DatePicker\r\n * Single or range calendar. Solid trigger + frost panel.\r\n * Day grid, plus month/year grids so you can jump without only using arrows.\r\n * Values are ISO `YYYY-MM-DD` (local calendar; no time/locale packs).\r\n */\r\nexport function DatePicker({\r\n mode = \"single\",\r\n value,\r\n defaultValue = \"\",\r\n onValueChange,\r\n rangeValue,\r\n defaultRangeValue = EMPTY_RANGE,\r\n onRangeValueChange,\r\n placeholder,\r\n size = \"md\",\r\n motion = MOTION_DEFAULTS.datePicker,\r\n label,\r\n hint,\r\n error,\r\n disabled,\r\n min,\r\n max,\r\n name,\r\n nameEnd,\r\n id,\r\n className,\r\n placement = \"auto\",\r\n \"aria-label\": ariaLabel,\r\n}: DatePickerProps) {\r\n const isRange = mode === \"range\";\r\n const resolvedPlaceholder =\r\n placeholder ?? (isRange ? \"Pick a date range…\" : \"Pick a date…\");\r\n\r\n const reactId = useId();\r\n const pickerId = id ?? `sg-datepicker-${reactId}`;\r\n const panelId = `${pickerId}-panel`;\r\n const hintId = hint ? `${pickerId}-hint` : undefined;\r\n const errorId = error ? `${pickerId}-error` : undefined;\r\n const describedBy = [hintId, errorId].filter(Boolean).join(\" \") || undefined;\r\n const invalid = Boolean(error);\r\n\r\n const isControlled = value !== undefined;\r\n const [uncontrolled, setUncontrolled] = useState(defaultValue);\r\n const current = isControlled ? value : uncontrolled;\r\n\r\n const isRangeControlled = rangeValue !== undefined;\r\n const [uncontrolledRange, setUncontrolledRange] =\r\n useState(defaultRangeValue);\r\n const committedRange = isRangeControlled\r\n ? (rangeValue ?? EMPTY_RANGE)\r\n : uncontrolledRange;\r\n /** While picking second endpoint (range mode). */\r\n const [rangeDraft, setRangeDraft] = useState(null);\r\n const activeRange = isRange ? (rangeDraft ?? committedRange) : EMPTY_RANGE;\r\n\r\n const selected = parseISO(isRange ? activeRange.start || activeRange.end : current);\r\n\r\n const today = useMemo(() => {\r\n const now = new Date();\r\n return toISO(now.getFullYear(), now.getMonth(), now.getDate());\r\n }, []);\r\n const todayYmd = parseISO(today)!;\r\n\r\n const [open, setOpen] = useState(false);\r\n const [panelMode, setPanelMode] = useState(\"day\");\r\n const [view, setView] = useState(() => {\r\n const base = selected ?? parseISO(today)!;\r\n return { y: base.y, m: base.m };\r\n });\r\n /** First year of the 12-year page in year mode. */\r\n const [yearPageStart, setYearPageStart] = useState(() => {\r\n const base = selected ?? parseISO(today)!;\r\n return Math.floor(base.y / YEAR_PAGE) * YEAR_PAGE;\r\n });\r\n const [focusISO, setFocusISO] = useState(() => {\r\n if (isRange) {\r\n return committedRange.start || committedRange.end || today;\r\n }\r\n return current || today;\r\n });\r\n const { mounted: panelMounted, exiting: panelExiting, state: panelState } =\r\n usePresence(open, { durationMs: exitDurationForMotion(motion) });\r\n\r\n const rootRef = useRef(null);\r\n const triggerRef = useRef(null);\r\n const panelRef = useRef(null);\r\n\r\n const { portalReady, floatingStyle, side: menuPlacement } = useFloatingPortal({\r\n open,\r\n mounted: panelMounted,\r\n triggerRef,\r\n panelRef,\r\n placement,\r\n align: \"start\",\r\n matchWidth: false,\r\n flipMinSpace: 320,\r\n gap: 6,\r\n });\r\n\r\n const cells = useMemo(\r\n () => buildMonthGrid(view.y, view.m),\r\n [view.y, view.m],\r\n );\r\n\r\n const years = useMemo(\r\n () =>\r\n Array.from({ length: YEAR_PAGE }, (_, i) => yearPageStart + i),\r\n [yearPageStart],\r\n );\r\n\r\n const commit = useCallback(\r\n (next: string) => {\r\n const clamped = clampISO(next, min, max);\r\n if (!isControlled) setUncontrolled(clamped);\r\n onValueChange?.(clamped);\r\n },\r\n [isControlled, max, min, onValueChange],\r\n );\r\n\r\n const commitRange = useCallback(\r\n (next: DateRangeValue) => {\r\n const start = next.start ? clampISO(next.start, min, max) : \"\";\r\n const end = next.end ? clampISO(next.end, min, max) : \"\";\r\n let range: DateRangeValue = { start, end };\r\n if (start && end && compareISO(end, start) < 0) {\r\n range = { start: end, end: start };\r\n }\r\n if (!isRangeControlled) setUncontrolledRange(range);\r\n onRangeValueChange?.(range);\r\n return range;\r\n },\r\n [isRangeControlled, max, min, onRangeValueChange],\r\n );\r\n\r\n const close = useCallback(() => {\r\n setOpen(false);\r\n setPanelMode(\"day\");\r\n setRangeDraft(null);\r\n }, []);\r\n\r\n const openPanel = useCallback(() => {\r\n if (disabled) return;\r\n\r\n const baseIso = isRange\r\n ? committedRange.start ||\r\n committedRange.end ||\r\n clampISO(today, min, max)\r\n : current || clampISO(today, min, max);\r\n const base =\r\n parseISO(baseIso) ??\r\n parseISO(clampISO(today, min, max)) ??\r\n parseISO(today)!;\r\n setView({ y: base.y, m: base.m });\r\n setYearPageStart(Math.floor(base.y / YEAR_PAGE) * YEAR_PAGE);\r\n setFocusISO(toISO(base.y, base.m, base.d));\r\n setPanelMode(\"day\");\r\n setRangeDraft(null);\r\n setOpen(true);\r\n }, [\r\n committedRange.end,\r\n committedRange.start,\r\n current,\r\n disabled,\r\n isRange,\r\n max,\r\n min,\r\n today,\r\n ]);\r\n\r\n useEffect(() => {\r\n if (!open || panelExiting) return;\r\n\r\n function onPointerDown(event: MouseEvent) {\r\n if (\r\n !eventInside(event.target, rootRef.current, panelRef.current)\r\n ) {\r\n close();\r\n }\r\n }\r\n\r\n function onDocKeyDown(event: globalThis.KeyboardEvent) {\r\n if (event.key !== \"Escape\") return;\r\n event.preventDefault();\r\n // Drill back: year → month → day → close\r\n setPanelMode((mode) => {\r\n if (mode === \"year\") return \"month\";\r\n if (mode === \"month\") return \"day\";\r\n close();\r\n triggerRef.current?.focus();\r\n return mode;\r\n });\r\n }\r\n\r\n document.addEventListener(\"mousedown\", onPointerDown);\r\n document.addEventListener(\"keydown\", onDocKeyDown);\r\n return () => {\r\n document.removeEventListener(\"mousedown\", onPointerDown);\r\n document.removeEventListener(\"keydown\", onDocKeyDown);\r\n };\r\n }, [close, open, panelExiting]);\r\n\r\n useEffect(() => {\r\n if (!open || panelExiting || panelMode !== \"day\") return;\r\n const el = panelRef.current?.querySelector(\r\n `[data-iso=\"${focusISO}\"]`,\r\n );\r\n el?.focus({ preventScroll: true });\r\n }, [focusISO, open, panelExiting, panelMode, view.y, view.m]);\r\n\r\n function selectISO(iso: string) {\r\n if (isDisabledISO(iso, min, max)) return;\r\n\r\n if (!isRange) {\r\n commit(iso);\r\n close();\r\n triggerRef.current?.focus();\r\n return;\r\n }\r\n\r\n // Range: first click = start; second = end (swap if needed), then close.\r\n if (!rangeDraft || !rangeDraft.start || rangeDraft.end) {\r\n setRangeDraft({ start: iso, end: \"\" });\r\n setFocusISO(iso);\r\n return;\r\n }\r\n\r\n const completed = commitRange({ start: rangeDraft.start, end: iso });\r\n setRangeDraft(null);\r\n setFocusISO(completed.end || completed.start || iso);\r\n close();\r\n triggerRef.current?.focus();\r\n }\r\n\r\n /** Keep focus day when jumping month/year (clamp day into new month). */\r\n function jumpTo(y: number, m: number) {\r\n const focus = parseISO(focusISO) ?? parseISO(today)!;\r\n const d = clampDayInMonth(y, m, focus.d);\r\n let iso = toISO(y, m, d);\r\n if (isDisabledISO(iso, min, max)) {\r\n // First enabled day in that month\r\n const last = new Date(y, m + 1, 0).getDate();\r\n let found = false;\r\n for (let day = 1; day <= last; day += 1) {\r\n const candidate = toISO(y, m, day);\r\n if (!isDisabledISO(candidate, min, max)) {\r\n iso = candidate;\r\n found = true;\r\n break;\r\n }\r\n }\r\n if (!found) return;\r\n }\r\n setView({ y, m });\r\n setFocusISO(iso);\r\n }\r\n\r\n function selectMonth(m: number) {\r\n if (isMonthDisabled(view.y, m, min, max)) return;\r\n jumpTo(view.y, m);\r\n setPanelMode(\"day\");\r\n }\r\n\r\n function selectYear(y: number) {\r\n if (isYearDisabled(y, min, max)) return;\r\n const m = view.m;\r\n // If current month is invalid in that year, pick first valid month\r\n if (!isMonthDisabled(y, m, min, max)) {\r\n jumpTo(y, m);\r\n } else {\r\n for (let month = 0; month < 12; month += 1) {\r\n if (!isMonthDisabled(y, month, min, max)) {\r\n jumpTo(y, month);\r\n break;\r\n }\r\n }\r\n setView((v) => ({ ...v, y }));\r\n }\r\n setPanelMode(\"month\");\r\n }\r\n\r\n function moveFocus(deltaDays: number) {\r\n const cur = parseISO(focusISO) ?? parseISO(today)!;\r\n let next = addDays(cur, deltaDays);\r\n let iso = toISO(next.y, next.m, next.d);\r\n let guard = 0;\r\n while (isDisabledISO(iso, min, max) && guard < 400) {\r\n next = addDays(next, deltaDays > 0 ? 1 : -1);\r\n iso = toISO(next.y, next.m, next.d);\r\n guard += 1;\r\n }\r\n if (isDisabledISO(iso, min, max)) return;\r\n setFocusISO(iso);\r\n setView({ y: next.y, m: next.m });\r\n }\r\n\r\n function shiftByNav(delta: number) {\r\n if (panelMode === \"year\") {\r\n setYearPageStart((s) => s + delta * YEAR_PAGE);\r\n return;\r\n }\r\n if (panelMode === \"month\") {\r\n // Prev/next year while picking month\r\n const y = view.y + delta;\r\n if (isYearDisabled(y, min, max)) return;\r\n setView((v) => ({ ...v, y }));\r\n setYearPageStart(Math.floor(y / YEAR_PAGE) * YEAR_PAGE);\r\n return;\r\n }\r\n // day mode — shift month\r\n const focus = parseISO(focusISO) ?? parseISO(today)!;\r\n const shifted = addMonths(focus, delta);\r\n jumpTo(shifted.y, shifted.m);\r\n }\r\n\r\n function onTriggerKeyDown(event: KeyboardEvent) {\r\n if (disabled) return;\r\n\r\n if (\r\n event.key === \"ArrowDown\" ||\r\n event.key === \"ArrowUp\" ||\r\n event.key === \"Enter\" ||\r\n event.key === \" \"\r\n ) {\r\n event.preventDefault();\r\n if (!open) openPanel();\r\n }\r\n }\r\n\r\n function onDayKeyDown(event: KeyboardEvent, iso: string) {\r\n if (event.key === \"ArrowLeft\") {\r\n event.preventDefault();\r\n moveFocus(-1);\r\n } else if (event.key === \"ArrowRight\") {\r\n event.preventDefault();\r\n moveFocus(1);\r\n } else if (event.key === \"ArrowUp\") {\r\n event.preventDefault();\r\n moveFocus(-7);\r\n } else if (event.key === \"ArrowDown\") {\r\n event.preventDefault();\r\n moveFocus(7);\r\n } else if (event.key === \"PageUp\") {\r\n event.preventDefault();\r\n shiftByNav(event.shiftKey ? -12 : -1);\r\n } else if (event.key === \"PageDown\") {\r\n event.preventDefault();\r\n shiftByNav(event.shiftKey ? 12 : 1);\r\n } else if (event.key === \"Home\") {\r\n event.preventDefault();\r\n const start = toISO(view.y, view.m, 1);\r\n if (!isDisabledISO(start, min, max)) setFocusISO(start);\r\n } else if (event.key === \"End\") {\r\n event.preventDefault();\r\n const lastDay = new Date(view.y, view.m + 1, 0).getDate();\r\n const end = toISO(view.y, view.m, lastDay);\r\n if (!isDisabledISO(end, min, max)) setFocusISO(end);\r\n } else if (event.key === \"Enter\" || event.key === \" \") {\r\n event.preventDefault();\r\n selectISO(iso);\r\n }\r\n }\r\n\r\n const display = isRange\r\n ? formatRangeDisplay(activeRange)\r\n : current && parseISO(current)\r\n ? formatDisplay(current)\r\n : null;\r\n\r\n const navPrevLabel =\r\n panelMode === \"year\"\r\n ? \"Previous years\"\r\n : panelMode === \"month\"\r\n ? \"Previous year\"\r\n : \"Previous month\";\r\n const navNextLabel =\r\n panelMode === \"year\"\r\n ? \"Next years\"\r\n : panelMode === \"month\"\r\n ? \"Next year\"\r\n : \"Next month\";\r\n\r\n const headerTitle =\r\n panelMode === \"year\"\r\n ? `${yearPageStart} – ${yearPageStart + YEAR_PAGE - 1}`\r\n : panelMode === \"month\"\r\n ? String(view.y)\r\n : null;\r\n\r\n const control = (\r\n \r\n {name ? (\r\n \r\n ) : null}\r\n {isRange && nameEnd ? (\r\n \r\n ) : null}\r\n\r\n (open ? close() : openPanel())}\r\n onKeyDown={onTriggerKeyDown}\r\n >\r\n \r\n {display ?? resolvedPlaceholder}\r\n \r\n \r\n \r\n \r\n \r\n\r\n {panelMounted && portalReady\r\n ? createPortal(\r\n \r\n
\r\n shiftByNav(-1)}\r\n >\r\n \r\n \r\n\r\n {panelMode === \"day\" ? (\r\n
\r\n setPanelMode(\"month\")}\r\n >\r\n {formatMonthName(view.m)}\r\n \r\n {\r\n setYearPageStart(\r\n Math.floor(view.y / YEAR_PAGE) * YEAR_PAGE,\r\n );\r\n setPanelMode(\"year\");\r\n }}\r\n >\r\n {view.y}\r\n \r\n
\r\n ) : (\r\n \r\n setPanelMode(panelMode === \"year\" ? \"month\" : \"day\")\r\n }\r\n >\r\n {headerTitle}\r\n \r\n )}\r\n\r\n shiftByNav(1)}\r\n >\r\n \r\n \r\n
\r\n\r\n {panelMode === \"day\" ? (\r\n <>\r\n {isRange ? (\r\n

\r\n {activeRange.start && !activeRange.end\r\n ? \"Select end date\"\r\n : \"Select start date\"}\r\n

\r\n ) : null}\r\n
\r\n {WEEKDAYS.map((wd) => (\r\n \r\n {wd}\r\n \r\n ))}\r\n
\r\n\r\n
\r\n {Array.from({ length: cells.length / 7 }, (_, week) => (\r\n
\r\n {cells.slice(week * 7, week * 7 + 7).map((cell) => {\r\n const rangeStart =\r\n isRange &&\r\n Boolean(activeRange.start) &&\r\n cell.iso === activeRange.start;\r\n const rangeEnd =\r\n isRange &&\r\n Boolean(activeRange.end) &&\r\n cell.iso === activeRange.end;\r\n const inRange =\r\n isRange &&\r\n Boolean(activeRange.start && activeRange.end) &&\r\n isInRangeISO(\r\n cell.iso,\r\n activeRange.start,\r\n activeRange.end,\r\n );\r\n // Preview span while choosing end: start → focusISO\r\n const draftStart = rangeDraft?.start;\r\n const inDraftPreview =\r\n isRange &&\r\n Boolean(draftStart) &&\r\n !rangeDraft?.end &&\r\n Boolean(focusISO) &&\r\n isInRangeISO(\r\n cell.iso,\r\n compareISO(draftStart!, focusISO) <= 0\r\n ? draftStart!\r\n : focusISO,\r\n compareISO(draftStart!, focusISO) <= 0\r\n ? focusISO\r\n : draftStart!,\r\n );\r\n const selectedDay = isRange\r\n ? rangeStart || rangeEnd\r\n : cell.iso === current;\r\n const isToday = cell.iso === today;\r\n const focused = cell.iso === focusISO;\r\n const dayDisabled = isDisabledISO(cell.iso, min, max);\r\n\r\n return (\r\n selectISO(cell.iso)}\r\n onKeyDown={(event) => onDayKeyDown(event, cell.iso)}\r\n onFocus={() => setFocusISO(cell.iso)}\r\n >\r\n {cell.day}\r\n \r\n );\r\n })}\r\n
\r\n ))}\r\n
\r\n \r\n ) : null}\r\n\r\n {panelMode === \"month\" ? (\r\n \r\n {Array.from({ length: 12 }, (_, m) => {\r\n const monthDisabled = isMonthDisabled(view.y, m, min, max);\r\n const isSelected = Boolean(\r\n isRange\r\n ? (activeRange.start &&\r\n parseISO(activeRange.start)?.y === view.y &&\r\n parseISO(activeRange.start)?.m === m) ||\r\n (activeRange.end &&\r\n parseISO(activeRange.end)?.y === view.y &&\r\n parseISO(activeRange.end)?.m === m)\r\n : selected?.y === view.y && selected?.m === m,\r\n );\r\n const isCurrent =\r\n todayYmd.y === view.y && todayYmd.m === m;\r\n return (\r\n selectMonth(m)}\r\n >\r\n {formatMonthShort(m)}\r\n \r\n );\r\n })}\r\n \r\n ) : null}\r\n\r\n {panelMode === \"year\" ? (\r\n \r\n {years.map((y) => {\r\n const yearDisabled = isYearDisabled(y, min, max);\r\n const isSelected = Boolean(\r\n isRange\r\n ? parseISO(activeRange.start)?.y === y ||\r\n parseISO(activeRange.end)?.y === y\r\n : selected?.y === y,\r\n );\r\n const isCurrent = todayYmd.y === y;\r\n return (\r\n selectYear(y)}\r\n >\r\n {y}\r\n \r\n );\r\n })}\r\n \r\n ) : null}\r\n ,\r\n document.body,\r\n )\r\n : null}\r\n \r\n );\r\n\r\n if (!label && !hint && !error) {\r\n return control;\r\n }\r\n\r\n return (\r\n
\r\n {label ? (\r\n \r\n ) : null}\r\n {control}\r\n {hint && !error ? (\r\n

\r\n {hint}\r\n

\r\n ) : null}\r\n {error ? (\r\n

\r\n {error}\r\n

\r\n ) : null}\r\n
\r\n );\r\n}\r\n", + "content": "\"use client\";\n\nimport { cn } from \"../lib/cn\";\nimport { MOTION_DEFAULTS, type DatePickerMotion } from \"../lib/motion\";\nimport { exitDurationForMotion, usePresence } from \"../lib/presence\";\nimport {\n eventInside,\n useFloatingPortal,\n} from \"../lib/use-floating-portal\";\nimport {\n Calendar,\n compareISODate,\n parseISODate,\n type DateRangeValue,\n} from \"./calendar\";\nimport {\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n type KeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\n\nexport type DatePickerSize = \"sm\" | \"md\" | \"lg\";\nexport type { DatePickerMotion };\nexport type { DateRangeValue };\nexport type DatePickerMode = \"single\" | \"range\";\n\nexport type DatePickerProps = {\n mode?: DatePickerMode;\n value?: string;\n defaultValue?: string;\n onValueChange?: (value: string) => void;\n rangeValue?: DateRangeValue;\n defaultRangeValue?: DateRangeValue;\n onRangeValueChange?: (value: DateRangeValue) => void;\n placeholder?: string;\n size?: DatePickerSize;\n motion?: DatePickerMotion;\n label?: ReactNode;\n hint?: ReactNode;\n error?: ReactNode;\n disabled?: boolean;\n min?: string;\n max?: string;\n name?: string;\n nameEnd?: string;\n id?: string;\n className?: string;\n placement?: \"auto\" | \"bottom\" | \"top\";\n \"aria-label\"?: string;\n};\n\nconst sizeClass: Record = {\n sm: \"sg-datepicker-trigger-sm\",\n md: \"\",\n lg: \"sg-datepicker-trigger-lg\",\n};\n\nconst EMPTY_RANGE: DateRangeValue = { start: \"\", end: \"\" };\n\nfunction clampISO(iso: string, min?: string, max?: string): string {\n let next = iso;\n if (min && compareISODate(next, min) < 0) next = min;\n if (max && compareISODate(next, max) > 0) next = max;\n return next;\n}\n\nfunction formatDisplay(iso: string): string {\n const ymd = parseISODate(iso);\n if (!ymd) return iso;\n try {\n return new Date(ymd.y, ymd.m, ymd.d).toLocaleDateString(undefined, {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n });\n } catch {\n return iso;\n }\n}\n\nfunction formatRangeDisplay(range: DateRangeValue): string | null {\n const startOk = Boolean(range.start && parseISODate(range.start));\n const endOk = Boolean(range.end && parseISODate(range.end));\n if (startOk && endOk) {\n return `${formatDisplay(range.start)} – ${formatDisplay(range.end)}`;\n }\n if (startOk) return `${formatDisplay(range.start)} – …`;\n return null;\n}\n\nfunction CalendarIcon() {\n return (\n \n \n \n \n \n );\n}\n\n/**\n * Atom — DatePicker\n * Solid trigger + frost panel. Panel body is the shared `Calendar` primitive.\n * Values: ISO `YYYY-MM-DD` (local; no time engine).\n */\nexport function DatePicker({\n mode = \"single\",\n value,\n defaultValue = \"\",\n onValueChange,\n rangeValue,\n defaultRangeValue = EMPTY_RANGE,\n onRangeValueChange,\n placeholder,\n size = \"md\",\n motion = MOTION_DEFAULTS.datePicker,\n label,\n hint,\n error,\n disabled,\n min,\n max,\n name,\n nameEnd,\n id,\n className,\n placement = \"auto\",\n \"aria-label\": ariaLabel,\n}: DatePickerProps) {\n const isRange = mode === \"range\";\n const resolvedPlaceholder =\n placeholder ?? (isRange ? \"Pick a date range…\" : \"Pick a date…\");\n\n const reactId = useId();\n const pickerId = id ?? `sg-datepicker-${reactId}`;\n const panelId = `${pickerId}-panel`;\n const hintId = hint ? `${pickerId}-hint` : undefined;\n const errorId = error ? `${pickerId}-error` : undefined;\n const describedBy = [hintId, errorId].filter(Boolean).join(\" \") || undefined;\n const invalid = Boolean(error);\n\n const isControlled = value !== undefined;\n const [uncontrolled, setUncontrolled] = useState(defaultValue);\n const current = isControlled ? value! : uncontrolled;\n\n const isRangeControlled = rangeValue !== undefined;\n const [uncontrolledRange, setUncontrolledRange] =\n useState(defaultRangeValue);\n const committedRange = isRangeControlled\n ? (rangeValue ?? EMPTY_RANGE)\n : uncontrolledRange;\n\n const [open, setOpen] = useState(false);\n const { mounted: panelMounted, exiting: panelExiting, state: panelState } =\n usePresence(open, { durationMs: exitDurationForMotion(motion) });\n\n const rootRef = useRef(null);\n const triggerRef = useRef(null);\n const panelRef = useRef(null);\n\n const { portalReady, floatingStyle, side: menuPlacement } = useFloatingPortal({\n open,\n mounted: panelMounted,\n triggerRef,\n panelRef,\n placement,\n align: \"start\",\n matchWidth: false,\n flipMinSpace: 320,\n gap: 6,\n });\n\n const commit = useCallback(\n (next: string) => {\n const clamped = clampISO(next, min, max);\n if (!isControlled) setUncontrolled(clamped);\n onValueChange?.(clamped);\n },\n [isControlled, max, min, onValueChange],\n );\n\n const commitRange = useCallback(\n (next: DateRangeValue) => {\n const start = next.start ? clampISO(next.start, min, max) : \"\";\n const end = next.end ? clampISO(next.end, min, max) : \"\";\n let range: DateRangeValue = { start, end };\n if (start && end && compareISODate(end, start) < 0) {\n range = { start: end, end: start };\n }\n if (!isRangeControlled) setUncontrolledRange(range);\n onRangeValueChange?.(range);\n },\n [isRangeControlled, max, min, onRangeValueChange],\n );\n\n const close = useCallback(() => {\n setOpen(false);\n }, []);\n\n const openPanel = useCallback(() => {\n if (disabled) return;\n setOpen(true);\n }, [disabled]);\n\n useEffect(() => {\n if (!open || panelExiting) return;\n\n function onPointerDown(event: MouseEvent) {\n if (!eventInside(event.target, rootRef.current, panelRef.current)) {\n close();\n }\n }\n\n function onDocKeyDown(event: globalThis.KeyboardEvent) {\n if (event.key !== \"Escape\") return;\n event.preventDefault();\n close();\n triggerRef.current?.focus();\n }\n\n document.addEventListener(\"mousedown\", onPointerDown);\n document.addEventListener(\"keydown\", onDocKeyDown);\n return () => {\n document.removeEventListener(\"mousedown\", onPointerDown);\n document.removeEventListener(\"keydown\", onDocKeyDown);\n };\n }, [close, open, panelExiting]);\n\n function onTriggerKeyDown(event: KeyboardEvent) {\n if (disabled) return;\n if (\n event.key === \"ArrowDown\" ||\n event.key === \"ArrowUp\" ||\n event.key === \"Enter\" ||\n event.key === \" \"\n ) {\n event.preventDefault();\n if (!open) openPanel();\n }\n }\n\n const display = isRange\n ? formatRangeDisplay(committedRange)\n : current && parseISODate(current)\n ? formatDisplay(current)\n : null;\n\n const control = (\n \n {name ? (\n \n ) : null}\n {isRange && nameEnd ? (\n \n ) : null}\n\n (open ? close() : openPanel())}\n onKeyDown={onTriggerKeyDown}\n >\n \n {display ?? resolvedPlaceholder}\n \n \n \n \n \n\n {panelMounted && portalReady\n ? createPortal(\n \n {\n close();\n triggerRef.current?.focus();\n }}\n />\n ,\n document.body,\n )\n : null}\n \n );\n\n if (!label && !hint && !error) return control;\n\n return (\n
\n {label ? (\n \n ) : null}\n {control}\n {hint && !error ? (\n

\n {hint}\n

\n ) : null}\n {error ? (\n

\n {error}\n

\n ) : null}\n
\n );\n}\n", "type": "registry:ui", "target": "components/softglass/atoms/date-picker.tsx" }, diff --git a/apps/web/public/r/registry.json b/apps/web/public/r/registry.json index 752b0cc..e16ea94 100644 --- a/apps/web/public/r/registry.json +++ b/apps/web/public/r/registry.json @@ -447,13 +447,45 @@ } ] }, + { + "name": "calendar", + "type": "registry:ui", + "title": "Calendar", + "description": "Inline day/month/year grid — single or range ISO dates.", + "registryDependencies": [ + "tokens" + ], + "files": [ + { + "path": "packages/ui/src/atoms/calendar.tsx", + "type": "registry:ui", + "target": "components/softglass/atoms/calendar.tsx" + }, + { + "path": "packages/ui/src/lib/cn.ts", + "type": "registry:lib", + "target": "components/softglass/lib/cn.ts" + }, + { + "path": "packages/ui/src/lib/looks.ts", + "type": "registry:lib", + "target": "components/softglass/lib/looks.ts" + }, + { + "path": "packages/ui/src/lib/motion.ts", + "type": "registry:lib", + "target": "components/softglass/lib/motion.ts" + } + ] + }, { "name": "date-picker", "type": "registry:ui", "title": "Date Picker", - "description": "Single ISO date calendar — body portal + flip/clamp panel.", + "description": "Single or range ISO dates — portal panel over Calendar.", "registryDependencies": [ - "tokens" + "tokens", + "calendar" ], "files": [ { @@ -493,6 +525,53 @@ } ] }, + { + "name": "time-picker", + "type": "registry:ui", + "title": "Time Picker", + "description": "HH:mm trigger + panel with TimeInput and presets.", + "registryDependencies": [ + "tokens", + "time-input" + ], + "files": [ + { + "path": "packages/ui/src/atoms/time-picker.tsx", + "type": "registry:ui", + "target": "components/softglass/atoms/time-picker.tsx" + }, + { + "path": "packages/ui/src/lib/cn.ts", + "type": "registry:lib", + "target": "components/softglass/lib/cn.ts" + }, + { + "path": "packages/ui/src/lib/looks.ts", + "type": "registry:lib", + "target": "components/softglass/lib/looks.ts" + }, + { + "path": "packages/ui/src/lib/motion.ts", + "type": "registry:lib", + "target": "components/softglass/lib/motion.ts" + }, + { + "path": "packages/ui/src/lib/presence.ts", + "type": "registry:lib", + "target": "components/softglass/lib/presence.ts" + }, + { + "path": "packages/ui/src/lib/floating.ts", + "type": "registry:lib", + "target": "components/softglass/lib/floating.ts" + }, + { + "path": "packages/ui/src/lib/use-floating-portal.ts", + "type": "registry:lib", + "target": "components/softglass/lib/use-floating-portal.ts" + } + ] + }, { "name": "tooltip", "type": "registry:ui", diff --git a/apps/web/public/r/time-picker.json b/apps/web/public/r/time-picker.json new file mode 100644 index 0000000..8fd08f8 --- /dev/null +++ b/apps/web/public/r/time-picker.json @@ -0,0 +1,55 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "time-picker", + "title": "Time Picker", + "description": "HH:mm trigger + panel with TimeInput and presets.", + "registryDependencies": [ + "tokens", + "time-input" + ], + "files": [ + { + "path": "packages/ui/src/atoms/time-picker.tsx", + "content": "\"use client\";\n\nimport { cn } from \"../lib/cn\";\nimport { MOTION_DEFAULTS, type DatePickerMotion } from \"../lib/motion\";\nimport { exitDurationForMotion, usePresence } from \"../lib/presence\";\nimport {\n eventInside,\n useFloatingPortal,\n} from \"../lib/use-floating-portal\";\nimport { TimeInput, type TimeInputLook, type TimeInputSize } from \"./time-input\";\nimport {\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n type KeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\n\nexport type TimePickerSize = TimeInputSize;\nexport type TimePickerLook = TimeInputLook;\nexport type TimePickerMotion = DatePickerMotion;\n\nexport type TimePickerProps = {\n value?: string;\n defaultValue?: string;\n onValueChange?: (value: string) => void;\n /** Quick picks under the control (HH:mm 24h). */\n presets?: string[];\n size?: TimePickerSize;\n look?: TimePickerLook;\n motion?: TimePickerMotion;\n hourCycle?: 24 | 12;\n minuteStep?: number;\n label?: ReactNode;\n hint?: ReactNode;\n error?: ReactNode;\n disabled?: boolean;\n placeholder?: string;\n name?: string;\n id?: string;\n className?: string;\n placement?: \"auto\" | \"bottom\" | \"top\";\n \"aria-label\"?: string;\n};\n\nconst DEFAULT_PRESETS = [\"09:00\", \"12:00\", \"13:00\", \"17:00\", \"18:00\"] as const;\n\nconst sizeClass: Record = {\n sm: \"sg-datepicker-trigger-sm\",\n md: \"\",\n lg: \"sg-datepicker-trigger-lg\",\n};\n\nfunction ClockIcon() {\n return (\n \n \n \n \n );\n}\n\n/**\n * Atom — TimePicker\n * Trigger + portaled panel with TimeInput and optional presets.\n * Value is always 24h `HH:mm`. No timezone engine.\n */\nexport function TimePicker({\n value,\n defaultValue = \"09:00\",\n onValueChange,\n presets = [...DEFAULT_PRESETS],\n size = \"md\",\n look = \"soft\",\n motion = MOTION_DEFAULTS.datePicker,\n hourCycle = 24,\n minuteStep = 5,\n label,\n hint,\n error,\n disabled,\n placeholder = \"Pick a time…\",\n name,\n id,\n className,\n placement = \"auto\",\n \"aria-label\": ariaLabel,\n}: TimePickerProps) {\n const reactId = useId();\n const pickerId = id ?? `sg-timepicker-${reactId}`;\n const panelId = `${pickerId}-panel`;\n const hintId = hint ? `${pickerId}-hint` : undefined;\n const errorId = error ? `${pickerId}-error` : undefined;\n const describedBy = [hintId, errorId].filter(Boolean).join(\" \") || undefined;\n const invalid = Boolean(error);\n\n const isControlled = value !== undefined;\n const [uncontrolled, setUncontrolled] = useState(defaultValue);\n const current = isControlled ? value! : uncontrolled;\n\n const [open, setOpen] = useState(false);\n const [draft, setDraft] = useState(current);\n const { mounted: panelMounted, exiting: panelExiting, state: panelState } =\n usePresence(open, { durationMs: exitDurationForMotion(motion) });\n\n const rootRef = useRef(null);\n const triggerRef = useRef(null);\n const panelRef = useRef(null);\n\n const { portalReady, floatingStyle, side: menuPlacement } = useFloatingPortal({\n open,\n mounted: panelMounted,\n triggerRef,\n panelRef,\n placement,\n align: \"start\",\n matchWidth: false,\n flipMinSpace: 240,\n gap: 6,\n });\n\n const commit = useCallback(\n (next: string) => {\n if (!isControlled) setUncontrolled(next);\n onValueChange?.(next);\n },\n [isControlled, onValueChange],\n );\n\n const close = useCallback(() => setOpen(false), []);\n\n const openPanel = useCallback(() => {\n if (disabled) return;\n setDraft(current);\n setOpen(true);\n }, [current, disabled]);\n\n useEffect(() => {\n if (!open || panelExiting) return;\n\n function onPointerDown(event: MouseEvent) {\n if (!eventInside(event.target, rootRef.current, panelRef.current)) {\n close();\n }\n }\n\n function onDocKeyDown(event: globalThis.KeyboardEvent) {\n if (event.key !== \"Escape\") return;\n event.preventDefault();\n close();\n triggerRef.current?.focus();\n }\n\n document.addEventListener(\"mousedown\", onPointerDown);\n document.addEventListener(\"keydown\", onDocKeyDown);\n return () => {\n document.removeEventListener(\"mousedown\", onPointerDown);\n document.removeEventListener(\"keydown\", onDocKeyDown);\n };\n }, [close, open, panelExiting]);\n\n function onTriggerKeyDown(event: KeyboardEvent) {\n if (disabled) return;\n if (\n event.key === \"ArrowDown\" ||\n event.key === \"Enter\" ||\n event.key === \" \"\n ) {\n event.preventDefault();\n if (!open) openPanel();\n }\n }\n\n function applyDraft() {\n commit(draft);\n close();\n triggerRef.current?.focus();\n }\n\n function pickPreset(hhmm: string) {\n setDraft(hhmm);\n commit(hhmm);\n close();\n triggerRef.current?.focus();\n }\n\n const control = (\n
\n {name ? : null}\n (open ? close() : openPanel())}\n onKeyDown={onTriggerKeyDown}\n >\n \n {current || placeholder}\n \n \n \n \n \n\n {panelMounted && portalReady\n ? createPortal(\n \n \n {presets.length > 0 ? (\n
\n {presets.map((p) => (\n pickPreset(p)}\n >\n {p}\n \n ))}\n
\n ) : null}\n
\n \n Apply\n \n
\n
,\n document.body,\n )\n : null}\n \n );\n\n if (!label && !hint && !error) return control;\n\n return (\n
\n {label ? (\n \n ) : null}\n {control}\n {hint && !error ? (\n

\n {hint}\n

\n ) : null}\n {error ? (\n

\n {error}\n

\n ) : null}\n
\n );\n}\n", + "type": "registry:ui", + "target": "components/softglass/atoms/time-picker.tsx" + }, + { + "path": "packages/ui/src/lib/cn.ts", + "content": "/** Values allowed in `cn(...)` — only strings survive. */\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\ntype ClassValue = string | false | null | undefined | 0 | 0n | boolean;\r\n\r\n/**\r\n * Tiny class merger — no runtime deps.\r\n * Prefer this over pulling tailwind-merge until we need conflict resolution.\r\n */\r\nexport function cn(...parts: ClassValue[]): string {\r\n return parts.filter((part): part is string => typeof part === \"string\" && part.length > 0).join(\" \");\r\n}\r\n", + "type": "registry:lib", + "target": "components/softglass/lib/cn.ts" + }, + { + "path": "packages/ui/src/lib/looks.ts", + "content": "/** Shared visual “look” languages across controls. */\r\n\r\nexport type ButtonLook = \"solid\" | \"soft\" | \"glass\" | \"gradient\" | \"neon\";\r\n\r\nexport type RadioLook = \"dot\" | \"card\" | \"chip\";\r\n\r\nexport type CheckboxLook = \"box\" | \"card\" | \"pill\";\r\n\r\nexport type SwitchLook = \"track\" | \"ios\" | \"minimal\";\r\n\r\nexport type BadgeLook = \"soft\" | \"solid\" | \"outline\" | \"glow\";\r\n\r\nexport type FieldLook = \"solid\" | \"underline\" | \"filled\" | \"ghost\";\r\n\r\nexport type SelectLook = \"solid\" | \"soft\" | \"glass\" | \"gradient\";\r\n\r\nexport type TabsLook = \"pill\" | \"underline\" | \"segmented\";\r\n\r\nexport type AvatarLook = \"circle\" | \"rounded\" | \"soft\";\r\n\r\nexport type TooltipLook = \"frost\" | \"solid\" | \"accent\";\r\n\r\nexport type CardLook = \"flat\" | \"raised\" | \"outline\" | \"glow\";\r\n\r\n/** NumberInput shell + stepper language */\r\nexport type NumberInputLook = \"soft\" | \"solid\" | \"outline\" | \"ghost\";\r\n\r\n/** Slider track / thumb language */\r\nexport type SliderLook = \"soft\" | \"solid\" | \"glass\" | \"accent\";\r\n\r\n/** FileField trigger surface */\r\nexport type FileFieldLook = \"solid\" | \"soft\" | \"dashed\" | \"ghost\";\r\n\r\n/** Progress track / fill language */\r\nexport type ProgressLook = \"soft\" | \"solid\" | \"glass\" | \"accent\" | \"striped\";\r\n\r\n/** StatusDot chrome language (color still from status / color) */\r\nexport type StatusDotLook = \"soft\" | \"solid\" | \"outline\" | \"glow\";\r\n\r\n/** Chip surface language */\r\nexport type ChipLook = \"soft\" | \"solid\" | \"outline\" | \"glass\" | \"glow\";\r\n\r\n/** CloseButton chrome */\r\nexport type CloseButtonLook = \"ghost\" | \"soft\" | \"solid\" | \"danger\";\r\n\r\n/**\r\n * Collapsible / Accordion disclosure surface\r\n * soft = frost card · solid = long content · glass = chrome · outline = border only · ghost = flush list\r\n */\r\nexport type CollapsibleLook = \"soft\" | \"solid\" | \"glass\" | \"outline\" | \"ghost\";\r\n\r\nexport type AccordionLook = CollapsibleLook;\r\n\r\n/** Breadcrumb chrome: plain path · soft tray · pill chips */\r\nexport type BreadcrumbLook = \"plain\" | \"soft\" | \"pill\";\r\n\r\n/** Pagination control chrome */\r\nexport type PaginationLook = \"soft\" | \"solid\" | \"ghost\" | \"glass\";\r\n\r\n/** EmptyState surface */\r\nexport type EmptyStateLook = \"solid\" | \"soft\" | \"outline\";\r\n\r\n/**\r\n * Stepper language\r\n * soft = frost track · solid = solid rail · outline = line only\r\n * dots = progress line + large dots · pills = segmented chip row\r\n */\r\nexport type StepperLook = \"soft\" | \"solid\" | \"outline\" | \"dots\" | \"pills\";\r\n\r\n/**\r\n * Toolbar strip\r\n * soft | solid | glass | ghost | accent (accent edge rail)\r\n */\r\nexport type ToolbarLook = \"soft\" | \"solid\" | \"glass\" | \"ghost\" | \"accent\";\r\n\r\n/** List host surface */\r\nexport type ListLook = \"soft\" | \"solid\" | \"outline\" | \"ghost\" | \"inset\";\r\n\r\n/**\r\n * Stat metric tile\r\n * solid | soft | glass | outline | accent (left rail)\r\n */\r\nexport type StatLook = \"solid\" | \"soft\" | \"glass\" | \"outline\" | \"accent\";\r\n\r\n/** PageHeader chrome: plain flush · soft frost · solid panel · glass chrome */\r\nexport type PageHeaderLook = \"plain\" | \"soft\" | \"solid\" | \"glass\";\r\n\r\n/** SettingsSection panel chrome */\r\nexport type SettingsSectionLook = \"soft\" | \"solid\" | \"glass\" | \"plain\";\r\n\r\n/**\r\n * DataTable surface language\r\n * soft = frost · solid = panel · glass = chrome · outline = border · ghost = flush\r\n */\r\nexport type DataTableLook = \"soft\" | \"solid\" | \"glass\" | \"outline\" | \"ghost\";\r\n", + "type": "registry:lib", + "target": "components/softglass/lib/looks.ts" + }, + { + "path": "packages/ui/src/lib/motion.ts", + "content": "/**\r\n * Motion recipes — component-specific animation menus.\r\n * Parallel to `look` (visual surface). Always pair with `prefers-reduced-motion`.\r\n *\r\n * Defaults match the pre-motion global micro-interactions so existing UIs\r\n * keep the same feel when no prop is passed.\r\n */\r\n\r\n/** Shared “off” token used on every control. */\r\nexport type MotionNone = \"none\";\r\n\r\nexport type ButtonMotion = MotionNone | \"lift\" | \"press\" | \"sheen\" | \"ripple\";\r\n\r\nexport type CheckboxMotion = MotionNone | \"pop\" | \"draw\" | \"fade-in\" | \"bounce\";\r\n\r\nexport type RadioMotion = MotionNone | \"pop\" | \"dot-scale\" | \"ring-expand\";\r\n\r\nexport type SwitchMotion = MotionNone | \"snap\" | \"spring\" | \"elastic\";\r\n\r\nexport type FieldMotion = MotionNone | \"ring\" | \"underline-grow\" | \"glow\";\r\n\r\nexport type SelectMotion = MotionNone | \"fade\" | \"scale\" | \"slide-down\";\r\n\r\nexport type TooltipMotion = MotionNone | \"fade\" | \"scale\" | \"slide\";\r\n\r\nexport type BadgeMotion = MotionNone | \"pulse\" | \"pop-in\" | \"shimmer\";\r\n\r\nexport type AvatarMotion = MotionNone | \"lift\" | \"ring-pulse\" | \"status-ping\";\r\n\r\n/**\r\n * Tabs indicator + content enter.\r\n * - none: instant active swap\r\n * - fade: soft cross-fade on active chrome / content\r\n * - slide: moving pill or underline indicator (content still fades)\r\n */\r\nexport type TabsMotion = MotionNone | \"fade\" | \"slide\";\r\n\r\nexport type ModalMotion = MotionNone | \"scale\" | \"fade\" | \"slide-up\";\r\n\r\nexport type ToastMotion = MotionNone | \"slide-in\" | \"pop\";\r\n\r\nexport type CardMotion = MotionNone | \"lift\" | \"glow-pulse\";\r\n\r\n/** Popover / DropdownMenu panel enter (same recipe set as Select menu). */\r\nexport type PopoverMotion = MotionNone | \"fade\" | \"scale\" | \"slide-down\";\r\n\r\nexport type DropdownMenuMotion = MotionNone | \"fade\" | \"scale\" | \"slide-down\";\r\n\r\n/** ContextMenu panel — same recipes as DropdownMenu / Popover. */\r\nexport type ContextMenuMotion = MotionNone | \"fade\" | \"scale\" | \"slide-down\";\r\n\r\n/** DatePicker calendar panel — same recipes as Select. */\r\nexport type DatePickerMotion = MotionNone | \"fade\" | \"scale\" | \"slide-down\";\r\n\r\n/** Collapsible / Accordion panel open motion. */\r\nexport type CollapsibleMotion = MotionNone | \"fade\" | \"height\";\r\n\r\nexport type AccordionMotion = CollapsibleMotion;\r\n\r\n/** Sheet edge panel enter/exit. */\r\nexport type SheetMotion = MotionNone | \"slide\" | \"fade\";\r\n\r\n/** HoverCard panel (Popover family). */\r\nexport type HoverCardMotion = MotionNone | \"fade\" | \"scale\" | \"slide-down\";\r\n\r\n/** Defaults — preserve current product feel. */\r\nexport const MOTION_DEFAULTS = {\r\n button: \"lift\",\r\n checkbox: \"pop\",\r\n radio: \"pop\",\r\n switch: \"spring\",\r\n field: \"ring\",\r\n select: \"scale\",\r\n tooltip: \"scale\",\r\n badge: \"none\",\r\n avatar: \"lift\",\r\n tabs: \"slide\",\r\n modal: \"scale\",\r\n toast: \"slide-in\",\r\n card: \"lift\",\r\n popover: \"scale\",\r\n dropdownMenu: \"scale\",\r\n contextMenu: \"scale\",\r\n datePicker: \"scale\",\r\n collapsible: \"height\",\r\n accordion: \"height\",\r\n sheet: \"slide\",\r\n hoverCard: \"scale\",\r\n} as const satisfies Record;\r\n", + "type": "registry:lib", + "target": "components/softglass/lib/motion.ts" + }, + { + "path": "packages/ui/src/lib/presence.ts", + "content": "\"use client\";\r\n\r\nimport { useEffect, useState } from \"react\";\r\n\r\n/**\r\n * Unmount delay so CSS exit animations can finish.\r\n * Keep in sync with token exit durations (~`--sg-duration-normal` / motion-med).\r\n */\r\nexport const EXIT_DURATION_MS = 200;\r\n\r\nexport type PresenceOptions = {\r\n /** 0 = unmount immediately (motion=\"none\" or reduced-motion). */\r\n durationMs?: number;\r\n};\r\n\r\nfunction prefersReducedMotion(): boolean {\r\n if (typeof window === \"undefined\") return false;\r\n return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\r\n}\r\n\r\n/** Map motion recipe → exit hold time. */\r\nexport function exitDurationForMotion(motion: string | undefined): number {\r\n if (motion === \"none\") return 0;\r\n if (prefersReducedMotion()) return 0;\r\n return EXIT_DURATION_MS;\r\n}\r\n\r\n/**\r\n * Keep a node mounted while `open` is true **and** briefly after it becomes false\r\n * so exit keyframes can play. Mirrors Radix presence / `data-state` pattern.\r\n */\r\nexport function usePresence(open: boolean, options?: PresenceOptions) {\r\n const durationMs = options?.durationMs ?? EXIT_DURATION_MS;\r\n const [mounted, setMounted] = useState(open);\r\n const [exiting, setExiting] = useState(false);\r\n\r\n useEffect(() => {\r\n if (open) {\r\n setMounted(true);\r\n setExiting(false);\r\n return;\r\n }\r\n\r\n if (!mounted) return;\r\n\r\n if (durationMs <= 0) {\r\n setMounted(false);\r\n setExiting(false);\r\n return;\r\n }\r\n\r\n setExiting(true);\r\n const id = window.setTimeout(() => {\r\n setMounted(false);\r\n setExiting(false);\r\n }, durationMs);\r\n\r\n return () => window.clearTimeout(id);\r\n }, [open, durationMs, mounted]);\r\n\r\n return {\r\n /** Still in the DOM (open or playing exit). */\r\n mounted,\r\n /** Exit animation should run. */\r\n exiting,\r\n /** For `data-state`: open while interactive, closed while exiting. */\r\n state: (open && !exiting ? \"open\" : \"closed\") as \"open\" | \"closed\",\r\n };\r\n}\r\n", + "type": "registry:lib", + "target": "components/softglass/lib/presence.ts" + }, + { + "path": "packages/ui/src/lib/floating.ts", + "content": "/**\r\n * Viewport-aware floating coordinates for portaled overlays.\r\n * No Floating UI dependency — pure getBoundingClientRect math.\r\n */\r\n\r\nexport type FloatingSide = \"top\" | \"bottom\";\r\nexport type FloatingAlign = \"start\" | \"center\" | \"end\";\r\nexport type FloatingPlacement = \"auto\" | FloatingSide;\r\n\r\nexport type FloatingCoords = {\r\n top: number;\r\n left: number;\r\n side: FloatingSide;\r\n /** When matchWidth, panel width equals trigger (clamped to viewport). */\r\n width?: number;\r\n};\r\n\r\nexport type ComputeFloatingArgs = {\r\n trigger: DOMRect;\r\n /** Measured panel size (offsetWidth / offsetHeight). */\r\n panelWidth: number;\r\n panelHeight: number;\r\n placement?: FloatingPlacement;\r\n align?: FloatingAlign;\r\n /** Gap between trigger and panel (px). */\r\n gap?: number;\r\n /** Viewport padding (px). */\r\n pad?: number;\r\n /**\r\n * Stretch panel to trigger width (Select / Combobox / MultiSelect).\r\n * When true, `align` is ignored for horizontal placement.\r\n */\r\n matchWidth?: boolean;\r\n /** Preferred flip threshold when placement is auto. */\r\n flipMinSpace?: number;\r\n};\r\n\r\nfunction clamp(n: number, min: number, max: number) {\r\n return Math.min(Math.max(n, min), max);\r\n}\r\n\r\n/**\r\n * Resolve fixed top/left for a panel relative to a trigger rect.\r\n * Flips top/bottom when needed; clamps left/right into the viewport.\r\n */\r\nexport function computeFloatingPosition({\r\n trigger,\r\n panelWidth,\r\n panelHeight,\r\n placement = \"auto\",\r\n align = \"start\",\r\n gap = 6,\r\n pad = 8,\r\n matchWidth = false,\r\n flipMinSpace,\r\n}: ComputeFloatingArgs): FloatingCoords {\r\n const vw = typeof window !== \"undefined\" ? window.innerWidth : 1024;\r\n const vh = typeof window !== \"undefined\" ? window.innerHeight : 768;\r\n\r\n const width = matchWidth\r\n ? clamp(trigger.width, 0, Math.max(0, vw - pad * 2))\r\n : panelWidth;\r\n\r\n const height = panelHeight > 0 ? panelHeight : flipMinSpace ?? 220;\r\n\r\n const spaceBelow = vh - trigger.bottom - gap;\r\n const spaceAbove = trigger.top - gap;\r\n\r\n let side: FloatingSide;\r\n if (placement === \"top\") side = \"top\";\r\n else if (placement === \"bottom\") side = \"bottom\";\r\n else {\r\n const minSpace = flipMinSpace ?? Math.min(height, 220);\r\n side =\r\n spaceBelow < minSpace && spaceAbove > spaceBelow ? \"top\" : \"bottom\";\r\n }\r\n\r\n // If chosen side still overflows badly, prefer the roomier side.\r\n if (placement === \"auto\") {\r\n if (side === \"bottom\" && spaceBelow < height && spaceAbove > spaceBelow) {\r\n side = \"top\";\r\n } else if (side === \"top\" && spaceAbove < height && spaceBelow > spaceAbove) {\r\n side = \"bottom\";\r\n }\r\n }\r\n\r\n let top =\r\n side === \"bottom\" ? trigger.bottom + gap : trigger.top - height - gap;\r\n\r\n // Keep panel inside vertical viewport when possible.\r\n top = clamp(top, pad, Math.max(pad, vh - height - pad));\r\n\r\n let left: number;\r\n if (matchWidth) {\r\n left = trigger.left;\r\n } else if (align === \"center\") {\r\n left = trigger.left + trigger.width / 2 - width / 2;\r\n } else if (align === \"end\") {\r\n left = trigger.right - width;\r\n } else {\r\n left = trigger.left;\r\n }\r\n\r\n left = clamp(left, pad, Math.max(pad, vw - width - pad));\r\n\r\n return matchWidth\r\n ? { top, left, side, width }\r\n : { top, left, side };\r\n}\r\n", + "type": "registry:lib", + "target": "components/softglass/lib/floating.ts" + }, + { + "path": "packages/ui/src/lib/use-floating-portal.ts", + "content": "\"use client\";\r\n\r\nimport {\r\n computeFloatingPosition,\r\n type FloatingAlign,\r\n type FloatingCoords,\r\n type FloatingPlacement,\r\n type FloatingSide,\r\n} from \"./floating\";\r\nimport {\r\n useCallback,\r\n useEffect,\r\n useLayoutEffect,\r\n useState,\r\n type CSSProperties,\r\n type RefObject,\r\n} from \"react\";\r\n\r\nconst useIsomorphicLayoutEffect =\r\n typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\r\n\r\nexport type UseFloatingPortalOptions = {\r\n /** Interactive open (not exiting-only). */\r\n open: boolean;\r\n /** Presence mounted — keep positioning while exit plays. */\r\n mounted: boolean;\r\n triggerRef: RefObject;\r\n panelRef: RefObject;\r\n placement?: FloatingPlacement;\r\n align?: FloatingAlign;\r\n matchWidth?: boolean;\r\n gap?: number;\r\n flipMinSpace?: number;\r\n};\r\n\r\n/**\r\n * Portaled overlay positioning: fixed coords + scroll/resize updates.\r\n * Caller still renders via createPortal(…, document.body).\r\n */\r\nexport function useFloatingPortal({\r\n open,\r\n mounted,\r\n triggerRef,\r\n panelRef,\r\n placement = \"auto\",\r\n align = \"start\",\r\n matchWidth = false,\r\n gap = 6,\r\n flipMinSpace,\r\n}: UseFloatingPortalOptions) {\r\n const [portalReady, setPortalReady] = useState(false);\r\n const [coords, setCoords] = useState({\r\n top: 0,\r\n left: 0,\r\n side: \"bottom\",\r\n });\r\n\r\n useEffect(() => {\r\n setPortalReady(true);\r\n }, []);\r\n\r\n const update = useCallback(() => {\r\n const trigger = triggerRef.current;\r\n const panel = panelRef.current;\r\n if (!trigger || !panel) return;\r\n\r\n const tRect = trigger.getBoundingClientRect();\r\n const next = computeFloatingPosition({\r\n trigger: tRect,\r\n panelWidth: panel.offsetWidth || tRect.width,\r\n panelHeight: panel.offsetHeight,\r\n placement,\r\n align,\r\n matchWidth,\r\n gap,\r\n flipMinSpace,\r\n });\r\n setCoords(next);\r\n }, [\r\n align,\r\n flipMinSpace,\r\n gap,\r\n matchWidth,\r\n panelRef,\r\n placement,\r\n triggerRef,\r\n ]);\r\n\r\n useIsomorphicLayoutEffect(() => {\r\n if (!mounted || !portalReady) return;\r\n update();\r\n // Second frame: fonts / max-height may change measured size.\r\n const id = window.requestAnimationFrame(() => update());\r\n return () => window.cancelAnimationFrame(id);\r\n }, [mounted, open, portalReady, update]);\r\n\r\n useEffect(() => {\r\n if (!mounted || !portalReady) return;\r\n\r\n function onScrollOrResize() {\r\n update();\r\n }\r\n\r\n window.addEventListener(\"resize\", onScrollOrResize);\r\n // Capture scroll on ancestors (overflow parents).\r\n window.addEventListener(\"scroll\", onScrollOrResize, true);\r\n return () => {\r\n window.removeEventListener(\"resize\", onScrollOrResize);\r\n window.removeEventListener(\"scroll\", onScrollOrResize, true);\r\n };\r\n }, [mounted, portalReady, update]);\r\n\r\n const floatingStyle: CSSProperties = {\r\n position: \"fixed\",\r\n top: coords.top,\r\n left: coords.left,\r\n ...(coords.width !== undefined ? { width: coords.width } : null),\r\n right: \"auto\",\r\n bottom: \"auto\",\r\n margin: 0,\r\n };\r\n\r\n return {\r\n portalReady,\r\n floatingStyle,\r\n side: coords.side as FloatingSide,\r\n update,\r\n };\r\n}\r\n\r\n/** True if event target is inside any of the given roots. */\r\nexport function eventInside(\r\n target: EventTarget | null,\r\n ...roots: Array\r\n): boolean {\r\n if (!(target instanceof Node)) return false;\r\n return roots.some((root) => root?.contains(target));\r\n}\r\n", + "type": "registry:lib", + "target": "components/softglass/lib/use-floating-portal.ts" + } + ], + "type": "registry:ui" +} \ No newline at end of file diff --git a/apps/web/public/r/tokens.json b/apps/web/public/r/tokens.json index 9aadc09..e216b7d 100644 --- a/apps/web/public/r/tokens.json +++ b/apps/web/public/r/tokens.json @@ -6,7 +6,7 @@ "files": [ { "path": "packages/tokens/src/base.css", - "content": "/**\r\n * Softglass — shared engine (all languages)\r\n * Only colors / surface recipes change per theme.\r\n * Radius, spacing, type scale, glass structure stay shared.\r\n */\r\n\r\n:root {\r\n /* —— Radius (soft, never sharp) —— */\r\n --sg-radius-xs: 0.75rem; /* 12px */\r\n --sg-radius-sm: 1rem; /* 16px */\r\n --sg-radius-md: 1.25rem; /* 20px */\r\n --sg-radius-lg: 1.5rem; /* 24px */\r\n --sg-radius-xl: 2rem; /* 32px */\r\n --sg-radius-pill: 999px;\r\n\r\n /* —— Spacing scale —— */\r\n --sg-space-0: 0;\r\n --sg-space-1: 0.25rem;\r\n --sg-space-2: 0.5rem;\r\n --sg-space-3: 0.75rem;\r\n --sg-space-4: 1rem;\r\n --sg-space-5: 1.25rem;\r\n --sg-space-6: 1.5rem;\r\n --sg-space-8: 2rem;\r\n --sg-space-10: 2.5rem;\r\n --sg-space-12: 3rem;\r\n --sg-space-16: 4rem;\r\n\r\n /* —— Type scale (fluid-friendly) —— */\r\n --sg-font-sans: ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto,\r\n \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif;\r\n --sg-font-display: var(--sg-font-sans);\r\n --sg-text-xs: 0.75rem;\r\n --sg-text-sm: 0.875rem;\r\n --sg-text-base: 1rem;\r\n --sg-text-lg: 1.125rem;\r\n --sg-text-xl: 1.25rem;\r\n --sg-text-2xl: 1.5rem;\r\n --sg-text-3xl: clamp(1.75rem, 1.4rem + 1.2vw, 2.25rem);\r\n --sg-text-4xl: clamp(2rem, 1.5rem + 2vw, 3rem);\r\n --sg-leading-tight: 1.25;\r\n --sg-leading-normal: 1.55;\r\n --sg-leading-relaxed: 1.7;\r\n --sg-tracking-tight: -0.02em;\r\n --sg-tracking-normal: 0;\r\n --sg-font-weight-normal: 400;\r\n --sg-font-weight-medium: 500;\r\n --sg-font-weight-semibold: 600;\r\n --sg-font-weight-bold: 700;\r\n\r\n /* —— Motion —— */\r\n --sg-ease-out: cubic-bezier(0.22, 1, 0.36, 1);\r\n --sg-duration-fast: 150ms;\r\n --sg-duration-normal: 220ms;\r\n --sg-duration-slow: 360ms;\r\n\r\n /* —— Glass structure defaults (themes override values) —— */\r\n --sg-glass-blur: 16px;\r\n --sg-glass-blur-mobile: 10px;\r\n --sg-glass-saturate: 1.35;\r\n --sg-border-width: 1px;\r\n\r\n /* Frost defaults — themes override for readable chrome */\r\n --sg-surface-frost: rgba(255, 255, 255, 0.9);\r\n --sg-surface-frost-strong: rgba(255, 255, 255, 0.96);\r\n --sg-border-frost: rgba(15, 23, 42, 0.12);\r\n\r\n /* —— Z-index —— */\r\n --sg-z-base: 0;\r\n --sg-z-raised: 10;\r\n --sg-z-dropdown: 40;\r\n --sg-z-sticky: 50;\r\n /* Overlays sit well above sticky chrome (header/sidebar) */\r\n --sg-z-modal: 1000;\r\n --sg-z-toast: 1100;\r\n\r\n /* Toast stack: tight elevation so shadows do not bleed into the gap */\r\n --sg-shadow-toast: 0 4px 14px rgba(15, 23, 42, 0.1),\r\n 0 1px 3px rgba(15, 23, 42, 0.06);\r\n\r\n /* —— Touch / layout —— */\r\n --sg-touch-min: 2.75rem; /* 44px */\r\n --sg-content-max: 72rem;\r\n --sg-sidebar-width: 16rem;\r\n --sg-sidebar-width-collapsed: 4.5rem;\r\n\r\n color-scheme: light;\r\n}\r\n\r\n/* Reduced transparency → solid fallbacks (accessibility) */\r\n@media (prefers-reduced-transparency: reduce) {\r\n :root {\r\n --sg-glass-blur: 0px;\r\n --sg-glass-blur-mobile: 0px;\r\n --sg-surface-glass: var(--sg-surface-solid);\r\n --sg-surface-glass-elevated: var(--sg-surface-solid);\r\n --sg-surface-frost: var(--sg-surface-solid);\r\n --sg-surface-frost-strong: var(--sg-surface-solid);\r\n }\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n :root {\r\n --sg-duration-fast: 0ms;\r\n --sg-duration-normal: 0ms;\r\n --sg-duration-slow: 0ms;\r\n }\r\n}\r\n\r\n/* Shared material utilities — language-agnostic structure */\r\n.sg-surface-solid {\r\n background: var(--sg-surface-solid);\r\n color: var(--sg-fg);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n border-radius: var(--sg-radius-md);\r\n box-shadow: var(--sg-shadow-soft);\r\n}\r\n\r\n/* Cards/chrome: prefer frost (opaque + cheap). True blur only when .sg-blur is set. */\r\n.sg-surface-glass {\r\n background: var(--sg-surface-frost);\r\n color: var(--sg-fg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n border-radius: var(--sg-radius-lg);\r\n box-shadow: var(--sg-shadow-glass);\r\n background-image: linear-gradient(\r\n 180deg,\r\n var(--sg-gloss-highlight) 0%,\r\n transparent 42%\r\n );\r\n}\r\n\r\n.sg-surface-glass-elevated {\r\n background: var(--sg-surface-frost-strong);\r\n color: var(--sg-fg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n border-radius: var(--sg-radius-lg);\r\n box-shadow: var(--sg-shadow-elevated);\r\n background-image: linear-gradient(\r\n 180deg,\r\n var(--sg-gloss-highlight) 0%,\r\n transparent 48%\r\n );\r\n}\r\n\r\n.sg-surface-glass.sg-blur,\r\n.sg-surface-glass-elevated.sg-blur {\r\n backdrop-filter: blur(var(--sg-glass-blur)) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(var(--sg-glass-blur))\r\n saturate(var(--sg-glass-saturate));\r\n background: var(--sg-surface-glass);\r\n}\r\n\r\n.sg-surface-glass-elevated.sg-blur {\r\n background: var(--sg-surface-glass-elevated);\r\n}\r\n\r\n.sg-badge-sm {\r\n padding: 0.15rem 0.45rem;\r\n font-size: 0.65rem;\r\n}\r\n\r\n.sg-badge-lg {\r\n padding: 0.35rem 0.85rem;\r\n font-size: var(--sg-text-sm);\r\n}\r\n\r\n.sg-badge-dot::before {\r\n content: \"\";\r\n width: 0.4rem;\r\n height: 0.4rem;\r\n border-radius: 50%;\r\n background: currentColor;\r\n opacity: 0.85;\r\n}\r\n\r\n.sg-field-required::after {\r\n content: \" *\";\r\n color: var(--sg-danger);\r\n}\r\n\r\n.sg-input-block {\r\n width: 100%;\r\n}\r\n\r\n.sg-input-group {\r\n display: flex;\r\n align-items: stretch;\r\n width: 100%;\r\n border-radius: var(--sg-radius-sm);\r\n overflow: hidden;\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-solid);\r\n}\r\n\r\n.sg-input-group:focus-within {\r\n border-color: var(--sg-accent);\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-input-group .sg-input {\r\n border: 0;\r\n border-radius: 0;\r\n box-shadow: none;\r\n}\r\n\r\n.sg-input-group .sg-input:focus {\r\n box-shadow: none;\r\n}\r\n\r\n.sg-input-addon {\r\n display: inline-flex;\r\n align-items: center;\r\n padding: 0 0.75rem;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg-muted);\r\n background: var(--sg-accent-soft);\r\n white-space: nowrap;\r\n}\r\n\r\n@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {\r\n .sg-surface-glass,\r\n .sg-surface-glass-elevated {\r\n background: var(--sg-surface-solid);\r\n background-image: none;\r\n }\r\n}\r\n\r\n@media (max-width: 767px) {\r\n .sg-surface-glass.sg-blur,\r\n .sg-surface-glass-elevated.sg-blur {\r\n backdrop-filter: blur(var(--sg-glass-blur-mobile))\r\n saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(var(--sg-glass-blur-mobile))\r\n saturate(var(--sg-glass-saturate));\r\n }\r\n}\r\n\r\n/* —— Button —— */\r\n.sg-btn {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: var(--sg-space-2);\r\n min-height: var(--sg-touch-min);\r\n padding: 0.625rem 1.25rem;\r\n border-radius: var(--sg-radius-pill);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n border: var(--sg-border-width) solid transparent;\r\n cursor: pointer;\r\n text-decoration: none;\r\n user-select: none;\r\n transition: transform var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out),\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n opacity var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-btn:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.sg-btn:active:not(:disabled) {\r\n transform: translateY(1px);\r\n}\r\n\r\n.sg-btn:disabled {\r\n opacity: 0.5;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-btn-sm {\r\n min-height: 2.25rem;\r\n padding: 0.4rem 0.9rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-btn-lg {\r\n min-height: 3rem;\r\n padding: 0.75rem 1.5rem;\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-btn-icon {\r\n width: var(--sg-touch-min);\r\n padding-inline: 0;\r\n}\r\n\r\n.sg-btn-sm.sg-btn-icon {\r\n width: 2.25rem;\r\n}\r\n\r\n.sg-btn-lg.sg-btn-icon {\r\n width: 3rem;\r\n}\r\n\r\n.sg-btn-primary {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n box-shadow: var(--sg-shadow-soft), inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-btn-primary:hover:not(:disabled) {\r\n background: var(--sg-accent-hover);\r\n}\r\n\r\n.sg-btn-secondary {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-btn-secondary:hover:not(:disabled) {\r\n background: var(--sg-surface-glass-elevated);\r\n}\r\n\r\n.sg-btn-ghost {\r\n background: var(--sg-surface-frost);\r\n color: var(--sg-fg);\r\n border-color: var(--sg-border-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-btn-ghost:hover:not(:disabled) {\r\n background: var(--sg-surface-frost-strong);\r\n}\r\n\r\n.sg-btn-outline {\r\n background: transparent;\r\n color: var(--sg-fg);\r\n border-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-btn-outline:hover:not(:disabled) {\r\n background: var(--sg-accent-soft);\r\n border-color: var(--sg-accent);\r\n}\r\n\r\n.sg-btn-danger {\r\n background: var(--sg-danger);\r\n color: #fff;\r\n box-shadow: var(--sg-shadow-soft), inset 0 1px 0 rgba(255, 255, 255, 0.25);\r\n}\r\n\r\n.sg-btn-danger:hover:not(:disabled) {\r\n filter: brightness(1.05);\r\n}\r\n\r\n.sg-btn-link {\r\n background: transparent;\r\n color: var(--sg-accent);\r\n border-color: transparent;\r\n box-shadow: none;\r\n min-height: auto;\r\n padding-inline: 0.25rem;\r\n text-decoration: underline;\r\n text-underline-offset: 0.15em;\r\n}\r\n\r\n.sg-btn-link:hover:not(:disabled) {\r\n color: var(--sg-accent-hover);\r\n}\r\n\r\n.sg-btn-block {\r\n width: 100%;\r\n}\r\n\r\n.sg-btn-rounded-soft {\r\n border-radius: var(--sg-radius-sm);\r\n}\r\n\r\n.sg-btn-rounded-md {\r\n border-radius: var(--sg-radius-md);\r\n}\r\n\r\n.sg-btn-loading {\r\n position: relative;\r\n color: transparent !important;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-btn-loading > * {\r\n opacity: 0;\r\n}\r\n\r\n.sg-btn-spinner {\r\n position: absolute;\r\n inset: 0;\r\n margin: auto;\r\n width: 1.1rem;\r\n height: 1.1rem;\r\n border-radius: 50%;\r\n border: 2px solid color-mix(in srgb, currentColor 25%, transparent);\r\n border-top-color: var(--sg-accent-fg);\r\n opacity: 1 !important;\r\n animation: sg-spin 0.7s linear infinite;\r\n}\r\n\r\n.sg-btn-ghost .sg-btn-spinner,\r\n.sg-btn-outline .sg-btn-spinner,\r\n.sg-btn-secondary .sg-btn-spinner,\r\n.sg-btn-link .sg-btn-spinner {\r\n border-top-color: var(--sg-fg);\r\n}\r\n\r\n.sg-btn-icon-slot {\r\n display: inline-flex;\r\n flex-shrink: 0;\r\n width: 1.1em;\r\n height: 1.1em;\r\n align-items: center;\r\n justify-content: center;\r\n}\r\n\r\n.sg-btn-icon-slot svg {\r\n width: 1.1em;\r\n height: 1.1em;\r\n}\r\n\r\n@keyframes sg-spin {\r\n to {\r\n transform: rotate(360deg);\r\n }\r\n}\r\n\r\n/* —— Input / Field —— */\r\n.sg-field {\r\n display: grid;\r\n gap: var(--sg-space-2);\r\n}\r\n\r\n.sg-field-label {\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-field-hint {\r\n font-size: var(--sg-text-xs);\r\n color: var(--sg-fg-subtle);\r\n}\r\n\r\n.sg-field-error {\r\n font-size: var(--sg-text-xs);\r\n color: var(--sg-danger);\r\n}\r\n\r\n.sg-input {\r\n width: 100%;\r\n min-height: var(--sg-touch-min);\r\n padding: 0.625rem 1rem;\r\n border-radius: var(--sg-radius-sm);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-solid);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-base);\r\n transition: border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-input::placeholder {\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-input:focus {\r\n outline: none;\r\n border-color: var(--sg-accent);\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-input:disabled {\r\n opacity: 0.55;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-input[aria-invalid=\"true\"] {\r\n border-color: var(--sg-danger);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-danger) 22%, transparent);\r\n}\r\n\r\n.sg-input-sm {\r\n min-height: 2.25rem;\r\n padding: 0.4rem 0.75rem;\r\n font-size: var(--sg-text-sm);\r\n}\r\n\r\n.sg-input-lg {\r\n min-height: 3rem;\r\n padding: 0.75rem 1.1rem;\r\n font-size: var(--sg-text-lg);\r\n}\r\n\r\n/* —— Textarea —— */\r\n.sg-textarea {\r\n width: 100%;\r\n min-height: 6.5rem;\r\n padding: 0.75rem 1rem;\r\n border-radius: var(--sg-radius-sm);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-solid);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-base);\r\n line-height: var(--sg-leading-normal);\r\n resize: vertical;\r\n transition: border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-textarea::placeholder {\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-textarea:focus {\r\n outline: none;\r\n border-color: var(--sg-accent);\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-textarea:disabled {\r\n opacity: 0.55;\r\n cursor: not-allowed;\r\n resize: none;\r\n}\r\n\r\n.sg-textarea[aria-invalid=\"true\"] {\r\n border-color: var(--sg-danger);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-danger) 22%, transparent);\r\n}\r\n\r\n.sg-textarea-sm {\r\n min-height: 4.5rem;\r\n padding: 0.5rem 0.75rem;\r\n font-size: var(--sg-text-sm);\r\n}\r\n\r\n.sg-textarea-lg {\r\n min-height: 9rem;\r\n padding: 0.9rem 1.1rem;\r\n font-size: var(--sg-text-lg);\r\n}\r\n\r\n.sg-textarea-auto {\r\n resize: none;\r\n field-sizing: content;\r\n min-height: 3.5rem;\r\n}\r\n\r\n/* —— Checkbox / Radio —— */\r\n.sg-check-root {\r\n display: inline-flex;\r\n align-items: flex-start;\r\n gap: var(--sg-space-3);\r\n cursor: pointer;\r\n user-select: none;\r\n max-width: 100%;\r\n}\r\n\r\n.sg-check-root[data-disabled=\"true\"] {\r\n opacity: 0.55;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-check-control {\r\n position: relative;\r\n flex-shrink: 0;\r\n width: 1.25rem;\r\n height: 1.25rem;\r\n margin-top: 0.15rem;\r\n border-radius: 0.4rem;\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n transition: background var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-check-control[data-kind=\"radio\"] {\r\n border-radius: var(--sg-radius-pill);\r\n}\r\n\r\n.sg-check-input {\r\n position: absolute;\r\n inset: 0;\r\n opacity: 0;\r\n margin: 0;\r\n width: 100%;\r\n height: 100%;\r\n cursor: inherit;\r\n}\r\n\r\n.sg-check-input:focus-visible + .sg-check-face {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.sg-check-face {\r\n width: 100%;\r\n height: 100%;\r\n border-radius: inherit;\r\n display: grid;\r\n place-items: center;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-check-control[data-checked=\"true\"],\r\n.sg-check-control[data-indeterminate=\"true\"] {\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-check-control[data-checked=\"true\"][data-kind=\"radio\"] .sg-check-dot {\r\n opacity: 1;\r\n transform: scale(1);\r\n}\r\n\r\n.sg-check-mark {\r\n width: 0.7rem;\r\n height: 0.7rem;\r\n opacity: 0;\r\n transform: scale(0.6);\r\n transition: opacity var(--sg-duration-fast) var(--sg-ease-out),\r\n transform var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-check-control[data-checked=\"true\"] .sg-check-mark,\r\n.sg-check-control[data-indeterminate=\"true\"] .sg-check-mark {\r\n opacity: 1;\r\n transform: scale(1);\r\n}\r\n\r\n.sg-check-dot {\r\n width: 0.45rem;\r\n height: 0.45rem;\r\n border-radius: 50%;\r\n background: var(--sg-accent-fg);\r\n opacity: 0;\r\n transform: scale(0.5);\r\n transition: opacity var(--sg-duration-fast) var(--sg-ease-out),\r\n transform var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-check-label {\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n color: var(--sg-fg);\r\n line-height: 1.4;\r\n}\r\n\r\n.sg-check-hint {\r\n display: block;\r\n margin-top: 0.15rem;\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-normal);\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-check-group {\r\n display: grid;\r\n gap: var(--sg-space-3);\r\n}\r\n\r\n.sg-check-group-label {\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n color: var(--sg-fg);\r\n margin: 0 0 var(--sg-space-1);\r\n}\r\n\r\n/* —— Tooltip —— */\r\n.sg-tooltip-wrap {\r\n position: relative;\r\n display: inline-flex;\r\n max-width: 100%;\r\n}\r\n\r\n.sg-tooltip {\r\n position: absolute;\r\n z-index: var(--sg-z-dropdown);\r\n left: 50%;\r\n bottom: calc(100% + 0.45rem);\r\n transform: translateX(-50%);\r\n min-width: max-content;\r\n max-width: min(16rem, 70vw);\r\n padding: 0.4rem 0.65rem;\r\n border-radius: var(--sg-radius-sm);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost-strong);\r\n color: var(--sg-fg);\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-medium);\r\n line-height: 1.4;\r\n box-shadow: var(--sg-shadow-elevated);\r\n pointer-events: none;\r\n opacity: 0;\r\n visibility: hidden;\r\n transition: opacity var(--sg-duration-fast) var(--sg-ease-out),\r\n visibility var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-tooltip[data-placement=\"bottom\"] {\r\n bottom: auto;\r\n top: calc(100% + 0.45rem);\r\n}\r\n\r\n.sg-tooltip[data-open=\"true\"] {\r\n opacity: 1;\r\n visibility: visible;\r\n}\r\n\r\n.sg-tooltip-arrow {\r\n position: absolute;\r\n left: 50%;\r\n top: 100%;\r\n width: 0.45rem;\r\n height: 0.45rem;\r\n background: var(--sg-surface-frost-strong);\r\n border-right: var(--sg-border-width) solid var(--sg-border-frost);\r\n border-bottom: var(--sg-border-width) solid var(--sg-border-frost);\r\n transform: translateX(-50%) translateY(-55%) rotate(45deg);\r\n}\r\n\r\n.sg-tooltip[data-placement=\"bottom\"] .sg-tooltip-arrow {\r\n top: auto;\r\n bottom: 100%;\r\n transform: translateX(-50%) translateY(55%) rotate(45deg);\r\n}\r\n\r\n/* —— Badge —— */\r\n.sg-badge {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.25rem;\r\n padding: 0.25rem 0.625rem;\r\n border-radius: var(--sg-radius-pill);\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-medium);\r\n line-height: 1.2;\r\n background: var(--sg-surface-glass);\r\n color: var(--sg-fg-muted);\r\n border: var(--sg-border-width) solid var(--sg-border-glass);\r\n}\r\n\r\n.sg-badge-accent {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n border-color: color-mix(in srgb, var(--sg-accent) 25%, transparent);\r\n}\r\n\r\n.sg-badge-solid {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n border-color: transparent;\r\n}\r\n\r\n.sg-badge-success {\r\n background: color-mix(in srgb, var(--sg-success) 14%, transparent);\r\n color: var(--sg-success);\r\n border-color: color-mix(in srgb, var(--sg-success) 28%, transparent);\r\n}\r\n\r\n.sg-badge-warning {\r\n background: color-mix(in srgb, var(--sg-warning) 14%, transparent);\r\n color: var(--sg-warning);\r\n border-color: color-mix(in srgb, var(--sg-warning) 28%, transparent);\r\n}\r\n\r\n.sg-badge-danger {\r\n background: color-mix(in srgb, var(--sg-danger) 14%, transparent);\r\n color: var(--sg-danger);\r\n border-color: color-mix(in srgb, var(--sg-danger) 28%, transparent);\r\n}\r\n\r\n/* —— Card —— */\r\n.sg-card {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-4);\r\n padding: var(--sg-space-5);\r\n border-radius: var(--sg-radius-lg);\r\n color: var(--sg-fg);\r\n /* Avoid layout containment — it traps absolute select/menus under next cards */\r\n overflow: visible;\r\n}\r\n\r\n.sg-card-sm {\r\n padding: var(--sg-space-3);\r\n gap: var(--sg-space-3);\r\n}\r\n\r\n.sg-card-lg {\r\n padding: var(--sg-space-8);\r\n gap: var(--sg-space-5);\r\n}\r\n\r\n.sg-card-flush {\r\n padding: 0;\r\n}\r\n\r\n.sg-card-hoverable {\r\n transition: transform var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-card-hoverable:hover {\r\n transform: translateY(-2px);\r\n box-shadow: var(--sg-shadow-elevated);\r\n}\r\n\r\n.sg-card-clickable {\r\n cursor: pointer;\r\n}\r\n\r\n.sg-card-header {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-1);\r\n}\r\n\r\n.sg-card-title {\r\n margin: 0;\r\n font-size: var(--sg-text-lg);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n line-height: var(--sg-leading-tight);\r\n}\r\n\r\n.sg-card-description {\r\n margin: 0;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg-muted);\r\n line-height: var(--sg-leading-normal);\r\n}\r\n\r\n.sg-card-content {\r\n flex: 1;\r\n min-width: 0;\r\n}\r\n\r\n.sg-card-footer {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n gap: var(--sg-space-3);\r\n margin-top: auto;\r\n}\r\n\r\n/* —— App shell —— */\r\n.sg-app-bg {\r\n min-height: 100dvh;\r\n background-color: var(--sg-bg);\r\n background-image: var(--sg-bg-image);\r\n /* fixed attachment forces full-viewport repaints — scroll jank */\r\n background-attachment: scroll;\r\n background-repeat: no-repeat;\r\n background-size: cover;\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n line-height: var(--sg-leading-normal);\r\n}\r\n\r\n.sg-shell {\r\n display: grid;\r\n min-height: 100dvh;\r\n grid-template-columns: 1fr;\r\n grid-template-rows: auto 1fr;\r\n grid-template-areas:\r\n \"header\"\r\n \"main\";\r\n}\r\n\r\n.sg-shell-with-sidebar {\r\n grid-template-columns: var(--sg-sidebar-width) 1fr;\r\n grid-template-areas:\r\n \"sidebar header\"\r\n \"sidebar main\";\r\n transition: grid-template-columns var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-shell-with-sidebar[data-collapsed=\"true\"] {\r\n grid-template-columns: var(--sg-sidebar-width-collapsed) 1fr;\r\n}\r\n\r\n.sg-shell-header {\r\n grid-area: header;\r\n position: sticky;\r\n top: 0;\r\n z-index: var(--sg-z-sticky);\r\n padding: var(--sg-space-3) var(--sg-space-4);\r\n}\r\n\r\n.sg-shell-header-inner {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n padding: 0.75rem 1rem;\r\n max-width: var(--sg-content-max);\r\n margin-inline: auto;\r\n width: 100%;\r\n}\r\n\r\n.sg-shell-with-sidebar .sg-shell-header-inner {\r\n max-width: none;\r\n margin-inline: 0;\r\n}\r\n\r\n.sg-shell-sidebar {\r\n grid-area: sidebar;\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-4);\r\n padding: var(--sg-space-5);\r\n border-right: var(--sg-border-width) solid var(--sg-border-glass);\r\n position: sticky;\r\n top: 0;\r\n height: 100dvh;\r\n overflow: auto;\r\n min-width: 0;\r\n}\r\n\r\n.sg-shell-with-sidebar[data-collapsed=\"true\"] .sg-shell-sidebar {\r\n padding-inline: var(--sg-space-2);\r\n align-items: stretch;\r\n}\r\n\r\n.sg-shell-main {\r\n grid-area: main;\r\n width: 100%;\r\n min-width: 0;\r\n}\r\n\r\n.sg-shell-main-inner {\r\n max-width: var(--sg-content-max);\r\n margin-inline: auto;\r\n padding: var(--sg-space-5) var(--sg-space-4) var(--sg-space-12);\r\n width: 100%;\r\n}\r\n\r\n.sg-shell-with-sidebar .sg-shell-main-inner {\r\n max-width: none;\r\n}\r\n\r\n.sg-shell-nav {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-1);\r\n}\r\n\r\n.sg-shell-nav-item {\r\n display: flex;\r\n align-items: center;\r\n gap: var(--sg-space-2);\r\n min-height: 2.5rem;\r\n padding: 0.5rem 0.75rem;\r\n border-radius: var(--sg-radius-sm);\r\n color: var(--sg-fg-muted);\r\n text-decoration: none;\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n border: var(--sg-border-width) solid transparent;\r\n transition: background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out);\r\n position: relative;\r\n}\r\n\r\n.sg-shell-nav-icon,\r\n.sg-shell-nav-glyph {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n flex-shrink: 0;\r\n width: 1.25rem;\r\n height: 1.25rem;\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n}\r\n\r\n/* Glyph is the collapsed-only fallback when no icon is passed. */\r\n.sg-shell-nav-glyph {\r\n display: none;\r\n}\r\n\r\n.sg-shell-nav-label {\r\n min-width: 0;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.sg-shell-with-sidebar[data-collapsed=\"true\"] .sg-shell-nav-item {\r\n justify-content: center;\r\n padding-inline: 0.5rem;\r\n gap: 0;\r\n}\r\n\r\n.sg-shell-with-sidebar[data-collapsed=\"true\"] .sg-shell-nav-label {\r\n position: absolute;\r\n width: 1px;\r\n height: 1px;\r\n padding: 0;\r\n margin: -1px;\r\n overflow: hidden;\r\n clip: rect(0, 0, 0, 0);\r\n white-space: nowrap;\r\n border: 0;\r\n}\r\n\r\n.sg-shell-with-sidebar[data-collapsed=\"true\"] .sg-shell-nav-glyph {\r\n display: inline-flex;\r\n}\r\n\r\n.sg-shell-with-sidebar[data-collapsed=\"true\"] .sg-shell-sidebar-collapse-hide {\r\n position: absolute;\r\n width: 1px;\r\n height: 1px;\r\n padding: 0;\r\n margin: -1px;\r\n overflow: hidden;\r\n clip: rect(0, 0, 0, 0);\r\n white-space: nowrap;\r\n border: 0;\r\n}\r\n\r\n.sg-shell-menu-icon {\r\n display: flex;\r\n flex-direction: column;\r\n justify-content: center;\r\n gap: 3px;\r\n width: 1rem;\r\n}\r\n\r\n.sg-shell-menu-icon span {\r\n display: block;\r\n height: 1.5px;\r\n width: 100%;\r\n border-radius: 1px;\r\n background: currentColor;\r\n}\r\n\r\n.sg-shell-collapse-icon {\r\n font-size: var(--sg-text-base);\r\n line-height: 1;\r\n font-weight: var(--sg-font-weight-semibold);\r\n}\r\n\r\n.sg-shell-mobile-nav {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-4);\r\n}\r\n\r\n.sg-shell-mobile-panel {\r\n max-width: min(20rem, 92vw);\r\n}\r\n\r\n.sg-shell-nav-item:hover {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-shell-nav-item[data-active=\"true\"] {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n border-color: color-mix(in srgb, var(--sg-accent) 20%, transparent);\r\n}\r\n\r\n.sg-blob {\r\n position: absolute;\r\n border-radius: 50%;\r\n /* No filter:blur — soft radial is enough and far cheaper */\r\n filter: none;\r\n opacity: var(--sg-blob-opacity, 0.35);\r\n pointer-events: none;\r\n z-index: var(--sg-z-base);\r\n background: radial-gradient(\r\n circle,\r\n var(--sg-blob-color, var(--sg-accent)) 0%,\r\n transparent 70%\r\n );\r\n}\r\n\r\n/* —— Avatar —— */\r\n.sg-avatar {\r\n position: relative;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n flex-shrink: 0;\r\n overflow: hidden;\r\n border-radius: var(--sg-radius-pill);\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n user-select: none;\r\n border: var(--sg-border-width) solid var(--sg-border-glass);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-avatar-sm {\r\n width: 2rem;\r\n height: 2rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-avatar-md {\r\n width: 2.5rem;\r\n height: 2.5rem;\r\n font-size: var(--sg-text-sm);\r\n}\r\n\r\n.sg-avatar-lg {\r\n width: 3.25rem;\r\n height: 3.25rem;\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-avatar-xl {\r\n width: 4rem;\r\n height: 4rem;\r\n font-size: var(--sg-text-lg);\r\n}\r\n\r\n.sg-avatar-image {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n display: block;\r\n}\r\n\r\n.sg-avatar-fallback {\r\n line-height: 1;\r\n}\r\n\r\n.sg-avatar-group {\r\n display: inline-flex;\r\n align-items: center;\r\n}\r\n\r\n.sg-avatar-group .sg-avatar {\r\n margin-left: -0.45rem;\r\n box-shadow: 0 0 0 2px var(--sg-bg), inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-avatar-group .sg-avatar:first-child {\r\n margin-left: 0;\r\n}\r\n\r\n/* —— Switch —— */\r\n.sg-switch-root {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: var(--sg-space-3);\r\n cursor: pointer;\r\n user-select: none;\r\n}\r\n\r\n.sg-switch-root[data-disabled=\"true\"] {\r\n opacity: 0.55;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-switch {\r\n position: relative;\r\n width: 2.75rem;\r\n height: 1.6rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid var(--sg-border-glass);\r\n background: var(--sg-surface-glass);\r\n backdrop-filter: blur(var(--sg-glass-blur-mobile));\r\n -webkit-backdrop-filter: blur(var(--sg-glass-blur-mobile));\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n transition: background var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n flex-shrink: 0;\r\n padding: 0;\r\n cursor: inherit;\r\n}\r\n\r\n.sg-switch:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.sg-switch[data-checked=\"true\"] {\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n}\r\n\r\n.sg-switch-thumb {\r\n position: absolute;\r\n top: 50%;\r\n left: 0.15rem;\r\n width: 1.2rem;\r\n height: 1.2rem;\r\n border-radius: var(--sg-radius-pill);\r\n background: #fff;\r\n box-shadow: 0 1px 3px rgba(15, 23, 42, 0.25),\r\n inset 0 1px 0 rgba(255, 255, 255, 0.8);\r\n transform: translate(0, -50%);\r\n transition: transform var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-switch[data-checked=\"true\"] .sg-switch-thumb {\r\n transform: translate(1.1rem, -50%);\r\n}\r\n\r\n.sg-switch-label {\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-switch-hint {\r\n display: block;\r\n margin-top: 0.15rem;\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-normal);\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n/* —— Tabs —— */\r\n.sg-tabs {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-4);\r\n width: 100%;\r\n}\r\n\r\n.sg-tabs-list {\r\n display: inline-flex;\r\n flex-wrap: wrap;\r\n gap: var(--sg-space-1);\r\n padding: var(--sg-space-1);\r\n border-radius: var(--sg-radius-pill);\r\n width: fit-content;\r\n max-width: 100%;\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight), var(--sg-shadow-soft);\r\n backdrop-filter: blur(var(--sg-glass-blur)) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(var(--sg-glass-blur))\r\n saturate(var(--sg-glass-saturate));\r\n}\r\n\r\n.sg-tabs-trigger {\r\n appearance: none;\r\n border: var(--sg-border-width) solid transparent;\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n letter-spacing: var(--sg-tracking-tight);\r\n min-height: 2.25rem;\r\n padding: 0.4rem 0.9rem;\r\n border-radius: var(--sg-radius-pill);\r\n cursor: pointer;\r\n transition: background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n/* Hover only for inactive tabs - active keeps solid accent */\r\n.sg-tabs-trigger:hover:not(:disabled):not([data-active=\"true\"]) {\r\n color: var(--sg-fg);\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 14%, transparent);\r\n}\r\n\r\n.sg-tabs-trigger:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.sg-tabs-trigger:disabled {\r\n opacity: 0.5;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-tabs-trigger[data-active=\"true\"] {\r\n color: var(--sg-accent-fg);\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n font-weight: var(--sg-font-weight-semibold);\r\n}\r\n\r\n.sg-tabs-trigger[data-active=\"true\"]:hover:not(:disabled) {\r\n color: var(--sg-accent-fg);\r\n background: var(--sg-accent-hover);\r\n border-color: transparent;\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-tabs-content {\r\n min-width: 0;\r\n outline: none;\r\n}\r\n\r\n.sg-tabs-content[hidden] {\r\n display: none;\r\n}\r\n\r\n/* —— Select (custom glass dropdown — matches Button language) —— */\r\n.sg-select-root {\r\n position: relative;\r\n width: 100%;\r\n}\r\n\r\n/*\r\n * Open menus must stack above following sibling cards (card paint order).\r\n * Without this, absolute menus render under the next .sg-card in the page.\r\n */\r\n.sg-select-root:has([data-open=\"true\"]),\r\n.sg-select-root:has([aria-expanded=\"true\"]),\r\n.sg-combobox-root:has([aria-expanded=\"true\"]),\r\n.sg-multiselect-root:has([data-open=\"true\"]),\r\n.sg-datepicker-root:has([data-open=\"true\"]),\r\n.sg-datepicker-root:has([aria-expanded=\"true\"]) {\r\n z-index: calc(var(--sg-z-dropdown) + 20);\r\n isolation: isolate;\r\n}\r\n\r\n.sg-select-trigger {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n width: 100%;\r\n min-height: var(--sg-touch-min);\r\n padding: 0.625rem 0.9rem 0.625rem 1.15rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n /* Frosted - readable, not see-through */\r\n background: var(--sg-surface-frost);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n line-height: 1.2;\r\n cursor: pointer;\r\n text-align: left;\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight), var(--sg-shadow-soft);\r\n backdrop-filter: blur(18px) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(18px) saturate(var(--sg-glass-saturate));\r\n transition: background var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out),\r\n transform var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-select-trigger:hover:not(:disabled) {\r\n background: var(--sg-surface-frost-strong);\r\n border-color: color-mix(in srgb, var(--sg-accent) 22%, var(--sg-border-frost));\r\n}\r\n\r\n.sg-select-trigger:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.sg-select-trigger:active:not(:disabled) {\r\n transform: translateY(1px);\r\n}\r\n\r\n.sg-select-trigger:disabled {\r\n opacity: 0.5;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-select-trigger[data-open=\"true\"] {\r\n background: var(--sg-surface-frost-strong);\r\n border-color: color-mix(in srgb, var(--sg-accent) 40%, var(--sg-border-frost));\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight), 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-select-trigger[aria-invalid=\"true\"] {\r\n border-color: var(--sg-danger);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 0 0 3px color-mix(in srgb, var(--sg-danger) 22%, transparent);\r\n}\r\n\r\n.sg-select-trigger-sm {\r\n min-height: 2.25rem;\r\n padding: 0.4rem 0.75rem 0.4rem 0.9rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-select-trigger-lg {\r\n min-height: 3rem;\r\n padding: 0.75rem 1rem 0.75rem 1.35rem;\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-select-value {\r\n flex: 1;\r\n min-width: 0;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.sg-select-value[data-placeholder=\"true\"] {\r\n color: var(--sg-fg-muted);\r\n font-weight: var(--sg-font-weight-medium);\r\n}\r\n\r\n.sg-select-chevron {\r\n flex-shrink: 0;\r\n width: 1.35rem;\r\n height: 1.35rem;\r\n border-radius: var(--sg-radius-pill);\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n transition: transform var(--sg-duration-fast) var(--sg-ease-out),\r\n background var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-select-trigger[data-open=\"true\"] .sg-select-chevron {\r\n transform: rotate(180deg);\r\n background: color-mix(in srgb, var(--sg-accent) 22%, transparent);\r\n}\r\n\r\n.sg-select-chevron svg {\r\n width: 0.85rem;\r\n height: 0.85rem;\r\n display: block;\r\n}\r\n\r\n.sg-select-menu {\r\n position: absolute;\r\n z-index: var(--sg-z-dropdown);\r\n top: calc(100% + 0.4rem);\r\n left: 0;\r\n right: 0;\r\n margin: 0;\r\n padding: var(--sg-space-2);\r\n list-style: none;\r\n max-height: min(16rem, 50dvh);\r\n overflow: auto;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n /* Strong frost so options stay readable on busy backgrounds */\r\n background-color: var(--sg-surface-frost-strong);\r\n background-image: linear-gradient(\r\n 180deg,\r\n var(--sg-gloss-highlight) 0%,\r\n transparent 36%\r\n );\r\n box-shadow: var(--sg-shadow-elevated);\r\n backdrop-filter: blur(22px) saturate(1.35);\r\n -webkit-backdrop-filter: blur(22px) saturate(1.35);\r\n animation: sg-select-in var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-select-menu[data-placement=\"top\"] {\r\n top: auto;\r\n bottom: calc(100% + 0.4rem);\r\n animation-name: sg-select-in-top;\r\n}\r\n\r\n/* Portaled panels (v1.2d): fixed coords from JS — ignore absolute anchors */\r\n.sg-select-menu[data-portaled],\r\n.sg-popover-content[data-portaled],\r\n.sg-menu-content[data-portaled] {\r\n position: fixed;\r\n right: auto;\r\n bottom: auto;\r\n /* top/left/width come from inline style */\r\n translate: none;\r\n z-index: calc(var(--sg-z-dropdown) + 40);\r\n}\r\n\r\n.sg-select-menu[data-portaled] {\r\n /* matchWidth sets width; drop stretch-to-parent */\r\n left: auto;\r\n right: auto;\r\n}\r\n\r\n.sg-select-menu[data-portaled][data-placement=\"top\"],\r\n.sg-popover-content[data-portaled][data-placement=\"top\"],\r\n.sg-menu-content[data-portaled][data-placement=\"top\"] {\r\n top: auto; /* still overridden by inline top */\r\n bottom: auto;\r\n animation-name: sg-select-in-top;\r\n}\r\n\r\n.sg-select-option {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n width: 100%;\r\n min-height: 2.5rem;\r\n padding: 0.5rem 0.85rem;\r\n border: var(--sg-border-width) solid transparent;\r\n border-radius: var(--sg-radius-pill);\r\n background: transparent;\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n letter-spacing: var(--sg-tracking-tight);\r\n text-align: left;\r\n cursor: pointer;\r\n transition: background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n/* Highlight only when not selected — avoids muddy mix on active row */\r\n.sg-select-option:hover:not(:disabled):not([data-selected=\"true\"]),\r\n.sg-select-option[data-highlighted=\"true\"]:not([data-selected=\"true\"]) {\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 18%, transparent);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-select-option[data-selected=\"true\"] {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n border-color: transparent;\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n font-weight: var(--sg-font-weight-semibold);\r\n}\r\n\r\n.sg-select-option[data-selected=\"true\"][data-highlighted=\"true\"],\r\n.sg-select-option[data-selected=\"true\"]:hover:not(:disabled) {\r\n background: var(--sg-accent-hover);\r\n color: var(--sg-accent-fg);\r\n filter: none;\r\n}\r\n\r\n.sg-select-option:disabled {\r\n opacity: 0.45;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-select-option:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 1px;\r\n}\r\n\r\n.sg-select-check {\r\n flex-shrink: 0;\r\n width: 1rem;\r\n height: 1rem;\r\n opacity: 0;\r\n}\r\n\r\n.sg-select-option[data-selected=\"true\"] .sg-select-check {\r\n opacity: 1;\r\n}\r\n\r\n/* —— DatePicker (v1.2b) — solid trigger + frost month panel —— */\r\n.sg-datepicker-root {\r\n position: relative;\r\n width: 100%;\r\n}\r\n\r\n.sg-datepicker-trigger {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n width: 100%;\r\n min-height: var(--sg-touch-min);\r\n padding: 0.625rem 0.9rem 0.625rem 1.15rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n /* Solid field for typing contrast — calendar chrome stays frost */\r\n background: var(--sg-surface-solid);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n line-height: 1.2;\r\n cursor: pointer;\r\n text-align: left;\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight), var(--sg-shadow-soft);\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-datepicker-trigger:hover:not(:disabled) {\r\n border-color: color-mix(in srgb, var(--sg-accent) 28%, var(--sg-border-subtle));\r\n}\r\n\r\n.sg-datepicker-trigger:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.sg-datepicker-trigger:disabled {\r\n opacity: 0.5;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-datepicker-trigger[data-open=\"true\"] {\r\n border-color: color-mix(in srgb, var(--sg-accent) 40%, var(--sg-border-subtle));\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-datepicker-trigger[aria-invalid=\"true\"] {\r\n border-color: var(--sg-danger);\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 0 0 3px color-mix(in srgb, var(--sg-danger) 22%, transparent);\r\n}\r\n\r\n.sg-datepicker-trigger-sm {\r\n min-height: 2.25rem;\r\n padding: 0.4rem 0.75rem 0.4rem 0.9rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-datepicker-trigger-lg {\r\n min-height: 3rem;\r\n padding: 0.75rem 1rem 0.75rem 1.35rem;\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-datepicker-value {\r\n flex: 1;\r\n min-width: 0;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.sg-datepicker-value[data-placeholder=\"true\"] {\r\n color: var(--sg-fg-muted);\r\n font-weight: var(--sg-font-weight-medium);\r\n}\r\n\r\n.sg-datepicker-icon {\r\n flex-shrink: 0;\r\n width: 1.35rem;\r\n height: 1.35rem;\r\n border-radius: var(--sg-radius-pill);\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-datepicker-icon svg {\r\n width: 0.9rem;\r\n height: 0.9rem;\r\n display: block;\r\n}\r\n\r\n.sg-datepicker-panel {\r\n position: absolute;\r\n z-index: var(--sg-z-dropdown);\r\n top: calc(100% + 0.4rem);\r\n left: 0;\r\n width: min(19.5rem, 100vw - 1.5rem);\r\n margin: 0;\r\n padding: var(--sg-space-3);\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background-color: var(--sg-surface-frost-strong);\r\n background-image: linear-gradient(\r\n 180deg,\r\n var(--sg-gloss-highlight) 0%,\r\n transparent 36%\r\n );\r\n box-shadow: var(--sg-shadow-elevated);\r\n backdrop-filter: blur(22px) saturate(1.35);\r\n -webkit-backdrop-filter: blur(22px) saturate(1.35);\r\n color: var(--sg-fg);\r\n animation: sg-select-in var(--sg-duration-fast) var(--sg-ease-out);\r\n box-sizing: border-box;\r\n}\r\n\r\n.sg-datepicker-panel[data-placement=\"top\"] {\r\n top: auto;\r\n bottom: calc(100% + 0.4rem);\r\n animation-name: sg-select-in-top;\r\n}\r\n\r\n/* Body portal — fixed coords from useFloatingPortal (Select language) */\r\n.sg-datepicker-panel[data-portaled] {\r\n position: fixed;\r\n top: auto;\r\n left: auto;\r\n bottom: auto;\r\n right: auto;\r\n margin: 0;\r\n}\r\n\r\n.sg-datepicker-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: var(--sg-space-2);\r\n margin-bottom: var(--sg-space-3);\r\n}\r\n\r\n.sg-datepicker-caption {\r\n flex: 1;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 0.25rem;\r\n min-width: 0;\r\n}\r\n\r\n.sg-datepicker-caption-btn {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-height: 2rem;\r\n padding: 0.25rem 0.55rem;\r\n border: var(--sg-border-width) solid transparent;\r\n border-radius: var(--sg-radius-pill);\r\n background: transparent;\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n cursor: pointer;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-datepicker-caption-btn:hover {\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 18%, transparent);\r\n}\r\n\r\n.sg-datepicker-caption-btn:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 1px;\r\n}\r\n\r\n.sg-datepicker-caption-btn-wide {\r\n flex: 1;\r\n}\r\n\r\n/* Month / year choice grid (same frost language as day cells) */\r\n.sg-datepicker-picker-grid {\r\n display: grid;\r\n grid-template-columns: repeat(3, 1fr);\r\n gap: 0.35rem;\r\n min-height: 12.5rem;\r\n}\r\n\r\n.sg-datepicker-picker-cell {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-height: 2.5rem;\r\n padding: 0.35rem 0.4rem;\r\n border: var(--sg-border-width) solid transparent;\r\n border-radius: var(--sg-radius-pill);\r\n background: transparent;\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n cursor: pointer;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-datepicker-picker-cell:hover:not(:disabled):not([data-selected=\"true\"]) {\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 18%, transparent);\r\n}\r\n\r\n.sg-datepicker-picker-cell[data-today=\"true\"]:not([data-selected=\"true\"]) {\r\n border-color: color-mix(in srgb, var(--sg-accent) 45%, transparent);\r\n font-weight: var(--sg-font-weight-semibold);\r\n}\r\n\r\n.sg-datepicker-picker-cell[data-selected=\"true\"] {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n border-color: transparent;\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n font-weight: var(--sg-font-weight-semibold);\r\n}\r\n\r\n.sg-datepicker-picker-cell[data-selected=\"true\"]:hover:not(:disabled) {\r\n background: var(--sg-accent-hover);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-datepicker-picker-cell:disabled {\r\n opacity: 0.35;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-datepicker-picker-cell:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 1px;\r\n}\r\n\r\n.sg-datepicker-nav {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 2rem;\r\n height: 2rem;\r\n border: var(--sg-border-width) solid transparent;\r\n border-radius: var(--sg-radius-pill);\r\n background: transparent;\r\n color: var(--sg-fg);\r\n cursor: pointer;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-datepicker-nav:hover {\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 18%, transparent);\r\n}\r\n\r\n.sg-datepicker-nav:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 1px;\r\n}\r\n\r\n.sg-datepicker-nav svg {\r\n width: 1rem;\r\n height: 1rem;\r\n display: block;\r\n}\r\n\r\n.sg-datepicker-weekdays {\r\n display: grid;\r\n grid-template-columns: repeat(7, 1fr);\r\n gap: 0.15rem;\r\n margin-bottom: 0.25rem;\r\n}\r\n\r\n.sg-datepicker-weekday {\r\n text-align: center;\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: 0.04em;\r\n text-transform: uppercase;\r\n color: var(--sg-fg-muted);\r\n padding: 0.2rem 0;\r\n}\r\n\r\n.sg-datepicker-grid {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 0.15rem;\r\n}\r\n\r\n.sg-datepicker-row {\r\n display: grid;\r\n grid-template-columns: repeat(7, 1fr);\r\n gap: 0.15rem;\r\n}\r\n\r\n.sg-datepicker-day {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-height: 2.15rem;\r\n width: 100%;\r\n padding: 0;\r\n border: var(--sg-border-width) solid transparent;\r\n border-radius: var(--sg-radius-pill);\r\n background: transparent;\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n cursor: pointer;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-datepicker-day:hover:not(:disabled):not([data-selected=\"true\"]) {\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 18%, transparent);\r\n}\r\n\r\n.sg-datepicker-day[data-outside=\"true\"] {\r\n color: var(--sg-fg-muted);\r\n opacity: 0.55;\r\n}\r\n\r\n.sg-datepicker-day[data-today=\"true\"]:not([data-selected=\"true\"]) {\r\n border-color: color-mix(in srgb, var(--sg-accent) 45%, transparent);\r\n font-weight: var(--sg-font-weight-semibold);\r\n}\r\n\r\n.sg-datepicker-day[data-selected=\"true\"] {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n border-color: transparent;\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n font-weight: var(--sg-font-weight-semibold);\r\n opacity: 1;\r\n}\r\n\r\n.sg-datepicker-day[data-selected=\"true\"]:hover:not(:disabled) {\r\n background: var(--sg-accent-hover);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n/* Range selection chrome (v1.5c) */\r\n.sg-datepicker-range-hint {\r\n margin: 0 0 0.45rem;\r\n padding: 0 0.15rem;\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-medium);\r\n color: var(--sg-fg-muted);\r\n text-align: center;\r\n}\r\n\r\n.sg-datepicker-day[data-in-range=\"true\"] {\r\n background: var(--sg-accent-soft);\r\n border-color: transparent;\r\n border-radius: 0;\r\n opacity: 1;\r\n}\r\n\r\n.sg-datepicker-day[data-range-start=\"true\"],\r\n.sg-datepicker-day[data-range-end=\"true\"] {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n border-color: transparent;\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n font-weight: var(--sg-font-weight-semibold);\r\n opacity: 1;\r\n}\r\n\r\n.sg-datepicker-day[data-range-start=\"true\"] {\r\n border-radius: var(--sg-radius-pill) 0 0 var(--sg-radius-pill);\r\n}\r\n\r\n.sg-datepicker-day[data-range-end=\"true\"] {\r\n border-radius: 0 var(--sg-radius-pill) var(--sg-radius-pill) 0;\r\n}\r\n\r\n.sg-datepicker-day[data-range-start=\"true\"][data-range-end=\"true\"] {\r\n border-radius: var(--sg-radius-pill);\r\n}\r\n\r\n.sg-datepicker-day[data-range-start=\"true\"]:hover:not(:disabled),\r\n.sg-datepicker-day[data-range-end=\"true\"]:hover:not(:disabled) {\r\n background: var(--sg-accent-hover);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-datepicker-day:disabled {\r\n opacity: 0.35;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-datepicker-day:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 1px;\r\n}\r\n\r\n.sg-select-check svg {\r\n width: 100%;\r\n height: 100%;\r\n display: block;\r\n}\r\n\r\n/* —— Combobox / MultiSelect (v1.1 Sprint D) —— */\r\n.sg-combobox-trigger {\r\n cursor: text;\r\n gap: var(--sg-space-2);\r\n}\r\n\r\n.sg-combobox-input {\r\n flex: 1;\r\n min-width: 0;\r\n border: 0;\r\n background: transparent;\r\n color: inherit;\r\n font: inherit;\r\n letter-spacing: inherit;\r\n outline: none;\r\n padding: 0;\r\n margin: 0;\r\n width: 100%;\r\n}\r\n\r\n.sg-combobox-input::placeholder {\r\n color: var(--sg-fg-muted);\r\n opacity: 1;\r\n}\r\n\r\n.sg-combobox-input:disabled {\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-combobox-empty {\r\n padding: 0.65rem 0.85rem;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg-muted);\r\n list-style: none;\r\n}\r\n\r\n.sg-multiselect-trigger {\r\n height: auto;\r\n min-height: var(--sg-touch-min);\r\n /* Center placeholder + chips with caret (flex-start left \"Pick tags\" too high) */\r\n align-items: center;\r\n padding-top: 0.4rem;\r\n padding-bottom: 0.4rem;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-multiselect-trigger[data-disabled=\"true\"] {\r\n opacity: 0.45;\r\n cursor: not-allowed;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-multiselect-caret {\r\n flex-shrink: 0;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n margin: 0;\r\n padding: 0.2rem;\r\n border: 0;\r\n background: transparent;\r\n color: inherit;\r\n cursor: pointer;\r\n border-radius: var(--sg-radius-pill);\r\n}\r\n\r\n.sg-multiselect-caret:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 1px;\r\n}\r\n\r\n.sg-multiselect-caret:disabled {\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-multiselect-chips {\r\n display: flex;\r\n flex: 1;\r\n flex-wrap: wrap;\r\n gap: 0.35rem;\r\n align-items: center;\r\n min-width: 0;\r\n min-height: 1.75rem;\r\n text-align: left;\r\n}\r\n\r\n/* Empty-state placeholder — sit on the same optical line as normal select text */\r\n.sg-multiselect-chips .sg-select-value[data-placeholder=\"true\"] {\r\n display: flex;\r\n align-items: center;\r\n min-height: 1.75rem;\r\n line-height: 1.35;\r\n padding: 0;\r\n}\r\n\r\n.sg-multiselect-chip {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.15rem;\r\n max-width: 100%;\r\n /* Extra right padding so X hit area fits comfortably */\r\n padding: 0.2rem 0.15rem 0.2rem 0.6rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid\r\n color-mix(in srgb, var(--sg-accent) 22%, var(--sg-border-frost));\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-medium);\r\n line-height: 1.2;\r\n}\r\n\r\n.sg-multiselect-chip-label {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n padding-right: 0.1rem;\r\n}\r\n\r\n.sg-multiselect-chip-x {\r\n position: relative;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n /* Visible control ~28px — easy finger/mouse target */\r\n min-width: 1.75rem;\r\n min-height: 1.75rem;\r\n width: 1.75rem;\r\n height: 1.75rem;\r\n margin: 0;\r\n padding: 0;\r\n border: 0;\r\n border-radius: 999px;\r\n background: color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n cursor: pointer;\r\n color: var(--sg-fg-muted);\r\n line-height: 1;\r\n font-size: 1.05rem;\r\n font-family: inherit;\r\n flex-shrink: 0;\r\n}\r\n\r\n/* Invisible expanded hit box beyond the visible circle */\r\n.sg-multiselect-chip-x::before {\r\n content: \"\";\r\n position: absolute;\r\n inset: -0.2rem;\r\n border-radius: 999px;\r\n}\r\n\r\n.sg-multiselect-chip-x:hover:not(:disabled) {\r\n color: var(--sg-fg);\r\n background: color-mix(in srgb, var(--sg-fg) 12%, transparent);\r\n}\r\n\r\n.sg-multiselect-chip-x:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 1px;\r\n}\r\n\r\n.sg-multiselect-trigger .sg-select-chevron {\r\n margin-top: 0;\r\n flex-shrink: 0;\r\n}\r\n\r\n@keyframes sg-select-in {\r\n from {\r\n opacity: 0;\r\n transform: translateY(-6px) scale(0.98);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0) scale(1);\r\n }\r\n}\r\n\r\n@keyframes sg-select-in-top {\r\n from {\r\n opacity: 0;\r\n transform: translateY(6px) scale(0.98);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0) scale(1);\r\n }\r\n}\r\n\r\n/* —— Popover + DropdownMenu (overlay family, v1.1 Sprint A) —— */\r\n.sg-popover-root,\r\n.sg-menu-root {\r\n position: relative;\r\n display: inline-flex;\r\n vertical-align: middle;\r\n}\r\n\r\n/* —— ContextMenu target (panel rules live after .sg-menu-content) —— */\r\n/* Avoid display:contents — fixed menu + focus are more reliable with a real box. */\r\n.sg-context-root {\r\n display: block;\r\n}\r\n\r\n.sg-context-target {\r\n display: block;\r\n outline: none;\r\n}\r\n\r\n.sg-context-target:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 2px;\r\n border-radius: var(--sg-radius-md);\r\n}\r\n\r\n.sg-popover-trigger {\r\n display: inline-flex;\r\n}\r\n\r\n.sg-menu-trigger-fallback {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: var(--sg-space-2);\r\n min-height: 2.5rem;\r\n padding: 0 1rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n cursor: pointer;\r\n}\r\n\r\n.sg-menu-trigger-fallback:hover:not(:disabled) {\r\n background: var(--sg-surface-frost-strong);\r\n}\r\n\r\n.sg-menu-trigger-fallback:disabled {\r\n opacity: 0.45;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-popover-content,\r\n.sg-menu-content {\r\n position: absolute;\r\n z-index: var(--sg-z-dropdown);\r\n min-width: 12rem;\r\n max-width: min(20rem, 90vw);\r\n margin: 0;\r\n padding: var(--sg-space-2);\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background-color: var(--sg-surface-frost-strong);\r\n background-image: linear-gradient(\r\n 180deg,\r\n var(--sg-gloss-highlight) 0%,\r\n transparent 36%\r\n );\r\n box-shadow: var(--sg-shadow-elevated);\r\n backdrop-filter: blur(22px) saturate(1.35);\r\n -webkit-backdrop-filter: blur(22px) saturate(1.35);\r\n color: var(--sg-fg);\r\n animation: sg-select-in var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-popover-content {\r\n padding: var(--sg-space-4);\r\n min-width: 14rem;\r\n}\r\n\r\n.sg-popover-content[data-placement=\"bottom\"],\r\n.sg-menu-content[data-placement=\"bottom\"] {\r\n top: calc(100% + 0.4rem);\r\n bottom: auto;\r\n}\r\n\r\n.sg-popover-content[data-placement=\"top\"],\r\n.sg-menu-content[data-placement=\"top\"] {\r\n top: auto;\r\n bottom: calc(100% + 0.4rem);\r\n animation-name: sg-select-in-top;\r\n}\r\n\r\n.sg-popover-content[data-align=\"start\"],\r\n.sg-menu-content[data-align=\"start\"] {\r\n left: 0;\r\n right: auto;\r\n}\r\n\r\n.sg-popover-content[data-align=\"center\"],\r\n.sg-menu-content[data-align=\"center\"] {\r\n left: 50%;\r\n right: auto;\r\n /* `translate` is independent of enter animation `transform` */\r\n translate: -50% 0;\r\n}\r\n\r\n.sg-popover-content[data-align=\"end\"],\r\n.sg-menu-content[data-align=\"end\"] {\r\n left: auto;\r\n right: 0;\r\n}\r\n\r\n.sg-menu-content {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 0.15rem;\r\n max-height: min(18rem, 50dvh);\r\n overflow: auto;\r\n min-width: 11rem;\r\n}\r\n\r\n/* ContextMenu: fixed at pointer — must beat .sg-menu-content absolute rules */\r\n.sg-menu-content.sg-context-menu-content {\r\n position: fixed;\r\n top: auto;\r\n bottom: auto;\r\n right: auto;\r\n translate: none;\r\n z-index: var(--sg-z-dropdown);\r\n}\r\n\r\n.sg-menu-item {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n width: 100%;\r\n min-height: 2.35rem;\r\n padding: 0.45rem 0.85rem;\r\n border: var(--sg-border-width) solid transparent;\r\n border-radius: var(--sg-radius-pill);\r\n background: transparent;\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n letter-spacing: var(--sg-tracking-tight);\r\n text-align: left;\r\n cursor: pointer;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-menu-item:hover:not(:disabled),\r\n.sg-menu-item[data-highlighted=\"true\"]:not(:disabled) {\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 18%, transparent);\r\n}\r\n\r\n.sg-menu-item[data-destructive=\"true\"] {\r\n color: var(--sg-danger, #e11d48);\r\n}\r\n\r\n.sg-menu-item[data-destructive=\"true\"]:hover:not(:disabled),\r\n.sg-menu-item[data-destructive=\"true\"][data-highlighted=\"true\"]:not(:disabled) {\r\n background: color-mix(in srgb, var(--sg-danger, #e11d48) 14%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-danger, #e11d48) 22%, transparent);\r\n color: var(--sg-danger, #e11d48);\r\n}\r\n\r\n.sg-menu-item:disabled {\r\n opacity: 0.45;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-menu-item:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 1px;\r\n}\r\n\r\n.sg-menu-item-label {\r\n flex: 1;\r\n min-width: 0;\r\n}\r\n\r\n.sg-menu-item-shortcut {\r\n flex-shrink: 0;\r\n font-size: var(--sg-text-xs);\r\n color: var(--sg-fg-muted);\r\n font-weight: var(--sg-font-weight-medium);\r\n letter-spacing: 0.02em;\r\n}\r\n\r\n.sg-menu-separator {\r\n height: 1px;\r\n margin: 0.3rem 0.4rem;\r\n background: var(--sg-border-frost);\r\n border: 0;\r\n}\r\n\r\n.sg-menu-label {\r\n padding: 0.4rem 0.85rem 0.25rem;\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: 0.04em;\r\n text-transform: uppercase;\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n/* —— Modal —— */\r\n/* Portaled to body: always viewport-centered, above sticky header */\r\n.sg-modal-root {\r\n position: fixed;\r\n inset: 0;\r\n z-index: var(--sg-z-modal);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n padding: var(--sg-space-4);\r\n isolation: isolate;\r\n box-sizing: border-box;\r\n /* Prefer dynamic viewport so mobile browser chrome does not shift center */\r\n width: 100vw;\r\n min-height: 100vh;\r\n min-height: 100dvh;\r\n}\r\n\r\n.sg-modal-backdrop {\r\n position: absolute;\r\n inset: 0;\r\n border: 0;\r\n padding: 0;\r\n margin: 0;\r\n cursor: pointer;\r\n background: color-mix(in srgb, var(--sg-bg) 35%, rgba(15, 23, 42, 0.45));\r\n backdrop-filter: blur(8px) saturate(1.1);\r\n -webkit-backdrop-filter: blur(8px) saturate(1.1);\r\n animation: sg-fade-in var(--sg-duration-normal) var(--sg-ease-out);\r\n}\r\n\r\n.sg-modal-panel {\r\n position: relative;\r\n z-index: 1;\r\n width: min(100%, 28rem);\r\n max-height: min(90dvh, 40rem);\r\n overflow: auto;\r\n padding: var(--sg-space-6);\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-4);\r\n margin: 0;\r\n flex-shrink: 0;\r\n /* enter animation: motion-recipes.css via data-motion */\r\n}\r\n\r\n.sg-modal-panel-lg {\r\n width: min(100%, 36rem);\r\n}\r\n\r\n.sg-modal-header {\r\n display: flex;\r\n align-items: flex-start;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n}\r\n\r\n.sg-modal-title {\r\n margin: 0;\r\n font-size: var(--sg-text-xl);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n line-height: var(--sg-leading-tight);\r\n}\r\n\r\n.sg-modal-description {\r\n margin: 0.35rem 0 0;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg-muted);\r\n line-height: var(--sg-leading-normal);\r\n}\r\n\r\n.sg-modal-body {\r\n min-width: 0;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-modal-footer {\r\n display: flex;\r\n flex-wrap: wrap;\r\n justify-content: flex-end;\r\n gap: var(--sg-space-3);\r\n margin-top: var(--sg-space-2);\r\n}\r\n\r\n.sg-modal-close {\r\n flex-shrink: 0;\r\n}\r\n\r\n/* —— Toast —— */\r\n.sg-toast-viewport {\r\n position: fixed;\r\n z-index: var(--sg-z-toast);\r\n display: flex;\r\n flex-direction: column;\r\n /* Clear air between cards so soft shadows do not look like one smudge */\r\n gap: var(--sg-space-3);\r\n width: min(100% - 2rem, 24rem);\r\n max-height: calc(100dvh - 2 * var(--sg-space-4));\r\n overflow: auto;\r\n overscroll-behavior: contain;\r\n padding: 0;\r\n margin: 0;\r\n list-style: none;\r\n pointer-events: none;\r\n isolation: isolate;\r\n}\r\n\r\n.sg-toast-viewport-top-right {\r\n top: var(--sg-space-4);\r\n right: var(--sg-space-4);\r\n flex-direction: column;\r\n}\r\n\r\n/* Newest toast sits closest to the corner (stack grows upward) */\r\n.sg-toast-viewport-bottom-right,\r\n.sg-toast-viewport-bottom-center {\r\n flex-direction: column-reverse;\r\n}\r\n\r\n.sg-toast-viewport-bottom-right {\r\n bottom: var(--sg-space-4);\r\n right: var(--sg-space-4);\r\n}\r\n\r\n.sg-toast-viewport-bottom-center {\r\n bottom: var(--sg-space-4);\r\n left: 50%;\r\n transform: translateX(-50%);\r\n}\r\n\r\n/* Dedicated toast surface — elevated glass shadow is too soft/wide for stacks */\r\n.sg-surface-frost-toast {\r\n background: var(--sg-surface-frost-strong);\r\n color: var(--sg-fg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n border-radius: var(--sg-radius-lg);\r\n box-shadow: var(--sg-shadow-toast);\r\n background-image: linear-gradient(\r\n 180deg,\r\n var(--sg-gloss-highlight) 0%,\r\n transparent 48%\r\n );\r\n}\r\n\r\n.sg-toast {\r\n pointer-events: auto;\r\n display: grid;\r\n grid-template-columns: 1fr auto;\r\n gap: var(--sg-space-2) var(--sg-space-3);\r\n align-items: start;\r\n padding: var(--sg-space-4);\r\n width: 100%;\r\n /* Own stacking context so sibling shadows do not composite into a dirty band */\r\n isolation: isolate;\r\n position: relative;\r\n /* enter: motion-recipes.css via data-motion */\r\n}\r\n\r\n/* leave recipes live in motion-recipes.css (data-leaving + data-motion) */\r\n\r\n.sg-toast-title {\r\n margin: 0;\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n}\r\n\r\n.sg-toast-description {\r\n margin: 0.2rem 0 0;\r\n font-size: var(--sg-text-xs);\r\n color: var(--sg-fg-muted);\r\n line-height: var(--sg-leading-normal);\r\n grid-column: 1;\r\n}\r\n\r\n.sg-toast-close {\r\n grid-column: 2;\r\n grid-row: 1;\r\n}\r\n\r\n.sg-toast-success {\r\n border-color: color-mix(in srgb, var(--sg-success) 35%, var(--sg-border-glass));\r\n}\r\n\r\n.sg-toast-warning {\r\n border-color: color-mix(in srgb, var(--sg-warning) 35%, var(--sg-border-glass));\r\n}\r\n\r\n.sg-toast-danger {\r\n border-color: color-mix(in srgb, var(--sg-danger) 35%, var(--sg-border-glass));\r\n}\r\n\r\n@keyframes sg-fade-in {\r\n from {\r\n opacity: 0;\r\n }\r\n to {\r\n opacity: 1;\r\n }\r\n}\r\n\r\n@keyframes sg-scale-in {\r\n from {\r\n opacity: 0;\r\n transform: translateY(8px) scale(0.97);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0) scale(1);\r\n }\r\n}\r\n\r\n@keyframes sg-toast-in {\r\n from {\r\n opacity: 0;\r\n transform: translateY(10px);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n}\r\n\r\n@keyframes sg-toast-out {\r\n from {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translateY(8px);\r\n }\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .sg-modal-backdrop,\r\n .sg-modal-panel,\r\n .sg-toast,\r\n .sg-toast[data-leaving=\"true\"],\r\n .sg-select-menu,\r\n .sg-select-menu[data-placement=\"top\"] {\r\n animation: none;\r\n }\r\n}\r\n\r\n/* Mobile: sidebar unmounted (JS); collapse rail only applies on desktop mount. */\r\n@media (max-width: 899px) {\r\n .sg-shell-with-sidebar {\r\n grid-template-columns: 1fr;\r\n grid-template-areas:\r\n \"header\"\r\n \"main\";\r\n }\r\n\r\n .sg-shell-with-sidebar[data-collapsed=\"true\"] {\r\n grid-template-columns: 1fr;\r\n }\r\n}\r\n\r\n/* —— PageHeader —— */\r\n.sg-page-header {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-3);\r\n margin-bottom: var(--sg-space-5);\r\n box-sizing: border-box;\r\n /* plain = flush, no chrome */\r\n padding: 0;\r\n border: var(--sg-border-width) solid transparent;\r\n border-radius: 0;\r\n background: transparent;\r\n box-shadow: none;\r\n transition:\r\n background var(--sg-duration-normal) var(--sg-ease-out),\r\n border-color var(--sg-duration-normal) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-normal) var(--sg-ease-out),\r\n padding var(--sg-duration-fast) var(--sg-ease-out),\r\n border-radius var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n/* soft = frost tray (distinct from solid page bg) */\r\n.sg-page-header[data-look=\"soft\"] {\r\n padding: var(--sg-space-4) var(--sg-space-5);\r\n border-radius: var(--sg-radius-lg);\r\n background: var(--sg-surface-frost);\r\n border-color: var(--sg-border-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n/* solid = opaque panel, hard edge, subtle lift */\r\n.sg-page-header[data-look=\"solid\"] {\r\n padding: var(--sg-space-4) var(--sg-space-5);\r\n border-radius: var(--sg-radius-lg);\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow:\r\n 0 1px 0 color-mix(in srgb, var(--sg-fg) 5%, transparent),\r\n 0 10px 28px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n}\r\n\r\n/* glass = translucent + blur + accent-tinted rim (needs colorful stage behind) */\r\n.sg-page-header[data-look=\"glass\"] {\r\n padding: var(--sg-space-4) var(--sg-space-5);\r\n border-radius: var(--sg-radius-lg);\r\n background: var(--sg-surface-frost-strong, var(--sg-surface-frost));\r\n border-color: color-mix(in srgb, var(--sg-border-frost) 70%, var(--sg-accent) 22%);\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 12px 32px color-mix(in srgb, var(--sg-accent) 12%, transparent);\r\n}\r\n\r\n@media (prefers-reduced-transparency: no-preference) {\r\n .sg-page-header[data-look=\"glass\"] {\r\n background: color-mix(in srgb, var(--sg-surface-glass) 82%, transparent);\r\n backdrop-filter: blur(var(--sg-glass-blur)) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(var(--sg-glass-blur))\r\n saturate(var(--sg-glass-saturate));\r\n }\r\n}\r\n\r\n.sg-page-header-crumbs {\r\n margin: 0;\r\n}\r\n\r\n.sg-page-header-row {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: flex-start;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3) var(--sg-space-4);\r\n}\r\n\r\n.sg-page-header-text {\r\n min-width: 0;\r\n flex: 1 1 12rem;\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-1);\r\n}\r\n\r\n.sg-page-header-title {\r\n margin: 0;\r\n font-size: var(--sg-text-2xl);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n line-height: var(--sg-leading-tight);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-page-header[data-size=\"sm\"] .sg-page-header-title {\r\n font-size: var(--sg-text-xl);\r\n}\r\n\r\n.sg-page-header[data-size=\"lg\"] .sg-page-header-title {\r\n font-size: var(--sg-text-3xl);\r\n}\r\n\r\n.sg-page-header-description {\r\n margin: 0;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg-muted);\r\n max-width: 60ch;\r\n line-height: var(--sg-leading-normal);\r\n}\r\n\r\n.sg-page-header[data-size=\"lg\"] .sg-page-header-description {\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-page-header-actions {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n gap: var(--sg-space-2);\r\n flex-shrink: 0;\r\n}\r\n\r\n/* Compact AppShell embed for playground / docs demos */\r\n.sg-shell.sg-shell-demo {\r\n min-height: 17rem;\r\n height: 17rem;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n overflow: hidden;\r\n background: var(--sg-surface-solid);\r\n}\r\n\r\n.sg-shell.sg-shell-demo .sg-shell-sidebar {\r\n height: 100%;\r\n position: relative;\r\n top: auto;\r\n}\r\n\r\n.sg-shell.sg-shell-demo .sg-shell-header {\r\n position: relative;\r\n top: auto;\r\n padding: var(--sg-space-2) var(--sg-space-3);\r\n}\r\n\r\n.sg-shell.sg-shell-demo .sg-shell-header-inner {\r\n padding: 0.45rem 0.65rem;\r\n}\r\n\r\n.sg-shell.sg-shell-demo .sg-shell-main-inner {\r\n padding: var(--sg-space-4);\r\n}\r\n\r\n/* Stronger collapse read: wider gap + rail tint */\r\n.sg-shell-with-sidebar {\r\n transition: grid-template-columns var(--sg-duration-normal) var(--sg-ease-out);\r\n}\r\n\r\n.sg-shell-with-sidebar[data-collapsed=\"true\"] .sg-shell-sidebar {\r\n background: color-mix(in srgb, var(--sg-accent-soft) 55%, var(--sg-surface-frost));\r\n border-right-color: color-mix(in srgb, var(--sg-accent) 22%, var(--sg-border-glass));\r\n}\r\n\r\n.sg-shell-with-sidebar[data-collapsed=\"true\"] .sg-shell-nav-item {\r\n min-height: 2.75rem;\r\n border-radius: var(--sg-radius-sm);\r\n}\r\n\r\n.sg-shell-with-sidebar[data-collapsed=\"true\"] .sg-shell-nav-glyph,\r\n.sg-shell-with-sidebar[data-collapsed=\"true\"] .sg-shell-nav-icon {\r\n width: 1.5rem;\r\n height: 1.5rem;\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n/* —— SettingsSection (v1.5b) —— */\r\n.sg-settings-section {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-4);\r\n width: 100%;\r\n padding: var(--sg-space-5);\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n box-sizing: border-box;\r\n}\r\n\r\n.sg-settings-section[data-look=\"solid\"] {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow:\r\n 0 1px 0 color-mix(in srgb, var(--sg-fg) 5%, transparent),\r\n 0 8px 24px color-mix(in srgb, var(--sg-fg) 5%, transparent);\r\n}\r\n\r\n.sg-settings-section[data-look=\"glass\"] {\r\n background: var(--sg-surface-frost-strong, var(--sg-surface-frost));\r\n border-color: color-mix(in srgb, var(--sg-border-frost) 70%, var(--sg-accent) 20%);\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 12px 28px color-mix(in srgb, var(--sg-accent) 10%, transparent);\r\n}\r\n\r\n@media (prefers-reduced-transparency: no-preference) {\r\n .sg-settings-section[data-look=\"glass\"] {\r\n background: color-mix(in srgb, var(--sg-surface-glass) 84%, transparent);\r\n backdrop-filter: blur(var(--sg-glass-blur)) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(var(--sg-glass-blur))\r\n saturate(var(--sg-glass-saturate));\r\n }\r\n}\r\n\r\n.sg-settings-section[data-look=\"plain\"] {\r\n padding: 0;\r\n border: none;\r\n background: transparent;\r\n box-shadow: none;\r\n border-radius: 0;\r\n}\r\n\r\n.sg-settings-section[data-density=\"compact\"] {\r\n gap: var(--sg-space-3);\r\n padding: var(--sg-space-4);\r\n}\r\n\r\n.sg-settings-section[data-look=\"plain\"][data-density=\"compact\"] {\r\n padding: 0;\r\n}\r\n\r\n.sg-settings-section-header {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: flex-start;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n}\r\n\r\n.sg-settings-section-heading {\r\n min-width: 0;\r\n flex: 1 1 12rem;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 0.25rem;\r\n}\r\n\r\n.sg-settings-section-title {\r\n margin: 0;\r\n font-size: var(--sg-text-lg);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n line-height: var(--sg-leading-tight);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-settings-section-description {\r\n margin: 0;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg-muted);\r\n line-height: var(--sg-leading-normal);\r\n max-width: 56ch;\r\n}\r\n\r\n.sg-settings-section-actions {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n gap: var(--sg-space-2);\r\n flex-shrink: 0;\r\n}\r\n\r\n.sg-settings-section-body {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-4);\r\n min-width: 0;\r\n}\r\n\r\n.sg-settings-section[data-density=\"compact\"] .sg-settings-section-body {\r\n gap: var(--sg-space-3);\r\n}\r\n\r\n/* —— CommandPalette (v1.5b minimal) —— */\r\n.sg-command-root {\r\n position: fixed;\r\n inset: 0;\r\n z-index: var(--sg-z-modal);\r\n display: flex;\r\n align-items: flex-start;\r\n justify-content: center;\r\n padding: min(18vh, 7rem) var(--sg-space-4) var(--sg-space-6);\r\n box-sizing: border-box;\r\n}\r\n\r\n.sg-command-backdrop {\r\n position: absolute;\r\n inset: 0;\r\n margin: 0;\r\n padding: 0;\r\n border: 0;\r\n cursor: pointer;\r\n background: color-mix(in srgb, var(--sg-fg) 28%, transparent);\r\n backdrop-filter: blur(4px);\r\n -webkit-backdrop-filter: blur(4px);\r\n}\r\n\r\n.sg-command-panel {\r\n position: relative;\r\n z-index: 1;\r\n width: min(34rem, 100%);\r\n max-height: min(28rem, 70vh);\r\n display: flex;\r\n flex-direction: column;\r\n overflow: hidden;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid\r\n color-mix(in srgb, var(--sg-border-frost) 70%, var(--sg-accent) 18%);\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 24px 64px color-mix(in srgb, var(--sg-fg) 18%, transparent);\r\n box-sizing: border-box;\r\n}\r\n\r\n.sg-command-sr-only {\r\n position: absolute;\r\n width: 1px;\r\n height: 1px;\r\n padding: 0;\r\n margin: -1px;\r\n overflow: hidden;\r\n clip: rect(0, 0, 0, 0);\r\n white-space: nowrap;\r\n border: 0;\r\n}\r\n\r\n.sg-command-search {\r\n display: flex;\r\n align-items: center;\r\n gap: var(--sg-space-2);\r\n padding: 0.85rem 1rem;\r\n border-bottom: var(--sg-border-width) solid var(--sg-border-frost);\r\n flex-shrink: 0;\r\n}\r\n\r\n.sg-command-search-icon {\r\n flex-shrink: 0;\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-lg);\r\n line-height: 1;\r\n opacity: 0.85;\r\n}\r\n\r\n.sg-command-input {\r\n flex: 1 1 auto;\r\n min-width: 0;\r\n border: 0;\r\n outline: none;\r\n background: transparent;\r\n color: var(--sg-fg);\r\n font: inherit;\r\n font-size: var(--sg-text-base);\r\n font-weight: var(--sg-font-weight-medium);\r\n line-height: 1.4;\r\n padding: 0.15rem 0;\r\n}\r\n\r\n.sg-command-input::placeholder {\r\n color: var(--sg-fg-muted);\r\n font-weight: var(--sg-font-weight-normal);\r\n}\r\n\r\n.sg-command-kbd {\r\n flex-shrink: 0;\r\n display: inline-flex;\r\n align-items: center;\r\n min-height: 1.35rem;\r\n padding: 0.1rem 0.4rem;\r\n border-radius: var(--sg-radius-xs);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: color-mix(in srgb, var(--sg-surface-solid) 70%, transparent);\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-xs);\r\n font-family: inherit;\r\n text-transform: lowercase;\r\n}\r\n\r\n.sg-command-list {\r\n overflow: auto;\r\n padding: 0.45rem;\r\n flex: 1 1 auto;\r\n min-height: 0;\r\n}\r\n\r\n.sg-command-empty {\r\n padding: 1.5rem 1rem;\r\n text-align: center;\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-sm);\r\n}\r\n\r\n.sg-command-group {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 0.15rem;\r\n}\r\n\r\n.sg-command-group + .sg-command-group {\r\n margin-top: 0.35rem;\r\n}\r\n\r\n.sg-command-group-label {\r\n padding: 0.4rem 0.65rem 0.25rem;\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: 0.04em;\r\n text-transform: uppercase;\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-command-item {\r\n display: flex;\r\n align-items: center;\r\n gap: 0.65rem;\r\n width: 100%;\r\n min-height: 2.6rem;\r\n padding: 0.5rem 0.65rem;\r\n border-radius: var(--sg-radius-sm);\r\n border: var(--sg-border-width) solid transparent;\r\n cursor: pointer;\r\n box-sizing: border-box;\r\n color: var(--sg-fg);\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-command-item-active,\r\n.sg-command-item[data-active=\"true\"] {\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 22%, transparent);\r\n}\r\n\r\n.sg-command-item-disabled {\r\n opacity: 0.45;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-command-item-icon {\r\n flex-shrink: 0;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 1.35rem;\r\n height: 1.35rem;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-command-item-active .sg-command-item-icon {\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-command-item-text {\r\n min-width: 0;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 0.1rem;\r\n}\r\n\r\n.sg-command-item-label {\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n line-height: 1.3;\r\n}\r\n\r\n.sg-command-item-desc {\r\n font-size: var(--sg-text-xs);\r\n color: var(--sg-fg-muted);\r\n line-height: 1.3;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .sg-command-root,\r\n .sg-command-panel,\r\n .sg-command-item {\r\n transition: none;\r\n }\r\n}\r\n\r\n\r\n/* —— Spinner (standalone) —— */\r\n.sg-spinner {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-spinner-visual {\r\n display: block;\r\n width: 1.25rem;\r\n height: 1.25rem;\r\n border-radius: 50%;\r\n border: 2px solid color-mix(in srgb, currentColor 22%, transparent);\r\n border-top-color: currentColor;\r\n animation: sg-spin 0.7s linear infinite;\r\n}\r\n\r\n.sg-spinner-sm .sg-spinner-visual {\r\n width: 0.9rem;\r\n height: 0.9rem;\r\n border-width: 1.5px;\r\n}\r\n\r\n.sg-spinner-lg .sg-spinner-visual {\r\n width: 1.75rem;\r\n height: 1.75rem;\r\n border-width: 2.5px;\r\n}\r\n\r\n/* —— Separator —— */\r\n.sg-separator {\r\n display: block;\r\n width: 100%;\r\n height: 1px;\r\n border: 0;\r\n margin: var(--sg-space-4) 0;\r\n background: linear-gradient(\r\n 90deg,\r\n transparent,\r\n var(--sg-border-subtle) 12%,\r\n var(--sg-border-subtle) 88%,\r\n transparent\r\n );\r\n flex-shrink: 0;\r\n}\r\n\r\n.sg-separator-vertical {\r\n display: inline-block;\r\n width: 1px;\r\n height: 1.25rem;\r\n margin: 0 var(--sg-space-3);\r\n align-self: center;\r\n background: linear-gradient(\r\n 180deg,\r\n transparent,\r\n var(--sg-border-subtle) 12%,\r\n var(--sg-border-subtle) 88%,\r\n transparent\r\n );\r\n}\r\n\r\n/* —— Skeleton —— */\r\n.sg-skeleton {\r\n display: block;\r\n width: 100%;\r\n height: 0.875rem;\r\n background: linear-gradient(\r\n 90deg,\r\n color-mix(in srgb, var(--sg-fg-muted) 10%, var(--sg-surface-solid)) 0%,\r\n color-mix(in srgb, var(--sg-fg-muted) 18%, var(--sg-surface-solid)) 45%,\r\n color-mix(in srgb, var(--sg-fg-muted) 10%, var(--sg-surface-solid)) 100%\r\n );\r\n background-size: 200% 100%;\r\n animation: sg-skeleton-shimmer 1.4s ease-in-out infinite;\r\n}\r\n\r\n.sg-skeleton-circle {\r\n border-radius: 50%;\r\n width: 2.5rem;\r\n height: 2.5rem;\r\n}\r\n\r\n.sg-skeleton-rounded-sm {\r\n border-radius: var(--sg-radius-xs);\r\n}\r\n\r\n.sg-skeleton-rounded-md {\r\n border-radius: var(--sg-radius-sm);\r\n}\r\n\r\n.sg-skeleton-rounded-lg {\r\n border-radius: var(--sg-radius-md);\r\n}\r\n\r\n.sg-skeleton-rounded-full {\r\n border-radius: var(--sg-radius-pill);\r\n}\r\n\r\n@keyframes sg-skeleton-shimmer {\r\n 0% {\r\n background-position: 100% 0;\r\n }\r\n 100% {\r\n background-position: -100% 0;\r\n }\r\n}\r\n\r\n/* —— Alert / Callout —— */\r\n.sg-alert {\r\n display: flex;\r\n align-items: flex-start;\r\n gap: var(--sg-space-3);\r\n padding: var(--sg-space-3) var(--sg-space-4);\r\n border-radius: var(--sg-radius-md);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n color: var(--sg-fg);\r\n font-size: var(--sg-text-sm);\r\n line-height: var(--sg-leading-normal);\r\n}\r\n\r\n.sg-alert-marker {\r\n flex-shrink: 0;\r\n width: 0.5rem;\r\n height: 0.5rem;\r\n margin-top: 0.4rem;\r\n border-radius: 50%;\r\n background: currentColor;\r\n}\r\n\r\n.sg-alert-icon {\r\n flex-shrink: 0;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 1.25rem;\r\n height: 1.25rem;\r\n margin-top: 0.1rem;\r\n}\r\n\r\n.sg-alert-body {\r\n min-width: 0;\r\n flex: 1;\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-1);\r\n}\r\n\r\n.sg-alert-title {\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n}\r\n\r\n.sg-alert-content {\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-alert-info {\r\n border-color: color-mix(in srgb, var(--sg-accent) 35%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-accent-soft) 70%, var(--sg-surface-frost));\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-alert-info .sg-alert-marker,\r\n.sg-alert-info .sg-alert-icon {\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-alert-success {\r\n border-color: color-mix(in srgb, var(--sg-success) 40%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-success) 12%, var(--sg-surface-frost));\r\n}\r\n\r\n.sg-alert-success .sg-alert-marker,\r\n.sg-alert-success .sg-alert-icon {\r\n color: var(--sg-success);\r\n}\r\n\r\n.sg-alert-warning {\r\n border-color: color-mix(in srgb, var(--sg-warning) 40%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-warning) 12%, var(--sg-surface-frost));\r\n}\r\n\r\n.sg-alert-warning .sg-alert-marker,\r\n.sg-alert-warning .sg-alert-icon {\r\n color: var(--sg-warning);\r\n}\r\n\r\n.sg-alert-danger {\r\n border-color: color-mix(in srgb, var(--sg-danger) 40%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-danger) 12%, var(--sg-surface-frost));\r\n}\r\n\r\n.sg-alert-danger .sg-alert-marker,\r\n.sg-alert-danger .sg-alert-icon {\r\n color: var(--sg-danger);\r\n}\r\n\r\n/* —— Progress (linear) —— */\r\n.sg-progress {\r\n display: block;\r\n width: 100%;\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-progress-track {\r\n position: relative;\r\n width: 100%;\r\n height: 0.5rem;\r\n overflow: hidden;\r\n border-radius: var(--sg-radius-pill);\r\n background: color-mix(in srgb, var(--sg-fg-muted) 12%, var(--sg-surface-solid));\r\n box-shadow: inset 0 1px 0 color-mix(in srgb, var(--sg-gloss-highlight) 55%, transparent);\r\n}\r\n\r\n.sg-progress-sm .sg-progress-track {\r\n height: 0.3rem;\r\n}\r\n\r\n.sg-progress-lg .sg-progress-track {\r\n height: 0.7rem;\r\n}\r\n\r\n.sg-progress-look-solid .sg-progress-track {\r\n background: var(--sg-surface-solid);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-progress-look-glass .sg-progress-track {\r\n background: var(--sg-surface-frost);\r\n border: var(--sg-border-width) solid var(--sg-border-glass);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-progress-look-accent .sg-progress-track {\r\n height: 0.6rem;\r\n background: color-mix(in srgb, var(--sg-accent) 14%, var(--sg-surface-solid));\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, var(--sg-gloss-highlight) 50%, transparent),\r\n 0 0 10px color-mix(in srgb, currentColor 12%, transparent);\r\n}\r\n\r\n.sg-progress-look-accent .sg-progress-fill {\r\n box-shadow:\r\n 0 0 0 1px color-mix(in srgb, currentColor 25%, transparent),\r\n 0 0 12px color-mix(in srgb, currentColor 40%, transparent);\r\n}\r\n\r\n.sg-progress-look-striped .sg-progress-fill {\r\n background-image:\r\n linear-gradient(\r\n 45deg,\r\n color-mix(in srgb, #fff 18%, transparent) 25%,\r\n transparent 25%,\r\n transparent 50%,\r\n color-mix(in srgb, #fff 18%, transparent) 50%,\r\n color-mix(in srgb, #fff 18%, transparent) 75%,\r\n transparent 75%,\r\n transparent\r\n ),\r\n linear-gradient(90deg, color-mix(in srgb, currentColor 88%, transparent), currentColor);\r\n background-size: 0.75rem 0.75rem, 100% 100%;\r\n animation: sg-progress-stripes 0.9s linear infinite;\r\n}\r\n\r\n.sg-progress-look-striped.sg-progress-indeterminate .sg-progress-fill {\r\n animation:\r\n sg-progress-indeterminate 1.2s ease-in-out infinite,\r\n sg-progress-stripes 0.9s linear infinite;\r\n}\r\n\r\n@keyframes sg-progress-stripes {\r\n 0% {\r\n background-position: 0 0, 0 0;\r\n }\r\n 100% {\r\n background-position: 0.75rem 0, 0 0;\r\n }\r\n}\r\n\r\n/* Progress semantic fill */\r\n.sg-progress-success {\r\n color: var(--sg-success);\r\n}\r\n\r\n.sg-progress-warning {\r\n color: var(--sg-warning);\r\n}\r\n\r\n.sg-progress-danger {\r\n color: var(--sg-danger);\r\n}\r\n\r\n.sg-progress-muted {\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-progress-fill {\r\n height: 100%;\r\n width: 0%;\r\n border-radius: inherit;\r\n background: linear-gradient(\r\n 90deg,\r\n color-mix(in srgb, currentColor 88%, transparent),\r\n currentColor\r\n );\r\n box-shadow: 0 0 0 1px color-mix(in srgb, currentColor 18%, transparent);\r\n transition: width 180ms ease;\r\n}\r\n\r\n.sg-progress-indeterminate .sg-progress-fill {\r\n width: 36%;\r\n transition: none;\r\n animation: sg-progress-indeterminate 1.2s ease-in-out infinite;\r\n}\r\n\r\n@keyframes sg-progress-indeterminate {\r\n 0% {\r\n transform: translateX(-120%);\r\n }\r\n 100% {\r\n transform: translateX(320%);\r\n }\r\n}\r\n\r\n/* —— StatusDot —— */\r\n.sg-status-dot {\r\n --sg-status-dot-color: var(--sg-success);\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 0.625rem;\r\n height: 0.625rem;\r\n flex-shrink: 0;\r\n vertical-align: middle;\r\n color: var(--sg-status-dot-color);\r\n}\r\n\r\n.sg-status-dot-sm {\r\n width: 0.45rem;\r\n height: 0.45rem;\r\n}\r\n\r\n.sg-status-dot-lg {\r\n width: 0.8rem;\r\n height: 0.8rem;\r\n}\r\n\r\n.sg-status-dot-core {\r\n display: block;\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 50%;\r\n background: currentColor;\r\n box-shadow:\r\n 0 0 0 1px color-mix(in srgb, var(--sg-surface-solid) 70%, transparent),\r\n 0 0 0 2px color-mix(in srgb, currentColor 22%, transparent);\r\n}\r\n\r\n.sg-status-dot-online {\r\n --sg-status-dot-color: var(--sg-success);\r\n}\r\n\r\n.sg-status-dot-busy {\r\n --sg-status-dot-color: var(--sg-danger);\r\n}\r\n\r\n.sg-status-dot-offline {\r\n --sg-status-dot-color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-status-dot-away {\r\n --sg-status-dot-color: var(--sg-warning);\r\n}\r\n\r\n/* StatusDot looks */\r\n.sg-status-dot-look-solid .sg-status-dot-core {\r\n box-shadow: none;\r\n}\r\n\r\n.sg-status-dot-look-outline .sg-status-dot-core {\r\n background: transparent;\r\n box-shadow: none;\r\n border: 2px solid currentColor;\r\n}\r\n\r\n.sg-status-dot-look-glow .sg-status-dot-core {\r\n box-shadow:\r\n 0 0 0 1px color-mix(in srgb, var(--sg-surface-solid) 55%, transparent),\r\n 0 0 0 3px color-mix(in srgb, currentColor 28%, transparent),\r\n 0 0 10px color-mix(in srgb, currentColor 55%, transparent);\r\n}\r\n\r\n/* custom color: set --sg-status-dot-color via inline style */\r\n\r\n.sg-status-dot-pulse {\r\n position: relative;\r\n}\r\n\r\n.sg-status-dot-pulse::before {\r\n content: \"\";\r\n position: absolute;\r\n inset: -2px;\r\n border-radius: 50%;\r\n border: 1.5px solid currentColor;\r\n opacity: 0.55;\r\n animation: sg-status-dot-ping 1.6s ease-out infinite;\r\n}\r\n\r\n@keyframes sg-status-dot-ping {\r\n 0% {\r\n transform: scale(0.85);\r\n opacity: 0.55;\r\n }\r\n 70% {\r\n transform: scale(1.55);\r\n opacity: 0;\r\n }\r\n 100% {\r\n transform: scale(1.55);\r\n opacity: 0;\r\n }\r\n}\r\n\r\n/* —— Slider —— */\r\n.sg-slider {\r\n --sg-slider-pct: 0%;\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n width: 100%;\r\n min-height: var(--sg-touch-min);\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-slider[data-disabled=\"true\"] {\r\n opacity: 0.55;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-slider-label-row {\r\n display: flex;\r\n align-items: baseline;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n}\r\n\r\n.sg-slider-value {\r\n font-size: var(--sg-text-sm);\r\n font-variant-numeric: tabular-nums;\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-slider-input {\r\n -webkit-appearance: none;\r\n appearance: none;\r\n width: 100%;\r\n height: 0.45rem;\r\n margin: 0;\r\n border-radius: var(--sg-radius-pill);\r\n background: linear-gradient(\r\n 90deg,\r\n currentColor 0%,\r\n currentColor var(--sg-slider-pct),\r\n color-mix(in srgb, var(--sg-fg-muted) 14%, var(--sg-surface-solid))\r\n var(--sg-slider-pct),\r\n color-mix(in srgb, var(--sg-fg-muted) 14%, var(--sg-surface-solid)) 100%\r\n );\r\n box-shadow: inset 0 1px 0 color-mix(in srgb, var(--sg-gloss-highlight) 50%, transparent);\r\n cursor: pointer;\r\n}\r\n\r\n.sg-slider-sm .sg-slider-input {\r\n height: 0.3rem;\r\n}\r\n\r\n.sg-slider-lg .sg-slider-input {\r\n height: 0.6rem;\r\n}\r\n\r\n.sg-slider-input:focus {\r\n outline: none;\r\n}\r\n\r\n.sg-slider-input:focus-visible {\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, var(--sg-gloss-highlight) 50%, transparent),\r\n 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-slider-input::-webkit-slider-thumb {\r\n -webkit-appearance: none;\r\n appearance: none;\r\n width: 1.15rem;\r\n height: 1.15rem;\r\n margin-top: 0;\r\n border-radius: 50%;\r\n border: 2px solid color-mix(in srgb, var(--sg-surface-solid) 80%, transparent);\r\n background: currentColor;\r\n box-shadow:\r\n 0 1px 3px color-mix(in srgb, var(--sg-fg) 18%, transparent),\r\n 0 0 0 1px color-mix(in srgb, currentColor 25%, transparent);\r\n cursor: grab;\r\n}\r\n\r\n.sg-slider-sm .sg-slider-input::-webkit-slider-thumb {\r\n width: 0.95rem;\r\n height: 0.95rem;\r\n}\r\n\r\n.sg-slider-lg .sg-slider-input::-webkit-slider-thumb {\r\n width: 1.35rem;\r\n height: 1.35rem;\r\n}\r\n\r\n.sg-slider-input::-moz-range-thumb {\r\n width: 1.15rem;\r\n height: 1.15rem;\r\n border-radius: 50%;\r\n border: 2px solid color-mix(in srgb, var(--sg-surface-solid) 80%, transparent);\r\n background: currentColor;\r\n box-shadow:\r\n 0 1px 3px color-mix(in srgb, var(--sg-fg) 18%, transparent),\r\n 0 0 0 1px color-mix(in srgb, currentColor 25%, transparent);\r\n cursor: grab;\r\n}\r\n\r\n.sg-slider-input::-moz-range-track {\r\n height: 0.45rem;\r\n border-radius: var(--sg-radius-pill);\r\n background: transparent;\r\n}\r\n\r\n/* Slider looks */\r\n.sg-slider-look-solid {\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-slider-look-solid .sg-slider-input {\r\n background: linear-gradient(\r\n 90deg,\r\n var(--sg-accent) 0%,\r\n var(--sg-accent) var(--sg-slider-pct),\r\n var(--sg-surface-solid) var(--sg-slider-pct),\r\n var(--sg-surface-solid) 100%\r\n );\r\n box-shadow: inset 0 0 0 1px var(--sg-border-subtle);\r\n}\r\n\r\n.sg-slider-look-solid .sg-slider-input::-webkit-slider-thumb {\r\n background: var(--sg-surface-solid);\r\n border: 2px solid var(--sg-accent);\r\n box-shadow: 0 1px 4px color-mix(in srgb, var(--sg-fg) 16%, transparent);\r\n}\r\n\r\n.sg-slider-look-solid .sg-slider-input::-moz-range-thumb {\r\n background: var(--sg-surface-solid);\r\n border: 2px solid var(--sg-accent);\r\n}\r\n\r\n.sg-slider-look-glass {\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-slider-look-glass .sg-slider-input {\r\n background: linear-gradient(\r\n 90deg,\r\n color-mix(in srgb, var(--sg-accent) 75%, transparent) 0%,\r\n color-mix(in srgb, var(--sg-accent) 75%, transparent) var(--sg-slider-pct),\r\n var(--sg-surface-frost) var(--sg-slider-pct),\r\n var(--sg-surface-frost) 100%\r\n );\r\n border: var(--sg-border-width) solid var(--sg-border-glass);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-slider-look-glass .sg-slider-input::-webkit-slider-thumb {\r\n background: var(--sg-surface-frost);\r\n border: 2px solid var(--sg-accent);\r\n backdrop-filter: blur(6px);\r\n}\r\n\r\n.sg-slider-look-glass .sg-slider-input::-moz-range-thumb {\r\n background: var(--sg-surface-frost);\r\n border: 2px solid var(--sg-accent);\r\n}\r\n\r\n.sg-slider-look-accent {\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-slider-look-accent .sg-slider-input {\r\n height: 0.55rem;\r\n background: linear-gradient(\r\n 90deg,\r\n var(--sg-accent) 0%,\r\n color-mix(in srgb, var(--sg-accent) 70%, #fff) var(--sg-slider-pct),\r\n color-mix(in srgb, var(--sg-accent) 16%, var(--sg-surface-solid))\r\n var(--sg-slider-pct),\r\n color-mix(in srgb, var(--sg-accent) 16%, var(--sg-surface-solid)) 100%\r\n );\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, var(--sg-gloss-highlight) 60%, transparent),\r\n 0 0 12px color-mix(in srgb, var(--sg-accent) 22%, transparent);\r\n}\r\n\r\n.sg-slider-look-accent .sg-slider-input::-webkit-slider-thumb {\r\n width: 1.3rem;\r\n height: 1.3rem;\r\n background: var(--sg-accent);\r\n border: 3px solid var(--sg-surface-solid);\r\n box-shadow:\r\n 0 0 0 2px color-mix(in srgb, var(--sg-accent) 35%, transparent),\r\n 0 2px 8px color-mix(in srgb, var(--sg-accent) 40%, transparent);\r\n}\r\n\r\n.sg-slider-look-accent .sg-slider-input::-moz-range-thumb {\r\n width: 1.3rem;\r\n height: 1.3rem;\r\n background: var(--sg-accent);\r\n border: 3px solid var(--sg-surface-solid);\r\n}\r\n\r\n/* —— NumberInput ——\r\n Softglass quantity pattern: [ − ] value [ + ]\r\n Soft pill steppers + solid center field (not Windows vertical spinner).\r\n*/\r\n.sg-number-input {\r\n display: inline-grid;\r\n grid-template-columns: auto minmax(3.25rem, 1fr) auto;\r\n align-items: center;\r\n gap: var(--sg-space-2);\r\n width: 100%;\r\n max-width: 14rem;\r\n padding: 0.25rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n transition: border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-number-input.sg-input-block {\r\n max-width: none;\r\n}\r\n\r\n.sg-number-input-plain {\r\n grid-template-columns: 1fr;\r\n max-width: none;\r\n border-radius: var(--sg-radius-sm);\r\n background: var(--sg-surface-solid);\r\n padding: 0;\r\n box-shadow: none;\r\n}\r\n\r\n.sg-number-input:focus-within {\r\n border-color: var(--sg-accent);\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-number-input[data-invalid=\"true\"] {\r\n border-color: var(--sg-danger);\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 0 0 3px color-mix(in srgb, var(--sg-danger) 22%, transparent);\r\n}\r\n\r\n.sg-number-input[data-disabled=\"true\"] {\r\n opacity: 0.55;\r\n}\r\n\r\n.sg-number-input-field {\r\n width: 100%;\r\n min-width: 0;\r\n min-height: calc(var(--sg-touch-min) - 0.5rem);\r\n margin: 0;\r\n padding: 0.35rem 0.5rem;\r\n border: 0;\r\n border-radius: var(--sg-radius-sm);\r\n background: var(--sg-surface-solid);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-base);\r\n font-variant-numeric: tabular-nums;\r\n font-weight: var(--sg-font-weight-medium);\r\n text-align: center;\r\n line-height: 1.2;\r\n box-shadow: inset 0 0 0 1px var(--sg-border-subtle);\r\n}\r\n\r\n.sg-number-input-sm .sg-number-input-field {\r\n min-height: 1.85rem;\r\n font-size: var(--sg-text-sm);\r\n}\r\n\r\n.sg-number-input-lg .sg-number-input-field {\r\n min-height: 2.5rem;\r\n font-size: var(--sg-text-lg);\r\n}\r\n\r\n.sg-number-input-plain .sg-number-input-field {\r\n min-height: var(--sg-touch-min);\r\n padding: 0.625rem 1rem;\r\n text-align: start;\r\n border-radius: var(--sg-radius-sm);\r\n box-shadow: none;\r\n font-weight: var(--sg-font-weight-normal);\r\n}\r\n\r\n.sg-number-input-field:focus {\r\n outline: none;\r\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sg-accent) 45%, var(--sg-border-subtle));\r\n}\r\n\r\n.sg-number-input-field:disabled {\r\n cursor: not-allowed;\r\n}\r\n\r\n/* Hide native number spinners — custom soft steppers */\r\n.sg-number-input-field::-webkit-outer-spin-button,\r\n.sg-number-input-field::-webkit-inner-spin-button {\r\n -webkit-appearance: none;\r\n margin: 0;\r\n}\r\n\r\n.sg-number-input-field[type=\"number\"] {\r\n -moz-appearance: textfield;\r\n appearance: textfield;\r\n}\r\n\r\n.sg-number-input-step {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n flex-shrink: 0;\r\n width: calc(var(--sg-touch-min) - 0.35rem);\r\n height: calc(var(--sg-touch-min) - 0.35rem);\r\n margin: 0;\r\n padding: 0;\r\n border: var(--sg-border-width) solid\r\n color-mix(in srgb, var(--sg-accent) 22%, var(--sg-border-subtle));\r\n border-radius: 50%;\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n font-size: 1.15rem;\r\n font-weight: var(--sg-font-weight-semibold);\r\n line-height: 1;\r\n cursor: pointer;\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n transform var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-number-input-sm .sg-number-input-step {\r\n width: 1.85rem;\r\n height: 1.85rem;\r\n font-size: 1rem;\r\n}\r\n\r\n.sg-number-input-lg .sg-number-input-step {\r\n width: 2.5rem;\r\n height: 2.5rem;\r\n font-size: 1.25rem;\r\n}\r\n\r\n.sg-number-input-step:hover:not(:disabled) {\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-number-input-step:active:not(:disabled) {\r\n transform: scale(0.96);\r\n}\r\n\r\n.sg-number-input-step:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-number-input-step:disabled {\r\n opacity: 0.35;\r\n cursor: not-allowed;\r\n background: color-mix(in srgb, var(--sg-fg-muted) 8%, var(--sg-surface-solid));\r\n border-color: var(--sg-border-subtle);\r\n color: var(--sg-fg-muted);\r\n box-shadow: none;\r\n}\r\n\r\n/* NumberInput looks */\r\n.sg-number-input-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-number-input-look-solid .sg-number-input-step {\r\n background: color-mix(in srgb, var(--sg-fg-muted) 10%, var(--sg-surface-solid));\r\n border-color: var(--sg-border-subtle);\r\n color: var(--sg-fg);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-number-input-look-solid .sg-number-input-step:hover:not(:disabled) {\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-number-input-look-outline {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 35%, var(--sg-border-subtle));\r\n box-shadow: none;\r\n}\r\n\r\n.sg-number-input-look-outline .sg-number-input-field {\r\n background: transparent;\r\n box-shadow: inset 0 0 0 1px var(--sg-border-subtle);\r\n}\r\n\r\n.sg-number-input-look-outline .sg-number-input-step {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 40%, var(--sg-border-subtle));\r\n color: var(--sg-accent);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-number-input-look-outline .sg-number-input-step:hover:not(:disabled) {\r\n background: var(--sg-accent-soft);\r\n border-color: var(--sg-accent);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-number-input-look-ghost {\r\n background: transparent;\r\n border-color: transparent;\r\n box-shadow: none;\r\n padding: 0;\r\n gap: var(--sg-space-1);\r\n}\r\n\r\n.sg-number-input-look-ghost .sg-number-input-field {\r\n background: color-mix(in srgb, var(--sg-fg-muted) 8%, transparent);\r\n box-shadow: none;\r\n border-radius: var(--sg-radius-pill);\r\n}\r\n\r\n.sg-number-input-look-ghost .sg-number-input-step {\r\n background: transparent;\r\n border-color: transparent;\r\n color: var(--sg-fg-muted);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-number-input-look-ghost .sg-number-input-step:hover:not(:disabled) {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-number-input-look-ghost:focus-within {\r\n box-shadow: none;\r\n}\r\n\r\n.sg-number-input-look-ghost:focus-within .sg-number-input-field {\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .sg-number-input-step {\r\n transition: none;\r\n }\r\n\r\n .sg-number-input-step:active:not(:disabled) {\r\n transform: none;\r\n }\r\n}\r\n\r\n/* —— FileField —— */\r\n.sg-file-field {\r\n display: grid;\r\n gap: var(--sg-space-2);\r\n width: 100%;\r\n}\r\n\r\n.sg-file-field[data-disabled=\"true\"] {\r\n opacity: 0.55;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-file-field-trigger {\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n gap: var(--sg-space-3);\r\n min-height: var(--sg-touch-min);\r\n padding: 0.4rem 0.5rem 0.4rem 0.4rem;\r\n border-radius: var(--sg-radius-sm);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-solid);\r\n transition: border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-file-field-trigger:focus-within {\r\n border-color: var(--sg-accent);\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-file-field[data-invalid=\"true\"] .sg-file-field-trigger {\r\n border-color: var(--sg-danger);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-danger) 22%, transparent);\r\n}\r\n\r\n.sg-file-field-sm .sg-file-field-trigger {\r\n min-height: 2.25rem;\r\n}\r\n\r\n.sg-file-field-lg .sg-file-field-trigger {\r\n min-height: 3rem;\r\n}\r\n\r\n.sg-file-field-input {\r\n position: absolute;\r\n inset: 0;\r\n width: 100%;\r\n height: 100%;\r\n opacity: 0;\r\n cursor: pointer;\r\n font-size: 0;\r\n}\r\n\r\n.sg-file-field-button {\r\n flex-shrink: 0;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n padding: 0.4rem 0.85rem;\r\n border-radius: var(--sg-radius-xs);\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n border: var(--sg-border-width) solid\r\n color-mix(in srgb, var(--sg-accent) 22%, transparent);\r\n pointer-events: none;\r\n}\r\n\r\n.sg-file-field-summary {\r\n min-width: 0;\r\n flex: 1;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg-muted);\r\n pointer-events: none;\r\n}\r\n\r\n.sg-file-field-list {\r\n list-style: none;\r\n margin: 0;\r\n padding: 0;\r\n display: grid;\r\n gap: var(--sg-space-1);\r\n}\r\n\r\n.sg-file-field-item {\r\n display: grid;\r\n grid-template-columns: 1fr auto auto;\r\n align-items: center;\r\n gap: var(--sg-space-2);\r\n padding: 0.45rem 0.65rem;\r\n border-radius: var(--sg-radius-xs);\r\n background: color-mix(in srgb, var(--sg-fg-muted) 6%, var(--sg-surface-solid));\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n font-size: var(--sg-text-sm);\r\n}\r\n\r\n.sg-file-field-name {\r\n min-width: 0;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-file-field-meta {\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-xs);\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n.sg-file-field-remove {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 1.5rem;\r\n height: 1.5rem;\r\n margin: 0;\r\n padding: 0;\r\n border: 0;\r\n border-radius: var(--sg-radius-xs);\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n font-size: 1.1rem;\r\n line-height: 1;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-file-field-remove:hover:not(:disabled) {\r\n background: color-mix(in srgb, var(--sg-danger) 12%, transparent);\r\n color: var(--sg-danger);\r\n}\r\n\r\n.sg-file-field-clear {\r\n justify-self: start;\r\n margin: 0;\r\n padding: 0.2rem 0.4rem;\r\n border: 0;\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-xs);\r\n cursor: pointer;\r\n text-decoration: underline;\r\n text-underline-offset: 2px;\r\n}\r\n\r\n.sg-file-field-clear:hover:not(:disabled) {\r\n color: var(--sg-fg);\r\n}\r\n\r\n/* FileField looks */\r\n.sg-file-field-look-soft .sg-file-field-trigger {\r\n background: var(--sg-surface-frost);\r\n border-color: var(--sg-border-glass);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n border-radius: var(--sg-radius-md);\r\n}\r\n\r\n.sg-file-field-look-soft .sg-file-field-button {\r\n background: color-mix(in srgb, var(--sg-accent) 18%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-accent) 28%, transparent);\r\n border-radius: var(--sg-radius-pill);\r\n}\r\n\r\n.sg-file-field-look-dashed .sg-file-field-trigger {\r\n flex-direction: column;\r\n justify-content: center;\r\n align-items: center;\r\n gap: var(--sg-space-2);\r\n min-height: 7.5rem;\r\n padding: var(--sg-space-5);\r\n border-style: dashed;\r\n border-width: 1.5px;\r\n border-color: color-mix(in srgb, var(--sg-accent) 35%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-accent-soft) 45%, var(--sg-surface-solid));\r\n border-radius: var(--sg-radius-md);\r\n text-align: center;\r\n}\r\n\r\n.sg-file-field-look-dashed .sg-file-field-button {\r\n background: var(--sg-surface-solid);\r\n border-color: color-mix(in srgb, var(--sg-accent) 25%, var(--sg-border-subtle));\r\n border-radius: var(--sg-radius-pill);\r\n padding: 0.5rem 1.1rem;\r\n}\r\n\r\n.sg-file-field-look-dashed .sg-file-field-summary {\r\n white-space: normal;\r\n text-align: center;\r\n max-width: 18rem;\r\n}\r\n\r\n.sg-file-field-look-ghost .sg-file-field-trigger {\r\n background: transparent;\r\n border-color: transparent;\r\n border-bottom: var(--sg-border-width) solid var(--sg-border-subtle);\r\n border-radius: 0;\r\n padding-inline: 0;\r\n box-shadow: none;\r\n}\r\n\r\n.sg-file-field-look-ghost .sg-file-field-trigger:focus-within {\r\n border-bottom-color: var(--sg-accent);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-file-field-look-ghost .sg-file-field-button {\r\n background: transparent;\r\n border-color: transparent;\r\n color: var(--sg-accent);\r\n text-decoration: underline;\r\n text-underline-offset: 0.15em;\r\n padding-inline: 0.25rem;\r\n}\r\n\r\n.sg-file-field-look-ghost[data-invalid=\"true\"] .sg-file-field-trigger {\r\n border-bottom-color: var(--sg-danger);\r\n box-shadow: none;\r\n}\r\n\r\n/* —— VisuallyHidden —— */\r\n.sg-visually-hidden {\r\n position: absolute !important;\r\n width: 1px !important;\r\n height: 1px !important;\r\n padding: 0 !important;\r\n margin: -1px !important;\r\n overflow: hidden !important;\r\n clip: rect(0, 0, 0, 0) !important;\r\n white-space: nowrap !important;\r\n border: 0 !important;\r\n}\r\n\r\n/* —— CloseButton —— */\r\n.sg-close-btn {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n flex-shrink: 0;\r\n width: 1.75rem;\r\n height: 1.75rem;\r\n margin: 0;\r\n padding: 0;\r\n border: 0;\r\n border-radius: 50%;\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n line-height: 0;\r\n cursor: pointer;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-close-btn-glyph {\r\n display: block;\r\n font-size: 1.15rem;\r\n line-height: 1;\r\n}\r\n\r\n.sg-close-btn:hover:not(:disabled) {\r\n background: color-mix(in srgb, var(--sg-fg-muted) 12%, transparent);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-close-btn:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-close-btn:disabled {\r\n opacity: 0.4;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-close-btn-sm {\r\n width: 1.4rem;\r\n height: 1.4rem;\r\n}\r\n\r\n.sg-close-btn-sm .sg-close-btn-glyph {\r\n font-size: 1rem;\r\n}\r\n\r\n.sg-close-btn-lg {\r\n width: 2.1rem;\r\n height: 2.1rem;\r\n}\r\n\r\n.sg-close-btn-lg .sg-close-btn-glyph {\r\n font-size: 1.35rem;\r\n}\r\n\r\n.sg-close-btn-look-soft {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-close-btn-look-soft:hover:not(:disabled) {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-close-btn-look-solid {\r\n background: var(--sg-surface-solid);\r\n color: var(--sg-fg-muted);\r\n box-shadow: inset 0 0 0 1px var(--sg-border-subtle);\r\n}\r\n\r\n.sg-close-btn-look-solid:hover:not(:disabled) {\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-close-btn-look-danger {\r\n background: color-mix(in srgb, var(--sg-danger) 12%, transparent);\r\n color: var(--sg-danger);\r\n}\r\n\r\n.sg-close-btn-look-danger:hover:not(:disabled) {\r\n background: var(--sg-danger);\r\n color: #fff;\r\n}\r\n\r\n/* —— Link —— */\r\n.sg-link {\r\n color: var(--sg-accent);\r\n font-weight: var(--sg-font-weight-medium);\r\n text-decoration: none;\r\n text-underline-offset: 0.15em;\r\n transition: color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-link:hover {\r\n color: var(--sg-accent-hover);\r\n text-decoration: underline;\r\n}\r\n\r\n.sg-link:focus-visible {\r\n outline: none;\r\n border-radius: 2px;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-link-muted {\r\n color: var(--sg-fg-muted);\r\n font-weight: var(--sg-font-weight-normal);\r\n}\r\n\r\n.sg-link-muted:hover {\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-link-subtle {\r\n color: var(--sg-fg);\r\n font-weight: var(--sg-font-weight-normal);\r\n text-decoration: underline;\r\n text-decoration-color: color-mix(in srgb, var(--sg-fg-muted) 45%, transparent);\r\n}\r\n\r\n.sg-link-subtle:hover {\r\n color: var(--sg-accent);\r\n text-decoration-color: var(--sg-accent);\r\n}\r\n\r\n.sg-link-underline {\r\n text-decoration: underline;\r\n}\r\n\r\n.sg-link-external {\r\n font-size: 0.85em;\r\n opacity: 0.75;\r\n}\r\n\r\n/* —— Chip ——\r\n Outer span is the visual pill. Trigger + × are siblings (never nested buttons).\r\n Fixed height + flex center keeps remove glyph aligned on every look. */\r\n.sg-chip {\r\n display: inline-flex;\r\n flex-direction: row;\r\n align-items: center;\r\n max-width: 100%;\r\n height: 2rem;\r\n box-sizing: border-box;\r\n padding: 0 0.15rem 0 0;\r\n border-radius: 999px;\r\n border: 1px solid color-mix(in srgb, var(--sg-accent) 22%, var(--sg-border-subtle));\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n line-height: 1;\r\n vertical-align: middle;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-chip-sm {\r\n height: 1.65rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-chip-lg {\r\n height: 2.35rem;\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-chip-disabled,\r\n.sg-chip[data-disabled=\"true\"] {\r\n opacity: 0.5;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-chip-trigger,\r\n.sg-chip-static {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.35rem;\r\n height: 100%;\r\n margin: 0;\r\n padding: 0 0.75rem;\r\n border: 0;\r\n background: transparent;\r\n color: inherit;\r\n font: inherit;\r\n line-height: 1;\r\n cursor: pointer;\r\n border-radius: 999px;\r\n min-width: 0;\r\n}\r\n\r\n.sg-chip-static {\r\n cursor: default;\r\n}\r\n\r\n.sg-chip-removable .sg-chip-trigger,\r\n.sg-chip-removable .sg-chip-static {\r\n padding-right: 0.35rem;\r\n border-radius: 999px 0 0 999px;\r\n}\r\n\r\n.sg-chip-sm .sg-chip-trigger,\r\n.sg-chip-sm .sg-chip-static {\r\n padding-left: 0.55rem;\r\n padding-right: 0.55rem;\r\n}\r\n\r\n.sg-chip-sm.sg-chip-removable .sg-chip-trigger,\r\n.sg-chip-sm.sg-chip-removable .sg-chip-static {\r\n padding-right: 0.25rem;\r\n}\r\n\r\n.sg-chip-lg .sg-chip-trigger,\r\n.sg-chip-lg .sg-chip-static {\r\n padding-left: 0.9rem;\r\n padding-right: 0.9rem;\r\n}\r\n\r\n.sg-chip-lg.sg-chip-removable .sg-chip-trigger,\r\n.sg-chip-lg.sg-chip-removable .sg-chip-static {\r\n padding-right: 0.4rem;\r\n}\r\n\r\n.sg-chip-trigger:focus-visible {\r\n outline: none;\r\n box-shadow: inset 0 0 0 2px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-chip-label {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.sg-chip-leading {\r\n display: inline-flex;\r\n flex-shrink: 0;\r\n}\r\n\r\n/* Dedicated remove control — not CloseButton (avoids size/baseline drift) */\r\n.sg-chip-x {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n flex: 0 0 auto;\r\n width: 1.5rem;\r\n height: 1.5rem;\r\n margin: 0 0.2rem 0 0;\r\n padding: 0;\r\n border: 0;\r\n border-radius: 999px;\r\n background: transparent;\r\n color: inherit;\r\n font-size: 1rem;\r\n line-height: 1;\r\n cursor: pointer;\r\n opacity: 0.75;\r\n}\r\n\r\n.sg-chip-sm .sg-chip-x {\r\n width: 1.25rem;\r\n height: 1.25rem;\r\n font-size: 0.9rem;\r\n}\r\n\r\n.sg-chip-lg .sg-chip-x {\r\n width: 1.7rem;\r\n height: 1.7rem;\r\n font-size: 1.1rem;\r\n}\r\n\r\n.sg-chip-x:hover:not(:disabled) {\r\n opacity: 1;\r\n background: color-mix(in srgb, var(--sg-fg) 12%, transparent);\r\n}\r\n\r\n.sg-chip-x:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 2px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-chip-selected,\r\n.sg-chip[data-selected=\"true\"] {\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n color: var(--sg-accent-fg);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-chip-selected .sg-chip-x:hover:not(:disabled),\r\n.sg-chip[data-selected=\"true\"] .sg-chip-x:hover:not(:disabled) {\r\n background: color-mix(in srgb, var(--sg-accent-fg) 20%, transparent);\r\n}\r\n\r\n.sg-chip-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-chip-look-solid.sg-chip-selected,\r\n.sg-chip-look-solid[data-selected=\"true\"] {\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-chip-look-outline {\r\n background: var(--sg-surface-solid);\r\n border-color: color-mix(in srgb, var(--sg-accent) 40%, var(--sg-border-subtle));\r\n box-shadow: none;\r\n}\r\n\r\n.sg-chip-look-outline.sg-chip-selected,\r\n.sg-chip-look-outline[data-selected=\"true\"] {\r\n background: var(--sg-accent-soft);\r\n border-color: var(--sg-accent);\r\n color: var(--sg-fg);\r\n box-shadow: none;\r\n}\r\n\r\n/* Glass must stay readable on light languages (frost alone washes out). */\r\n.sg-chip-look-glass {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 55%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-fg) 14%, var(--sg-border-subtle));\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, #fff 55%, transparent),\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n backdrop-filter: blur(10px);\r\n}\r\n\r\n.sg-chip-look-glass.sg-chip-selected,\r\n.sg-chip-look-glass[data-selected=\"true\"] {\r\n background: color-mix(in srgb, var(--sg-accent) 55%, var(--sg-surface-solid));\r\n border-color: color-mix(in srgb, var(--sg-accent) 50%, transparent);\r\n color: var(--sg-accent-fg);\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 0 0 1px color-mix(in srgb, var(--sg-accent) 25%, transparent);\r\n}\r\n\r\n.sg-chip-look-glow {\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 40%, transparent);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-accent) 16%, transparent);\r\n}\r\n\r\n.sg-chip-look-glow.sg-chip-selected,\r\n.sg-chip-look-glow[data-selected=\"true\"] {\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n color: var(--sg-accent-fg);\r\n box-shadow:\r\n 0 0 0 3px color-mix(in srgb, var(--sg-accent) 28%, transparent),\r\n inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n/* —— Password / Search — ride on .sg-input-group (proven shell) —— */\r\n.sg-password-input,\r\n.sg-search-input {\r\n /* inherits .sg-input-group flex */\r\n}\r\n\r\n.sg-password-input[data-invalid=\"true\"],\r\n.sg-search-input[data-invalid=\"true\"] {\r\n border-color: var(--sg-danger);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-danger) 22%, transparent);\r\n}\r\n\r\n.sg-password-input-soft,\r\n.sg-search-input-soft {\r\n border-radius: var(--sg-radius-pill);\r\n background: color-mix(in srgb, var(--sg-surface-solid) 82%, var(--sg-accent-soft));\r\n border-color: color-mix(in srgb, var(--sg-accent) 22%, var(--sg-border-subtle));\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-password-input-glass,\r\n.sg-search-input-glass {\r\n border-radius: var(--sg-radius-md);\r\n background: color-mix(in srgb, var(--sg-surface-solid) 50%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-fg) 12%, var(--sg-border-subtle));\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, #fff 50%, transparent),\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n backdrop-filter: blur(10px);\r\n}\r\n\r\n.sg-password-toggle {\r\n flex: 0 0 auto;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n align-self: stretch;\r\n margin: 0;\r\n padding: 0 1rem;\r\n border: 0;\r\n border-left: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n line-height: 1;\r\n cursor: pointer;\r\n white-space: nowrap;\r\n}\r\n\r\n.sg-password-toggle:hover:not(:disabled) {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-password-toggle:disabled {\r\n opacity: 0.45;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-password-toggle:focus-visible {\r\n outline: none;\r\n box-shadow: inset 0 0 0 2px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-search-addon {\r\n font-weight: var(--sg-font-weight-medium);\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-search-clear {\r\n flex: 0 0 auto;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n margin: 0;\r\n padding: 0 0.85rem;\r\n border: 0;\r\n border-left: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n line-height: 1;\r\n cursor: pointer;\r\n white-space: nowrap;\r\n}\r\n\r\n.sg-search-clear:hover:not(:disabled) {\r\n background: color-mix(in srgb, var(--sg-danger) 10%, transparent);\r\n color: var(--sg-danger);\r\n}\r\n\r\n.sg-search-clear:focus-visible {\r\n outline: none;\r\n box-shadow: inset 0 0 0 2px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-search-input-field::-webkit-search-cancel-button,\r\n.sg-search-input-field::-webkit-search-decoration {\r\n -webkit-appearance: none;\r\n appearance: none;\r\n}\r\n\r\n/* —— CircularProgress —— */\r\n.sg-circular-progress {\r\n position: relative;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n color: var(--sg-accent);\r\n flex-shrink: 0;\r\n vertical-align: middle;\r\n}\r\n\r\n.sg-circular-progress-svg {\r\n display: block;\r\n transform: rotate(-90deg);\r\n}\r\n\r\n.sg-circular-progress-track {\r\n stroke: color-mix(in srgb, var(--sg-fg-muted) 16%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-circular-progress-fill {\r\n stroke: currentColor;\r\n transition: stroke-dashoffset 180ms ease;\r\n}\r\n\r\n.sg-circular-progress-value {\r\n position: absolute;\r\n inset: 0;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n font-size: 0.7rem;\r\n font-weight: var(--sg-font-weight-semibold);\r\n font-variant-numeric: tabular-nums;\r\n color: var(--sg-fg);\r\n line-height: 1;\r\n}\r\n\r\n.sg-circular-progress-lg .sg-circular-progress-value {\r\n font-size: 0.85rem;\r\n}\r\n\r\n.sg-circular-progress-sm .sg-circular-progress-value {\r\n font-size: 0.6rem;\r\n}\r\n\r\n.sg-circular-progress-success {\r\n color: var(--sg-success);\r\n}\r\n\r\n.sg-circular-progress-warning {\r\n color: var(--sg-warning);\r\n}\r\n\r\n.sg-circular-progress-danger {\r\n color: var(--sg-danger);\r\n}\r\n\r\n.sg-circular-progress-muted {\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-circular-progress-look-solid .sg-circular-progress-track {\r\n stroke: var(--sg-surface-solid);\r\n stroke-width: inherit;\r\n /* thicker track feel via filter-like opacity edge */\r\n opacity: 1;\r\n filter: drop-shadow(0 0 0 var(--sg-border-subtle));\r\n}\r\n\r\n.sg-circular-progress-look-solid {\r\n filter: none;\r\n}\r\n\r\n.sg-circular-progress-look-solid .sg-circular-progress-track {\r\n stroke: color-mix(in srgb, var(--sg-fg-muted) 28%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-circular-progress-look-glass .sg-circular-progress-track {\r\n stroke: color-mix(in srgb, var(--sg-accent) 18%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-circular-progress-look-glass {\r\n filter: drop-shadow(0 0 6px color-mix(in srgb, currentColor 22%, transparent));\r\n}\r\n\r\n.sg-circular-progress-look-accent .sg-circular-progress-track {\r\n stroke: color-mix(in srgb, var(--sg-accent) 22%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-circular-progress-look-accent .sg-circular-progress-fill {\r\n filter: drop-shadow(0 0 4px color-mix(in srgb, currentColor 45%, transparent));\r\n}\r\n\r\n.sg-circular-progress-indeterminate .sg-circular-progress-svg {\r\n animation: sg-circular-spin 0.9s linear infinite;\r\n}\r\n\r\n.sg-circular-progress-indeterminate .sg-circular-progress-fill {\r\n transition: none;\r\n}\r\n\r\n@keyframes sg-circular-spin {\r\n to {\r\n transform: rotate(270deg);\r\n }\r\n}\r\n\r\n/* —— SegmentedControl —— */\r\n.sg-segmented {\r\n display: inline-flex;\r\n align-items: stretch;\r\n padding: 0.2rem;\r\n gap: 0.15rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: color-mix(in srgb, var(--sg-fg-muted) 8%, var(--sg-surface-solid));\r\n box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 40%, transparent);\r\n vertical-align: middle;\r\n}\r\n\r\n.sg-segmented-block {\r\n display: flex;\r\n width: 100%;\r\n}\r\n\r\n.sg-segmented-block .sg-segmented-item {\r\n flex: 1;\r\n}\r\n\r\n.sg-segmented[data-disabled=\"true\"] {\r\n opacity: 0.55;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-segmented-item {\r\n appearance: none;\r\n margin: 0;\r\n padding: 0.45rem 0.9rem;\r\n border: 0;\r\n border-radius: var(--sg-radius-pill);\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n line-height: 1.2;\r\n cursor: pointer;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-segmented-sm .sg-segmented-item {\r\n padding: 0.3rem 0.65rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-segmented-lg .sg-segmented-item {\r\n padding: 0.55rem 1.1rem;\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-segmented-item:hover:not(:disabled):not(.sg-segmented-item-selected) {\r\n color: var(--sg-fg);\r\n background: color-mix(in srgb, var(--sg-fg-muted) 8%, transparent);\r\n}\r\n\r\n.sg-segmented-item:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-segmented-item:disabled {\r\n opacity: 0.45;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-segmented-item-selected {\r\n background: var(--sg-surface-solid);\r\n color: var(--sg-fg);\r\n box-shadow:\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 10%, transparent),\r\n inset 0 1px 0 color-mix(in srgb, #fff 55%, transparent);\r\n}\r\n\r\n.sg-segmented-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-segmented-look-solid .sg-segmented-item-selected {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-segmented-look-outline {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 30%, var(--sg-border-subtle));\r\n box-shadow: none;\r\n}\r\n\r\n.sg-segmented-look-outline .sg-segmented-item-selected {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sg-accent) 35%, transparent);\r\n}\r\n\r\n.sg-segmented-look-glass {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 55%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-fg) 12%, var(--sg-border-subtle));\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, #fff 50%, transparent),\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n backdrop-filter: blur(10px);\r\n}\r\n\r\n.sg-segmented-look-glass .sg-segmented-item-selected {\r\n background: color-mix(in srgb, var(--sg-accent) 18%, var(--sg-surface-solid));\r\n color: var(--sg-fg);\r\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sg-accent) 28%, transparent);\r\n}\r\n\r\n/* —— Kbd —— */\r\n.sg-kbd {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-width: 1.4em;\r\n padding: 0.15rem 0.4rem;\r\n border-radius: var(--sg-radius-xs);\r\n border: 1px solid color-mix(in srgb, var(--sg-fg-muted) 22%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-fg-muted) 8%, var(--sg-surface-solid));\r\n box-shadow:\r\n 0 1px 0 color-mix(in srgb, var(--sg-fg) 10%, transparent),\r\n inset 0 1px 0 color-mix(in srgb, #fff 50%, transparent);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: 0.75rem;\r\n font-weight: var(--sg-font-weight-medium);\r\n line-height: 1.2;\r\n vertical-align: middle;\r\n}\r\n\r\n.sg-kbd-sm {\r\n padding: 0.1rem 0.3rem;\r\n font-size: 0.65rem;\r\n}\r\n\r\n.sg-kbd-lg {\r\n padding: 0.25rem 0.55rem;\r\n font-size: 0.85rem;\r\n}\r\n\r\n.sg-kbd-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-kbd-look-outline {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 35%, var(--sg-border-subtle));\r\n box-shadow: none;\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-kbd-look-glass {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 50%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-fg) 12%, transparent);\r\n box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 45%, transparent);\r\n backdrop-filter: blur(8px);\r\n}\r\n\r\n/* —— Code —— */\r\n.sg-code {\r\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\r\n font-size: 0.9em;\r\n padding: 0.12em 0.35em;\r\n border-radius: var(--sg-radius-xs);\r\n background: color-mix(in srgb, var(--sg-fg-muted) 10%, var(--sg-surface-solid));\r\n border: 1px solid color-mix(in srgb, var(--sg-fg-muted) 12%, transparent);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-code-block {\r\n display: block;\r\n overflow: auto;\r\n margin: 0;\r\n padding: 0.85rem 1rem;\r\n font-size: var(--sg-text-sm);\r\n line-height: 1.5;\r\n white-space: pre;\r\n}\r\n\r\n.sg-code-block code {\r\n padding: 0;\r\n border: 0;\r\n background: transparent;\r\n font: inherit;\r\n}\r\n\r\n.sg-code-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-code-look-accent {\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 28%, transparent);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-code-look-muted {\r\n background: transparent;\r\n border-color: transparent;\r\n color: var(--sg-fg-muted);\r\n padding-inline: 0.15em;\r\n}\r\n\r\n/* —— SkipLink ——\r\n clip-based hide (not translate) so overflow:hidden parents cannot swallow it.\r\n alwaysVisible / .sg-skip-link-visible for docs demos. */\r\n.sg-skip-link {\r\n position: fixed;\r\n top: 0.75rem;\r\n z-index: 10000;\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.35rem;\r\n padding: 0.55rem 0.95rem;\r\n border-radius: var(--sg-radius-sm);\r\n border: 1px solid transparent;\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n line-height: 1.2;\r\n text-decoration: none;\r\n white-space: nowrap;\r\n /* visually hide until focus / alwaysVisible */\r\n width: 1px;\r\n height: 1px;\r\n margin: -1px;\r\n padding: 0;\r\n overflow: hidden;\r\n clip: rect(0, 0, 0, 0);\r\n clip-path: inset(50%);\r\n border: 0;\r\n}\r\n\r\n.sg-skip-link-start {\r\n left: 0.75rem;\r\n right: auto;\r\n transform: none;\r\n}\r\n\r\n.sg-skip-link-center {\r\n left: 50%;\r\n right: auto;\r\n transform: translateX(-50%);\r\n}\r\n\r\n.sg-skip-link-end {\r\n left: auto;\r\n right: 0.75rem;\r\n transform: none;\r\n}\r\n\r\n.sg-skip-link:focus,\r\n.sg-skip-link:focus-visible,\r\n.sg-skip-link.sg-skip-link-visible,\r\n.sg-skip-link[data-always-visible=\"true\"] {\r\n width: auto;\r\n height: auto;\r\n margin: 0;\r\n padding: 0.55rem 0.95rem;\r\n overflow: visible;\r\n clip: auto;\r\n clip-path: none;\r\n border: 1px solid transparent;\r\n outline: none;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-skip-link-center:focus,\r\n.sg-skip-link-center:focus-visible,\r\n.sg-skip-link-center.sg-skip-link-visible,\r\n.sg-skip-link-center[data-always-visible=\"true\"] {\r\n transform: translateX(-50%);\r\n}\r\n\r\n/* SkipLink looks (when visible) */\r\n.sg-skip-link-look-soft:focus,\r\n.sg-skip-link-look-soft:focus-visible,\r\n.sg-skip-link-look-soft.sg-skip-link-visible,\r\n.sg-skip-link-look-soft[data-always-visible=\"true\"] {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n border-color: color-mix(in srgb, var(--sg-accent) 30%, transparent);\r\n}\r\n\r\n.sg-skip-link-look-outline:focus,\r\n.sg-skip-link-look-outline:focus-visible,\r\n.sg-skip-link-look-outline.sg-skip-link-visible,\r\n.sg-skip-link-look-outline[data-always-visible=\"true\"] {\r\n background: var(--sg-surface-solid);\r\n color: var(--sg-accent);\r\n border-color: color-mix(in srgb, var(--sg-accent) 45%, var(--sg-border-subtle));\r\n}\r\n\r\n.sg-skip-link-look-glass:focus,\r\n.sg-skip-link-look-glass:focus-visible,\r\n.sg-skip-link-look-glass.sg-skip-link-visible,\r\n.sg-skip-link-look-glass[data-always-visible=\"true\"] {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 55%, transparent);\r\n color: var(--sg-fg);\r\n border-color: color-mix(in srgb, var(--sg-fg) 12%, transparent);\r\n backdrop-filter: blur(10px);\r\n box-shadow:\r\n 0 0 0 3px var(--sg-ring-soft),\r\n inset 0 1px 0 color-mix(in srgb, #fff 50%, transparent);\r\n}\r\n\r\n/* —— PinInput —— */\r\n.sg-pin {\r\n display: inline-flex;\r\n gap: 0.45rem;\r\n align-items: center;\r\n vertical-align: middle;\r\n}\r\n\r\n.sg-pin[data-disabled=\"true\"] {\r\n opacity: 0.55;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-pin-cell {\r\n width: 2.5rem;\r\n height: 2.75rem;\r\n margin: 0;\r\n padding: 0;\r\n border-radius: var(--sg-radius-sm);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-solid);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-lg);\r\n font-weight: var(--sg-font-weight-semibold);\r\n font-variant-numeric: tabular-nums;\r\n text-align: center;\r\n line-height: 1;\r\n transition:\r\n border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-pin-sm .sg-pin-cell {\r\n width: 2rem;\r\n height: 2.25rem;\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-pin-lg .sg-pin-cell {\r\n width: 3rem;\r\n height: 3.25rem;\r\n font-size: 1.35rem;\r\n}\r\n\r\n.sg-pin-cell:focus {\r\n outline: none;\r\n border-color: var(--sg-accent);\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-pin[data-invalid=\"true\"] .sg-pin-cell {\r\n border-color: var(--sg-danger);\r\n}\r\n\r\n.sg-pin-look-soft .sg-pin-cell {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 82%, var(--sg-accent-soft));\r\n border-color: color-mix(in srgb, var(--sg-accent) 18%, var(--sg-border-subtle));\r\n border-radius: var(--sg-radius-md);\r\n}\r\n\r\n.sg-pin-look-outline .sg-pin-cell {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 35%, var(--sg-border-subtle));\r\n}\r\n\r\n.sg-pin-look-glass .sg-pin-cell {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 50%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-fg) 12%, var(--sg-border-subtle));\r\n box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 45%, transparent);\r\n backdrop-filter: blur(8px);\r\n}\r\n\r\n/* —— NavLink —— */\r\n.sg-nav-link {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.45rem;\r\n padding: 0.4rem 0.65rem;\r\n border-radius: var(--sg-radius-sm);\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n text-decoration: none;\r\n line-height: 1.2;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-nav-link:hover {\r\n color: var(--sg-fg);\r\n background: color-mix(in srgb, var(--sg-fg-muted) 8%, transparent);\r\n}\r\n\r\n.sg-nav-link:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-nav-link-active,\r\n.sg-nav-link[data-active=\"true\"] {\r\n color: var(--sg-fg);\r\n background: var(--sg-accent-soft);\r\n}\r\n\r\n.sg-nav-link-look-solid.sg-nav-link-active,\r\n.sg-nav-link-look-solid[data-active=\"true\"] {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-nav-link-look-underline {\r\n border-radius: 0;\r\n padding-inline: 0.15rem;\r\n background: transparent;\r\n box-shadow: inset 0 -2px 0 transparent;\r\n}\r\n\r\n.sg-nav-link-look-underline:hover {\r\n background: transparent;\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-nav-link-look-underline.sg-nav-link-active,\r\n.sg-nav-link-look-underline[data-active=\"true\"] {\r\n background: transparent;\r\n color: var(--sg-accent);\r\n box-shadow: inset 0 -2px 0 var(--sg-accent);\r\n}\r\n\r\n.sg-nav-link-look-pill {\r\n border-radius: var(--sg-radius-pill);\r\n}\r\n\r\n.sg-nav-link-look-pill.sg-nav-link-active,\r\n.sg-nav-link-look-pill[data-active=\"true\"] {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-nav-link-leading {\r\n display: inline-flex;\r\n flex-shrink: 0;\r\n}\r\n\r\n/* —— ListItem —— */\r\n.sg-list-item {\r\n display: flex;\r\n align-items: center;\r\n gap: 0.75rem;\r\n width: 100%;\r\n padding: 0.75rem 0.9rem;\r\n border-radius: var(--sg-radius-md);\r\n border: 1px solid color-mix(in srgb, var(--sg-accent) 12%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-fg-muted) 5%, var(--sg-surface-solid));\r\n color: var(--sg-fg);\r\n box-sizing: border-box;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-list-item-interactive {\r\n cursor: pointer;\r\n}\r\n\r\n.sg-list-item-interactive:hover {\r\n background: color-mix(in srgb, var(--sg-accent) 8%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-list-item-interactive:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-list-item-selected,\r\n.sg-list-item[data-selected=\"true\"] {\r\n border-color: color-mix(in srgb, var(--sg-accent) 40%, transparent);\r\n background: var(--sg-accent-soft);\r\n}\r\n\r\n.sg-list-item-disabled,\r\n.sg-list-item[data-disabled=\"true\"] {\r\n opacity: 0.5;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-list-item-leading {\r\n flex-shrink: 0;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n}\r\n\r\n.sg-list-item-body {\r\n min-width: 0;\r\n flex: 1;\r\n display: grid;\r\n gap: 0.15rem;\r\n}\r\n\r\n.sg-list-item-title {\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n line-height: 1.3;\r\n}\r\n\r\n.sg-list-item-description {\r\n font-size: var(--sg-text-xs);\r\n color: var(--sg-fg-muted);\r\n line-height: 1.35;\r\n}\r\n\r\n.sg-list-item-trailing {\r\n flex-shrink: 0;\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.35rem;\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-list-item-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-list-item-look-outline {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 30%, var(--sg-border-subtle));\r\n}\r\n\r\n.sg-list-item-look-ghost {\r\n background: transparent;\r\n border-color: transparent;\r\n padding-inline: 0.25rem;\r\n}\r\n\r\n/* —— CopyButton —— */\r\n.sg-copy-btn {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 0.35rem;\r\n margin: 0;\r\n padding: 0.45rem 0.85rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: 1px solid color-mix(in srgb, var(--sg-accent) 22%, var(--sg-border-subtle));\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n line-height: 1;\r\n cursor: pointer;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-copy-btn:hover:not(:disabled) {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n border-color: transparent;\r\n}\r\n\r\n.sg-copy-btn:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-copy-btn:disabled {\r\n opacity: 0.5;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-copy-btn-sm {\r\n padding: 0.3rem 0.65rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-copy-btn-lg {\r\n padding: 0.55rem 1.1rem;\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-copy-btn-copied,\r\n.sg-copy-btn[data-copied=\"true\"] {\r\n background: color-mix(in srgb, var(--sg-success) 16%, var(--sg-surface-solid));\r\n border-color: color-mix(in srgb, var(--sg-success) 35%, transparent);\r\n color: var(--sg-success);\r\n}\r\n\r\n.sg-copy-btn-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-copy-btn-look-solid:hover:not(:disabled) {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-copy-btn-look-outline {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 40%, var(--sg-border-subtle));\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-copy-btn-look-outline:hover:not(:disabled) {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-copy-btn-look-ghost {\r\n background: transparent;\r\n border-color: transparent;\r\n color: var(--sg-fg-muted);\r\n text-decoration: underline;\r\n text-underline-offset: 0.12em;\r\n}\r\n\r\n.sg-copy-btn-look-ghost:hover:not(:disabled) {\r\n background: color-mix(in srgb, var(--sg-fg-muted) 8%, transparent);\r\n color: var(--sg-fg);\r\n text-decoration: none;\r\n}\r\n\r\n/* —— CharacterCount —— */\r\n.sg-char-count {\r\n margin: 0;\r\n font-size: var(--sg-text-xs);\r\n font-variant-numeric: tabular-nums;\r\n color: var(--sg-fg-muted);\r\n line-height: 1.3;\r\n}\r\n\r\n.sg-char-count-look-soft {\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-char-count-look-solid {\r\n display: inline-flex;\r\n padding: 0.1rem 0.4rem;\r\n border-radius: var(--sg-radius-xs);\r\n background: var(--sg-surface-solid);\r\n border: 1px solid var(--sg-border-subtle);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-char-count-look-muted {\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-char-count-look-danger,\r\n.sg-char-count-over,\r\n.sg-char-count[data-over=\"true\"] {\r\n color: var(--sg-danger);\r\n font-weight: var(--sg-font-weight-medium);\r\n}\r\n\r\n/* —— Fieldset —— */\r\n.sg-fieldset {\r\n margin: 0;\r\n padding: 1rem 1.1rem 1.1rem;\r\n border-radius: var(--sg-radius-md);\r\n border: 1px solid color-mix(in srgb, var(--sg-accent) 14%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-fg-muted) 4%, var(--sg-surface-solid));\r\n min-width: 0;\r\n}\r\n\r\n.sg-fieldset-legend {\r\n padding: 0 0.4rem;\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-fieldset-body {\r\n display: grid;\r\n gap: var(--sg-space-3);\r\n}\r\n\r\n.sg-fieldset-compact {\r\n padding: 0.65rem 0.75rem 0.75rem;\r\n}\r\n\r\n.sg-fieldset-compact .sg-fieldset-body {\r\n gap: var(--sg-space-2);\r\n}\r\n\r\n.sg-fieldset-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-fieldset-look-outline {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 30%, var(--sg-border-subtle));\r\n}\r\n\r\n.sg-fieldset-look-ghost {\r\n background: transparent;\r\n border-color: transparent;\r\n padding-inline: 0;\r\n}\r\n\r\n/* —— Icon —— */\r\n.sg-icon {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n flex-shrink: 0;\r\n width: 2rem;\r\n height: 2rem;\r\n border-radius: var(--sg-radius-sm);\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n font-size: 1rem;\r\n line-height: 1;\r\n vertical-align: middle;\r\n}\r\n\r\n.sg-icon > svg {\r\n width: 1.1em;\r\n height: 1.1em;\r\n display: block;\r\n}\r\n\r\n.sg-icon-sm {\r\n width: 1.5rem;\r\n height: 1.5rem;\r\n font-size: 0.85rem;\r\n}\r\n\r\n.sg-icon-lg {\r\n width: 2.5rem;\r\n height: 2.5rem;\r\n font-size: 1.25rem;\r\n}\r\n\r\n.sg-icon-xl {\r\n width: 3rem;\r\n height: 3rem;\r\n font-size: 1.5rem;\r\n}\r\n\r\n.sg-icon-look-solid {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-icon-look-outline {\r\n background: transparent;\r\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sg-accent) 40%, transparent);\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-icon-look-ghost {\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n/* —— Image —— */\r\n.sg-image {\r\n position: relative;\r\n display: block;\r\n overflow: hidden;\r\n width: 100%;\r\n background: color-mix(in srgb, var(--sg-fg-muted) 8%, var(--sg-surface-solid));\r\n border: 1px solid color-mix(in srgb, var(--sg-fg-muted) 12%, transparent);\r\n border-radius: var(--sg-radius-md);\r\n}\r\n\r\n.sg-image-media {\r\n display: block;\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n}\r\n\r\n.sg-image-fit-cover .sg-image-media {\r\n object-fit: cover;\r\n}\r\n\r\n.sg-image-fit-contain .sg-image-media {\r\n object-fit: contain;\r\n}\r\n\r\n.sg-image-fit-fill .sg-image-media {\r\n object-fit: fill;\r\n}\r\n\r\n.sg-image-fit-none .sg-image-media {\r\n object-fit: none;\r\n}\r\n\r\n.sg-image-rounded-none {\r\n border-radius: 0;\r\n}\r\n\r\n.sg-image-rounded-sm {\r\n border-radius: var(--sg-radius-sm);\r\n}\r\n\r\n.sg-image-rounded-lg {\r\n border-radius: var(--sg-radius-lg);\r\n}\r\n\r\n.sg-image-rounded-full {\r\n border-radius: 999px;\r\n}\r\n\r\n.sg-image-fallback {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 100%;\r\n height: 100%;\r\n min-height: 4rem;\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: 0.04em;\r\n}\r\n\r\n.sg-image-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-image-look-outline {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 30%, var(--sg-border-subtle));\r\n}\r\n\r\n.sg-image-look-glass {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 50%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-fg) 12%, transparent);\r\n backdrop-filter: blur(8px);\r\n}\r\n\r\n/* —— Meter —— */\r\n.sg-meter {\r\n display: grid;\r\n gap: 0.35rem;\r\n width: 100%;\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-meter-header {\r\n display: flex;\r\n justify-content: space-between;\r\n gap: 0.75rem;\r\n font-size: var(--sg-text-sm);\r\n}\r\n\r\n.sg-meter-label {\r\n color: var(--sg-fg);\r\n font-weight: var(--sg-font-weight-medium);\r\n}\r\n\r\n.sg-meter-value {\r\n color: var(--sg-fg-muted);\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n.sg-meter-track {\r\n height: 0.5rem;\r\n overflow: hidden;\r\n border-radius: var(--sg-radius-pill);\r\n background: color-mix(in srgb, var(--sg-fg-muted) 14%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-meter-fill {\r\n height: 100%;\r\n border-radius: inherit;\r\n background: currentColor;\r\n transition: width 160ms ease;\r\n}\r\n\r\n.sg-meter-look-solid .sg-meter-track {\r\n background: var(--sg-surface-solid);\r\n box-shadow: inset 0 0 0 1px var(--sg-border-subtle);\r\n}\r\n\r\n.sg-meter-look-glass .sg-meter-track {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 55%, transparent);\r\n box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 45%, transparent);\r\n}\r\n\r\n.sg-meter-look-striped .sg-meter-fill {\r\n background-image:\r\n linear-gradient(\r\n 45deg,\r\n color-mix(in srgb, #fff 18%, transparent) 25%,\r\n transparent 25%,\r\n transparent 50%,\r\n color-mix(in srgb, #fff 18%, transparent) 50%,\r\n color-mix(in srgb, #fff 18%, transparent) 75%,\r\n transparent 75%,\r\n transparent\r\n ),\r\n linear-gradient(90deg, currentColor, currentColor);\r\n background-size: 0.7rem 0.7rem, 100% 100%;\r\n}\r\n\r\n.sg-meter-success {\r\n color: var(--sg-success);\r\n}\r\n\r\n.sg-meter-warning {\r\n color: var(--sg-warning);\r\n}\r\n\r\n.sg-meter-danger {\r\n color: var(--sg-danger);\r\n}\r\n\r\n.sg-meter-muted {\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n/* —— TimeInput (custom Softglass HH:mm — not native OS picker) —— */\r\n.sg-time {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.65rem;\r\n padding: 0.35rem 0.55rem 0.35rem 0.45rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: 1px solid color-mix(in srgb, var(--sg-accent) 20%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-surface-solid) 78%, var(--sg-accent-soft));\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, #fff 55%, transparent),\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n vertical-align: middle;\r\n user-select: none;\r\n}\r\n\r\n.sg-time-block {\r\n display: flex;\r\n width: 100%;\r\n}\r\n\r\n.sg-time[data-disabled=\"true\"] {\r\n opacity: 0.55;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-time[data-invalid=\"true\"] {\r\n border-color: var(--sg-danger);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-danger) 20%, transparent);\r\n}\r\n\r\n.sg-time-glyph {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n flex-shrink: 0;\r\n width: 2.1rem;\r\n height: 2.1rem;\r\n border-radius: 50%;\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sg-accent) 22%, transparent);\r\n}\r\n\r\n.sg-time-glyph-face {\r\n position: relative;\r\n width: 1.05rem;\r\n height: 1.05rem;\r\n border-radius: 50%;\r\n border: 1.5px solid currentColor;\r\n}\r\n\r\n.sg-time-glyph-hand {\r\n position: absolute;\r\n left: 50%;\r\n bottom: 50%;\r\n width: 1.5px;\r\n background: currentColor;\r\n transform-origin: bottom center;\r\n border-radius: 1px;\r\n}\r\n\r\n.sg-time-glyph-hand-h {\r\n height: 0.28rem;\r\n transform: translateX(-50%) rotate(40deg);\r\n}\r\n\r\n.sg-time-glyph-hand-m {\r\n height: 0.38rem;\r\n transform: translateX(-50%) rotate(120deg);\r\n opacity: 0.85;\r\n}\r\n\r\n.sg-time-segments {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.2rem;\r\n}\r\n\r\n.sg-time-segment {\r\n display: inline-flex;\r\n flex-direction: column;\r\n align-items: center;\r\n gap: 0.05rem;\r\n}\r\n\r\n.sg-time-step {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 1.6rem;\r\n height: 0.85rem;\r\n margin: 0;\r\n padding: 0;\r\n border: 0;\r\n border-radius: 999px;\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n font-size: 0.55rem;\r\n line-height: 1;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-time-step:hover:not(:disabled) {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-time-step:disabled {\r\n opacity: 0.4;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-time-value {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-width: 2.1rem;\r\n height: 1.85rem;\r\n margin: 0;\r\n padding: 0 0.25rem;\r\n border: 0;\r\n border-radius: var(--sg-radius-sm);\r\n background: var(--sg-surface-solid);\r\n box-shadow: inset 0 0 0 1px var(--sg-border-subtle);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: 1.05rem;\r\n font-weight: var(--sg-font-weight-semibold);\r\n font-variant-numeric: tabular-nums;\r\n letter-spacing: 0.02em;\r\n line-height: 1;\r\n cursor: default;\r\n}\r\n\r\n.sg-time-value:focus-visible {\r\n outline: none;\r\n box-shadow:\r\n inset 0 0 0 1px var(--sg-accent),\r\n 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-time-colon {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 0.65rem;\r\n padding-bottom: 0.15rem;\r\n color: var(--sg-accent);\r\n font-size: 1.15rem;\r\n font-weight: var(--sg-font-weight-semibold);\r\n line-height: 1;\r\n opacity: 0.85;\r\n animation: sg-time-colon-blink 1.4s steps(1) infinite;\r\n}\r\n\r\n@keyframes sg-time-colon-blink {\r\n 0%,\r\n 50% {\r\n opacity: 0.9;\r\n }\r\n 51%,\r\n 100% {\r\n opacity: 0.25;\r\n }\r\n}\r\n\r\n.sg-time-ampm {\r\n display: inline-flex;\r\n flex-direction: column;\r\n gap: 0.15rem;\r\n margin-left: 0.25rem;\r\n}\r\n\r\n.sg-time-ampm-btn {\r\n margin: 0;\r\n padding: 0.15rem 0.4rem;\r\n border: 0;\r\n border-radius: 999px;\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n font-size: 0.65rem;\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: 0.04em;\r\n line-height: 1.2;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-time-ampm-btn:hover:not(:disabled) {\r\n color: var(--sg-fg);\r\n background: color-mix(in srgb, var(--sg-fg-muted) 10%, transparent);\r\n}\r\n\r\n.sg-time-ampm-active {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-time-sm .sg-time-glyph {\r\n width: 1.75rem;\r\n height: 1.75rem;\r\n}\r\n\r\n.sg-time-sm .sg-time-value {\r\n min-width: 1.75rem;\r\n height: 1.55rem;\r\n font-size: 0.9rem;\r\n}\r\n\r\n.sg-time-lg .sg-time-glyph {\r\n width: 2.45rem;\r\n height: 2.45rem;\r\n}\r\n\r\n.sg-time-lg .sg-time-value {\r\n min-width: 2.45rem;\r\n height: 2.15rem;\r\n font-size: 1.25rem;\r\n}\r\n\r\n/* Looks */\r\n.sg-time-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n border-radius: var(--sg-radius-md);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-time-look-outline {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 40%, var(--sg-border-subtle));\r\n box-shadow: none;\r\n}\r\n\r\n.sg-time-look-outline .sg-time-value {\r\n background: transparent;\r\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sg-accent) 28%, transparent);\r\n}\r\n\r\n.sg-time-look-glass {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 48%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-fg) 12%, var(--sg-border-subtle));\r\n backdrop-filter: blur(10px);\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, #fff 50%, transparent),\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n}\r\n\r\n/* —— ScrollArea —— */\r\n.sg-scroll-area {\r\n overflow-y: auto;\r\n overflow-x: hidden;\r\n padding: 0.5rem;\r\n border-radius: var(--sg-radius-md);\r\n border: 1px solid color-mix(in srgb, var(--sg-fg-muted) 14%, transparent);\r\n background: color-mix(in srgb, var(--sg-fg-muted) 4%, var(--sg-surface-solid));\r\n scrollbar-width: thin;\r\n scrollbar-color: color-mix(in srgb, var(--sg-fg-muted) 35%, transparent) transparent;\r\n}\r\n\r\n.sg-scroll-area-x {\r\n overflow-x: auto;\r\n overflow-y: hidden;\r\n}\r\n\r\n.sg-scroll-area-both {\r\n overflow: auto;\r\n}\r\n\r\n.sg-scroll-area-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-scroll-area-look-ghost {\r\n background: transparent;\r\n border-color: transparent;\r\n padding-inline: 0;\r\n}\r\n\r\n/* —— Rating (Softglass SVG stars in a soft shell) —— */\r\n.sg-rating {\r\n --sg-rating-on: #f5b942;\r\n --sg-rating-on-soft: color-mix(in srgb, #f5b942 28%, transparent);\r\n --sg-rating-off: color-mix(in srgb, var(--sg-fg-muted) 28%, var(--sg-surface-solid));\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.55rem;\r\n padding: 0.4rem 0.65rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: 1px solid color-mix(in srgb, var(--sg-warning) 22%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-surface-solid) 82%, #f5b942);\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, #fff 55%, transparent),\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n vertical-align: middle;\r\n user-select: none;\r\n}\r\n\r\n.sg-rating-color-accent {\r\n --sg-rating-on: var(--sg-accent);\r\n --sg-rating-on-soft: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 22%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-surface-solid) 82%, var(--sg-accent-soft));\r\n}\r\n\r\n.sg-rating-color-rose {\r\n --sg-rating-on: #f472b6;\r\n --sg-rating-on-soft: color-mix(in srgb, #f472b6 26%, transparent);\r\n border-color: color-mix(in srgb, #f472b6 28%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-surface-solid) 82%, #f472b6);\r\n}\r\n\r\n.sg-rating-caption {\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-semibold);\r\n color: var(--sg-fg-muted);\r\n letter-spacing: 0.02em;\r\n padding-inline: 0.15rem 0.25rem;\r\n white-space: nowrap;\r\n}\r\n\r\n.sg-rating-stars {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.1rem;\r\n}\r\n\r\n.sg-rating-star {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 1.85rem;\r\n height: 1.85rem;\r\n margin: 0;\r\n padding: 0;\r\n border: 0;\r\n border-radius: 50%;\r\n background: transparent;\r\n color: var(--sg-rating-on);\r\n font-size: 1.2rem;\r\n line-height: 1;\r\n cursor: pointer;\r\n transition:\r\n transform var(--sg-duration-fast) var(--sg-ease-out),\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-rating-svg {\r\n display: block;\r\n overflow: visible;\r\n}\r\n\r\n.sg-rating-svg-path {\r\n transition:\r\n fill var(--sg-duration-fast) var(--sg-ease-out),\r\n stroke var(--sg-duration-fast) var(--sg-ease-out),\r\n filter var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-rating-svg-path-off {\r\n fill: var(--sg-rating-off);\r\n stroke: color-mix(in srgb, var(--sg-fg-muted) 35%, transparent);\r\n stroke-width: 1;\r\n}\r\n\r\n.sg-rating-svg-path-on {\r\n fill: var(--sg-rating-on);\r\n stroke: color-mix(in srgb, var(--sg-rating-on) 55%, #000);\r\n stroke-width: 0.6;\r\n filter: drop-shadow(0 1px 2px color-mix(in srgb, var(--sg-rating-on) 45%, transparent));\r\n}\r\n\r\n.sg-rating-star-active {\r\n background: color-mix(in srgb, var(--sg-rating-on) 14%, transparent);\r\n box-shadow: 0 0 0 1px color-mix(in srgb, var(--sg-rating-on) 18%, transparent);\r\n}\r\n\r\n.sg-rating-star:hover:not(:disabled) {\r\n transform: translateY(-1px) scale(1.08);\r\n background: var(--sg-rating-on-soft);\r\n}\r\n\r\n.sg-rating-star:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-rating-star:disabled {\r\n cursor: default;\r\n}\r\n\r\n.sg-rating-score {\r\n min-width: 2rem;\r\n padding: 0.2rem 0.45rem;\r\n border-radius: 999px;\r\n background: var(--sg-surface-solid);\r\n box-shadow: inset 0 0 0 1px var(--sg-border-subtle);\r\n color: var(--sg-fg);\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-semibold);\r\n font-variant-numeric: tabular-nums;\r\n text-align: center;\r\n line-height: 1.2;\r\n}\r\n\r\n.sg-rating-sm {\r\n padding: 0.28rem 0.5rem;\r\n gap: 0.4rem;\r\n}\r\n\r\n.sg-rating-sm .sg-rating-star {\r\n width: 1.45rem;\r\n height: 1.45rem;\r\n font-size: 0.95rem;\r\n}\r\n\r\n.sg-rating-lg {\r\n padding: 0.5rem 0.8rem;\r\n gap: 0.65rem;\r\n}\r\n\r\n.sg-rating-lg .sg-rating-star {\r\n width: 2.25rem;\r\n height: 2.25rem;\r\n font-size: 1.45rem;\r\n}\r\n\r\n.sg-rating-lg .sg-rating-score {\r\n min-width: 2.4rem;\r\n font-size: var(--sg-text-sm);\r\n padding: 0.3rem 0.55rem;\r\n}\r\n\r\n/* Looks */\r\n.sg-rating-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n border-radius: var(--sg-radius-md);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-rating-look-outline {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-rating-on) 40%, var(--sg-border-subtle));\r\n box-shadow: none;\r\n}\r\n\r\n.sg-rating-look-outline .sg-rating-svg-path-off {\r\n fill: transparent;\r\n stroke: color-mix(in srgb, var(--sg-fg-muted) 45%, transparent);\r\n stroke-width: 1.4;\r\n}\r\n\r\n.sg-rating-look-glass {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 48%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-fg) 12%, var(--sg-border-subtle));\r\n backdrop-filter: blur(10px);\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, #fff 50%, transparent),\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n}\r\n\r\n.sg-rating-look-glow {\r\n border-color: color-mix(in srgb, var(--sg-rating-on) 40%, transparent);\r\n box-shadow:\r\n 0 0 0 3px color-mix(in srgb, var(--sg-rating-on) 16%, transparent),\r\n inset 0 1px 0 color-mix(in srgb, #fff 50%, transparent);\r\n}\r\n\r\n.sg-rating-look-glow .sg-rating-star-active .sg-rating-svg-path-on {\r\n filter: drop-shadow(0 0 6px color-mix(in srgb, var(--sg-rating-on) 65%, transparent));\r\n}\r\n\r\n.sg-rating-static .sg-rating-star:hover {\r\n transform: none;\r\n background: transparent;\r\n}\r\n\r\n.sg-rating-static .sg-rating-star-active:hover {\r\n background: color-mix(in srgb, var(--sg-rating-on) 14%, transparent);\r\n}\r\n\r\n.sg-rating[data-disabled=\"true\"] {\r\n opacity: 0.55;\r\n pointer-events: none;\r\n}\r\n\r\n/* —— AspectRatio —— */\r\n.sg-aspect-ratio {\r\n position: relative;\r\n width: 100%;\r\n overflow: hidden;\r\n}\r\n\r\n.sg-aspect-ratio-content {\r\n position: absolute;\r\n inset: 0;\r\n width: 100%;\r\n height: 100%;\r\n}\r\n\r\n.sg-aspect-ratio-content > * {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .sg-spinner-visual,\r\n .sg-skeleton,\r\n .sg-progress-indeterminate .sg-progress-fill,\r\n .sg-progress-look-striped .sg-progress-fill,\r\n .sg-status-dot-pulse::before,\r\n .sg-circular-progress-indeterminate .sg-circular-progress-svg {\r\n animation: none;\r\n }\r\n\r\n .sg-progress-fill,\r\n .sg-circular-progress-fill,\r\n .sg-meter-fill,\r\n .sg-rating-star,\r\n .sg-time-colon {\r\n transition: none;\r\n animation: none;\r\n }\r\n\r\n .sg-rating-star:hover:not(:disabled) {\r\n transform: none;\r\n }\r\n}\r\n\r\n/* —— RangeSlider (dual thumb on Slider track language) —— */\r\n.sg-range-slider {\r\n position: relative;\r\n height: var(--sg-touch-min);\r\n display: flex;\r\n align-items: center;\r\n}\r\n\r\n.sg-range-slider-track {\r\n position: absolute;\r\n left: 0;\r\n right: 0;\r\n height: 0.45rem;\r\n border-radius: var(--sg-radius-pill);\r\n background: color-mix(in srgb, var(--sg-fg-muted) 14%, var(--sg-surface-solid));\r\n pointer-events: none;\r\n}\r\n\r\n.sg-range-slider-track::after {\r\n content: \"\";\r\n position: absolute;\r\n top: 0;\r\n bottom: 0;\r\n left: var(--sg-range-left, 0%);\r\n width: calc(var(--sg-range-right, 100%) - var(--sg-range-left, 0%));\r\n border-radius: inherit;\r\n background: var(--sg-accent);\r\n}\r\n\r\n.sg-range-slider-input {\r\n position: absolute;\r\n left: 0;\r\n width: 100%;\r\n margin: 0;\r\n background: transparent !important;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-range-slider-input::-webkit-slider-thumb {\r\n pointer-events: auto;\r\n position: relative;\r\n z-index: 2;\r\n}\r\n\r\n.sg-range-slider-input::-moz-range-thumb {\r\n pointer-events: auto;\r\n position: relative;\r\n z-index: 2;\r\n}\r\n\r\n.sg-range-slider-max {\r\n z-index: 3;\r\n}\r\n\r\n/* —— ToggleGroup —— */\r\n.sg-toggle-group {\r\n display: inline-flex;\r\n flex-wrap: wrap;\r\n gap: 0.4rem;\r\n vertical-align: middle;\r\n}\r\n\r\n.sg-toggle-group-block {\r\n display: flex;\r\n}\r\n\r\n.sg-toggle-group-block .sg-toggle-group-item {\r\n flex: 1;\r\n}\r\n\r\n.sg-toggle-group-item {\r\n margin: 0;\r\n padding: 0.45rem 0.85rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: 1px solid color-mix(in srgb, var(--sg-accent) 18%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-fg-muted) 6%, var(--sg-surface-solid));\r\n color: var(--sg-fg-muted);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n line-height: 1.2;\r\n cursor: pointer;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-toggle-group-item:hover:not(:disabled):not(.sg-toggle-group-item-on) {\r\n color: var(--sg-fg);\r\n border-color: color-mix(in srgb, var(--sg-accent) 30%, var(--sg-border-subtle));\r\n}\r\n\r\n.sg-toggle-group-item-on {\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-toggle-group-item:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-toggle-group-item:disabled {\r\n opacity: 0.45;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-toggle-group-sm .sg-toggle-group-item {\r\n padding: 0.3rem 0.65rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-toggle-group-lg .sg-toggle-group-item {\r\n padding: 0.55rem 1.05rem;\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-toggle-group-look-solid .sg-toggle-group-item {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-toggle-group-look-outline .sg-toggle-group-item {\r\n background: transparent;\r\n}\r\n\r\n.sg-toggle-group-look-outline .sg-toggle-group-item-on {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n border-color: var(--sg-accent);\r\n}\r\n\r\n.sg-toggle-group-look-glass .sg-toggle-group-item {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 50%, transparent);\r\n backdrop-filter: blur(8px);\r\n}\r\n\r\n/* —— CountBadge —— */\r\n.sg-count-badge {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-width: 1.25rem;\r\n height: 1.25rem;\r\n padding: 0 0.35rem;\r\n border-radius: 999px;\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n border: 1px solid color-mix(in srgb, var(--sg-accent) 25%, transparent);\r\n font-size: 0.7rem;\r\n font-weight: var(--sg-font-weight-semibold);\r\n font-variant-numeric: tabular-nums;\r\n line-height: 1;\r\n vertical-align: middle;\r\n}\r\n\r\n.sg-count-badge-sm {\r\n min-width: 1rem;\r\n height: 1rem;\r\n font-size: 0.6rem;\r\n padding: 0 0.25rem;\r\n}\r\n\r\n.sg-count-badge-lg {\r\n min-width: 1.5rem;\r\n height: 1.5rem;\r\n font-size: 0.8rem;\r\n padding: 0 0.45rem;\r\n}\r\n\r\n.sg-count-badge-look-solid {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n border-color: transparent;\r\n}\r\n\r\n.sg-count-badge-look-outline {\r\n background: transparent;\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-count-badge-look-danger {\r\n background: color-mix(in srgb, var(--sg-danger) 14%, transparent);\r\n color: var(--sg-danger);\r\n border-color: color-mix(in srgb, var(--sg-danger) 30%, transparent);\r\n}\r\n\r\n/* —— ColorSwatch / ColorInput —— */\r\n.sg-swatch {\r\n --sg-swatch-color: #7c3aed;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 2rem;\r\n height: 2rem;\r\n margin: 0;\r\n padding: 0.2rem;\r\n border-radius: 50%;\r\n border: 1px solid color-mix(in srgb, var(--sg-fg) 12%, transparent);\r\n background: var(--sg-surface-solid);\r\n cursor: pointer;\r\n box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 40%, transparent);\r\n}\r\n\r\n.sg-swatch-fill {\r\n display: block;\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 50%;\r\n background: var(--sg-swatch-color);\r\n box-shadow: inset 0 0 0 1px color-mix(in srgb, #000 12%, transparent);\r\n}\r\n\r\n.sg-swatch-sm {\r\n width: 1.5rem;\r\n height: 1.5rem;\r\n}\r\n\r\n.sg-swatch-lg {\r\n width: 2.5rem;\r\n height: 2.5rem;\r\n}\r\n\r\n.sg-swatch-selected {\r\n box-shadow:\r\n 0 0 0 2px var(--sg-surface-solid),\r\n 0 0 0 4px var(--sg-accent);\r\n}\r\n\r\n.sg-swatch:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-swatch-look-outline {\r\n background: transparent;\r\n}\r\n\r\n.sg-swatch-look-glass {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 50%, transparent);\r\n backdrop-filter: blur(6px);\r\n}\r\n\r\n.sg-color-input {\r\n display: flex;\r\n align-items: center;\r\n gap: 0.5rem;\r\n padding: 0.3rem 0.45rem 0.3rem 0.35rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: 1px solid color-mix(in srgb, var(--sg-accent) 18%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-surface-solid) 85%, var(--sg-accent-soft));\r\n box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 45%, transparent);\r\n}\r\n\r\n.sg-color-input-swatch {\r\n position: relative;\r\n width: 2rem;\r\n height: 2rem;\r\n border-radius: 50%;\r\n overflow: hidden;\r\n flex-shrink: 0;\r\n border: 1px solid var(--sg-border-subtle);\r\n cursor: pointer;\r\n}\r\n\r\n.sg-color-input-native {\r\n position: absolute;\r\n inset: -20%;\r\n width: 140%;\r\n height: 140%;\r\n border: 0;\r\n padding: 0;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-color-input-text {\r\n flex: 1;\r\n min-width: 0;\r\n margin: 0;\r\n padding: 0.35rem 0.4rem;\r\n border: 0;\r\n background: transparent;\r\n color: var(--sg-fg);\r\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\r\n font-size: var(--sg-text-sm);\r\n outline: none;\r\n}\r\n\r\n.sg-color-input-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-radius: var(--sg-radius-md);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-color-input-look-outline {\r\n background: transparent;\r\n box-shadow: none;\r\n}\r\n\r\n.sg-color-input-look-glass {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 50%, transparent);\r\n backdrop-filter: blur(8px);\r\n}\r\n\r\n.sg-color-input[data-invalid=\"true\"] {\r\n border-color: var(--sg-danger);\r\n}\r\n\r\n/* —— Highlight / Truncate / LiveRegion —— */\r\n.sg-highlight {\r\n padding: 0.05em 0.2em;\r\n border-radius: 0.25em;\r\n background: color-mix(in srgb, var(--sg-warning) 28%, transparent);\r\n color: inherit;\r\n}\r\n\r\n.sg-highlight-look-solid {\r\n background: var(--sg-warning);\r\n color: #1a1200;\r\n}\r\n\r\n.sg-highlight-look-accent {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-highlight-look-warning {\r\n background: color-mix(in srgb, var(--sg-warning) 40%, transparent);\r\n}\r\n\r\n.sg-truncate {\r\n display: inline-block;\r\n max-width: 100%;\r\n overflow: hidden;\r\n}\r\n\r\n.sg-truncate-1 {\r\n white-space: nowrap;\r\n text-overflow: ellipsis;\r\n}\r\n\r\n.sg-truncate-2,\r\n.sg-truncate-3,\r\n.sg-truncate-4 {\r\n display: -webkit-box;\r\n -webkit-box-orient: vertical;\r\n overflow: hidden;\r\n}\r\n\r\n.sg-truncate-2 {\r\n -webkit-line-clamp: 2;\r\n line-clamp: 2;\r\n}\r\n\r\n.sg-truncate-3 {\r\n -webkit-line-clamp: 3;\r\n line-clamp: 3;\r\n}\r\n\r\n.sg-truncate-4 {\r\n -webkit-line-clamp: 4;\r\n line-clamp: 4;\r\n}\r\n\r\n/* —— NativeDateInput (custom Softglass YYYY-MM-DD — not OS picker) —— */\r\n.sg-ndate {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.65rem;\r\n padding: 0.4rem 0.6rem 0.4rem 0.45rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: 1px solid color-mix(in srgb, var(--sg-accent) 20%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-surface-solid) 78%, var(--sg-accent-soft));\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, #fff 55%, transparent),\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n vertical-align: middle;\r\n user-select: none;\r\n}\r\n\r\n.sg-ndate-block {\r\n display: flex;\r\n width: 100%;\r\n}\r\n\r\n.sg-ndate[data-disabled=\"true\"] {\r\n opacity: 0.55;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-ndate[data-invalid=\"true\"] {\r\n border-color: var(--sg-danger);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-danger) 20%, transparent);\r\n}\r\n\r\n.sg-ndate-glyph {\r\n display: inline-flex;\r\n flex-direction: column;\r\n align-items: stretch;\r\n width: 2rem;\r\n height: 2rem;\r\n border-radius: 0.45rem;\r\n overflow: hidden;\r\n flex-shrink: 0;\r\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sg-accent) 25%, transparent);\r\n background: var(--sg-surface-solid);\r\n}\r\n\r\n.sg-ndate-glyph-top {\r\n height: 0.45rem;\r\n background: var(--sg-accent);\r\n}\r\n\r\n.sg-ndate-glyph-body {\r\n flex: 1;\r\n display: grid;\r\n grid-template-columns: 1fr 1fr;\r\n gap: 0.12rem;\r\n padding: 0.22rem;\r\n background: color-mix(in srgb, var(--sg-accent-soft) 55%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-ndate-glyph-dot {\r\n border-radius: 2px;\r\n background: color-mix(in srgb, var(--sg-accent) 45%, transparent);\r\n}\r\n\r\n.sg-ndate-segments {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.15rem;\r\n}\r\n\r\n.sg-ndate-segment {\r\n display: inline-flex;\r\n flex-direction: column;\r\n align-items: center;\r\n gap: 0.05rem;\r\n}\r\n\r\n.sg-ndate-step {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 1.7rem;\r\n height: 0.85rem;\r\n margin: 0;\r\n padding: 0;\r\n border: 0;\r\n border-radius: 999px;\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n font-size: 0.55rem;\r\n line-height: 1;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-ndate-step:hover:not(:disabled) {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-ndate-value {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-width: 2rem;\r\n height: 1.85rem;\r\n margin: 0;\r\n padding: 0 0.35rem;\r\n border: 0;\r\n border-radius: var(--sg-radius-sm);\r\n background: var(--sg-surface-solid);\r\n box-shadow: inset 0 0 0 1px var(--sg-border-subtle);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: 0.95rem;\r\n font-weight: var(--sg-font-weight-semibold);\r\n font-variant-numeric: tabular-nums;\r\n line-height: 1;\r\n cursor: default;\r\n}\r\n\r\n.sg-ndate-value-month {\r\n min-width: 2.6rem;\r\n letter-spacing: 0.02em;\r\n}\r\n\r\n.sg-ndate-value-year {\r\n min-width: 3rem;\r\n}\r\n\r\n.sg-ndate-value:focus-visible {\r\n outline: none;\r\n box-shadow:\r\n inset 0 0 0 1px var(--sg-accent),\r\n 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-ndate-sep {\r\n color: var(--sg-fg-muted);\r\n font-weight: var(--sg-font-weight-semibold);\r\n opacity: 0.55;\r\n padding-bottom: 0.1rem;\r\n}\r\n\r\n.sg-ndate-sm .sg-ndate-glyph {\r\n width: 1.7rem;\r\n height: 1.7rem;\r\n}\r\n\r\n.sg-ndate-sm .sg-ndate-value {\r\n height: 1.55rem;\r\n font-size: 0.82rem;\r\n min-width: 1.7rem;\r\n}\r\n\r\n.sg-ndate-lg .sg-ndate-glyph {\r\n width: 2.35rem;\r\n height: 2.35rem;\r\n}\r\n\r\n.sg-ndate-lg .sg-ndate-value {\r\n height: 2.15rem;\r\n font-size: 1.1rem;\r\n min-width: 2.35rem;\r\n}\r\n\r\n.sg-ndate-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n border-radius: var(--sg-radius-md);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-ndate-look-outline {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 40%, var(--sg-border-subtle));\r\n box-shadow: none;\r\n}\r\n\r\n.sg-ndate-look-outline .sg-ndate-value {\r\n background: transparent;\r\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sg-accent) 28%, transparent);\r\n}\r\n\r\n.sg-ndate-look-glass {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 48%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-fg) 12%, var(--sg-border-subtle));\r\n backdrop-filter: blur(10px);\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, #fff 50%, transparent),\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n}\r\n\r\n/* —— LiveRegion (visible Softglass status banner) —— */\r\n.sg-live-region-visible {\r\n display: flex;\r\n align-items: flex-start;\r\n gap: 0.65rem;\r\n width: 100%;\r\n padding: 0.7rem 0.9rem;\r\n border-radius: var(--sg-radius-md);\r\n border: 1px solid color-mix(in srgb, var(--sg-accent) 22%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-accent-soft) 70%, var(--sg-surface-solid));\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, #fff 45%, transparent),\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 5%, transparent);\r\n color: var(--sg-fg);\r\n font-size: var(--sg-text-sm);\r\n line-height: 1.4;\r\n}\r\n\r\n.sg-live-region-marker {\r\n flex-shrink: 0;\r\n width: 0.5rem;\r\n height: 0.5rem;\r\n margin-top: 0.35rem;\r\n border-radius: 50%;\r\n background: var(--sg-accent);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-accent) 22%, transparent);\r\n}\r\n\r\n.sg-live-region-body {\r\n min-width: 0;\r\n flex: 1;\r\n display: grid;\r\n gap: 0.15rem;\r\n}\r\n\r\n.sg-live-region-title {\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight, -0.01em);\r\n}\r\n\r\n.sg-live-region-message {\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-live-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-live-look-glass {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 50%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-fg) 12%, transparent);\r\n backdrop-filter: blur(10px);\r\n}\r\n\r\n.sg-live-look-info {\r\n border-color: color-mix(in srgb, var(--sg-accent) 35%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-accent-soft) 80%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-live-look-info .sg-live-region-marker {\r\n background: var(--sg-accent);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-accent) 22%, transparent);\r\n}\r\n\r\n.sg-live-look-success {\r\n border-color: color-mix(in srgb, var(--sg-success) 35%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-success) 12%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-live-look-success .sg-live-region-marker {\r\n background: var(--sg-success);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-success) 22%, transparent);\r\n}\r\n\r\n.sg-live-look-success .sg-live-region-message {\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-live-look-warning {\r\n border-color: color-mix(in srgb, var(--sg-warning) 40%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-warning) 14%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-live-look-warning .sg-live-region-marker {\r\n background: var(--sg-warning);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-warning) 22%, transparent);\r\n}\r\n\r\n.sg-live-look-danger {\r\n border-color: color-mix(in srgb, var(--sg-danger) 40%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-danger) 12%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-live-look-danger .sg-live-region-marker {\r\n background: var(--sg-danger);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-danger) 22%, transparent);\r\n}\r\n\r\n/* —— Text / Heading —— */\r\n.sg-text {\r\n margin: 0;\r\n color: var(--sg-fg);\r\n font-size: var(--sg-text-base);\r\n line-height: var(--sg-leading-normal, 1.5);\r\n}\r\n\r\n.sg-text-muted {\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-text-subtle {\r\n color: var(--sg-fg-subtle, var(--sg-fg-muted));\r\n}\r\n\r\n.sg-text-accent {\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-text-danger {\r\n color: var(--sg-danger);\r\n}\r\n\r\n.sg-text-size-xs {\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-text-size-sm {\r\n font-size: var(--sg-text-sm);\r\n}\r\n\r\n.sg-text-size-lg {\r\n font-size: var(--sg-text-lg);\r\n}\r\n\r\n.sg-text-size-xl {\r\n font-size: var(--sg-text-xl, 1.25rem);\r\n}\r\n\r\n.sg-text-weight-medium {\r\n font-weight: var(--sg-font-weight-medium);\r\n}\r\n\r\n.sg-text-weight-semibold {\r\n font-weight: var(--sg-font-weight-semibold);\r\n}\r\n\r\n.sg-text-weight-bold {\r\n font-weight: var(--sg-font-weight-bold, 700);\r\n}\r\n\r\n.sg-heading {\r\n margin: 0;\r\n color: var(--sg-fg);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight, -0.02em);\r\n line-height: 1.25;\r\n}\r\n\r\n.sg-heading-sm {\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-heading-lg {\r\n font-size: var(--sg-text-xl, 1.25rem);\r\n}\r\n\r\n.sg-heading-xl {\r\n font-size: 1.5rem;\r\n}\r\n\r\n.sg-heading-2xl {\r\n font-size: 1.85rem;\r\n}\r\n\r\n/* Chip variants (filter / check) */\r\n.sg-chip-variant-filter {\r\n border-style: dashed;\r\n}\r\n\r\n.sg-chip-variant-check .sg-chip-check {\r\n font-size: 0.75em;\r\n font-weight: 700;\r\n}\r\n\r\n/* —— Collapsible (v1.4a) — cheap frost; glass blur only when look=glass —— */\r\n.sg-collapsible {\r\n --sg-disclosure-pad-x: 1.05rem;\r\n --sg-disclosure-pad-y: 0.85rem;\r\n display: flex;\r\n flex-direction: column;\r\n width: 100%;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n overflow: clip;\r\n contain: layout style;\r\n}\r\n\r\n.sg-collapsible[data-look=\"solid\"] {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-collapsible[data-look=\"glass\"] {\r\n /* Frost-first (cheap). Blur only if transparency allowed. */\r\n background: var(--sg-surface-frost-strong, var(--sg-surface-frost));\r\n border-color: color-mix(in srgb, var(--sg-border-frost) 80%, var(--sg-accent) 12%);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n@media (prefers-reduced-transparency: no-preference) {\r\n .sg-collapsible[data-look=\"glass\"] {\r\n background: color-mix(in srgb, var(--sg-surface-glass) 92%, transparent);\r\n backdrop-filter: blur(12px) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(12px) saturate(var(--sg-glass-saturate));\r\n }\r\n}\r\n\r\n.sg-collapsible[data-look=\"outline\"] {\r\n background: transparent;\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-collapsible[data-look=\"ghost\"] {\r\n background: transparent;\r\n border-color: transparent;\r\n border-radius: var(--sg-radius-md);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-collapsible-trigger {\r\n appearance: none;\r\n position: relative;\r\n display: flex;\r\n width: 100%;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n margin: 0;\r\n padding: var(--sg-disclosure-pad-y) var(--sg-disclosure-pad-x);\r\n border: 0;\r\n background: transparent;\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n text-align: left;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-collapsible-trigger::before {\r\n content: \"\";\r\n position: absolute;\r\n left: 0.45rem;\r\n top: 50%;\r\n width: 3px;\r\n height: 0;\r\n border-radius: 999px;\r\n background: var(--sg-accent);\r\n transform: translateY(-50%);\r\n opacity: 0;\r\n}\r\n\r\n.sg-collapsible-trigger[data-state=\"open\"]::before {\r\n height: 1.15rem;\r\n opacity: 1;\r\n}\r\n\r\n.sg-collapsible[data-look=\"ghost\"] .sg-collapsible-trigger::before {\r\n left: 0;\r\n}\r\n\r\n.sg-collapsible-trigger:hover:not(:disabled) {\r\n background: color-mix(in srgb, var(--sg-accent-soft) 70%, transparent);\r\n}\r\n\r\n.sg-collapsible-trigger[data-state=\"open\"] {\r\n background: color-mix(in srgb, var(--sg-accent-soft) 55%, transparent);\r\n}\r\n\r\n.sg-collapsible-trigger:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: -2px;\r\n z-index: 1;\r\n}\r\n\r\n.sg-collapsible-trigger:disabled {\r\n opacity: 0.5;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-collapsible-trigger-label {\r\n flex: 1;\r\n min-width: 0;\r\n padding-left: 0.35rem;\r\n}\r\n\r\n.sg-collapsible-chevron-wrap {\r\n flex-shrink: 0;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 1.75rem;\r\n height: 1.75rem;\r\n border-radius: 999px;\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: color-mix(in srgb, var(--sg-surface-solid) 70%, var(--sg-accent-soft));\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-collapsible-trigger:hover:not(:disabled) .sg-collapsible-chevron-wrap {\r\n border-color: color-mix(in srgb, var(--sg-accent) 28%, var(--sg-border-frost));\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-collapsible-chevron-wrap[data-open=\"true\"] {\r\n transform: rotate(180deg);\r\n color: var(--sg-accent-fg);\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n}\r\n\r\n.sg-collapsible-chevron {\r\n width: 0.95rem;\r\n height: 0.95rem;\r\n display: block;\r\n}\r\n\r\n.sg-collapsible-panel[hidden] {\r\n display: none;\r\n}\r\n\r\n.sg-collapsible-content {\r\n padding: 0.7rem var(--sg-disclosure-pad-x) 1.05rem;\r\n padding-left: calc(var(--sg-disclosure-pad-x) + 0.35rem);\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-sm);\r\n line-height: 1.6;\r\n border-top: var(--sg-border-width) solid\r\n color-mix(in srgb, var(--sg-border-frost) 85%, transparent);\r\n}\r\n\r\n.sg-collapsible[data-look=\"ghost\"] .sg-collapsible-content {\r\n border-top-color: transparent;\r\n}\r\n\r\n/* Fade only when requested — no layout animation */\r\n.sg-collapsible[data-motion=\"fade\"] .sg-collapsible-panel[data-state=\"open\"] .sg-collapsible-content {\r\n animation: sg-disclosure-fade var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n@keyframes sg-disclosure-fade {\r\n from { opacity: 0; }\r\n to { opacity: 1; }\r\n}\r\n\r\n/* —— Accordion (v1.4a) —— */\r\n.sg-accordion {\r\n --sg-disclosure-pad-x: 1.05rem;\r\n --sg-disclosure-pad-y: 0.9rem;\r\n display: flex;\r\n flex-direction: column;\r\n width: 100%;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n overflow: clip;\r\n contain: layout style;\r\n}\r\n\r\n.sg-accordion[data-look=\"solid\"] {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-accordion[data-look=\"glass\"] {\r\n background: var(--sg-surface-frost-strong, var(--sg-surface-frost));\r\n border-color: color-mix(in srgb, var(--sg-border-frost) 80%, var(--sg-accent) 12%);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n@media (prefers-reduced-transparency: no-preference) {\r\n .sg-accordion[data-look=\"glass\"] {\r\n background: color-mix(in srgb, var(--sg-surface-glass) 92%, transparent);\r\n backdrop-filter: blur(12px) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(12px) saturate(var(--sg-glass-saturate));\r\n }\r\n}\r\n\r\n.sg-accordion[data-look=\"outline\"] {\r\n background: transparent;\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-accordion[data-look=\"ghost\"] {\r\n background: transparent;\r\n border-color: transparent;\r\n border-radius: 0;\r\n box-shadow: none;\r\n}\r\n\r\n.sg-accordion-item {\r\n contain: layout style;\r\n}\r\n\r\n.sg-accordion-item + .sg-accordion-item {\r\n border-top: var(--sg-border-width) solid\r\n color-mix(in srgb, var(--sg-border-frost) 90%, transparent);\r\n}\r\n\r\n.sg-accordion[data-look=\"ghost\"] .sg-accordion-item + .sg-accordion-item {\r\n border-top-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-accordion-heading {\r\n margin: 0;\r\n font-size: inherit;\r\n font-weight: inherit;\r\n}\r\n\r\n.sg-accordion-trigger {\r\n appearance: none;\r\n position: relative;\r\n display: flex;\r\n width: 100%;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n margin: 0;\r\n padding: var(--sg-disclosure-pad-y) var(--sg-disclosure-pad-x);\r\n border: 0;\r\n background: transparent;\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n text-align: left;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-accordion-trigger::before {\r\n content: \"\";\r\n position: absolute;\r\n left: 0.45rem;\r\n top: 50%;\r\n width: 3px;\r\n height: 0;\r\n border-radius: 999px;\r\n background: var(--sg-accent);\r\n transform: translateY(-50%);\r\n opacity: 0;\r\n}\r\n\r\n.sg-accordion-trigger[data-state=\"open\"]::before {\r\n height: 1.15rem;\r\n opacity: 1;\r\n}\r\n\r\n.sg-accordion[data-look=\"ghost\"] .sg-accordion-trigger::before {\r\n left: 0;\r\n}\r\n\r\n.sg-accordion-trigger:hover:not(:disabled) {\r\n background: color-mix(in srgb, var(--sg-accent-soft) 70%, transparent);\r\n}\r\n\r\n.sg-accordion-trigger[data-state=\"open\"] {\r\n background: color-mix(in srgb, var(--sg-accent-soft) 55%, transparent);\r\n}\r\n\r\n.sg-accordion-trigger:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: -2px;\r\n z-index: 1;\r\n}\r\n\r\n.sg-accordion-trigger:disabled {\r\n opacity: 0.5;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-accordion-trigger-label {\r\n flex: 1;\r\n min-width: 0;\r\n padding-left: 0.35rem;\r\n}\r\n\r\n.sg-accordion-chevron-wrap {\r\n flex-shrink: 0;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 1.75rem;\r\n height: 1.75rem;\r\n border-radius: 999px;\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: color-mix(in srgb, var(--sg-surface-solid) 70%, var(--sg-accent-soft));\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-accordion-trigger:hover:not(:disabled) .sg-accordion-chevron-wrap {\r\n border-color: color-mix(in srgb, var(--sg-accent) 28%, var(--sg-border-frost));\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-accordion-chevron-wrap[data-open=\"true\"] {\r\n transform: rotate(180deg);\r\n color: var(--sg-accent-fg);\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n}\r\n\r\n.sg-accordion-chevron {\r\n width: 0.95rem;\r\n height: 0.95rem;\r\n display: block;\r\n}\r\n\r\n.sg-accordion-panel[hidden] {\r\n display: none;\r\n}\r\n\r\n.sg-accordion-content {\r\n padding: 0 var(--sg-disclosure-pad-x) 1.05rem;\r\n padding-left: calc(var(--sg-disclosure-pad-x) + 0.35rem);\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-sm);\r\n line-height: 1.6;\r\n}\r\n\r\n.sg-accordion[data-motion=\"fade\"] .sg-accordion-panel[data-state=\"open\"] .sg-accordion-content {\r\n animation: sg-disclosure-fade var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n/* —— Breadcrumb (v1.4a) —— */\r\n.sg-breadcrumb {\r\n min-width: 0;\r\n width: fit-content;\r\n max-width: 100%;\r\n}\r\n\r\n.sg-breadcrumb-list {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n gap: 0.2rem 0.15rem;\r\n margin: 0;\r\n padding: 0;\r\n list-style: none;\r\n}\r\n\r\n.sg-breadcrumb[data-look=\"soft\"] {\r\n padding: 0.35rem 0.55rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-breadcrumb[data-look=\"pill\"] .sg-breadcrumb-list {\r\n gap: 0.35rem;\r\n}\r\n\r\n.sg-breadcrumb-item {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.35rem;\r\n min-width: 0;\r\n font-size: var(--sg-text-sm);\r\n line-height: 1.4;\r\n}\r\n\r\n.sg-breadcrumb[data-size=\"sm\"] .sg-breadcrumb-item {\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-breadcrumb-separator {\r\n color: var(--sg-fg-subtle, var(--sg-fg-muted));\r\n opacity: 0.7;\r\n user-select: none;\r\n font-weight: var(--sg-font-weight-medium);\r\n}\r\n\r\n.sg-breadcrumb-link.sg-link {\r\n font-size: inherit;\r\n font-weight: var(--sg-font-weight-medium);\r\n text-decoration: none;\r\n border-radius: var(--sg-radius-sm);\r\n padding: 0.15rem 0.3rem;\r\n margin: -0.15rem -0.15rem;\r\n}\r\n\r\n.sg-breadcrumb-link.sg-link:hover {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-breadcrumb-page {\r\n color: var(--sg-fg);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n padding: 0.15rem 0.3rem;\r\n}\r\n\r\n.sg-breadcrumb[data-look=\"pill\"] .sg-breadcrumb-separator {\r\n opacity: 0.45;\r\n font-size: 0.85em;\r\n}\r\n\r\n.sg-breadcrumb[data-look=\"pill\"] .sg-breadcrumb-link.sg-link,\r\n.sg-breadcrumb[data-look=\"pill\"] .sg-breadcrumb-page {\r\n display: inline-flex;\r\n align-items: center;\r\n min-height: 1.7rem;\r\n padding: 0.2rem 0.7rem;\r\n margin: 0;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-breadcrumb[data-look=\"pill\"] .sg-breadcrumb-link.sg-link:hover {\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 22%, var(--sg-border-frost));\r\n}\r\n\r\n.sg-breadcrumb[data-look=\"pill\"] .sg-breadcrumb-page {\r\n color: var(--sg-accent-fg);\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n}\r\n\r\n/* —— Pagination (v1.4a) —— */\r\n.sg-pagination {\r\n width: fit-content;\r\n max-width: 100%;\r\n contain: layout style;\r\n}\r\n\r\n.sg-pagination-list {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n gap: 0.3rem;\r\n margin: 0;\r\n padding: 0;\r\n list-style: none;\r\n}\r\n\r\n.sg-pagination[data-look=\"soft\"] .sg-pagination-list,\r\n.sg-pagination[data-look=\"glass\"] .sg-pagination-list {\r\n gap: 0.2rem;\r\n padding: 0.3rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-pagination[data-look=\"glass\"] .sg-pagination-list {\r\n background: var(--sg-surface-frost-strong, var(--sg-surface-frost));\r\n border-color: color-mix(in srgb, var(--sg-border-frost) 75%, var(--sg-accent) 14%);\r\n}\r\n\r\n@media (prefers-reduced-transparency: no-preference) {\r\n .sg-pagination[data-look=\"glass\"] .sg-pagination-list {\r\n background: color-mix(in srgb, var(--sg-surface-glass) 88%, transparent);\r\n backdrop-filter: blur(10px) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(10px) saturate(var(--sg-glass-saturate));\r\n }\r\n}\r\n\r\n.sg-pagination-item {\r\n display: inline-flex;\r\n}\r\n\r\n.sg-pagination-btn {\r\n appearance: none;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-width: 2.35rem;\r\n min-height: 2.35rem;\r\n padding: 0 0.7rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid transparent;\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n letter-spacing: var(--sg-tracking-tight);\r\n font-variant-numeric: tabular-nums;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-pagination[data-size=\"sm\"] .sg-pagination-btn {\r\n min-width: 1.95rem;\r\n min-height: 1.95rem;\r\n padding: 0 0.5rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-pagination[data-look=\"solid\"] .sg-pagination-btn {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-pagination[data-look=\"ghost\"] .sg-pagination-btn {\r\n background: transparent;\r\n border-color: transparent;\r\n}\r\n\r\n.sg-pagination-btn:hover:not(:disabled):not([data-active=\"true\"]) {\r\n color: var(--sg-fg);\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 16%, transparent);\r\n}\r\n\r\n.sg-pagination-btn:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.sg-pagination-btn[data-active=\"true\"] {\r\n color: var(--sg-accent-fg);\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n font-weight: var(--sg-font-weight-semibold);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-pagination-btn[data-active=\"true\"]:hover:not(:disabled) {\r\n background: var(--sg-accent-hover);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-pagination-btn:disabled {\r\n opacity: 0.42;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-pagination-prev,\r\n.sg-pagination-next {\r\n min-width: auto;\r\n padding-inline: 0.9rem;\r\n font-weight: var(--sg-font-weight-semibold);\r\n}\r\n\r\n.sg-pagination-ellipsis {\r\n min-width: 1.4rem;\r\n justify-content: center;\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-sm);\r\n user-select: none;\r\n opacity: 0.75;\r\n}\r\n\r\n.sg-pagination-status {\r\n display: inline-flex;\r\n align-items: center;\r\n min-height: 2.35rem;\r\n padding: 0 0.75rem;\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .sg-collapsible[data-motion=\"fade\"] .sg-collapsible-content,\r\n .sg-accordion[data-motion=\"fade\"] .sg-accordion-content {\r\n animation: none !important;\r\n }\r\n\r\n .sg-collapsible-chevron-wrap[data-open=\"true\"],\r\n .sg-accordion-chevron-wrap[data-open=\"true\"] {\r\n transition: none;\r\n }\r\n}\r\n\r\n/* —— EmptyState (v1.4b) —— */\r\n.sg-empty-state {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n text-align: center;\r\n gap: 0.65rem;\r\n width: 100%;\r\n padding: 2rem 1.5rem;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-solid);\r\n box-sizing: border-box;\r\n contain: layout style;\r\n}\r\n\r\n.sg-empty-state[data-look=\"soft\"] {\r\n background: var(--sg-surface-frost);\r\n border-color: var(--sg-border-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-empty-state[data-look=\"outline\"] {\r\n background: transparent;\r\n border-style: dashed;\r\n border-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-empty-state[data-size=\"sm\"] {\r\n padding: 1.25rem 1rem;\r\n gap: 0.45rem;\r\n}\r\n\r\n.sg-empty-state[data-size=\"lg\"] {\r\n padding: 2.75rem 2rem;\r\n gap: 0.85rem;\r\n}\r\n\r\n.sg-empty-state-icon {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 3rem;\r\n height: 3rem;\r\n border-radius: var(--sg-radius-md);\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n font-size: 1.35rem;\r\n margin-bottom: 0.25rem;\r\n}\r\n\r\n.sg-empty-state[data-size=\"sm\"] .sg-empty-state-icon {\r\n width: 2.35rem;\r\n height: 2.35rem;\r\n font-size: 1.1rem;\r\n}\r\n\r\n.sg-empty-state-title {\r\n margin: 0;\r\n max-width: 28ch;\r\n font-size: var(--sg-text-lg);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n color: var(--sg-fg);\r\n line-height: var(--sg-leading-tight);\r\n}\r\n\r\n.sg-empty-state[data-size=\"sm\"] .sg-empty-state-title {\r\n font-size: var(--sg-text-md, var(--sg-text-base));\r\n}\r\n\r\n.sg-empty-state[data-size=\"lg\"] .sg-empty-state-title {\r\n font-size: var(--sg-text-xl);\r\n}\r\n\r\n.sg-empty-state-description {\r\n margin: 0;\r\n max-width: 42ch;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg-muted);\r\n line-height: 1.55;\r\n}\r\n\r\n.sg-empty-state-actions {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 0.55rem;\r\n margin-top: 0.55rem;\r\n}\r\n\r\n/* —— Sheet (v1.4b) —— */\r\n.sg-sheet-root {\r\n position: fixed;\r\n inset: 0;\r\n z-index: var(--sg-z-modal);\r\n isolation: isolate;\r\n box-sizing: border-box;\r\n}\r\n\r\n.sg-sheet-backdrop {\r\n position: absolute;\r\n inset: 0;\r\n border: 0;\r\n padding: 0;\r\n margin: 0;\r\n cursor: pointer;\r\n background: color-mix(in srgb, var(--sg-bg) 40%, rgba(15, 23, 42, 0.4));\r\n /* frost first — light blur only */\r\n backdrop-filter: blur(6px);\r\n -webkit-backdrop-filter: blur(6px);\r\n}\r\n\r\n.sg-sheet-root[data-state=\"open\"] .sg-sheet-backdrop {\r\n animation: sg-disclosure-fade var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-sheet-root[data-state=\"closed\"] .sg-sheet-backdrop {\r\n opacity: 0;\r\n transition: opacity var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-sheet-panel {\r\n position: absolute;\r\n z-index: 1;\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-4);\r\n box-sizing: border-box;\r\n padding: var(--sg-space-5);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-solid);\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 16px 48px color-mix(in srgb, var(--sg-fg) 12%, transparent);\r\n outline: none;\r\n}\r\n\r\n.sg-sheet-panel[data-side=\"right\"] {\r\n top: 0;\r\n right: 0;\r\n bottom: 0;\r\n width: min(100%, 24rem);\r\n border-right: 0;\r\n border-radius: var(--sg-radius-lg) 0 0 var(--sg-radius-lg);\r\n}\r\n\r\n.sg-sheet-panel[data-side=\"left\"] {\r\n top: 0;\r\n left: 0;\r\n bottom: 0;\r\n width: min(100%, 24rem);\r\n border-left: 0;\r\n border-radius: 0 var(--sg-radius-lg) var(--sg-radius-lg) 0;\r\n}\r\n\r\n.sg-sheet-panel[data-side=\"bottom\"] {\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n width: 100%;\r\n max-height: min(80dvh, 32rem);\r\n border-bottom: 0;\r\n border-radius: var(--sg-radius-lg) var(--sg-radius-lg) 0 0;\r\n}\r\n\r\n/* Slide enter/exit */\r\n.sg-sheet-panel[data-motion=\"slide\"][data-side=\"right\"][data-state=\"open\"] {\r\n animation: sg-sheet-in-right var(--sg-duration-normal) var(--sg-ease-out);\r\n}\r\n.sg-sheet-panel[data-motion=\"slide\"][data-side=\"right\"][data-state=\"closed\"] {\r\n transform: translateX(100%);\r\n transition: transform var(--sg-duration-normal) var(--sg-ease-out);\r\n}\r\n\r\n.sg-sheet-panel[data-motion=\"slide\"][data-side=\"left\"][data-state=\"open\"] {\r\n animation: sg-sheet-in-left var(--sg-duration-normal) var(--sg-ease-out);\r\n}\r\n.sg-sheet-panel[data-motion=\"slide\"][data-side=\"left\"][data-state=\"closed\"] {\r\n transform: translateX(-100%);\r\n transition: transform var(--sg-duration-normal) var(--sg-ease-out);\r\n}\r\n\r\n.sg-sheet-panel[data-motion=\"slide\"][data-side=\"bottom\"][data-state=\"open\"] {\r\n animation: sg-sheet-in-bottom var(--sg-duration-normal) var(--sg-ease-out);\r\n}\r\n.sg-sheet-panel[data-motion=\"slide\"][data-side=\"bottom\"][data-state=\"closed\"] {\r\n transform: translateY(100%);\r\n transition: transform var(--sg-duration-normal) var(--sg-ease-out);\r\n}\r\n\r\n.sg-sheet-panel[data-motion=\"fade\"][data-state=\"open\"] {\r\n animation: sg-disclosure-fade var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n.sg-sheet-panel[data-motion=\"fade\"][data-state=\"closed\"] {\r\n opacity: 0;\r\n transition: opacity var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n@keyframes sg-sheet-in-right {\r\n from { transform: translateX(100%); }\r\n to { transform: translateX(0); }\r\n}\r\n@keyframes sg-sheet-in-left {\r\n from { transform: translateX(-100%); }\r\n to { transform: translateX(0); }\r\n}\r\n@keyframes sg-sheet-in-bottom {\r\n from { transform: translateY(100%); }\r\n to { transform: translateY(0); }\r\n}\r\n\r\n.sg-sheet-header {\r\n display: flex;\r\n align-items: flex-start;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n flex-shrink: 0;\r\n}\r\n\r\n.sg-sheet-heading {\r\n min-width: 0;\r\n}\r\n\r\n.sg-sheet-title {\r\n margin: 0;\r\n font-size: var(--sg-text-lg);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n line-height: var(--sg-leading-tight);\r\n}\r\n\r\n.sg-sheet-description {\r\n margin: 0.35rem 0 0;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg-muted);\r\n line-height: 1.5;\r\n}\r\n\r\n.sg-sheet-body {\r\n min-height: 0;\r\n flex: 1;\r\n overflow: auto;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-sheet-footer {\r\n display: flex;\r\n flex-wrap: wrap;\r\n justify-content: flex-end;\r\n gap: var(--sg-space-3);\r\n flex-shrink: 0;\r\n padding-top: var(--sg-space-2);\r\n border-top: var(--sg-border-width) solid var(--sg-border-subtle);\r\n}\r\n\r\n.sg-sheet-close {\r\n flex-shrink: 0;\r\n}\r\n\r\n/* —— HoverCard (v1.4b) —— */\r\n.sg-hover-card-root {\r\n display: inline-flex;\r\n vertical-align: middle;\r\n}\r\n\r\n.sg-hover-card-trigger {\r\n display: inline-flex;\r\n max-width: 100%;\r\n}\r\n\r\n.sg-hover-card-content {\r\n z-index: var(--sg-z-popover, var(--sg-z-modal));\r\n min-width: 12rem;\r\n max-width: min(20rem, calc(100vw - 1.5rem));\r\n padding: 0.85rem 1rem;\r\n border-radius: var(--sg-radius-md);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 10px 28px color-mix(in srgb, var(--sg-fg) 10%, transparent);\r\n color: var(--sg-fg);\r\n font-size: var(--sg-text-sm);\r\n line-height: 1.5;\r\n box-sizing: border-box;\r\n contain: layout style;\r\n}\r\n\r\n.sg-hover-card-content[data-motion=\"scale\"][data-state=\"open\"] {\r\n animation: sg-hover-card-scale var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n.sg-hover-card-content[data-motion=\"fade\"][data-state=\"open\"] {\r\n animation: sg-disclosure-fade var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n.sg-hover-card-content[data-motion=\"slide-down\"][data-state=\"open\"] {\r\n animation: sg-hover-card-slide var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-hover-card-content[data-state=\"closed\"] {\r\n opacity: 0;\r\n transition: opacity var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n@keyframes sg-hover-card-scale {\r\n from { opacity: 0; transform: scale(0.96); }\r\n to { opacity: 1; transform: scale(1); }\r\n}\r\n@keyframes sg-hover-card-slide {\r\n from { opacity: 0; transform: translateY(-4px); }\r\n to { opacity: 1; transform: translateY(0); }\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .sg-sheet-backdrop,\r\n .sg-sheet-panel,\r\n .sg-hover-card-content {\r\n animation: none !important;\r\n transition: none !important;\r\n }\r\n}\r\n\r\n@media (prefers-reduced-transparency: reduce) {\r\n .sg-sheet-backdrop {\r\n backdrop-filter: none;\r\n -webkit-backdrop-filter: none;\r\n }\r\n .sg-hover-card-content {\r\n background: var(--sg-surface-solid);\r\n }\r\n}\r\n\r\n/* —— Stepper (v1.4c) — structural looks —— */\r\n.sg-stepper {\r\n --sg-stepper-progress: 0%;\r\n position: relative;\r\n width: 100%;\r\n contain: layout style;\r\n}\r\n.sg-stepper-track { display: none; }\r\n.sg-stepper[data-look=\"soft\"] {\r\n padding: 1rem 1.1rem 1.15rem;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n.sg-stepper[data-look=\"solid\"] {\r\n padding: 1rem 1.1rem;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-solid);\r\n}\r\n.sg-stepper[data-look=\"outline\"] { padding: 0.25rem 0; }\r\n\r\n/*\r\n * Dots: NO continuous track under the circles (gloss/frost bg used to show\r\n * the line through halos). Instead each gap is its own segment, inset so\r\n * it only sits *between* opaque markers.\r\n */\r\n.sg-stepper[data-look=\"dots\"] {\r\n --sg-stepper-dot: 2.5rem;\r\n --sg-stepper-track-h: 2px;\r\n --sg-stepper-pad-y: 0.35rem;\r\n --sg-stepper-count: 3;\r\n padding: var(--sg-stepper-pad-y) 0 0.65rem;\r\n}\r\n.sg-stepper[data-look=\"dots\"] .sg-stepper-trigger {\r\n padding-top: 0;\r\n padding-bottom: 0.5rem;\r\n gap: 0.55rem;\r\n}\r\n/* Hide the global track entirely for dots */\r\n.sg-stepper[data-look=\"dots\"] .sg-stepper-track {\r\n display: none !important;\r\n}\r\n/* Gap connector: from previous circle edge → this circle edge */\r\n.sg-stepper[data-look=\"dots\"] .sg-stepper-item:not(:first-child)::before {\r\n content: \"\";\r\n position: absolute;\r\n z-index: 0;\r\n pointer-events: none;\r\n height: var(--sg-stepper-track-h);\r\n top: calc(\r\n var(--sg-stepper-pad-y) + (var(--sg-stepper-dot) / 2) -\r\n (var(--sg-stepper-track-h) / 2)\r\n );\r\n /* Span previous half + this half, then inset by half a dot on each side */\r\n left: calc(-50% + (var(--sg-stepper-dot) / 2));\r\n width: calc(100% - var(--sg-stepper-dot));\r\n border-radius: 999px;\r\n background: color-mix(in srgb, var(--sg-border-subtle) 90%, var(--sg-fg) 6%);\r\n}\r\n/* Completed path segments (line leading into complete/current) */\r\n.sg-stepper[data-look=\"dots\"]\r\n .sg-stepper-item[data-status=\"complete\"]:not(:first-child)::before,\r\n.sg-stepper[data-look=\"dots\"]\r\n .sg-stepper-item[data-status=\"current\"]:not(:first-child)::before {\r\n background: var(--sg-accent);\r\n}\r\n.sg-stepper[data-look=\"pills\"] {\r\n padding: 0.35rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n.sg-stepper[data-look=\"pills\"] .sg-stepper-list { gap: 0.25rem; }\r\n.sg-stepper[data-look=\"pills\"] .sg-stepper-item { flex: 1 1 auto; min-width: 0; }\r\n.sg-stepper[data-look=\"pills\"] .sg-stepper-trigger {\r\n flex-direction: row;\r\n gap: 0.45rem;\r\n padding: 0.45rem 0.75rem;\r\n border-radius: var(--sg-radius-pill);\r\n text-align: left;\r\n}\r\n.sg-stepper[data-look=\"pills\"] .sg-stepper-item[data-status=\"current\"] .sg-stepper-trigger {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n.sg-stepper[data-look=\"pills\"] .sg-stepper-item[data-status=\"current\"] .sg-stepper-label,\r\n.sg-stepper[data-look=\"pills\"] .sg-stepper-item[data-status=\"current\"] .sg-stepper-description {\r\n color: var(--sg-accent-fg);\r\n}\r\n.sg-stepper[data-look=\"pills\"] .sg-stepper-item[data-status=\"current\"] .sg-stepper-indicator {\r\n background: color-mix(in srgb, #fff 22%, transparent);\r\n color: var(--sg-accent-fg);\r\n border-color: transparent;\r\n}\r\n.sg-stepper[data-look=\"pills\"] .sg-stepper-item[data-status=\"complete\"] .sg-stepper-trigger {\r\n background: var(--sg-accent-soft);\r\n}\r\n.sg-stepper[data-look=\"pills\"] .sg-stepper-description { display: none; }\r\n.sg-stepper[data-look=\"pills\"] .sg-stepper-indicator {\r\n width: 1.55rem;\r\n height: 1.55rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n.sg-stepper-list {\r\n position: relative;\r\n z-index: 1;\r\n display: flex;\r\n flex-wrap: nowrap;\r\n align-items: flex-start;\r\n gap: 0;\r\n margin: 0;\r\n padding: 0;\r\n list-style: none;\r\n width: 100%;\r\n}\r\n.sg-stepper[data-orientation=\"vertical\"] .sg-stepper-list {\r\n flex-direction: column;\r\n gap: 0.15rem;\r\n}\r\n.sg-stepper[data-orientation=\"vertical\"][data-look=\"dots\"] .sg-stepper-item:not(:first-child)::before {\r\n left: calc(var(--sg-stepper-dot) / 2 - var(--sg-stepper-track-h) / 2);\r\n width: var(--sg-stepper-track-h);\r\n top: calc(-50% + (var(--sg-stepper-dot) / 2));\r\n height: calc(100% - var(--sg-stepper-dot));\r\n}\r\n.sg-stepper-item {\r\n position: relative;\r\n display: flex;\r\n flex: 1 1 0;\r\n min-width: 0;\r\n justify-content: center;\r\n}\r\n.sg-stepper[data-orientation=\"vertical\"] .sg-stepper-item {\r\n flex: 0 0 auto;\r\n width: 100%;\r\n justify-content: flex-start;\r\n}\r\n.sg-stepper-trigger {\r\n appearance: none;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n gap: 0.5rem;\r\n width: 100%;\r\n margin: 0;\r\n padding: 0.2rem 0.4rem;\r\n border: 0;\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n font-family: var(--sg-font-sans);\r\n text-align: center;\r\n cursor: default;\r\n border-radius: var(--sg-radius-md);\r\n}\r\n.sg-stepper[data-orientation=\"vertical\"] .sg-stepper-trigger {\r\n flex-direction: row;\r\n text-align: left;\r\n gap: 0.8rem;\r\n padding: 0.45rem 0.35rem;\r\n}\r\n.sg-stepper-trigger:not(:disabled) { cursor: pointer; }\r\n.sg-stepper-trigger:not(:disabled):hover .sg-stepper-indicator {\r\n transform: translateY(-1px);\r\n border-color: color-mix(in srgb, var(--sg-accent) 40%, var(--sg-border-frost));\r\n}\r\n.sg-stepper-trigger:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 2px;\r\n}\r\n.sg-stepper-indicator {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 2.25rem;\r\n height: 2.25rem;\r\n border-radius: 999px;\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-solid);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight), 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n color: var(--sg-fg-muted);\r\n flex-shrink: 0;\r\n transition: transform var(--sg-duration-fast) var(--sg-ease-out), background var(--sg-duration-fast) var(--sg-ease-out), border-color var(--sg-duration-fast) var(--sg-ease-out), color var(--sg-duration-fast) var(--sg-ease-out), box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n.sg-stepper[data-look=\"soft\"] .sg-stepper-indicator {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 70%, var(--sg-accent-soft));\r\n}\r\n.sg-stepper[data-look=\"outline\"] .sg-stepper-indicator {\r\n background: transparent;\r\n box-shadow: none;\r\n}\r\n.sg-stepper[data-look=\"dots\"] .sg-stepper-indicator {\r\n position: relative;\r\n z-index: 2;\r\n width: var(--sg-stepper-dot);\r\n height: var(--sg-stepper-dot);\r\n /* Fully opaque — no solid-halo (visible on glossy/frost card bg) */\r\n background: var(--sg-surface-solid);\r\n border: 2px solid var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n.sg-stepper[data-look=\"dots\"] .sg-stepper-item[data-status=\"current\"] .sg-stepper-indicator {\r\n color: var(--sg-accent-fg);\r\n background: var(--sg-accent);\r\n border-color: var(--sg-accent);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n.sg-stepper[data-look=\"dots\"] .sg-stepper-item[data-status=\"complete\"] .sg-stepper-indicator {\r\n color: var(--sg-accent);\r\n /* Opaque tint (not translucent accent-soft) */\r\n background: color-mix(in srgb, var(--sg-accent) 18%, var(--sg-surface-solid));\r\n border-color: color-mix(in srgb, var(--sg-accent) 45%, var(--sg-border-subtle));\r\n box-shadow: none;\r\n}\r\n/* Non-dots status chrome */\r\n.sg-stepper:not([data-look=\"dots\"]) .sg-stepper-item[data-status=\"current\"] .sg-stepper-indicator {\r\n color: var(--sg-accent-fg);\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 0 0 4px color-mix(in srgb, var(--sg-accent) 18%, transparent);\r\n}\r\n.sg-stepper:not([data-look=\"dots\"]) .sg-stepper-item[data-status=\"complete\"] .sg-stepper-indicator {\r\n color: var(--sg-accent);\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 30%, transparent);\r\n}\r\n.sg-stepper-item[data-status=\"current\"] .sg-stepper-label {\r\n color: var(--sg-fg);\r\n font-weight: var(--sg-font-weight-semibold);\r\n}\r\n.sg-stepper-item[data-status=\"complete\"] .sg-stepper-label { color: var(--sg-fg); }\r\n.sg-stepper-check { width: 1rem; height: 1rem; display: block; }\r\n.sg-stepper-text { display: flex; flex-direction: column; gap: 0.12rem; min-width: 0; }\r\n.sg-stepper-label {\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n letter-spacing: var(--sg-tracking-tight);\r\n color: var(--sg-fg-muted);\r\n}\r\n.sg-stepper-description {\r\n font-size: var(--sg-text-xs);\r\n color: var(--sg-fg-subtle, var(--sg-fg-muted));\r\n line-height: 1.35;\r\n}\r\n.sg-stepper[data-size=\"sm\"] .sg-stepper-indicator {\r\n width: 1.85rem;\r\n height: 1.85rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n.sg-stepper[data-size=\"sm\"] .sg-stepper-label { font-size: var(--sg-text-xs); }\r\n\r\n/* —— Toolbar (v1.4c) —— */\r\n.sg-toolbar {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n gap: 0.55rem;\r\n width: 100%;\r\n padding: 0.6rem 0.75rem;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n box-sizing: border-box;\r\n contain: layout style;\r\n}\r\n.sg-toolbar[data-size=\"sm\"] {\r\n padding: 0.4rem 0.55rem;\r\n gap: 0.4rem;\r\n border-radius: var(--sg-radius-md);\r\n}\r\n.sg-toolbar[data-look=\"solid\"] {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: 0 1px 0 color-mix(in srgb, var(--sg-fg) 4%, transparent);\r\n}\r\n.sg-toolbar[data-look=\"glass\"] {\r\n background: var(--sg-surface-frost-strong, var(--sg-surface-frost));\r\n border-color: color-mix(in srgb, var(--sg-border-frost) 75%, var(--sg-accent) 16%);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight), 0 8px 24px color-mix(in srgb, var(--sg-accent) 8%, transparent);\r\n}\r\n@media (prefers-reduced-transparency: no-preference) {\r\n .sg-toolbar[data-look=\"glass\"] {\r\n background: color-mix(in srgb, var(--sg-surface-glass) 88%, transparent);\r\n backdrop-filter: blur(12px) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(12px) saturate(var(--sg-glass-saturate));\r\n }\r\n}\r\n.sg-toolbar[data-look=\"ghost\"] {\r\n background: transparent;\r\n border-color: transparent;\r\n box-shadow: none;\r\n padding-inline: 0;\r\n}\r\n.sg-toolbar[data-look=\"accent\"] {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n border-left: 3px solid var(--sg-accent);\r\n border-radius: var(--sg-radius-md);\r\n}\r\n.sg-toolbar[data-align=\"start\"] { justify-content: flex-start; }\r\n.sg-toolbar[data-align=\"end\"] { justify-content: flex-end; }\r\n.sg-toolbar:not([data-align]) { justify-content: space-between; }\r\n.sg-toolbar-group {\r\n display: inline-flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n gap: 0.45rem;\r\n min-width: 0;\r\n}\r\n.sg-toolbar-group-inset,\r\n.sg-toolbar-group[data-inset=\"true\"] {\r\n padding: 0.25rem;\r\n border-radius: var(--sg-radius-md);\r\n background: color-mix(in srgb, var(--sg-surface-solid) 55%, transparent);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n}\r\n.sg-toolbar-spacer { flex: 1 1 auto; min-width: 0.5rem; }\r\n.sg-toolbar-sep {\r\n width: 1px;\r\n align-self: stretch;\r\n min-height: 1.5rem;\r\n background: var(--sg-border-subtle);\r\n margin-inline: 0.15rem;\r\n}\r\n\r\n/* —— List (v1.4c) —— */\r\n.sg-list {\r\n display: flex;\r\n flex-direction: column;\r\n width: 100%;\r\n margin: 0;\r\n padding: 0.4rem;\r\n list-style: none;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n box-sizing: border-box;\r\n contain: layout style;\r\n gap: 0.2rem;\r\n}\r\n.sg-list[data-look=\"solid\"] {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n.sg-list[data-look=\"outline\"] {\r\n background: transparent;\r\n box-shadow: none;\r\n}\r\n.sg-list[data-look=\"ghost\"] {\r\n background: transparent;\r\n border-color: transparent;\r\n box-shadow: none;\r\n padding: 0;\r\n gap: 0.35rem;\r\n}\r\n.sg-list[data-look=\"inset\"] {\r\n background: color-mix(in srgb, var(--sg-fg) 4%, var(--sg-surface-solid));\r\n border-color: transparent;\r\n box-shadow: inset 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n padding: 0.5rem;\r\n}\r\n.sg-list[data-density=\"compact\"] { padding: 0.2rem; gap: 0.08rem; }\r\n.sg-list[data-density=\"compact\"] .sg-list-item { padding-block: 0.4rem; }\r\n.sg-list[data-density=\"relaxed\"] { padding: 0.55rem; gap: 0.35rem; }\r\n.sg-list[data-density=\"relaxed\"] .sg-list-item { padding-block: 0.85rem; }\r\n/* Dividers: seamless stack — first/last keep radius, middles square edges */\r\n.sg-list[data-dividers=\"true\"] {\r\n gap: 0;\r\n padding: 0.25rem;\r\n}\r\n.sg-list[data-dividers=\"true\"] > * {\r\n border-radius: 0;\r\n}\r\n.sg-list[data-dividers=\"true\"] > *:first-child {\r\n border-radius: var(--sg-radius-md) var(--sg-radius-md) 0 0;\r\n}\r\n.sg-list[data-dividers=\"true\"] > *:last-child {\r\n border-radius: 0 0 var(--sg-radius-md) var(--sg-radius-md);\r\n}\r\n.sg-list[data-dividers=\"true\"] > *:only-child {\r\n border-radius: var(--sg-radius-md);\r\n}\r\n.sg-list[data-dividers=\"true\"] > * + * {\r\n border-top: var(--sg-border-width) solid\r\n color-mix(in srgb, var(--sg-border-frost) 88%, transparent);\r\n}\r\n.sg-list[data-look=\"ghost\"][data-dividers=\"true\"] {\r\n padding: 0;\r\n}\r\n.sg-list[data-look=\"ghost\"][data-dividers=\"true\"] > * + * {\r\n border-top-color: var(--sg-border-subtle);\r\n}\r\n\r\n/* —— Stat (v1.4c) —— */\r\n.sg-stat {\r\n position: relative;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 0.4rem;\r\n min-width: 0;\r\n padding: 1.05rem 1.15rem;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-solid);\r\n box-sizing: border-box;\r\n contain: layout style;\r\n overflow: hidden;\r\n}\r\n.sg-stat[data-size=\"sm\"] { padding: 0.75rem 0.85rem; gap: 0.25rem; }\r\n.sg-stat[data-size=\"lg\"] { padding: 1.35rem 1.4rem; gap: 0.5rem; }\r\n.sg-stat[data-look=\"soft\"] {\r\n background: var(--sg-surface-frost);\r\n border-color: var(--sg-border-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n.sg-stat[data-look=\"glass\"] {\r\n background: var(--sg-surface-frost-strong, var(--sg-surface-frost));\r\n border-color: color-mix(in srgb, var(--sg-border-frost) 75%, var(--sg-accent) 16%);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight), 0 10px 28px color-mix(in srgb, var(--sg-accent) 10%, transparent);\r\n}\r\n@media (prefers-reduced-transparency: no-preference) {\r\n .sg-stat[data-look=\"glass\"] {\r\n background: color-mix(in srgb, var(--sg-surface-glass) 88%, transparent);\r\n backdrop-filter: blur(12px) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(12px) saturate(var(--sg-glass-saturate));\r\n }\r\n}\r\n.sg-stat[data-look=\"outline\"] {\r\n background: transparent;\r\n border-style: dashed;\r\n}\r\n.sg-stat[data-look=\"accent\"] {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n padding-left: 1.25rem;\r\n}\r\n.sg-stat[data-look=\"accent\"]::before {\r\n content: \"\";\r\n position: absolute;\r\n left: 0;\r\n top: 0.65rem;\r\n bottom: 0.65rem;\r\n width: 4px;\r\n border-radius: 999px;\r\n background: linear-gradient(180deg, var(--sg-accent), color-mix(in srgb, var(--sg-accent) 55%, var(--sg-ring)));\r\n box-shadow: 0 0 12px color-mix(in srgb, var(--sg-accent) 40%, transparent);\r\n}\r\n.sg-stat-icon {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 2rem;\r\n height: 2rem;\r\n border-radius: var(--sg-radius-sm);\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n font-size: 1rem;\r\n margin-bottom: 0.15rem;\r\n}\r\n.sg-stat-label {\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: 0.06em;\r\n text-transform: uppercase;\r\n color: var(--sg-fg-muted);\r\n}\r\n.sg-stat-value-row {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: baseline;\r\n gap: 0.45rem 0.65rem;\r\n}\r\n.sg-stat-value {\r\n font-size: 1.7rem;\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n color: var(--sg-fg);\r\n font-variant-numeric: tabular-nums;\r\n line-height: 1.1;\r\n}\r\n.sg-stat[data-size=\"sm\"] .sg-stat-value { font-size: 1.3rem; }\r\n.sg-stat[data-size=\"lg\"] .sg-stat-value { font-size: 2.05rem; }\r\n.sg-stat-trend {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.2rem;\r\n padding: 0.18rem 0.5rem;\r\n border-radius: var(--sg-radius-pill);\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-semibold);\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n font-variant-numeric: tabular-nums;\r\n}\r\n.sg-stat-trend[data-trend=\"up\"] {\r\n background: color-mix(in srgb, var(--sg-success, #16a34a) 16%, transparent);\r\n color: var(--sg-success, #16a34a);\r\n}\r\n.sg-stat-trend[data-trend=\"down\"] {\r\n background: color-mix(in srgb, var(--sg-danger, #dc2626) 14%, transparent);\r\n color: var(--sg-danger, #dc2626);\r\n}\r\n.sg-stat-trend[data-trend=\"flat\"] {\r\n background: color-mix(in srgb, var(--sg-fg-muted) 12%, transparent);\r\n color: var(--sg-fg-muted);\r\n}\r\n.sg-stat-hint {\r\n font-size: var(--sg-text-xs);\r\n color: var(--sg-fg-muted);\r\n line-height: 1.4;\r\n}\r\n@media (prefers-reduced-motion: reduce) {\r\n .sg-stepper-indicator { transition: none !important; }\r\n .sg-stepper-trigger:not(:disabled):hover .sg-stepper-indicator { transform: none; }\r\n}\r\n/* MultiSelect in-menu filter (v1.4d) */\r\n.sg-multiselect-menu {\r\n display: flex;\r\n flex-direction: column;\r\n padding: 0;\r\n overflow: hidden;\r\n max-height: min(20rem, 70vh);\r\n}\r\n.sg-multiselect-filter {\r\n flex-shrink: 0;\r\n padding: 0.45rem 0.5rem;\r\n border-bottom: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: color-mix(in srgb, var(--sg-surface-solid) 55%, transparent);\r\n}\r\n.sg-multiselect-filter-input {\r\n appearance: none;\r\n width: 100%;\r\n margin: 0;\r\n padding: 0.45rem 0.65rem;\r\n border-radius: var(--sg-radius-sm);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-solid);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n box-sizing: border-box;\r\n}\r\n.sg-multiselect-filter-input:focus {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 1px;\r\n}\r\n.sg-multiselect-options {\r\n margin: 0;\r\n padding: 0.35rem;\r\n list-style: none;\r\n overflow: auto;\r\n flex: 1 1 auto;\r\n min-height: 0;\r\n}\r\n.sg-combobox-loading {\r\n display: flex;\r\n align-items: center;\r\n gap: 0.4rem;\r\n opacity: 0.85;\r\n}\r\n\r\n/* —— DataTable (v1.7-0) —— */\r\n.sg-data-table {\r\n --sg-table-row-pad-y: 0.7rem;\r\n --sg-table-row-pad-x: 0.85rem;\r\n --sg-table-head-pad-y: 0.65rem;\r\n display: flex;\r\n flex-direction: column;\r\n width: 100%;\r\n min-width: 0;\r\n min-height: 0;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n box-sizing: border-box;\r\n contain: layout style;\r\n overflow: hidden;\r\n}\r\n\r\n.sg-data-table[data-look=\"solid\"] {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-data-table[data-look=\"outline\"] {\r\n background: transparent;\r\n box-shadow: none;\r\n}\r\n\r\n.sg-data-table[data-look=\"ghost\"] {\r\n background: transparent;\r\n border-color: transparent;\r\n box-shadow: none;\r\n border-radius: 0;\r\n}\r\n\r\n.sg-data-table[data-look=\"glass\"] {\r\n background: var(--sg-surface-frost-strong, var(--sg-surface-frost));\r\n border-color: color-mix(in srgb, var(--sg-border-frost) 75%, var(--sg-accent) 12%);\r\n}\r\n\r\n@supports (backdrop-filter: blur(1px)) {\r\n .sg-data-table[data-look=\"glass\"] {\r\n background: color-mix(in srgb, var(--sg-surface-glass) 88%, transparent);\r\n backdrop-filter: blur(12px) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(12px) saturate(var(--sg-glass-saturate));\r\n }\r\n}\r\n\r\n.sg-data-table[data-density=\"compact\"] {\r\n --sg-table-row-pad-y: 0.4rem;\r\n --sg-table-row-pad-x: 0.65rem;\r\n --sg-table-head-pad-y: 0.4rem;\r\n}\r\n\r\n.sg-data-table-scroll {\r\n flex: 1 1 auto;\r\n width: 100%;\r\n min-height: 0;\r\n max-width: 100%;\r\n overflow: auto;\r\n}\r\n\r\n/* Virtualization spacers — pure height, no chrome */\r\n.sg-table-row-spacer,\r\n.sg-table-row-spacer:hover {\r\n background: transparent !important;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-table-cell-spacer {\r\n padding: 0 !important;\r\n border: 0 !important;\r\n line-height: 0;\r\n font-size: 0;\r\n vertical-align: top;\r\n}\r\n\r\n.sg-table {\r\n width: 100%;\r\n border-collapse: separate;\r\n border-spacing: 0;\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-table-header {\r\n background: color-mix(in srgb, var(--sg-fg) 4%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-data-table[data-look=\"soft\"] .sg-table-header,\r\n.sg-data-table[data-look=\"glass\"] .sg-table-header {\r\n background: color-mix(in srgb, var(--sg-fg) 5%, transparent);\r\n}\r\n\r\n.sg-data-table[data-look=\"ghost\"] .sg-table-header {\r\n background: transparent;\r\n}\r\n\r\n.sg-table-head {\r\n position: relative;\r\n padding: var(--sg-table-head-pad-y) var(--sg-table-row-pad-x);\r\n text-align: start;\r\n font-weight: 600;\r\n font-size: var(--sg-text-xs);\r\n letter-spacing: 0.02em;\r\n text-transform: none;\r\n color: color-mix(in srgb, var(--sg-fg) 72%, transparent);\r\n border-bottom: var(--sg-border-width) solid\r\n color-mix(in srgb, var(--sg-border-frost) 90%, transparent);\r\n white-space: nowrap;\r\n vertical-align: middle;\r\n box-sizing: border-box;\r\n}\r\n\r\n.sg-data-table:not([data-sticky-header=\"false\"]) .sg-table-head {\r\n position: sticky;\r\n top: 0;\r\n z-index: 2;\r\n background: inherit;\r\n}\r\n\r\n.sg-data-table:not([data-sticky-header=\"false\"]) .sg-table-header {\r\n position: sticky;\r\n top: 0;\r\n z-index: 2;\r\n}\r\n\r\n.sg-data-table:not([data-sticky-header=\"false\"]) .sg-table-head {\r\n background: color-mix(in srgb, var(--sg-fg) 4%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-data-table[data-look=\"soft\"]:not([data-sticky-header=\"false\"]) .sg-table-head,\r\n.sg-data-table[data-look=\"glass\"]:not([data-sticky-header=\"false\"]) .sg-table-head {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 88%, var(--sg-surface-frost));\r\n}\r\n\r\n@supports (backdrop-filter: blur(1px)) {\r\n .sg-data-table[data-look=\"glass\"]:not([data-sticky-header=\"false\"]) .sg-table-head {\r\n background: color-mix(in srgb, var(--sg-surface-glass) 92%, transparent);\r\n backdrop-filter: blur(10px) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(10px) saturate(var(--sg-glass-saturate));\r\n }\r\n}\r\n\r\n.sg-table-head[data-align=\"center\"],\r\n.sg-table-cell[data-align=\"center\"] {\r\n text-align: center;\r\n}\r\n\r\n.sg-table-head[data-align=\"end\"],\r\n.sg-table-cell[data-align=\"end\"] {\r\n text-align: end;\r\n}\r\n\r\n.sg-table-head-select,\r\n.sg-table-cell-select {\r\n width: 2.75rem;\r\n padding-inline: 0.65rem;\r\n text-align: center;\r\n}\r\n\r\n.sg-table-select-spacer {\r\n display: inline-block;\r\n width: 1rem;\r\n height: 1rem;\r\n}\r\n\r\n.sg-table-sort-btn {\r\n appearance: none;\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.35rem;\r\n max-width: 100%;\r\n margin: 0;\r\n padding: 0.15rem 0.25rem;\r\n margin-inline: -0.25rem;\r\n border: none;\r\n border-radius: var(--sg-radius-sm);\r\n background: transparent;\r\n color: inherit;\r\n font: inherit;\r\n font-weight: inherit;\r\n letter-spacing: inherit;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-table-sort-btn:hover {\r\n background: color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-table-sort-btn:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 1px;\r\n}\r\n\r\n.sg-table-head-label {\r\n min-width: 0;\r\n}\r\n\r\n.sg-table-sort-icon {\r\n flex-shrink: 0;\r\n font-size: 0.75em;\r\n opacity: 0.85;\r\n line-height: 1;\r\n}\r\n\r\n.sg-table-sort-icon-idle {\r\n opacity: 0.35;\r\n}\r\n\r\n.sg-table-head[data-sorted] .sg-table-sort-icon {\r\n color: var(--sg-accent);\r\n opacity: 1;\r\n}\r\n\r\n.sg-table-row {\r\n transition: background-color 120ms ease;\r\n}\r\n\r\n.sg-table-row:hover {\r\n background: color-mix(in srgb, var(--sg-fg) 3.5%, transparent);\r\n}\r\n\r\n.sg-table-row[data-selected=\"true\"] {\r\n background: color-mix(in srgb, var(--sg-accent) 10%, transparent);\r\n}\r\n\r\n.sg-table-row[data-selected=\"true\"]:hover {\r\n background: color-mix(in srgb, var(--sg-accent) 14%, transparent);\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .sg-table-row {\r\n transition: none;\r\n }\r\n}\r\n\r\n.sg-table-cell {\r\n padding: var(--sg-table-row-pad-y) var(--sg-table-row-pad-x);\r\n text-align: start;\r\n vertical-align: middle;\r\n border-bottom: var(--sg-border-width) solid\r\n color-mix(in srgb, var(--sg-border-frost) 80%, transparent);\r\n box-sizing: border-box;\r\n}\r\n\r\n.sg-table-body .sg-table-row:last-child .sg-table-cell {\r\n border-bottom: none;\r\n}\r\n\r\n.sg-table-cell-empty {\r\n padding: 1.25rem 1rem;\r\n text-align: center;\r\n}\r\n\r\n.sg-table-cell-empty .sg-empty-state {\r\n margin-inline: auto;\r\n max-width: 22rem;\r\n}\r\n\r\n.sg-table-skeleton-block {\r\n display: block;\r\n height: 0.75rem;\r\n width: 72%;\r\n max-width: 12rem;\r\n border-radius: var(--sg-radius-pill);\r\n background: linear-gradient(\r\n 90deg,\r\n color-mix(in srgb, var(--sg-fg) 6%, transparent) 0%,\r\n color-mix(in srgb, var(--sg-fg) 12%, transparent) 50%,\r\n color-mix(in srgb, var(--sg-fg) 6%, transparent) 100%\r\n );\r\n background-size: 200% 100%;\r\n animation: sg-table-skeleton 1.2s ease-in-out infinite;\r\n}\r\n\r\n.sg-data-table[data-density=\"compact\"] .sg-table-skeleton-block {\r\n height: 0.55rem;\r\n max-width: 9rem;\r\n}\r\n\r\n.sg-table-skeleton-check {\r\n width: 1.1rem !important;\r\n height: 1.1rem !important;\r\n max-width: 1.1rem !important;\r\n margin-inline: auto;\r\n border-radius: var(--sg-radius-sm);\r\n}\r\n\r\n.sg-data-table[data-density=\"compact\"] .sg-table-skeleton-check {\r\n width: 0.95rem !important;\r\n height: 0.95rem !important;\r\n max-width: 0.95rem !important;\r\n}\r\n\r\n.sg-table-row-skeleton {\r\n pointer-events: none;\r\n}\r\n\r\n@keyframes sg-table-skeleton {\r\n 0% {\r\n background-position: 100% 0;\r\n }\r\n 100% {\r\n background-position: -100% 0;\r\n }\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .sg-table-skeleton-block {\r\n animation: none;\r\n }\r\n}\r\n", + "content": "/**\r\n * Softglass — shared engine (all languages)\r\n * Only colors / surface recipes change per theme.\r\n * Radius, spacing, type scale, glass structure stay shared.\r\n */\r\n\r\n:root {\r\n /* —— Radius (soft, never sharp) —— */\r\n --sg-radius-xs: 0.75rem; /* 12px */\r\n --sg-radius-sm: 1rem; /* 16px */\r\n --sg-radius-md: 1.25rem; /* 20px */\r\n --sg-radius-lg: 1.5rem; /* 24px */\r\n --sg-radius-xl: 2rem; /* 32px */\r\n --sg-radius-pill: 999px;\r\n\r\n /* —— Spacing scale —— */\r\n --sg-space-0: 0;\r\n --sg-space-1: 0.25rem;\r\n --sg-space-2: 0.5rem;\r\n --sg-space-3: 0.75rem;\r\n --sg-space-4: 1rem;\r\n --sg-space-5: 1.25rem;\r\n --sg-space-6: 1.5rem;\r\n --sg-space-8: 2rem;\r\n --sg-space-10: 2.5rem;\r\n --sg-space-12: 3rem;\r\n --sg-space-16: 4rem;\r\n\r\n /* —— Type scale (fluid-friendly) —— */\r\n --sg-font-sans: ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto,\r\n \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif;\r\n --sg-font-display: var(--sg-font-sans);\r\n --sg-text-xs: 0.75rem;\r\n --sg-text-sm: 0.875rem;\r\n --sg-text-base: 1rem;\r\n --sg-text-lg: 1.125rem;\r\n --sg-text-xl: 1.25rem;\r\n --sg-text-2xl: 1.5rem;\r\n --sg-text-3xl: clamp(1.75rem, 1.4rem + 1.2vw, 2.25rem);\r\n --sg-text-4xl: clamp(2rem, 1.5rem + 2vw, 3rem);\r\n --sg-leading-tight: 1.25;\r\n --sg-leading-normal: 1.55;\r\n --sg-leading-relaxed: 1.7;\r\n --sg-tracking-tight: -0.02em;\r\n --sg-tracking-normal: 0;\r\n --sg-font-weight-normal: 400;\r\n --sg-font-weight-medium: 500;\r\n --sg-font-weight-semibold: 600;\r\n --sg-font-weight-bold: 700;\r\n\r\n /* —— Motion —— */\r\n --sg-ease-out: cubic-bezier(0.22, 1, 0.36, 1);\r\n --sg-duration-fast: 150ms;\r\n --sg-duration-normal: 220ms;\r\n --sg-duration-slow: 360ms;\r\n\r\n /* —— Glass structure defaults (themes override values) —— */\r\n --sg-glass-blur: 16px;\r\n --sg-glass-blur-mobile: 10px;\r\n --sg-glass-saturate: 1.35;\r\n --sg-border-width: 1px;\r\n\r\n /* Frost defaults — themes override for readable chrome */\r\n --sg-surface-frost: rgba(255, 255, 255, 0.9);\r\n --sg-surface-frost-strong: rgba(255, 255, 255, 0.96);\r\n --sg-border-frost: rgba(15, 23, 42, 0.12);\r\n\r\n /* —— Z-index —— */\r\n --sg-z-base: 0;\r\n --sg-z-raised: 10;\r\n --sg-z-dropdown: 40;\r\n --sg-z-sticky: 50;\r\n /* Overlays sit well above sticky chrome (header/sidebar) */\r\n --sg-z-modal: 1000;\r\n --sg-z-toast: 1100;\r\n\r\n /* Toast stack: tight elevation so shadows do not bleed into the gap */\r\n --sg-shadow-toast: 0 4px 14px rgba(15, 23, 42, 0.1),\r\n 0 1px 3px rgba(15, 23, 42, 0.06);\r\n\r\n /* —— Touch / layout —— */\r\n --sg-touch-min: 2.75rem; /* 44px */\r\n --sg-content-max: 72rem;\r\n --sg-sidebar-width: 16rem;\r\n --sg-sidebar-width-collapsed: 4.5rem;\r\n\r\n color-scheme: light;\r\n}\r\n\r\n/* Reduced transparency → solid fallbacks (accessibility) */\r\n@media (prefers-reduced-transparency: reduce) {\r\n :root {\r\n --sg-glass-blur: 0px;\r\n --sg-glass-blur-mobile: 0px;\r\n --sg-surface-glass: var(--sg-surface-solid);\r\n --sg-surface-glass-elevated: var(--sg-surface-solid);\r\n --sg-surface-frost: var(--sg-surface-solid);\r\n --sg-surface-frost-strong: var(--sg-surface-solid);\r\n }\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n :root {\r\n --sg-duration-fast: 0ms;\r\n --sg-duration-normal: 0ms;\r\n --sg-duration-slow: 0ms;\r\n }\r\n}\r\n\r\n/* Shared material utilities — language-agnostic structure */\r\n.sg-surface-solid {\r\n background: var(--sg-surface-solid);\r\n color: var(--sg-fg);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n border-radius: var(--sg-radius-md);\r\n box-shadow: var(--sg-shadow-soft);\r\n}\r\n\r\n/* Cards/chrome: prefer frost (opaque + cheap). True blur only when .sg-blur is set. */\r\n.sg-surface-glass {\r\n background: var(--sg-surface-frost);\r\n color: var(--sg-fg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n border-radius: var(--sg-radius-lg);\r\n box-shadow: var(--sg-shadow-glass);\r\n background-image: linear-gradient(\r\n 180deg,\r\n var(--sg-gloss-highlight) 0%,\r\n transparent 42%\r\n );\r\n}\r\n\r\n.sg-surface-glass-elevated {\r\n background: var(--sg-surface-frost-strong);\r\n color: var(--sg-fg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n border-radius: var(--sg-radius-lg);\r\n box-shadow: var(--sg-shadow-elevated);\r\n background-image: linear-gradient(\r\n 180deg,\r\n var(--sg-gloss-highlight) 0%,\r\n transparent 48%\r\n );\r\n}\r\n\r\n.sg-surface-glass.sg-blur,\r\n.sg-surface-glass-elevated.sg-blur {\r\n backdrop-filter: blur(var(--sg-glass-blur)) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(var(--sg-glass-blur))\r\n saturate(var(--sg-glass-saturate));\r\n background: var(--sg-surface-glass);\r\n}\r\n\r\n.sg-surface-glass-elevated.sg-blur {\r\n background: var(--sg-surface-glass-elevated);\r\n}\r\n\r\n.sg-badge-sm {\r\n padding: 0.15rem 0.45rem;\r\n font-size: 0.65rem;\r\n}\r\n\r\n.sg-badge-lg {\r\n padding: 0.35rem 0.85rem;\r\n font-size: var(--sg-text-sm);\r\n}\r\n\r\n.sg-badge-dot::before {\r\n content: \"\";\r\n width: 0.4rem;\r\n height: 0.4rem;\r\n border-radius: 50%;\r\n background: currentColor;\r\n opacity: 0.85;\r\n}\r\n\r\n.sg-field-required::after {\r\n content: \" *\";\r\n color: var(--sg-danger);\r\n}\r\n\r\n.sg-input-block {\r\n width: 100%;\r\n}\r\n\r\n.sg-input-group {\r\n display: flex;\r\n align-items: stretch;\r\n width: 100%;\r\n border-radius: var(--sg-radius-sm);\r\n overflow: hidden;\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-solid);\r\n}\r\n\r\n.sg-input-group:focus-within {\r\n border-color: var(--sg-accent);\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-input-group .sg-input {\r\n border: 0;\r\n border-radius: 0;\r\n box-shadow: none;\r\n}\r\n\r\n.sg-input-group .sg-input:focus {\r\n box-shadow: none;\r\n}\r\n\r\n.sg-input-addon {\r\n display: inline-flex;\r\n align-items: center;\r\n padding: 0 0.75rem;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg-muted);\r\n background: var(--sg-accent-soft);\r\n white-space: nowrap;\r\n}\r\n\r\n@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {\r\n .sg-surface-glass,\r\n .sg-surface-glass-elevated {\r\n background: var(--sg-surface-solid);\r\n background-image: none;\r\n }\r\n}\r\n\r\n@media (max-width: 767px) {\r\n .sg-surface-glass.sg-blur,\r\n .sg-surface-glass-elevated.sg-blur {\r\n backdrop-filter: blur(var(--sg-glass-blur-mobile))\r\n saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(var(--sg-glass-blur-mobile))\r\n saturate(var(--sg-glass-saturate));\r\n }\r\n}\r\n\r\n/* —— Button —— */\r\n.sg-btn {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: var(--sg-space-2);\r\n min-height: var(--sg-touch-min);\r\n padding: 0.625rem 1.25rem;\r\n border-radius: var(--sg-radius-pill);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n border: var(--sg-border-width) solid transparent;\r\n cursor: pointer;\r\n text-decoration: none;\r\n user-select: none;\r\n transition: transform var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out),\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n opacity var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-btn:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.sg-btn:active:not(:disabled) {\r\n transform: translateY(1px);\r\n}\r\n\r\n.sg-btn:disabled {\r\n opacity: 0.5;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-btn-sm {\r\n min-height: 2.25rem;\r\n padding: 0.4rem 0.9rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-btn-lg {\r\n min-height: 3rem;\r\n padding: 0.75rem 1.5rem;\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-btn-icon {\r\n width: var(--sg-touch-min);\r\n padding-inline: 0;\r\n}\r\n\r\n.sg-btn-sm.sg-btn-icon {\r\n width: 2.25rem;\r\n}\r\n\r\n.sg-btn-lg.sg-btn-icon {\r\n width: 3rem;\r\n}\r\n\r\n.sg-btn-primary {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n box-shadow: var(--sg-shadow-soft), inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-btn-primary:hover:not(:disabled) {\r\n background: var(--sg-accent-hover);\r\n}\r\n\r\n.sg-btn-secondary {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-btn-secondary:hover:not(:disabled) {\r\n background: var(--sg-surface-glass-elevated);\r\n}\r\n\r\n.sg-btn-ghost {\r\n background: var(--sg-surface-frost);\r\n color: var(--sg-fg);\r\n border-color: var(--sg-border-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-btn-ghost:hover:not(:disabled) {\r\n background: var(--sg-surface-frost-strong);\r\n}\r\n\r\n.sg-btn-outline {\r\n background: transparent;\r\n color: var(--sg-fg);\r\n border-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-btn-outline:hover:not(:disabled) {\r\n background: var(--sg-accent-soft);\r\n border-color: var(--sg-accent);\r\n}\r\n\r\n.sg-btn-danger {\r\n background: var(--sg-danger);\r\n color: #fff;\r\n box-shadow: var(--sg-shadow-soft), inset 0 1px 0 rgba(255, 255, 255, 0.25);\r\n}\r\n\r\n.sg-btn-danger:hover:not(:disabled) {\r\n filter: brightness(1.05);\r\n}\r\n\r\n.sg-btn-link {\r\n background: transparent;\r\n color: var(--sg-accent);\r\n border-color: transparent;\r\n box-shadow: none;\r\n min-height: auto;\r\n padding-inline: 0.25rem;\r\n text-decoration: underline;\r\n text-underline-offset: 0.15em;\r\n}\r\n\r\n.sg-btn-link:hover:not(:disabled) {\r\n color: var(--sg-accent-hover);\r\n}\r\n\r\n.sg-btn-block {\r\n width: 100%;\r\n}\r\n\r\n.sg-btn-rounded-soft {\r\n border-radius: var(--sg-radius-sm);\r\n}\r\n\r\n.sg-btn-rounded-md {\r\n border-radius: var(--sg-radius-md);\r\n}\r\n\r\n.sg-btn-loading {\r\n position: relative;\r\n color: transparent !important;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-btn-loading > * {\r\n opacity: 0;\r\n}\r\n\r\n.sg-btn-spinner {\r\n position: absolute;\r\n inset: 0;\r\n margin: auto;\r\n width: 1.1rem;\r\n height: 1.1rem;\r\n border-radius: 50%;\r\n border: 2px solid color-mix(in srgb, currentColor 25%, transparent);\r\n border-top-color: var(--sg-accent-fg);\r\n opacity: 1 !important;\r\n animation: sg-spin 0.7s linear infinite;\r\n}\r\n\r\n.sg-btn-ghost .sg-btn-spinner,\r\n.sg-btn-outline .sg-btn-spinner,\r\n.sg-btn-secondary .sg-btn-spinner,\r\n.sg-btn-link .sg-btn-spinner {\r\n border-top-color: var(--sg-fg);\r\n}\r\n\r\n.sg-btn-icon-slot {\r\n display: inline-flex;\r\n flex-shrink: 0;\r\n width: 1.1em;\r\n height: 1.1em;\r\n align-items: center;\r\n justify-content: center;\r\n}\r\n\r\n.sg-btn-icon-slot svg {\r\n width: 1.1em;\r\n height: 1.1em;\r\n}\r\n\r\n@keyframes sg-spin {\r\n to {\r\n transform: rotate(360deg);\r\n }\r\n}\r\n\r\n/* —— Input / Field —— */\r\n.sg-field {\r\n display: grid;\r\n gap: var(--sg-space-2);\r\n}\r\n\r\n.sg-field-label {\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-field-hint {\r\n font-size: var(--sg-text-xs);\r\n color: var(--sg-fg-subtle);\r\n}\r\n\r\n.sg-field-error {\r\n font-size: var(--sg-text-xs);\r\n color: var(--sg-danger);\r\n}\r\n\r\n.sg-input {\r\n width: 100%;\r\n min-height: var(--sg-touch-min);\r\n padding: 0.625rem 1rem;\r\n border-radius: var(--sg-radius-sm);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-solid);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-base);\r\n transition: border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-input::placeholder {\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-input:focus {\r\n outline: none;\r\n border-color: var(--sg-accent);\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-input:disabled {\r\n opacity: 0.55;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-input[aria-invalid=\"true\"] {\r\n border-color: var(--sg-danger);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-danger) 22%, transparent);\r\n}\r\n\r\n.sg-input-sm {\r\n min-height: 2.25rem;\r\n padding: 0.4rem 0.75rem;\r\n font-size: var(--sg-text-sm);\r\n}\r\n\r\n.sg-input-lg {\r\n min-height: 3rem;\r\n padding: 0.75rem 1.1rem;\r\n font-size: var(--sg-text-lg);\r\n}\r\n\r\n/* —— Textarea —— */\r\n.sg-textarea {\r\n width: 100%;\r\n min-height: 6.5rem;\r\n padding: 0.75rem 1rem;\r\n border-radius: var(--sg-radius-sm);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-solid);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-base);\r\n line-height: var(--sg-leading-normal);\r\n resize: vertical;\r\n transition: border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-textarea::placeholder {\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-textarea:focus {\r\n outline: none;\r\n border-color: var(--sg-accent);\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-textarea:disabled {\r\n opacity: 0.55;\r\n cursor: not-allowed;\r\n resize: none;\r\n}\r\n\r\n.sg-textarea[aria-invalid=\"true\"] {\r\n border-color: var(--sg-danger);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-danger) 22%, transparent);\r\n}\r\n\r\n.sg-textarea-sm {\r\n min-height: 4.5rem;\r\n padding: 0.5rem 0.75rem;\r\n font-size: var(--sg-text-sm);\r\n}\r\n\r\n.sg-textarea-lg {\r\n min-height: 9rem;\r\n padding: 0.9rem 1.1rem;\r\n font-size: var(--sg-text-lg);\r\n}\r\n\r\n.sg-textarea-auto {\r\n resize: none;\r\n field-sizing: content;\r\n min-height: 3.5rem;\r\n}\r\n\r\n/* —— Checkbox / Radio —— */\r\n.sg-check-root {\r\n display: inline-flex;\r\n align-items: flex-start;\r\n gap: var(--sg-space-3);\r\n cursor: pointer;\r\n user-select: none;\r\n max-width: 100%;\r\n}\r\n\r\n.sg-check-root[data-disabled=\"true\"] {\r\n opacity: 0.55;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-check-control {\r\n position: relative;\r\n flex-shrink: 0;\r\n width: 1.25rem;\r\n height: 1.25rem;\r\n margin-top: 0.15rem;\r\n border-radius: 0.4rem;\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n transition: background var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-check-control[data-kind=\"radio\"] {\r\n border-radius: var(--sg-radius-pill);\r\n}\r\n\r\n.sg-check-input {\r\n position: absolute;\r\n inset: 0;\r\n opacity: 0;\r\n margin: 0;\r\n width: 100%;\r\n height: 100%;\r\n cursor: inherit;\r\n}\r\n\r\n.sg-check-input:focus-visible + .sg-check-face {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.sg-check-face {\r\n width: 100%;\r\n height: 100%;\r\n border-radius: inherit;\r\n display: grid;\r\n place-items: center;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-check-control[data-checked=\"true\"],\r\n.sg-check-control[data-indeterminate=\"true\"] {\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-check-control[data-checked=\"true\"][data-kind=\"radio\"] .sg-check-dot {\r\n opacity: 1;\r\n transform: scale(1);\r\n}\r\n\r\n.sg-check-mark {\r\n width: 0.7rem;\r\n height: 0.7rem;\r\n opacity: 0;\r\n transform: scale(0.6);\r\n transition: opacity var(--sg-duration-fast) var(--sg-ease-out),\r\n transform var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-check-control[data-checked=\"true\"] .sg-check-mark,\r\n.sg-check-control[data-indeterminate=\"true\"] .sg-check-mark {\r\n opacity: 1;\r\n transform: scale(1);\r\n}\r\n\r\n.sg-check-dot {\r\n width: 0.45rem;\r\n height: 0.45rem;\r\n border-radius: 50%;\r\n background: var(--sg-accent-fg);\r\n opacity: 0;\r\n transform: scale(0.5);\r\n transition: opacity var(--sg-duration-fast) var(--sg-ease-out),\r\n transform var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-check-label {\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n color: var(--sg-fg);\r\n line-height: 1.4;\r\n}\r\n\r\n.sg-check-hint {\r\n display: block;\r\n margin-top: 0.15rem;\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-normal);\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-check-group {\r\n display: grid;\r\n gap: var(--sg-space-3);\r\n}\r\n\r\n.sg-check-group-label {\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n color: var(--sg-fg);\r\n margin: 0 0 var(--sg-space-1);\r\n}\r\n\r\n/* —— Tooltip —— */\r\n.sg-tooltip-wrap {\r\n position: relative;\r\n display: inline-flex;\r\n max-width: 100%;\r\n}\r\n\r\n.sg-tooltip {\r\n position: absolute;\r\n z-index: var(--sg-z-dropdown);\r\n left: 50%;\r\n bottom: calc(100% + 0.45rem);\r\n transform: translateX(-50%);\r\n min-width: max-content;\r\n max-width: min(16rem, 70vw);\r\n padding: 0.4rem 0.65rem;\r\n border-radius: var(--sg-radius-sm);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost-strong);\r\n color: var(--sg-fg);\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-medium);\r\n line-height: 1.4;\r\n box-shadow: var(--sg-shadow-elevated);\r\n pointer-events: none;\r\n opacity: 0;\r\n visibility: hidden;\r\n transition: opacity var(--sg-duration-fast) var(--sg-ease-out),\r\n visibility var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-tooltip[data-placement=\"bottom\"] {\r\n bottom: auto;\r\n top: calc(100% + 0.45rem);\r\n}\r\n\r\n.sg-tooltip[data-open=\"true\"] {\r\n opacity: 1;\r\n visibility: visible;\r\n}\r\n\r\n.sg-tooltip-arrow {\r\n position: absolute;\r\n left: 50%;\r\n top: 100%;\r\n width: 0.45rem;\r\n height: 0.45rem;\r\n background: var(--sg-surface-frost-strong);\r\n border-right: var(--sg-border-width) solid var(--sg-border-frost);\r\n border-bottom: var(--sg-border-width) solid var(--sg-border-frost);\r\n transform: translateX(-50%) translateY(-55%) rotate(45deg);\r\n}\r\n\r\n.sg-tooltip[data-placement=\"bottom\"] .sg-tooltip-arrow {\r\n top: auto;\r\n bottom: 100%;\r\n transform: translateX(-50%) translateY(55%) rotate(45deg);\r\n}\r\n\r\n/* —— Badge —— */\r\n.sg-badge {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.25rem;\r\n padding: 0.25rem 0.625rem;\r\n border-radius: var(--sg-radius-pill);\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-medium);\r\n line-height: 1.2;\r\n background: var(--sg-surface-glass);\r\n color: var(--sg-fg-muted);\r\n border: var(--sg-border-width) solid var(--sg-border-glass);\r\n}\r\n\r\n.sg-badge-accent {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n border-color: color-mix(in srgb, var(--sg-accent) 25%, transparent);\r\n}\r\n\r\n.sg-badge-solid {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n border-color: transparent;\r\n}\r\n\r\n.sg-badge-success {\r\n background: color-mix(in srgb, var(--sg-success) 14%, transparent);\r\n color: var(--sg-success);\r\n border-color: color-mix(in srgb, var(--sg-success) 28%, transparent);\r\n}\r\n\r\n.sg-badge-warning {\r\n background: color-mix(in srgb, var(--sg-warning) 14%, transparent);\r\n color: var(--sg-warning);\r\n border-color: color-mix(in srgb, var(--sg-warning) 28%, transparent);\r\n}\r\n\r\n.sg-badge-danger {\r\n background: color-mix(in srgb, var(--sg-danger) 14%, transparent);\r\n color: var(--sg-danger);\r\n border-color: color-mix(in srgb, var(--sg-danger) 28%, transparent);\r\n}\r\n\r\n/* —— Card —— */\r\n.sg-card {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-4);\r\n padding: var(--sg-space-5);\r\n border-radius: var(--sg-radius-lg);\r\n color: var(--sg-fg);\r\n /* Avoid layout containment — it traps absolute select/menus under next cards */\r\n overflow: visible;\r\n}\r\n\r\n.sg-card-sm {\r\n padding: var(--sg-space-3);\r\n gap: var(--sg-space-3);\r\n}\r\n\r\n.sg-card-lg {\r\n padding: var(--sg-space-8);\r\n gap: var(--sg-space-5);\r\n}\r\n\r\n.sg-card-flush {\r\n padding: 0;\r\n}\r\n\r\n.sg-card-hoverable {\r\n transition: transform var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-card-hoverable:hover {\r\n transform: translateY(-2px);\r\n box-shadow: var(--sg-shadow-elevated);\r\n}\r\n\r\n.sg-card-clickable {\r\n cursor: pointer;\r\n}\r\n\r\n.sg-card-header {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-1);\r\n}\r\n\r\n.sg-card-title {\r\n margin: 0;\r\n font-size: var(--sg-text-lg);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n line-height: var(--sg-leading-tight);\r\n}\r\n\r\n.sg-card-description {\r\n margin: 0;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg-muted);\r\n line-height: var(--sg-leading-normal);\r\n}\r\n\r\n.sg-card-content {\r\n flex: 1;\r\n min-width: 0;\r\n}\r\n\r\n.sg-card-footer {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n gap: var(--sg-space-3);\r\n margin-top: auto;\r\n}\r\n\r\n/* —— App shell —— */\r\n.sg-app-bg {\r\n min-height: 100dvh;\r\n background-color: var(--sg-bg);\r\n background-image: var(--sg-bg-image);\r\n /* fixed attachment forces full-viewport repaints — scroll jank */\r\n background-attachment: scroll;\r\n background-repeat: no-repeat;\r\n background-size: cover;\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n line-height: var(--sg-leading-normal);\r\n}\r\n\r\n.sg-shell {\r\n display: grid;\r\n min-height: 100dvh;\r\n grid-template-columns: 1fr;\r\n grid-template-rows: auto 1fr;\r\n grid-template-areas:\r\n \"header\"\r\n \"main\";\r\n}\r\n\r\n.sg-shell-with-sidebar {\r\n grid-template-columns: var(--sg-sidebar-width) 1fr;\r\n grid-template-areas:\r\n \"sidebar header\"\r\n \"sidebar main\";\r\n transition: grid-template-columns var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-shell-with-sidebar[data-collapsed=\"true\"] {\r\n grid-template-columns: var(--sg-sidebar-width-collapsed) 1fr;\r\n}\r\n\r\n.sg-shell-header {\r\n grid-area: header;\r\n position: sticky;\r\n top: 0;\r\n z-index: var(--sg-z-sticky);\r\n padding: var(--sg-space-3) var(--sg-space-4);\r\n}\r\n\r\n.sg-shell-header-inner {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n padding: 0.75rem 1rem;\r\n max-width: var(--sg-content-max);\r\n margin-inline: auto;\r\n width: 100%;\r\n}\r\n\r\n.sg-shell-with-sidebar .sg-shell-header-inner {\r\n max-width: none;\r\n margin-inline: 0;\r\n}\r\n\r\n.sg-shell-sidebar {\r\n grid-area: sidebar;\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-4);\r\n padding: var(--sg-space-5);\r\n border-right: var(--sg-border-width) solid var(--sg-border-glass);\r\n position: sticky;\r\n top: 0;\r\n height: 100dvh;\r\n overflow: auto;\r\n min-width: 0;\r\n}\r\n\r\n.sg-shell-with-sidebar[data-collapsed=\"true\"] .sg-shell-sidebar {\r\n padding-inline: var(--sg-space-2);\r\n align-items: stretch;\r\n}\r\n\r\n.sg-shell-main {\r\n grid-area: main;\r\n width: 100%;\r\n min-width: 0;\r\n}\r\n\r\n.sg-shell-main-inner {\r\n max-width: var(--sg-content-max);\r\n margin-inline: auto;\r\n padding: var(--sg-space-5) var(--sg-space-4) var(--sg-space-12);\r\n width: 100%;\r\n}\r\n\r\n.sg-shell-with-sidebar .sg-shell-main-inner {\r\n max-width: none;\r\n}\r\n\r\n.sg-shell-nav {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-1);\r\n}\r\n\r\n.sg-shell-nav-item {\r\n display: flex;\r\n align-items: center;\r\n gap: var(--sg-space-2);\r\n min-height: 2.5rem;\r\n padding: 0.5rem 0.75rem;\r\n border-radius: var(--sg-radius-sm);\r\n color: var(--sg-fg-muted);\r\n text-decoration: none;\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n border: var(--sg-border-width) solid transparent;\r\n transition: background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out);\r\n position: relative;\r\n}\r\n\r\n.sg-shell-nav-icon,\r\n.sg-shell-nav-glyph {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n flex-shrink: 0;\r\n width: 1.25rem;\r\n height: 1.25rem;\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n}\r\n\r\n/* Glyph is the collapsed-only fallback when no icon is passed. */\r\n.sg-shell-nav-glyph {\r\n display: none;\r\n}\r\n\r\n.sg-shell-nav-label {\r\n min-width: 0;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.sg-shell-with-sidebar[data-collapsed=\"true\"] .sg-shell-nav-item {\r\n justify-content: center;\r\n padding-inline: 0.5rem;\r\n gap: 0;\r\n}\r\n\r\n.sg-shell-with-sidebar[data-collapsed=\"true\"] .sg-shell-nav-label {\r\n position: absolute;\r\n width: 1px;\r\n height: 1px;\r\n padding: 0;\r\n margin: -1px;\r\n overflow: hidden;\r\n clip: rect(0, 0, 0, 0);\r\n white-space: nowrap;\r\n border: 0;\r\n}\r\n\r\n.sg-shell-with-sidebar[data-collapsed=\"true\"] .sg-shell-nav-glyph {\r\n display: inline-flex;\r\n}\r\n\r\n.sg-shell-with-sidebar[data-collapsed=\"true\"] .sg-shell-sidebar-collapse-hide {\r\n position: absolute;\r\n width: 1px;\r\n height: 1px;\r\n padding: 0;\r\n margin: -1px;\r\n overflow: hidden;\r\n clip: rect(0, 0, 0, 0);\r\n white-space: nowrap;\r\n border: 0;\r\n}\r\n\r\n.sg-shell-menu-icon {\r\n display: flex;\r\n flex-direction: column;\r\n justify-content: center;\r\n gap: 3px;\r\n width: 1rem;\r\n}\r\n\r\n.sg-shell-menu-icon span {\r\n display: block;\r\n height: 1.5px;\r\n width: 100%;\r\n border-radius: 1px;\r\n background: currentColor;\r\n}\r\n\r\n.sg-shell-collapse-icon {\r\n font-size: var(--sg-text-base);\r\n line-height: 1;\r\n font-weight: var(--sg-font-weight-semibold);\r\n}\r\n\r\n.sg-shell-mobile-nav {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-4);\r\n}\r\n\r\n.sg-shell-mobile-panel {\r\n max-width: min(20rem, 92vw);\r\n}\r\n\r\n.sg-shell-nav-item:hover {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-shell-nav-item[data-active=\"true\"] {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n border-color: color-mix(in srgb, var(--sg-accent) 20%, transparent);\r\n}\r\n\r\n.sg-blob {\r\n position: absolute;\r\n border-radius: 50%;\r\n /* No filter:blur — soft radial is enough and far cheaper */\r\n filter: none;\r\n opacity: var(--sg-blob-opacity, 0.35);\r\n pointer-events: none;\r\n z-index: var(--sg-z-base);\r\n background: radial-gradient(\r\n circle,\r\n var(--sg-blob-color, var(--sg-accent)) 0%,\r\n transparent 70%\r\n );\r\n}\r\n\r\n/* —— Avatar —— */\r\n.sg-avatar {\r\n position: relative;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n flex-shrink: 0;\r\n overflow: hidden;\r\n border-radius: var(--sg-radius-pill);\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n user-select: none;\r\n border: var(--sg-border-width) solid var(--sg-border-glass);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-avatar-sm {\r\n width: 2rem;\r\n height: 2rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-avatar-md {\r\n width: 2.5rem;\r\n height: 2.5rem;\r\n font-size: var(--sg-text-sm);\r\n}\r\n\r\n.sg-avatar-lg {\r\n width: 3.25rem;\r\n height: 3.25rem;\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-avatar-xl {\r\n width: 4rem;\r\n height: 4rem;\r\n font-size: var(--sg-text-lg);\r\n}\r\n\r\n.sg-avatar-image {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n display: block;\r\n}\r\n\r\n.sg-avatar-fallback {\r\n line-height: 1;\r\n}\r\n\r\n.sg-avatar-group {\r\n display: inline-flex;\r\n align-items: center;\r\n}\r\n\r\n.sg-avatar-group .sg-avatar {\r\n margin-left: -0.45rem;\r\n box-shadow: 0 0 0 2px var(--sg-bg), inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-avatar-group .sg-avatar:first-child {\r\n margin-left: 0;\r\n}\r\n\r\n/* —— Switch —— */\r\n.sg-switch-root {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: var(--sg-space-3);\r\n cursor: pointer;\r\n user-select: none;\r\n}\r\n\r\n.sg-switch-root[data-disabled=\"true\"] {\r\n opacity: 0.55;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-switch {\r\n position: relative;\r\n width: 2.75rem;\r\n height: 1.6rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid var(--sg-border-glass);\r\n background: var(--sg-surface-glass);\r\n backdrop-filter: blur(var(--sg-glass-blur-mobile));\r\n -webkit-backdrop-filter: blur(var(--sg-glass-blur-mobile));\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n transition: background var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n flex-shrink: 0;\r\n padding: 0;\r\n cursor: inherit;\r\n}\r\n\r\n.sg-switch:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.sg-switch[data-checked=\"true\"] {\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n}\r\n\r\n.sg-switch-thumb {\r\n position: absolute;\r\n top: 50%;\r\n left: 0.15rem;\r\n width: 1.2rem;\r\n height: 1.2rem;\r\n border-radius: var(--sg-radius-pill);\r\n background: #fff;\r\n box-shadow: 0 1px 3px rgba(15, 23, 42, 0.25),\r\n inset 0 1px 0 rgba(255, 255, 255, 0.8);\r\n transform: translate(0, -50%);\r\n transition: transform var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-switch[data-checked=\"true\"] .sg-switch-thumb {\r\n transform: translate(1.1rem, -50%);\r\n}\r\n\r\n.sg-switch-label {\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-switch-hint {\r\n display: block;\r\n margin-top: 0.15rem;\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-normal);\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n/* —— Tabs —— */\r\n.sg-tabs {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-4);\r\n width: 100%;\r\n}\r\n\r\n.sg-tabs-list {\r\n display: inline-flex;\r\n flex-wrap: wrap;\r\n gap: var(--sg-space-1);\r\n padding: var(--sg-space-1);\r\n border-radius: var(--sg-radius-pill);\r\n width: fit-content;\r\n max-width: 100%;\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight), var(--sg-shadow-soft);\r\n backdrop-filter: blur(var(--sg-glass-blur)) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(var(--sg-glass-blur))\r\n saturate(var(--sg-glass-saturate));\r\n}\r\n\r\n.sg-tabs-trigger {\r\n appearance: none;\r\n border: var(--sg-border-width) solid transparent;\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n letter-spacing: var(--sg-tracking-tight);\r\n min-height: 2.25rem;\r\n padding: 0.4rem 0.9rem;\r\n border-radius: var(--sg-radius-pill);\r\n cursor: pointer;\r\n transition: background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n/* Hover only for inactive tabs - active keeps solid accent */\r\n.sg-tabs-trigger:hover:not(:disabled):not([data-active=\"true\"]) {\r\n color: var(--sg-fg);\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 14%, transparent);\r\n}\r\n\r\n.sg-tabs-trigger:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.sg-tabs-trigger:disabled {\r\n opacity: 0.5;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-tabs-trigger[data-active=\"true\"] {\r\n color: var(--sg-accent-fg);\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n font-weight: var(--sg-font-weight-semibold);\r\n}\r\n\r\n.sg-tabs-trigger[data-active=\"true\"]:hover:not(:disabled) {\r\n color: var(--sg-accent-fg);\r\n background: var(--sg-accent-hover);\r\n border-color: transparent;\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-tabs-content {\r\n min-width: 0;\r\n outline: none;\r\n}\r\n\r\n.sg-tabs-content[hidden] {\r\n display: none;\r\n}\r\n\r\n/* —— Select (custom glass dropdown — matches Button language) —— */\r\n.sg-select-root {\r\n position: relative;\r\n width: 100%;\r\n}\r\n\r\n/*\r\n * Open menus must stack above following sibling cards (card paint order).\r\n * Without this, absolute menus render under the next .sg-card in the page.\r\n */\r\n.sg-select-root:has([data-open=\"true\"]),\r\n.sg-select-root:has([aria-expanded=\"true\"]),\r\n.sg-combobox-root:has([aria-expanded=\"true\"]),\r\n.sg-multiselect-root:has([data-open=\"true\"]),\r\n.sg-datepicker-root:has([data-open=\"true\"]),\r\n.sg-datepicker-root:has([aria-expanded=\"true\"]) {\r\n z-index: calc(var(--sg-z-dropdown) + 20);\r\n isolation: isolate;\r\n}\r\n\r\n.sg-select-trigger {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n width: 100%;\r\n min-height: var(--sg-touch-min);\r\n padding: 0.625rem 0.9rem 0.625rem 1.15rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n /* Frosted - readable, not see-through */\r\n background: var(--sg-surface-frost);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n line-height: 1.2;\r\n cursor: pointer;\r\n text-align: left;\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight), var(--sg-shadow-soft);\r\n backdrop-filter: blur(18px) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(18px) saturate(var(--sg-glass-saturate));\r\n transition: background var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out),\r\n transform var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-select-trigger:hover:not(:disabled) {\r\n background: var(--sg-surface-frost-strong);\r\n border-color: color-mix(in srgb, var(--sg-accent) 22%, var(--sg-border-frost));\r\n}\r\n\r\n.sg-select-trigger:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.sg-select-trigger:active:not(:disabled) {\r\n transform: translateY(1px);\r\n}\r\n\r\n.sg-select-trigger:disabled {\r\n opacity: 0.5;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-select-trigger[data-open=\"true\"] {\r\n background: var(--sg-surface-frost-strong);\r\n border-color: color-mix(in srgb, var(--sg-accent) 40%, var(--sg-border-frost));\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight), 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-select-trigger[aria-invalid=\"true\"] {\r\n border-color: var(--sg-danger);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 0 0 3px color-mix(in srgb, var(--sg-danger) 22%, transparent);\r\n}\r\n\r\n.sg-select-trigger-sm {\r\n min-height: 2.25rem;\r\n padding: 0.4rem 0.75rem 0.4rem 0.9rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-select-trigger-lg {\r\n min-height: 3rem;\r\n padding: 0.75rem 1rem 0.75rem 1.35rem;\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-select-value {\r\n flex: 1;\r\n min-width: 0;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.sg-select-value[data-placeholder=\"true\"] {\r\n color: var(--sg-fg-muted);\r\n font-weight: var(--sg-font-weight-medium);\r\n}\r\n\r\n.sg-select-chevron {\r\n flex-shrink: 0;\r\n width: 1.35rem;\r\n height: 1.35rem;\r\n border-radius: var(--sg-radius-pill);\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n transition: transform var(--sg-duration-fast) var(--sg-ease-out),\r\n background var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-select-trigger[data-open=\"true\"] .sg-select-chevron {\r\n transform: rotate(180deg);\r\n background: color-mix(in srgb, var(--sg-accent) 22%, transparent);\r\n}\r\n\r\n.sg-select-chevron svg {\r\n width: 0.85rem;\r\n height: 0.85rem;\r\n display: block;\r\n}\r\n\r\n.sg-select-menu {\r\n position: absolute;\r\n z-index: var(--sg-z-dropdown);\r\n top: calc(100% + 0.4rem);\r\n left: 0;\r\n right: 0;\r\n margin: 0;\r\n padding: var(--sg-space-2);\r\n list-style: none;\r\n max-height: min(16rem, 50dvh);\r\n overflow: auto;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n /* Strong frost so options stay readable on busy backgrounds */\r\n background-color: var(--sg-surface-frost-strong);\r\n background-image: linear-gradient(\r\n 180deg,\r\n var(--sg-gloss-highlight) 0%,\r\n transparent 36%\r\n );\r\n box-shadow: var(--sg-shadow-elevated);\r\n backdrop-filter: blur(22px) saturate(1.35);\r\n -webkit-backdrop-filter: blur(22px) saturate(1.35);\r\n animation: sg-select-in var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-select-menu[data-placement=\"top\"] {\r\n top: auto;\r\n bottom: calc(100% + 0.4rem);\r\n animation-name: sg-select-in-top;\r\n}\r\n\r\n/* Portaled panels (v1.2d): fixed coords from JS — ignore absolute anchors */\r\n.sg-select-menu[data-portaled],\r\n.sg-popover-content[data-portaled],\r\n.sg-menu-content[data-portaled] {\r\n position: fixed;\r\n right: auto;\r\n bottom: auto;\r\n /* top/left/width come from inline style */\r\n translate: none;\r\n z-index: calc(var(--sg-z-dropdown) + 40);\r\n}\r\n\r\n.sg-select-menu[data-portaled] {\r\n /* matchWidth sets width; drop stretch-to-parent */\r\n left: auto;\r\n right: auto;\r\n}\r\n\r\n.sg-select-menu[data-portaled][data-placement=\"top\"],\r\n.sg-popover-content[data-portaled][data-placement=\"top\"],\r\n.sg-menu-content[data-portaled][data-placement=\"top\"] {\r\n top: auto; /* still overridden by inline top */\r\n bottom: auto;\r\n animation-name: sg-select-in-top;\r\n}\r\n\r\n.sg-select-option {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n width: 100%;\r\n min-height: 2.5rem;\r\n padding: 0.5rem 0.85rem;\r\n border: var(--sg-border-width) solid transparent;\r\n border-radius: var(--sg-radius-pill);\r\n background: transparent;\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n letter-spacing: var(--sg-tracking-tight);\r\n text-align: left;\r\n cursor: pointer;\r\n transition: background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n/* Highlight only when not selected — avoids muddy mix on active row */\r\n.sg-select-option:hover:not(:disabled):not([data-selected=\"true\"]),\r\n.sg-select-option[data-highlighted=\"true\"]:not([data-selected=\"true\"]) {\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 18%, transparent);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-select-option[data-selected=\"true\"] {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n border-color: transparent;\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n font-weight: var(--sg-font-weight-semibold);\r\n}\r\n\r\n.sg-select-option[data-selected=\"true\"][data-highlighted=\"true\"],\r\n.sg-select-option[data-selected=\"true\"]:hover:not(:disabled) {\r\n background: var(--sg-accent-hover);\r\n color: var(--sg-accent-fg);\r\n filter: none;\r\n}\r\n\r\n.sg-select-option:disabled {\r\n opacity: 0.45;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-select-option:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 1px;\r\n}\r\n\r\n.sg-select-check {\r\n flex-shrink: 0;\r\n width: 1rem;\r\n height: 1rem;\r\n opacity: 0;\r\n}\r\n\r\n.sg-select-option[data-selected=\"true\"] .sg-select-check {\r\n opacity: 1;\r\n}\r\n\r\n/* —— DatePicker (v1.2b) — solid trigger + frost month panel —— */\r\n.sg-datepicker-root {\r\n position: relative;\r\n width: 100%;\r\n}\r\n\r\n.sg-datepicker-trigger {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n width: 100%;\r\n min-height: var(--sg-touch-min);\r\n padding: 0.625rem 0.9rem 0.625rem 1.15rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n /* Solid field for typing contrast — calendar chrome stays frost */\r\n background: var(--sg-surface-solid);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n line-height: 1.2;\r\n cursor: pointer;\r\n text-align: left;\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight), var(--sg-shadow-soft);\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-datepicker-trigger:hover:not(:disabled) {\r\n border-color: color-mix(in srgb, var(--sg-accent) 28%, var(--sg-border-subtle));\r\n}\r\n\r\n.sg-datepicker-trigger:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.sg-datepicker-trigger:disabled {\r\n opacity: 0.5;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-datepicker-trigger[data-open=\"true\"] {\r\n border-color: color-mix(in srgb, var(--sg-accent) 40%, var(--sg-border-subtle));\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-datepicker-trigger[aria-invalid=\"true\"] {\r\n border-color: var(--sg-danger);\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 0 0 3px color-mix(in srgb, var(--sg-danger) 22%, transparent);\r\n}\r\n\r\n.sg-datepicker-trigger-sm {\r\n min-height: 2.25rem;\r\n padding: 0.4rem 0.75rem 0.4rem 0.9rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-datepicker-trigger-lg {\r\n min-height: 3rem;\r\n padding: 0.75rem 1rem 0.75rem 1.35rem;\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-datepicker-value {\r\n flex: 1;\r\n min-width: 0;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.sg-datepicker-value[data-placeholder=\"true\"] {\r\n color: var(--sg-fg-muted);\r\n font-weight: var(--sg-font-weight-medium);\r\n}\r\n\r\n.sg-datepicker-icon {\r\n flex-shrink: 0;\r\n width: 1.35rem;\r\n height: 1.35rem;\r\n border-radius: var(--sg-radius-pill);\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-datepicker-icon svg {\r\n width: 0.9rem;\r\n height: 0.9rem;\r\n display: block;\r\n}\r\n\r\n.sg-datepicker-panel {\r\n position: absolute;\r\n z-index: var(--sg-z-dropdown);\r\n top: calc(100% + 0.4rem);\r\n left: 0;\r\n width: min(19.5rem, 100vw - 1.5rem);\r\n margin: 0;\r\n padding: var(--sg-space-3);\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background-color: var(--sg-surface-frost-strong);\r\n background-image: linear-gradient(\r\n 180deg,\r\n var(--sg-gloss-highlight) 0%,\r\n transparent 36%\r\n );\r\n box-shadow: var(--sg-shadow-elevated);\r\n backdrop-filter: blur(22px) saturate(1.35);\r\n -webkit-backdrop-filter: blur(22px) saturate(1.35);\r\n color: var(--sg-fg);\r\n animation: sg-select-in var(--sg-duration-fast) var(--sg-ease-out);\r\n box-sizing: border-box;\r\n}\r\n\r\n.sg-datepicker-panel[data-placement=\"top\"] {\r\n top: auto;\r\n bottom: calc(100% + 0.4rem);\r\n animation-name: sg-select-in-top;\r\n}\r\n\r\n/* Body portal — fixed coords from useFloatingPortal (Select language) */\r\n.sg-datepicker-panel[data-portaled] {\r\n position: fixed;\r\n top: auto;\r\n left: auto;\r\n bottom: auto;\r\n right: auto;\r\n margin: 0;\r\n}\r\n\r\n/* Inline Calendar (shared by DatePicker panel) */\r\n.sg-calendar {\r\n width: 100%;\r\n min-width: 0;\r\n box-sizing: border-box;\r\n}\r\n\r\n/* TimePicker panel (DatePicker chrome language) */\r\n.sg-timepicker-root {\r\n position: relative;\r\n display: inline-flex;\r\n min-width: 0;\r\n}\r\n\r\n.sg-timepicker-panel {\r\n position: absolute;\r\n z-index: var(--sg-z-dropdown);\r\n top: calc(100% + 0.4rem);\r\n left: 0;\r\n width: min(17.5rem, 100vw - 1.5rem);\r\n margin: 0;\r\n padding: var(--sg-space-3);\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background-color: var(--sg-surface-frost-strong);\r\n background-image: linear-gradient(\r\n 180deg,\r\n var(--sg-gloss-highlight) 0%,\r\n transparent 36%\r\n );\r\n box-shadow: var(--sg-shadow-elevated);\r\n backdrop-filter: blur(22px) saturate(1.35);\r\n -webkit-backdrop-filter: blur(22px) saturate(1.35);\r\n color: var(--sg-fg);\r\n animation: sg-select-in var(--sg-duration-fast) var(--sg-ease-out);\r\n box-sizing: border-box;\r\n display: grid;\r\n gap: var(--sg-space-3);\r\n}\r\n\r\n.sg-timepicker-panel[data-portaled] {\r\n position: fixed;\r\n top: auto;\r\n left: auto;\r\n bottom: auto;\r\n right: auto;\r\n margin: 0;\r\n}\r\n\r\n.sg-timepicker-presets {\r\n display: flex;\r\n flex-wrap: wrap;\r\n gap: 0.4rem;\r\n}\r\n\r\n.sg-timepicker-preset {\r\n appearance: none;\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: color-mix(in srgb, var(--sg-fg) 4%, transparent);\r\n color: var(--sg-fg);\r\n border-radius: 999px;\r\n padding: 0.28rem 0.65rem;\r\n font: inherit;\r\n font-size: var(--sg-text-xs);\r\n font-weight: 600;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-timepicker-preset:hover {\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 28%, var(--sg-border-frost));\r\n}\r\n\r\n.sg-timepicker-preset[data-selected=\"true\"] {\r\n background: var(--sg-accent);\r\n border-color: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-timepicker-actions {\r\n display: flex;\r\n justify-content: flex-end;\r\n}\r\n\r\n.sg-timepicker-apply {\r\n appearance: none;\r\n border: 0;\r\n border-radius: var(--sg-radius-md);\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n font: inherit;\r\n font-size: var(--sg-text-sm);\r\n font-weight: 600;\r\n padding: 0.45rem 0.9rem;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-timepicker-apply:hover {\r\n background: var(--sg-accent-hover);\r\n}\r\n\r\n.sg-timepicker-apply:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.sg-datepicker-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: var(--sg-space-2);\r\n margin-bottom: var(--sg-space-3);\r\n}\r\n\r\n.sg-datepicker-caption {\r\n flex: 1;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 0.25rem;\r\n min-width: 0;\r\n}\r\n\r\n.sg-datepicker-caption-btn {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-height: 2rem;\r\n padding: 0.25rem 0.55rem;\r\n border: var(--sg-border-width) solid transparent;\r\n border-radius: var(--sg-radius-pill);\r\n background: transparent;\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n cursor: pointer;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-datepicker-caption-btn:hover {\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 18%, transparent);\r\n}\r\n\r\n.sg-datepicker-caption-btn:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 1px;\r\n}\r\n\r\n.sg-datepicker-caption-btn-wide {\r\n flex: 1;\r\n}\r\n\r\n/* Month / year choice grid (same frost language as day cells) */\r\n.sg-datepicker-picker-grid {\r\n display: grid;\r\n grid-template-columns: repeat(3, 1fr);\r\n gap: 0.35rem;\r\n min-height: 12.5rem;\r\n}\r\n\r\n.sg-datepicker-picker-cell {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-height: 2.5rem;\r\n padding: 0.35rem 0.4rem;\r\n border: var(--sg-border-width) solid transparent;\r\n border-radius: var(--sg-radius-pill);\r\n background: transparent;\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n cursor: pointer;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-datepicker-picker-cell:hover:not(:disabled):not([data-selected=\"true\"]) {\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 18%, transparent);\r\n}\r\n\r\n.sg-datepicker-picker-cell[data-today=\"true\"]:not([data-selected=\"true\"]) {\r\n border-color: color-mix(in srgb, var(--sg-accent) 45%, transparent);\r\n font-weight: var(--sg-font-weight-semibold);\r\n}\r\n\r\n.sg-datepicker-picker-cell[data-selected=\"true\"] {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n border-color: transparent;\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n font-weight: var(--sg-font-weight-semibold);\r\n}\r\n\r\n.sg-datepicker-picker-cell[data-selected=\"true\"]:hover:not(:disabled) {\r\n background: var(--sg-accent-hover);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-datepicker-picker-cell:disabled {\r\n opacity: 0.35;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-datepicker-picker-cell:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 1px;\r\n}\r\n\r\n.sg-datepicker-nav {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 2rem;\r\n height: 2rem;\r\n border: var(--sg-border-width) solid transparent;\r\n border-radius: var(--sg-radius-pill);\r\n background: transparent;\r\n color: var(--sg-fg);\r\n cursor: pointer;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-datepicker-nav:hover {\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 18%, transparent);\r\n}\r\n\r\n.sg-datepicker-nav:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 1px;\r\n}\r\n\r\n.sg-datepicker-nav svg {\r\n width: 1rem;\r\n height: 1rem;\r\n display: block;\r\n}\r\n\r\n.sg-datepicker-weekdays {\r\n display: grid;\r\n grid-template-columns: repeat(7, 1fr);\r\n gap: 0.15rem;\r\n margin-bottom: 0.25rem;\r\n}\r\n\r\n.sg-datepicker-weekday {\r\n text-align: center;\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: 0.04em;\r\n text-transform: uppercase;\r\n color: var(--sg-fg-muted);\r\n padding: 0.2rem 0;\r\n}\r\n\r\n.sg-datepicker-grid {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 0.15rem;\r\n}\r\n\r\n.sg-datepicker-row {\r\n display: grid;\r\n grid-template-columns: repeat(7, 1fr);\r\n gap: 0.15rem;\r\n}\r\n\r\n.sg-datepicker-day {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-height: 2.15rem;\r\n width: 100%;\r\n padding: 0;\r\n border: var(--sg-border-width) solid transparent;\r\n border-radius: var(--sg-radius-pill);\r\n background: transparent;\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n cursor: pointer;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-datepicker-day:hover:not(:disabled):not([data-selected=\"true\"]) {\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 18%, transparent);\r\n}\r\n\r\n.sg-datepicker-day[data-outside=\"true\"] {\r\n color: var(--sg-fg-muted);\r\n opacity: 0.55;\r\n}\r\n\r\n.sg-datepicker-day[data-today=\"true\"]:not([data-selected=\"true\"]) {\r\n border-color: color-mix(in srgb, var(--sg-accent) 45%, transparent);\r\n font-weight: var(--sg-font-weight-semibold);\r\n}\r\n\r\n.sg-datepicker-day[data-selected=\"true\"] {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n border-color: transparent;\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n font-weight: var(--sg-font-weight-semibold);\r\n opacity: 1;\r\n}\r\n\r\n.sg-datepicker-day[data-selected=\"true\"]:hover:not(:disabled) {\r\n background: var(--sg-accent-hover);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n/* Range selection chrome (v1.5c) */\r\n.sg-datepicker-range-hint {\r\n margin: 0 0 0.45rem;\r\n padding: 0 0.15rem;\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-medium);\r\n color: var(--sg-fg-muted);\r\n text-align: center;\r\n}\r\n\r\n.sg-datepicker-day[data-in-range=\"true\"] {\r\n background: var(--sg-accent-soft);\r\n border-color: transparent;\r\n border-radius: 0;\r\n opacity: 1;\r\n}\r\n\r\n.sg-datepicker-day[data-range-start=\"true\"],\r\n.sg-datepicker-day[data-range-end=\"true\"] {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n border-color: transparent;\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n font-weight: var(--sg-font-weight-semibold);\r\n opacity: 1;\r\n}\r\n\r\n.sg-datepicker-day[data-range-start=\"true\"] {\r\n border-radius: var(--sg-radius-pill) 0 0 var(--sg-radius-pill);\r\n}\r\n\r\n.sg-datepicker-day[data-range-end=\"true\"] {\r\n border-radius: 0 var(--sg-radius-pill) var(--sg-radius-pill) 0;\r\n}\r\n\r\n.sg-datepicker-day[data-range-start=\"true\"][data-range-end=\"true\"] {\r\n border-radius: var(--sg-radius-pill);\r\n}\r\n\r\n.sg-datepicker-day[data-range-start=\"true\"]:hover:not(:disabled),\r\n.sg-datepicker-day[data-range-end=\"true\"]:hover:not(:disabled) {\r\n background: var(--sg-accent-hover);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-datepicker-day:disabled {\r\n opacity: 0.35;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-datepicker-day:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 1px;\r\n}\r\n\r\n.sg-select-check svg {\r\n width: 100%;\r\n height: 100%;\r\n display: block;\r\n}\r\n\r\n/* —— Combobox / MultiSelect (v1.1 Sprint D) —— */\r\n.sg-combobox-trigger {\r\n cursor: text;\r\n gap: var(--sg-space-2);\r\n}\r\n\r\n.sg-combobox-input {\r\n flex: 1;\r\n min-width: 0;\r\n border: 0;\r\n background: transparent;\r\n color: inherit;\r\n font: inherit;\r\n letter-spacing: inherit;\r\n outline: none;\r\n padding: 0;\r\n margin: 0;\r\n width: 100%;\r\n}\r\n\r\n.sg-combobox-input::placeholder {\r\n color: var(--sg-fg-muted);\r\n opacity: 1;\r\n}\r\n\r\n.sg-combobox-input:disabled {\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-combobox-empty {\r\n padding: 0.65rem 0.85rem;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg-muted);\r\n list-style: none;\r\n}\r\n\r\n.sg-multiselect-trigger {\r\n height: auto;\r\n min-height: var(--sg-touch-min);\r\n /* Center placeholder + chips with caret (flex-start left \"Pick tags\" too high) */\r\n align-items: center;\r\n padding-top: 0.4rem;\r\n padding-bottom: 0.4rem;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-multiselect-trigger[data-disabled=\"true\"] {\r\n opacity: 0.45;\r\n cursor: not-allowed;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-multiselect-caret {\r\n flex-shrink: 0;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n margin: 0;\r\n padding: 0.2rem;\r\n border: 0;\r\n background: transparent;\r\n color: inherit;\r\n cursor: pointer;\r\n border-radius: var(--sg-radius-pill);\r\n}\r\n\r\n.sg-multiselect-caret:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 1px;\r\n}\r\n\r\n.sg-multiselect-caret:disabled {\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-multiselect-chips {\r\n display: flex;\r\n flex: 1;\r\n flex-wrap: wrap;\r\n gap: 0.35rem;\r\n align-items: center;\r\n min-width: 0;\r\n min-height: 1.75rem;\r\n text-align: left;\r\n}\r\n\r\n/* Empty-state placeholder — sit on the same optical line as normal select text */\r\n.sg-multiselect-chips .sg-select-value[data-placeholder=\"true\"] {\r\n display: flex;\r\n align-items: center;\r\n min-height: 1.75rem;\r\n line-height: 1.35;\r\n padding: 0;\r\n}\r\n\r\n.sg-multiselect-chip {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.15rem;\r\n max-width: 100%;\r\n /* Extra right padding so X hit area fits comfortably */\r\n padding: 0.2rem 0.15rem 0.2rem 0.6rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid\r\n color-mix(in srgb, var(--sg-accent) 22%, var(--sg-border-frost));\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-medium);\r\n line-height: 1.2;\r\n}\r\n\r\n.sg-multiselect-chip-label {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n padding-right: 0.1rem;\r\n}\r\n\r\n.sg-multiselect-chip-x {\r\n position: relative;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n /* Visible control ~28px — easy finger/mouse target */\r\n min-width: 1.75rem;\r\n min-height: 1.75rem;\r\n width: 1.75rem;\r\n height: 1.75rem;\r\n margin: 0;\r\n padding: 0;\r\n border: 0;\r\n border-radius: 999px;\r\n background: color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n cursor: pointer;\r\n color: var(--sg-fg-muted);\r\n line-height: 1;\r\n font-size: 1.05rem;\r\n font-family: inherit;\r\n flex-shrink: 0;\r\n}\r\n\r\n/* Invisible expanded hit box beyond the visible circle */\r\n.sg-multiselect-chip-x::before {\r\n content: \"\";\r\n position: absolute;\r\n inset: -0.2rem;\r\n border-radius: 999px;\r\n}\r\n\r\n.sg-multiselect-chip-x:hover:not(:disabled) {\r\n color: var(--sg-fg);\r\n background: color-mix(in srgb, var(--sg-fg) 12%, transparent);\r\n}\r\n\r\n.sg-multiselect-chip-x:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 1px;\r\n}\r\n\r\n.sg-multiselect-trigger .sg-select-chevron {\r\n margin-top: 0;\r\n flex-shrink: 0;\r\n}\r\n\r\n@keyframes sg-select-in {\r\n from {\r\n opacity: 0;\r\n transform: translateY(-6px) scale(0.98);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0) scale(1);\r\n }\r\n}\r\n\r\n@keyframes sg-select-in-top {\r\n from {\r\n opacity: 0;\r\n transform: translateY(6px) scale(0.98);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0) scale(1);\r\n }\r\n}\r\n\r\n/* —— Popover + DropdownMenu (overlay family, v1.1 Sprint A) —— */\r\n.sg-popover-root,\r\n.sg-menu-root {\r\n position: relative;\r\n display: inline-flex;\r\n vertical-align: middle;\r\n}\r\n\r\n/* —— ContextMenu target (panel rules live after .sg-menu-content) —— */\r\n/* Avoid display:contents — fixed menu + focus are more reliable with a real box. */\r\n.sg-context-root {\r\n display: block;\r\n}\r\n\r\n.sg-context-target {\r\n display: block;\r\n outline: none;\r\n}\r\n\r\n.sg-context-target:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 2px;\r\n border-radius: var(--sg-radius-md);\r\n}\r\n\r\n.sg-popover-trigger {\r\n display: inline-flex;\r\n}\r\n\r\n.sg-menu-trigger-fallback {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: var(--sg-space-2);\r\n min-height: 2.5rem;\r\n padding: 0 1rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n cursor: pointer;\r\n}\r\n\r\n.sg-menu-trigger-fallback:hover:not(:disabled) {\r\n background: var(--sg-surface-frost-strong);\r\n}\r\n\r\n.sg-menu-trigger-fallback:disabled {\r\n opacity: 0.45;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-popover-content,\r\n.sg-menu-content {\r\n position: absolute;\r\n z-index: var(--sg-z-dropdown);\r\n min-width: 12rem;\r\n max-width: min(20rem, 90vw);\r\n margin: 0;\r\n padding: var(--sg-space-2);\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background-color: var(--sg-surface-frost-strong);\r\n background-image: linear-gradient(\r\n 180deg,\r\n var(--sg-gloss-highlight) 0%,\r\n transparent 36%\r\n );\r\n box-shadow: var(--sg-shadow-elevated);\r\n backdrop-filter: blur(22px) saturate(1.35);\r\n -webkit-backdrop-filter: blur(22px) saturate(1.35);\r\n color: var(--sg-fg);\r\n animation: sg-select-in var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-popover-content {\r\n padding: var(--sg-space-4);\r\n min-width: 14rem;\r\n}\r\n\r\n.sg-popover-content[data-placement=\"bottom\"],\r\n.sg-menu-content[data-placement=\"bottom\"] {\r\n top: calc(100% + 0.4rem);\r\n bottom: auto;\r\n}\r\n\r\n.sg-popover-content[data-placement=\"top\"],\r\n.sg-menu-content[data-placement=\"top\"] {\r\n top: auto;\r\n bottom: calc(100% + 0.4rem);\r\n animation-name: sg-select-in-top;\r\n}\r\n\r\n.sg-popover-content[data-align=\"start\"],\r\n.sg-menu-content[data-align=\"start\"] {\r\n left: 0;\r\n right: auto;\r\n}\r\n\r\n.sg-popover-content[data-align=\"center\"],\r\n.sg-menu-content[data-align=\"center\"] {\r\n left: 50%;\r\n right: auto;\r\n /* `translate` is independent of enter animation `transform` */\r\n translate: -50% 0;\r\n}\r\n\r\n.sg-popover-content[data-align=\"end\"],\r\n.sg-menu-content[data-align=\"end\"] {\r\n left: auto;\r\n right: 0;\r\n}\r\n\r\n.sg-menu-content {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 0.15rem;\r\n max-height: min(18rem, 50dvh);\r\n overflow: auto;\r\n min-width: 11rem;\r\n}\r\n\r\n/* ContextMenu: fixed at pointer — must beat .sg-menu-content absolute rules */\r\n.sg-menu-content.sg-context-menu-content {\r\n position: fixed;\r\n top: auto;\r\n bottom: auto;\r\n right: auto;\r\n translate: none;\r\n z-index: var(--sg-z-dropdown);\r\n}\r\n\r\n.sg-menu-item {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n width: 100%;\r\n min-height: 2.35rem;\r\n padding: 0.45rem 0.85rem;\r\n border: var(--sg-border-width) solid transparent;\r\n border-radius: var(--sg-radius-pill);\r\n background: transparent;\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n letter-spacing: var(--sg-tracking-tight);\r\n text-align: left;\r\n cursor: pointer;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-menu-item:hover:not(:disabled),\r\n.sg-menu-item[data-highlighted=\"true\"]:not(:disabled) {\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 18%, transparent);\r\n}\r\n\r\n.sg-menu-item[data-destructive=\"true\"] {\r\n color: var(--sg-danger, #e11d48);\r\n}\r\n\r\n.sg-menu-item[data-destructive=\"true\"]:hover:not(:disabled),\r\n.sg-menu-item[data-destructive=\"true\"][data-highlighted=\"true\"]:not(:disabled) {\r\n background: color-mix(in srgb, var(--sg-danger, #e11d48) 14%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-danger, #e11d48) 22%, transparent);\r\n color: var(--sg-danger, #e11d48);\r\n}\r\n\r\n.sg-menu-item:disabled {\r\n opacity: 0.45;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-menu-item:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 1px;\r\n}\r\n\r\n.sg-menu-item-label {\r\n flex: 1;\r\n min-width: 0;\r\n}\r\n\r\n.sg-menu-item-shortcut {\r\n flex-shrink: 0;\r\n font-size: var(--sg-text-xs);\r\n color: var(--sg-fg-muted);\r\n font-weight: var(--sg-font-weight-medium);\r\n letter-spacing: 0.02em;\r\n}\r\n\r\n.sg-menu-separator {\r\n height: 1px;\r\n margin: 0.3rem 0.4rem;\r\n background: var(--sg-border-frost);\r\n border: 0;\r\n}\r\n\r\n.sg-menu-label {\r\n padding: 0.4rem 0.85rem 0.25rem;\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: 0.04em;\r\n text-transform: uppercase;\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n/* —— Modal —— */\r\n/* Portaled to body: always viewport-centered, above sticky header */\r\n.sg-modal-root {\r\n position: fixed;\r\n inset: 0;\r\n z-index: var(--sg-z-modal);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n padding: var(--sg-space-4);\r\n isolation: isolate;\r\n box-sizing: border-box;\r\n /* Prefer dynamic viewport so mobile browser chrome does not shift center */\r\n width: 100vw;\r\n min-height: 100vh;\r\n min-height: 100dvh;\r\n}\r\n\r\n.sg-modal-backdrop {\r\n position: absolute;\r\n inset: 0;\r\n border: 0;\r\n padding: 0;\r\n margin: 0;\r\n cursor: pointer;\r\n background: color-mix(in srgb, var(--sg-bg) 35%, rgba(15, 23, 42, 0.45));\r\n backdrop-filter: blur(8px) saturate(1.1);\r\n -webkit-backdrop-filter: blur(8px) saturate(1.1);\r\n animation: sg-fade-in var(--sg-duration-normal) var(--sg-ease-out);\r\n}\r\n\r\n.sg-modal-panel {\r\n position: relative;\r\n z-index: 1;\r\n width: min(100%, 28rem);\r\n max-height: min(90dvh, 40rem);\r\n overflow: auto;\r\n padding: var(--sg-space-6);\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-4);\r\n margin: 0;\r\n flex-shrink: 0;\r\n /* enter animation: motion-recipes.css via data-motion */\r\n}\r\n\r\n.sg-modal-panel-lg {\r\n width: min(100%, 36rem);\r\n}\r\n\r\n.sg-modal-header {\r\n display: flex;\r\n align-items: flex-start;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n}\r\n\r\n.sg-modal-title {\r\n margin: 0;\r\n font-size: var(--sg-text-xl);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n line-height: var(--sg-leading-tight);\r\n}\r\n\r\n.sg-modal-description {\r\n margin: 0.35rem 0 0;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg-muted);\r\n line-height: var(--sg-leading-normal);\r\n}\r\n\r\n.sg-modal-body {\r\n min-width: 0;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-modal-footer {\r\n display: flex;\r\n flex-wrap: wrap;\r\n justify-content: flex-end;\r\n gap: var(--sg-space-3);\r\n margin-top: var(--sg-space-2);\r\n}\r\n\r\n.sg-modal-close {\r\n flex-shrink: 0;\r\n}\r\n\r\n/* —— Toast —— */\r\n.sg-toast-viewport {\r\n position: fixed;\r\n z-index: var(--sg-z-toast);\r\n display: flex;\r\n flex-direction: column;\r\n /* Clear air between cards so soft shadows do not look like one smudge */\r\n gap: var(--sg-space-3);\r\n width: min(100% - 2rem, 24rem);\r\n max-height: calc(100dvh - 2 * var(--sg-space-4));\r\n overflow: auto;\r\n overscroll-behavior: contain;\r\n padding: 0;\r\n margin: 0;\r\n list-style: none;\r\n pointer-events: none;\r\n isolation: isolate;\r\n}\r\n\r\n.sg-toast-viewport-top-right {\r\n top: var(--sg-space-4);\r\n right: var(--sg-space-4);\r\n flex-direction: column;\r\n}\r\n\r\n/* Newest toast sits closest to the corner (stack grows upward) */\r\n.sg-toast-viewport-bottom-right,\r\n.sg-toast-viewport-bottom-center {\r\n flex-direction: column-reverse;\r\n}\r\n\r\n.sg-toast-viewport-bottom-right {\r\n bottom: var(--sg-space-4);\r\n right: var(--sg-space-4);\r\n}\r\n\r\n.sg-toast-viewport-bottom-center {\r\n bottom: var(--sg-space-4);\r\n left: 50%;\r\n transform: translateX(-50%);\r\n}\r\n\r\n/* Dedicated toast surface — elevated glass shadow is too soft/wide for stacks */\r\n.sg-surface-frost-toast {\r\n background: var(--sg-surface-frost-strong);\r\n color: var(--sg-fg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n border-radius: var(--sg-radius-lg);\r\n box-shadow: var(--sg-shadow-toast);\r\n background-image: linear-gradient(\r\n 180deg,\r\n var(--sg-gloss-highlight) 0%,\r\n transparent 48%\r\n );\r\n}\r\n\r\n.sg-toast {\r\n pointer-events: auto;\r\n display: grid;\r\n grid-template-columns: 1fr auto;\r\n gap: var(--sg-space-2) var(--sg-space-3);\r\n align-items: start;\r\n padding: var(--sg-space-4);\r\n width: 100%;\r\n /* Own stacking context so sibling shadows do not composite into a dirty band */\r\n isolation: isolate;\r\n position: relative;\r\n /* enter: motion-recipes.css via data-motion */\r\n}\r\n\r\n/* leave recipes live in motion-recipes.css (data-leaving + data-motion) */\r\n\r\n.sg-toast-title {\r\n margin: 0;\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n}\r\n\r\n.sg-toast-description {\r\n margin: 0.2rem 0 0;\r\n font-size: var(--sg-text-xs);\r\n color: var(--sg-fg-muted);\r\n line-height: var(--sg-leading-normal);\r\n grid-column: 1;\r\n}\r\n\r\n.sg-toast-close {\r\n grid-column: 2;\r\n grid-row: 1;\r\n}\r\n\r\n.sg-toast-success {\r\n border-color: color-mix(in srgb, var(--sg-success) 35%, var(--sg-border-glass));\r\n}\r\n\r\n.sg-toast-warning {\r\n border-color: color-mix(in srgb, var(--sg-warning) 35%, var(--sg-border-glass));\r\n}\r\n\r\n.sg-toast-danger {\r\n border-color: color-mix(in srgb, var(--sg-danger) 35%, var(--sg-border-glass));\r\n}\r\n\r\n@keyframes sg-fade-in {\r\n from {\r\n opacity: 0;\r\n }\r\n to {\r\n opacity: 1;\r\n }\r\n}\r\n\r\n@keyframes sg-scale-in {\r\n from {\r\n opacity: 0;\r\n transform: translateY(8px) scale(0.97);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0) scale(1);\r\n }\r\n}\r\n\r\n@keyframes sg-toast-in {\r\n from {\r\n opacity: 0;\r\n transform: translateY(10px);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n}\r\n\r\n@keyframes sg-toast-out {\r\n from {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translateY(8px);\r\n }\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .sg-modal-backdrop,\r\n .sg-modal-panel,\r\n .sg-toast,\r\n .sg-toast[data-leaving=\"true\"],\r\n .sg-select-menu,\r\n .sg-select-menu[data-placement=\"top\"] {\r\n animation: none;\r\n }\r\n}\r\n\r\n/* Mobile: sidebar unmounted (JS); collapse rail only applies on desktop mount. */\r\n@media (max-width: 899px) {\r\n .sg-shell-with-sidebar {\r\n grid-template-columns: 1fr;\r\n grid-template-areas:\r\n \"header\"\r\n \"main\";\r\n }\r\n\r\n .sg-shell-with-sidebar[data-collapsed=\"true\"] {\r\n grid-template-columns: 1fr;\r\n }\r\n}\r\n\r\n/* —— PageHeader —— */\r\n.sg-page-header {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-3);\r\n margin-bottom: var(--sg-space-5);\r\n box-sizing: border-box;\r\n /* plain = flush, no chrome */\r\n padding: 0;\r\n border: var(--sg-border-width) solid transparent;\r\n border-radius: 0;\r\n background: transparent;\r\n box-shadow: none;\r\n transition:\r\n background var(--sg-duration-normal) var(--sg-ease-out),\r\n border-color var(--sg-duration-normal) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-normal) var(--sg-ease-out),\r\n padding var(--sg-duration-fast) var(--sg-ease-out),\r\n border-radius var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n/* soft = frost tray (distinct from solid page bg) */\r\n.sg-page-header[data-look=\"soft\"] {\r\n padding: var(--sg-space-4) var(--sg-space-5);\r\n border-radius: var(--sg-radius-lg);\r\n background: var(--sg-surface-frost);\r\n border-color: var(--sg-border-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n/* solid = opaque panel, hard edge, subtle lift */\r\n.sg-page-header[data-look=\"solid\"] {\r\n padding: var(--sg-space-4) var(--sg-space-5);\r\n border-radius: var(--sg-radius-lg);\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow:\r\n 0 1px 0 color-mix(in srgb, var(--sg-fg) 5%, transparent),\r\n 0 10px 28px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n}\r\n\r\n/* glass = translucent + blur + accent-tinted rim (needs colorful stage behind) */\r\n.sg-page-header[data-look=\"glass\"] {\r\n padding: var(--sg-space-4) var(--sg-space-5);\r\n border-radius: var(--sg-radius-lg);\r\n background: var(--sg-surface-frost-strong, var(--sg-surface-frost));\r\n border-color: color-mix(in srgb, var(--sg-border-frost) 70%, var(--sg-accent) 22%);\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 12px 32px color-mix(in srgb, var(--sg-accent) 12%, transparent);\r\n}\r\n\r\n@media (prefers-reduced-transparency: no-preference) {\r\n .sg-page-header[data-look=\"glass\"] {\r\n background: color-mix(in srgb, var(--sg-surface-glass) 82%, transparent);\r\n backdrop-filter: blur(var(--sg-glass-blur)) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(var(--sg-glass-blur))\r\n saturate(var(--sg-glass-saturate));\r\n }\r\n}\r\n\r\n.sg-page-header-crumbs {\r\n margin: 0;\r\n}\r\n\r\n.sg-page-header-row {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: flex-start;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3) var(--sg-space-4);\r\n}\r\n\r\n.sg-page-header-text {\r\n min-width: 0;\r\n flex: 1 1 12rem;\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-1);\r\n}\r\n\r\n.sg-page-header-title {\r\n margin: 0;\r\n font-size: var(--sg-text-2xl);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n line-height: var(--sg-leading-tight);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-page-header[data-size=\"sm\"] .sg-page-header-title {\r\n font-size: var(--sg-text-xl);\r\n}\r\n\r\n.sg-page-header[data-size=\"lg\"] .sg-page-header-title {\r\n font-size: var(--sg-text-3xl);\r\n}\r\n\r\n.sg-page-header-description {\r\n margin: 0;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg-muted);\r\n max-width: 60ch;\r\n line-height: var(--sg-leading-normal);\r\n}\r\n\r\n.sg-page-header[data-size=\"lg\"] .sg-page-header-description {\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-page-header-actions {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n gap: var(--sg-space-2);\r\n flex-shrink: 0;\r\n}\r\n\r\n/* Compact AppShell embed for playground / docs demos */\r\n.sg-shell.sg-shell-demo {\r\n min-height: 17rem;\r\n height: 17rem;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n overflow: hidden;\r\n background: var(--sg-surface-solid);\r\n}\r\n\r\n/* Full mini-product embed (inventory + settings).\r\n Contain layout — override base .sg-shell min-height: 100dvh. */\r\n.sg-shell.sg-shell-demo-app {\r\n min-height: 0;\r\n height: min(34rem, 70vh);\r\n max-height: 70vh;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n overflow: hidden;\r\n background: var(--sg-surface-solid);\r\n}\r\n\r\n.sg-shell.sg-shell-demo .sg-shell-sidebar,\r\n.sg-shell.sg-shell-demo-app .sg-shell-sidebar {\r\n height: 100%;\r\n position: relative;\r\n top: auto;\r\n}\r\n\r\n.sg-shell.sg-shell-demo .sg-shell-header,\r\n.sg-shell.sg-shell-demo-app .sg-shell-header {\r\n position: relative;\r\n top: auto;\r\n padding: var(--sg-space-2) var(--sg-space-3);\r\n}\r\n\r\n.sg-shell.sg-shell-demo .sg-shell-header-inner,\r\n.sg-shell.sg-shell-demo-app .sg-shell-header-inner {\r\n padding: 0.45rem 0.65rem;\r\n}\r\n\r\n.sg-shell.sg-shell-demo .sg-shell-main-inner {\r\n padding: var(--sg-space-4);\r\n}\r\n\r\n.sg-shell.sg-shell-demo-app .sg-shell-main {\r\n min-height: 0;\r\n overflow: auto;\r\n}\r\n\r\n.sg-shell.sg-shell-demo-app .sg-shell-main-inner {\r\n padding: var(--sg-space-4);\r\n display: grid;\r\n gap: 0.85rem;\r\n align-content: start;\r\n}\r\n\r\n/* Stronger collapse read: wider gap + rail tint */\r\n.sg-shell-with-sidebar {\r\n transition: grid-template-columns var(--sg-duration-normal) var(--sg-ease-out);\r\n}\r\n\r\n.sg-shell-with-sidebar[data-collapsed=\"true\"] .sg-shell-sidebar {\r\n background: color-mix(in srgb, var(--sg-accent-soft) 55%, var(--sg-surface-frost));\r\n border-right-color: color-mix(in srgb, var(--sg-accent) 22%, var(--sg-border-glass));\r\n}\r\n\r\n.sg-shell-with-sidebar[data-collapsed=\"true\"] .sg-shell-nav-item {\r\n min-height: 2.75rem;\r\n border-radius: var(--sg-radius-sm);\r\n}\r\n\r\n.sg-shell-with-sidebar[data-collapsed=\"true\"] .sg-shell-nav-glyph,\r\n.sg-shell-with-sidebar[data-collapsed=\"true\"] .sg-shell-nav-icon {\r\n width: 1.5rem;\r\n height: 1.5rem;\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n/* —— SettingsSection (v1.5b) —— */\r\n.sg-settings-section {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-4);\r\n width: 100%;\r\n padding: var(--sg-space-5);\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n box-sizing: border-box;\r\n}\r\n\r\n.sg-settings-section[data-look=\"solid\"] {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow:\r\n 0 1px 0 color-mix(in srgb, var(--sg-fg) 5%, transparent),\r\n 0 8px 24px color-mix(in srgb, var(--sg-fg) 5%, transparent);\r\n}\r\n\r\n.sg-settings-section[data-look=\"glass\"] {\r\n background: var(--sg-surface-frost-strong, var(--sg-surface-frost));\r\n border-color: color-mix(in srgb, var(--sg-border-frost) 70%, var(--sg-accent) 20%);\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 12px 28px color-mix(in srgb, var(--sg-accent) 10%, transparent);\r\n}\r\n\r\n@media (prefers-reduced-transparency: no-preference) {\r\n .sg-settings-section[data-look=\"glass\"] {\r\n background: color-mix(in srgb, var(--sg-surface-glass) 84%, transparent);\r\n backdrop-filter: blur(var(--sg-glass-blur)) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(var(--sg-glass-blur))\r\n saturate(var(--sg-glass-saturate));\r\n }\r\n}\r\n\r\n.sg-settings-section[data-look=\"plain\"] {\r\n padding: 0;\r\n border: none;\r\n background: transparent;\r\n box-shadow: none;\r\n border-radius: 0;\r\n}\r\n\r\n.sg-settings-section[data-density=\"compact\"] {\r\n gap: var(--sg-space-3);\r\n padding: var(--sg-space-4);\r\n}\r\n\r\n.sg-settings-section[data-look=\"plain\"][data-density=\"compact\"] {\r\n padding: 0;\r\n}\r\n\r\n.sg-settings-section-header {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: flex-start;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n}\r\n\r\n.sg-settings-section-heading {\r\n min-width: 0;\r\n flex: 1 1 12rem;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 0.25rem;\r\n}\r\n\r\n.sg-settings-section-title {\r\n margin: 0;\r\n font-size: var(--sg-text-lg);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n line-height: var(--sg-leading-tight);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-settings-section-description {\r\n margin: 0;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg-muted);\r\n line-height: var(--sg-leading-normal);\r\n max-width: 56ch;\r\n}\r\n\r\n.sg-settings-section-actions {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n gap: var(--sg-space-2);\r\n flex-shrink: 0;\r\n}\r\n\r\n.sg-settings-section-body {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-4);\r\n min-width: 0;\r\n}\r\n\r\n.sg-settings-section[data-density=\"compact\"] .sg-settings-section-body {\r\n gap: var(--sg-space-3);\r\n}\r\n\r\n/* —— CommandPalette (v1.5b minimal) —— */\r\n.sg-command-root {\r\n position: fixed;\r\n inset: 0;\r\n z-index: var(--sg-z-modal);\r\n display: flex;\r\n align-items: flex-start;\r\n justify-content: center;\r\n padding: min(18vh, 7rem) var(--sg-space-4) var(--sg-space-6);\r\n box-sizing: border-box;\r\n}\r\n\r\n.sg-command-backdrop {\r\n position: absolute;\r\n inset: 0;\r\n margin: 0;\r\n padding: 0;\r\n border: 0;\r\n cursor: pointer;\r\n background: color-mix(in srgb, var(--sg-fg) 28%, transparent);\r\n backdrop-filter: blur(4px);\r\n -webkit-backdrop-filter: blur(4px);\r\n}\r\n\r\n.sg-command-panel {\r\n position: relative;\r\n z-index: 1;\r\n width: min(34rem, 100%);\r\n max-height: min(28rem, 70vh);\r\n display: flex;\r\n flex-direction: column;\r\n overflow: hidden;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid\r\n color-mix(in srgb, var(--sg-border-frost) 70%, var(--sg-accent) 18%);\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 24px 64px color-mix(in srgb, var(--sg-fg) 18%, transparent);\r\n box-sizing: border-box;\r\n}\r\n\r\n.sg-command-sr-only {\r\n position: absolute;\r\n width: 1px;\r\n height: 1px;\r\n padding: 0;\r\n margin: -1px;\r\n overflow: hidden;\r\n clip: rect(0, 0, 0, 0);\r\n white-space: nowrap;\r\n border: 0;\r\n}\r\n\r\n.sg-command-search {\r\n display: flex;\r\n align-items: center;\r\n gap: var(--sg-space-2);\r\n padding: 0.85rem 1rem;\r\n border-bottom: var(--sg-border-width) solid var(--sg-border-frost);\r\n flex-shrink: 0;\r\n}\r\n\r\n.sg-command-search-icon {\r\n flex-shrink: 0;\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-lg);\r\n line-height: 1;\r\n opacity: 0.85;\r\n}\r\n\r\n.sg-command-input {\r\n flex: 1 1 auto;\r\n min-width: 0;\r\n border: 0;\r\n outline: none;\r\n background: transparent;\r\n color: var(--sg-fg);\r\n font: inherit;\r\n font-size: var(--sg-text-base);\r\n font-weight: var(--sg-font-weight-medium);\r\n line-height: 1.4;\r\n padding: 0.15rem 0;\r\n}\r\n\r\n.sg-command-input::placeholder {\r\n color: var(--sg-fg-muted);\r\n font-weight: var(--sg-font-weight-normal);\r\n}\r\n\r\n.sg-command-kbd {\r\n flex-shrink: 0;\r\n display: inline-flex;\r\n align-items: center;\r\n min-height: 1.35rem;\r\n padding: 0.1rem 0.4rem;\r\n border-radius: var(--sg-radius-xs);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: color-mix(in srgb, var(--sg-surface-solid) 70%, transparent);\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-xs);\r\n font-family: inherit;\r\n text-transform: lowercase;\r\n}\r\n\r\n.sg-command-list {\r\n overflow: auto;\r\n padding: 0.45rem;\r\n flex: 1 1 auto;\r\n min-height: 0;\r\n}\r\n\r\n.sg-command-empty {\r\n padding: 1.5rem 1rem;\r\n text-align: center;\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-sm);\r\n}\r\n\r\n.sg-command-group {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 0.15rem;\r\n}\r\n\r\n.sg-command-group + .sg-command-group {\r\n margin-top: 0.35rem;\r\n}\r\n\r\n.sg-command-group-label {\r\n padding: 0.4rem 0.65rem 0.25rem;\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: 0.04em;\r\n text-transform: uppercase;\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-command-item {\r\n display: flex;\r\n align-items: center;\r\n gap: 0.65rem;\r\n width: 100%;\r\n min-height: 2.6rem;\r\n padding: 0.5rem 0.65rem;\r\n border-radius: var(--sg-radius-sm);\r\n border: var(--sg-border-width) solid transparent;\r\n cursor: pointer;\r\n box-sizing: border-box;\r\n color: var(--sg-fg);\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-command-item-active,\r\n.sg-command-item[data-active=\"true\"] {\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 22%, transparent);\r\n}\r\n\r\n.sg-command-item-disabled {\r\n opacity: 0.45;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-command-item-icon {\r\n flex-shrink: 0;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 1.35rem;\r\n height: 1.35rem;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-command-item-active .sg-command-item-icon {\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-command-item-text {\r\n min-width: 0;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 0.1rem;\r\n}\r\n\r\n.sg-command-item-label {\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n line-height: 1.3;\r\n}\r\n\r\n.sg-command-item-desc {\r\n font-size: var(--sg-text-xs);\r\n color: var(--sg-fg-muted);\r\n line-height: 1.3;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .sg-command-root,\r\n .sg-command-panel,\r\n .sg-command-item {\r\n transition: none;\r\n }\r\n}\r\n\r\n\r\n/* —— Spinner (standalone) —— */\r\n.sg-spinner {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-spinner-visual {\r\n display: block;\r\n width: 1.25rem;\r\n height: 1.25rem;\r\n border-radius: 50%;\r\n border: 2px solid color-mix(in srgb, currentColor 22%, transparent);\r\n border-top-color: currentColor;\r\n animation: sg-spin 0.7s linear infinite;\r\n}\r\n\r\n.sg-spinner-sm .sg-spinner-visual {\r\n width: 0.9rem;\r\n height: 0.9rem;\r\n border-width: 1.5px;\r\n}\r\n\r\n.sg-spinner-lg .sg-spinner-visual {\r\n width: 1.75rem;\r\n height: 1.75rem;\r\n border-width: 2.5px;\r\n}\r\n\r\n/* —— Separator —— */\r\n.sg-separator {\r\n display: block;\r\n width: 100%;\r\n height: 1px;\r\n border: 0;\r\n margin: var(--sg-space-4) 0;\r\n background: linear-gradient(\r\n 90deg,\r\n transparent,\r\n var(--sg-border-subtle) 12%,\r\n var(--sg-border-subtle) 88%,\r\n transparent\r\n );\r\n flex-shrink: 0;\r\n}\r\n\r\n.sg-separator-vertical {\r\n display: inline-block;\r\n width: 1px;\r\n height: 1.25rem;\r\n margin: 0 var(--sg-space-3);\r\n align-self: center;\r\n background: linear-gradient(\r\n 180deg,\r\n transparent,\r\n var(--sg-border-subtle) 12%,\r\n var(--sg-border-subtle) 88%,\r\n transparent\r\n );\r\n}\r\n\r\n/* —— Skeleton —— */\r\n.sg-skeleton {\r\n display: block;\r\n width: 100%;\r\n height: 0.875rem;\r\n background: linear-gradient(\r\n 90deg,\r\n color-mix(in srgb, var(--sg-fg-muted) 10%, var(--sg-surface-solid)) 0%,\r\n color-mix(in srgb, var(--sg-fg-muted) 18%, var(--sg-surface-solid)) 45%,\r\n color-mix(in srgb, var(--sg-fg-muted) 10%, var(--sg-surface-solid)) 100%\r\n );\r\n background-size: 200% 100%;\r\n animation: sg-skeleton-shimmer 1.4s ease-in-out infinite;\r\n}\r\n\r\n.sg-skeleton-circle {\r\n border-radius: 50%;\r\n width: 2.5rem;\r\n height: 2.5rem;\r\n}\r\n\r\n.sg-skeleton-rounded-sm {\r\n border-radius: var(--sg-radius-xs);\r\n}\r\n\r\n.sg-skeleton-rounded-md {\r\n border-radius: var(--sg-radius-sm);\r\n}\r\n\r\n.sg-skeleton-rounded-lg {\r\n border-radius: var(--sg-radius-md);\r\n}\r\n\r\n.sg-skeleton-rounded-full {\r\n border-radius: var(--sg-radius-pill);\r\n}\r\n\r\n@keyframes sg-skeleton-shimmer {\r\n 0% {\r\n background-position: 100% 0;\r\n }\r\n 100% {\r\n background-position: -100% 0;\r\n }\r\n}\r\n\r\n/* —— Alert / Callout —— */\r\n.sg-alert {\r\n display: flex;\r\n align-items: flex-start;\r\n gap: var(--sg-space-3);\r\n padding: var(--sg-space-3) var(--sg-space-4);\r\n border-radius: var(--sg-radius-md);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n color: var(--sg-fg);\r\n font-size: var(--sg-text-sm);\r\n line-height: var(--sg-leading-normal);\r\n}\r\n\r\n.sg-alert-marker {\r\n flex-shrink: 0;\r\n width: 0.5rem;\r\n height: 0.5rem;\r\n margin-top: 0.4rem;\r\n border-radius: 50%;\r\n background: currentColor;\r\n}\r\n\r\n.sg-alert-icon {\r\n flex-shrink: 0;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 1.25rem;\r\n height: 1.25rem;\r\n margin-top: 0.1rem;\r\n}\r\n\r\n.sg-alert-body {\r\n min-width: 0;\r\n flex: 1;\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-1);\r\n}\r\n\r\n.sg-alert-title {\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n}\r\n\r\n.sg-alert-content {\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-alert-info {\r\n border-color: color-mix(in srgb, var(--sg-accent) 35%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-accent-soft) 70%, var(--sg-surface-frost));\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-alert-info .sg-alert-marker,\r\n.sg-alert-info .sg-alert-icon {\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-alert-success {\r\n border-color: color-mix(in srgb, var(--sg-success) 40%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-success) 12%, var(--sg-surface-frost));\r\n}\r\n\r\n.sg-alert-success .sg-alert-marker,\r\n.sg-alert-success .sg-alert-icon {\r\n color: var(--sg-success);\r\n}\r\n\r\n.sg-alert-warning {\r\n border-color: color-mix(in srgb, var(--sg-warning) 40%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-warning) 12%, var(--sg-surface-frost));\r\n}\r\n\r\n.sg-alert-warning .sg-alert-marker,\r\n.sg-alert-warning .sg-alert-icon {\r\n color: var(--sg-warning);\r\n}\r\n\r\n.sg-alert-danger {\r\n border-color: color-mix(in srgb, var(--sg-danger) 40%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-danger) 12%, var(--sg-surface-frost));\r\n}\r\n\r\n.sg-alert-danger .sg-alert-marker,\r\n.sg-alert-danger .sg-alert-icon {\r\n color: var(--sg-danger);\r\n}\r\n\r\n/* —— Progress (linear) —— */\r\n.sg-progress {\r\n display: block;\r\n width: 100%;\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-progress-track {\r\n position: relative;\r\n width: 100%;\r\n height: 0.5rem;\r\n overflow: hidden;\r\n border-radius: var(--sg-radius-pill);\r\n background: color-mix(in srgb, var(--sg-fg-muted) 12%, var(--sg-surface-solid));\r\n box-shadow: inset 0 1px 0 color-mix(in srgb, var(--sg-gloss-highlight) 55%, transparent);\r\n}\r\n\r\n.sg-progress-sm .sg-progress-track {\r\n height: 0.3rem;\r\n}\r\n\r\n.sg-progress-lg .sg-progress-track {\r\n height: 0.7rem;\r\n}\r\n\r\n.sg-progress-look-solid .sg-progress-track {\r\n background: var(--sg-surface-solid);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-progress-look-glass .sg-progress-track {\r\n background: var(--sg-surface-frost);\r\n border: var(--sg-border-width) solid var(--sg-border-glass);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-progress-look-accent .sg-progress-track {\r\n height: 0.6rem;\r\n background: color-mix(in srgb, var(--sg-accent) 14%, var(--sg-surface-solid));\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, var(--sg-gloss-highlight) 50%, transparent),\r\n 0 0 10px color-mix(in srgb, currentColor 12%, transparent);\r\n}\r\n\r\n.sg-progress-look-accent .sg-progress-fill {\r\n box-shadow:\r\n 0 0 0 1px color-mix(in srgb, currentColor 25%, transparent),\r\n 0 0 12px color-mix(in srgb, currentColor 40%, transparent);\r\n}\r\n\r\n.sg-progress-look-striped .sg-progress-fill {\r\n background-image:\r\n linear-gradient(\r\n 45deg,\r\n color-mix(in srgb, #fff 18%, transparent) 25%,\r\n transparent 25%,\r\n transparent 50%,\r\n color-mix(in srgb, #fff 18%, transparent) 50%,\r\n color-mix(in srgb, #fff 18%, transparent) 75%,\r\n transparent 75%,\r\n transparent\r\n ),\r\n linear-gradient(90deg, color-mix(in srgb, currentColor 88%, transparent), currentColor);\r\n background-size: 0.75rem 0.75rem, 100% 100%;\r\n animation: sg-progress-stripes 0.9s linear infinite;\r\n}\r\n\r\n.sg-progress-look-striped.sg-progress-indeterminate .sg-progress-fill {\r\n animation:\r\n sg-progress-indeterminate 1.2s ease-in-out infinite,\r\n sg-progress-stripes 0.9s linear infinite;\r\n}\r\n\r\n@keyframes sg-progress-stripes {\r\n 0% {\r\n background-position: 0 0, 0 0;\r\n }\r\n 100% {\r\n background-position: 0.75rem 0, 0 0;\r\n }\r\n}\r\n\r\n/* Progress semantic fill */\r\n.sg-progress-success {\r\n color: var(--sg-success);\r\n}\r\n\r\n.sg-progress-warning {\r\n color: var(--sg-warning);\r\n}\r\n\r\n.sg-progress-danger {\r\n color: var(--sg-danger);\r\n}\r\n\r\n.sg-progress-muted {\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-progress-fill {\r\n height: 100%;\r\n width: 0%;\r\n border-radius: inherit;\r\n background: linear-gradient(\r\n 90deg,\r\n color-mix(in srgb, currentColor 88%, transparent),\r\n currentColor\r\n );\r\n box-shadow: 0 0 0 1px color-mix(in srgb, currentColor 18%, transparent);\r\n transition: width 180ms ease;\r\n}\r\n\r\n.sg-progress-indeterminate .sg-progress-fill {\r\n width: 36%;\r\n transition: none;\r\n animation: sg-progress-indeterminate 1.2s ease-in-out infinite;\r\n}\r\n\r\n@keyframes sg-progress-indeterminate {\r\n 0% {\r\n transform: translateX(-120%);\r\n }\r\n 100% {\r\n transform: translateX(320%);\r\n }\r\n}\r\n\r\n/* —— StatusDot —— */\r\n.sg-status-dot {\r\n --sg-status-dot-color: var(--sg-success);\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 0.625rem;\r\n height: 0.625rem;\r\n flex-shrink: 0;\r\n vertical-align: middle;\r\n color: var(--sg-status-dot-color);\r\n}\r\n\r\n.sg-status-dot-sm {\r\n width: 0.45rem;\r\n height: 0.45rem;\r\n}\r\n\r\n.sg-status-dot-lg {\r\n width: 0.8rem;\r\n height: 0.8rem;\r\n}\r\n\r\n.sg-status-dot-core {\r\n display: block;\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 50%;\r\n background: currentColor;\r\n box-shadow:\r\n 0 0 0 1px color-mix(in srgb, var(--sg-surface-solid) 70%, transparent),\r\n 0 0 0 2px color-mix(in srgb, currentColor 22%, transparent);\r\n}\r\n\r\n.sg-status-dot-online {\r\n --sg-status-dot-color: var(--sg-success);\r\n}\r\n\r\n.sg-status-dot-busy {\r\n --sg-status-dot-color: var(--sg-danger);\r\n}\r\n\r\n.sg-status-dot-offline {\r\n --sg-status-dot-color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-status-dot-away {\r\n --sg-status-dot-color: var(--sg-warning);\r\n}\r\n\r\n/* StatusDot looks */\r\n.sg-status-dot-look-solid .sg-status-dot-core {\r\n box-shadow: none;\r\n}\r\n\r\n.sg-status-dot-look-outline .sg-status-dot-core {\r\n background: transparent;\r\n box-shadow: none;\r\n border: 2px solid currentColor;\r\n}\r\n\r\n.sg-status-dot-look-glow .sg-status-dot-core {\r\n box-shadow:\r\n 0 0 0 1px color-mix(in srgb, var(--sg-surface-solid) 55%, transparent),\r\n 0 0 0 3px color-mix(in srgb, currentColor 28%, transparent),\r\n 0 0 10px color-mix(in srgb, currentColor 55%, transparent);\r\n}\r\n\r\n/* custom color: set --sg-status-dot-color via inline style */\r\n\r\n.sg-status-dot-pulse {\r\n position: relative;\r\n}\r\n\r\n.sg-status-dot-pulse::before {\r\n content: \"\";\r\n position: absolute;\r\n inset: -2px;\r\n border-radius: 50%;\r\n border: 1.5px solid currentColor;\r\n opacity: 0.55;\r\n animation: sg-status-dot-ping 1.6s ease-out infinite;\r\n}\r\n\r\n@keyframes sg-status-dot-ping {\r\n 0% {\r\n transform: scale(0.85);\r\n opacity: 0.55;\r\n }\r\n 70% {\r\n transform: scale(1.55);\r\n opacity: 0;\r\n }\r\n 100% {\r\n transform: scale(1.55);\r\n opacity: 0;\r\n }\r\n}\r\n\r\n/* —— Slider —— */\r\n.sg-slider {\r\n --sg-slider-pct: 0%;\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n width: 100%;\r\n min-height: var(--sg-touch-min);\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-slider[data-disabled=\"true\"] {\r\n opacity: 0.55;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-slider-label-row {\r\n display: flex;\r\n align-items: baseline;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n}\r\n\r\n.sg-slider-value {\r\n font-size: var(--sg-text-sm);\r\n font-variant-numeric: tabular-nums;\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-slider-input {\r\n -webkit-appearance: none;\r\n appearance: none;\r\n width: 100%;\r\n height: 0.45rem;\r\n margin: 0;\r\n border-radius: var(--sg-radius-pill);\r\n background: linear-gradient(\r\n 90deg,\r\n currentColor 0%,\r\n currentColor var(--sg-slider-pct),\r\n color-mix(in srgb, var(--sg-fg-muted) 14%, var(--sg-surface-solid))\r\n var(--sg-slider-pct),\r\n color-mix(in srgb, var(--sg-fg-muted) 14%, var(--sg-surface-solid)) 100%\r\n );\r\n box-shadow: inset 0 1px 0 color-mix(in srgb, var(--sg-gloss-highlight) 50%, transparent);\r\n cursor: pointer;\r\n}\r\n\r\n.sg-slider-sm .sg-slider-input {\r\n height: 0.3rem;\r\n}\r\n\r\n.sg-slider-lg .sg-slider-input {\r\n height: 0.6rem;\r\n}\r\n\r\n.sg-slider-input:focus {\r\n outline: none;\r\n}\r\n\r\n.sg-slider-input:focus-visible {\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, var(--sg-gloss-highlight) 50%, transparent),\r\n 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-slider-input::-webkit-slider-thumb {\r\n -webkit-appearance: none;\r\n appearance: none;\r\n width: 1.15rem;\r\n height: 1.15rem;\r\n margin-top: 0;\r\n border-radius: 50%;\r\n border: 2px solid color-mix(in srgb, var(--sg-surface-solid) 80%, transparent);\r\n background: currentColor;\r\n box-shadow:\r\n 0 1px 3px color-mix(in srgb, var(--sg-fg) 18%, transparent),\r\n 0 0 0 1px color-mix(in srgb, currentColor 25%, transparent);\r\n cursor: grab;\r\n}\r\n\r\n.sg-slider-sm .sg-slider-input::-webkit-slider-thumb {\r\n width: 0.95rem;\r\n height: 0.95rem;\r\n}\r\n\r\n.sg-slider-lg .sg-slider-input::-webkit-slider-thumb {\r\n width: 1.35rem;\r\n height: 1.35rem;\r\n}\r\n\r\n.sg-slider-input::-moz-range-thumb {\r\n width: 1.15rem;\r\n height: 1.15rem;\r\n border-radius: 50%;\r\n border: 2px solid color-mix(in srgb, var(--sg-surface-solid) 80%, transparent);\r\n background: currentColor;\r\n box-shadow:\r\n 0 1px 3px color-mix(in srgb, var(--sg-fg) 18%, transparent),\r\n 0 0 0 1px color-mix(in srgb, currentColor 25%, transparent);\r\n cursor: grab;\r\n}\r\n\r\n.sg-slider-input::-moz-range-track {\r\n height: 0.45rem;\r\n border-radius: var(--sg-radius-pill);\r\n background: transparent;\r\n}\r\n\r\n/* Slider looks */\r\n.sg-slider-look-solid {\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-slider-look-solid .sg-slider-input {\r\n background: linear-gradient(\r\n 90deg,\r\n var(--sg-accent) 0%,\r\n var(--sg-accent) var(--sg-slider-pct),\r\n var(--sg-surface-solid) var(--sg-slider-pct),\r\n var(--sg-surface-solid) 100%\r\n );\r\n box-shadow: inset 0 0 0 1px var(--sg-border-subtle);\r\n}\r\n\r\n.sg-slider-look-solid .sg-slider-input::-webkit-slider-thumb {\r\n background: var(--sg-surface-solid);\r\n border: 2px solid var(--sg-accent);\r\n box-shadow: 0 1px 4px color-mix(in srgb, var(--sg-fg) 16%, transparent);\r\n}\r\n\r\n.sg-slider-look-solid .sg-slider-input::-moz-range-thumb {\r\n background: var(--sg-surface-solid);\r\n border: 2px solid var(--sg-accent);\r\n}\r\n\r\n.sg-slider-look-glass {\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-slider-look-glass .sg-slider-input {\r\n background: linear-gradient(\r\n 90deg,\r\n color-mix(in srgb, var(--sg-accent) 75%, transparent) 0%,\r\n color-mix(in srgb, var(--sg-accent) 75%, transparent) var(--sg-slider-pct),\r\n var(--sg-surface-frost) var(--sg-slider-pct),\r\n var(--sg-surface-frost) 100%\r\n );\r\n border: var(--sg-border-width) solid var(--sg-border-glass);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-slider-look-glass .sg-slider-input::-webkit-slider-thumb {\r\n background: var(--sg-surface-frost);\r\n border: 2px solid var(--sg-accent);\r\n backdrop-filter: blur(6px);\r\n}\r\n\r\n.sg-slider-look-glass .sg-slider-input::-moz-range-thumb {\r\n background: var(--sg-surface-frost);\r\n border: 2px solid var(--sg-accent);\r\n}\r\n\r\n.sg-slider-look-accent {\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-slider-look-accent .sg-slider-input {\r\n height: 0.55rem;\r\n background: linear-gradient(\r\n 90deg,\r\n var(--sg-accent) 0%,\r\n color-mix(in srgb, var(--sg-accent) 70%, #fff) var(--sg-slider-pct),\r\n color-mix(in srgb, var(--sg-accent) 16%, var(--sg-surface-solid))\r\n var(--sg-slider-pct),\r\n color-mix(in srgb, var(--sg-accent) 16%, var(--sg-surface-solid)) 100%\r\n );\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, var(--sg-gloss-highlight) 60%, transparent),\r\n 0 0 12px color-mix(in srgb, var(--sg-accent) 22%, transparent);\r\n}\r\n\r\n.sg-slider-look-accent .sg-slider-input::-webkit-slider-thumb {\r\n width: 1.3rem;\r\n height: 1.3rem;\r\n background: var(--sg-accent);\r\n border: 3px solid var(--sg-surface-solid);\r\n box-shadow:\r\n 0 0 0 2px color-mix(in srgb, var(--sg-accent) 35%, transparent),\r\n 0 2px 8px color-mix(in srgb, var(--sg-accent) 40%, transparent);\r\n}\r\n\r\n.sg-slider-look-accent .sg-slider-input::-moz-range-thumb {\r\n width: 1.3rem;\r\n height: 1.3rem;\r\n background: var(--sg-accent);\r\n border: 3px solid var(--sg-surface-solid);\r\n}\r\n\r\n/* —— NumberInput ——\r\n Softglass quantity pattern: [ − ] value [ + ]\r\n Soft pill steppers + solid center field (not Windows vertical spinner).\r\n*/\r\n.sg-number-input {\r\n display: inline-grid;\r\n grid-template-columns: auto minmax(3.25rem, 1fr) auto;\r\n align-items: center;\r\n gap: var(--sg-space-2);\r\n width: 100%;\r\n max-width: 14rem;\r\n padding: 0.25rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n transition: border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-number-input.sg-input-block {\r\n max-width: none;\r\n}\r\n\r\n.sg-number-input-plain {\r\n grid-template-columns: 1fr;\r\n max-width: none;\r\n border-radius: var(--sg-radius-sm);\r\n background: var(--sg-surface-solid);\r\n padding: 0;\r\n box-shadow: none;\r\n}\r\n\r\n.sg-number-input:focus-within {\r\n border-color: var(--sg-accent);\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-number-input[data-invalid=\"true\"] {\r\n border-color: var(--sg-danger);\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 0 0 3px color-mix(in srgb, var(--sg-danger) 22%, transparent);\r\n}\r\n\r\n.sg-number-input[data-disabled=\"true\"] {\r\n opacity: 0.55;\r\n}\r\n\r\n.sg-number-input-field {\r\n width: 100%;\r\n min-width: 0;\r\n min-height: calc(var(--sg-touch-min) - 0.5rem);\r\n margin: 0;\r\n padding: 0.35rem 0.5rem;\r\n border: 0;\r\n border-radius: var(--sg-radius-sm);\r\n background: var(--sg-surface-solid);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-base);\r\n font-variant-numeric: tabular-nums;\r\n font-weight: var(--sg-font-weight-medium);\r\n text-align: center;\r\n line-height: 1.2;\r\n box-shadow: inset 0 0 0 1px var(--sg-border-subtle);\r\n}\r\n\r\n.sg-number-input-sm .sg-number-input-field {\r\n min-height: 1.85rem;\r\n font-size: var(--sg-text-sm);\r\n}\r\n\r\n.sg-number-input-lg .sg-number-input-field {\r\n min-height: 2.5rem;\r\n font-size: var(--sg-text-lg);\r\n}\r\n\r\n.sg-number-input-plain .sg-number-input-field {\r\n min-height: var(--sg-touch-min);\r\n padding: 0.625rem 1rem;\r\n text-align: start;\r\n border-radius: var(--sg-radius-sm);\r\n box-shadow: none;\r\n font-weight: var(--sg-font-weight-normal);\r\n}\r\n\r\n.sg-number-input-field:focus {\r\n outline: none;\r\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sg-accent) 45%, var(--sg-border-subtle));\r\n}\r\n\r\n.sg-number-input-field:disabled {\r\n cursor: not-allowed;\r\n}\r\n\r\n/* Hide native number spinners — custom soft steppers */\r\n.sg-number-input-field::-webkit-outer-spin-button,\r\n.sg-number-input-field::-webkit-inner-spin-button {\r\n -webkit-appearance: none;\r\n margin: 0;\r\n}\r\n\r\n.sg-number-input-field[type=\"number\"] {\r\n -moz-appearance: textfield;\r\n appearance: textfield;\r\n}\r\n\r\n.sg-number-input-step {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n flex-shrink: 0;\r\n width: calc(var(--sg-touch-min) - 0.35rem);\r\n height: calc(var(--sg-touch-min) - 0.35rem);\r\n margin: 0;\r\n padding: 0;\r\n border: var(--sg-border-width) solid\r\n color-mix(in srgb, var(--sg-accent) 22%, var(--sg-border-subtle));\r\n border-radius: 50%;\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n font-size: 1.15rem;\r\n font-weight: var(--sg-font-weight-semibold);\r\n line-height: 1;\r\n cursor: pointer;\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n transform var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-number-input-sm .sg-number-input-step {\r\n width: 1.85rem;\r\n height: 1.85rem;\r\n font-size: 1rem;\r\n}\r\n\r\n.sg-number-input-lg .sg-number-input-step {\r\n width: 2.5rem;\r\n height: 2.5rem;\r\n font-size: 1.25rem;\r\n}\r\n\r\n.sg-number-input-step:hover:not(:disabled) {\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-number-input-step:active:not(:disabled) {\r\n transform: scale(0.96);\r\n}\r\n\r\n.sg-number-input-step:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-number-input-step:disabled {\r\n opacity: 0.35;\r\n cursor: not-allowed;\r\n background: color-mix(in srgb, var(--sg-fg-muted) 8%, var(--sg-surface-solid));\r\n border-color: var(--sg-border-subtle);\r\n color: var(--sg-fg-muted);\r\n box-shadow: none;\r\n}\r\n\r\n/* NumberInput looks */\r\n.sg-number-input-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-number-input-look-solid .sg-number-input-step {\r\n background: color-mix(in srgb, var(--sg-fg-muted) 10%, var(--sg-surface-solid));\r\n border-color: var(--sg-border-subtle);\r\n color: var(--sg-fg);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-number-input-look-solid .sg-number-input-step:hover:not(:disabled) {\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-number-input-look-outline {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 35%, var(--sg-border-subtle));\r\n box-shadow: none;\r\n}\r\n\r\n.sg-number-input-look-outline .sg-number-input-field {\r\n background: transparent;\r\n box-shadow: inset 0 0 0 1px var(--sg-border-subtle);\r\n}\r\n\r\n.sg-number-input-look-outline .sg-number-input-step {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 40%, var(--sg-border-subtle));\r\n color: var(--sg-accent);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-number-input-look-outline .sg-number-input-step:hover:not(:disabled) {\r\n background: var(--sg-accent-soft);\r\n border-color: var(--sg-accent);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-number-input-look-ghost {\r\n background: transparent;\r\n border-color: transparent;\r\n box-shadow: none;\r\n padding: 0;\r\n gap: var(--sg-space-1);\r\n}\r\n\r\n.sg-number-input-look-ghost .sg-number-input-field {\r\n background: color-mix(in srgb, var(--sg-fg-muted) 8%, transparent);\r\n box-shadow: none;\r\n border-radius: var(--sg-radius-pill);\r\n}\r\n\r\n.sg-number-input-look-ghost .sg-number-input-step {\r\n background: transparent;\r\n border-color: transparent;\r\n color: var(--sg-fg-muted);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-number-input-look-ghost .sg-number-input-step:hover:not(:disabled) {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-number-input-look-ghost:focus-within {\r\n box-shadow: none;\r\n}\r\n\r\n.sg-number-input-look-ghost:focus-within .sg-number-input-field {\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .sg-number-input-step {\r\n transition: none;\r\n }\r\n\r\n .sg-number-input-step:active:not(:disabled) {\r\n transform: none;\r\n }\r\n}\r\n\r\n/* —— FileField —— */\r\n.sg-file-field {\r\n display: grid;\r\n gap: var(--sg-space-2);\r\n width: 100%;\r\n}\r\n\r\n.sg-file-field[data-disabled=\"true\"] {\r\n opacity: 0.55;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-file-field-trigger {\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n gap: var(--sg-space-3);\r\n min-height: var(--sg-touch-min);\r\n padding: 0.4rem 0.5rem 0.4rem 0.4rem;\r\n border-radius: var(--sg-radius-sm);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-solid);\r\n transition: border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-file-field-trigger:focus-within {\r\n border-color: var(--sg-accent);\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-file-field[data-invalid=\"true\"] .sg-file-field-trigger {\r\n border-color: var(--sg-danger);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-danger) 22%, transparent);\r\n}\r\n\r\n.sg-file-field-sm .sg-file-field-trigger {\r\n min-height: 2.25rem;\r\n}\r\n\r\n.sg-file-field-lg .sg-file-field-trigger {\r\n min-height: 3rem;\r\n}\r\n\r\n.sg-file-field-input {\r\n position: absolute;\r\n inset: 0;\r\n width: 100%;\r\n height: 100%;\r\n opacity: 0;\r\n cursor: pointer;\r\n font-size: 0;\r\n}\r\n\r\n.sg-file-field-button {\r\n flex-shrink: 0;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n padding: 0.4rem 0.85rem;\r\n border-radius: var(--sg-radius-xs);\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n border: var(--sg-border-width) solid\r\n color-mix(in srgb, var(--sg-accent) 22%, transparent);\r\n pointer-events: none;\r\n}\r\n\r\n.sg-file-field-summary {\r\n min-width: 0;\r\n flex: 1;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg-muted);\r\n pointer-events: none;\r\n}\r\n\r\n.sg-file-field-list {\r\n list-style: none;\r\n margin: 0;\r\n padding: 0;\r\n display: grid;\r\n gap: var(--sg-space-1);\r\n}\r\n\r\n.sg-file-field-item {\r\n display: grid;\r\n grid-template-columns: 1fr auto auto;\r\n align-items: center;\r\n gap: var(--sg-space-2);\r\n padding: 0.45rem 0.65rem;\r\n border-radius: var(--sg-radius-xs);\r\n background: color-mix(in srgb, var(--sg-fg-muted) 6%, var(--sg-surface-solid));\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n font-size: var(--sg-text-sm);\r\n}\r\n\r\n.sg-file-field-name {\r\n min-width: 0;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-file-field-meta {\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-xs);\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n.sg-file-field-remove {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 1.5rem;\r\n height: 1.5rem;\r\n margin: 0;\r\n padding: 0;\r\n border: 0;\r\n border-radius: var(--sg-radius-xs);\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n font-size: 1.1rem;\r\n line-height: 1;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-file-field-remove:hover:not(:disabled) {\r\n background: color-mix(in srgb, var(--sg-danger) 12%, transparent);\r\n color: var(--sg-danger);\r\n}\r\n\r\n.sg-file-field-clear {\r\n justify-self: start;\r\n margin: 0;\r\n padding: 0.2rem 0.4rem;\r\n border: 0;\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-xs);\r\n cursor: pointer;\r\n text-decoration: underline;\r\n text-underline-offset: 2px;\r\n}\r\n\r\n.sg-file-field-clear:hover:not(:disabled) {\r\n color: var(--sg-fg);\r\n}\r\n\r\n/* FileField looks */\r\n.sg-file-field-look-soft .sg-file-field-trigger {\r\n background: var(--sg-surface-frost);\r\n border-color: var(--sg-border-glass);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n border-radius: var(--sg-radius-md);\r\n}\r\n\r\n.sg-file-field-look-soft .sg-file-field-button {\r\n background: color-mix(in srgb, var(--sg-accent) 18%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-accent) 28%, transparent);\r\n border-radius: var(--sg-radius-pill);\r\n}\r\n\r\n.sg-file-field-look-dashed .sg-file-field-trigger {\r\n flex-direction: column;\r\n justify-content: center;\r\n align-items: center;\r\n gap: var(--sg-space-2);\r\n min-height: 7.5rem;\r\n padding: var(--sg-space-5);\r\n border-style: dashed;\r\n border-width: 1.5px;\r\n border-color: color-mix(in srgb, var(--sg-accent) 35%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-accent-soft) 45%, var(--sg-surface-solid));\r\n border-radius: var(--sg-radius-md);\r\n text-align: center;\r\n}\r\n\r\n.sg-file-field-look-dashed .sg-file-field-button {\r\n background: var(--sg-surface-solid);\r\n border-color: color-mix(in srgb, var(--sg-accent) 25%, var(--sg-border-subtle));\r\n border-radius: var(--sg-radius-pill);\r\n padding: 0.5rem 1.1rem;\r\n}\r\n\r\n.sg-file-field-look-dashed .sg-file-field-summary {\r\n white-space: normal;\r\n text-align: center;\r\n max-width: 18rem;\r\n}\r\n\r\n.sg-file-field-look-ghost .sg-file-field-trigger {\r\n background: transparent;\r\n border-color: transparent;\r\n border-bottom: var(--sg-border-width) solid var(--sg-border-subtle);\r\n border-radius: 0;\r\n padding-inline: 0;\r\n box-shadow: none;\r\n}\r\n\r\n.sg-file-field-look-ghost .sg-file-field-trigger:focus-within {\r\n border-bottom-color: var(--sg-accent);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-file-field-look-ghost .sg-file-field-button {\r\n background: transparent;\r\n border-color: transparent;\r\n color: var(--sg-accent);\r\n text-decoration: underline;\r\n text-underline-offset: 0.15em;\r\n padding-inline: 0.25rem;\r\n}\r\n\r\n.sg-file-field-look-ghost[data-invalid=\"true\"] .sg-file-field-trigger {\r\n border-bottom-color: var(--sg-danger);\r\n box-shadow: none;\r\n}\r\n\r\n/* —— VisuallyHidden —— */\r\n.sg-visually-hidden {\r\n position: absolute !important;\r\n width: 1px !important;\r\n height: 1px !important;\r\n padding: 0 !important;\r\n margin: -1px !important;\r\n overflow: hidden !important;\r\n clip: rect(0, 0, 0, 0) !important;\r\n white-space: nowrap !important;\r\n border: 0 !important;\r\n}\r\n\r\n/* —— CloseButton —— */\r\n.sg-close-btn {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n flex-shrink: 0;\r\n width: 1.75rem;\r\n height: 1.75rem;\r\n margin: 0;\r\n padding: 0;\r\n border: 0;\r\n border-radius: 50%;\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n line-height: 0;\r\n cursor: pointer;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-close-btn-glyph {\r\n display: block;\r\n font-size: 1.15rem;\r\n line-height: 1;\r\n}\r\n\r\n.sg-close-btn:hover:not(:disabled) {\r\n background: color-mix(in srgb, var(--sg-fg-muted) 12%, transparent);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-close-btn:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-close-btn:disabled {\r\n opacity: 0.4;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-close-btn-sm {\r\n width: 1.4rem;\r\n height: 1.4rem;\r\n}\r\n\r\n.sg-close-btn-sm .sg-close-btn-glyph {\r\n font-size: 1rem;\r\n}\r\n\r\n.sg-close-btn-lg {\r\n width: 2.1rem;\r\n height: 2.1rem;\r\n}\r\n\r\n.sg-close-btn-lg .sg-close-btn-glyph {\r\n font-size: 1.35rem;\r\n}\r\n\r\n.sg-close-btn-look-soft {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-close-btn-look-soft:hover:not(:disabled) {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-close-btn-look-solid {\r\n background: var(--sg-surface-solid);\r\n color: var(--sg-fg-muted);\r\n box-shadow: inset 0 0 0 1px var(--sg-border-subtle);\r\n}\r\n\r\n.sg-close-btn-look-solid:hover:not(:disabled) {\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-close-btn-look-danger {\r\n background: color-mix(in srgb, var(--sg-danger) 12%, transparent);\r\n color: var(--sg-danger);\r\n}\r\n\r\n.sg-close-btn-look-danger:hover:not(:disabled) {\r\n background: var(--sg-danger);\r\n color: #fff;\r\n}\r\n\r\n/* —— Link —— */\r\n.sg-link {\r\n color: var(--sg-accent);\r\n font-weight: var(--sg-font-weight-medium);\r\n text-decoration: none;\r\n text-underline-offset: 0.15em;\r\n transition: color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-link:hover {\r\n color: var(--sg-accent-hover);\r\n text-decoration: underline;\r\n}\r\n\r\n.sg-link:focus-visible {\r\n outline: none;\r\n border-radius: 2px;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-link-muted {\r\n color: var(--sg-fg-muted);\r\n font-weight: var(--sg-font-weight-normal);\r\n}\r\n\r\n.sg-link-muted:hover {\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-link-subtle {\r\n color: var(--sg-fg);\r\n font-weight: var(--sg-font-weight-normal);\r\n text-decoration: underline;\r\n text-decoration-color: color-mix(in srgb, var(--sg-fg-muted) 45%, transparent);\r\n}\r\n\r\n.sg-link-subtle:hover {\r\n color: var(--sg-accent);\r\n text-decoration-color: var(--sg-accent);\r\n}\r\n\r\n.sg-link-underline {\r\n text-decoration: underline;\r\n}\r\n\r\n.sg-link-external {\r\n font-size: 0.85em;\r\n opacity: 0.75;\r\n}\r\n\r\n/* —— Chip ——\r\n Outer span is the visual pill. Trigger + × are siblings (never nested buttons).\r\n Fixed height + flex center keeps remove glyph aligned on every look. */\r\n.sg-chip {\r\n display: inline-flex;\r\n flex-direction: row;\r\n align-items: center;\r\n max-width: 100%;\r\n height: 2rem;\r\n box-sizing: border-box;\r\n padding: 0 0.15rem 0 0;\r\n border-radius: 999px;\r\n border: 1px solid color-mix(in srgb, var(--sg-accent) 22%, var(--sg-border-subtle));\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n line-height: 1;\r\n vertical-align: middle;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-chip-sm {\r\n height: 1.65rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-chip-lg {\r\n height: 2.35rem;\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-chip-disabled,\r\n.sg-chip[data-disabled=\"true\"] {\r\n opacity: 0.5;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-chip-trigger,\r\n.sg-chip-static {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.35rem;\r\n height: 100%;\r\n margin: 0;\r\n padding: 0 0.75rem;\r\n border: 0;\r\n background: transparent;\r\n color: inherit;\r\n font: inherit;\r\n line-height: 1;\r\n cursor: pointer;\r\n border-radius: 999px;\r\n min-width: 0;\r\n}\r\n\r\n.sg-chip-static {\r\n cursor: default;\r\n}\r\n\r\n.sg-chip-removable .sg-chip-trigger,\r\n.sg-chip-removable .sg-chip-static {\r\n padding-right: 0.35rem;\r\n border-radius: 999px 0 0 999px;\r\n}\r\n\r\n.sg-chip-sm .sg-chip-trigger,\r\n.sg-chip-sm .sg-chip-static {\r\n padding-left: 0.55rem;\r\n padding-right: 0.55rem;\r\n}\r\n\r\n.sg-chip-sm.sg-chip-removable .sg-chip-trigger,\r\n.sg-chip-sm.sg-chip-removable .sg-chip-static {\r\n padding-right: 0.25rem;\r\n}\r\n\r\n.sg-chip-lg .sg-chip-trigger,\r\n.sg-chip-lg .sg-chip-static {\r\n padding-left: 0.9rem;\r\n padding-right: 0.9rem;\r\n}\r\n\r\n.sg-chip-lg.sg-chip-removable .sg-chip-trigger,\r\n.sg-chip-lg.sg-chip-removable .sg-chip-static {\r\n padding-right: 0.4rem;\r\n}\r\n\r\n.sg-chip-trigger:focus-visible {\r\n outline: none;\r\n box-shadow: inset 0 0 0 2px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-chip-label {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.sg-chip-leading {\r\n display: inline-flex;\r\n flex-shrink: 0;\r\n}\r\n\r\n/* Dedicated remove control — not CloseButton (avoids size/baseline drift) */\r\n.sg-chip-x {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n flex: 0 0 auto;\r\n width: 1.5rem;\r\n height: 1.5rem;\r\n margin: 0 0.2rem 0 0;\r\n padding: 0;\r\n border: 0;\r\n border-radius: 999px;\r\n background: transparent;\r\n color: inherit;\r\n font-size: 1rem;\r\n line-height: 1;\r\n cursor: pointer;\r\n opacity: 0.75;\r\n}\r\n\r\n.sg-chip-sm .sg-chip-x {\r\n width: 1.25rem;\r\n height: 1.25rem;\r\n font-size: 0.9rem;\r\n}\r\n\r\n.sg-chip-lg .sg-chip-x {\r\n width: 1.7rem;\r\n height: 1.7rem;\r\n font-size: 1.1rem;\r\n}\r\n\r\n.sg-chip-x:hover:not(:disabled) {\r\n opacity: 1;\r\n background: color-mix(in srgb, var(--sg-fg) 12%, transparent);\r\n}\r\n\r\n.sg-chip-x:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 2px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-chip-selected,\r\n.sg-chip[data-selected=\"true\"] {\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n color: var(--sg-accent-fg);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-chip-selected .sg-chip-x:hover:not(:disabled),\r\n.sg-chip[data-selected=\"true\"] .sg-chip-x:hover:not(:disabled) {\r\n background: color-mix(in srgb, var(--sg-accent-fg) 20%, transparent);\r\n}\r\n\r\n.sg-chip-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-chip-look-solid.sg-chip-selected,\r\n.sg-chip-look-solid[data-selected=\"true\"] {\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-chip-look-outline {\r\n background: var(--sg-surface-solid);\r\n border-color: color-mix(in srgb, var(--sg-accent) 40%, var(--sg-border-subtle));\r\n box-shadow: none;\r\n}\r\n\r\n.sg-chip-look-outline.sg-chip-selected,\r\n.sg-chip-look-outline[data-selected=\"true\"] {\r\n background: var(--sg-accent-soft);\r\n border-color: var(--sg-accent);\r\n color: var(--sg-fg);\r\n box-shadow: none;\r\n}\r\n\r\n/* Glass must stay readable on light languages (frost alone washes out). */\r\n.sg-chip-look-glass {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 55%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-fg) 14%, var(--sg-border-subtle));\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, #fff 55%, transparent),\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n backdrop-filter: blur(10px);\r\n}\r\n\r\n.sg-chip-look-glass.sg-chip-selected,\r\n.sg-chip-look-glass[data-selected=\"true\"] {\r\n background: color-mix(in srgb, var(--sg-accent) 55%, var(--sg-surface-solid));\r\n border-color: color-mix(in srgb, var(--sg-accent) 50%, transparent);\r\n color: var(--sg-accent-fg);\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 0 0 1px color-mix(in srgb, var(--sg-accent) 25%, transparent);\r\n}\r\n\r\n.sg-chip-look-glow {\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 40%, transparent);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-accent) 16%, transparent);\r\n}\r\n\r\n.sg-chip-look-glow.sg-chip-selected,\r\n.sg-chip-look-glow[data-selected=\"true\"] {\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n color: var(--sg-accent-fg);\r\n box-shadow:\r\n 0 0 0 3px color-mix(in srgb, var(--sg-accent) 28%, transparent),\r\n inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n/* —— Password / Search — ride on .sg-input-group (proven shell) —— */\r\n.sg-password-input,\r\n.sg-search-input {\r\n /* inherits .sg-input-group flex */\r\n}\r\n\r\n.sg-password-input[data-invalid=\"true\"],\r\n.sg-search-input[data-invalid=\"true\"] {\r\n border-color: var(--sg-danger);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-danger) 22%, transparent);\r\n}\r\n\r\n.sg-password-input-soft,\r\n.sg-search-input-soft {\r\n border-radius: var(--sg-radius-pill);\r\n background: color-mix(in srgb, var(--sg-surface-solid) 82%, var(--sg-accent-soft));\r\n border-color: color-mix(in srgb, var(--sg-accent) 22%, var(--sg-border-subtle));\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-password-input-glass,\r\n.sg-search-input-glass {\r\n border-radius: var(--sg-radius-md);\r\n background: color-mix(in srgb, var(--sg-surface-solid) 50%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-fg) 12%, var(--sg-border-subtle));\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, #fff 50%, transparent),\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n backdrop-filter: blur(10px);\r\n}\r\n\r\n.sg-password-toggle {\r\n flex: 0 0 auto;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n align-self: stretch;\r\n margin: 0;\r\n padding: 0 1rem;\r\n border: 0;\r\n border-left: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n line-height: 1;\r\n cursor: pointer;\r\n white-space: nowrap;\r\n}\r\n\r\n.sg-password-toggle:hover:not(:disabled) {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-password-toggle:disabled {\r\n opacity: 0.45;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-password-toggle:focus-visible {\r\n outline: none;\r\n box-shadow: inset 0 0 0 2px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-search-addon {\r\n font-weight: var(--sg-font-weight-medium);\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-search-clear {\r\n flex: 0 0 auto;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n margin: 0;\r\n padding: 0 0.85rem;\r\n border: 0;\r\n border-left: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n line-height: 1;\r\n cursor: pointer;\r\n white-space: nowrap;\r\n}\r\n\r\n.sg-search-clear:hover:not(:disabled) {\r\n background: color-mix(in srgb, var(--sg-danger) 10%, transparent);\r\n color: var(--sg-danger);\r\n}\r\n\r\n.sg-search-clear:focus-visible {\r\n outline: none;\r\n box-shadow: inset 0 0 0 2px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-search-input-field::-webkit-search-cancel-button,\r\n.sg-search-input-field::-webkit-search-decoration {\r\n -webkit-appearance: none;\r\n appearance: none;\r\n}\r\n\r\n/* —— CircularProgress —— */\r\n.sg-circular-progress {\r\n position: relative;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n color: var(--sg-accent);\r\n flex-shrink: 0;\r\n vertical-align: middle;\r\n}\r\n\r\n.sg-circular-progress-svg {\r\n display: block;\r\n transform: rotate(-90deg);\r\n}\r\n\r\n.sg-circular-progress-track {\r\n stroke: color-mix(in srgb, var(--sg-fg-muted) 16%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-circular-progress-fill {\r\n stroke: currentColor;\r\n transition: stroke-dashoffset 180ms ease;\r\n}\r\n\r\n.sg-circular-progress-value {\r\n position: absolute;\r\n inset: 0;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n font-size: 0.7rem;\r\n font-weight: var(--sg-font-weight-semibold);\r\n font-variant-numeric: tabular-nums;\r\n color: var(--sg-fg);\r\n line-height: 1;\r\n}\r\n\r\n.sg-circular-progress-lg .sg-circular-progress-value {\r\n font-size: 0.85rem;\r\n}\r\n\r\n.sg-circular-progress-sm .sg-circular-progress-value {\r\n font-size: 0.6rem;\r\n}\r\n\r\n.sg-circular-progress-success {\r\n color: var(--sg-success);\r\n}\r\n\r\n.sg-circular-progress-warning {\r\n color: var(--sg-warning);\r\n}\r\n\r\n.sg-circular-progress-danger {\r\n color: var(--sg-danger);\r\n}\r\n\r\n.sg-circular-progress-muted {\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-circular-progress-look-solid .sg-circular-progress-track {\r\n stroke: var(--sg-surface-solid);\r\n stroke-width: inherit;\r\n /* thicker track feel via filter-like opacity edge */\r\n opacity: 1;\r\n filter: drop-shadow(0 0 0 var(--sg-border-subtle));\r\n}\r\n\r\n.sg-circular-progress-look-solid {\r\n filter: none;\r\n}\r\n\r\n.sg-circular-progress-look-solid .sg-circular-progress-track {\r\n stroke: color-mix(in srgb, var(--sg-fg-muted) 28%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-circular-progress-look-glass .sg-circular-progress-track {\r\n stroke: color-mix(in srgb, var(--sg-accent) 18%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-circular-progress-look-glass {\r\n filter: drop-shadow(0 0 6px color-mix(in srgb, currentColor 22%, transparent));\r\n}\r\n\r\n.sg-circular-progress-look-accent .sg-circular-progress-track {\r\n stroke: color-mix(in srgb, var(--sg-accent) 22%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-circular-progress-look-accent .sg-circular-progress-fill {\r\n filter: drop-shadow(0 0 4px color-mix(in srgb, currentColor 45%, transparent));\r\n}\r\n\r\n.sg-circular-progress-indeterminate .sg-circular-progress-svg {\r\n animation: sg-circular-spin 0.9s linear infinite;\r\n}\r\n\r\n.sg-circular-progress-indeterminate .sg-circular-progress-fill {\r\n transition: none;\r\n}\r\n\r\n@keyframes sg-circular-spin {\r\n to {\r\n transform: rotate(270deg);\r\n }\r\n}\r\n\r\n/* —— SegmentedControl —— */\r\n.sg-segmented {\r\n display: inline-flex;\r\n align-items: stretch;\r\n padding: 0.2rem;\r\n gap: 0.15rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: color-mix(in srgb, var(--sg-fg-muted) 8%, var(--sg-surface-solid));\r\n box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 40%, transparent);\r\n vertical-align: middle;\r\n}\r\n\r\n.sg-segmented-block {\r\n display: flex;\r\n width: 100%;\r\n}\r\n\r\n.sg-segmented-block .sg-segmented-item {\r\n flex: 1;\r\n}\r\n\r\n.sg-segmented[data-disabled=\"true\"] {\r\n opacity: 0.55;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-segmented-item {\r\n appearance: none;\r\n margin: 0;\r\n padding: 0.45rem 0.9rem;\r\n border: 0;\r\n border-radius: var(--sg-radius-pill);\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n line-height: 1.2;\r\n cursor: pointer;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-segmented-sm .sg-segmented-item {\r\n padding: 0.3rem 0.65rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-segmented-lg .sg-segmented-item {\r\n padding: 0.55rem 1.1rem;\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-segmented-item:hover:not(:disabled):not(.sg-segmented-item-selected) {\r\n color: var(--sg-fg);\r\n background: color-mix(in srgb, var(--sg-fg-muted) 8%, transparent);\r\n}\r\n\r\n.sg-segmented-item:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-segmented-item:disabled {\r\n opacity: 0.45;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-segmented-item-selected {\r\n background: var(--sg-surface-solid);\r\n color: var(--sg-fg);\r\n box-shadow:\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 10%, transparent),\r\n inset 0 1px 0 color-mix(in srgb, #fff 55%, transparent);\r\n}\r\n\r\n.sg-segmented-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-segmented-look-solid .sg-segmented-item-selected {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-segmented-look-outline {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 30%, var(--sg-border-subtle));\r\n box-shadow: none;\r\n}\r\n\r\n.sg-segmented-look-outline .sg-segmented-item-selected {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sg-accent) 35%, transparent);\r\n}\r\n\r\n.sg-segmented-look-glass {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 55%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-fg) 12%, var(--sg-border-subtle));\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, #fff 50%, transparent),\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n backdrop-filter: blur(10px);\r\n}\r\n\r\n.sg-segmented-look-glass .sg-segmented-item-selected {\r\n background: color-mix(in srgb, var(--sg-accent) 18%, var(--sg-surface-solid));\r\n color: var(--sg-fg);\r\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sg-accent) 28%, transparent);\r\n}\r\n\r\n/* —— Kbd —— */\r\n.sg-kbd {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-width: 1.4em;\r\n padding: 0.15rem 0.4rem;\r\n border-radius: var(--sg-radius-xs);\r\n border: 1px solid color-mix(in srgb, var(--sg-fg-muted) 22%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-fg-muted) 8%, var(--sg-surface-solid));\r\n box-shadow:\r\n 0 1px 0 color-mix(in srgb, var(--sg-fg) 10%, transparent),\r\n inset 0 1px 0 color-mix(in srgb, #fff 50%, transparent);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: 0.75rem;\r\n font-weight: var(--sg-font-weight-medium);\r\n line-height: 1.2;\r\n vertical-align: middle;\r\n}\r\n\r\n.sg-kbd-sm {\r\n padding: 0.1rem 0.3rem;\r\n font-size: 0.65rem;\r\n}\r\n\r\n.sg-kbd-lg {\r\n padding: 0.25rem 0.55rem;\r\n font-size: 0.85rem;\r\n}\r\n\r\n.sg-kbd-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-kbd-look-outline {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 35%, var(--sg-border-subtle));\r\n box-shadow: none;\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-kbd-look-glass {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 50%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-fg) 12%, transparent);\r\n box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 45%, transparent);\r\n backdrop-filter: blur(8px);\r\n}\r\n\r\n/* —— Code —— */\r\n.sg-code {\r\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\r\n font-size: 0.9em;\r\n padding: 0.12em 0.35em;\r\n border-radius: var(--sg-radius-xs);\r\n background: color-mix(in srgb, var(--sg-fg-muted) 10%, var(--sg-surface-solid));\r\n border: 1px solid color-mix(in srgb, var(--sg-fg-muted) 12%, transparent);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-code-block {\r\n display: block;\r\n overflow: auto;\r\n margin: 0;\r\n padding: 0.85rem 1rem;\r\n font-size: var(--sg-text-sm);\r\n line-height: 1.5;\r\n white-space: pre;\r\n}\r\n\r\n.sg-code-block code {\r\n padding: 0;\r\n border: 0;\r\n background: transparent;\r\n font: inherit;\r\n}\r\n\r\n.sg-code-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-code-look-accent {\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 28%, transparent);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-code-look-muted {\r\n background: transparent;\r\n border-color: transparent;\r\n color: var(--sg-fg-muted);\r\n padding-inline: 0.15em;\r\n}\r\n\r\n/* —— SkipLink ——\r\n clip-based hide (not translate) so overflow:hidden parents cannot swallow it.\r\n alwaysVisible / .sg-skip-link-visible for docs demos. */\r\n.sg-skip-link {\r\n position: fixed;\r\n top: 0.75rem;\r\n z-index: 10000;\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.35rem;\r\n padding: 0.55rem 0.95rem;\r\n border-radius: var(--sg-radius-sm);\r\n border: 1px solid transparent;\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n line-height: 1.2;\r\n text-decoration: none;\r\n white-space: nowrap;\r\n /* visually hide until focus / alwaysVisible */\r\n width: 1px;\r\n height: 1px;\r\n margin: -1px;\r\n padding: 0;\r\n overflow: hidden;\r\n clip: rect(0, 0, 0, 0);\r\n clip-path: inset(50%);\r\n border: 0;\r\n}\r\n\r\n.sg-skip-link-start {\r\n left: 0.75rem;\r\n right: auto;\r\n transform: none;\r\n}\r\n\r\n.sg-skip-link-center {\r\n left: 50%;\r\n right: auto;\r\n transform: translateX(-50%);\r\n}\r\n\r\n.sg-skip-link-end {\r\n left: auto;\r\n right: 0.75rem;\r\n transform: none;\r\n}\r\n\r\n.sg-skip-link:focus,\r\n.sg-skip-link:focus-visible,\r\n.sg-skip-link.sg-skip-link-visible,\r\n.sg-skip-link[data-always-visible=\"true\"] {\r\n width: auto;\r\n height: auto;\r\n margin: 0;\r\n padding: 0.55rem 0.95rem;\r\n overflow: visible;\r\n clip: auto;\r\n clip-path: none;\r\n border: 1px solid transparent;\r\n outline: none;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-skip-link-center:focus,\r\n.sg-skip-link-center:focus-visible,\r\n.sg-skip-link-center.sg-skip-link-visible,\r\n.sg-skip-link-center[data-always-visible=\"true\"] {\r\n transform: translateX(-50%);\r\n}\r\n\r\n/* SkipLink looks (when visible) */\r\n.sg-skip-link-look-soft:focus,\r\n.sg-skip-link-look-soft:focus-visible,\r\n.sg-skip-link-look-soft.sg-skip-link-visible,\r\n.sg-skip-link-look-soft[data-always-visible=\"true\"] {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n border-color: color-mix(in srgb, var(--sg-accent) 30%, transparent);\r\n}\r\n\r\n.sg-skip-link-look-outline:focus,\r\n.sg-skip-link-look-outline:focus-visible,\r\n.sg-skip-link-look-outline.sg-skip-link-visible,\r\n.sg-skip-link-look-outline[data-always-visible=\"true\"] {\r\n background: var(--sg-surface-solid);\r\n color: var(--sg-accent);\r\n border-color: color-mix(in srgb, var(--sg-accent) 45%, var(--sg-border-subtle));\r\n}\r\n\r\n.sg-skip-link-look-glass:focus,\r\n.sg-skip-link-look-glass:focus-visible,\r\n.sg-skip-link-look-glass.sg-skip-link-visible,\r\n.sg-skip-link-look-glass[data-always-visible=\"true\"] {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 55%, transparent);\r\n color: var(--sg-fg);\r\n border-color: color-mix(in srgb, var(--sg-fg) 12%, transparent);\r\n backdrop-filter: blur(10px);\r\n box-shadow:\r\n 0 0 0 3px var(--sg-ring-soft),\r\n inset 0 1px 0 color-mix(in srgb, #fff 50%, transparent);\r\n}\r\n\r\n/* —— PinInput —— */\r\n.sg-pin {\r\n display: inline-flex;\r\n gap: 0.45rem;\r\n align-items: center;\r\n vertical-align: middle;\r\n}\r\n\r\n.sg-pin[data-disabled=\"true\"] {\r\n opacity: 0.55;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-pin-cell {\r\n width: 2.5rem;\r\n height: 2.75rem;\r\n margin: 0;\r\n padding: 0;\r\n border-radius: var(--sg-radius-sm);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-solid);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-lg);\r\n font-weight: var(--sg-font-weight-semibold);\r\n font-variant-numeric: tabular-nums;\r\n text-align: center;\r\n line-height: 1;\r\n transition:\r\n border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-pin-sm .sg-pin-cell {\r\n width: 2rem;\r\n height: 2.25rem;\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-pin-lg .sg-pin-cell {\r\n width: 3rem;\r\n height: 3.25rem;\r\n font-size: 1.35rem;\r\n}\r\n\r\n.sg-pin-cell:focus {\r\n outline: none;\r\n border-color: var(--sg-accent);\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-pin[data-invalid=\"true\"] .sg-pin-cell {\r\n border-color: var(--sg-danger);\r\n}\r\n\r\n.sg-pin-look-soft .sg-pin-cell {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 82%, var(--sg-accent-soft));\r\n border-color: color-mix(in srgb, var(--sg-accent) 18%, var(--sg-border-subtle));\r\n border-radius: var(--sg-radius-md);\r\n}\r\n\r\n.sg-pin-look-outline .sg-pin-cell {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 35%, var(--sg-border-subtle));\r\n}\r\n\r\n.sg-pin-look-glass .sg-pin-cell {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 50%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-fg) 12%, var(--sg-border-subtle));\r\n box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 45%, transparent);\r\n backdrop-filter: blur(8px);\r\n}\r\n\r\n/* —— NavLink —— */\r\n.sg-nav-link {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.45rem;\r\n padding: 0.4rem 0.65rem;\r\n border-radius: var(--sg-radius-sm);\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n text-decoration: none;\r\n line-height: 1.2;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-nav-link:hover {\r\n color: var(--sg-fg);\r\n background: color-mix(in srgb, var(--sg-fg-muted) 8%, transparent);\r\n}\r\n\r\n.sg-nav-link:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-nav-link-active,\r\n.sg-nav-link[data-active=\"true\"] {\r\n color: var(--sg-fg);\r\n background: var(--sg-accent-soft);\r\n}\r\n\r\n.sg-nav-link-look-solid.sg-nav-link-active,\r\n.sg-nav-link-look-solid[data-active=\"true\"] {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-nav-link-look-underline {\r\n border-radius: 0;\r\n padding-inline: 0.15rem;\r\n background: transparent;\r\n box-shadow: inset 0 -2px 0 transparent;\r\n}\r\n\r\n.sg-nav-link-look-underline:hover {\r\n background: transparent;\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-nav-link-look-underline.sg-nav-link-active,\r\n.sg-nav-link-look-underline[data-active=\"true\"] {\r\n background: transparent;\r\n color: var(--sg-accent);\r\n box-shadow: inset 0 -2px 0 var(--sg-accent);\r\n}\r\n\r\n.sg-nav-link-look-pill {\r\n border-radius: var(--sg-radius-pill);\r\n}\r\n\r\n.sg-nav-link-look-pill.sg-nav-link-active,\r\n.sg-nav-link-look-pill[data-active=\"true\"] {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-nav-link-leading {\r\n display: inline-flex;\r\n flex-shrink: 0;\r\n}\r\n\r\n/* —— ListItem —— */\r\n.sg-list-item {\r\n display: flex;\r\n align-items: center;\r\n gap: 0.75rem;\r\n width: 100%;\r\n padding: 0.75rem 0.9rem;\r\n border-radius: var(--sg-radius-md);\r\n border: 1px solid color-mix(in srgb, var(--sg-accent) 12%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-fg-muted) 5%, var(--sg-surface-solid));\r\n color: var(--sg-fg);\r\n box-sizing: border-box;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-list-item-interactive {\r\n cursor: pointer;\r\n}\r\n\r\n.sg-list-item-interactive:hover {\r\n background: color-mix(in srgb, var(--sg-accent) 8%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-list-item-interactive:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-list-item-selected,\r\n.sg-list-item[data-selected=\"true\"] {\r\n border-color: color-mix(in srgb, var(--sg-accent) 40%, transparent);\r\n background: var(--sg-accent-soft);\r\n}\r\n\r\n.sg-list-item-disabled,\r\n.sg-list-item[data-disabled=\"true\"] {\r\n opacity: 0.5;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-list-item-leading {\r\n flex-shrink: 0;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n}\r\n\r\n.sg-list-item-body {\r\n min-width: 0;\r\n flex: 1;\r\n display: grid;\r\n gap: 0.15rem;\r\n}\r\n\r\n.sg-list-item-title {\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n line-height: 1.3;\r\n}\r\n\r\n.sg-list-item-description {\r\n font-size: var(--sg-text-xs);\r\n color: var(--sg-fg-muted);\r\n line-height: 1.35;\r\n}\r\n\r\n.sg-list-item-trailing {\r\n flex-shrink: 0;\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.35rem;\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-list-item-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-list-item-look-outline {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 30%, var(--sg-border-subtle));\r\n}\r\n\r\n.sg-list-item-look-ghost {\r\n background: transparent;\r\n border-color: transparent;\r\n padding-inline: 0.25rem;\r\n}\r\n\r\n/* —— CopyButton —— */\r\n.sg-copy-btn {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 0.35rem;\r\n margin: 0;\r\n padding: 0.45rem 0.85rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: 1px solid color-mix(in srgb, var(--sg-accent) 22%, var(--sg-border-subtle));\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n line-height: 1;\r\n cursor: pointer;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-copy-btn:hover:not(:disabled) {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n border-color: transparent;\r\n}\r\n\r\n.sg-copy-btn:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-copy-btn:disabled {\r\n opacity: 0.5;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-copy-btn-sm {\r\n padding: 0.3rem 0.65rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-copy-btn-lg {\r\n padding: 0.55rem 1.1rem;\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-copy-btn-copied,\r\n.sg-copy-btn[data-copied=\"true\"] {\r\n background: color-mix(in srgb, var(--sg-success) 16%, var(--sg-surface-solid));\r\n border-color: color-mix(in srgb, var(--sg-success) 35%, transparent);\r\n color: var(--sg-success);\r\n}\r\n\r\n.sg-copy-btn-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-copy-btn-look-solid:hover:not(:disabled) {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-copy-btn-look-outline {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 40%, var(--sg-border-subtle));\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-copy-btn-look-outline:hover:not(:disabled) {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-copy-btn-look-ghost {\r\n background: transparent;\r\n border-color: transparent;\r\n color: var(--sg-fg-muted);\r\n text-decoration: underline;\r\n text-underline-offset: 0.12em;\r\n}\r\n\r\n.sg-copy-btn-look-ghost:hover:not(:disabled) {\r\n background: color-mix(in srgb, var(--sg-fg-muted) 8%, transparent);\r\n color: var(--sg-fg);\r\n text-decoration: none;\r\n}\r\n\r\n/* —— CharacterCount —— */\r\n.sg-char-count {\r\n margin: 0;\r\n font-size: var(--sg-text-xs);\r\n font-variant-numeric: tabular-nums;\r\n color: var(--sg-fg-muted);\r\n line-height: 1.3;\r\n}\r\n\r\n.sg-char-count-look-soft {\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-char-count-look-solid {\r\n display: inline-flex;\r\n padding: 0.1rem 0.4rem;\r\n border-radius: var(--sg-radius-xs);\r\n background: var(--sg-surface-solid);\r\n border: 1px solid var(--sg-border-subtle);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-char-count-look-muted {\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-char-count-look-danger,\r\n.sg-char-count-over,\r\n.sg-char-count[data-over=\"true\"] {\r\n color: var(--sg-danger);\r\n font-weight: var(--sg-font-weight-medium);\r\n}\r\n\r\n/* —— Fieldset —— */\r\n.sg-fieldset {\r\n margin: 0;\r\n padding: 1rem 1.1rem 1.1rem;\r\n border-radius: var(--sg-radius-md);\r\n border: 1px solid color-mix(in srgb, var(--sg-accent) 14%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-fg-muted) 4%, var(--sg-surface-solid));\r\n min-width: 0;\r\n}\r\n\r\n.sg-fieldset-legend {\r\n padding: 0 0.4rem;\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-fieldset-body {\r\n display: grid;\r\n gap: var(--sg-space-3);\r\n}\r\n\r\n.sg-fieldset-compact {\r\n padding: 0.65rem 0.75rem 0.75rem;\r\n}\r\n\r\n.sg-fieldset-compact .sg-fieldset-body {\r\n gap: var(--sg-space-2);\r\n}\r\n\r\n.sg-fieldset-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-fieldset-look-outline {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 30%, var(--sg-border-subtle));\r\n}\r\n\r\n.sg-fieldset-look-ghost {\r\n background: transparent;\r\n border-color: transparent;\r\n padding-inline: 0;\r\n}\r\n\r\n/* —— Icon —— */\r\n.sg-icon {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n flex-shrink: 0;\r\n width: 2rem;\r\n height: 2rem;\r\n border-radius: var(--sg-radius-sm);\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n font-size: 1rem;\r\n line-height: 1;\r\n vertical-align: middle;\r\n}\r\n\r\n.sg-icon > svg {\r\n width: 1.1em;\r\n height: 1.1em;\r\n display: block;\r\n}\r\n\r\n.sg-icon-sm {\r\n width: 1.5rem;\r\n height: 1.5rem;\r\n font-size: 0.85rem;\r\n}\r\n\r\n.sg-icon-lg {\r\n width: 2.5rem;\r\n height: 2.5rem;\r\n font-size: 1.25rem;\r\n}\r\n\r\n.sg-icon-xl {\r\n width: 3rem;\r\n height: 3rem;\r\n font-size: 1.5rem;\r\n}\r\n\r\n.sg-icon-look-solid {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-icon-look-outline {\r\n background: transparent;\r\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sg-accent) 40%, transparent);\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-icon-look-ghost {\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n/* —— Image —— */\r\n.sg-image {\r\n position: relative;\r\n display: block;\r\n overflow: hidden;\r\n width: 100%;\r\n background: color-mix(in srgb, var(--sg-fg-muted) 8%, var(--sg-surface-solid));\r\n border: 1px solid color-mix(in srgb, var(--sg-fg-muted) 12%, transparent);\r\n border-radius: var(--sg-radius-md);\r\n}\r\n\r\n.sg-image-media {\r\n display: block;\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n}\r\n\r\n.sg-image-fit-cover .sg-image-media {\r\n object-fit: cover;\r\n}\r\n\r\n.sg-image-fit-contain .sg-image-media {\r\n object-fit: contain;\r\n}\r\n\r\n.sg-image-fit-fill .sg-image-media {\r\n object-fit: fill;\r\n}\r\n\r\n.sg-image-fit-none .sg-image-media {\r\n object-fit: none;\r\n}\r\n\r\n.sg-image-rounded-none {\r\n border-radius: 0;\r\n}\r\n\r\n.sg-image-rounded-sm {\r\n border-radius: var(--sg-radius-sm);\r\n}\r\n\r\n.sg-image-rounded-lg {\r\n border-radius: var(--sg-radius-lg);\r\n}\r\n\r\n.sg-image-rounded-full {\r\n border-radius: 999px;\r\n}\r\n\r\n.sg-image-fallback {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 100%;\r\n height: 100%;\r\n min-height: 4rem;\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: 0.04em;\r\n}\r\n\r\n.sg-image-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-image-look-outline {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 30%, var(--sg-border-subtle));\r\n}\r\n\r\n.sg-image-look-glass {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 50%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-fg) 12%, transparent);\r\n backdrop-filter: blur(8px);\r\n}\r\n\r\n/* —— Meter —— */\r\n.sg-meter {\r\n display: grid;\r\n gap: 0.35rem;\r\n width: 100%;\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-meter-header {\r\n display: flex;\r\n justify-content: space-between;\r\n gap: 0.75rem;\r\n font-size: var(--sg-text-sm);\r\n}\r\n\r\n.sg-meter-label {\r\n color: var(--sg-fg);\r\n font-weight: var(--sg-font-weight-medium);\r\n}\r\n\r\n.sg-meter-value {\r\n color: var(--sg-fg-muted);\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n.sg-meter-track {\r\n height: 0.5rem;\r\n overflow: hidden;\r\n border-radius: var(--sg-radius-pill);\r\n background: color-mix(in srgb, var(--sg-fg-muted) 14%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-meter-fill {\r\n height: 100%;\r\n border-radius: inherit;\r\n background: currentColor;\r\n transition: width 160ms ease;\r\n}\r\n\r\n.sg-meter-look-solid .sg-meter-track {\r\n background: var(--sg-surface-solid);\r\n box-shadow: inset 0 0 0 1px var(--sg-border-subtle);\r\n}\r\n\r\n.sg-meter-look-glass .sg-meter-track {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 55%, transparent);\r\n box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 45%, transparent);\r\n}\r\n\r\n.sg-meter-look-striped .sg-meter-fill {\r\n background-image:\r\n linear-gradient(\r\n 45deg,\r\n color-mix(in srgb, #fff 18%, transparent) 25%,\r\n transparent 25%,\r\n transparent 50%,\r\n color-mix(in srgb, #fff 18%, transparent) 50%,\r\n color-mix(in srgb, #fff 18%, transparent) 75%,\r\n transparent 75%,\r\n transparent\r\n ),\r\n linear-gradient(90deg, currentColor, currentColor);\r\n background-size: 0.7rem 0.7rem, 100% 100%;\r\n}\r\n\r\n.sg-meter-success {\r\n color: var(--sg-success);\r\n}\r\n\r\n.sg-meter-warning {\r\n color: var(--sg-warning);\r\n}\r\n\r\n.sg-meter-danger {\r\n color: var(--sg-danger);\r\n}\r\n\r\n.sg-meter-muted {\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n/* —— TimeInput (custom Softglass HH:mm — not native OS picker) —— */\r\n.sg-time {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.65rem;\r\n padding: 0.35rem 0.55rem 0.35rem 0.45rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: 1px solid color-mix(in srgb, var(--sg-accent) 20%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-surface-solid) 78%, var(--sg-accent-soft));\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, #fff 55%, transparent),\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n vertical-align: middle;\r\n user-select: none;\r\n}\r\n\r\n.sg-time-block {\r\n display: flex;\r\n width: 100%;\r\n}\r\n\r\n.sg-time[data-disabled=\"true\"] {\r\n opacity: 0.55;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-time[data-invalid=\"true\"] {\r\n border-color: var(--sg-danger);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-danger) 20%, transparent);\r\n}\r\n\r\n.sg-time-glyph {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n flex-shrink: 0;\r\n width: 2.1rem;\r\n height: 2.1rem;\r\n border-radius: 50%;\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sg-accent) 22%, transparent);\r\n}\r\n\r\n.sg-time-glyph-face {\r\n position: relative;\r\n width: 1.05rem;\r\n height: 1.05rem;\r\n border-radius: 50%;\r\n border: 1.5px solid currentColor;\r\n}\r\n\r\n.sg-time-glyph-hand {\r\n position: absolute;\r\n left: 50%;\r\n bottom: 50%;\r\n width: 1.5px;\r\n background: currentColor;\r\n transform-origin: bottom center;\r\n border-radius: 1px;\r\n}\r\n\r\n.sg-time-glyph-hand-h {\r\n height: 0.28rem;\r\n transform: translateX(-50%) rotate(40deg);\r\n}\r\n\r\n.sg-time-glyph-hand-m {\r\n height: 0.38rem;\r\n transform: translateX(-50%) rotate(120deg);\r\n opacity: 0.85;\r\n}\r\n\r\n.sg-time-segments {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.2rem;\r\n}\r\n\r\n.sg-time-segment {\r\n display: inline-flex;\r\n flex-direction: column;\r\n align-items: center;\r\n gap: 0.05rem;\r\n}\r\n\r\n.sg-time-step {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 1.6rem;\r\n height: 0.85rem;\r\n margin: 0;\r\n padding: 0;\r\n border: 0;\r\n border-radius: 999px;\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n font-size: 0.55rem;\r\n line-height: 1;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-time-step:hover:not(:disabled) {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-time-step:disabled {\r\n opacity: 0.4;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-time-value {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-width: 2.1rem;\r\n height: 1.85rem;\r\n margin: 0;\r\n padding: 0 0.25rem;\r\n border: 0;\r\n border-radius: var(--sg-radius-sm);\r\n background: var(--sg-surface-solid);\r\n box-shadow: inset 0 0 0 1px var(--sg-border-subtle);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: 1.05rem;\r\n font-weight: var(--sg-font-weight-semibold);\r\n font-variant-numeric: tabular-nums;\r\n letter-spacing: 0.02em;\r\n line-height: 1;\r\n cursor: default;\r\n}\r\n\r\n.sg-time-value:focus-visible {\r\n outline: none;\r\n box-shadow:\r\n inset 0 0 0 1px var(--sg-accent),\r\n 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-time-colon {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 0.65rem;\r\n padding-bottom: 0.15rem;\r\n color: var(--sg-accent);\r\n font-size: 1.15rem;\r\n font-weight: var(--sg-font-weight-semibold);\r\n line-height: 1;\r\n opacity: 0.85;\r\n animation: sg-time-colon-blink 1.4s steps(1) infinite;\r\n}\r\n\r\n@keyframes sg-time-colon-blink {\r\n 0%,\r\n 50% {\r\n opacity: 0.9;\r\n }\r\n 51%,\r\n 100% {\r\n opacity: 0.25;\r\n }\r\n}\r\n\r\n.sg-time-ampm {\r\n display: inline-flex;\r\n flex-direction: column;\r\n gap: 0.15rem;\r\n margin-left: 0.25rem;\r\n}\r\n\r\n.sg-time-ampm-btn {\r\n margin: 0;\r\n padding: 0.15rem 0.4rem;\r\n border: 0;\r\n border-radius: 999px;\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n font-size: 0.65rem;\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: 0.04em;\r\n line-height: 1.2;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-time-ampm-btn:hover:not(:disabled) {\r\n color: var(--sg-fg);\r\n background: color-mix(in srgb, var(--sg-fg-muted) 10%, transparent);\r\n}\r\n\r\n.sg-time-ampm-active {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-time-sm .sg-time-glyph {\r\n width: 1.75rem;\r\n height: 1.75rem;\r\n}\r\n\r\n.sg-time-sm .sg-time-value {\r\n min-width: 1.75rem;\r\n height: 1.55rem;\r\n font-size: 0.9rem;\r\n}\r\n\r\n.sg-time-lg .sg-time-glyph {\r\n width: 2.45rem;\r\n height: 2.45rem;\r\n}\r\n\r\n.sg-time-lg .sg-time-value {\r\n min-width: 2.45rem;\r\n height: 2.15rem;\r\n font-size: 1.25rem;\r\n}\r\n\r\n/* Looks */\r\n.sg-time-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n border-radius: var(--sg-radius-md);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-time-look-outline {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 40%, var(--sg-border-subtle));\r\n box-shadow: none;\r\n}\r\n\r\n.sg-time-look-outline .sg-time-value {\r\n background: transparent;\r\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sg-accent) 28%, transparent);\r\n}\r\n\r\n.sg-time-look-glass {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 48%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-fg) 12%, var(--sg-border-subtle));\r\n backdrop-filter: blur(10px);\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, #fff 50%, transparent),\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n}\r\n\r\n/* —— ScrollArea —— */\r\n.sg-scroll-area {\r\n overflow-y: auto;\r\n overflow-x: hidden;\r\n padding: 0.5rem;\r\n border-radius: var(--sg-radius-md);\r\n border: 1px solid color-mix(in srgb, var(--sg-fg-muted) 14%, transparent);\r\n background: color-mix(in srgb, var(--sg-fg-muted) 4%, var(--sg-surface-solid));\r\n scrollbar-width: thin;\r\n scrollbar-color: color-mix(in srgb, var(--sg-fg-muted) 35%, transparent) transparent;\r\n}\r\n\r\n.sg-scroll-area-x {\r\n overflow-x: auto;\r\n overflow-y: hidden;\r\n}\r\n\r\n.sg-scroll-area-both {\r\n overflow: auto;\r\n}\r\n\r\n.sg-scroll-area-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-scroll-area-look-ghost {\r\n background: transparent;\r\n border-color: transparent;\r\n padding-inline: 0;\r\n}\r\n\r\n/* —— Rating (Softglass SVG stars in a soft shell) —— */\r\n.sg-rating {\r\n --sg-rating-on: #f5b942;\r\n --sg-rating-on-soft: color-mix(in srgb, #f5b942 28%, transparent);\r\n --sg-rating-off: color-mix(in srgb, var(--sg-fg-muted) 28%, var(--sg-surface-solid));\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.55rem;\r\n padding: 0.4rem 0.65rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: 1px solid color-mix(in srgb, var(--sg-warning) 22%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-surface-solid) 82%, #f5b942);\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, #fff 55%, transparent),\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n vertical-align: middle;\r\n user-select: none;\r\n}\r\n\r\n.sg-rating-color-accent {\r\n --sg-rating-on: var(--sg-accent);\r\n --sg-rating-on-soft: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 22%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-surface-solid) 82%, var(--sg-accent-soft));\r\n}\r\n\r\n.sg-rating-color-rose {\r\n --sg-rating-on: #f472b6;\r\n --sg-rating-on-soft: color-mix(in srgb, #f472b6 26%, transparent);\r\n border-color: color-mix(in srgb, #f472b6 28%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-surface-solid) 82%, #f472b6);\r\n}\r\n\r\n.sg-rating-caption {\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-semibold);\r\n color: var(--sg-fg-muted);\r\n letter-spacing: 0.02em;\r\n padding-inline: 0.15rem 0.25rem;\r\n white-space: nowrap;\r\n}\r\n\r\n.sg-rating-stars {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.1rem;\r\n}\r\n\r\n.sg-rating-star {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 1.85rem;\r\n height: 1.85rem;\r\n margin: 0;\r\n padding: 0;\r\n border: 0;\r\n border-radius: 50%;\r\n background: transparent;\r\n color: var(--sg-rating-on);\r\n font-size: 1.2rem;\r\n line-height: 1;\r\n cursor: pointer;\r\n transition:\r\n transform var(--sg-duration-fast) var(--sg-ease-out),\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-rating-svg {\r\n display: block;\r\n overflow: visible;\r\n}\r\n\r\n.sg-rating-svg-path {\r\n transition:\r\n fill var(--sg-duration-fast) var(--sg-ease-out),\r\n stroke var(--sg-duration-fast) var(--sg-ease-out),\r\n filter var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-rating-svg-path-off {\r\n fill: var(--sg-rating-off);\r\n stroke: color-mix(in srgb, var(--sg-fg-muted) 35%, transparent);\r\n stroke-width: 1;\r\n}\r\n\r\n.sg-rating-svg-path-on {\r\n fill: var(--sg-rating-on);\r\n stroke: color-mix(in srgb, var(--sg-rating-on) 55%, #000);\r\n stroke-width: 0.6;\r\n filter: drop-shadow(0 1px 2px color-mix(in srgb, var(--sg-rating-on) 45%, transparent));\r\n}\r\n\r\n.sg-rating-star-active {\r\n background: color-mix(in srgb, var(--sg-rating-on) 14%, transparent);\r\n box-shadow: 0 0 0 1px color-mix(in srgb, var(--sg-rating-on) 18%, transparent);\r\n}\r\n\r\n.sg-rating-star:hover:not(:disabled) {\r\n transform: translateY(-1px) scale(1.08);\r\n background: var(--sg-rating-on-soft);\r\n}\r\n\r\n.sg-rating-star:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-rating-star:disabled {\r\n cursor: default;\r\n}\r\n\r\n.sg-rating-score {\r\n min-width: 2rem;\r\n padding: 0.2rem 0.45rem;\r\n border-radius: 999px;\r\n background: var(--sg-surface-solid);\r\n box-shadow: inset 0 0 0 1px var(--sg-border-subtle);\r\n color: var(--sg-fg);\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-semibold);\r\n font-variant-numeric: tabular-nums;\r\n text-align: center;\r\n line-height: 1.2;\r\n}\r\n\r\n.sg-rating-sm {\r\n padding: 0.28rem 0.5rem;\r\n gap: 0.4rem;\r\n}\r\n\r\n.sg-rating-sm .sg-rating-star {\r\n width: 1.45rem;\r\n height: 1.45rem;\r\n font-size: 0.95rem;\r\n}\r\n\r\n.sg-rating-lg {\r\n padding: 0.5rem 0.8rem;\r\n gap: 0.65rem;\r\n}\r\n\r\n.sg-rating-lg .sg-rating-star {\r\n width: 2.25rem;\r\n height: 2.25rem;\r\n font-size: 1.45rem;\r\n}\r\n\r\n.sg-rating-lg .sg-rating-score {\r\n min-width: 2.4rem;\r\n font-size: var(--sg-text-sm);\r\n padding: 0.3rem 0.55rem;\r\n}\r\n\r\n/* Looks */\r\n.sg-rating-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n border-radius: var(--sg-radius-md);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-rating-look-outline {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-rating-on) 40%, var(--sg-border-subtle));\r\n box-shadow: none;\r\n}\r\n\r\n.sg-rating-look-outline .sg-rating-svg-path-off {\r\n fill: transparent;\r\n stroke: color-mix(in srgb, var(--sg-fg-muted) 45%, transparent);\r\n stroke-width: 1.4;\r\n}\r\n\r\n.sg-rating-look-glass {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 48%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-fg) 12%, var(--sg-border-subtle));\r\n backdrop-filter: blur(10px);\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, #fff 50%, transparent),\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n}\r\n\r\n.sg-rating-look-glow {\r\n border-color: color-mix(in srgb, var(--sg-rating-on) 40%, transparent);\r\n box-shadow:\r\n 0 0 0 3px color-mix(in srgb, var(--sg-rating-on) 16%, transparent),\r\n inset 0 1px 0 color-mix(in srgb, #fff 50%, transparent);\r\n}\r\n\r\n.sg-rating-look-glow .sg-rating-star-active .sg-rating-svg-path-on {\r\n filter: drop-shadow(0 0 6px color-mix(in srgb, var(--sg-rating-on) 65%, transparent));\r\n}\r\n\r\n.sg-rating-static .sg-rating-star:hover {\r\n transform: none;\r\n background: transparent;\r\n}\r\n\r\n.sg-rating-static .sg-rating-star-active:hover {\r\n background: color-mix(in srgb, var(--sg-rating-on) 14%, transparent);\r\n}\r\n\r\n.sg-rating[data-disabled=\"true\"] {\r\n opacity: 0.55;\r\n pointer-events: none;\r\n}\r\n\r\n/* —— AspectRatio —— */\r\n.sg-aspect-ratio {\r\n position: relative;\r\n width: 100%;\r\n overflow: hidden;\r\n}\r\n\r\n.sg-aspect-ratio-content {\r\n position: absolute;\r\n inset: 0;\r\n width: 100%;\r\n height: 100%;\r\n}\r\n\r\n.sg-aspect-ratio-content > * {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .sg-spinner-visual,\r\n .sg-skeleton,\r\n .sg-progress-indeterminate .sg-progress-fill,\r\n .sg-progress-look-striped .sg-progress-fill,\r\n .sg-status-dot-pulse::before,\r\n .sg-circular-progress-indeterminate .sg-circular-progress-svg {\r\n animation: none;\r\n }\r\n\r\n .sg-progress-fill,\r\n .sg-circular-progress-fill,\r\n .sg-meter-fill,\r\n .sg-rating-star,\r\n .sg-time-colon {\r\n transition: none;\r\n animation: none;\r\n }\r\n\r\n .sg-rating-star:hover:not(:disabled) {\r\n transform: none;\r\n }\r\n}\r\n\r\n/* —— RangeSlider (dual thumb on Slider track language) —— */\r\n.sg-range-slider {\r\n position: relative;\r\n height: var(--sg-touch-min);\r\n display: flex;\r\n align-items: center;\r\n}\r\n\r\n.sg-range-slider-track {\r\n position: absolute;\r\n left: 0;\r\n right: 0;\r\n height: 0.45rem;\r\n border-radius: var(--sg-radius-pill);\r\n background: color-mix(in srgb, var(--sg-fg-muted) 14%, var(--sg-surface-solid));\r\n pointer-events: none;\r\n}\r\n\r\n.sg-range-slider-track::after {\r\n content: \"\";\r\n position: absolute;\r\n top: 0;\r\n bottom: 0;\r\n left: var(--sg-range-left, 0%);\r\n width: calc(var(--sg-range-right, 100%) - var(--sg-range-left, 0%));\r\n border-radius: inherit;\r\n background: var(--sg-accent);\r\n}\r\n\r\n.sg-range-slider-input {\r\n position: absolute;\r\n left: 0;\r\n width: 100%;\r\n margin: 0;\r\n background: transparent !important;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-range-slider-input::-webkit-slider-thumb {\r\n pointer-events: auto;\r\n position: relative;\r\n z-index: 2;\r\n}\r\n\r\n.sg-range-slider-input::-moz-range-thumb {\r\n pointer-events: auto;\r\n position: relative;\r\n z-index: 2;\r\n}\r\n\r\n.sg-range-slider-max {\r\n z-index: 3;\r\n}\r\n\r\n/* —— ToggleGroup —— */\r\n.sg-toggle-group {\r\n display: inline-flex;\r\n flex-wrap: wrap;\r\n gap: 0.4rem;\r\n vertical-align: middle;\r\n}\r\n\r\n.sg-toggle-group-block {\r\n display: flex;\r\n}\r\n\r\n.sg-toggle-group-block .sg-toggle-group-item {\r\n flex: 1;\r\n}\r\n\r\n.sg-toggle-group-item {\r\n margin: 0;\r\n padding: 0.45rem 0.85rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: 1px solid color-mix(in srgb, var(--sg-accent) 18%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-fg-muted) 6%, var(--sg-surface-solid));\r\n color: var(--sg-fg-muted);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n line-height: 1.2;\r\n cursor: pointer;\r\n transition:\r\n background var(--sg-duration-fast) var(--sg-ease-out),\r\n color var(--sg-duration-fast) var(--sg-ease-out),\r\n border-color var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-toggle-group-item:hover:not(:disabled):not(.sg-toggle-group-item-on) {\r\n color: var(--sg-fg);\r\n border-color: color-mix(in srgb, var(--sg-accent) 30%, var(--sg-border-subtle));\r\n}\r\n\r\n.sg-toggle-group-item-on {\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-toggle-group-item:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-toggle-group-item:disabled {\r\n opacity: 0.45;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-toggle-group-sm .sg-toggle-group-item {\r\n padding: 0.3rem 0.65rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-toggle-group-lg .sg-toggle-group-item {\r\n padding: 0.55rem 1.05rem;\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-toggle-group-look-solid .sg-toggle-group-item {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-toggle-group-look-outline .sg-toggle-group-item {\r\n background: transparent;\r\n}\r\n\r\n.sg-toggle-group-look-outline .sg-toggle-group-item-on {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n border-color: var(--sg-accent);\r\n}\r\n\r\n.sg-toggle-group-look-glass .sg-toggle-group-item {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 50%, transparent);\r\n backdrop-filter: blur(8px);\r\n}\r\n\r\n/* —— CountBadge —— */\r\n.sg-count-badge {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-width: 1.25rem;\r\n height: 1.25rem;\r\n padding: 0 0.35rem;\r\n border-radius: 999px;\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n border: 1px solid color-mix(in srgb, var(--sg-accent) 25%, transparent);\r\n font-size: 0.7rem;\r\n font-weight: var(--sg-font-weight-semibold);\r\n font-variant-numeric: tabular-nums;\r\n line-height: 1;\r\n vertical-align: middle;\r\n}\r\n\r\n.sg-count-badge-sm {\r\n min-width: 1rem;\r\n height: 1rem;\r\n font-size: 0.6rem;\r\n padding: 0 0.25rem;\r\n}\r\n\r\n.sg-count-badge-lg {\r\n min-width: 1.5rem;\r\n height: 1.5rem;\r\n font-size: 0.8rem;\r\n padding: 0 0.45rem;\r\n}\r\n\r\n.sg-count-badge-look-solid {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n border-color: transparent;\r\n}\r\n\r\n.sg-count-badge-look-outline {\r\n background: transparent;\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-count-badge-look-danger {\r\n background: color-mix(in srgb, var(--sg-danger) 14%, transparent);\r\n color: var(--sg-danger);\r\n border-color: color-mix(in srgb, var(--sg-danger) 30%, transparent);\r\n}\r\n\r\n/* —— ColorSwatch / ColorInput —— */\r\n.sg-swatch {\r\n --sg-swatch-color: #7c3aed;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 2rem;\r\n height: 2rem;\r\n margin: 0;\r\n padding: 0.2rem;\r\n border-radius: 50%;\r\n border: 1px solid color-mix(in srgb, var(--sg-fg) 12%, transparent);\r\n background: var(--sg-surface-solid);\r\n cursor: pointer;\r\n box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 40%, transparent);\r\n}\r\n\r\n.sg-swatch-fill {\r\n display: block;\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 50%;\r\n background: var(--sg-swatch-color);\r\n box-shadow: inset 0 0 0 1px color-mix(in srgb, #000 12%, transparent);\r\n}\r\n\r\n.sg-swatch-sm {\r\n width: 1.5rem;\r\n height: 1.5rem;\r\n}\r\n\r\n.sg-swatch-lg {\r\n width: 2.5rem;\r\n height: 2.5rem;\r\n}\r\n\r\n.sg-swatch-selected {\r\n box-shadow:\r\n 0 0 0 2px var(--sg-surface-solid),\r\n 0 0 0 4px var(--sg-accent);\r\n}\r\n\r\n.sg-swatch:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-swatch-look-outline {\r\n background: transparent;\r\n}\r\n\r\n.sg-swatch-look-glass {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 50%, transparent);\r\n backdrop-filter: blur(6px);\r\n}\r\n\r\n.sg-color-input {\r\n display: flex;\r\n align-items: center;\r\n gap: 0.5rem;\r\n padding: 0.3rem 0.45rem 0.3rem 0.35rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: 1px solid color-mix(in srgb, var(--sg-accent) 18%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-surface-solid) 85%, var(--sg-accent-soft));\r\n box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 45%, transparent);\r\n}\r\n\r\n.sg-color-input-swatch {\r\n position: relative;\r\n width: 2rem;\r\n height: 2rem;\r\n border-radius: 50%;\r\n overflow: hidden;\r\n flex-shrink: 0;\r\n border: 1px solid var(--sg-border-subtle);\r\n cursor: pointer;\r\n}\r\n\r\n.sg-color-input-native {\r\n position: absolute;\r\n inset: -20%;\r\n width: 140%;\r\n height: 140%;\r\n border: 0;\r\n padding: 0;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-color-input-text {\r\n flex: 1;\r\n min-width: 0;\r\n margin: 0;\r\n padding: 0.35rem 0.4rem;\r\n border: 0;\r\n background: transparent;\r\n color: var(--sg-fg);\r\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\r\n font-size: var(--sg-text-sm);\r\n outline: none;\r\n}\r\n\r\n.sg-color-input-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-radius: var(--sg-radius-md);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-color-input-look-outline {\r\n background: transparent;\r\n box-shadow: none;\r\n}\r\n\r\n.sg-color-input-look-glass {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 50%, transparent);\r\n backdrop-filter: blur(8px);\r\n}\r\n\r\n.sg-color-input[data-invalid=\"true\"] {\r\n border-color: var(--sg-danger);\r\n}\r\n\r\n/* —— Highlight / Truncate / LiveRegion —— */\r\n.sg-highlight {\r\n padding: 0.05em 0.2em;\r\n border-radius: 0.25em;\r\n background: color-mix(in srgb, var(--sg-warning) 28%, transparent);\r\n color: inherit;\r\n}\r\n\r\n.sg-highlight-look-solid {\r\n background: var(--sg-warning);\r\n color: #1a1200;\r\n}\r\n\r\n.sg-highlight-look-accent {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-highlight-look-warning {\r\n background: color-mix(in srgb, var(--sg-warning) 40%, transparent);\r\n}\r\n\r\n.sg-truncate {\r\n display: inline-block;\r\n max-width: 100%;\r\n overflow: hidden;\r\n}\r\n\r\n.sg-truncate-1 {\r\n white-space: nowrap;\r\n text-overflow: ellipsis;\r\n}\r\n\r\n.sg-truncate-2,\r\n.sg-truncate-3,\r\n.sg-truncate-4 {\r\n display: -webkit-box;\r\n -webkit-box-orient: vertical;\r\n overflow: hidden;\r\n}\r\n\r\n.sg-truncate-2 {\r\n -webkit-line-clamp: 2;\r\n line-clamp: 2;\r\n}\r\n\r\n.sg-truncate-3 {\r\n -webkit-line-clamp: 3;\r\n line-clamp: 3;\r\n}\r\n\r\n.sg-truncate-4 {\r\n -webkit-line-clamp: 4;\r\n line-clamp: 4;\r\n}\r\n\r\n/* —— NativeDateInput (custom Softglass YYYY-MM-DD — not OS picker) —— */\r\n.sg-ndate {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.65rem;\r\n padding: 0.4rem 0.6rem 0.4rem 0.45rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: 1px solid color-mix(in srgb, var(--sg-accent) 20%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-surface-solid) 78%, var(--sg-accent-soft));\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, #fff 55%, transparent),\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n vertical-align: middle;\r\n user-select: none;\r\n}\r\n\r\n.sg-ndate-block {\r\n display: flex;\r\n width: 100%;\r\n}\r\n\r\n.sg-ndate[data-disabled=\"true\"] {\r\n opacity: 0.55;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-ndate[data-invalid=\"true\"] {\r\n border-color: var(--sg-danger);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-danger) 20%, transparent);\r\n}\r\n\r\n.sg-ndate-glyph {\r\n display: inline-flex;\r\n flex-direction: column;\r\n align-items: stretch;\r\n width: 2rem;\r\n height: 2rem;\r\n border-radius: 0.45rem;\r\n overflow: hidden;\r\n flex-shrink: 0;\r\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sg-accent) 25%, transparent);\r\n background: var(--sg-surface-solid);\r\n}\r\n\r\n.sg-ndate-glyph-top {\r\n height: 0.45rem;\r\n background: var(--sg-accent);\r\n}\r\n\r\n.sg-ndate-glyph-body {\r\n flex: 1;\r\n display: grid;\r\n grid-template-columns: 1fr 1fr;\r\n gap: 0.12rem;\r\n padding: 0.22rem;\r\n background: color-mix(in srgb, var(--sg-accent-soft) 55%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-ndate-glyph-dot {\r\n border-radius: 2px;\r\n background: color-mix(in srgb, var(--sg-accent) 45%, transparent);\r\n}\r\n\r\n.sg-ndate-segments {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.15rem;\r\n}\r\n\r\n.sg-ndate-segment {\r\n display: inline-flex;\r\n flex-direction: column;\r\n align-items: center;\r\n gap: 0.05rem;\r\n}\r\n\r\n.sg-ndate-step {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 1.7rem;\r\n height: 0.85rem;\r\n margin: 0;\r\n padding: 0;\r\n border: 0;\r\n border-radius: 999px;\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n font-size: 0.55rem;\r\n line-height: 1;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-ndate-step:hover:not(:disabled) {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-ndate-value {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-width: 2rem;\r\n height: 1.85rem;\r\n margin: 0;\r\n padding: 0 0.35rem;\r\n border: 0;\r\n border-radius: var(--sg-radius-sm);\r\n background: var(--sg-surface-solid);\r\n box-shadow: inset 0 0 0 1px var(--sg-border-subtle);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: 0.95rem;\r\n font-weight: var(--sg-font-weight-semibold);\r\n font-variant-numeric: tabular-nums;\r\n line-height: 1;\r\n cursor: default;\r\n}\r\n\r\n.sg-ndate-value-month {\r\n min-width: 2.6rem;\r\n letter-spacing: 0.02em;\r\n}\r\n\r\n.sg-ndate-value-year {\r\n min-width: 3rem;\r\n}\r\n\r\n.sg-ndate-value:focus-visible {\r\n outline: none;\r\n box-shadow:\r\n inset 0 0 0 1px var(--sg-accent),\r\n 0 0 0 3px var(--sg-ring-soft);\r\n}\r\n\r\n.sg-ndate-sep {\r\n color: var(--sg-fg-muted);\r\n font-weight: var(--sg-font-weight-semibold);\r\n opacity: 0.55;\r\n padding-bottom: 0.1rem;\r\n}\r\n\r\n.sg-ndate-sm .sg-ndate-glyph {\r\n width: 1.7rem;\r\n height: 1.7rem;\r\n}\r\n\r\n.sg-ndate-sm .sg-ndate-value {\r\n height: 1.55rem;\r\n font-size: 0.82rem;\r\n min-width: 1.7rem;\r\n}\r\n\r\n.sg-ndate-lg .sg-ndate-glyph {\r\n width: 2.35rem;\r\n height: 2.35rem;\r\n}\r\n\r\n.sg-ndate-lg .sg-ndate-value {\r\n height: 2.15rem;\r\n font-size: 1.1rem;\r\n min-width: 2.35rem;\r\n}\r\n\r\n.sg-ndate-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n border-radius: var(--sg-radius-md);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-ndate-look-outline {\r\n background: transparent;\r\n border-color: color-mix(in srgb, var(--sg-accent) 40%, var(--sg-border-subtle));\r\n box-shadow: none;\r\n}\r\n\r\n.sg-ndate-look-outline .sg-ndate-value {\r\n background: transparent;\r\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sg-accent) 28%, transparent);\r\n}\r\n\r\n.sg-ndate-look-glass {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 48%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-fg) 12%, var(--sg-border-subtle));\r\n backdrop-filter: blur(10px);\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, #fff 50%, transparent),\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n}\r\n\r\n/* —— LiveRegion (visible Softglass status banner) —— */\r\n.sg-live-region-visible {\r\n display: flex;\r\n align-items: flex-start;\r\n gap: 0.65rem;\r\n width: 100%;\r\n padding: 0.7rem 0.9rem;\r\n border-radius: var(--sg-radius-md);\r\n border: 1px solid color-mix(in srgb, var(--sg-accent) 22%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-accent-soft) 70%, var(--sg-surface-solid));\r\n box-shadow:\r\n inset 0 1px 0 color-mix(in srgb, #fff 45%, transparent),\r\n 0 1px 2px color-mix(in srgb, var(--sg-fg) 5%, transparent);\r\n color: var(--sg-fg);\r\n font-size: var(--sg-text-sm);\r\n line-height: 1.4;\r\n}\r\n\r\n.sg-live-region-marker {\r\n flex-shrink: 0;\r\n width: 0.5rem;\r\n height: 0.5rem;\r\n margin-top: 0.35rem;\r\n border-radius: 50%;\r\n background: var(--sg-accent);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-accent) 22%, transparent);\r\n}\r\n\r\n.sg-live-region-body {\r\n min-width: 0;\r\n flex: 1;\r\n display: grid;\r\n gap: 0.15rem;\r\n}\r\n\r\n.sg-live-region-title {\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight, -0.01em);\r\n}\r\n\r\n.sg-live-region-message {\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-live-look-solid {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-live-look-glass {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 50%, transparent);\r\n border-color: color-mix(in srgb, var(--sg-fg) 12%, transparent);\r\n backdrop-filter: blur(10px);\r\n}\r\n\r\n.sg-live-look-info {\r\n border-color: color-mix(in srgb, var(--sg-accent) 35%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-accent-soft) 80%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-live-look-info .sg-live-region-marker {\r\n background: var(--sg-accent);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-accent) 22%, transparent);\r\n}\r\n\r\n.sg-live-look-success {\r\n border-color: color-mix(in srgb, var(--sg-success) 35%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-success) 12%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-live-look-success .sg-live-region-marker {\r\n background: var(--sg-success);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-success) 22%, transparent);\r\n}\r\n\r\n.sg-live-look-success .sg-live-region-message {\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-live-look-warning {\r\n border-color: color-mix(in srgb, var(--sg-warning) 40%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-warning) 14%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-live-look-warning .sg-live-region-marker {\r\n background: var(--sg-warning);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-warning) 22%, transparent);\r\n}\r\n\r\n.sg-live-look-danger {\r\n border-color: color-mix(in srgb, var(--sg-danger) 40%, var(--sg-border-subtle));\r\n background: color-mix(in srgb, var(--sg-danger) 12%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-live-look-danger .sg-live-region-marker {\r\n background: var(--sg-danger);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-danger) 22%, transparent);\r\n}\r\n\r\n/* —— Text / Heading —— */\r\n.sg-text {\r\n margin: 0;\r\n color: var(--sg-fg);\r\n font-size: var(--sg-text-base);\r\n line-height: var(--sg-leading-normal, 1.5);\r\n}\r\n\r\n.sg-text-muted {\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-text-subtle {\r\n color: var(--sg-fg-subtle, var(--sg-fg-muted));\r\n}\r\n\r\n.sg-text-accent {\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-text-danger {\r\n color: var(--sg-danger);\r\n}\r\n\r\n.sg-text-size-xs {\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-text-size-sm {\r\n font-size: var(--sg-text-sm);\r\n}\r\n\r\n.sg-text-size-lg {\r\n font-size: var(--sg-text-lg);\r\n}\r\n\r\n.sg-text-size-xl {\r\n font-size: var(--sg-text-xl, 1.25rem);\r\n}\r\n\r\n.sg-text-weight-medium {\r\n font-weight: var(--sg-font-weight-medium);\r\n}\r\n\r\n.sg-text-weight-semibold {\r\n font-weight: var(--sg-font-weight-semibold);\r\n}\r\n\r\n.sg-text-weight-bold {\r\n font-weight: var(--sg-font-weight-bold, 700);\r\n}\r\n\r\n.sg-heading {\r\n margin: 0;\r\n color: var(--sg-fg);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight, -0.02em);\r\n line-height: 1.25;\r\n}\r\n\r\n.sg-heading-sm {\r\n font-size: var(--sg-text-base);\r\n}\r\n\r\n.sg-heading-lg {\r\n font-size: var(--sg-text-xl, 1.25rem);\r\n}\r\n\r\n.sg-heading-xl {\r\n font-size: 1.5rem;\r\n}\r\n\r\n.sg-heading-2xl {\r\n font-size: 1.85rem;\r\n}\r\n\r\n/* Chip variants (filter / check) */\r\n.sg-chip-variant-filter {\r\n border-style: dashed;\r\n}\r\n\r\n.sg-chip-variant-check .sg-chip-check {\r\n font-size: 0.75em;\r\n font-weight: 700;\r\n}\r\n\r\n/* —— Collapsible (v1.4a) — cheap frost; glass blur only when look=glass —— */\r\n.sg-collapsible {\r\n --sg-disclosure-pad-x: 1.05rem;\r\n --sg-disclosure-pad-y: 0.85rem;\r\n display: flex;\r\n flex-direction: column;\r\n width: 100%;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n overflow: clip;\r\n contain: layout style;\r\n}\r\n\r\n.sg-collapsible[data-look=\"solid\"] {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-collapsible[data-look=\"glass\"] {\r\n /* Frost-first (cheap). Blur only if transparency allowed. */\r\n background: var(--sg-surface-frost-strong, var(--sg-surface-frost));\r\n border-color: color-mix(in srgb, var(--sg-border-frost) 80%, var(--sg-accent) 12%);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n@media (prefers-reduced-transparency: no-preference) {\r\n .sg-collapsible[data-look=\"glass\"] {\r\n background: color-mix(in srgb, var(--sg-surface-glass) 92%, transparent);\r\n backdrop-filter: blur(12px) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(12px) saturate(var(--sg-glass-saturate));\r\n }\r\n}\r\n\r\n.sg-collapsible[data-look=\"outline\"] {\r\n background: transparent;\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-collapsible[data-look=\"ghost\"] {\r\n background: transparent;\r\n border-color: transparent;\r\n border-radius: var(--sg-radius-md);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-collapsible-trigger {\r\n appearance: none;\r\n position: relative;\r\n display: flex;\r\n width: 100%;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n margin: 0;\r\n padding: var(--sg-disclosure-pad-y) var(--sg-disclosure-pad-x);\r\n border: 0;\r\n background: transparent;\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n text-align: left;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-collapsible-trigger::before {\r\n content: \"\";\r\n position: absolute;\r\n left: 0.45rem;\r\n top: 50%;\r\n width: 3px;\r\n height: 0;\r\n border-radius: 999px;\r\n background: var(--sg-accent);\r\n transform: translateY(-50%);\r\n opacity: 0;\r\n}\r\n\r\n.sg-collapsible-trigger[data-state=\"open\"]::before {\r\n height: 1.15rem;\r\n opacity: 1;\r\n}\r\n\r\n.sg-collapsible[data-look=\"ghost\"] .sg-collapsible-trigger::before {\r\n left: 0;\r\n}\r\n\r\n.sg-collapsible-trigger:hover:not(:disabled) {\r\n background: color-mix(in srgb, var(--sg-accent-soft) 70%, transparent);\r\n}\r\n\r\n.sg-collapsible-trigger[data-state=\"open\"] {\r\n background: color-mix(in srgb, var(--sg-accent-soft) 55%, transparent);\r\n}\r\n\r\n.sg-collapsible-trigger:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: -2px;\r\n z-index: 1;\r\n}\r\n\r\n.sg-collapsible-trigger:disabled {\r\n opacity: 0.5;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-collapsible-trigger-label {\r\n flex: 1;\r\n min-width: 0;\r\n padding-left: 0.35rem;\r\n}\r\n\r\n.sg-collapsible-chevron-wrap {\r\n flex-shrink: 0;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 1.75rem;\r\n height: 1.75rem;\r\n border-radius: 999px;\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: color-mix(in srgb, var(--sg-surface-solid) 70%, var(--sg-accent-soft));\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-collapsible-trigger:hover:not(:disabled) .sg-collapsible-chevron-wrap {\r\n border-color: color-mix(in srgb, var(--sg-accent) 28%, var(--sg-border-frost));\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-collapsible-chevron-wrap[data-open=\"true\"] {\r\n transform: rotate(180deg);\r\n color: var(--sg-accent-fg);\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n}\r\n\r\n.sg-collapsible-chevron {\r\n width: 0.95rem;\r\n height: 0.95rem;\r\n display: block;\r\n}\r\n\r\n.sg-collapsible-panel[hidden] {\r\n display: none;\r\n}\r\n\r\n.sg-collapsible-content {\r\n padding: 0.7rem var(--sg-disclosure-pad-x) 1.05rem;\r\n padding-left: calc(var(--sg-disclosure-pad-x) + 0.35rem);\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-sm);\r\n line-height: 1.6;\r\n border-top: var(--sg-border-width) solid\r\n color-mix(in srgb, var(--sg-border-frost) 85%, transparent);\r\n}\r\n\r\n.sg-collapsible[data-look=\"ghost\"] .sg-collapsible-content {\r\n border-top-color: transparent;\r\n}\r\n\r\n/* Fade only when requested — no layout animation */\r\n.sg-collapsible[data-motion=\"fade\"] .sg-collapsible-panel[data-state=\"open\"] .sg-collapsible-content {\r\n animation: sg-disclosure-fade var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n@keyframes sg-disclosure-fade {\r\n from { opacity: 0; }\r\n to { opacity: 1; }\r\n}\r\n\r\n/* —— Accordion (v1.4a) —— */\r\n.sg-accordion {\r\n --sg-disclosure-pad-x: 1.05rem;\r\n --sg-disclosure-pad-y: 0.9rem;\r\n display: flex;\r\n flex-direction: column;\r\n width: 100%;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n overflow: clip;\r\n contain: layout style;\r\n}\r\n\r\n.sg-accordion[data-look=\"solid\"] {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-accordion[data-look=\"glass\"] {\r\n background: var(--sg-surface-frost-strong, var(--sg-surface-frost));\r\n border-color: color-mix(in srgb, var(--sg-border-frost) 80%, var(--sg-accent) 12%);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n@media (prefers-reduced-transparency: no-preference) {\r\n .sg-accordion[data-look=\"glass\"] {\r\n background: color-mix(in srgb, var(--sg-surface-glass) 92%, transparent);\r\n backdrop-filter: blur(12px) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(12px) saturate(var(--sg-glass-saturate));\r\n }\r\n}\r\n\r\n.sg-accordion[data-look=\"outline\"] {\r\n background: transparent;\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-accordion[data-look=\"ghost\"] {\r\n background: transparent;\r\n border-color: transparent;\r\n border-radius: 0;\r\n box-shadow: none;\r\n}\r\n\r\n.sg-accordion-item {\r\n contain: layout style;\r\n}\r\n\r\n.sg-accordion-item + .sg-accordion-item {\r\n border-top: var(--sg-border-width) solid\r\n color-mix(in srgb, var(--sg-border-frost) 90%, transparent);\r\n}\r\n\r\n.sg-accordion[data-look=\"ghost\"] .sg-accordion-item + .sg-accordion-item {\r\n border-top-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-accordion-heading {\r\n margin: 0;\r\n font-size: inherit;\r\n font-weight: inherit;\r\n}\r\n\r\n.sg-accordion-trigger {\r\n appearance: none;\r\n position: relative;\r\n display: flex;\r\n width: 100%;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n margin: 0;\r\n padding: var(--sg-disclosure-pad-y) var(--sg-disclosure-pad-x);\r\n border: 0;\r\n background: transparent;\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n text-align: left;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-accordion-trigger::before {\r\n content: \"\";\r\n position: absolute;\r\n left: 0.45rem;\r\n top: 50%;\r\n width: 3px;\r\n height: 0;\r\n border-radius: 999px;\r\n background: var(--sg-accent);\r\n transform: translateY(-50%);\r\n opacity: 0;\r\n}\r\n\r\n.sg-accordion-trigger[data-state=\"open\"]::before {\r\n height: 1.15rem;\r\n opacity: 1;\r\n}\r\n\r\n.sg-accordion[data-look=\"ghost\"] .sg-accordion-trigger::before {\r\n left: 0;\r\n}\r\n\r\n.sg-accordion-trigger:hover:not(:disabled) {\r\n background: color-mix(in srgb, var(--sg-accent-soft) 70%, transparent);\r\n}\r\n\r\n.sg-accordion-trigger[data-state=\"open\"] {\r\n background: color-mix(in srgb, var(--sg-accent-soft) 55%, transparent);\r\n}\r\n\r\n.sg-accordion-trigger:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: -2px;\r\n z-index: 1;\r\n}\r\n\r\n.sg-accordion-trigger:disabled {\r\n opacity: 0.5;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-accordion-trigger-label {\r\n flex: 1;\r\n min-width: 0;\r\n padding-left: 0.35rem;\r\n}\r\n\r\n.sg-accordion-chevron-wrap {\r\n flex-shrink: 0;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 1.75rem;\r\n height: 1.75rem;\r\n border-radius: 999px;\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: color-mix(in srgb, var(--sg-surface-solid) 70%, var(--sg-accent-soft));\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n color: var(--sg-fg-muted);\r\n}\r\n\r\n.sg-accordion-trigger:hover:not(:disabled) .sg-accordion-chevron-wrap {\r\n border-color: color-mix(in srgb, var(--sg-accent) 28%, var(--sg-border-frost));\r\n color: var(--sg-accent);\r\n}\r\n\r\n.sg-accordion-chevron-wrap[data-open=\"true\"] {\r\n transform: rotate(180deg);\r\n color: var(--sg-accent-fg);\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n}\r\n\r\n.sg-accordion-chevron {\r\n width: 0.95rem;\r\n height: 0.95rem;\r\n display: block;\r\n}\r\n\r\n.sg-accordion-panel[hidden] {\r\n display: none;\r\n}\r\n\r\n.sg-accordion-content {\r\n padding: 0 var(--sg-disclosure-pad-x) 1.05rem;\r\n padding-left: calc(var(--sg-disclosure-pad-x) + 0.35rem);\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-sm);\r\n line-height: 1.6;\r\n}\r\n\r\n.sg-accordion[data-motion=\"fade\"] .sg-accordion-panel[data-state=\"open\"] .sg-accordion-content {\r\n animation: sg-disclosure-fade var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n/* —— Breadcrumb (v1.4a) —— */\r\n.sg-breadcrumb {\r\n min-width: 0;\r\n width: fit-content;\r\n max-width: 100%;\r\n}\r\n\r\n.sg-breadcrumb-list {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n gap: 0.2rem 0.15rem;\r\n margin: 0;\r\n padding: 0;\r\n list-style: none;\r\n}\r\n\r\n.sg-breadcrumb[data-look=\"soft\"] {\r\n padding: 0.35rem 0.55rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-breadcrumb[data-look=\"pill\"] .sg-breadcrumb-list {\r\n gap: 0.35rem;\r\n}\r\n\r\n.sg-breadcrumb-item {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.35rem;\r\n min-width: 0;\r\n font-size: var(--sg-text-sm);\r\n line-height: 1.4;\r\n}\r\n\r\n.sg-breadcrumb[data-size=\"sm\"] .sg-breadcrumb-item {\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-breadcrumb-separator {\r\n color: var(--sg-fg-subtle, var(--sg-fg-muted));\r\n opacity: 0.7;\r\n user-select: none;\r\n font-weight: var(--sg-font-weight-medium);\r\n}\r\n\r\n.sg-breadcrumb-link.sg-link {\r\n font-size: inherit;\r\n font-weight: var(--sg-font-weight-medium);\r\n text-decoration: none;\r\n border-radius: var(--sg-radius-sm);\r\n padding: 0.15rem 0.3rem;\r\n margin: -0.15rem -0.15rem;\r\n}\r\n\r\n.sg-breadcrumb-link.sg-link:hover {\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-breadcrumb-page {\r\n color: var(--sg-fg);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n padding: 0.15rem 0.3rem;\r\n}\r\n\r\n.sg-breadcrumb[data-look=\"pill\"] .sg-breadcrumb-separator {\r\n opacity: 0.45;\r\n font-size: 0.85em;\r\n}\r\n\r\n.sg-breadcrumb[data-look=\"pill\"] .sg-breadcrumb-link.sg-link,\r\n.sg-breadcrumb[data-look=\"pill\"] .sg-breadcrumb-page {\r\n display: inline-flex;\r\n align-items: center;\r\n min-height: 1.7rem;\r\n padding: 0.2rem 0.7rem;\r\n margin: 0;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-breadcrumb[data-look=\"pill\"] .sg-breadcrumb-link.sg-link:hover {\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 22%, var(--sg-border-frost));\r\n}\r\n\r\n.sg-breadcrumb[data-look=\"pill\"] .sg-breadcrumb-page {\r\n color: var(--sg-accent-fg);\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n}\r\n\r\n/* —— Pagination (v1.4a) —— */\r\n.sg-pagination {\r\n width: fit-content;\r\n max-width: 100%;\r\n contain: layout style;\r\n}\r\n\r\n.sg-pagination-list {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n gap: 0.3rem;\r\n margin: 0;\r\n padding: 0;\r\n list-style: none;\r\n}\r\n\r\n.sg-pagination[data-look=\"soft\"] .sg-pagination-list,\r\n.sg-pagination[data-look=\"glass\"] .sg-pagination-list {\r\n gap: 0.2rem;\r\n padding: 0.3rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-pagination[data-look=\"glass\"] .sg-pagination-list {\r\n background: var(--sg-surface-frost-strong, var(--sg-surface-frost));\r\n border-color: color-mix(in srgb, var(--sg-border-frost) 75%, var(--sg-accent) 14%);\r\n}\r\n\r\n@media (prefers-reduced-transparency: no-preference) {\r\n .sg-pagination[data-look=\"glass\"] .sg-pagination-list {\r\n background: color-mix(in srgb, var(--sg-surface-glass) 88%, transparent);\r\n backdrop-filter: blur(10px) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(10px) saturate(var(--sg-glass-saturate));\r\n }\r\n}\r\n\r\n.sg-pagination-item {\r\n display: inline-flex;\r\n}\r\n\r\n.sg-pagination-btn {\r\n appearance: none;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-width: 2.35rem;\r\n min-height: 2.35rem;\r\n padding: 0 0.7rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid transparent;\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n letter-spacing: var(--sg-tracking-tight);\r\n font-variant-numeric: tabular-nums;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-pagination[data-size=\"sm\"] .sg-pagination-btn {\r\n min-width: 1.95rem;\r\n min-height: 1.95rem;\r\n padding: 0 0.5rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n\r\n.sg-pagination[data-look=\"solid\"] .sg-pagination-btn {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-pagination[data-look=\"ghost\"] .sg-pagination-btn {\r\n background: transparent;\r\n border-color: transparent;\r\n}\r\n\r\n.sg-pagination-btn:hover:not(:disabled):not([data-active=\"true\"]) {\r\n color: var(--sg-fg);\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 16%, transparent);\r\n}\r\n\r\n.sg-pagination-btn:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.sg-pagination-btn[data-active=\"true\"] {\r\n color: var(--sg-accent-fg);\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n font-weight: var(--sg-font-weight-semibold);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-pagination-btn[data-active=\"true\"]:hover:not(:disabled) {\r\n background: var(--sg-accent-hover);\r\n color: var(--sg-accent-fg);\r\n}\r\n\r\n.sg-pagination-btn:disabled {\r\n opacity: 0.42;\r\n cursor: not-allowed;\r\n}\r\n\r\n.sg-pagination-prev,\r\n.sg-pagination-next {\r\n min-width: auto;\r\n padding-inline: 0.9rem;\r\n font-weight: var(--sg-font-weight-semibold);\r\n}\r\n\r\n.sg-pagination-ellipsis {\r\n min-width: 1.4rem;\r\n justify-content: center;\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-sm);\r\n user-select: none;\r\n opacity: 0.75;\r\n}\r\n\r\n.sg-pagination-status {\r\n display: inline-flex;\r\n align-items: center;\r\n min-height: 2.35rem;\r\n padding: 0 0.75rem;\r\n color: var(--sg-fg-muted);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .sg-collapsible[data-motion=\"fade\"] .sg-collapsible-content,\r\n .sg-accordion[data-motion=\"fade\"] .sg-accordion-content {\r\n animation: none !important;\r\n }\r\n\r\n .sg-collapsible-chevron-wrap[data-open=\"true\"],\r\n .sg-accordion-chevron-wrap[data-open=\"true\"] {\r\n transition: none;\r\n }\r\n}\r\n\r\n/* —— EmptyState (v1.4b) —— */\r\n.sg-empty-state {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n text-align: center;\r\n gap: 0.65rem;\r\n width: 100%;\r\n padding: 2rem 1.5rem;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-solid);\r\n box-sizing: border-box;\r\n contain: layout style;\r\n}\r\n\r\n.sg-empty-state[data-look=\"soft\"] {\r\n background: var(--sg-surface-frost);\r\n border-color: var(--sg-border-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n\r\n.sg-empty-state[data-look=\"outline\"] {\r\n background: transparent;\r\n border-style: dashed;\r\n border-color: var(--sg-border-subtle);\r\n}\r\n\r\n.sg-empty-state[data-size=\"sm\"] {\r\n padding: 1.25rem 1rem;\r\n gap: 0.45rem;\r\n}\r\n\r\n.sg-empty-state[data-size=\"lg\"] {\r\n padding: 2.75rem 2rem;\r\n gap: 0.85rem;\r\n}\r\n\r\n.sg-empty-state-icon {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 3rem;\r\n height: 3rem;\r\n border-radius: var(--sg-radius-md);\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n font-size: 1.35rem;\r\n margin-bottom: 0.25rem;\r\n}\r\n\r\n.sg-empty-state[data-size=\"sm\"] .sg-empty-state-icon {\r\n width: 2.35rem;\r\n height: 2.35rem;\r\n font-size: 1.1rem;\r\n}\r\n\r\n.sg-empty-state-title {\r\n margin: 0;\r\n max-width: 28ch;\r\n font-size: var(--sg-text-lg);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n color: var(--sg-fg);\r\n line-height: var(--sg-leading-tight);\r\n}\r\n\r\n.sg-empty-state[data-size=\"sm\"] .sg-empty-state-title {\r\n font-size: var(--sg-text-md, var(--sg-text-base));\r\n}\r\n\r\n.sg-empty-state[data-size=\"lg\"] .sg-empty-state-title {\r\n font-size: var(--sg-text-xl);\r\n}\r\n\r\n.sg-empty-state-description {\r\n margin: 0;\r\n max-width: 42ch;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg-muted);\r\n line-height: 1.55;\r\n}\r\n\r\n.sg-empty-state-actions {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 0.55rem;\r\n margin-top: 0.55rem;\r\n}\r\n\r\n/* —— Sheet (v1.4b) —— */\r\n.sg-sheet-root {\r\n position: fixed;\r\n inset: 0;\r\n z-index: var(--sg-z-modal);\r\n isolation: isolate;\r\n box-sizing: border-box;\r\n}\r\n\r\n.sg-sheet-backdrop {\r\n position: absolute;\r\n inset: 0;\r\n border: 0;\r\n padding: 0;\r\n margin: 0;\r\n cursor: pointer;\r\n background: color-mix(in srgb, var(--sg-bg) 40%, rgba(15, 23, 42, 0.4));\r\n /* frost first — light blur only */\r\n backdrop-filter: blur(6px);\r\n -webkit-backdrop-filter: blur(6px);\r\n}\r\n\r\n.sg-sheet-root[data-state=\"open\"] .sg-sheet-backdrop {\r\n animation: sg-disclosure-fade var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-sheet-root[data-state=\"closed\"] .sg-sheet-backdrop {\r\n opacity: 0;\r\n transition: opacity var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-sheet-panel {\r\n position: absolute;\r\n z-index: 1;\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--sg-space-4);\r\n box-sizing: border-box;\r\n padding: var(--sg-space-5);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-solid);\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 16px 48px color-mix(in srgb, var(--sg-fg) 12%, transparent);\r\n outline: none;\r\n}\r\n\r\n.sg-sheet-panel[data-side=\"right\"] {\r\n top: 0;\r\n right: 0;\r\n bottom: 0;\r\n width: min(100%, 24rem);\r\n border-right: 0;\r\n border-radius: var(--sg-radius-lg) 0 0 var(--sg-radius-lg);\r\n}\r\n\r\n.sg-sheet-panel[data-side=\"left\"] {\r\n top: 0;\r\n left: 0;\r\n bottom: 0;\r\n width: min(100%, 24rem);\r\n border-left: 0;\r\n border-radius: 0 var(--sg-radius-lg) var(--sg-radius-lg) 0;\r\n}\r\n\r\n.sg-sheet-panel[data-side=\"bottom\"] {\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n width: 100%;\r\n max-height: min(80dvh, 32rem);\r\n border-bottom: 0;\r\n border-radius: var(--sg-radius-lg) var(--sg-radius-lg) 0 0;\r\n}\r\n\r\n/* Slide enter/exit */\r\n.sg-sheet-panel[data-motion=\"slide\"][data-side=\"right\"][data-state=\"open\"] {\r\n animation: sg-sheet-in-right var(--sg-duration-normal) var(--sg-ease-out);\r\n}\r\n.sg-sheet-panel[data-motion=\"slide\"][data-side=\"right\"][data-state=\"closed\"] {\r\n transform: translateX(100%);\r\n transition: transform var(--sg-duration-normal) var(--sg-ease-out);\r\n}\r\n\r\n.sg-sheet-panel[data-motion=\"slide\"][data-side=\"left\"][data-state=\"open\"] {\r\n animation: sg-sheet-in-left var(--sg-duration-normal) var(--sg-ease-out);\r\n}\r\n.sg-sheet-panel[data-motion=\"slide\"][data-side=\"left\"][data-state=\"closed\"] {\r\n transform: translateX(-100%);\r\n transition: transform var(--sg-duration-normal) var(--sg-ease-out);\r\n}\r\n\r\n.sg-sheet-panel[data-motion=\"slide\"][data-side=\"bottom\"][data-state=\"open\"] {\r\n animation: sg-sheet-in-bottom var(--sg-duration-normal) var(--sg-ease-out);\r\n}\r\n.sg-sheet-panel[data-motion=\"slide\"][data-side=\"bottom\"][data-state=\"closed\"] {\r\n transform: translateY(100%);\r\n transition: transform var(--sg-duration-normal) var(--sg-ease-out);\r\n}\r\n\r\n.sg-sheet-panel[data-motion=\"fade\"][data-state=\"open\"] {\r\n animation: sg-disclosure-fade var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n.sg-sheet-panel[data-motion=\"fade\"][data-state=\"closed\"] {\r\n opacity: 0;\r\n transition: opacity var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n@keyframes sg-sheet-in-right {\r\n from { transform: translateX(100%); }\r\n to { transform: translateX(0); }\r\n}\r\n@keyframes sg-sheet-in-left {\r\n from { transform: translateX(-100%); }\r\n to { transform: translateX(0); }\r\n}\r\n@keyframes sg-sheet-in-bottom {\r\n from { transform: translateY(100%); }\r\n to { transform: translateY(0); }\r\n}\r\n\r\n.sg-sheet-header {\r\n display: flex;\r\n align-items: flex-start;\r\n justify-content: space-between;\r\n gap: var(--sg-space-3);\r\n flex-shrink: 0;\r\n}\r\n\r\n.sg-sheet-heading {\r\n min-width: 0;\r\n}\r\n\r\n.sg-sheet-title {\r\n margin: 0;\r\n font-size: var(--sg-text-lg);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n line-height: var(--sg-leading-tight);\r\n}\r\n\r\n.sg-sheet-description {\r\n margin: 0.35rem 0 0;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg-muted);\r\n line-height: 1.5;\r\n}\r\n\r\n.sg-sheet-body {\r\n min-height: 0;\r\n flex: 1;\r\n overflow: auto;\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-sheet-footer {\r\n display: flex;\r\n flex-wrap: wrap;\r\n justify-content: flex-end;\r\n gap: var(--sg-space-3);\r\n flex-shrink: 0;\r\n padding-top: var(--sg-space-2);\r\n border-top: var(--sg-border-width) solid var(--sg-border-subtle);\r\n}\r\n\r\n.sg-sheet-close {\r\n flex-shrink: 0;\r\n}\r\n\r\n/* —— HoverCard (v1.4b) —— */\r\n.sg-hover-card-root {\r\n display: inline-flex;\r\n vertical-align: middle;\r\n}\r\n\r\n.sg-hover-card-trigger {\r\n display: inline-flex;\r\n max-width: 100%;\r\n}\r\n\r\n.sg-hover-card-content {\r\n z-index: var(--sg-z-popover, var(--sg-z-modal));\r\n min-width: 12rem;\r\n max-width: min(20rem, calc(100vw - 1.5rem));\r\n padding: 0.85rem 1rem;\r\n border-radius: var(--sg-radius-md);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 10px 28px color-mix(in srgb, var(--sg-fg) 10%, transparent);\r\n color: var(--sg-fg);\r\n font-size: var(--sg-text-sm);\r\n line-height: 1.5;\r\n box-sizing: border-box;\r\n contain: layout style;\r\n}\r\n\r\n.sg-hover-card-content[data-motion=\"scale\"][data-state=\"open\"] {\r\n animation: sg-hover-card-scale var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n.sg-hover-card-content[data-motion=\"fade\"][data-state=\"open\"] {\r\n animation: sg-disclosure-fade var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n.sg-hover-card-content[data-motion=\"slide-down\"][data-state=\"open\"] {\r\n animation: sg-hover-card-slide var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n.sg-hover-card-content[data-state=\"closed\"] {\r\n opacity: 0;\r\n transition: opacity var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n\r\n@keyframes sg-hover-card-scale {\r\n from { opacity: 0; transform: scale(0.96); }\r\n to { opacity: 1; transform: scale(1); }\r\n}\r\n@keyframes sg-hover-card-slide {\r\n from { opacity: 0; transform: translateY(-4px); }\r\n to { opacity: 1; transform: translateY(0); }\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .sg-sheet-backdrop,\r\n .sg-sheet-panel,\r\n .sg-hover-card-content {\r\n animation: none !important;\r\n transition: none !important;\r\n }\r\n}\r\n\r\n@media (prefers-reduced-transparency: reduce) {\r\n .sg-sheet-backdrop {\r\n backdrop-filter: none;\r\n -webkit-backdrop-filter: none;\r\n }\r\n .sg-hover-card-content {\r\n background: var(--sg-surface-solid);\r\n }\r\n}\r\n\r\n/* —— Stepper (v1.4c) — structural looks —— */\r\n.sg-stepper {\r\n --sg-stepper-progress: 0%;\r\n position: relative;\r\n width: 100%;\r\n contain: layout style;\r\n}\r\n.sg-stepper-track { display: none; }\r\n.sg-stepper[data-look=\"soft\"] {\r\n padding: 1rem 1.1rem 1.15rem;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n.sg-stepper[data-look=\"solid\"] {\r\n padding: 1rem 1.1rem;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-solid);\r\n}\r\n.sg-stepper[data-look=\"outline\"] { padding: 0.25rem 0; }\r\n\r\n/*\r\n * Dots: NO continuous track under the circles (gloss/frost bg used to show\r\n * the line through halos). Instead each gap is its own segment, inset so\r\n * it only sits *between* opaque markers.\r\n */\r\n.sg-stepper[data-look=\"dots\"] {\r\n --sg-stepper-dot: 2.5rem;\r\n --sg-stepper-track-h: 2px;\r\n --sg-stepper-pad-y: 0.35rem;\r\n --sg-stepper-count: 3;\r\n padding: var(--sg-stepper-pad-y) 0 0.65rem;\r\n}\r\n.sg-stepper[data-look=\"dots\"] .sg-stepper-trigger {\r\n padding-top: 0;\r\n padding-bottom: 0.5rem;\r\n gap: 0.55rem;\r\n}\r\n/* Hide the global track entirely for dots */\r\n.sg-stepper[data-look=\"dots\"] .sg-stepper-track {\r\n display: none !important;\r\n}\r\n/* Gap connector: from previous circle edge → this circle edge */\r\n.sg-stepper[data-look=\"dots\"] .sg-stepper-item:not(:first-child)::before {\r\n content: \"\";\r\n position: absolute;\r\n z-index: 0;\r\n pointer-events: none;\r\n height: var(--sg-stepper-track-h);\r\n top: calc(\r\n var(--sg-stepper-pad-y) + (var(--sg-stepper-dot) / 2) -\r\n (var(--sg-stepper-track-h) / 2)\r\n );\r\n /* Span previous half + this half, then inset by half a dot on each side */\r\n left: calc(-50% + (var(--sg-stepper-dot) / 2));\r\n width: calc(100% - var(--sg-stepper-dot));\r\n border-radius: 999px;\r\n background: color-mix(in srgb, var(--sg-border-subtle) 90%, var(--sg-fg) 6%);\r\n}\r\n/* Completed path segments (line leading into complete/current) */\r\n.sg-stepper[data-look=\"dots\"]\r\n .sg-stepper-item[data-status=\"complete\"]:not(:first-child)::before,\r\n.sg-stepper[data-look=\"dots\"]\r\n .sg-stepper-item[data-status=\"current\"]:not(:first-child)::before {\r\n background: var(--sg-accent);\r\n}\r\n.sg-stepper[data-look=\"pills\"] {\r\n padding: 0.35rem;\r\n border-radius: var(--sg-radius-pill);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n.sg-stepper[data-look=\"pills\"] .sg-stepper-list { gap: 0.25rem; }\r\n.sg-stepper[data-look=\"pills\"] .sg-stepper-item { flex: 1 1 auto; min-width: 0; }\r\n.sg-stepper[data-look=\"pills\"] .sg-stepper-trigger {\r\n flex-direction: row;\r\n gap: 0.45rem;\r\n padding: 0.45rem 0.75rem;\r\n border-radius: var(--sg-radius-pill);\r\n text-align: left;\r\n}\r\n.sg-stepper[data-look=\"pills\"] .sg-stepper-item[data-status=\"current\"] .sg-stepper-trigger {\r\n background: var(--sg-accent);\r\n color: var(--sg-accent-fg);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n.sg-stepper[data-look=\"pills\"] .sg-stepper-item[data-status=\"current\"] .sg-stepper-label,\r\n.sg-stepper[data-look=\"pills\"] .sg-stepper-item[data-status=\"current\"] .sg-stepper-description {\r\n color: var(--sg-accent-fg);\r\n}\r\n.sg-stepper[data-look=\"pills\"] .sg-stepper-item[data-status=\"current\"] .sg-stepper-indicator {\r\n background: color-mix(in srgb, #fff 22%, transparent);\r\n color: var(--sg-accent-fg);\r\n border-color: transparent;\r\n}\r\n.sg-stepper[data-look=\"pills\"] .sg-stepper-item[data-status=\"complete\"] .sg-stepper-trigger {\r\n background: var(--sg-accent-soft);\r\n}\r\n.sg-stepper[data-look=\"pills\"] .sg-stepper-description { display: none; }\r\n.sg-stepper[data-look=\"pills\"] .sg-stepper-indicator {\r\n width: 1.55rem;\r\n height: 1.55rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n.sg-stepper-list {\r\n position: relative;\r\n z-index: 1;\r\n display: flex;\r\n flex-wrap: nowrap;\r\n align-items: flex-start;\r\n gap: 0;\r\n margin: 0;\r\n padding: 0;\r\n list-style: none;\r\n width: 100%;\r\n}\r\n.sg-stepper[data-orientation=\"vertical\"] .sg-stepper-list {\r\n flex-direction: column;\r\n gap: 0.15rem;\r\n}\r\n.sg-stepper[data-orientation=\"vertical\"][data-look=\"dots\"] .sg-stepper-item:not(:first-child)::before {\r\n left: calc(var(--sg-stepper-dot) / 2 - var(--sg-stepper-track-h) / 2);\r\n width: var(--sg-stepper-track-h);\r\n top: calc(-50% + (var(--sg-stepper-dot) / 2));\r\n height: calc(100% - var(--sg-stepper-dot));\r\n}\r\n.sg-stepper-item {\r\n position: relative;\r\n display: flex;\r\n flex: 1 1 0;\r\n min-width: 0;\r\n justify-content: center;\r\n}\r\n.sg-stepper[data-orientation=\"vertical\"] .sg-stepper-item {\r\n flex: 0 0 auto;\r\n width: 100%;\r\n justify-content: flex-start;\r\n}\r\n.sg-stepper-trigger {\r\n appearance: none;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n gap: 0.5rem;\r\n width: 100%;\r\n margin: 0;\r\n padding: 0.2rem 0.4rem;\r\n border: 0;\r\n background: transparent;\r\n color: var(--sg-fg-muted);\r\n font-family: var(--sg-font-sans);\r\n text-align: center;\r\n cursor: default;\r\n border-radius: var(--sg-radius-md);\r\n}\r\n.sg-stepper[data-orientation=\"vertical\"] .sg-stepper-trigger {\r\n flex-direction: row;\r\n text-align: left;\r\n gap: 0.8rem;\r\n padding: 0.45rem 0.35rem;\r\n}\r\n.sg-stepper-trigger:not(:disabled) { cursor: pointer; }\r\n.sg-stepper-trigger:not(:disabled):hover .sg-stepper-indicator {\r\n transform: translateY(-1px);\r\n border-color: color-mix(in srgb, var(--sg-accent) 40%, var(--sg-border-frost));\r\n}\r\n.sg-stepper-trigger:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 2px;\r\n}\r\n.sg-stepper-indicator {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 2.25rem;\r\n height: 2.25rem;\r\n border-radius: 999px;\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-solid);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight), 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-semibold);\r\n color: var(--sg-fg-muted);\r\n flex-shrink: 0;\r\n transition: transform var(--sg-duration-fast) var(--sg-ease-out), background var(--sg-duration-fast) var(--sg-ease-out), border-color var(--sg-duration-fast) var(--sg-ease-out), color var(--sg-duration-fast) var(--sg-ease-out), box-shadow var(--sg-duration-fast) var(--sg-ease-out);\r\n}\r\n.sg-stepper[data-look=\"soft\"] .sg-stepper-indicator {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 70%, var(--sg-accent-soft));\r\n}\r\n.sg-stepper[data-look=\"outline\"] .sg-stepper-indicator {\r\n background: transparent;\r\n box-shadow: none;\r\n}\r\n.sg-stepper[data-look=\"dots\"] .sg-stepper-indicator {\r\n position: relative;\r\n z-index: 2;\r\n width: var(--sg-stepper-dot);\r\n height: var(--sg-stepper-dot);\r\n /* Fully opaque — no solid-halo (visible on glossy/frost card bg) */\r\n background: var(--sg-surface-solid);\r\n border: 2px solid var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n.sg-stepper[data-look=\"dots\"] .sg-stepper-item[data-status=\"current\"] .sg-stepper-indicator {\r\n color: var(--sg-accent-fg);\r\n background: var(--sg-accent);\r\n border-color: var(--sg-accent);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n.sg-stepper[data-look=\"dots\"] .sg-stepper-item[data-status=\"complete\"] .sg-stepper-indicator {\r\n color: var(--sg-accent);\r\n /* Opaque tint (not translucent accent-soft) */\r\n background: color-mix(in srgb, var(--sg-accent) 18%, var(--sg-surface-solid));\r\n border-color: color-mix(in srgb, var(--sg-accent) 45%, var(--sg-border-subtle));\r\n box-shadow: none;\r\n}\r\n/* Non-dots status chrome */\r\n.sg-stepper:not([data-look=\"dots\"]) .sg-stepper-item[data-status=\"current\"] .sg-stepper-indicator {\r\n color: var(--sg-accent-fg);\r\n background: var(--sg-accent);\r\n border-color: transparent;\r\n box-shadow:\r\n inset 0 1px 0 var(--sg-gloss-highlight),\r\n 0 0 0 4px color-mix(in srgb, var(--sg-accent) 18%, transparent);\r\n}\r\n.sg-stepper:not([data-look=\"dots\"]) .sg-stepper-item[data-status=\"complete\"] .sg-stepper-indicator {\r\n color: var(--sg-accent);\r\n background: var(--sg-accent-soft);\r\n border-color: color-mix(in srgb, var(--sg-accent) 30%, transparent);\r\n}\r\n.sg-stepper-item[data-status=\"current\"] .sg-stepper-label {\r\n color: var(--sg-fg);\r\n font-weight: var(--sg-font-weight-semibold);\r\n}\r\n.sg-stepper-item[data-status=\"complete\"] .sg-stepper-label { color: var(--sg-fg); }\r\n.sg-stepper-check { width: 1rem; height: 1rem; display: block; }\r\n.sg-stepper-text { display: flex; flex-direction: column; gap: 0.12rem; min-width: 0; }\r\n.sg-stepper-label {\r\n font-size: var(--sg-text-sm);\r\n font-weight: var(--sg-font-weight-medium);\r\n letter-spacing: var(--sg-tracking-tight);\r\n color: var(--sg-fg-muted);\r\n}\r\n.sg-stepper-description {\r\n font-size: var(--sg-text-xs);\r\n color: var(--sg-fg-subtle, var(--sg-fg-muted));\r\n line-height: 1.35;\r\n}\r\n.sg-stepper[data-size=\"sm\"] .sg-stepper-indicator {\r\n width: 1.85rem;\r\n height: 1.85rem;\r\n font-size: var(--sg-text-xs);\r\n}\r\n.sg-stepper[data-size=\"sm\"] .sg-stepper-label { font-size: var(--sg-text-xs); }\r\n\r\n/* —— Toolbar (v1.4c) —— */\r\n.sg-toolbar {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n gap: 0.55rem;\r\n width: 100%;\r\n padding: 0.6rem 0.75rem;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n box-sizing: border-box;\r\n contain: layout style;\r\n}\r\n.sg-toolbar[data-size=\"sm\"] {\r\n padding: 0.4rem 0.55rem;\r\n gap: 0.4rem;\r\n border-radius: var(--sg-radius-md);\r\n}\r\n.sg-toolbar[data-look=\"solid\"] {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: 0 1px 0 color-mix(in srgb, var(--sg-fg) 4%, transparent);\r\n}\r\n.sg-toolbar[data-look=\"glass\"] {\r\n background: var(--sg-surface-frost-strong, var(--sg-surface-frost));\r\n border-color: color-mix(in srgb, var(--sg-border-frost) 75%, var(--sg-accent) 16%);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight), 0 8px 24px color-mix(in srgb, var(--sg-accent) 8%, transparent);\r\n}\r\n@media (prefers-reduced-transparency: no-preference) {\r\n .sg-toolbar[data-look=\"glass\"] {\r\n background: color-mix(in srgb, var(--sg-surface-glass) 88%, transparent);\r\n backdrop-filter: blur(12px) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(12px) saturate(var(--sg-glass-saturate));\r\n }\r\n}\r\n.sg-toolbar[data-look=\"ghost\"] {\r\n background: transparent;\r\n border-color: transparent;\r\n box-shadow: none;\r\n padding-inline: 0;\r\n}\r\n.sg-toolbar[data-look=\"accent\"] {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n border-left: 3px solid var(--sg-accent);\r\n border-radius: var(--sg-radius-md);\r\n}\r\n.sg-toolbar[data-align=\"start\"] { justify-content: flex-start; }\r\n.sg-toolbar[data-align=\"end\"] { justify-content: flex-end; }\r\n.sg-toolbar:not([data-align]) { justify-content: space-between; }\r\n.sg-toolbar-group {\r\n display: inline-flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n gap: 0.45rem;\r\n min-width: 0;\r\n}\r\n.sg-toolbar-group-inset,\r\n.sg-toolbar-group[data-inset=\"true\"] {\r\n padding: 0.25rem;\r\n border-radius: var(--sg-radius-md);\r\n background: color-mix(in srgb, var(--sg-surface-solid) 55%, transparent);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n}\r\n.sg-toolbar-spacer { flex: 1 1 auto; min-width: 0.5rem; }\r\n.sg-toolbar-sep {\r\n width: 1px;\r\n align-self: stretch;\r\n min-height: 1.5rem;\r\n background: var(--sg-border-subtle);\r\n margin-inline: 0.15rem;\r\n}\r\n\r\n/* —— List (v1.4c) —— */\r\n.sg-list {\r\n display: flex;\r\n flex-direction: column;\r\n width: 100%;\r\n margin: 0;\r\n padding: 0.4rem;\r\n list-style: none;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n box-sizing: border-box;\r\n contain: layout style;\r\n gap: 0.2rem;\r\n}\r\n.sg-list[data-look=\"solid\"] {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n.sg-list[data-look=\"outline\"] {\r\n background: transparent;\r\n box-shadow: none;\r\n}\r\n.sg-list[data-look=\"ghost\"] {\r\n background: transparent;\r\n border-color: transparent;\r\n box-shadow: none;\r\n padding: 0;\r\n gap: 0.35rem;\r\n}\r\n.sg-list[data-look=\"inset\"] {\r\n background: color-mix(in srgb, var(--sg-fg) 4%, var(--sg-surface-solid));\r\n border-color: transparent;\r\n box-shadow: inset 0 1px 2px color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n padding: 0.5rem;\r\n}\r\n.sg-list[data-density=\"compact\"] { padding: 0.2rem; gap: 0.08rem; }\r\n.sg-list[data-density=\"compact\"] .sg-list-item { padding-block: 0.4rem; }\r\n.sg-list[data-density=\"relaxed\"] { padding: 0.55rem; gap: 0.35rem; }\r\n.sg-list[data-density=\"relaxed\"] .sg-list-item { padding-block: 0.85rem; }\r\n/* Dividers: seamless stack — first/last keep radius, middles square edges */\r\n.sg-list[data-dividers=\"true\"] {\r\n gap: 0;\r\n padding: 0.25rem;\r\n}\r\n.sg-list[data-dividers=\"true\"] > * {\r\n border-radius: 0;\r\n}\r\n.sg-list[data-dividers=\"true\"] > *:first-child {\r\n border-radius: var(--sg-radius-md) var(--sg-radius-md) 0 0;\r\n}\r\n.sg-list[data-dividers=\"true\"] > *:last-child {\r\n border-radius: 0 0 var(--sg-radius-md) var(--sg-radius-md);\r\n}\r\n.sg-list[data-dividers=\"true\"] > *:only-child {\r\n border-radius: var(--sg-radius-md);\r\n}\r\n.sg-list[data-dividers=\"true\"] > * + * {\r\n border-top: var(--sg-border-width) solid\r\n color-mix(in srgb, var(--sg-border-frost) 88%, transparent);\r\n}\r\n.sg-list[data-look=\"ghost\"][data-dividers=\"true\"] {\r\n padding: 0;\r\n}\r\n.sg-list[data-look=\"ghost\"][data-dividers=\"true\"] > * + * {\r\n border-top-color: var(--sg-border-subtle);\r\n}\r\n\r\n/* —— Stat (v1.4c) —— */\r\n.sg-stat {\r\n position: relative;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 0.4rem;\r\n min-width: 0;\r\n padding: 1.05rem 1.15rem;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-solid);\r\n box-sizing: border-box;\r\n contain: layout style;\r\n overflow: hidden;\r\n}\r\n.sg-stat[data-size=\"sm\"] { padding: 0.75rem 0.85rem; gap: 0.25rem; }\r\n.sg-stat[data-size=\"lg\"] { padding: 1.35rem 1.4rem; gap: 0.5rem; }\r\n.sg-stat[data-look=\"soft\"] {\r\n background: var(--sg-surface-frost);\r\n border-color: var(--sg-border-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n}\r\n.sg-stat[data-look=\"glass\"] {\r\n background: var(--sg-surface-frost-strong, var(--sg-surface-frost));\r\n border-color: color-mix(in srgb, var(--sg-border-frost) 75%, var(--sg-accent) 16%);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight), 0 10px 28px color-mix(in srgb, var(--sg-accent) 10%, transparent);\r\n}\r\n@media (prefers-reduced-transparency: no-preference) {\r\n .sg-stat[data-look=\"glass\"] {\r\n background: color-mix(in srgb, var(--sg-surface-glass) 88%, transparent);\r\n backdrop-filter: blur(12px) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(12px) saturate(var(--sg-glass-saturate));\r\n }\r\n}\r\n.sg-stat[data-look=\"outline\"] {\r\n background: transparent;\r\n border-style: dashed;\r\n}\r\n.sg-stat[data-look=\"accent\"] {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n padding-left: 1.25rem;\r\n}\r\n.sg-stat[data-look=\"accent\"]::before {\r\n content: \"\";\r\n position: absolute;\r\n left: 0;\r\n top: 0.65rem;\r\n bottom: 0.65rem;\r\n width: 4px;\r\n border-radius: 999px;\r\n background: linear-gradient(180deg, var(--sg-accent), color-mix(in srgb, var(--sg-accent) 55%, var(--sg-ring)));\r\n box-shadow: 0 0 12px color-mix(in srgb, var(--sg-accent) 40%, transparent);\r\n}\r\n.sg-stat-icon {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 2rem;\r\n height: 2rem;\r\n border-radius: var(--sg-radius-sm);\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n font-size: 1rem;\r\n margin-bottom: 0.15rem;\r\n}\r\n.sg-stat-label {\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: 0.06em;\r\n text-transform: uppercase;\r\n color: var(--sg-fg-muted);\r\n}\r\n.sg-stat-value-row {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: baseline;\r\n gap: 0.45rem 0.65rem;\r\n}\r\n.sg-stat-value {\r\n font-size: 1.7rem;\r\n font-weight: var(--sg-font-weight-semibold);\r\n letter-spacing: var(--sg-tracking-tight);\r\n color: var(--sg-fg);\r\n font-variant-numeric: tabular-nums;\r\n line-height: 1.1;\r\n}\r\n.sg-stat[data-size=\"sm\"] .sg-stat-value { font-size: 1.3rem; }\r\n.sg-stat[data-size=\"lg\"] .sg-stat-value { font-size: 2.05rem; }\r\n.sg-stat-trend {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.2rem;\r\n padding: 0.18rem 0.5rem;\r\n border-radius: var(--sg-radius-pill);\r\n font-size: var(--sg-text-xs);\r\n font-weight: var(--sg-font-weight-semibold);\r\n background: var(--sg-accent-soft);\r\n color: var(--sg-accent);\r\n font-variant-numeric: tabular-nums;\r\n}\r\n.sg-stat-trend[data-trend=\"up\"] {\r\n background: color-mix(in srgb, var(--sg-success, #16a34a) 16%, transparent);\r\n color: var(--sg-success, #16a34a);\r\n}\r\n.sg-stat-trend[data-trend=\"down\"] {\r\n background: color-mix(in srgb, var(--sg-danger, #dc2626) 14%, transparent);\r\n color: var(--sg-danger, #dc2626);\r\n}\r\n.sg-stat-trend[data-trend=\"flat\"] {\r\n background: color-mix(in srgb, var(--sg-fg-muted) 12%, transparent);\r\n color: var(--sg-fg-muted);\r\n}\r\n.sg-stat-hint {\r\n font-size: var(--sg-text-xs);\r\n color: var(--sg-fg-muted);\r\n line-height: 1.4;\r\n}\r\n@media (prefers-reduced-motion: reduce) {\r\n .sg-stepper-indicator { transition: none !important; }\r\n .sg-stepper-trigger:not(:disabled):hover .sg-stepper-indicator { transform: none; }\r\n}\r\n/* MultiSelect in-menu filter (v1.4d) */\r\n.sg-multiselect-menu {\r\n display: flex;\r\n flex-direction: column;\r\n padding: 0;\r\n overflow: hidden;\r\n max-height: min(20rem, 70vh);\r\n}\r\n.sg-multiselect-filter {\r\n flex-shrink: 0;\r\n padding: 0.45rem 0.5rem;\r\n border-bottom: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: color-mix(in srgb, var(--sg-surface-solid) 55%, transparent);\r\n}\r\n.sg-multiselect-filter-input {\r\n appearance: none;\r\n width: 100%;\r\n margin: 0;\r\n padding: 0.45rem 0.65rem;\r\n border-radius: var(--sg-radius-sm);\r\n border: var(--sg-border-width) solid var(--sg-border-subtle);\r\n background: var(--sg-surface-solid);\r\n color: var(--sg-fg);\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n box-sizing: border-box;\r\n}\r\n.sg-multiselect-filter-input:focus {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 1px;\r\n}\r\n.sg-multiselect-options {\r\n margin: 0;\r\n padding: 0.35rem;\r\n list-style: none;\r\n overflow: auto;\r\n flex: 1 1 auto;\r\n min-height: 0;\r\n}\r\n.sg-combobox-loading {\r\n display: flex;\r\n align-items: center;\r\n gap: 0.4rem;\r\n opacity: 0.85;\r\n}\r\n\r\n/* —— DataTable (v1.7-0) —— */\r\n.sg-data-table {\r\n --sg-table-row-pad-y: 0.7rem;\r\n --sg-table-row-pad-x: 0.85rem;\r\n --sg-table-head-pad-y: 0.65rem;\r\n display: flex;\r\n flex-direction: column;\r\n width: 100%;\r\n min-width: 0;\r\n min-height: 0;\r\n border-radius: var(--sg-radius-lg);\r\n border: var(--sg-border-width) solid var(--sg-border-frost);\r\n background: var(--sg-surface-frost);\r\n box-shadow: inset 0 1px 0 var(--sg-gloss-highlight);\r\n box-sizing: border-box;\r\n contain: layout style;\r\n overflow: hidden;\r\n}\r\n\r\n.sg-data-table[data-look=\"solid\"] {\r\n background: var(--sg-surface-solid);\r\n border-color: var(--sg-border-subtle);\r\n box-shadow: none;\r\n}\r\n\r\n.sg-data-table[data-look=\"outline\"] {\r\n background: transparent;\r\n box-shadow: none;\r\n}\r\n\r\n.sg-data-table[data-look=\"ghost\"] {\r\n background: transparent;\r\n border-color: transparent;\r\n box-shadow: none;\r\n border-radius: 0;\r\n}\r\n\r\n.sg-data-table[data-look=\"glass\"] {\r\n background: var(--sg-surface-frost-strong, var(--sg-surface-frost));\r\n border-color: color-mix(in srgb, var(--sg-border-frost) 75%, var(--sg-accent) 12%);\r\n}\r\n\r\n@supports (backdrop-filter: blur(1px)) {\r\n .sg-data-table[data-look=\"glass\"] {\r\n background: color-mix(in srgb, var(--sg-surface-glass) 88%, transparent);\r\n backdrop-filter: blur(12px) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(12px) saturate(var(--sg-glass-saturate));\r\n }\r\n}\r\n\r\n.sg-data-table[data-density=\"compact\"] {\r\n --sg-table-row-pad-y: 0.4rem;\r\n --sg-table-row-pad-x: 0.65rem;\r\n --sg-table-head-pad-y: 0.4rem;\r\n}\r\n\r\n.sg-data-table-scroll {\r\n flex: 1 1 auto;\r\n width: 100%;\r\n min-height: 0;\r\n max-width: 100%;\r\n overflow: auto;\r\n}\r\n\r\n/* Virtualization spacers — pure height, no chrome */\r\n.sg-table-row-spacer,\r\n.sg-table-row-spacer:hover {\r\n background: transparent !important;\r\n pointer-events: none;\r\n}\r\n\r\n.sg-table-cell-spacer {\r\n padding: 0 !important;\r\n border: 0 !important;\r\n line-height: 0;\r\n font-size: 0;\r\n vertical-align: top;\r\n}\r\n\r\n.sg-table {\r\n width: 100%;\r\n border-collapse: separate;\r\n border-spacing: 0;\r\n font-family: var(--sg-font-sans);\r\n font-size: var(--sg-text-sm);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-table-header {\r\n background: color-mix(in srgb, var(--sg-fg) 4%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-data-table[data-look=\"soft\"] .sg-table-header,\r\n.sg-data-table[data-look=\"glass\"] .sg-table-header {\r\n background: color-mix(in srgb, var(--sg-fg) 5%, transparent);\r\n}\r\n\r\n.sg-data-table[data-look=\"ghost\"] .sg-table-header {\r\n background: transparent;\r\n}\r\n\r\n.sg-table-head {\r\n position: relative;\r\n padding: var(--sg-table-head-pad-y) var(--sg-table-row-pad-x);\r\n text-align: start;\r\n font-weight: 600;\r\n font-size: var(--sg-text-xs);\r\n letter-spacing: 0.02em;\r\n text-transform: none;\r\n color: color-mix(in srgb, var(--sg-fg) 72%, transparent);\r\n border-bottom: var(--sg-border-width) solid\r\n color-mix(in srgb, var(--sg-border-frost) 90%, transparent);\r\n white-space: nowrap;\r\n vertical-align: middle;\r\n box-sizing: border-box;\r\n}\r\n\r\n.sg-data-table:not([data-sticky-header=\"false\"]) .sg-table-head {\r\n position: sticky;\r\n top: 0;\r\n z-index: 2;\r\n background: inherit;\r\n}\r\n\r\n.sg-data-table:not([data-sticky-header=\"false\"]) .sg-table-header {\r\n position: sticky;\r\n top: 0;\r\n z-index: 2;\r\n}\r\n\r\n.sg-data-table:not([data-sticky-header=\"false\"]) .sg-table-head {\r\n background: color-mix(in srgb, var(--sg-fg) 4%, var(--sg-surface-solid));\r\n}\r\n\r\n.sg-data-table[data-look=\"soft\"]:not([data-sticky-header=\"false\"]) .sg-table-head,\r\n.sg-data-table[data-look=\"glass\"]:not([data-sticky-header=\"false\"]) .sg-table-head {\r\n background: color-mix(in srgb, var(--sg-surface-solid) 88%, var(--sg-surface-frost));\r\n}\r\n\r\n@supports (backdrop-filter: blur(1px)) {\r\n .sg-data-table[data-look=\"glass\"]:not([data-sticky-header=\"false\"]) .sg-table-head {\r\n background: color-mix(in srgb, var(--sg-surface-glass) 92%, transparent);\r\n backdrop-filter: blur(10px) saturate(var(--sg-glass-saturate));\r\n -webkit-backdrop-filter: blur(10px) saturate(var(--sg-glass-saturate));\r\n }\r\n}\r\n\r\n.sg-table-head[data-align=\"center\"],\r\n.sg-table-cell[data-align=\"center\"] {\r\n text-align: center;\r\n}\r\n\r\n.sg-table-head[data-align=\"end\"],\r\n.sg-table-cell[data-align=\"end\"] {\r\n text-align: end;\r\n}\r\n\r\n.sg-table-head-select,\r\n.sg-table-cell-select {\r\n width: 2.75rem;\r\n padding-inline: 0.65rem;\r\n text-align: center;\r\n}\r\n\r\n.sg-table-select-spacer {\r\n display: inline-block;\r\n width: 1rem;\r\n height: 1rem;\r\n}\r\n\r\n.sg-table-sort-btn {\r\n appearance: none;\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.35rem;\r\n max-width: 100%;\r\n margin: 0;\r\n padding: 0.15rem 0.25rem;\r\n margin-inline: -0.25rem;\r\n border: none;\r\n border-radius: var(--sg-radius-sm);\r\n background: transparent;\r\n color: inherit;\r\n font: inherit;\r\n font-weight: inherit;\r\n letter-spacing: inherit;\r\n cursor: pointer;\r\n}\r\n\r\n.sg-table-sort-btn:hover {\r\n background: color-mix(in srgb, var(--sg-fg) 6%, transparent);\r\n color: var(--sg-fg);\r\n}\r\n\r\n.sg-table-sort-btn:focus-visible {\r\n outline: 2px solid var(--sg-ring);\r\n outline-offset: 1px;\r\n}\r\n\r\n.sg-table-head-label {\r\n min-width: 0;\r\n}\r\n\r\n.sg-table-sort-icon {\r\n flex-shrink: 0;\r\n font-size: 0.75em;\r\n opacity: 0.85;\r\n line-height: 1;\r\n}\r\n\r\n.sg-table-sort-icon-idle {\r\n opacity: 0.35;\r\n}\r\n\r\n.sg-table-head[data-sorted] .sg-table-sort-icon {\r\n color: var(--sg-accent);\r\n opacity: 1;\r\n}\r\n\r\n.sg-table-row {\r\n transition: background-color 120ms ease;\r\n}\r\n\r\n.sg-table-row:hover {\r\n background: color-mix(in srgb, var(--sg-fg) 3.5%, transparent);\r\n}\r\n\r\n.sg-table-row[data-selected=\"true\"] {\r\n background: color-mix(in srgb, var(--sg-accent) 10%, transparent);\r\n}\r\n\r\n.sg-table-row[data-selected=\"true\"]:hover {\r\n background: color-mix(in srgb, var(--sg-accent) 14%, transparent);\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .sg-table-row {\r\n transition: none;\r\n }\r\n}\r\n\r\n.sg-table-cell {\r\n padding: var(--sg-table-row-pad-y) var(--sg-table-row-pad-x);\r\n text-align: start;\r\n vertical-align: middle;\r\n border-bottom: var(--sg-border-width) solid\r\n color-mix(in srgb, var(--sg-border-frost) 80%, transparent);\r\n box-sizing: border-box;\r\n}\r\n\r\n.sg-table-body .sg-table-row:last-child .sg-table-cell {\r\n border-bottom: none;\r\n}\r\n\r\n.sg-table-cell-empty {\r\n padding: 1.25rem 1rem;\r\n text-align: center;\r\n}\r\n\r\n.sg-table-cell-empty .sg-empty-state {\r\n margin-inline: auto;\r\n max-width: 22rem;\r\n}\r\n\r\n.sg-table-skeleton-block {\r\n display: block;\r\n height: 0.75rem;\r\n width: 72%;\r\n max-width: 12rem;\r\n border-radius: var(--sg-radius-pill);\r\n background: linear-gradient(\r\n 90deg,\r\n color-mix(in srgb, var(--sg-fg) 6%, transparent) 0%,\r\n color-mix(in srgb, var(--sg-fg) 12%, transparent) 50%,\r\n color-mix(in srgb, var(--sg-fg) 6%, transparent) 100%\r\n );\r\n background-size: 200% 100%;\r\n animation: sg-table-skeleton 1.2s ease-in-out infinite;\r\n}\r\n\r\n.sg-data-table[data-density=\"compact\"] .sg-table-skeleton-block {\r\n height: 0.55rem;\r\n max-width: 9rem;\r\n}\r\n\r\n.sg-table-skeleton-check {\r\n width: 1.1rem !important;\r\n height: 1.1rem !important;\r\n max-width: 1.1rem !important;\r\n margin-inline: auto;\r\n border-radius: var(--sg-radius-sm);\r\n}\r\n\r\n.sg-data-table[data-density=\"compact\"] .sg-table-skeleton-check {\r\n width: 0.95rem !important;\r\n height: 0.95rem !important;\r\n max-width: 0.95rem !important;\r\n}\r\n\r\n.sg-table-row-skeleton {\r\n pointer-events: none;\r\n}\r\n\r\n@keyframes sg-table-skeleton {\r\n 0% {\r\n background-position: 100% 0;\r\n }\r\n 100% {\r\n background-position: -100% 0;\r\n }\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .sg-table-skeleton-block {\r\n animation: none;\r\n }\r\n}\r\n", "type": "registry:style", "target": "styles/softglass/base.css" }, diff --git a/apps/web/src/components/playground/library/pages/fields.tsx b/apps/web/src/components/playground/library/pages/fields.tsx index 6b45a64..823f39b 100644 --- a/apps/web/src/components/playground/library/pages/fields.tsx +++ b/apps/web/src/components/playground/library/pages/fields.tsx @@ -17,6 +17,7 @@ import { import { Checkbox, Combobox, + Calendar, DatePicker, FileField, Input, @@ -31,6 +32,7 @@ import { Slider, Switch, Textarea, + TimePicker, type DatePickerMode, type DatePickerSize, type FieldLook, @@ -386,6 +388,37 @@ export const fieldPlaygrounds: ComponentPlayground[] = [ ), ], }, + { + id: "calendar", + title: "Calendar", + controls: [ + enumControl("mode", ["single", "range"], "single"), + boolControl("disabledMin", false, "min bound demo"), + ], + render: (p) => ( +
+ +
+ ), + showcases: [ + showcase("Single", () => ( +
+ +
+ )), + showcase("Range", () => ( +
+ +
+ )), + ], + }, { id: "date-picker", title: "DatePicker", @@ -423,6 +456,35 @@ export const fieldPlaygrounds: ComponentPlayground[] = [ ), ], }, + { + id: "time-picker", + title: "TimePicker", + controls: [ + enumControl("size", SIZES, "md"), + enumControl("look", ["soft", "solid", "outline", "glass"], "soft"), + enumControl("hourCycle", ["24", "12"], "24"), + textControl("label", "Start time"), + boolControl("disabled", false), + ], + render: (p) => ( + + ), + showcases: [ + showcase("Presets", () => ( + + )), + showcase("12h", () => ( + + )), + ], + }, { id: "slider", title: "Slider", diff --git a/apps/web/src/lib/docs.ts b/apps/web/src/lib/docs.ts index 6bdff27..0d21461 100644 --- a/apps/web/src/lib/docs.ts +++ b/apps/web/src/lib/docs.ts @@ -423,12 +423,58 @@ export const COMPONENT_DOCS: ComponentDoc[] = [ }, ], }, + { + id: "calendar", + name: "Calendar", + layer: "atom", + summary: + "Inline day/month/year grids. Single or range ISO dates — shared by DatePicker.", + importLine: 'import { Calendar } from "@softglass/ui";', + example: ` +{/* range */} +`, + props: [ + { + name: "mode", + type: '"single" | "range"', + default: '"single"', + description: "Single day or start/end range.", + }, + { + name: "value / defaultValue", + type: "string (YYYY-MM-DD)", + description: "Single mode ISO date.", + }, + { + name: "rangeValue / defaultRangeValue", + type: "{ start: string; end: string }", + description: "Range mode ISO start/end.", + }, + { + name: "min / max", + type: "string (YYYY-MM-DD)", + description: "Inclusive bounds.", + }, + { + name: "onSelectComplete", + type: "() => void", + description: "Fires when a full selection is done (DatePicker closes).", + }, + ], + }, { id: "date-picker", name: "DatePicker", layer: "atom", summary: - "Single or range ISO dates. Day/month/year grids. Body portal + flip/clamp.", + "Trigger + portaled Calendar. Single or range ISO dates. Flip/clamp panel.", importLine: 'import { DatePicker } from "@softglass/ui";', example: ``, + props: [ + { + name: "value / defaultValue", + type: "string (HH:mm)", + description: "24h clock string.", + }, + { + name: "presets", + type: "string[]", + default: '["09:00","12:00","13:00","17:00","18:00"]', + description: "Quick pick chips in the panel.", + }, + { + name: "hourCycle", + type: "24 | 12", + default: "24", + description: "Display cycle; stored value stays HH:mm 24h.", + }, + { + name: "minuteStep", + type: "number", + default: "5", + description: "Stepper step for minutes.", + }, + ], + }, { id: "switch", name: "Switch", diff --git a/docs/LIMITATIONS.md b/docs/LIMITATIONS.md index 615329f..baf67cf 100644 --- a/docs/LIMITATIONS.md +++ b/docs/LIMITATIONS.md @@ -24,7 +24,9 @@ Honest scope notes for Softglass. Read this before treating the kit like Radix / | **Select** | Custom listbox-ish menu, keyboard, placement auto, **body portal + flip/clamp**, **enter/exit** | Virtualization for 500+ options; native form autofill parity | | **Combobox** | Type-to-filter; option-only; body portal; **`onSearch` + `loading` async skeleton** | Creatable free-form; full data layer / cancellation | | **MultiSelect** | Multi chips; body portal; **filter-in-menu** (default on) | Select-all; paste list | -| **DatePicker** | Single **or range** ISO `YYYY-MM-DD`; day/month/year grids; **body portal + flip/clamp**; min/max; range span highlight. Range: first click is draft start; `onRangeValueChange` fires when **both** ends chosen | Dual-month calendar; partial range callback; time; locale packs / full i18n; typing into field | +| **Calendar** | Inline day/month/year; single **or range** ISO `YYYY-MM-DD`; min/max | Dual-month; locale packs / full i18n | +| **DatePicker** | Trigger + portal over **Calendar**; flip/clamp; range draft → complete | Dual-month; typing into field; partial range callback | +| **TimePicker** | Trigger + panel with TimeInput, presets, Apply (HH:mm 24h value) | Seconds; timezone; ranges | | **DropdownMenu** | Items API, keyboard, Escape + outside, `Button` merge, **body portal + collision**, **enter/exit** | Submenus; checkbox/radio items | | **ContextMenu** | Same `items` language; right-click + long-press; fixed at pointer; Escape + outside; **enter/exit** | Nested submenu tree; full OS parity; Floating UI-level collision | | **Popover** | Anchored panel, Escape + outside, non-modal, **body portal + collision**, **enter/exit** | Focus trap option; arrow | @@ -60,9 +62,8 @@ Honest scope notes for Softglass. Read this before treating the kit like Radix / | **Chip** | Select / remove / filter / check | Multi-select store; avatar chips | | **PasswordInput** | Show/hide | Strength meter; generator | | **SearchInput** | Free text + clear | Command palette; async suggestions | -| **TimeInput** | Custom HH:mm Softglass UI | Timezone / seconds / range | -| **NativeDateInput** | Custom day/month/year steppers (YYYY-MM-DD) | Full calendar suite (use DatePicker) | -| **DatePicker** | Day/month/year grids; body portal; **range mode** | Dual-month UI; locale packs; Calendar extract | +| **TimeInput** | Custom HH:mm Softglass UI | Timezone / seconds / range (use TimePicker for popover) | +| **NativeDateInput** | Custom day/month/year steppers (YYYY-MM-DD) | Full calendar suite (use Calendar / DatePicker) | | **Rating** | SVG stars + shell | Half-stars; custom icon sets | | **Glass surfaces** | Frost-first (cheap); optional blur | Guaranteed WCAG on every glass+text combo | diff --git a/docs/claude/todos.md b/docs/claude/todos.md index fa48751..aa83804 100644 --- a/docs/claude/todos.md +++ b/docs/claude/todos.md @@ -25,12 +25,17 @@ --- +## v1.9 calendar (this branch) + +- [x] Extract **`Calendar`** from DatePicker +- [x] **`TimePicker`** suite (presets + panel) +- [x] Studio + registry + tests + ## v1.9+ backlog (kalan) - [ ] Token JSON / Figma bridge - [ ] i18n / RTL -- [ ] Full calendar / TimePicker suite -- [ ] DatePicker Calendar extract +- [ ] Dual-month range calendar / full suite polish - [ ] (optional) expand visual baselines / mobile project --- diff --git a/package-lock.json b/package-lock.json index e9dbc3d..88f331e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2664,6 +2664,36 @@ "tailwindcss": "4.3.3" } }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/dom/node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, "node_modules/@testing-library/jest-dom": { "version": "6.9.1", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", @@ -2744,6 +2774,13 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -3715,6 +3752,16 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -4484,6 +4531,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -4507,6 +4564,13 @@ "node": ">=0.10.0" } }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT" + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -6875,6 +6939,16 @@ "yallist": "^3.0.2" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -7584,6 +7658,41 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -9476,6 +9585,7 @@ "version": "1.8.0", "license": "MIT", "devDependencies": { + "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^14.6.1", diff --git a/packages/tokens/src/base.css b/packages/tokens/src/base.css index 5adaaa6..9874d38 100644 --- a/packages/tokens/src/base.css +++ b/packages/tokens/src/base.css @@ -1716,6 +1716,112 @@ margin: 0; } +/* Inline Calendar (shared by DatePicker panel) */ +.sg-calendar { + width: 100%; + min-width: 0; + box-sizing: border-box; +} + +/* TimePicker panel (DatePicker chrome language) */ +.sg-timepicker-root { + position: relative; + display: inline-flex; + min-width: 0; +} + +.sg-timepicker-panel { + position: absolute; + z-index: var(--sg-z-dropdown); + top: calc(100% + 0.4rem); + left: 0; + width: min(17.5rem, 100vw - 1.5rem); + margin: 0; + padding: var(--sg-space-3); + border-radius: var(--sg-radius-lg); + border: var(--sg-border-width) solid var(--sg-border-frost); + background-color: var(--sg-surface-frost-strong); + background-image: linear-gradient( + 180deg, + var(--sg-gloss-highlight) 0%, + transparent 36% + ); + box-shadow: var(--sg-shadow-elevated); + backdrop-filter: blur(22px) saturate(1.35); + -webkit-backdrop-filter: blur(22px) saturate(1.35); + color: var(--sg-fg); + animation: sg-select-in var(--sg-duration-fast) var(--sg-ease-out); + box-sizing: border-box; + display: grid; + gap: var(--sg-space-3); +} + +.sg-timepicker-panel[data-portaled] { + position: fixed; + top: auto; + left: auto; + bottom: auto; + right: auto; + margin: 0; +} + +.sg-timepicker-presets { + display: flex; + flex-wrap: wrap; + gap: 0.4rem; +} + +.sg-timepicker-preset { + appearance: none; + border: var(--sg-border-width) solid var(--sg-border-frost); + background: color-mix(in srgb, var(--sg-fg) 4%, transparent); + color: var(--sg-fg); + border-radius: 999px; + padding: 0.28rem 0.65rem; + font: inherit; + font-size: var(--sg-text-xs); + font-weight: 600; + cursor: pointer; +} + +.sg-timepicker-preset:hover { + background: var(--sg-accent-soft); + border-color: color-mix(in srgb, var(--sg-accent) 28%, var(--sg-border-frost)); +} + +.sg-timepicker-preset[data-selected="true"] { + background: var(--sg-accent); + border-color: var(--sg-accent); + color: var(--sg-accent-fg); +} + +.sg-timepicker-actions { + display: flex; + justify-content: flex-end; +} + +.sg-timepicker-apply { + appearance: none; + border: 0; + border-radius: var(--sg-radius-md); + background: var(--sg-accent); + color: var(--sg-accent-fg); + font: inherit; + font-size: var(--sg-text-sm); + font-weight: 600; + padding: 0.45rem 0.9rem; + cursor: pointer; +} + +.sg-timepicker-apply:hover { + background: var(--sg-accent-hover); +} + +.sg-timepicker-apply:focus-visible { + outline: 2px solid var(--sg-ring); + outline-offset: 2px; +} + .sg-datepicker-header { display: flex; align-items: center; diff --git a/packages/ui/package.json b/packages/ui/package.json index d7631ba..0cc91e3 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -65,6 +65,7 @@ } }, "devDependencies": { + "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^14.6.1", diff --git a/packages/ui/src/atoms/calendar.test.tsx b/packages/ui/src/atoms/calendar.test.tsx new file mode 100644 index 0000000..6bae5c9 --- /dev/null +++ b/packages/ui/src/atoms/calendar.test.tsx @@ -0,0 +1,52 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { describe, expect, it, vi } from "vitest"; +import { Calendar } from "./calendar"; + +describe("Calendar (smoke)", () => { + it("selects a day in single mode", async () => { + const user = userEvent.setup(); + const onValueChange = vi.fn(); + + render( + , + ); + + // Caption shows month; grid is present + expect(screen.getByRole("grid")).toBeInTheDocument(); + + const day = document.querySelector( + '[data-iso="2026-07-20"]', + ); + expect(day).toBeTruthy(); + await user.click(day!); + expect(onValueChange).toHaveBeenCalledWith("2026-07-20"); + }); + + it("supports range start then end", async () => { + const user = userEvent.setup(); + const onRangeValueChange = vi.fn(); + + render( + , + ); + + await user.click(document.querySelector('[data-iso="2026-07-10"]')!); + expect(onRangeValueChange).not.toHaveBeenCalled(); + + await user.click(document.querySelector('[data-iso="2026-07-12"]')!); + expect(onRangeValueChange).toHaveBeenCalledWith({ + start: "2026-07-10", + end: "2026-07-12", + }); + }); +}); diff --git a/packages/ui/src/atoms/calendar.tsx b/packages/ui/src/atoms/calendar.tsx new file mode 100644 index 0000000..78ba576 --- /dev/null +++ b/packages/ui/src/atoms/calendar.tsx @@ -0,0 +1,677 @@ +"use client"; + +import { cn } from "../lib/cn"; +import { + useEffect, + useId, + useMemo, + useState, + type HTMLAttributes, + type KeyboardEvent, +} from "react"; + +/** + * ISO calendar date `YYYY-MM-DD` (local calendar parts — no timezone engine). + */ +export type DateRangeValue = { + start: string; + end: string; +}; + +export type CalendarMode = "single" | "range"; + +export type CalendarProps = Omit, "onChange"> & { + mode?: CalendarMode; + /** Single: controlled ISO date. */ + value?: string; + defaultValue?: string; + onValueChange?: (value: string) => void; + /** Range: controlled `{ start, end }` (empty = unset). */ + rangeValue?: DateRangeValue; + defaultRangeValue?: DateRangeValue; + onRangeValueChange?: (value: DateRangeValue) => void; + min?: string; + max?: string; + /** + * Called after a complete selection (single day, or range start+end). + * DatePicker uses this to close the popover. + */ + onSelectComplete?: () => void; + "aria-label"?: string; +}; + +const WEEKDAYS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"] as const; +const YEAR_PAGE = 12; +const ISO_RE = /^(\d{4})-(\d{2})-(\d{2})$/; +const EMPTY_RANGE: DateRangeValue = { start: "", end: "" }; + +type Ymd = { y: number; m: number; d: number }; +type PanelMode = "day" | "month" | "year"; +type Cell = { iso: string; day: number; inMonth: boolean }; + +export function parseISODate(iso: string | undefined): Ymd | null { + if (!iso) return null; + const match = ISO_RE.exec(iso); + if (!match) return null; + const y = Number(match[1]); + const m = Number(match[2]) - 1; + const d = Number(match[3]); + if (!Number.isFinite(y) || m < 0 || m > 11 || d < 1 || d > 31) return null; + const dt = new Date(y, m, d); + if (dt.getFullYear() !== y || dt.getMonth() !== m || dt.getDate() !== d) { + return null; + } + return { y, m, d }; +} + +export function toISODate(y: number, m: number, d: number): string { + return `${y}-${String(m + 1).padStart(2, "0")}-${String(d).padStart(2, "0")}`; +} + +function ymdToDate({ y, m, d }: Ymd): Date { + return new Date(y, m, d); +} + +export function compareISODate(a: string, b: string): number { + return a < b ? -1 : a > b ? 1 : 0; +} + +function isDisabledISO(iso: string, min?: string, max?: string): boolean { + if (min && compareISODate(iso, min) < 0) return true; + if (max && compareISODate(iso, max) > 0) return true; + return false; +} + +function isMonthDisabled(y: number, m: number, min?: string, max?: string): boolean { + const lastDay = new Date(y, m + 1, 0).getDate(); + const first = toISODate(y, m, 1); + const last = toISODate(y, m, lastDay); + if (max && compareISODate(first, max) > 0) return true; + if (min && compareISODate(last, min) < 0) return true; + return false; +} + +function isYearDisabled(y: number, min?: string, max?: string): boolean { + const first = toISODate(y, 0, 1); + const last = toISODate(y, 11, 31); + if (max && compareISODate(first, max) > 0) return true; + if (min && compareISODate(last, min) < 0) return true; + return false; +} + +function addDays(ymd: Ymd, delta: number): Ymd { + const dt = ymdToDate(ymd); + dt.setDate(dt.getDate() + delta); + return { y: dt.getFullYear(), m: dt.getMonth(), d: dt.getDate() }; +} + +function addMonths(ymd: Ymd, delta: number): Ymd { + const dt = new Date(ymd.y, ymd.m + delta, 1); + const last = new Date(dt.getFullYear(), dt.getMonth() + 1, 0).getDate(); + return { y: dt.getFullYear(), m: dt.getMonth(), d: Math.min(ymd.d, last) }; +} + +function clampDayInMonth(y: number, m: number, d: number): number { + return Math.min(d, new Date(y, m + 1, 0).getDate()); +} + +function formatMonthName(m: number): string { + try { + return new Date(2000, m, 1).toLocaleDateString(undefined, { month: "long" }); + } catch { + return String(m + 1); + } +} + +function formatMonthShort(m: number): string { + try { + return new Date(2000, m, 1).toLocaleDateString(undefined, { month: "short" }); + } catch { + return String(m + 1); + } +} + +function buildMonthGrid(viewY: number, viewM: number): Cell[] { + const first = new Date(viewY, viewM, 1); + const startPad = first.getDay(); + const daysInMonth = new Date(viewY, viewM + 1, 0).getDate(); + const cells: Cell[] = []; + const prevLast = new Date(viewY, viewM, 0).getDate(); + + for (let i = startPad - 1; i >= 0; i -= 1) { + const d = prevLast - i; + const dt = new Date(viewY, viewM - 1, d); + cells.push({ + iso: toISODate(dt.getFullYear(), dt.getMonth(), dt.getDate()), + day: d, + inMonth: false, + }); + } + for (let d = 1; d <= daysInMonth; d += 1) { + cells.push({ iso: toISODate(viewY, viewM, d), day: d, inMonth: true }); + } + while (cells.length % 7 !== 0 || cells.length < 42) { + const last = cells[cells.length - 1]!; + const next = addDays(parseISODate(last.iso)!, 1); + cells.push({ iso: toISODate(next.y, next.m, next.d), day: next.d, inMonth: false }); + } + return cells; +} + +function isInRangeISO(iso: string, start: string, end: string): boolean { + if (!start || !end) return false; + return compareISODate(iso, start) >= 0 && compareISODate(iso, end) <= 0; +} + +function ChevronLeftIcon() { + return ( + + ); +} + +function ChevronRightIcon() { + return ( + + ); +} + +/** + * Atom — Calendar + * Inline day / month / year grids (shared by DatePicker panel). + * Values: ISO `YYYY-MM-DD`. No timezone engine. + */ +export function Calendar({ + className, + mode = "single", + value, + defaultValue = "", + onValueChange, + rangeValue, + defaultRangeValue = EMPTY_RANGE, + onRangeValueChange, + min, + max, + onSelectComplete, + "aria-label": ariaLabel, + ...props +}: CalendarProps) { + const isRange = mode === "range"; + const reactId = useId(); + const today = useMemo(() => { + const n = new Date(); + return toISODate(n.getFullYear(), n.getMonth(), n.getDate()); + }, []); + const todayYmd = parseISODate(today)!; + + const singleControlled = value !== undefined; + const [singleUncontrolled, setSingleUncontrolled] = useState(defaultValue); + const current = singleControlled ? value! : singleUncontrolled; + + const rangeControlled = rangeValue !== undefined; + const [rangeUncontrolled, setRangeUncontrolled] = + useState(defaultRangeValue); + const activeRange = rangeControlled ? rangeValue! : rangeUncontrolled; + + const seed = + parseISODate(isRange ? activeRange.start || activeRange.end : current) ?? + todayYmd; + + const [view, setView] = useState({ y: seed.y, m: seed.m }); + const [focusISO, setFocusISO] = useState( + isRange + ? activeRange.end || activeRange.start || today + : current || today, + ); + const [panelMode, setPanelMode] = useState("day"); + const [yearPageStart, setYearPageStart] = useState( + () => Math.floor(seed.y / YEAR_PAGE) * YEAR_PAGE, + ); + const [rangeDraft, setRangeDraft] = useState(null); + + // Keep view in sync when controlled value jumps months + useEffect(() => { + const next = parseISODate( + isRange ? activeRange.start || activeRange.end : current, + ); + if (next) setView({ y: next.y, m: next.m }); + }, [current, activeRange.start, activeRange.end, isRange]); + + const selected = parseISODate(current); + const cells = useMemo( + () => buildMonthGrid(view.y, view.m), + [view.y, view.m], + ); + const years = useMemo( + () => Array.from({ length: YEAR_PAGE }, (_, i) => yearPageStart + i), + [yearPageStart], + ); + + function commitSingle(iso: string) { + if (!singleControlled) setSingleUncontrolled(iso); + onValueChange?.(iso); + } + + function commitRange(next: DateRangeValue) { + let { start, end } = next; + if (start && end && compareISODate(start, end) > 0) { + [start, end] = [end, start]; + } + const ordered = { start, end }; + if (!rangeControlled) setRangeUncontrolled(ordered); + onRangeValueChange?.(ordered); + return ordered; + } + + function selectISO(iso: string) { + if (isDisabledISO(iso, min, max)) return; + + if (!isRange) { + commitSingle(iso); + setFocusISO(iso); + onSelectComplete?.(); + return; + } + + if (!rangeDraft || !rangeDraft.start || rangeDraft.end) { + setRangeDraft({ start: iso, end: "" }); + setFocusISO(iso); + return; + } + + const completed = commitRange({ start: rangeDraft.start, end: iso }); + setRangeDraft(null); + setFocusISO(completed.end || completed.start || iso); + onSelectComplete?.(); + } + + function jumpTo(y: number, m: number) { + const focus = parseISODate(focusISO) ?? todayYmd; + const d = clampDayInMonth(y, m, focus.d); + let iso = toISODate(y, m, d); + if (isDisabledISO(iso, min, max)) { + const last = new Date(y, m + 1, 0).getDate(); + let found = false; + for (let day = 1; day <= last; day += 1) { + const candidate = toISODate(y, m, day); + if (!isDisabledISO(candidate, min, max)) { + iso = candidate; + found = true; + break; + } + } + if (!found) return; + } + setView({ y, m }); + setFocusISO(iso); + } + + function selectMonth(m: number) { + if (isMonthDisabled(view.y, m, min, max)) return; + jumpTo(view.y, m); + setPanelMode("day"); + } + + function selectYear(y: number) { + if (isYearDisabled(y, min, max)) return; + if (!isMonthDisabled(y, view.m, min, max)) { + jumpTo(y, view.m); + } else { + for (let month = 0; month < 12; month += 1) { + if (!isMonthDisabled(y, month, min, max)) { + jumpTo(y, month); + break; + } + } + setView((v) => ({ ...v, y })); + } + setPanelMode("month"); + } + + function moveFocus(deltaDays: number) { + const cur = parseISODate(focusISO) ?? todayYmd; + let next = addDays(cur, deltaDays); + let iso = toISODate(next.y, next.m, next.d); + let guard = 0; + while (isDisabledISO(iso, min, max) && guard < 400) { + next = addDays(next, deltaDays > 0 ? 1 : -1); + iso = toISODate(next.y, next.m, next.d); + guard += 1; + } + if (isDisabledISO(iso, min, max)) return; + setFocusISO(iso); + setView({ y: next.y, m: next.m }); + } + + function shiftByNav(delta: number) { + if (panelMode === "year") { + setYearPageStart((s) => s + delta * YEAR_PAGE); + return; + } + if (panelMode === "month") { + const y = view.y + delta; + if (isYearDisabled(y, min, max)) return; + setView((v) => ({ ...v, y })); + setYearPageStart(Math.floor(y / YEAR_PAGE) * YEAR_PAGE); + return; + } + const focus = parseISODate(focusISO) ?? todayYmd; + const shifted = addMonths(focus, delta); + jumpTo(shifted.y, shifted.m); + } + + function onDayKeyDown(event: KeyboardEvent, iso: string) { + if (event.key === "ArrowLeft") { + event.preventDefault(); + moveFocus(-1); + } else if (event.key === "ArrowRight") { + event.preventDefault(); + moveFocus(1); + } else if (event.key === "ArrowUp") { + event.preventDefault(); + moveFocus(-7); + } else if (event.key === "ArrowDown") { + event.preventDefault(); + moveFocus(7); + } else if (event.key === "PageUp") { + event.preventDefault(); + shiftByNav(event.shiftKey ? -12 : -1); + } else if (event.key === "PageDown") { + event.preventDefault(); + shiftByNav(event.shiftKey ? 12 : 1); + } else if (event.key === "Home") { + event.preventDefault(); + const start = toISODate(view.y, view.m, 1); + if (!isDisabledISO(start, min, max)) setFocusISO(start); + } else if (event.key === "End") { + event.preventDefault(); + const lastDay = new Date(view.y, view.m + 1, 0).getDate(); + const end = toISODate(view.y, view.m, lastDay); + if (!isDisabledISO(end, min, max)) setFocusISO(end); + } else if (event.key === "Enter" || event.key === " ") { + event.preventDefault(); + selectISO(iso); + } + } + + const navPrevLabel = + panelMode === "year" + ? "Previous years" + : panelMode === "month" + ? "Previous year" + : "Previous month"; + const navNextLabel = + panelMode === "year" + ? "Next years" + : panelMode === "month" + ? "Next year" + : "Next month"; + const headerTitle = + panelMode === "year" + ? `${yearPageStart} – ${yearPageStart + YEAR_PAGE - 1}` + : panelMode === "month" + ? String(view.y) + : null; + + const displayRange = rangeDraft ?? activeRange; + + return ( +
+
+ + + {panelMode === "day" ? ( +
+ + +
+ ) : ( + + )} + + +
+ + {panelMode === "day" ? ( + <> + {isRange ? ( +

+ {displayRange.start && !displayRange.end + ? "Select end date" + : "Select start date"} +

+ ) : null} + +
+ {Array.from({ length: cells.length / 7 }, (_, week) => ( +
+ {cells.slice(week * 7, week * 7 + 7).map((cell) => { + const rangeStart = + isRange && + Boolean(displayRange.start) && + cell.iso === displayRange.start; + const rangeEnd = + isRange && + Boolean(displayRange.end) && + cell.iso === displayRange.end; + const inRange = + isRange && + Boolean(displayRange.start && displayRange.end) && + isInRangeISO( + cell.iso, + displayRange.start, + displayRange.end, + ); + const draftStart = rangeDraft?.start; + const inDraftPreview = + isRange && + Boolean(draftStart) && + !rangeDraft?.end && + Boolean(focusISO) && + isInRangeISO( + cell.iso, + compareISODate(draftStart!, focusISO) <= 0 + ? draftStart! + : focusISO, + compareISODate(draftStart!, focusISO) <= 0 + ? focusISO + : draftStart!, + ); + const selectedDay = isRange + ? rangeStart || rangeEnd + : cell.iso === current; + const isToday = cell.iso === today; + const focused = cell.iso === focusISO; + const dayDisabled = isDisabledISO(cell.iso, min, max); + + return ( + + ); + })} +
+ ))} +
+ + ) : null} + + {panelMode === "month" ? ( +
+ {Array.from({ length: 12 }, (_, m) => { + const monthDisabled = isMonthDisabled(view.y, m, min, max); + const isSelected = Boolean( + isRange + ? (activeRange.start && + parseISODate(activeRange.start)?.y === view.y && + parseISODate(activeRange.start)?.m === m) || + (activeRange.end && + parseISODate(activeRange.end)?.y === view.y && + parseISODate(activeRange.end)?.m === m) + : selected?.y === view.y && selected?.m === m, + ); + const isCurrent = todayYmd.y === view.y && todayYmd.m === m; + return ( + + ); + })} +
+ ) : null} + + {panelMode === "year" ? ( +
+ {years.map((y) => { + const yearDisabled = isYearDisabled(y, min, max); + const isSelected = Boolean( + isRange + ? parseISODate(activeRange.start)?.y === y || + parseISODate(activeRange.end)?.y === y + : selected?.y === y, + ); + const isCurrent = todayYmd.y === y; + return ( + + ); + })} +
+ ) : null} + +
+ ); +} diff --git a/packages/ui/src/atoms/date-picker.tsx b/packages/ui/src/atoms/date-picker.tsx index d64806c..14b4d91 100644 --- a/packages/ui/src/atoms/date-picker.tsx +++ b/packages/ui/src/atoms/date-picker.tsx @@ -7,11 +7,16 @@ import { eventInside, useFloatingPortal, } from "../lib/use-floating-portal"; +import { + Calendar, + compareISODate, + parseISODate, + type DateRangeValue, +} from "./calendar"; import { useCallback, useEffect, useId, - useMemo, useRef, useState, type KeyboardEvent, @@ -21,42 +26,27 @@ import { createPortal } from "react-dom"; export type DatePickerSize = "sm" | "md" | "lg"; export type { DatePickerMotion }; - -/** - * ISO calendar date `YYYY-MM-DD` (local calendar parts — no timezone engine). - */ -export type DateRangeValue = { - start: string; - end: string; -}; - +export type { DateRangeValue }; export type DatePickerMode = "single" | "range"; export type DatePickerProps = { - /** single (default) | range */ mode?: DatePickerMode; - /** Single mode: controlled ISO date. */ value?: string; defaultValue?: string; onValueChange?: (value: string) => void; - /** Range mode: controlled `{ start, end }` ISO strings (empty string = unset). */ rangeValue?: DateRangeValue; defaultRangeValue?: DateRangeValue; onRangeValueChange?: (value: DateRangeValue) => void; placeholder?: string; size?: DatePickerSize; - /** Panel enter/exit motion (same recipes as Select). */ motion?: DatePickerMotion; label?: ReactNode; hint?: ReactNode; error?: ReactNode; disabled?: boolean; - /** Inclusive min date `YYYY-MM-DD`. */ min?: string; - /** Inclusive max date `YYYY-MM-DD`. */ max?: string; name?: string; - /** Range mode: optional second hidden input name for end date. */ nameEnd?: string; id?: string; className?: string; @@ -70,105 +60,20 @@ const sizeClass: Record = { lg: "sg-datepicker-trigger-lg", }; -const WEEKDAYS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"] as const; - -/** How many years in the year grid page. */ -const YEAR_PAGE = 12; - -const ISO_RE = /^(\d{4})-(\d{2})-(\d{2})$/; - -type Ymd = { y: number; m: number; d: number }; -type PanelMode = "day" | "month" | "year"; - -function parseISO(iso: string | undefined): Ymd | null { - if (!iso) return null; - const match = ISO_RE.exec(iso); - if (!match) return null; - const y = Number(match[1]); - const m = Number(match[2]) - 1; - const d = Number(match[3]); - if (!Number.isFinite(y) || m < 0 || m > 11 || d < 1 || d > 31) return null; - const dt = new Date(y, m, d); - if (dt.getFullYear() !== y || dt.getMonth() !== m || dt.getDate() !== d) { - return null; - } - return { y, m, d }; -} - -function toISO(y: number, m: number, d: number): string { - const mm = String(m + 1).padStart(2, "0"); - const dd = String(d).padStart(2, "0"); - return `${y}-${mm}-${dd}`; -} - -function ymdToDate({ y, m, d }: Ymd): Date { - return new Date(y, m, d); -} - -function compareISO(a: string, b: string): number { - return a < b ? -1 : a > b ? 1 : 0; -} +const EMPTY_RANGE: DateRangeValue = { start: "", end: "" }; function clampISO(iso: string, min?: string, max?: string): string { let next = iso; - if (min && compareISO(next, min) < 0) next = min; - if (max && compareISO(next, max) > 0) next = max; + if (min && compareISODate(next, min) < 0) next = min; + if (max && compareISODate(next, max) > 0) next = max; return next; } -function isDisabledISO(iso: string, min?: string, max?: string): boolean { - if (min && compareISO(iso, min) < 0) return true; - if (max && compareISO(iso, max) > 0) return true; - return false; -} - -/** Month has no selectable day inside min/max. */ -function isMonthDisabled( - y: number, - m: number, - min?: string, - max?: string, -): boolean { - const lastDay = new Date(y, m + 1, 0).getDate(); - const first = toISO(y, m, 1); - const last = toISO(y, m, lastDay); - if (max && compareISO(first, max) > 0) return true; - if (min && compareISO(last, min) < 0) return true; - return false; -} - -/** Year has no selectable day inside min/max. */ -function isYearDisabled(y: number, min?: string, max?: string): boolean { - const first = toISO(y, 0, 1); - const last = toISO(y, 11, 31); - if (max && compareISO(first, max) > 0) return true; - if (min && compareISO(last, min) < 0) return true; - return false; -} - -function addDays(ymd: Ymd, delta: number): Ymd { - const dt = ymdToDate(ymd); - dt.setDate(dt.getDate() + delta); - return { y: dt.getFullYear(), m: dt.getMonth(), d: dt.getDate() }; -} - -function addMonths(ymd: Ymd, delta: number): Ymd { - const dt = new Date(ymd.y, ymd.m + delta, 1); - const last = new Date(dt.getFullYear(), dt.getMonth() + 1, 0).getDate(); - const d = Math.min(ymd.d, last); - return { y: dt.getFullYear(), m: dt.getMonth(), d }; -} - -function clampDayInMonth(y: number, m: number, d: number): number { - const last = new Date(y, m + 1, 0).getDate(); - return Math.min(d, last); -} - function formatDisplay(iso: string): string { - const ymd = parseISO(iso); + const ymd = parseISODate(iso); if (!ymd) return iso; try { - return ymdToDate(ymd).toLocaleDateString(undefined, { + return new Date(ymd.y, ymd.m, ymd.d).toLocaleDateString(undefined, { year: "numeric", month: "short", day: "numeric", @@ -178,77 +83,14 @@ function formatDisplay(iso: string): string { } } -function formatMonthName(m: number): string { - try { - return new Date(2000, m, 1).toLocaleDateString(undefined, { - month: "long", - }); - } catch { - return String(m + 1); - } -} - -function formatMonthShort(m: number): string { - try { - return new Date(2000, m, 1).toLocaleDateString(undefined, { - month: "short", - }); - } catch { - return String(m + 1); - } -} - -type Cell = { - iso: string; - day: number; - inMonth: boolean; -}; - -function buildMonthGrid(viewY: number, viewM: number): Cell[] { - const first = new Date(viewY, viewM, 1); - const startPad = first.getDay(); // 0 = Sunday - const daysInMonth = new Date(viewY, viewM + 1, 0).getDate(); - const cells: Cell[] = []; - - const prevLast = new Date(viewY, viewM, 0).getDate(); - for (let i = startPad - 1; i >= 0; i -= 1) { - const d = prevLast - i; - const dt = new Date(viewY, viewM - 1, d); - cells.push({ - iso: toISO(dt.getFullYear(), dt.getMonth(), dt.getDate()), - day: d, - inMonth: false, - }); - } - - for (let d = 1; d <= daysInMonth; d += 1) { - cells.push({ - iso: toISO(viewY, viewM, d), - day: d, - inMonth: true, - }); - } - - while (cells.length % 7 !== 0) { - const last = cells[cells.length - 1]!; - const next = addDays(parseISO(last.iso)!, 1); - cells.push({ - iso: toISO(next.y, next.m, next.d), - day: next.d, - inMonth: false, - }); - } - while (cells.length < 42) { - const last = cells[cells.length - 1]!; - const next = addDays(parseISO(last.iso)!, 1); - cells.push({ - iso: toISO(next.y, next.m, next.d), - day: next.d, - inMonth: false, - }); +function formatRangeDisplay(range: DateRangeValue): string | null { + const startOk = Boolean(range.start && parseISODate(range.start)); + const endOk = Boolean(range.end && parseISODate(range.end)); + if (startOk && endOk) { + return `${formatDisplay(range.start)} – ${formatDisplay(range.end)}`; } - - return cells; + if (startOk) return `${formatDisplay(range.start)} – …`; + return null; } function CalendarIcon() { @@ -279,58 +121,10 @@ function CalendarIcon() { ); } -function ChevronLeftIcon() { - return ( - - ); -} - -function ChevronRightIcon() { - return ( - - ); -} - -const EMPTY_RANGE: DateRangeValue = { start: "", end: "" }; - -function formatRangeDisplay(range: DateRangeValue): string | null { - const startOk = Boolean(range.start && parseISO(range.start)); - const endOk = Boolean(range.end && parseISO(range.end)); - if (startOk && endOk) { - return `${formatDisplay(range.start)} – ${formatDisplay(range.end)}`; - } - if (startOk) { - return `${formatDisplay(range.start)} – …`; - } - return null; -} - -function isInRangeISO(iso: string, start: string, end: string): boolean { - if (!start || !end) return false; - return compareISO(iso, start) >= 0 && compareISO(iso, end) <= 0; -} - /** * Atom — DatePicker - * Single or range calendar. Solid trigger + frost panel. - * Day grid, plus month/year grids so you can jump without only using arrows. - * Values are ISO `YYYY-MM-DD` (local calendar; no time/locale packs). + * Solid trigger + frost panel. Panel body is the shared `Calendar` primitive. + * Values: ISO `YYYY-MM-DD` (local; no time engine). */ export function DatePicker({ mode = "single", @@ -370,7 +164,7 @@ export function DatePicker({ const isControlled = value !== undefined; const [uncontrolled, setUncontrolled] = useState(defaultValue); - const current = isControlled ? value : uncontrolled; + const current = isControlled ? value! : uncontrolled; const isRangeControlled = rangeValue !== undefined; const [uncontrolledRange, setUncontrolledRange] = @@ -378,35 +172,8 @@ export function DatePicker({ const committedRange = isRangeControlled ? (rangeValue ?? EMPTY_RANGE) : uncontrolledRange; - /** While picking second endpoint (range mode). */ - const [rangeDraft, setRangeDraft] = useState(null); - const activeRange = isRange ? (rangeDraft ?? committedRange) : EMPTY_RANGE; - - const selected = parseISO(isRange ? activeRange.start || activeRange.end : current); - - const today = useMemo(() => { - const now = new Date(); - return toISO(now.getFullYear(), now.getMonth(), now.getDate()); - }, []); - const todayYmd = parseISO(today)!; const [open, setOpen] = useState(false); - const [panelMode, setPanelMode] = useState("day"); - const [view, setView] = useState(() => { - const base = selected ?? parseISO(today)!; - return { y: base.y, m: base.m }; - }); - /** First year of the 12-year page in year mode. */ - const [yearPageStart, setYearPageStart] = useState(() => { - const base = selected ?? parseISO(today)!; - return Math.floor(base.y / YEAR_PAGE) * YEAR_PAGE; - }); - const [focusISO, setFocusISO] = useState(() => { - if (isRange) { - return committedRange.start || committedRange.end || today; - } - return current || today; - }); const { mounted: panelMounted, exiting: panelExiting, state: panelState } = usePresence(open, { durationMs: exitDurationForMotion(motion) }); @@ -426,17 +193,6 @@ export function DatePicker({ gap: 6, }); - const cells = useMemo( - () => buildMonthGrid(view.y, view.m), - [view.y, view.m], - ); - - const years = useMemo( - () => - Array.from({ length: YEAR_PAGE }, (_, i) => yearPageStart + i), - [yearPageStart], - ); - const commit = useCallback( (next: string) => { const clamped = clampISO(next, min, max); @@ -451,58 +207,29 @@ export function DatePicker({ const start = next.start ? clampISO(next.start, min, max) : ""; const end = next.end ? clampISO(next.end, min, max) : ""; let range: DateRangeValue = { start, end }; - if (start && end && compareISO(end, start) < 0) { + if (start && end && compareISODate(end, start) < 0) { range = { start: end, end: start }; } if (!isRangeControlled) setUncontrolledRange(range); onRangeValueChange?.(range); - return range; }, [isRangeControlled, max, min, onRangeValueChange], ); const close = useCallback(() => { setOpen(false); - setPanelMode("day"); - setRangeDraft(null); }, []); const openPanel = useCallback(() => { if (disabled) return; - - const baseIso = isRange - ? committedRange.start || - committedRange.end || - clampISO(today, min, max) - : current || clampISO(today, min, max); - const base = - parseISO(baseIso) ?? - parseISO(clampISO(today, min, max)) ?? - parseISO(today)!; - setView({ y: base.y, m: base.m }); - setYearPageStart(Math.floor(base.y / YEAR_PAGE) * YEAR_PAGE); - setFocusISO(toISO(base.y, base.m, base.d)); - setPanelMode("day"); - setRangeDraft(null); setOpen(true); - }, [ - committedRange.end, - committedRange.start, - current, - disabled, - isRange, - max, - min, - today, - ]); + }, [disabled]); useEffect(() => { if (!open || panelExiting) return; function onPointerDown(event: MouseEvent) { - if ( - !eventInside(event.target, rootRef.current, panelRef.current) - ) { + if (!eventInside(event.target, rootRef.current, panelRef.current)) { close(); } } @@ -510,14 +237,8 @@ export function DatePicker({ function onDocKeyDown(event: globalThis.KeyboardEvent) { if (event.key !== "Escape") return; event.preventDefault(); - // Drill back: year → month → day → close - setPanelMode((mode) => { - if (mode === "year") return "month"; - if (mode === "month") return "day"; - close(); - triggerRef.current?.focus(); - return mode; - }); + close(); + triggerRef.current?.focus(); } document.addEventListener("mousedown", onPointerDown); @@ -528,122 +249,8 @@ export function DatePicker({ }; }, [close, open, panelExiting]); - useEffect(() => { - if (!open || panelExiting || panelMode !== "day") return; - const el = panelRef.current?.querySelector( - `[data-iso="${focusISO}"]`, - ); - el?.focus({ preventScroll: true }); - }, [focusISO, open, panelExiting, panelMode, view.y, view.m]); - - function selectISO(iso: string) { - if (isDisabledISO(iso, min, max)) return; - - if (!isRange) { - commit(iso); - close(); - triggerRef.current?.focus(); - return; - } - - // Range: first click = start; second = end (swap if needed), then close. - if (!rangeDraft || !rangeDraft.start || rangeDraft.end) { - setRangeDraft({ start: iso, end: "" }); - setFocusISO(iso); - return; - } - - const completed = commitRange({ start: rangeDraft.start, end: iso }); - setRangeDraft(null); - setFocusISO(completed.end || completed.start || iso); - close(); - triggerRef.current?.focus(); - } - - /** Keep focus day when jumping month/year (clamp day into new month). */ - function jumpTo(y: number, m: number) { - const focus = parseISO(focusISO) ?? parseISO(today)!; - const d = clampDayInMonth(y, m, focus.d); - let iso = toISO(y, m, d); - if (isDisabledISO(iso, min, max)) { - // First enabled day in that month - const last = new Date(y, m + 1, 0).getDate(); - let found = false; - for (let day = 1; day <= last; day += 1) { - const candidate = toISO(y, m, day); - if (!isDisabledISO(candidate, min, max)) { - iso = candidate; - found = true; - break; - } - } - if (!found) return; - } - setView({ y, m }); - setFocusISO(iso); - } - - function selectMonth(m: number) { - if (isMonthDisabled(view.y, m, min, max)) return; - jumpTo(view.y, m); - setPanelMode("day"); - } - - function selectYear(y: number) { - if (isYearDisabled(y, min, max)) return; - const m = view.m; - // If current month is invalid in that year, pick first valid month - if (!isMonthDisabled(y, m, min, max)) { - jumpTo(y, m); - } else { - for (let month = 0; month < 12; month += 1) { - if (!isMonthDisabled(y, month, min, max)) { - jumpTo(y, month); - break; - } - } - setView((v) => ({ ...v, y })); - } - setPanelMode("month"); - } - - function moveFocus(deltaDays: number) { - const cur = parseISO(focusISO) ?? parseISO(today)!; - let next = addDays(cur, deltaDays); - let iso = toISO(next.y, next.m, next.d); - let guard = 0; - while (isDisabledISO(iso, min, max) && guard < 400) { - next = addDays(next, deltaDays > 0 ? 1 : -1); - iso = toISO(next.y, next.m, next.d); - guard += 1; - } - if (isDisabledISO(iso, min, max)) return; - setFocusISO(iso); - setView({ y: next.y, m: next.m }); - } - - function shiftByNav(delta: number) { - if (panelMode === "year") { - setYearPageStart((s) => s + delta * YEAR_PAGE); - return; - } - if (panelMode === "month") { - // Prev/next year while picking month - const y = view.y + delta; - if (isYearDisabled(y, min, max)) return; - setView((v) => ({ ...v, y })); - setYearPageStart(Math.floor(y / YEAR_PAGE) * YEAR_PAGE); - return; - } - // day mode — shift month - const focus = parseISO(focusISO) ?? parseISO(today)!; - const shifted = addMonths(focus, delta); - jumpTo(shifted.y, shifted.m); - } - function onTriggerKeyDown(event: KeyboardEvent) { if (disabled) return; - if ( event.key === "ArrowDown" || event.key === "ArrowUp" || @@ -655,66 +262,12 @@ export function DatePicker({ } } - function onDayKeyDown(event: KeyboardEvent, iso: string) { - if (event.key === "ArrowLeft") { - event.preventDefault(); - moveFocus(-1); - } else if (event.key === "ArrowRight") { - event.preventDefault(); - moveFocus(1); - } else if (event.key === "ArrowUp") { - event.preventDefault(); - moveFocus(-7); - } else if (event.key === "ArrowDown") { - event.preventDefault(); - moveFocus(7); - } else if (event.key === "PageUp") { - event.preventDefault(); - shiftByNav(event.shiftKey ? -12 : -1); - } else if (event.key === "PageDown") { - event.preventDefault(); - shiftByNav(event.shiftKey ? 12 : 1); - } else if (event.key === "Home") { - event.preventDefault(); - const start = toISO(view.y, view.m, 1); - if (!isDisabledISO(start, min, max)) setFocusISO(start); - } else if (event.key === "End") { - event.preventDefault(); - const lastDay = new Date(view.y, view.m + 1, 0).getDate(); - const end = toISO(view.y, view.m, lastDay); - if (!isDisabledISO(end, min, max)) setFocusISO(end); - } else if (event.key === "Enter" || event.key === " ") { - event.preventDefault(); - selectISO(iso); - } - } - const display = isRange - ? formatRangeDisplay(activeRange) - : current && parseISO(current) + ? formatRangeDisplay(committedRange) + : current && parseISODate(current) ? formatDisplay(current) : null; - const navPrevLabel = - panelMode === "year" - ? "Previous years" - : panelMode === "month" - ? "Previous year" - : "Previous month"; - const navNextLabel = - panelMode === "year" - ? "Next years" - : panelMode === "month" - ? "Next year" - : "Next month"; - - const headerTitle = - panelMode === "year" - ? `${yearPageStart} – ${yearPageStart + YEAR_PAGE - 1}` - : panelMode === "month" - ? String(view.y) - : null; - const control = (
) : null} {isRange && nameEnd ? ( - + ) : null} - - {panelMode === "day" ? ( -
- - -
- ) : ( - - )} - - -
- - {panelMode === "day" ? ( - <> - {isRange ? ( -

- {activeRange.start && !activeRange.end - ? "Select end date" - : "Select start date"} -

- ) : null} - - -
- {Array.from({ length: cells.length / 7 }, (_, week) => ( -
- {cells.slice(week * 7, week * 7 + 7).map((cell) => { - const rangeStart = - isRange && - Boolean(activeRange.start) && - cell.iso === activeRange.start; - const rangeEnd = - isRange && - Boolean(activeRange.end) && - cell.iso === activeRange.end; - const inRange = - isRange && - Boolean(activeRange.start && activeRange.end) && - isInRangeISO( - cell.iso, - activeRange.start, - activeRange.end, - ); - // Preview span while choosing end: start → focusISO - const draftStart = rangeDraft?.start; - const inDraftPreview = - isRange && - Boolean(draftStart) && - !rangeDraft?.end && - Boolean(focusISO) && - isInRangeISO( - cell.iso, - compareISO(draftStart!, focusISO) <= 0 - ? draftStart! - : focusISO, - compareISO(draftStart!, focusISO) <= 0 - ? focusISO - : draftStart!, - ); - const selectedDay = isRange - ? rangeStart || rangeEnd - : cell.iso === current; - const isToday = cell.iso === today; - const focused = cell.iso === focusISO; - const dayDisabled = isDisabledISO(cell.iso, min, max); - - return ( - - ); - })} -
- ))} -
- - ) : null} - - {panelMode === "month" ? (
- {Array.from({ length: 12 }, (_, m) => { - const monthDisabled = isMonthDisabled(view.y, m, min, max); - const isSelected = Boolean( - isRange - ? (activeRange.start && - parseISO(activeRange.start)?.y === view.y && - parseISO(activeRange.start)?.m === m) || - (activeRange.end && - parseISO(activeRange.end)?.y === view.y && - parseISO(activeRange.end)?.m === m) - : selected?.y === view.y && selected?.m === m, - ); - const isCurrent = - todayYmd.y === view.y && todayYmd.m === m; - return ( - - ); - })} -
- ) : null} - - {panelMode === "year" ? ( -
- {years.map((y) => { - const yearDisabled = isYearDisabled(y, min, max); - const isSelected = Boolean( - isRange - ? parseISO(activeRange.start)?.y === y || - parseISO(activeRange.end)?.y === y - : selected?.y === y, - ); - const isCurrent = todayYmd.y === y; - return ( - - ); - })} -
- ) : null} - , + { + close(); + triggerRef.current?.focus(); + }} + /> + , document.body, ) : null} ); - if (!label && !hint && !error) { - return control; - } + if (!label && !hint && !error) return control; return (
diff --git a/packages/ui/src/atoms/time-picker.test.tsx b/packages/ui/src/atoms/time-picker.test.tsx new file mode 100644 index 0000000..8dcf5a5 --- /dev/null +++ b/packages/ui/src/atoms/time-picker.test.tsx @@ -0,0 +1,28 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { describe, expect, it, vi } from "vitest"; +import { TimePicker } from "./time-picker"; + +describe("TimePicker (smoke)", () => { + it("opens panel and applies a preset", async () => { + const user = userEvent.setup(); + const onValueChange = vi.fn(); + + render( + , + ); + + await user.click(screen.getByRole("button", { name: /Start|Time/i })); + const dialog = await screen.findByRole("dialog", { name: /Time picker/i }); + expect(dialog).toBeInTheDocument(); + + await user.click(screen.getByText("12:00")); + expect(onValueChange).toHaveBeenCalledWith("12:00"); + }); +}); diff --git a/packages/ui/src/atoms/time-picker.tsx b/packages/ui/src/atoms/time-picker.tsx new file mode 100644 index 0000000..853fbdd --- /dev/null +++ b/packages/ui/src/atoms/time-picker.tsx @@ -0,0 +1,311 @@ +"use client"; + +import { cn } from "../lib/cn"; +import { MOTION_DEFAULTS, type DatePickerMotion } from "../lib/motion"; +import { exitDurationForMotion, usePresence } from "../lib/presence"; +import { + eventInside, + useFloatingPortal, +} from "../lib/use-floating-portal"; +import { TimeInput, type TimeInputLook, type TimeInputSize } from "./time-input"; +import { + useCallback, + useEffect, + useId, + useRef, + useState, + type KeyboardEvent, + type ReactNode, +} from "react"; +import { createPortal } from "react-dom"; + +export type TimePickerSize = TimeInputSize; +export type TimePickerLook = TimeInputLook; +export type TimePickerMotion = DatePickerMotion; + +export type TimePickerProps = { + value?: string; + defaultValue?: string; + onValueChange?: (value: string) => void; + /** Quick picks under the control (HH:mm 24h). */ + presets?: string[]; + size?: TimePickerSize; + look?: TimePickerLook; + motion?: TimePickerMotion; + hourCycle?: 24 | 12; + minuteStep?: number; + label?: ReactNode; + hint?: ReactNode; + error?: ReactNode; + disabled?: boolean; + placeholder?: string; + name?: string; + id?: string; + className?: string; + placement?: "auto" | "bottom" | "top"; + "aria-label"?: string; +}; + +const DEFAULT_PRESETS = ["09:00", "12:00", "13:00", "17:00", "18:00"] as const; + +const sizeClass: Record = { + sm: "sg-datepicker-trigger-sm", + md: "", + lg: "sg-datepicker-trigger-lg", +}; + +function ClockIcon() { + return ( + + ); +} + +/** + * Atom — TimePicker + * Trigger + portaled panel with TimeInput and optional presets. + * Value is always 24h `HH:mm`. No timezone engine. + */ +export function TimePicker({ + value, + defaultValue = "09:00", + onValueChange, + presets = [...DEFAULT_PRESETS], + size = "md", + look = "soft", + motion = MOTION_DEFAULTS.datePicker, + hourCycle = 24, + minuteStep = 5, + label, + hint, + error, + disabled, + placeholder = "Pick a time…", + name, + id, + className, + placement = "auto", + "aria-label": ariaLabel, +}: TimePickerProps) { + const reactId = useId(); + const pickerId = id ?? `sg-timepicker-${reactId}`; + const panelId = `${pickerId}-panel`; + const hintId = hint ? `${pickerId}-hint` : undefined; + const errorId = error ? `${pickerId}-error` : undefined; + const describedBy = [hintId, errorId].filter(Boolean).join(" ") || undefined; + const invalid = Boolean(error); + + const isControlled = value !== undefined; + const [uncontrolled, setUncontrolled] = useState(defaultValue); + const current = isControlled ? value! : uncontrolled; + + const [open, setOpen] = useState(false); + const [draft, setDraft] = useState(current); + const { mounted: panelMounted, exiting: panelExiting, state: panelState } = + usePresence(open, { durationMs: exitDurationForMotion(motion) }); + + const rootRef = useRef(null); + const triggerRef = useRef(null); + const panelRef = useRef(null); + + const { portalReady, floatingStyle, side: menuPlacement } = useFloatingPortal({ + open, + mounted: panelMounted, + triggerRef, + panelRef, + placement, + align: "start", + matchWidth: false, + flipMinSpace: 240, + gap: 6, + }); + + const commit = useCallback( + (next: string) => { + if (!isControlled) setUncontrolled(next); + onValueChange?.(next); + }, + [isControlled, onValueChange], + ); + + const close = useCallback(() => setOpen(false), []); + + const openPanel = useCallback(() => { + if (disabled) return; + setDraft(current); + setOpen(true); + }, [current, disabled]); + + useEffect(() => { + if (!open || panelExiting) return; + + function onPointerDown(event: MouseEvent) { + if (!eventInside(event.target, rootRef.current, panelRef.current)) { + close(); + } + } + + function onDocKeyDown(event: globalThis.KeyboardEvent) { + if (event.key !== "Escape") return; + event.preventDefault(); + close(); + triggerRef.current?.focus(); + } + + document.addEventListener("mousedown", onPointerDown); + document.addEventListener("keydown", onDocKeyDown); + return () => { + document.removeEventListener("mousedown", onPointerDown); + document.removeEventListener("keydown", onDocKeyDown); + }; + }, [close, open, panelExiting]); + + function onTriggerKeyDown(event: KeyboardEvent) { + if (disabled) return; + if ( + event.key === "ArrowDown" || + event.key === "Enter" || + event.key === " " + ) { + event.preventDefault(); + if (!open) openPanel(); + } + } + + function applyDraft() { + commit(draft); + close(); + triggerRef.current?.focus(); + } + + function pickPreset(hhmm: string) { + setDraft(hhmm); + commit(hhmm); + close(); + triggerRef.current?.focus(); + } + + const control = ( +
+ {name ? : null} + + + {panelMounted && portalReady + ? createPortal( + , + document.body, + ) + : null} +
+ ); + + if (!label && !hint && !error) return control; + + return ( +
+ {label ? ( + + ) : null} + {control} + {hint && !error ? ( +

+ {hint} +

+ ) : null} + {error ? ( + + ) : null} +
+ ); +} diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 83874d3..261404e 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -87,13 +87,25 @@ export type { ComboboxProps } from "./atoms/combobox"; export { MultiSelect } from "./atoms/multi-select"; export type { MultiSelectProps } from "./atoms/multi-select"; +export { Calendar } from "./atoms/calendar"; +export type { + CalendarMode, + CalendarProps, + DateRangeValue, +} from "./atoms/calendar"; +export { + compareISODate, + parseISODate, + toISODate, +} from "./atoms/calendar"; + export { DatePicker } from "./atoms/date-picker"; export type { DatePickerMode, DatePickerProps, DatePickerSize, - DateRangeValue, } from "./atoms/date-picker"; +// DateRangeValue re-exported from calendar (shared) export { Checkbox } from "./atoms/checkbox"; export type { CheckboxProps } from "./atoms/checkbox"; @@ -430,6 +442,14 @@ export type { TimeInputSize, } from "./atoms/time-input"; +export { TimePicker } from "./atoms/time-picker"; +export type { + TimePickerLook, + TimePickerMotion, + TimePickerProps, + TimePickerSize, +} from "./atoms/time-picker"; + export { ClientOnly } from "./atoms/client-only"; export type { ClientOnlyProps } from "./atoms/client-only"; diff --git a/registry.json b/registry.json index 752b0cc..e16ea94 100644 --- a/registry.json +++ b/registry.json @@ -447,13 +447,45 @@ } ] }, + { + "name": "calendar", + "type": "registry:ui", + "title": "Calendar", + "description": "Inline day/month/year grid — single or range ISO dates.", + "registryDependencies": [ + "tokens" + ], + "files": [ + { + "path": "packages/ui/src/atoms/calendar.tsx", + "type": "registry:ui", + "target": "components/softglass/atoms/calendar.tsx" + }, + { + "path": "packages/ui/src/lib/cn.ts", + "type": "registry:lib", + "target": "components/softglass/lib/cn.ts" + }, + { + "path": "packages/ui/src/lib/looks.ts", + "type": "registry:lib", + "target": "components/softglass/lib/looks.ts" + }, + { + "path": "packages/ui/src/lib/motion.ts", + "type": "registry:lib", + "target": "components/softglass/lib/motion.ts" + } + ] + }, { "name": "date-picker", "type": "registry:ui", "title": "Date Picker", - "description": "Single ISO date calendar — body portal + flip/clamp panel.", + "description": "Single or range ISO dates — portal panel over Calendar.", "registryDependencies": [ - "tokens" + "tokens", + "calendar" ], "files": [ { @@ -493,6 +525,53 @@ } ] }, + { + "name": "time-picker", + "type": "registry:ui", + "title": "Time Picker", + "description": "HH:mm trigger + panel with TimeInput and presets.", + "registryDependencies": [ + "tokens", + "time-input" + ], + "files": [ + { + "path": "packages/ui/src/atoms/time-picker.tsx", + "type": "registry:ui", + "target": "components/softglass/atoms/time-picker.tsx" + }, + { + "path": "packages/ui/src/lib/cn.ts", + "type": "registry:lib", + "target": "components/softglass/lib/cn.ts" + }, + { + "path": "packages/ui/src/lib/looks.ts", + "type": "registry:lib", + "target": "components/softglass/lib/looks.ts" + }, + { + "path": "packages/ui/src/lib/motion.ts", + "type": "registry:lib", + "target": "components/softglass/lib/motion.ts" + }, + { + "path": "packages/ui/src/lib/presence.ts", + "type": "registry:lib", + "target": "components/softglass/lib/presence.ts" + }, + { + "path": "packages/ui/src/lib/floating.ts", + "type": "registry:lib", + "target": "components/softglass/lib/floating.ts" + }, + { + "path": "packages/ui/src/lib/use-floating-portal.ts", + "type": "registry:lib", + "target": "components/softglass/lib/use-floating-portal.ts" + } + ] + }, { "name": "tooltip", "type": "registry:ui", diff --git a/registry/registry.json b/registry/registry.json index 752b0cc..e16ea94 100644 --- a/registry/registry.json +++ b/registry/registry.json @@ -447,13 +447,45 @@ } ] }, + { + "name": "calendar", + "type": "registry:ui", + "title": "Calendar", + "description": "Inline day/month/year grid — single or range ISO dates.", + "registryDependencies": [ + "tokens" + ], + "files": [ + { + "path": "packages/ui/src/atoms/calendar.tsx", + "type": "registry:ui", + "target": "components/softglass/atoms/calendar.tsx" + }, + { + "path": "packages/ui/src/lib/cn.ts", + "type": "registry:lib", + "target": "components/softglass/lib/cn.ts" + }, + { + "path": "packages/ui/src/lib/looks.ts", + "type": "registry:lib", + "target": "components/softglass/lib/looks.ts" + }, + { + "path": "packages/ui/src/lib/motion.ts", + "type": "registry:lib", + "target": "components/softglass/lib/motion.ts" + } + ] + }, { "name": "date-picker", "type": "registry:ui", "title": "Date Picker", - "description": "Single ISO date calendar — body portal + flip/clamp panel.", + "description": "Single or range ISO dates — portal panel over Calendar.", "registryDependencies": [ - "tokens" + "tokens", + "calendar" ], "files": [ { @@ -493,6 +525,53 @@ } ] }, + { + "name": "time-picker", + "type": "registry:ui", + "title": "Time Picker", + "description": "HH:mm trigger + panel with TimeInput and presets.", + "registryDependencies": [ + "tokens", + "time-input" + ], + "files": [ + { + "path": "packages/ui/src/atoms/time-picker.tsx", + "type": "registry:ui", + "target": "components/softglass/atoms/time-picker.tsx" + }, + { + "path": "packages/ui/src/lib/cn.ts", + "type": "registry:lib", + "target": "components/softglass/lib/cn.ts" + }, + { + "path": "packages/ui/src/lib/looks.ts", + "type": "registry:lib", + "target": "components/softglass/lib/looks.ts" + }, + { + "path": "packages/ui/src/lib/motion.ts", + "type": "registry:lib", + "target": "components/softglass/lib/motion.ts" + }, + { + "path": "packages/ui/src/lib/presence.ts", + "type": "registry:lib", + "target": "components/softglass/lib/presence.ts" + }, + { + "path": "packages/ui/src/lib/floating.ts", + "type": "registry:lib", + "target": "components/softglass/lib/floating.ts" + }, + { + "path": "packages/ui/src/lib/use-floating-portal.ts", + "type": "registry:lib", + "target": "components/softglass/lib/use-floating-portal.ts" + } + ] + }, { "name": "tooltip", "type": "registry:ui", diff --git a/scripts/generate-registry.mjs b/scripts/generate-registry.mjs index dfea150..fe532b1 100644 --- a/scripts/generate-registry.mjs +++ b/scripts/generate-registry.mjs @@ -92,12 +92,25 @@ const registry = { "packages/ui/src/atoms/multi-select.tsx", { extra: [PRESENCE, FLOATING, FLOATING_HOOK] }, ), + ui( + "calendar", + "Calendar", + "Inline day/month/year grid — single or range ISO dates.", + "packages/ui/src/atoms/calendar.tsx", + ), ui( "date-picker", "Date Picker", - "Single ISO date calendar — body portal + flip/clamp panel.", + "Single or range ISO dates — portal panel over Calendar.", "packages/ui/src/atoms/date-picker.tsx", - { extra: [PRESENCE, FLOATING, FLOATING_HOOK] }, + { extra: [PRESENCE, FLOATING, FLOATING_HOOK], deps: ["calendar"] }, + ), + ui( + "time-picker", + "Time Picker", + "HH:mm trigger + panel with TimeInput and presets.", + "packages/ui/src/atoms/time-picker.tsx", + { extra: [PRESENCE, FLOATING, FLOATING_HOOK], deps: ["time-input"] }, ), ui("tooltip", "Tooltip", "Frost tooltip on hover/focus.", "packages/ui/src/atoms/tooltip.tsx"), ui("alert", "Alert", "Inline callout — info / success / warning / danger.", "packages/ui/src/atoms/alert.tsx"),