/* ═══════════════════════════════════════════════════════════════════════════
   ALFRED INTERACTION — v1.0 · Studies 01–06 LOCKED (Peter, 2026-09-08); 07 onward LOCKED by Fable under Peter's 2026-09-08 ruling; later studies append
   The physics layer over the locked foundation. Consumes alfred.tokens.css
   only: every duration is a motion tier (--al-t-*), every colour a token.
   Nothing here changes how a surface looks at rest — that is the foundation's
   job. This file says how it MOVES and how it ANSWERS the hand.

   Study 01 — see a list · select one · inspect · act · return.
     · a SWAP (another row) is nothing — no motion at all; the eye stays on the list
     · an OPEN (the pane arriving) is structural; a CLOSE is faster than the open
     · layout never animates: things MOVE (transform) and FADE (opacity); layout
       changes once, under the motion — never a table re-flowing for 250ms
     · selection is addressable; return restores scroll + selection + filters
     · nothing dims; no overlay over the list; the Frame never moves
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Press, hover, focus — the immediate and micro tiers ───────────────────── */
.al-btn, .al-action, .al-tab, .al-lens, .al-iconbtn, .al-seg button, .al-source, .al-cite, .al-rail-item, .al-page {
  transition: background-color var(--al-t-micro) var(--al-ease), border-color var(--al-t-micro) var(--al-ease),
              color var(--al-t-micro) var(--al-ease), box-shadow var(--al-t-micro) var(--al-ease);
}
.al-btn:active, .al-action:active, .al-iconbtn:active { transform: translateY(1px); transition-duration: var(--al-t-immediate); }
.al-table tbody tr td { transition: background-color var(--al-t-immediate) linear; }
.al-table tbody tr:focus-visible { outline: none; }
.al-table tbody tr:focus-visible td:first-child { box-shadow: inset 3px 0 0 var(--al-navy-900); }
.al-table tbody tr[aria-selected="true"]:focus-visible td { background: var(--al-navy-wash); }
/* A row promises it can be opened: the pointer says so, nothing else moves. */
.al-table tbody tr { cursor: pointer; user-select: none; }

/* ── The pane: three sizes of one object, and how it arrives and leaves ─────
   .al-split            list + pane (inspect)
   .al-split.is-wide    list as a spine + pane as the work (Finder's column)
   .al-split.is-collapsed  list alone
   The geometry itself never animates. The JS drives two phases:
     leaving:  .is-leaving  → pane translates out + fades (local tier, 190)
               then the grid changes at once and the table's newly-revealed
               columns fade in (.is-reflow, micro tier)
     arriving: the grid changes at once (table narrows under a micro fade),
               then .is-arriving → pane translates in (structural tier, 250)  */
.al-split.is-collapsed { grid-template-columns: minmax(0, 1fr); gap: 0; }
.al-split.is-collapsed > .al-detail { display: none; }
.al-split.is-wide { grid-template-columns: minmax(0, 0.72fr) minmax(0, 1.28fr); }
.al-split.is-wide .al-table th:nth-child(n+3), .al-split.is-wide .al-table td:nth-child(n+3) { display: none; }
.al-split.is-wide .al-table .al-cell-2 { max-width: none; }
.al-split.is-wide .al-detail { position: static; }
.al-split > .al-detail { will-change: transform, opacity; }
/* The pane is Work territory and may scroll itself; the page never grows past the
   viewport because of it (caught at 1440: the pane, not the list, set the height). */
.al-split > .al-detail { top: var(--al-topbar-h); height: calc(100vh - var(--al-topbar-h)); max-height: calc(100vh - var(--al-topbar-h)); border-radius: var(--al-r-card) var(--al-r-card) 0 0; overflow-y: auto; scrollbar-width: thin; }
.al-split.is-leaving > .al-detail { transform: translateX(32px); opacity: 0; transition: transform var(--al-t-local) var(--al-ease), opacity var(--al-t-local) var(--al-ease); }
.al-split.is-arriving > .al-detail { animation: al-arrive var(--al-t-structural) var(--al-ease) 1 both; }
@keyframes al-arrive { from { transform: translateX(32px); opacity: 0; } to { transform: none; opacity: 1; } }
.al-split .al-table tbody { transition: opacity var(--al-t-micro) linear; }
.al-split.is-reflow .al-table tbody { opacity: 0; transition: none; }
.al-split.is-collapsed .al-table td.al-cats, .al-split.is-collapsed .al-table td.al-absent { max-width: none; }
.al-split.is-collapsed .al-table .al-cell-2 { max-width: none; }

/* The list scrolls inside its own instrument; the header stays; the pane stays */
.al-scroll { overflow-y: auto; scrollbar-width: thin; scroll-behavior: smooth; }
.al-scroll .al-table thead th { position: sticky; top: 0; background: var(--al-work); z-index: 1; box-shadow: inset 0 -1px 0 var(--al-line); }

/* ── Waiting: a skeleton in the shape of what arrives, only past 100ms ─────── */
.al-skel { display: block; height: 12px; border-radius: 4px; background: var(--al-inset); position: relative; overflow: hidden; }
.al-skel::after { content: ""; position: absolute; inset: 0; transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, var(--al-line), transparent); animation: al-shimmer 1200ms linear infinite; }
@keyframes al-shimmer { to { transform: translateX(100%); } }
.al-skel.is-title { height: 20px; width: 55%; }
.al-skel.is-line { width: 80%; }
.al-skel.is-short { width: 40%; }

