a11y-kit 0.2.0

Accessibility semantics layer for the estate — platform-free semantic node model (role, name, states, relations), tree audit as a CI gate, AccessKit conversion behind a feature. The layer nobody funds, owned
Documentation
# a11y-kit

The accessibility semantics layer for the estate — the keystone nobody in
the ecosystem funds (ADR-0004 §2; measured Oct 2026: AccessKit runs on one
maintainer's spare time, has no web adapter, and *no project ships an a11y CI
gate*).

```rust
use a11y_kit::{A11yNode, A11yRole, A11yTree, NodeId, Relation, States};

let mut tree = A11yTree::new();
let window = tree.push(A11yNode::new(NodeId(0), A11yRole::Window, "Chisel Docs"));
let toolbar = tree.push(
    A11yNode::new(NodeId(1), A11yRole::Toolbar, "Tool palette")
        .with_parent(window)
        .with_states(States { focused: true, ..States::default() }),
);
tree.push(
    A11yNode::new(NodeId(2), A11yRole::Button, "Save document")
        .with_parent(toolbar)
        .with_relation(Relation::LabelledBy(toolbar)),
);
assert!(tree.audit().is_ok());
```

## The gate

`tree.audit()` enforces five rules headlessly in CI:

1. ids are unique;
2. parents and relation targets resolve;
3. no parent cycles;
4. every interactive role carries an accessible name;
5. at most one focused node, and it lives inside a `Window` subtree — the
   "focus is somewhere the screen reader can't see" bug.

Every failure names the node (`AuditError::MissingAccessibleName(NodeId(5))`),
because a gate that cannot be acted on is not a gate.

## Properties

- **L1 substrate** — dependency-free by default (`thiserror` only);
  AccessKit transport conversion lands behind the `accesskit` feature
  (Wave 2.1) so the audit layer stays headless and any CI can run it.
- Toolkits lower *into* this model; none of them own the gate.

Licensed under MIT OR Apache-2.0.