{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "toggle-group",
  "title": "Toggle group",
  "description": "Words in a row you can hold down, one or many; held neighbours are tied under one slur or share one pair of parentheses, held words are numbered in the order you held them, or cross into the margin.",
  "dependencies": [
    "@radix-ui/react-toggle-group"
  ],
  "registryDependencies": [
    "https://thedirectors.agency/ui/r/0nlytype.json"
  ],
  "files": [
    {
      "path": "registry/0nlytype/ui/toggle-group.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as ToggleGroupPrimitive from \"@radix-ui/react-toggle-group\"\n\nimport { useComposedRefs } from \"@/lib/0nlytype/refs\"\nimport { cn } from \"@/lib/utils\"\n\ntype Run = { items: HTMLElement[]; l: number; r: number; t: number; h: number }\ntype Mark = { el: HTMLElement; items: HTMLElement[] }\n\n/**\n * One mark per run of held words that stand side by side on a row: the slur over them, or the\n * parentheses round them. The mark reads --l, --r, --t and --h from the run's words: from the\n * italic you see, not the cell it shares with the wider or narrower roman, so the mark sits even\n * on both sides. Physical measurements, so right to left needs nothing more.\n *\n * As the held words change, a mark that covered one of a run's words stretches to fit it; another\n * mark that also did (two runs just joined) fades where it is; a run with no mark gets one, which\n * lands; a mark with no run lets go. With one held at a time, the mark glides from the old word to\n * the new one, leading edge first (data-dir). The engine also marks the first word of each wrapped\n * row (data-line-start), so no hairline stands at the start of a line.\n *\n * fingering: each held word carries the order it was held in (--n), set over the middle of the face\n * you see (--fx), and the group carries the number the next one would get (--next). Seeded from the\n * value's order, which is the order Radix keeps.\n */\nfunction useMarks(single: boolean, fingering: boolean, seed: readonly string[]) {\n  const ref = React.useRef<HTMLDivElement | null>(null)\n  const first = React.useRef(seed)\n  React.useLayoutEffect(() => {\n    const root = ref.current\n    const layer = root?.querySelector<HTMLElement>(\":scope > .ot-toggles-marks\")\n    if (!root || !layer) return\n    let marks: Mark[] = []\n    const all = () => [...root.querySelectorAll<HTMLElement>('[data-slot=\"toggle-group-item\"]')]\n    let order = first.current.flatMap((v) => all().filter((i) => i.dataset.value === v && i.dataset.state === \"on\"))\n\n    // The face you see, left and right in px: held, the italic copy; otherwise the roman. They share\n    // one cell (start-aligned on the first word of a line, centred elsewhere) but not one width.\n    const face = (item: HTMLElement, k: number, rtl: boolean) => {\n      const word = item.querySelector<HTMLElement>(\":scope > .ot-toggles-word\")\n      if (!word) {\n        const r = item.getBoundingClientRect()\n        const cs = getComputedStyle(item)\n        return [r.left + parseFloat(cs.paddingLeft) * k, r.right - parseFloat(cs.paddingRight) * k]\n      }\n      if (item.dataset.state !== \"on\") {\n        const r = word.firstElementChild!.getBoundingClientRect()\n        return [r.left, r.right]\n      }\n      const cell = word.getBoundingClientRect()\n      const w = parseFloat(getComputedStyle(word, \"::after\").width) * k\n      const l = getComputedStyle(word).justifyItems.includes(\"center\") ? (cell.left + cell.right - w) / 2 : rtl ? cell.right - w : cell.left\n      return [l, l + w]\n    }\n\n    const place = (moving: boolean) => {\n      // Resizes, fonts arriving and the first place are not a choice: the marks go straight there.\n      if (!moving) root.setAttribute(\"data-still\", \"\")\n      const b = root.getBoundingClientRect()\n      const k = b.width / root.offsetWidth || 1 // on-screen px per CSS px: a scaled ancestor would throw the marks off\n      const rtl = getComputedStyle(root).direction === \"rtl\"\n      const items = all()\n      const runs: Run[] = []\n      let run: Run | undefined\n      let top: number | undefined\n      for (const item of items) {\n        const r = item.getBoundingClientRect()\n        const turned = top !== undefined && Math.abs(r.top - top) > 1\n        top = r.top\n        item.toggleAttribute(\"data-line-start\", turned)\n      }\n      for (const item of items) {\n        const r = item.getBoundingClientRect()\n        const turned = item.hasAttribute(\"data-line-start\")\n        const [fl, fr] = face(item, k, rtl)\n        if (fingering) item.style.setProperty(\"--fx\", `${((fl + fr) / 2 - r.left) / k}px`)\n        if (item.dataset.state !== \"on\") run = undefined\n        else {\n          // The mark stands off the italic by the item's own padding, the room its parentheses hang in.\n          const cs = getComputedStyle(item)\n          const l = fl - parseFloat(cs.paddingLeft) * k\n          const rr = fr + parseFloat(cs.paddingRight) * k\n          if (run && !turned) {\n            run.items.push(item)\n            run.l = Math.min(run.l, l)\n            run.r = Math.max(run.r, rr)\n          } else runs.push((run = { items: [item], l, r: rr, t: r.top, h: r.height }))\n        }\n      }\n      if (fingering) {\n        const on = items.filter((i) => i.dataset.state === \"on\")\n        order = order.filter((i) => on.includes(i)).concat(on.filter((i) => !order.includes(i)))\n        order.forEach((i, n) => i.style.setProperty(\"--n\", `\"${n + 1}\"`))\n        root.style.setProperty(\"--next\", `\"${order.length + 1}\"`)\n      }\n\n      const set = (mark: Mark, run: Run, glide = false) => {\n        const { el } = mark\n        const l = (run.l - b.left) / k\n        const r = (b.right - run.r) / k\n        const was = parseFloat(el.style.getPropertyValue(\"--l\")) - parseFloat(el.style.getPropertyValue(\"--r\"))\n        if (glide) el.dataset.dir = l - r >= was ? \"right\" : \"left\"\n        else delete el.dataset.dir\n        el.style.setProperty(\"--l\", `${l}px`)\n        el.style.setProperty(\"--r\", `${r}px`)\n        el.style.setProperty(\"--t\", `${(run.t - b.top) / k}px`)\n        el.style.setProperty(\"--h\", `${run.h / k}px`)\n        el.toggleAttribute(\"data-muted\", run.items.every((i) => i.hasAttribute(\"data-disabled\")))\n        mark.items = run.items\n        return mark\n      }\n      const leave = (el: HTMLElement, how: \"merge\" | \"release\") => {\n        el.setAttribute(\"data-leave\", how)\n        const done = () => el.remove()\n        Promise.all(el.getAnimations({ subtree: true }).map((a) => a.finished)).then(done, done)\n      }\n\n      const claimed = new Set<Mark>()\n      const next: Mark[] = []\n      const fresh: Run[] = []\n      for (const run of runs) {\n        const over = marks.filter((m) => !claimed.has(m) && m.items.some((i) => run.items.includes(i)))\n        if (!over.length) {\n          fresh.push(run)\n          continue\n        }\n        over.forEach((m) => claimed.add(m))\n        over.slice(1).forEach((m) => leave(m.el, \"merge\"))\n        next.push(set(over[0], run))\n      }\n      const entering: HTMLElement[] = []\n      for (const run of fresh) {\n        const left = single ? marks.find((m) => !claimed.has(m)) : undefined\n        if (left) {\n          claimed.add(left)\n          next.push(set(left, run, moving))\n          continue\n        }\n        const el = document.createElement(\"i\")\n        el.className = \"ot-toggles-mark\"\n        el.setAttribute(\"data-enter\", \"\")\n        layer.append(el)\n        entering.push(el)\n        next.push(set({ el, items: [] }, run))\n      }\n      marks.filter((m) => !claimed.has(m)).forEach((m) => leave(m.el, \"release\"))\n      marks = next\n\n      // Read once so the entering marks start from their first frame, then let them land.\n      void root.offsetWidth\n      entering.forEach((el) => el.removeAttribute(\"data-enter\"))\n      if (!moving) {\n        void root.offsetWidth\n        root.removeAttribute(\"data-still\")\n      }\n    }\n\n    place(false)\n    // A choice moves the marks; the direction arriving (read as the group mounts) only puts them in place.\n    const chose = new MutationObserver((list) => {\n      const own = list.filter((m) => !layer.contains(m.target))\n      if (own.length) place(!own.some((m) => m.attributeName === \"dir\"))\n    })\n    chose.observe(root, { attributes: true, attributeFilter: [\"data-state\", \"data-disabled\", \"dir\"], subtree: true, childList: true })\n    const resize = new ResizeObserver(() => place(false))\n    resize.observe(root)\n    root.querySelectorAll('[data-slot=\"toggle-group-item\"]').forEach((i) => resize.observe(i))\n    document.fonts?.ready.then(() => place(false))\n    return () => {\n      chose.disconnect()\n      resize.disconnect()\n      layer.replaceChildren()\n    }\n  }, [single, fingering])\n  return ref\n}\n\ntype ToggleGroupProps = React.ComponentProps<typeof ToggleGroupPrimitive.Root> & {\n  /**\n   * How held words are marked. slur: hairlines stand between the words, and a run of held neighbours\n   * is tied under one slur. bracket: no hairlines; the held words are set in parentheses, and\n   * neighbours share one pair. fingering: each held word carries, over it, the number of its turn\n   * (for type=\"multiple\"). margin: the words stand in a column beside a hairline and an empty\n   * margin; a held word crosses the hairline into the margin, like a book's side head.\n   */\n  variant?: \"slur\" | \"bracket\" | \"fingering\" | \"margin\"\n}\n\n/**\n * Words in a row that you can hold down. type=\"single\" holds one at a time, \"multiple\" any number.\n * Held, a word is yours, so it turns italic; its mark (a slur, or parentheses) stretches over the\n * held words beside it and glides to a new choice. Radix keeps the roving focus and the arrow keys.\n */\nfunction ToggleGroup({ className, variant = \"slur\", dir, children, ref, ...props }: ToggleGroupProps) {\n  const held = props.value ?? props.defaultValue\n  const ownRef = useMarks(props.type === \"single\", variant === \"fingering\", typeof held === \"string\" ? [held] : (held ?? []))\n  // Radix writes dir=\"ltr\" on the group unless told otherwise, which would turn a right-to-left page's\n  // words and arrow keys around. Without a dir, the group takes the direction of the page around it,\n  // read as the group mounts, before the first paint.\n  const [around, setAround] = React.useState<\"rtl\">()\n  const composedRef = useComposedRefs(React.useCallback((node: HTMLDivElement | null) => {\n    ownRef.current = node\n    const up = node?.parentElement\n    if (!dir && up && getComputedStyle(up).direction === \"rtl\") setAround(\"rtl\")\n  }, [dir, ownRef]), ref)\n  return (\n    <ToggleGroupPrimitive.Root\n      ref={composedRef}\n      data-slot=\"toggle-group\"\n      data-variant={variant}\n      dir={dir ?? around}\n      className={cn(\"ot-toggles\", className)}\n      {...props}\n    >\n      {children}\n      <span data-slot=\"toggle-group-marks\" className=\"ot-toggles-marks\" aria-hidden=\"true\" />\n    </ToggleGroupPrimitive.Root>\n  )\n}\n\ntype ToggleGroupItemProps = React.ComponentProps<typeof ToggleGroupPrimitive.Item> & {\n  /** Pins a state for documentation (\"hover\", \"focus\"); set on the root. */\n  \"data-force\"?: string\n}\n\n/**\n * One word in the group. A plain-text word shares its grid cell with an italic copy of itself (drawn\n * by the stylesheet from data-text), so it is always as wide as its wider state and nothing beside it\n * moves when it turns.\n */\nfunction ToggleGroupItem({ className, children, ...props }: ToggleGroupItemProps) {\n  return (\n    <ToggleGroupPrimitive.Item data-slot=\"toggle-group-item\" data-value={props.value} className={cn(\"ot-toggles-item\", className)} {...props}>\n      {typeof children === \"string\" ? (\n        <span className=\"ot-toggles-word\" data-text={children}>\n          <span>{children}</span>\n        </span>\n      ) : (\n        children\n      )}\n    </ToggleGroupPrimitive.Item>\n  )\n}\n\nexport { ToggleGroup, ToggleGroupItem, type ToggleGroupProps, type ToggleGroupItemProps }\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/0nlytype/styles/toggle-group.css",
      "content": "@layer components {\n  /* ── ot-toggles ── toggle group: words in a row that you can hold down. Held, a word is yours and turns\n     italic, and one mark stretches over each run of held neighbours: a slur (the score's sign for notes\n     played together), or, in the bracket variant, a pair of parentheses. The marks are drawn by\n     toggle-group.tsx from --l, --r, --t and --h; everything else is here, so the group needs no other item. */\n  .ot-toggles {\n    --gap: 1.3em;  /* between two words */\n    --room: 0.62em; /* over the words, where the slur arches */\n    position: relative; display: flex; flex-wrap: wrap; align-items: baseline; column-gap: var(--gap); row-gap: var(--ot-space-2);\n    font-size: var(--ot-mp); line-height: 1.2; letter-spacing: var(--ot-mp-tr);\n  }\n  .ot-toggles-item {\n    position: relative; appearance: none; border: 0; background: none; margin: 0; padding: var(--room) 0 calc(var(--room) / 2);\n    font: inherit; letter-spacing: inherit; color: var(--ot-graphite); cursor: pointer; -webkit-tap-highlight-color: transparent;\n    /* The focus ring rests open and invisible, so it can close onto the word you arrive at. It rings the\n       whole button, so it holds the word together with its mark: the slur over it, or its parentheses. */\n    outline: var(--ot-stroke) solid transparent; outline-offset: 12px;\n    transition: color var(--ot-allegro), outline-color var(--ot-allegro), outline-offset var(--ot-moderato) var(--ot-spiccato);\n  }\n  .ot-toggles-item:is(:focus-visible, [data-force~=\"focus\"]) { outline-color: var(--ot-accent); outline-offset: 5px; }\n  .ot-toggles-item:is(:hover, [data-force~=\"hover\"]):not(:disabled),\n  .ot-toggles-item[data-state=\"on\"] { color: var(--ot-ink); }\n  .ot-toggles-item:disabled { color: var(--ot-pencil); opacity: 0.55; cursor: not-allowed; }\n\n  /* The word and its italic copy share one cell: the roman sinks away as the italic rises into its place.\n     Centred, the spare width splits evenly either side; the first word of each line starts at the cell's\n     start edge instead, so the line lines up with the label above it. */\n  .ot-toggles-word { display: inline-grid; justify-items: center; align-items: baseline; transition: translate var(--ot-allegro) var(--ot-exhale); }\n  .ot-toggles-item:is(:first-child, [data-line-start]) .ot-toggles-word { justify-items: start; }\n  /* The roman carries its own pencil sketch, so the sketch sits even on the word you see, whatever the cell's width. */\n  .ot-toggles-word > span { position: relative; --up: -0.12em; }\n  .ot-toggles-word > span,\n  .ot-toggles-word::after { grid-area: 1 / 1; transition: opacity var(--ot-moderato) var(--ot-breath), translate var(--ot-moderato) var(--ot-exhale); }\n  .ot-toggles-word::after {\n    content: attr(data-text) / \"\";\n    font-family: var(--ot-expression); font-style: italic; font-weight: 400; font-size: calc(var(--ot-expression-scale) * 1em); line-height: 1; letter-spacing: 0;\n    opacity: 0; translate: 0 0.16em;\n  }\n  .ot-toggles-item[data-state=\"on\"] .ot-toggles-word > span { opacity: 0; translate: 0 -0.16em; }\n  .ot-toggles-item[data-state=\"on\"] .ot-toggles-word::after { opacity: 1; translate: 0 0; }\n  /* A word that isn't plain text has no copy to turn to, so it turns in place. */\n  .ot-toggles-item[data-state=\"on\"]:not(:has(.ot-toggles-word)) { font-family: var(--ot-expression); font-style: italic; }\n  /* Pressing sets the word down a hair before it turns. */\n  .ot-toggles-item:active:not(:disabled) .ot-toggles-word { translate: 0 1px; }\n\n  /* The marks: one per run of held neighbours, laid over the words and moved by their edges. */\n  .ot-toggles-marks { position: absolute; inset: 0; pointer-events: none; }\n  .ot-toggles-mark {\n    position: absolute; left: var(--l); right: var(--r); top: var(--t); height: var(--h);\n    container-type: inline-size; color: var(--ot-ink);\n    transition: left var(--ot-moderato) var(--ot-exhale), right var(--ot-moderato) var(--ot-exhale), top var(--ot-moderato) var(--ot-breath), height var(--ot-moderato) var(--ot-breath);\n  }\n  /* Gliding to a new word (one at a time), the leading edge sets off first and the other follows: legato. */\n  .ot-toggles-mark[data-dir] { transition-duration: var(--ot-andante); transition-timing-function: var(--ot-breath); }\n  .ot-toggles-mark[data-dir=\"right\"] { transition-delay: calc(var(--ot-arpeggio) * 3), 0s, 0s, 0s; }\n  .ot-toggles-mark[data-dir=\"left\"] { transition-delay: 0s, calc(var(--ot-arpeggio) * 3), 0s, 0s; }\n  .ot-toggles-mark[data-muted] { color: var(--ot-pencil); opacity: 0.55; }\n  .ot-toggles[data-still] .ot-toggles-mark,\n  .ot-toggles[data-still] .ot-toggles-mark::before,\n  .ot-toggles[data-still] .ot-toggles-mark::after { transition: none; }\n\n  /* ── slur (the default) ── hairlines stand between the words; the held ones are tied under a slur */\n  .ot-toggles[data-variant=\"slur\"] > .ot-toggles-item + .ot-toggles-item::before {\n    content: \"\"; position: absolute; inset-inline-start: calc(var(--gap) / -2); bottom: calc(var(--room) / 2 + 0.08em); height: 1.02em;\n    border-inline-start: var(--ot-hairline) solid var(--ot-rule-strong); transform-origin: 50% 100%; pointer-events: none;\n    transition: scale var(--ot-moderato) var(--ot-breath);\n  }\n  /* Two held neighbours are one phrase: the hairline between them lies down under the slur. */\n  .ot-toggles[data-variant=\"slur\"] > .ot-toggles-item[data-state=\"on\"] + .ot-toggles-item[data-state=\"on\"]::before { scale: 1 0; }\n  .ot-toggles[data-variant=\"slur\"] > .ot-toggles-item[data-line-start]::before { content: none; }\n  /* The slur is engraved, not drawn: one border on a half-ellipse, so it swells in the middle and tapers to\n     points. It bows deeper the further it reaches (cqi is the mark's own width). */\n  .ot-toggles[data-variant=\"slur\"] :is(.ot-toggles-mark, .ot-toggles-item:not(:has(> .ot-toggles-word)), .ot-toggles-word > span)::after {\n    content: \"\"; position: absolute; inset-inline: 0.1em; top: var(--up, calc(var(--room) - 0.12em)); height: clamp(0.2em, 3.2cqi, 0.44em); translate: 0 -100%;\n    border-top: calc(var(--ot-stroke) * 1.4) solid currentColor; border-radius: 50% 50% 0 0 / 100% 100% 0 0;\n    transform-origin: 50% 100%; pointer-events: none;\n  }\n  .ot-toggles[data-variant=\"slur\"] .ot-toggles-mark::after { transition: scale var(--ot-moderato) var(--ot-spiccato), opacity var(--ot-allegro); }\n  .ot-toggles[data-variant=\"slur\"] .ot-toggles-mark[data-enter]::after { scale: 0.4 0; opacity: 0; }\n  .ot-toggles[data-variant=\"slur\"] .ot-toggles-mark[data-leave=\"release\"]::after { scale: 0.4 0; opacity: 0; transition-timing-function: var(--ot-breath); }\n  /* Pointing sketches the slur in pencil, from the middle out, over the roman. */\n  .ot-toggles[data-variant=\"slur\"] :is(.ot-toggles-item:not(:has(> .ot-toggles-word)), .ot-toggles-word > span)::after {\n    height: 0.22em; border-top-width: var(--ot-hairline); border-top-color: var(--ot-rule-strong);\n    scale: 0 1; opacity: 0; transition: scale var(--ot-moderato) var(--ot-exhale), opacity var(--ot-allegro);\n  }\n  .ot-toggles[data-variant=\"slur\"] .ot-toggles-item:is(:hover, [data-force~=\"hover\"]):not(:disabled, [data-state=\"on\"]) > .ot-toggles-word > span::after,\n  .ot-toggles[data-variant=\"slur\"] .ot-toggles-item:not(:has(> .ot-toggles-word)):is(:hover, [data-force~=\"hover\"]):not(:disabled, [data-state=\"on\"])::after { scale: 1; opacity: 1; }\n\n  /* ── bracket ── no hairlines: the held words are set in parentheses, after 20(25), and neighbours share one pair */\n  /* The room for the parentheses hangs into the margin, so the words at rest start where the label does. */\n  .ot-toggles[data-variant=\"bracket\"] { --gap: 0.2em; --room: 0em; --pad: 0.55em; row-gap: var(--ot-space-3); margin-inline: calc(var(--pad) * -1); }\n  /* The ring already stands clear of the word by the room for the parentheses, so it closes onto the button's edge. */\n  .ot-toggles[data-variant=\"bracket\"] .ot-toggles-item { padding-inline: var(--pad); }\n  .ot-toggles[data-variant=\"bracket\"] .ot-toggles-item:is(:focus-visible, [data-force~=\"focus\"]) { outline-offset: 1px; }\n  .ot-toggles[data-variant=\"bracket\"] :is(.ot-toggles-mark, .ot-toggles-item:not(:has(> .ot-toggles-word)), .ot-toggles-word > span)::before,\n  .ot-toggles[data-variant=\"bracket\"] :is(.ot-toggles-mark, .ot-toggles-item:not(:has(> .ot-toggles-word)), .ot-toggles-word > span)::after {\n    position: absolute; inset-block: 0; display: grid; align-items: center; pointer-events: none;\n    font-family: var(--ot-expression); font-style: italic; font-weight: 400; font-size: calc(var(--ot-expression-scale) * 1.18em); line-height: 1;\n    direction: ltr; /* a parenthesis mirrors in right-to-left text; these are placed by hand, so they mustn't */\n    transition: translate var(--ot-moderato) var(--ot-spiccato), opacity var(--ot-allegro), color var(--ot-allegro);\n  }\n  .ot-toggles[data-variant=\"bracket\"] :is(.ot-toggles-mark, .ot-toggles-item:not(:has(> .ot-toggles-word)), .ot-toggles-word > span)::before { content: \"(\" / \"\"; left: var(--side, 0); }\n  .ot-toggles[data-variant=\"bracket\"] :is(.ot-toggles-mark, .ot-toggles-item:not(:has(> .ot-toggles-word)), .ot-toggles-word > span)::after { content: \")\" / \"\"; right: var(--side, 0); }\n  /* Over the roman, the pencil pair stands off it by the same room the ink pair keeps round the italic. */\n  .ot-toggles[data-variant=\"bracket\"] .ot-toggles-word > span { --side: calc(var(--pad) * -1); }\n  .ot-toggles[data-variant=\"bracket\"] .ot-toggles-mark:is([data-enter], [data-leave=\"release\"])::before { translate: -0.35em 0; opacity: 0; }\n  .ot-toggles[data-variant=\"bracket\"] .ot-toggles-mark:is([data-enter], [data-leave=\"release\"])::after { translate: 0.35em 0; opacity: 0; }\n  /* Pointing sketches a pair in pencil, standing a little apart; holding closes the ink pair in. */\n  .ot-toggles[data-variant=\"bracket\"] :is(.ot-toggles-item:not(:has(> .ot-toggles-word)), .ot-toggles-word > span)::before,\n  .ot-toggles[data-variant=\"bracket\"] :is(.ot-toggles-item:not(:has(> .ot-toggles-word)), .ot-toggles-word > span)::after { color: var(--ot-rule-strong); opacity: 0; }\n  .ot-toggles[data-variant=\"bracket\"] :is(.ot-toggles-item:not(:has(> .ot-toggles-word)), .ot-toggles-word > span)::before { translate: -0.3em 0; }\n  .ot-toggles[data-variant=\"bracket\"] :is(.ot-toggles-item:not(:has(> .ot-toggles-word)), .ot-toggles-word > span)::after { translate: 0.3em 0; }\n  .ot-toggles[data-variant=\"bracket\"] .ot-toggles-item:is(:hover, [data-force~=\"hover\"]):not(:disabled, [data-state=\"on\"]) > .ot-toggles-word > span::before,\n  .ot-toggles[data-variant=\"bracket\"] .ot-toggles-item:not(:has(> .ot-toggles-word)):is(:hover, [data-force~=\"hover\"]):not(:disabled, [data-state=\"on\"])::before { translate: -0.12em 0; opacity: 1; }\n  .ot-toggles[data-variant=\"bracket\"] .ot-toggles-item:is(:hover, [data-force~=\"hover\"]):not(:disabled, [data-state=\"on\"]) > .ot-toggles-word > span::after,\n  .ot-toggles[data-variant=\"bracket\"] .ot-toggles-item:not(:has(> .ot-toggles-word)):is(:hover, [data-force~=\"hover\"]):not(:disabled, [data-state=\"on\"])::after { translate: 0.12em 0; opacity: 1; }\n\n  /* Merged into a neighbour's mark, a mark fades where it stands while the other stretches over it. */\n  .ot-toggles-mark[data-leave=\"merge\"]::before,\n  .ot-toggles-mark[data-leave=\"merge\"]::after { opacity: 0; transition: opacity var(--ot-moderato) var(--ot-breath); }\n\n  /* ── fingering ── after the score's fingering, the small numbers over the notes that say which finger plays each,\n     and the \"01\" of \"Less is more.\". No hairlines: each held word carries, over the middle of it, the number of its\n     turn, the order you held them in (the value's order). The numbers are yours, so they're italic. Pointing at a word\n     sketches in pencil the number it would get; holding inks it and it lands on the word; letting one go counts the\n     later ones down. Meant for type=\"multiple\"; held one at a time, the number is always 1. */\n  .ot-toggles[data-variant=\"fingering\"] { --room: 1.15em; }\n  .ot-toggles[data-variant=\"fingering\"] .ot-toggles-item::after {\n    content: var(--next, \"\") / \"\"; position: absolute; top: 0; left: var(--fx, 50%); translate: -50% -0.3em; pointer-events: none;\n    font-family: var(--ot-expression); font-style: italic; font-weight: 400; font-size: calc(var(--ot-expression-scale) * 0.62em); line-height: 1; letter-spacing: 0;\n    font-variant-numeric: lining-nums tabular-nums; color: var(--ot-rule-strong); opacity: 0;\n    transition: opacity var(--ot-allegro), translate var(--ot-moderato) var(--ot-spiccato), left var(--ot-moderato) var(--ot-exhale), color var(--ot-allegro);\n  }\n  .ot-toggles[data-variant=\"fingering\"] .ot-toggles-item:is(:hover, [data-force~=\"hover\"]):not(:disabled, [data-state=\"on\"])::after { opacity: 1; translate: -50% -0.12em; }\n  .ot-toggles[data-variant=\"fingering\"] .ot-toggles-item[data-state=\"on\"]::after { content: var(--n, \"\") / \"\"; color: var(--ot-ink); opacity: 1; translate: -50% 0; }\n  .ot-toggles[data-variant=\"fingering\"] .ot-toggles-item[data-state=\"on\"]:disabled::after { color: var(--ot-pencil); }\n\n  /* ── margin ── after the book's side head, set in the margin beside the column it heads. One hairline stands\n     between an empty margin and the column of words; a held word crosses it. At rest a word stands in the column,\n     flush against the rule; held, it steps over into the margin and turns italic, flush against the rule's other\n     side, so what you hold reads down the margin and the rest down the column. The margin is as wide as the column\n     (two equal tracks), so the widest word has room to cross. Pointing leans the word toward the rule. */\n  .ot-toggles[data-variant=\"margin\"] {\n    --dir: 1; --gap: 0.6em; display: inline-grid; grid-template-columns: 1fr 1fr; column-gap: calc(var(--gap) * 2); row-gap: 0;\n  }\n  .ot-toggles[data-variant=\"margin\"]:is([dir=\"rtl\"], [dir=\"rtl\"] *):not([dir=\"ltr\"]) { --dir: -1; }\n  .ot-toggles[data-variant=\"margin\"]::before {\n    content: \"\"; position: absolute; inset-block: 0.2em; inset-inline-start: 50%; margin-inline-start: calc(var(--ot-hairline) / -2);\n    border-inline-start: var(--ot-hairline) solid var(--ot-rule-strong); pointer-events: none;\n  }\n  .ot-toggles[data-variant=\"margin\"] .ot-toggles-item {\n    --k: 0; --lean: 0em; grid-column: 2; padding-block: 0.12em; text-align: start;\n    translate: calc(((-100% - var(--gap) * 2) * var(--k) - var(--lean)) * var(--dir)) 0;\n    transition: color var(--ot-allegro), outline-color var(--ot-allegro), outline-offset var(--ot-moderato) var(--ot-spiccato), translate var(--ot-moderato) var(--ot-exhale);\n  }\n  .ot-toggles[data-variant=\"margin\"] .ot-toggles-item:is(:hover, [data-force~=\"hover\"]):not(:disabled, [data-state=\"on\"]) { --lean: calc(var(--gap) / 2); }\n  .ot-toggles[data-variant=\"margin\"] .ot-toggles-item[data-state=\"on\"] { --k: 1; }\n  /* The roman stands at the column's edge, the italic at the margin's, each flush against the rule. */\n  .ot-toggles[data-variant=\"margin\"] .ot-toggles-word { display: grid; justify-items: start; }\n  .ot-toggles[data-variant=\"margin\"] .ot-toggles-word::after { justify-self: end; }\n\n  @media (forced-colors: active) {\n    .ot-toggles { forced-color-adjust: none; --ot-ink: ButtonText; --ot-graphite: ButtonText; --ot-pencil: GrayText; --ot-rule-strong: GrayText; --ot-accent: Highlight; }\n    .ot-toggles-mark { color: Highlight; }\n  }\n}\n",
      "type": "registry:file",
      "target": "styles/0nlytype/toggle-group.css"
    }
  ],
  "css": {
    "@import \"../styles/0nlytype/toggle-group.css\"": {}
  },
  "meta": {
    "movement": "VI",
    "underneath": "radix",
    "contract": "ot-toggles"
  },
  "type": "registry:ui"
}