/* ── A page arriving by promotion — the only page-level motion ─────────────── */
.al-page-enter { animation: al-page var(--al-t-structural) var(--al-ease) 1 both; }
@keyframes al-page { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
/* The way back is a real control, first thing the eye meets, and it names where you land */
.al-back { display: inline-flex; align-items: center; gap: 8px; height: 32px; padding: 0 12px 0 8px; margin-bottom: 14px; border-radius: var(--al-r-control); background: none; border: 1px solid transparent; font: inherit; font-size: var(--al-fs-13); color: var(--al-navy-900); cursor: pointer; }
.al-back:hover { background: var(--al-inset); border-color: var(--al-line); text-decoration: none; }
.al-back svg { width: 14px; height: 14px; }

/* ── STUDY 02 · the table as an instrument ─────────────────────────────────────
   Sort lives in the header: one click sorts, a second reverses; the indicator is
   navy (orientation), never colour-as-meaning. Column edges resize by drag,
   double-click fits; widths are remembered per table per person. A truncated
   value is always recoverable (the full value on hover). An identifier can be
   copied without selecting text. A row says it opens (›) only when hovered. */
.al-table { table-layout: fixed; }
.al-table th { position: relative; user-select: none; }
.al-table th[aria-sort] { cursor: pointer; color: var(--al-text-2); }
.al-table th[aria-sort]:hover { color: var(--al-text); }
.al-table th .al-sort { display: inline-block; width: 10px; height: 10px; margin-left: 5px; vertical-align: -1px; opacity: 0; transition: opacity var(--al-t-micro) linear; }
.al-table th[aria-sort]:hover .al-sort { opacity: .5; }
.al-table th[aria-sort="ascending"], .al-table th[aria-sort="descending"] { color: var(--al-navy-900); }
.al-table th[aria-sort="ascending"] .al-sort, .al-table th[aria-sort="descending"] .al-sort { opacity: 1; }
.al-table th[aria-sort="descending"] .al-sort { transform: rotate(180deg); }
/* The handle sits INSIDE its own header (sticky headers are separate stacking layers,
   so anything crossing into the neighbour is covered by it and clicks land there). */
/* The grab zone STRADDLES the line: 7px each side. A hand aims at the line, not at one side of
   it (Peter, twice: "I can't resize"). Crossing into the neighbour is safe because the header
   cells are stacked right-to-left by the JS (each th's z-index descends), so this half paints
   above the next cell instead of under it. */
.al-th-resize { position: absolute; top: 0; right: -7px; width: 14px; height: 100%; cursor: col-resize; z-index: 2; }
/* The edge is visible: a faint separator at rest (so the hand knows where the columns are),
   clearer when the header row is hovered, navy while grabbed. Peter could not find an
   invisible 8px handle (2026-09-08) -- an affordance that must be discovered is not one. */
.al-th-resize::after { content: ""; position: absolute; top: 22%; bottom: 22%; left: 6px; width: 1px; background: var(--al-line); transition: background-color var(--al-t-micro) linear, width var(--al-t-micro) linear; }
.al-table thead:hover .al-th-resize::after { background: var(--al-line-strong); }
.al-th-resize:hover::after, .al-table.is-resizing .al-th-resize.is-active::after { background: var(--al-navy-900); width: 2px; }
.al-table.is-resizing { cursor: col-resize; user-select: none; }
.al-table.is-resizing tbody tr { pointer-events: none; }
/* Cells: one line, truncated, the full value recoverable on hover (title=) */
.al-table td { overflow: hidden; text-overflow: ellipsis; }
.al-table td .al-cell-2 { max-width: none; }
.al-table td .al-cell-2 > span:not(.al-initials) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.al-table td .al-cell-2 > .al-initials { flex: none; overflow: visible; }   /* the badge never truncates; the name beside it does */
/* Text may be selected in a cell; a double-click opens the row and must not also select a word */
.al-table tbody tr { user-select: text; }
/* Copy affordance on identifiers: appears on hover of the cell, copies on click, says so */
.al-copyable { position: relative; display: inline-flex; align-items: center; gap: 6px; max-width: 100%; }
.al-copy { width: 18px; height: 18px; flex: none; display: inline-grid; place-items: center; border-radius: 4px; color: var(--al-text-3); opacity: 0; transition: opacity var(--al-t-micro) linear, background-color var(--al-t-micro) linear; cursor: pointer; }
.al-copy svg { width: 12px; height: 12px; }
td:hover .al-copy, .al-copyable:focus-within .al-copy { opacity: 1; }
.al-copy:hover { background: var(--al-inset); color: var(--al-text); }
.al-copy.is-done { color: var(--al-success); opacity: 1; }
/* A row promises it opens: the chevron in the last cell, hover only */
.al-table td.al-open { width: 28px; padding-left: 0; padding-right: 10px; text-align: right; color: var(--al-text-3); }
.al-table td.al-open svg { width: 14px; height: 14px; opacity: 0; transition: opacity var(--al-t-micro) linear; vertical-align: -2px; }
.al-table tbody tr:hover td.al-open svg, .al-table tbody tr[aria-selected="true"] td.al-open svg { opacity: 1; }
/* Loading: skeleton rows in the table's own shape */
.al-table tr.is-skel td { padding-top: 12px; padding-bottom: 12px; }
.al-table tr.is-skel .al-skel { height: 10px; }
/* Empty: plain words, no illustration */
.al-empty { padding: 28px 16px; color: var(--al-text-3); font-size: var(--al-fs-13); text-align: center; }

/* ── STUDY 03 · waiting ───────────────────────────────────────────────────────
   The three thresholds (Nielsen; Apple HIG): under 100ms nothing appears but the
   press itself; 100ms–1s a skeleton in the exact shape of what arrives, in place;
   past 1s the skeleton stays and the region says, in words, what it waits for.
   Never a spinner, never a blank, never a dialog. Routine actions are optimistic
   (shown at once, marked pending, reverted in place with words if they fail);
   material actions show their progress inside the control that started them.
   The shimmer is the only motion, and it stops on arrival. */

/* Composite skeletons — each the shape of one foundation component */
.al-skel-kv { display: grid; grid-template-columns: 120px 1fr; gap: 10px 12px; }
.al-skel-kv .al-skel { height: 11px; }
/* A skeleton row is the flow row it stands for: same padding, same rule, one line slot
   per line of text (20px, the body line; 18px in a compact flow). The bar sits inside the
   slot, so the row's height equals the row that will replace it and nothing shifts. */
.al-skel-flow > div { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; padding: 10px 0; border-bottom: 1px solid var(--al-line); }
.al-skel-flow > div:last-child { border-bottom: 0; }
.al-skel-flow.is-compact > div { padding: 7px 0; }
.al-skel-line { display: flex; align-items: center; height: 20px; }
.al-skel-flow.is-compact .al-skel-line { height: 18px; }
.al-skel-line .al-skel, .al-skel-flow .al-skel { height: 11px; }
.al-skel-line.is-pill .al-skel { height: 18px; border-radius: 9px; }
.al-skel-line.is-btn { height: 27px; align-items: flex-end; }
.al-skel-line.is-btn .al-skel { height: 26px; border-radius: var(--al-r-control); }
.al-skel-aside { display: flex; flex-direction: column; align-items: flex-end; }
.al-skel-figure .al-skel-line { height: 17px; }
.al-skel-figure .al-skel-line.is-big { height: 28px; }
.al-skel-figure .al-skel-line.is-big .al-skel { height: 22px; width: 42%; }
.al-skel-bars { display: grid; grid-template-columns: max-content 1fr max-content; gap: 8px 14px; align-items: center; }
.al-skel-bars .al-skel-line { height: 16px; }
.al-skel-bars .al-skel { height: 10px; }
.al-skel-bars .al-skel.is-track { height: 10px; border-radius: 5px; }
.al-skel-text .al-skel-line { height: 20px; }
.al-skel-text .al-skel { height: 12px; }

/* Past one second: words. Below it: never. */
.al-wait { display: flex; align-items: center; gap: 8px; margin-top: 12px; font-size: var(--al-fs-12); color: var(--al-text-3); }
.al-wait::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--al-text-3); flex: none; }
.al-wait .al-btn { height: 26px; padding: 0 10px; font-size: var(--al-fs-12); margin-left: 6px; }

/* Optimistic: shown at once, marked pending, confirmed silently */
.is-pending { color: var(--al-text-3); }
.is-pending .al-status::before { background: var(--al-text-3); }
.al-pending-note { font-size: var(--al-fs-11); color: var(--al-text-3); margin-left: 8px; }
.al-region.is-pending-region { opacity: .75; transition: opacity var(--al-t-micro) linear; }

/* A control doing material work shows it inside itself: label changes, nothing spins */
.al-btn.is-busy { pointer-events: none; color: var(--al-text-3); background: var(--al-inset); border-color: var(--al-line); }
.al-btn.is-done { color: var(--al-success); border-color: var(--al-line); background: var(--al-raised); pointer-events: none; }

/* Failure is inline, in the region that failed, in plain words, with a way to retry.
   Never alert(), never a modal, never a red screen. Only what failed is marked. */
.al-fail { display: flex; align-items: flex-start; gap: 12px; padding: 12px 14px; background: var(--al-critical-wash); border-left: 3px solid var(--al-critical); border-radius: var(--al-r-control); font-size: var(--al-fs-13); color: var(--al-text); }
.al-fail b { color: var(--al-critical); font-weight: 500; }
.al-fail .al-fail-what { color: var(--al-text-2); font-size: var(--al-fs-12); margin-top: 2px; }
.al-fail .al-btn { margin-left: auto; flex: none; height: 28px; }
.al-region.is-failed { border-color: var(--al-line); }

/* Search: results stay until the next ones arrive; "buscando" only past 400ms */
.al-search .al-searching { margin-left: auto; font-size: var(--al-fs-11); color: var(--al-text-3); white-space: nowrap; }

/* ── The rejected alternative, kept so it can be FELT (Study 01, mode C):
      today's overlay — a panel over the list under a scrim. Never ship. ───── */
.al-overlay-scrim { position: fixed; inset: 0; background: var(--al-navy-950); opacity: 0; pointer-events: none; transition: opacity var(--al-t-structural) var(--al-ease); z-index: var(--al-z-layer); }
.al-overlay-scrim.is-on { opacity: .35; pointer-events: auto; }
.al-overlay-panel { position: fixed; top: 0; right: 0; bottom: 0; width: 640px; background: var(--al-raised); box-shadow: var(--al-shadow-modal); transform: translateX(100%); transition: transform var(--al-t-structural) var(--al-ease); z-index: var(--al-z-layer); overflow-y: auto; }
.al-overlay-panel.is-on { transform: none; }

/* ── The other rejected method, kept for the motion lab (Study 01b): animating
      the layout itself. The table re-flows on every frame. Never ship. ───── */
.al-split.is-layout-anim { transition: grid-template-columns var(--al-t-structural) var(--al-ease), gap var(--al-t-structural) var(--al-ease); }
.al-split.is-layout-anim.is-collapsed { grid-template-columns: minmax(0, 1fr) 0px; }
.al-split.is-layout-anim.is-collapsed > .al-detail { display: block; opacity: 0; transform: translateX(24px); pointer-events: none; overflow: hidden; transition: opacity var(--al-t-local) var(--al-ease), transform var(--al-t-structural) var(--al-ease); }

/* ═══════════════════════════════════════════════════════════════════════════
   STUDY 04 — LAYERS. What comes forward over the surface, and how it leaves.
   Four kinds, one ladder (--al-z-*): attached (menu · popover · picker · tooltip · inline
   confirm — belongs to a control, no scrim, one at a time, click-outside/Esc/scroll closes),
   the layer (scrim + dialog — a task to finish or abandon, one at a time, never nested),
   the notice (the system speaking, one place, one material, never a failure), and the
   pane/desk — which is NOT a layer: it lives in the layout (Study 01, Ask Alfred anchor).
   Physics: attached in micro, out immediate · dialog in modal, out local · notice in local,
   out micro. Everything moves by opacity and ≤8px of transform; layout never animates.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Attached layers share one material: raised plane, popover radius, raised shadow, a line. */
.al-menu, .al-pop { position: fixed; z-index: var(--al-z-attached); background: var(--al-raised); border: 1px solid var(--al-line); border-radius: var(--al-r-popover); box-shadow: var(--al-shadow-raised); opacity: 0; transform: translateY(-4px); pointer-events: none; transition: opacity var(--al-t-immediate) var(--al-ease), transform var(--al-t-immediate) var(--al-ease); }
.al-menu.is-open, .al-pop.is-open { opacity: 1; transform: none; pointer-events: auto; transition-duration: var(--al-t-micro); }
.al-menu.is-above, .al-pop.is-above { transform: translateY(4px); }
.al-menu.is-above.is-open, .al-pop.is-above.is-open { transform: none; }

/* Menu: 32px rows, the hover is the inset plane, a destructive row is critical text only. */
.al-menu { min-width: 200px; padding: 6px; }
.al-menu-item { display: flex; align-items: center; gap: 10px; width: 100%; height: 32px; padding: 0 10px; border: 0; border-radius: var(--al-r-control); background: none; font: inherit; font-size: var(--al-fs-13); color: var(--al-text); text-align: left; cursor: pointer; }
.al-menu-item:hover, .al-menu-item:focus-visible { background: var(--al-inset); outline: none; }
.al-menu-item .al-menu-hint { margin-left: auto; font-size: var(--al-fs-11); color: var(--al-text-3); }
.al-menu-item.is-destructive { color: var(--al-critical); }
.al-menu-item.is-destructive:hover { background: var(--al-critical-wash); }
.al-menu-item[aria-disabled="true"] { color: var(--al-text-3); cursor: default; }
.al-menu-sep { height: 1px; margin: 6px 4px; background: var(--al-line); }
.al-menu-head { padding: 6px 10px 4px; font-size: var(--al-fs-12); font-weight: 500; color: var(--al-text-3); }

/* Popover: an explanation or a small instrument, 14px padding, never wider than 360. */
.al-pop { padding: 14px 16px; max-width: 360px; font-size: var(--al-fs-13); line-height: 1.5; color: var(--al-text); }
.al-pop .al-pop-title { font-weight: 500; margin-bottom: 4px; }
.al-pop .al-pop-foot { display: flex; gap: 8px; justify-content: flex-end; margin-top: 12px; }

/* Picker: a popover that is a search over a short list. */
.al-pick { padding: 0; width: 280px; }
.al-pick .al-search { margin: 8px; height: 32px; }
.al-pick-list { max-height: 264px; overflow-y: auto; padding: 0 6px 6px; }
.al-pick-list .al-menu-item { height: 40px; }
.al-pick-list .al-menu-item .al-initials { width: 24px; height: 24px; font-size: var(--al-fs-11); }
.al-pick-list .al-menu-item[aria-selected="true"] { background: var(--al-navy-wash); }
.al-pick-list .al-pick-sub { display: block; font-size: var(--al-fs-11); color: var(--al-text-3); }

/* Tooltip: the shell's material, one line, after a 500ms rest on an icon-only control.
   Never carries anything you need; never appears on touch; shown/hidden in the immediate tier. */
.al-tip { position: fixed; z-index: var(--al-z-attached); background: var(--al-navy-950); color: var(--al-rail-text-hi); font-size: var(--al-fs-11); line-height: 1.3; padding: 5px 8px; border-radius: var(--al-r-control); pointer-events: none; opacity: 0; transition: opacity var(--al-t-immediate) var(--al-ease); white-space: nowrap; }
.al-tip.is-open { opacity: 1; }

/* Inline confirm: a popover anchored to the control that would destroy something.
   One line of what, the destructive action, and Cancel — which holds focus. */
.al-confirm { display: flex; align-items: center; gap: 12px; }
.al-confirm .al-confirm-what { flex: 1; }
.al-confirm .al-btn { height: 30px; padding: 0 12px; }

/* The layer: scrim in the shell's colour at .35, dialog on the raised plane. */
.al-scrim { position: fixed; inset: 0; z-index: var(--al-z-layer); background: var(--al-navy-950); opacity: 0; pointer-events: none; transition: opacity var(--al-t-local) var(--al-ease); }
.al-scrim.is-open { opacity: .35; pointer-events: auto; transition-duration: var(--al-t-modal); }
.al-dialog { position: fixed; z-index: var(--al-z-layer); top: 12vh; left: 50%; width: 560px; max-width: calc(100vw - 48px); max-height: 76vh; display: flex; flex-direction: column; background: var(--al-raised); border-radius: var(--al-r-modal); box-shadow: var(--al-shadow-modal); opacity: 0; transform: translate(-50%, 8px); pointer-events: none; transition: opacity var(--al-t-local) var(--al-ease), transform var(--al-t-local) var(--al-ease); }
.al-dialog.is-open { opacity: 1; transform: translate(-50%, 0); pointer-events: auto; transition-duration: var(--al-t-modal); }
.al-dialog.is-wide { width: 720px; }
.al-dialog-hd { display: flex; align-items: flex-start; gap: 12px; padding: 18px 22px 14px; border-bottom: 1px solid var(--al-line); }
.al-dialog-kind { font-size: var(--al-fs-12); font-weight: 500; color: var(--al-text-3); }
.al-dialog-title { font-size: var(--al-fs-16); font-weight: 500; color: var(--al-text); margin-top: 2px; line-height: 1.3; }
.al-dialog-hd .al-iconbtn { margin-left: auto; width: 30px; height: 30px; border-color: transparent; }
.al-dialog-body { padding: 18px 22px; overflow-y: auto; font-size: var(--al-fs-13); line-height: 1.5; }
.al-dialog-ft { display: flex; align-items: center; gap: 10px; padding: 14px 22px; border-top: 1px solid var(--al-line); }
.al-dialog-ft .al-btn-quiet { margin-right: auto; }
.al-dialog-ft .al-confirm { flex: 1; padding: 0; }
.al-dialog-ft .al-confirm .al-confirm-what { color: var(--al-text-2); }

/* Fields inside the layer — the minimum for the study; the form grammar is Study 06. */
.al-field { display: grid; gap: 6px; margin-bottom: 14px; }
.al-field label { font-size: var(--al-fs-12); color: var(--al-text-2); }
.al-field input, .al-field textarea, .al-field select { font: inherit; font-size: var(--al-fs-13); color: var(--al-text); background: var(--al-work); border: 1px solid var(--al-line-strong); border-radius: var(--al-r-control); padding: 8px 10px; outline: none; }
.al-field input:focus, .al-field textarea:focus, .al-field select:focus { border-color: var(--al-navy-900); box-shadow: 0 0 0 3px var(--al-navy-wash); }
.al-field textarea { min-height: 96px; resize: vertical; }

/* The notice: one place (bottom, at the main area's left edge), one material (the shell's),
   one voice. Never a failure (Study 03: failure is inline), never a colour for a type.
   Enters in local from 8px below, leaves in micro. Hover holds it. Two at most. */
.al-notices { position: fixed; z-index: var(--al-z-notice); left: calc(var(--al-rail-w) + 28px); bottom: 24px; display: flex; flex-direction: column; gap: 8px; pointer-events: none; }   /* newest lowest, nearest the eye */
.al-notice { display: flex; align-items: center; gap: 14px; min-height: 40px; padding: 0 8px 0 14px; background: var(--al-navy-950); color: var(--al-text-inverse); border-radius: var(--al-r-popover); box-shadow: var(--al-shadow-raised); font-size: var(--al-fs-13); pointer-events: auto; opacity: 0; transform: translateY(8px); transition: opacity var(--al-t-local) var(--al-ease), transform var(--al-t-local) var(--al-ease); }
.al-notice.is-open { opacity: 1; transform: none; }
.al-notice.is-leaving { opacity: 0; transform: none; transition-duration: var(--al-t-micro); }
.al-notice .al-notice-act { font: inherit; font-size: var(--al-fs-13); font-weight: 500; color: var(--al-text-inverse); background: none; border: 0; border-radius: var(--al-r-control); padding: 6px 8px; cursor: pointer; text-decoration: underline; text-underline-offset: 3px; text-decoration-color: color-mix(in srgb, var(--al-text-inverse) 72%, transparent); }
.al-notice .al-notice-act:hover { background: color-mix(in srgb, var(--al-text-inverse) 16%, transparent); }
.al-notice .al-notice-x { width: 28px; height: 28px; display: grid; place-items: center; border: 0; background: none; color: color-mix(in srgb, var(--al-text-inverse) 72%, transparent); border-radius: var(--al-r-control); cursor: pointer; }
.al-notice .al-notice-x:hover { background: color-mix(in srgb, var(--al-text-inverse) 10%, transparent); color: var(--al-text-inverse); }

/* While a layer is open, the surface behind does not scroll. */
.al.has-layer { overflow: hidden; }

/* ═══════════════════════════════════════════════════════════════════════════
   STUDY 05 — FEEDBACK, CONFIRMATION, UNDO. The policy after a click, by consequence class
   (Step 6 capability map: routine · material · material-external · destructive · permanent).
   Routine: the change is the feedback; Undo in the notice only when out of view or removing.
   Destructive: inline confirm, soft delete, Undo. Material: the form is the confirmation, the
   result written in place, View not Undo. External: consequence dialog, then send at once (the
   held send was felt and rejected). Permanent: type the reference. The record is the feedback that lasts.
   ═══════════════════════════════════════════════════════════════════════════ */

/* A material one-click confirm: same anchored popover as the destructive one, primary focused. */
.al-confirm.is-material .al-btn-strong { height: 30px; }

/* A held notice that counts down (background jobs only — the held SEND was rejected by Peter 2026-09-08: a send follows the press and nothing else). */
.al-notice .al-notice-count { font-family: var(--al-font-id); font-size: var(--al-fs-12); color: color-mix(in srgb, var(--al-text-inverse) 72%, transparent); min-width: 3ch; text-align: right; }
.al-notice.is-held { border-left: 3px solid var(--al-text-inverse); padding-left: 11px; }

/* Inline edit: a value that becomes a field where it stands. Rest is text; hover shows the seam. */
.al-inline { display: inline-flex; align-items: center; gap: 6px; }
.al-inline .al-inline-value { border-bottom: 1px dashed transparent; cursor: text; padding: 0 1px; }
.al-inline:hover .al-inline-value { border-bottom-color: var(--al-line-strong); }
.al-inline input { font: inherit; color: var(--al-text); background: var(--al-work); border: 1px solid var(--al-navy-900); border-radius: var(--al-r-control); padding: 1px 6px; outline: none; box-shadow: 0 0 0 3px var(--al-navy-wash); width: 12ch; }
.al-inline .al-pending-note { margin-left: 0; }

/* The record: who · when · what, with the counter-action on the line that can be reversed. */
.al-record .al-flow-row { align-items: flex-start; }
.al-record .al-flow-key { width: 46px; }
.al-record .al-record-who { color: var(--al-text-3); }
.al-record .al-record-state { font-family: var(--al-font-id); font-size: var(--al-fs-11); color: var(--al-text-3); margin-left: 8px; }
.al-record .al-record-state.is-live { color: var(--al-text-2); }
.al-record .al-btn-quiet { height: 24px; padding: 0 8px; font-size: var(--al-fs-12); }
.al-record .is-removed .al-flow-title { color: var(--al-text-3); text-decoration: line-through; text-decoration-color: var(--al-line-strong); }

/* Typed confirmation: the field must match before the destructive control wakes. */
.al-typed { margin-top: 14px; }
.al-typed input { font-family: var(--al-font-id); letter-spacing: .02em; }

/* ═══════════════════════════════════════════════════════════════════════════
   STUDY 06 — FORMS AND INPUT. Label above, always; the optional marked, never the required;
   validation late and local (on leaving a field, on submit — never while typing the first time);
   Spanish shapes known and forgiven (NIE letter, IBAN mod-97, amounts es-ES); dates typed or
   picked in Alfred's own popover; people picked, never selected; drafts saved as you type; the
   whole form is the drop zone. The submit is never disabled: pressing it shows what is missing.
   ═══════════════════════════════════════════════════════════════════════════ */

.al-form { display: block; }
.al-field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 14px; }
.al-field-row.is-narrow-first { grid-template-columns: 140px 1fr; }
.al-field label .al-optional { color: var(--al-text-3); font-weight: 400; margin-left: 4px; }
.al-field-hint { font-size: var(--al-fs-11); color: var(--al-text-3); margin-top: -2px; }
.al-field input::placeholder, .al-field textarea::placeholder { color: var(--al-text-3); }
.al-field input, .al-field select { height: 34px; }
.al-field textarea { line-height: 1.5; overflow: hidden; }                 /* grows by JS to 12 lines, then scrolls */
.al-field textarea.is-max { overflow-y: auto; }

/* Affixes: a unit or prefix that belongs to the field, inside its border. */
.al-affix { display: flex; align-items: center; height: 34px; border: 1px solid var(--al-line-strong); border-radius: var(--al-r-control); background: var(--al-work); }
.al-affix:focus-within { border-color: var(--al-navy-900); box-shadow: 0 0 0 3px var(--al-navy-wash); }
.al-affix input { height: 32px; border: 0 !important; box-shadow: none !important; background: transparent; flex: 1; min-width: 0; }
.al-affix .al-affix-txt { padding: 0 10px; font-size: var(--al-fs-13); color: var(--al-text-3); white-space: nowrap; }
.al-affix .al-affix-txt.is-lead { border-right: 1px solid var(--al-line); }
.al-affix .al-affix-txt.is-trail { border-left: 1px solid var(--al-line); }
.al-affix .al-iconbtn { width: 30px; height: 30px; border: 0; margin-right: 2px; }
.al-field input.al-mono, .al-affix input.al-mono { font-family: var(--al-font-id); letter-spacing: .02em; }

/* Invalid: the border goes critical, one line says what is wrong and what right looks like. */
/* A wrong field is something to ATTEND to, not a failure: attention, never critical (Peter, 2026-09-08:
   "the red color when a form is wrong. Its too aggressive" — chose B of three). Critical stays for
   "the save did not happen" (.al-fail). */
.al-field.is-invalid input, .al-field.is-invalid textarea, .al-field.is-invalid select, .al-field.is-invalid .al-affix { border-color: var(--al-attention); }
.al-field.is-invalid input:focus, .al-field.is-invalid .al-affix:focus-within { box-shadow: 0 0 0 3px var(--al-attention-wash); }
.al-field-error { display: flex; gap: 6px; align-items: flex-start; font-size: var(--al-fs-12); color: var(--al-attention); line-height: 1.4; }
.al-field-error::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--al-attention); flex: none; margin-top: 5px; }
.al-form-errors { margin-bottom: 14px; }                                   /* the server's un-fielded failure: an .al-fail, nothing else */

/* A picked value: the control shows who was picked and opens the picker (Study 04). */
.al-pickbtn { display: flex; align-items: center; gap: 8px; height: 34px; width: 100%; padding: 0 10px; border: 1px solid var(--al-line-strong); border-radius: var(--al-r-control); background: var(--al-work); font: inherit; font-size: var(--al-fs-13); color: var(--al-text); text-align: left; cursor: pointer; }
.al-pickbtn:hover { border-color: var(--al-text-3); }
.al-pickbtn:focus-visible { outline: none; border-color: var(--al-navy-900); box-shadow: 0 0 0 3px var(--al-navy-wash); }
.al-pickbtn > span:first-child { display: flex; align-items: center; gap: 8px; min-width: 0; }
.al-pickbtn .al-initials { width: 22px; height: 22px; font-size: var(--al-fs-11); flex: none; }
.al-pickbtn .al-chev { margin-left: auto; color: var(--al-text-3); }
.al-pickbtn.is-empty { color: var(--al-text-3); }
.al-pick .al-pick-create { border-top: 1px solid var(--al-line); margin-top: 4px; padding-top: 4px; }

/* The date popover: the calendar anchor's mini-month, with a month bar. */
.al-datepop { padding: 10px 12px 12px; width: 268px; }
.al-datepop .al-datepop-bar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; font-size: var(--al-fs-13); font-weight: 500; color: var(--al-text); }
.al-datepop .al-datepop-bar .al-iconbtn { width: 26px; height: 26px; border-color: transparent; }
.al-datepop .al-mini-d { cursor: pointer; }
.al-datepop .al-mini-d:hover { background: var(--al-inset); }
.al-datepop .al-datepop-quick { display: flex; gap: 6px; margin-top: 8px; }
.al-datepop .al-datepop-quick .al-btn { height: 26px; padding: 0 8px; font-size: var(--al-fs-12); }

/* Files: the whole form is the drop zone; the region says where the file will land. */
.al-drop { border: 1px dashed var(--al-line-strong); border-radius: var(--al-r-control); padding: 14px 16px; font-size: var(--al-fs-13); color: var(--al-text-2); display: flex; align-items: center; gap: 12px; background: var(--al-work); transition: border-color var(--al-t-micro) var(--al-ease), background var(--al-t-micro) var(--al-ease); }
.al-drop .al-glyph { width: 18px; height: 18px; color: var(--al-text-3); flex: none; }
.al-drop .al-drop-hint { color: var(--al-text-3); font-size: var(--al-fs-12); }
.al-drop .al-btn { margin-left: auto; height: 28px; }
.al-form.is-dragover .al-drop, .al-drop.is-over { border-color: var(--al-navy-900); border-style: solid; background: var(--al-navy-wash); color: var(--al-navy-900); }
.al-files { display: grid; gap: 6px; margin-top: 8px; }
.al-file { display: flex; align-items: center; gap: 10px; height: 36px; padding: 0 10px; border: 1px solid var(--al-line); border-radius: var(--al-r-control); background: var(--al-work); font-size: var(--al-fs-13); }
.al-file .al-file-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.al-file .al-file-size { font-family: var(--al-font-id); font-size: var(--al-fs-11); color: var(--al-text-3); }
.al-file .al-file-state { font-size: var(--al-fs-12); color: var(--al-text-3); }
.al-file .al-iconbtn { width: 26px; height: 26px; border-color: transparent; }
.al-file.is-failed { border-color: var(--al-critical); }
.al-file.is-failed .al-file-state { color: var(--al-critical); }

/* The form's foot: the draft mark is quiet, the submit is never disabled. */
.al-form-foot { display: flex; align-items: center; gap: 10px; padding-top: 14px; border-top: 1px solid var(--al-line); margin-top: 6px; }
.al-draft { font-size: var(--al-fs-12); color: var(--al-text-3); margin-right: auto; }
.al-draft.is-saving { color: var(--al-text-3); }

/* ═══════════════════════════════════════════════════════════════════════════
   STUDY 07 — SELECTION, FILTERING, BULK. One filter bar per list (lenses with counts · a Filter
   menu that adds chips · search · Clear); every filter a chip and in the address. Click inspects,
   the tick selects; range with Shift; the page with the header tick; the whole match set only in
   words. The selection bar replaces the filter bar in place. Bulk follows the consequence classes
   with the count said; feedback per row and in total.
   ═══════════════════════════════════════════════════════════════════════════ */

/* The filter bar keeps its place and its height. The selection bar FLOATS over the foot of the
   list (raised plane, centred on the main area) so the filters stay reachable while rows are
   selected — the first version replaced the filter bar in place and driving proved you could not
   change a filter with a selection alive. No layout shift either way. */
.al-listbar { position: relative; height: 44px; margin-bottom: 12px; }
.al-filterbar { position: absolute; inset: 0; display: flex; align-items: center; gap: 10px; }
.al-selbar { position: fixed; z-index: var(--al-z-frame); left: calc(var(--al-rail-w) + (100vw - var(--al-rail-w)) / 2); bottom: 24px; transform: translate(-50%, 8px); display: flex; align-items: center; gap: 10px; height: 48px; padding: 0 8px 0 16px; background: var(--al-raised); border: 1px solid var(--al-line); border-radius: var(--al-r-popover); box-shadow: var(--al-shadow-raised); opacity: 0; pointer-events: none; transition: opacity var(--al-t-local) var(--al-ease), transform var(--al-t-local) var(--al-ease); white-space: nowrap; }
.al-listbar.has-selection .al-selbar { opacity: 1; pointer-events: auto; transform: translate(-50%, 0); }
.al-selbar .al-sel-count { font-weight: 500; color: var(--al-text); font-variant-numeric: tabular-nums; }
.al-selbar .al-sel-hidden { color: var(--al-text-3); font-size: var(--al-fs-12); }
.al-selbar .al-sel-actions { display: flex; gap: 6px; align-items: center; margin-left: 14px; padding-left: 14px; border-left: 1px solid var(--al-line); }
.al-selbar .al-btn { height: 30px; padding: 0 12px; }
.al-selbar .al-sel-progress { display: flex; align-items: center; gap: 10px; font-size: var(--al-fs-12); color: var(--al-text-2); font-variant-numeric: tabular-nums; margin-left: 10px; }
.al-selbar .al-sel-progress .al-bar-track { width: 120px; height: 6px; }
.al-selbar .al-sel-progress .al-bar-fill { height: 6px; transition: width var(--al-t-local) var(--al-ease); }
.al-selbar .al-sel-failed { color: var(--al-critical); }
/* Notices keep the left edge (Study 04); the selection bar the centre — they never collide. */

/* Lenses are the FOUNDATION's (.al-lenses / .al-lens tiles with name + count, aria-pressed) — the locked
   Tax Clients grammar. Study 07 first drew its own pill lenses under the same class and driving the
   controls matrix exposed the collision; the pills are gone, the tiles are the lens. */

/* Chips: field and value, removable. */
.al-chips { display: flex; gap: 6px; align-items: center; flex-wrap: nowrap; }
.al-filter-chip { display: inline-flex; align-items: center; gap: 4px; height: 28px; padding: 0 4px 0 10px; border: 1px solid var(--al-line-strong); border-radius: 14px; background: var(--al-raised); font-size: var(--al-fs-12); color: var(--al-text); white-space: nowrap; }
.al-filter-chip .al-chip-field { color: var(--al-text-3); }
.al-filter-chip .al-chip-x { width: 20px; height: 20px; display: grid; place-items: center; border: 0; background: none; color: var(--al-text-3); border-radius: 50%; cursor: pointer; }
.al-filter-chip .al-chip-x:hover { background: var(--al-inset); color: var(--al-text); }
.al-filterbar .al-search { height: 32px; min-width: 240px; margin-left: auto; }
.al-filterbar .al-clear { height: 28px; padding: 0 8px; font-size: var(--al-fs-12); }
.al-filterbar .al-btn-filter { height: 32px; padding: 0 10px; gap: 6px; }

/* The tick: quiet at rest, firm on hover or when the list has a selection. */
.al-tick { appearance: none; width: 16px; height: 16px; margin: 0; border: 1px solid var(--al-line-strong); border-radius: 4px; background: var(--al-raised); cursor: pointer; display: inline-grid; place-items: center; vertical-align: middle; transition: border-color var(--al-t-immediate) var(--al-ease), background var(--al-t-immediate) var(--al-ease); }
.al-tick:hover, tr:hover .al-tick { border-color: var(--al-text-3); }
.al-tick:checked { background: var(--al-navy-900); border-color: var(--al-navy-900); }
.al-tick:checked::after { content: ""; width: 9px; height: 5px; border-left: 2px solid var(--al-text-inverse); border-bottom: 2px solid var(--al-text-inverse); transform: translateY(-1px) rotate(-45deg); }
.al-tick:indeterminate { background: var(--al-navy-900); border-color: var(--al-navy-900); }
.al-tick:indeterminate::after { content: ""; width: 8px; height: 2px; background: var(--al-text-inverse); }
.al-tick:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--al-navy-wash); }
/* .al-page became keyboard-reachable 2026-09-12 (P13); it lives in .al-main, not .al, so the
   generic ring never reaches it. Same ring as .al-tick -- a 28px chip is the same object. */
.al-page:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--al-navy-wash); }
.al-table th.al-col-tick, .al-table td.al-col-tick { width: 36px; padding-right: 0; }
.al-table td.al-col-tick { cursor: default; }
.al-table tbody tr .al-tick { opacity: .55; }
.al-table tbody tr:hover .al-tick, .al-table tbody tr .al-tick:checked, .al-table.has-selection tbody tr .al-tick, .al-table tbody tr .al-tick:focus-visible { opacity: 1; }
/* A checked row carries the inset plate; the inspected row keeps the navy wash (Study 01). Both may coincide. */
.al-table tbody tr.is-checked td { background: var(--al-inset); }
.al-table tbody tr.is-checked.is-selected td { background: var(--al-navy-wash); }

/* "All 50 on this page selected · Select all 348 that match" — one line, never silent. */
.al-selectall { display: flex; gap: 6px; align-items: center; justify-content: center; height: 34px; font-size: var(--al-fs-12); color: var(--al-text-2); background: var(--al-inset); border-bottom: 1px solid var(--al-line); }
.al-selectall .al-btn-quiet { height: 26px; padding: 0 8px; font-size: var(--al-fs-12); }

/* hidden means hidden, whatever display a class gives the element (found by driving: a hidden
   .al-btn stayed visible because inline-flex beat the browser's [hidden] rule). */
.al [hidden] { display: none !important; }

/* Per-row bulk outcome: pending, then settled or failed in its own place. */
.al-table tbody tr.is-pending td { color: var(--al-text-3); }
.al-table tbody tr.is-pending .al-row-state { color: var(--al-text-3); font-size: var(--al-fs-11); }
.al-table tbody tr.is-failed td:first-child { box-shadow: inset 3px 0 0 var(--al-critical); }
.al-table tbody tr.is-failed .al-row-state { color: var(--al-critical); font-size: var(--al-fs-11); }
.al-row-state .al-btn-quiet { height: 22px; padding: 0 6px; font-size: var(--al-fs-11); margin-left: 6px; }

/* ═══════════════════════════════════════════════════════════════════════════
   STUDY 08 — CONTROL STATES AND THE INVISIBLE KEYBOARD. Seven states per kind, one look each:
   rest · hover (a plane step, never text alone) · pressed (1px down + one plane darker, immediate)
   · focus (keyboard only, the one ring) · selected/current (ARIA, never a class) · disabled (rare,
   focusable, says why) · busy (Study 03). Every actor is a real <button> or <a>. Hover-revealed
   controls also appear on focus and always on touch. The keyboard is standard and never painted.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Pressed: one plane darker, per kind. The 1px drop is Study 01's. */
.al-btn-strong:active { background: var(--al-navy-950); border-color: var(--al-navy-950); }
.al-btn-standard:active, .al-btn-quiet:active, .al-iconbtn:active, .al-lens:active, .al-tab:active, .al-menu-item:active { background: var(--al-inset); }
.al-btn-destructive:active { background: var(--al-critical-wash); }
.al-seg button:active { background: var(--al-inset); }
.al-seg button[aria-pressed="true"]:active { background: var(--al-navy-950); }
.al-rail-item:active { background: var(--al-rail-plate-hi); }
.al-tab:active, .al-lens:active, .al-seg button:active, .al-rail-item:active, .al-menu-item:active { transition-duration: var(--al-t-immediate); }

/* Quiet things step to the inset plane on hover; the pointer becomes a hand only on actors. */
.al-tab:hover { background: var(--al-inset); }
.al-tab, .al-lens, .al-seg button, .al-menu-item, .al-btn, .al-iconbtn, .al-action, .al-rail-item, .al-chip-x, .al-notice-act, .al-tick, .al-pickbtn, .al-why, .al-back, .al-link { cursor: pointer; }
.al-btn[aria-disabled="true"], .al-iconbtn[aria-disabled="true"], .al-menu-item[aria-disabled="true"] { cursor: default; }

/* Selected / current: the attribute is the truth; the look reads it. */
.al-iconbtn[aria-pressed="true"] { background: var(--al-navy-wash); border-color: var(--al-navy-wash); color: var(--al-navy-900); }
.al-btn[aria-pressed="true"] { background: var(--al-navy-wash); border-color: var(--al-navy-wash); color: var(--al-navy-900); }

/* Disabled is rare, stays focusable, and says why (data-tip carries the reason; Study 04's tooltip shows it on hover and on focus). */
.al-iconbtn[aria-disabled="true"], .al-lens[aria-disabled="true"], .al-tab[aria-disabled="true"] { color: var(--al-text-3); background: var(--al-inset); border-color: var(--al-line); }
.al-btn[aria-disabled="true"]:active, .al-iconbtn[aria-disabled="true"]:active { transform: none; }
.al-btn[aria-disabled="true"]:hover { border-color: var(--al-line); background: var(--al-inset); }

/* Links go somewhere; buttons do something. */
.al-link { color: var(--al-navy-900); text-decoration: underline; text-decoration-color: var(--al-line-strong); text-underline-offset: 3px; }
button.al-link { font: inherit; background: none; border: 0; padding: 0; }  /* an inline actor is a real button (Study 08) that reads as a link */
.al-link:hover { text-decoration-color: var(--al-navy-900); }

/* Hover-revealed controls also appear on keyboard focus, and always where there is no hover. */
.al-reveal { opacity: 0; transition: opacity var(--al-t-immediate) var(--al-ease); }
:hover > .al-reveal, :hover > * > .al-reveal, :focus-within > .al-reveal, :focus-within > * > .al-reveal, .al-reveal:focus-visible, .al-reveal[aria-expanded="true"] { opacity: 1; }
@media (hover: none) { .al-reveal { opacity: 1; } }

/* One skip link for screen readers and keyboard users; visible only when it has focus. */
.al-skip { position: absolute; left: 12px; top: -48px; z-index: var(--al-z-notice); height: 34px; display: inline-flex; align-items: center; padding: 0 14px; background: var(--al-navy-950); color: var(--al-rail-text-hi); border-radius: var(--al-r-control); font-size: var(--al-fs-13); text-decoration: none; transition: top var(--al-t-immediate) var(--al-ease); }
.al-skip:focus-visible { top: 12px; box-shadow: var(--al-focus); }

/* Classes that were once spans or divs are real buttons now: the button chrome is reset, the class's look stays. */
button.al-mini-d, button.al-inline-value, button.al-copy { font: inherit; border: 0; background: none; padding: 0; color: inherit; margin: 0; }
button.al-mini-d { width: 100%; }

/* A pressed row: nothing moves (Study 02); the plane says it was heard. */
.al-table tbody tr:active td { background: var(--al-inset); }

/* ═══════════════════════════════════════════════════════════════════════════
   STUDY 09 — RECOVERY. Offline is one state shown once (a chip by the identity, never a banner);
   routine changes queue and settle, material and external actions wait; failures are local and
   say why, a request over 10 s is a failure; sessions slide and re-sign-in happens in a dialog with
   the work kept; return refreshes silently; conflicts show both values and never resolve alone.
   ═══════════════════════════════════════════════════════════════════════════ */

/* The connection chip: present only while offline or reconnecting. A state, not an error — no status colour. */
.al-conn { display: none; align-items: center; gap: 7px; height: 26px; padding: 0 10px 0 8px; border-radius: 13px; background: var(--al-inset); border: 1px solid var(--al-line); font-size: var(--al-fs-12); color: var(--al-text-2); white-space: nowrap; }
.al-conn::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--al-text-3); }
.al-conn.is-offline, .al-conn.is-reconnecting { display: inline-flex; }
.al-conn.is-reconnecting::before { animation: al-shimmer 1200ms linear infinite; background: linear-gradient(90deg, var(--al-text-3), var(--al-line-strong), var(--al-text-3)); background-size: 200% 100%; }

/* A queued change keeps its pending mark (Study 03) and says so. */
.al-pending-note.is-queued::before { content: "queued · "; }

/* A conflict on one fact: their value, the time, two choices. Attention, never critical — nothing failed. */
.al-conflict { display: grid; gap: 8px; margin-top: 8px; padding: 0; border: 0; border-radius: 0; background: none; font-size: var(--al-fs-12); font-weight: 600; color: var(--al-attention); }
.al-conflict .al-conflict-who { color: var(--al-text-2); }
.al-conflict .al-conflict-vals { display: grid; grid-template-columns: max-content 1fr; gap: 4px 12px; }
.al-conflict .al-conflict-vals dt { color: var(--al-text-3); }
.al-conflict .al-conflict-vals dd { margin: 0; font-weight: 500; color: var(--al-text); }
.al-conflict .al-conflict-acts { display: flex; gap: 8px; }
.al-conflict .al-btn { height: 28px; padding: 0 10px; font-size: var(--al-fs-12); }

/* The open object changed while you were away: one quiet line, no flash. */
.al-stale { display: flex; align-items: center; gap: 8px; font-size: var(--al-fs-12); color: var(--al-text-3); padding: 6px 0; }
.al-stale .al-btn-quiet { height: 24px; padding: 0 8px; font-size: var(--al-fs-12); }

/* Sign in again, inside the layer; the fields are Study 06's. */
.al-signin .al-field:last-child { margin-bottom: 0; }
.al-signin .al-signin-who { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; font-size: var(--al-fs-13); }
.al-signin .al-signin-who .al-avatar { width: 30px; height: 30px; border-radius: 50%; overflow: hidden; display: block; flex: none; }
.al-signin .al-signin-who .al-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }   /* the foundation styles the avatar only inside .al-who (found by driving) */

@media (prefers-reduced-motion: reduce) {
  .al-split > .al-detail, .al-split.is-leaving > .al-detail, .al-btn, .al-action, .al-tab, .al-lens, .al-iconbtn, .al-overlay-panel, .al-overlay-scrim, .al-scroll, .al-split .al-table tbody, .al-menu, .al-pop, .al-tip, .al-scrim, .al-dialog, .al-notice, .al-drop, .al-filterbar, .al-selbar, .al-tick, .al-bar-fill, .al-reveal, .al-skip, .al-conn::before { transition: none !important; animation: none !important; scroll-behavior: auto !important; }
  .al-split.is-arriving > .al-detail, .al-page-enter, .al-skel::after { animation: none !important; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE REMOTE — Alfred, the one door (ALFRED_REMOTE_STEP8_LOCK_v1.0_2026-09-11,
   LOCKED by Peter). Lifted verbatim from specimen/interaction/remote_lab.html
   per the lock's own instruction. One control, fixed in the Frame's reserved
   corner on every surface: his name, mic, pen. One press: mic records AND
   opens the dialogue; write opens it as a chat. The breath means "Alfred is
   here" -- the plate alone, scale only, no shadow/colour/glint change,
   stills while he listens. ═══════════════════════════════════════════════ */

.al-pop.al-dialogue { right: 28px; bottom: 80px; left: auto; top: auto; width: 500px; max-width: 500px; max-height: calc(100vh - 120px); display: flex; flex-direction: column; padding: 0; transform: translateY(8px); }
.al-pop.al-dialogue.is-open { transform: none; }
.al-dialogue-hd { display: flex; align-items: center; gap: 10px; padding: 12px 16px; border-bottom: 1px solid var(--al-line); }
.al-dialogue-hd .al-state { font-size: var(--al-fs-12); color: var(--al-text-3); margin-left: auto; }
.al-dialogue-body { padding: 14px 16px; overflow-y: auto; font-size: var(--al-fs-13); line-height: 1.5; }
.al-dialogue .al-heard { font-family: var(--al-font-ui); font-size: var(--al-fs-13); color: var(--al-text-2); padding: 10px 12px; background: var(--al-inset); border-radius: var(--al-r-control); min-height: 40px; }
.al-dialogue .al-heard.is-live { color: var(--al-text); }
.al-dialogue .al-label { margin-top: 14px; }
.al-dialogue .al-flow-row { padding: 8px 0; }
.al-dialogue .al-flow-key { width: 22px; font-family: var(--al-font-id); font-size: var(--al-fs-11); color: var(--al-text-3); }
/* ── the conversation (slice 2, 2026-09-17) ───────────────────────────────
   A revision is not a new card: it is the same card with two things the
   person did not have before -- what Alfred ASSUMED, and what CHANGED. Both
   are read in the seconds before confirming, so neither may shout. A brass
   rule marks the step the correction touched, the same brass the rest of
   Alfred uses for provenance; the outgoing revision fades rather than
   vanishing, so the eye can follow one card becoming another. */
.al-dialogue .al-flow-row.al-changed { border-left: 2px solid var(--al-brass); padding-left: 10px; margin-left: -12px; }
.al-dialogue .al-superseded { opacity: .38; transition: opacity var(--al-t-structural) var(--al-ease); }
.al-dialogue #al-rm-diff .al-flow-title, .al-dialogue #al-rm-assume .al-flow-title { font-size: var(--al-fs-12); color: var(--al-text-2); font-weight: 400; }
.al-dialogue-ft { display: flex; align-items: center; gap: 8px; padding: 10px 12px; border-top: 1px solid var(--al-line); }
.al-dialogue-ft input { flex: 1; font: inherit; font-size: var(--al-fs-13); color: var(--al-text); background: var(--al-work); border: 1px solid var(--al-line-strong); border-radius: var(--al-r-control); padding: 8px 10px; outline: none; height: 34px; }
.al-dialogue-ft input:focus { border-color: var(--al-navy-900); box-shadow: 0 0 0 3px var(--al-navy-wash); }
/* Every one of the dialogue's own sub-panels toggles the plain `hidden`
   attribute from JS -- an author class with its own `display` (.al-fail,
   .al-flow…) otherwise silently outranks the UA default, so state stays
   painted underneath. This one rule is the fix for the whole family. */
.al-dialogue[hidden], .al-dialogue [hidden] { display: none !important; }


/* ── Phase 7 (Peter, 11 Oct 2026: the pack may change): P-9, P-10, P-7, P-2 ─────────────────────────────────────────────── */
/* P-9: the rail's items and the chips and tabs show the focus ring like every other focusable element */
.al-rail-item:focus-visible, .alf-filter:focus-visible, .alf-tab:focus-visible, .al-chip:focus-visible { outline: none; box-shadow: var(--al-focus); }
/* P-10: a link inside a row has a hit area of at least 24 px, by padding that does not move the text */
.rv-table td a, .rv-table td .rv-link, .al-flow-row .rv-link, .al-flow-row a:not(.al-action) { position: relative; }
.rv-table td a::after, .rv-table td .rv-link::after, .al-flow-row .rv-link::after, .al-flow-row a:not(.al-action)::after { content: ""; position: absolute; inset: -6px -2px; }
/* P-7: secondary lines on dense registers are 500, not 400, so they read on an office laptop */
.rv-table td .al-secondary, .rv-table td .al-meta, .rv-table td .al-absent { font-weight: 500; }
/* P-2: a fourth row height, 40 px, for the registers of hundreds of rows (a screen chooses it, never mixed with the others on one screen) */
#view-tax-register .rv-table td, #view-campaign-210 .rv-table td, #view-contacts .rv-table td, #view-agencies .rv-table td { height: var(--al-row-dense); }

/* P-5 (Phase 7): on a head row of figures, only the FIRST figure that needs the person's act is amber; the others stay text */
.iv-stats .iv-stat.is-attention ~ .iv-stat.is-attention .iv-stat-v { color: var(--al-text) !important; }

/* v3.1: no brass hairline under page titles (brass is a fine accent: menu edge, tab underline, lead band); small section labels are 12px text-2 labels */
.al-sec-title { color: var(--al-text-2); font-size: var(--al-fs-rail-label); font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }

/* P-1 (Phase 7, Peter 11 Oct 2026; overrides the 2 Oct pill ruling): Alfred is in the top bar. The field and the microphone are on every
   place; the dialogue opens as a right-hand drawer under the bar; the floating pill is retired (kept in the DOM, hidden, so its wiring is one) */
#al-rm-ctrl { display: none !important; }
.al-abar { display: flex; align-items: center; gap: 6px; flex: 0 1 460px; min-width: 180px; margin: 0 16px; }
.al-abar-field { flex: 1; display: flex; align-items: center; gap: 8px; height: var(--al-control-h); padding: 0 8px 0 14px; border: 0; border-radius: var(--al-r-control); background: var(--al-selected); box-shadow: none; color: var(--al-text-2); font: 400 var(--al-fs-ui)/1 var(--al-font-ui); cursor: text; text-align: left; min-width: 0; }
.al-abar-field:hover { box-shadow: inset 0 0 0 1px var(--al-action); }
.al-abar-field:focus-visible, .al-abar-mic:focus-visible { outline: none; box-shadow: var(--al-focus); }
.al-abar-ph { flex: 1; line-height: 1.5; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.al-abar-kbd { font: 700 var(--al-fs-rail-label)/1 var(--al-font-ui); color: var(--al-text-2); border: 0; box-shadow: inset 0 0 0 1px var(--al-scroll-thumb); border-radius: 5px; padding: 3px 7px; background: var(--al-card); }
.al-abar-mic { flex: none; width: var(--al-control-h); height: var(--al-control-h); display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 50%; background: var(--al-card); box-shadow: inset 0 0 0 1px var(--al-divider); color: var(--al-action); cursor: pointer; transition: background var(--al-t-hover) var(--al-ease); }
.al-abar-mic:hover { background: var(--al-selected); }
.al-abar.is-listening .al-abar-mic { background: var(--al-critical); color: var(--al-text-inverse); box-shadow: none; }
.al-pop.al-dialogue { top: var(--al-topbar-h); right: 0; bottom: 0; left: auto; width: 420px; max-width: 100vw; max-height: none; height: auto; border-radius: 0; border-width: 0 0 0 1px; transform: translateX(24px); border: 1px solid var(--al-divider); box-shadow: var(--al-shadow-card); }
.al-pop.al-dialogue.is-open { transform: none; }
@media (max-width: 760px) { .al-abar-kbd { display: none; } .al-abar { flex-basis: 220px; margin: 0 8px; } .al-pop.al-dialogue { width: 100vw; } }

/* KD-2 (RULES 13; CD audits TK2-01 and PE2-11, 17 Oct): the title with the head's actions, then the tab strip on its own line, then the content. This replaces
   the one-line title | tabs of P-3 (Phase 7) and S3-1 (Materials step 3), which pre-date the v3.1 pack. */
#view-calendar, #view-operations, #ph-root, #tk-root, #am-root { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 24px; row-gap: 16px; align-items: center; }
#view-calendar > .cv-head.cv-head, #view-operations > #op-head.rv-head, #ph-root > #ph-head.rv-head, #tk-root > .rv-head.rv-head, #am-root > .rv-head.rv-head { display: contents; }
#view-calendar > .cv-head .al-title, #view-operations > #op-head .al-title, #ph-root > #ph-head .al-title, #tk-root > .rv-head .al-title, #am-root > .rv-head .al-title { grid-column: 1; grid-row: 1; margin: 0; }
#view-calendar > .cv-head .cv-head-r, #tk-root > .rv-head .rv-head-r { grid-column: 2; grid-row: 1; }
#view-calendar > .cv-tabs, #view-operations > #op-states, #ph-root > #ph-states, #tk-root > #tk-body > .rv-tabs, #am-root > #am-body > .rv-tabs { grid-column: 1 / -1; grid-row: 2; margin: 0; min-width: 0; }
#view-operations > #op-states > .rv-tabs, #ph-root > #ph-states > .rv-tabs, #tk-root > #tk-body > .rv-tabs, #am-root > #am-body > .rv-tabs { margin: 0; width: auto; }
#view-calendar > #cl-body, #view-operations > #op-body, #ph-root > #ph-body { grid-column: 1 / -1; grid-row: 3; min-width: 0; }
#tk-root > #tk-body#tk-body, #am-root > #am-body#am-body { display: contents; }
#tk-root > #tk-body > :not(.rv-tabs), #am-root > #am-body > :not(.rv-tabs) { grid-column: 1 / -1; min-width: 0; }

/* the shimmer of Materials v2.1 loading placeholders; reduced motion stills it */
@keyframes alShimmer { from { background-position: -240px 0; } to { background-position: 360px 0; } }
@media (prefers-reduced-motion: reduce) { .al-skel::before, .al-skel::after { animation: none !important; } }
