/* ═══════════════════════════════════════════════════════════════════════
   FIELDCUT UI — COMPONENTS
   Every shape FieldCut uses, built once, with all six states:
   default · hover · active · focus-visible · disabled · loading

   A component that is not also on ui/demo.html does not exist. Add it in
   both places or not at all.

   THE TWO RULES THIS FILE ENFORCES
   1. The chrome is greyscale. Panels, toolbars, tabs, menus, tables and
      headers never carry a hue. Two colours are left, and because nothing
      else is coloured they carry across a room.
   2. Kept and dropped never differ by hue alone. Kept is a FLAT fill with
      a solid edge and a tick. Dropped is a HATCHED fill with a dashed edge
      and a cross. Turn the screen greyscale and the two are still obvious.

   Never `transition: all`, and never a transition on width, height, top or
   left. Colour, transform, opacity and outline only.

   Every :hover, :active and :focus rule below also answers to .is-hover,
   .is-active and .is-focus, so any state can be pinned open for the demo
   page without restating a single declaration.
   ═══════════════════════════════════════════════════════════════════════ */

/* The dropped texture. 45° stripes at 4px, laid over the dropped fill. It
   is the fill that separates kept from dropped for a colourblind user;
   the hue is only there for everyone else. */
/* On a SOLID dropped fill the stripes have to be lighter than the fill. */
.hatch,
.chip--reject.is-on,
.chip--reject[aria-pressed="true"],
.badge--dropped{
  background-image:repeating-linear-gradient(45deg,
    rgba(255,255,255,.22) 0 2px, transparent 2px 6px);
}
/* On a WASH — a row, a table cell, the veil over a photograph — the ground is
   nearly the page, so the stripes have to be darker instead. Same texture,
   opposite direction, and --fc-rj-line already flips with the theme. */
.hatch--wash,
.thumb.is-dropped .thumb__veil,
.row.is-dropped,
.table tbody tr.is-dropped td{
  background-image:repeating-linear-gradient(45deg,
    var(--fc-rj-line) 0 1px, transparent 1px 6px);
}

/* ═════ BUTTON ═══════════════════════════════════════════════════════════
   The default button is the reference's own: a borderless grey fill. An
   outline is the exception, not the rule, because a page of outlined boxes
   is a page of boxes. One filled accent button per view, and one only.

   14px at 600. The single place weight does work: at 14px there is no
   smaller step to demote a label to, so the reference's own trick stands in.
   ═══════════════════════════════════════════════════════════════════════ */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--fc-s-2);
  min-height:var(--fc-ctrl-h);min-width:var(--fc-hit);
  padding:0 var(--fc-pad-btn);
  border:var(--fc-bw) solid transparent;
  border-radius:var(--fc-r-pill);
  background-color:var(--fc-bg3);color:var(--fc-fg);
  font-size:var(--fc-fs-4);font-weight:var(--fc-fw-label);line-height:1;
  text-decoration:none;white-space:nowrap;
  transition:background-color var(--fc-dur-tap) var(--fc-ease),
             border-color var(--fc-dur-tap) var(--fc-ease),
             color var(--fc-dur-tap) var(--fc-ease),
             transform var(--fc-dur-tap) var(--fc-ease),
             opacity var(--fc-dur-tap) var(--fc-ease);
}
.btn:hover,.btn.is-hover{background-color:var(--fc-bg2)}
.btn:active,.btn.is-active{transform:scale(var(--fc-press));background-color:var(--fc-bg3)}
.btn:disabled,.btn[aria-disabled="true"]{
  opacity:.45;background-color:var(--fc-bg3);color:var(--fc-fg3);transform:none;
}
.btn--secondary{background-color:var(--fc-bg3);color:var(--fc-fg)}   /* the default, named */

/* Outline. For a control that must show its edge on a busy surface. The
   edge is --fc-border2, which is the only border token that clears 3:1. */
.btn--outline{background-color:transparent;border-color:var(--fc-border2)}
.btn--outline:hover,.btn--outline.is-hover{background-color:var(--fc-bg2);border-color:var(--fc-fg2)}
.btn--outline:active,.btn--outline.is-active{background-color:var(--fc-bg3)}

/* Quiet. Lives inside a toolbar where anything more would be noise.
   --ghost is the same button under its older name. */
.btn--quiet,.btn--ghost{background-color:transparent;border-color:var(--fc-border);color:var(--fc-fg)}
.btn--quiet:hover,.btn--quiet.is-hover,.btn--ghost:hover,.btn--ghost.is-hover{background-color:var(--fc-bg2);color:var(--fc-fg)}
.btn--quiet:active,.btn--quiet.is-active,.btn--ghost:active,.btn--ghost.is-active{background-color:var(--fc-bg3)}
.btn--quiet:disabled,.btn--ghost:disabled{background-color:transparent;color:var(--fc-fg3)}

/* INK — the reference's solid black capsule, and the loudest button in the
   kit. A neutral call to action: a download, a next step, where nothing is
   being kept or dropped. It inverts itself in dark because --fc-fg does.

   --primary and --ink are deliberately the same declaration rather than two
   that happen to match. 16 September 2026 --primary stopped being the blue
   fill and became this, because the reference's primary button is black and
   because blue in FieldCut is a meaning rather than a rank. Anything that
   genuinely means KEPT asks for --accent below. Two names survive because
   markup across five pages already uses both, and one declaration is the
   only way they cannot drift apart. */
.btn--ink,.btn--primary{background-color:var(--fc-fg);border-color:transparent;color:var(--fc-canvas)}
.btn--ink:hover,.btn--ink.is-hover,
.btn--primary:hover,.btn--primary.is-hover{background-color:var(--fc-fg2)}
.btn--ink:active,.btn--ink.is-active,
.btn--primary:active,.btn--primary.is-active{background-color:var(--fc-fg);opacity:1;transform:scale(var(--fc-press))}
.btn--ink:disabled,
.btn--primary:disabled{background-color:var(--fc-fg);border-color:transparent;color:var(--fc-canvas);opacity:.45}

/* Accent — KEPT. What primary used to be: the one flat accent fill, for
   "keep / go / confirm" inside the app. Solid edge, never hatched. */
.btn--accent{background-color:var(--fc-ac);border-color:var(--fc-ac);color:var(--fc-ac-on)}
.btn--accent:hover,.btn--accent.is-hover{background-color:var(--fc-ac);border-color:var(--fc-ac);opacity:.88}
.btn--accent:active,.btn--accent.is-active{background-color:var(--fc-ac);opacity:1;transform:scale(var(--fc-press))}
.btn--accent:disabled{background-color:var(--fc-ac);border-color:var(--fc-ac);color:var(--fc-ac-on);opacity:.45}

/* Destructive — DROPPED. Same weight as primary, opposite meaning, and it
   never leans on hue: the quiet variant carries a DASHED edge, which no
   other control in the kit has. --danger-solid is its older name. */
.btn--destructive,.btn--danger-solid{background-color:var(--fc-rj);border-color:var(--fc-rj);color:var(--fc-rj-on)}
.btn--destructive:hover,.btn--destructive.is-hover,.btn--danger-solid:hover,.btn--danger-solid.is-hover{background-color:var(--fc-rj);opacity:.88}
.btn--destructive:active,.btn--destructive.is-active,.btn--danger-solid:active,.btn--danger-solid.is-active{background-color:var(--fc-rj);opacity:1;transform:scale(var(--fc-press))}
.btn--destructive:disabled,.btn--danger-solid:disabled{background-color:var(--fc-rj);border-color:var(--fc-rj);color:var(--fc-rj-on);opacity:.45}

.btn--danger{background-color:transparent;border:var(--fc-bw) dashed var(--fc-rj-line);color:var(--fc-rj-ink)}
.btn--danger:hover,.btn--danger.is-hover{background-color:var(--fc-rj-wash);border-color:var(--fc-rj)}
.btn--danger:active,.btn--danger.is-active{background-color:var(--fc-rj-wash);transform:scale(var(--fc-press))}
.btn--danger:disabled{background-color:transparent;border-color:var(--fc-border);color:var(--fc-fg3)}

.btn--sm{min-height:var(--fc-ctrl-h-sm);padding:0 var(--fc-pad-btn-sm);font-size:var(--fc-fs-3)}
.btn--lg{min-height:var(--fc-ctrl-h-lg);padding:0 var(--fc-pad-btn)}
/* Kept so existing markup resolves. There is no capsule any more, so this
   and .chip--pill now match the plain shape on purpose. */
.btn--pill{border-radius:var(--fc-r-pill)}
.btn--block{display:flex;width:100%}

/* Loading. The label stays in place, so the button never changes width
   mid-press and the row behind it never reflows. */
.btn.is-loading{pointer-events:none;position:relative;color:transparent}
.btn.is-loading::after{
  content:"";position:absolute;width:16px;height:16px;border-radius:var(--fc-r-pill);
  border:2px solid currentColor;border-top-color:transparent;color:var(--fc-fg2);
  animation:fc-spin .7s linear infinite;
}
.btn--accent.is-loading::after{color:var(--fc-ac-on)}
/* Vermilion fills ask for the vermilion pairing, not the accent's. Both
   resolve to white today, so this renders identically either way — which is
   exactly why it is worth naming correctly now rather than after one of the
   two moves and a spinner quietly goes invisible on one button. */
.btn--destructive.is-loading::after,
.btn--danger-solid.is-loading::after{color:var(--fc-rj-on)}
.btn--primary.is-loading::after,
.btn--ink.is-loading::after{color:var(--fc-canvas)}
@keyframes fc-spin{to{transform:rotate(360deg)}}

/* An icon button whose art is smaller than a finger. The visual stays
   small, the reachable area never does. */
.btn--icon{padding:0;width:var(--fc-ctrl-h);height:var(--fc-ctrl-h);min-width:var(--fc-ctrl-h)}
.btn--icon.btn--sm{width:var(--fc-ctrl-h-sm);height:var(--fc-ctrl-h-sm);min-width:var(--fc-ctrl-h-sm)}
.btn__ico{width:16px;height:16px;flex:none;display:block}

/* Bolt a legal tap target onto anything drawn smaller than one. The overlay
   is part of the element, so the click still lands on the control; only the
   reach grows, never the drawing. Every control in this kit that is under
   40px tall is in the list below, so none of them has to remember. */
.hit-pad,.btn--sm,.chip,.seg__btn,.check,.radio,.switch,.table__sort{position:relative}
.hit-pad::before,
.btn--sm::before,.chip::before,.seg__btn::before,
.check::before,.radio::before,.switch::before,.table__sort::before{
  content:"";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
  width:max(100%,var(--fc-hit));height:max(100%,var(--fc-hit));
}
/* One exception, and it is deliberate: the X inside a chip is a target
   nested inside a bigger target. Blowing it up to 40px would eat the chip
   it sits in, so it takes the 24px floor that applies to nested controls. */
.chip__x{position:relative}
.chip__x::before{
  content:"";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
  width:max(100%,24px);height:max(100%,24px);
}

/* ═════ FIELD ════════════════════════════════════════════════════════════
   Filled, and borderless. The reference's input is a grey block with no
   edge at all, which is quieter than an outline and reads as a slot rather
   than a box. Focus is the ink ring, on click as well as on tab, because a
   text field is the one control where a mouse user still needs to see it.
   ═══════════════════════════════════════════════════════════════════════ */
.field{display:flex;flex-direction:column;gap:var(--fc-s-2)}
.field > label,.field__label{font-size:var(--fc-fs-4);font-weight:var(--fc-fw-label);color:var(--fc-fg2)}

.input,.select,.textarea{
  min-height:var(--fc-ctrl-h);width:100%;
  padding:0 var(--fc-s-3);
  background-color:var(--fc-bg);color:var(--fc-fg);
  border:var(--fc-bw) solid var(--fc-border);border-radius:var(--fc-radius);
  font-size:var(--fc-fs-4);
  transition:background-color var(--fc-dur-tap) var(--fc-ease),
             border-color var(--fc-dur-tap) var(--fc-ease),
             opacity var(--fc-dur-tap) var(--fc-ease);
}
.textarea{min-height:96px;padding:var(--fc-s-3);line-height:var(--fc-lh);resize:vertical}
.input::placeholder,.textarea::placeholder{color:var(--fc-fg3)}
.input:hover,.input.is-hover,.select:hover,.select.is-hover,.textarea:hover,.textarea.is-hover{border-color:var(--fc-border2)}
.input:focus,.input.is-focus,.select:focus,.select.is-focus,.textarea:focus,.textarea.is-focus{
  background-color:var(--fc-bg);outline:var(--fc-bw2) solid var(--fc-ring);outline-offset:2px;
}
.input:disabled,.select:disabled,.textarea:disabled{
  opacity:.5;background-color:var(--fc-bg2);color:var(--fc-fg3);
}
.input.is-loading,.select.is-loading{opacity:.55;pointer-events:none}
.field--err .input,.field--err .select,.field--err .textarea{
  border-color:var(--fc-err);background-color:var(--fc-err-wash);
}
.field__err{font-size:var(--fc-fs-3);color:var(--fc-err)}
.field__hint{font-size:var(--fc-fs-3);color:var(--fc-fg2)}

/* A key, a filename, a machine value. Mono, always. */
.input--mono{font-family:var(--fc-font-mono);letter-spacing:.02em}

/* The chevron is drawn from two gradients rather than an image, so it
   takes the ink colour in both themes and fetches nothing. */
.select{
  appearance:none;-webkit-appearance:none;
  padding-right:var(--fc-s-6);cursor:pointer;
  background-image:
    linear-gradient(45deg,transparent 50%,var(--fc-fg2) 50%),
    linear-gradient(135deg,var(--fc-fg2) 50%,transparent 50%);
  background-position:right 18px center,right 12px center;
  background-size:6px 6px,6px 6px;
  background-repeat:no-repeat;
}
.select:disabled{background-image:
    linear-gradient(45deg,transparent 50%,var(--fc-fg3) 50%),
    linear-gradient(135deg,var(--fc-fg3) 50%,transparent 50%)}

/* ═════ CHECKBOX · RADIO ═════════════════════════════════════════════════
   The unchecked edge is --fc-border2 because an empty box is defined by
   nothing else, and a control's only defining line has to clear 3:1.
   Checked is the KEPT fill with a tick — flat, solid, never hatched.
   ═══════════════════════════════════════════════════════════════════════ */
.check,.radio{
  appearance:none;-webkit-appearance:none;position:relative;flex:none;
  width:18px;height:18px;margin:0;
  background-color:var(--fc-bg);border:var(--fc-bw) solid var(--fc-border2);
  border-radius:var(--fc-r-sm);cursor:pointer;
  transition:background-color var(--fc-dur-tap) var(--fc-ease),
             border-color var(--fc-dur-tap) var(--fc-ease),
             transform var(--fc-dur-tap) var(--fc-ease),
             opacity var(--fc-dur-tap) var(--fc-ease);
}
/* Square, like everything else. A radio is normally told apart from a
   checkbox by being round, so with the round gone the MARK has to carry it:
   a checkbox gets a tick, a radio gets a hard filled square. Two different
   marks read apart at a glance as reliably as two different outlines did. */
.radio{border-radius:var(--fc-r-sm)}
.check:hover,.check.is-hover,.radio:hover,.radio.is-hover{border-color:var(--fc-fg2);background-color:var(--fc-bg2)}
.check:active,.check.is-active,.radio:active,.radio.is-active{transform:scale(var(--fc-press))}
.check:checked,.radio:checked{background-color:var(--fc-ac);border-color:var(--fc-ac)}
.check:checked:hover,.check:checked.is-hover,.radio:checked:hover,.radio:checked.is-hover{opacity:.88}
.check:checked::after{
  content:"";position:absolute;left:5px;top:1px;width:5px;height:10px;
  border:solid var(--fc-ac-on);border-width:0 2px 2px 0;transform:rotate(45deg);
}
.check:indeterminate{background-color:var(--fc-ac);border-color:var(--fc-ac)}
.check:indeterminate::after{
  content:"";position:absolute;left:3px;top:7px;width:10px;height:2px;background-color:var(--fc-ac-on);
}
/* THE RADIO'S MARK IS ALWAYS THERE, IT ONLY FILLS IN.
   Checked, a radio and a checkbox read apart instantly: a tick is diagonal, a
   filled square is a block. UNCHECKED they were pixel-identical once the round
   went, both an 18px square with one hairline, so a radio group with nothing
   chosen yet was indistinguishable from a checkbox group. Roundness used to
   carry that and roundness is gone, so the mark carries it instead: an empty
   square at rest, exactly where the filled one lands. One glance, no circle.
   ::before is spoken for by the 40px reach, so both states share ::after. */
.radio::after{
  content:"";position:absolute;inset:5px;border-radius:0;
  border:var(--fc-bw) solid var(--fc-border2);background-color:transparent;
  transition:background-color var(--fc-dur-tap) var(--fc-ease),
             border-color var(--fc-dur-tap) var(--fc-ease);
}
.radio:checked::after{border-color:transparent;background-color:var(--fc-ac-on)}
.check:disabled,.radio:disabled{opacity:.45;background-color:var(--fc-bg2);border-color:var(--fc-border2);transform:none}
.check.is-loading,.radio.is-loading{opacity:.5;pointer-events:none}

/* A whole clickable line: box, label, and a 40px reach around both. */
.choice{
  display:flex;align-items:center;gap:var(--fc-s-3);
  min-height:var(--fc-hit);padding:0 var(--fc-s-2);
  border-radius:var(--fc-radius);cursor:pointer;
  transition:background-color var(--fc-dur-tap) var(--fc-ease);
}
.choice:hover,.choice.is-hover{background-color:var(--fc-bg2)}
.choice.is-disabled{opacity:.45;cursor:not-allowed}

/* ═════ SWITCH ═══════════════════════════════════════════════════════════
   A capsule track with a capsule knob, which is what macOS draws and what
   the reference draws. This was square for four days in September on the
   argument that a round shape inside a squared system reads as a foreign
   object. That argument died with the squared system: the kit has a corner
   ladder now and a real capsule on everything you press, so the switch is
   back to the shape every other control of its kind has.
   ═══════════════════════════════════════════════════════════════════════ */
.switch{
  position:relative;width:44px;height:26px;flex:none;padding:0;
  background-color:var(--fc-bg3);border:var(--fc-bw) solid var(--fc-border2);
  border-radius:var(--fc-r-pill);cursor:pointer;appearance:none;-webkit-appearance:none;
  transition:background-color var(--fc-dur-tap) var(--fc-ease),
             border-color var(--fc-dur-tap) var(--fc-ease),
             opacity var(--fc-dur-tap) var(--fc-ease);
}
.switch:hover,.switch.is-hover{border-color:var(--fc-fg2)}
.switch[aria-checked="true"],.switch:checked{background-color:var(--fc-ac);border-color:var(--fc-ac)}
.switch::after{
  content:"";position:absolute;top:2px;left:2px;width:20px;height:20px;
  border-radius:var(--fc-r-pill);background-color:var(--fc-bg);box-shadow:var(--fc-sh-1);
  transition:transform var(--fc-dur-tap) var(--fc-ease);
}
.switch[aria-checked="true"]::after,.switch:checked::after{transform:translateX(18px)}
.switch:active::after,.switch.is-active::after{transform:scale(var(--fc-press))}
.switch[aria-checked="true"]:active::after,.switch[aria-checked="true"].is-active::after,.switch:checked:active::after,.switch:checked.is-active::after{transform:translateX(18px) scale(var(--fc-press))}
.switch:disabled,.switch[aria-disabled="true"]{opacity:.45;cursor:not-allowed}
.switch.is-loading{opacity:.55;pointer-events:none}

/* ═════ CHIP ═════════════════════════════════════════════════════════════
   A chip is a decision you can take back. Kept is flat; dropped is hatched
   and struck through. Grey means undecided, and grey is most of the screen.
   ═══════════════════════════════════════════════════════════════════════ */
.chip{
  display:inline-flex;align-items:center;gap:var(--fc-s-2);
  min-height:var(--fc-ctrl-h-sm);padding:0 var(--fc-s-3);
  border:var(--fc-bw) solid var(--fc-border);border-radius:var(--fc-r-pill);
  background-color:var(--fc-bg);color:var(--fc-fg);font-size:var(--fc-fs-3);
  white-space:nowrap;
  transition:background-color var(--fc-dur-tap) var(--fc-ease),
             border-color var(--fc-dur-tap) var(--fc-ease),
             color var(--fc-dur-tap) var(--fc-ease),
             transform var(--fc-dur-tap) var(--fc-ease),
             opacity var(--fc-dur-tap) var(--fc-ease);
}
.chip:hover,.chip.is-hover{background-color:var(--fc-bg2);border-color:var(--fc-border2)}
.chip:active,.chip.is-active{transform:scale(var(--fc-press))}
.chip:disabled,.chip[aria-disabled="true"]{opacity:.45;transform:none;background-color:var(--fc-bg2)}
.chip.is-loading{opacity:.55;pointer-events:none}
.chip--pill{border-radius:var(--fc-r-pill)}

/* A plain chip that is ON. Without this a pressed filter chip looked exactly
   like an unpressed one, which is the whole job of a filter chip. It fills
   with ink rather than colour, because colour in this kit means kept or
   dropped and "this filter is on" is neither. Declared BEFORE the pick and
   reject variants so those still win when a chip carries both. */
.chip[aria-pressed="true"],.chip.is-on{
  background-color:var(--fc-fg);border-color:var(--fc-fg);color:var(--fc-canvas);
}
.chip[aria-pressed="true"]:hover,.chip[aria-pressed="true"].is-hover,
.chip.is-on:hover,.chip.is-on.is-hover{
  background-color:var(--fc-fg);border-color:var(--fc-fg);opacity:.88;
}

.chip--pick[aria-pressed="true"],.chip--pick.is-on{
  background-color:var(--fc-ac);border-color:var(--fc-ac);color:var(--fc-ac-on);
}
.chip--reject[aria-pressed="true"],.chip--reject.is-on{
  background-color:var(--fc-rj);border-color:var(--fc-rj);color:var(--fc-rj-on);
  text-decoration:line-through;text-decoration-thickness:1px;
}
/* A decided chip keeps its colour on hover; it only dims. Turning it grey
   would read as the decision being undone. */
.chip--pick[aria-pressed="true"]:hover,.chip--pick[aria-pressed="true"].is-hover,.chip--pick.is-on:hover,.chip--pick.is-on.is-hover{background-color:var(--fc-ac);opacity:.88}
.chip--reject[aria-pressed="true"]:hover,.chip--reject[aria-pressed="true"].is-hover,.chip--reject.is-on:hover,.chip--reject.is-on.is-hover{background-color:var(--fc-rj);opacity:.88}
.chip--folder{font-family:var(--fc-font-mono);font-size:var(--fc-fs-2)}
/* A hard square, not a rounded one. At 8px a 5px radius clamps to 4px, which
   is a perfect circle, so the one radius token would have quietly put a dot
   back into a kit that has no round shapes. Anything under about 12px has to
   say its corner outright. */
.chip__dot{width:8px;height:8px;border-radius:0;background:currentColor;flex:none}
.chip__x{display:inline-flex;align-items:center;justify-content:center;
  width:16px;height:16px;margin-right:calc(-1 * var(--fc-s-1));
  border-radius:var(--fc-r-sm);color:currentColor;opacity:.7}
.chip__x:hover,.chip__x.is-hover{opacity:1;background:rgba(128,128,128,.25)}

/* ═════ TAG — a noun, not a decision. Never interactive on its own. ══════ */
.tag{
  display:inline-flex;align-items:center;gap:var(--fc-s-1);
  padding:var(--fc-s-0) var(--fc-s-2);border-radius:var(--fc-r-sm);
  background-color:var(--fc-bg3);color:var(--fc-fg2);
  font-size:var(--fc-fs-2);line-height:20px;
}
.tag--mono{font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums}
.tag--kept{background-color:var(--fc-ac-wash);color:var(--fc-ac-ink)}
.tag--dropped{background-color:var(--fc-rj-wash);color:var(--fc-rj-ink)}

/* ═════ BADGE — status only, never decoration ════════════════════════════ */
.badge{
  display:inline-flex;align-items:center;gap:var(--fc-s-1);
  padding:var(--fc-s-0) var(--fc-s-2);border-radius:var(--fc-r-pill);
  background-color:var(--fc-bg3);color:var(--fc-fg2);
  font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);font-weight:var(--fc-fw-label);
  letter-spacing:.01em;line-height:18px;
}
.badge--ok{background-color:var(--fc-ok-wash);color:var(--fc-ok)}
.badge--warn{background-color:var(--fc-warn-wash);color:var(--fc-warn)}
.badge--err{background-color:var(--fc-err-wash);color:var(--fc-err)}
.badge--info{background-color:var(--fc-ac-wash);color:var(--fc-ac-ink)}
.badge--kept{background-color:var(--fc-ac);color:var(--fc-ac-on)}
.badge--dropped{background-color:var(--fc-rj);color:var(--fc-rj-on)}

/* ═════ CARD · PANEL ═════════════════════════════════════════════════════
   A card is a white surface on a white page, so nothing but the shadow
   separates the two. That is the reference's whole move, and it is why
   --fc-sh-1 is structural here rather than decoration: take it away and
   the card stops existing. On black the same token becomes an inset white
   hairline, because a shadow cannot be seen there.

   Concentric: the card is --fc-r-box 12px, so a control inside it at
   --fc-s-1 4px of padding wants --fc-radius 8px, and a chip inside that
   wants --fc-r-sm 6px. The ladder already does the arithmetic.
   ═══════════════════════════════════════════════════════════════════════ */
.card{
  background-color:var(--fc-bg);
  border-radius:var(--fc-r-box);box-shadow:var(--fc-sh-1);padding:var(--fc-s-5);
}
.card--flat{background-color:transparent;box-shadow:none;padding:0}
.card--raised{background-color:var(--fc-bg);box-shadow:var(--fc-sh-soft)}
/* The base card carries no border any more, so pick/drop state their own —
   the coloured edge is the whole signal these two exist to give. Both keep
   --fc-sh-1 on the end of the shadow list: a picked card is still a card,
   and dropping the lift would sink it into the page. */
.card--pick{border:var(--fc-bw) solid var(--fc-ac);
  box-shadow:inset 2px 0 0 var(--fc-ac),var(--fc-sh-1)}
/* The other half of the same pair. Dashed as well as vermilion, so the
   two still read apart with the colour taken away. */
.card--drop{border:var(--fc-bw) dashed var(--fc-rj);
  box-shadow:inset 2px 0 0 var(--fc-rj),var(--fc-sh-1)}
/* A card that is itself the control. Same six states as a button. Hover
   lifts it rather than darkening an edge, because there is no edge left to
   darken — the base card lost its border when the shadow took over. */
.card--tap{cursor:pointer;text-align:left;width:100%;display:block;font:inherit;color:inherit;
  transition:background-color var(--fc-dur-tap) var(--fc-ease),
             box-shadow var(--fc-dur-tap) var(--fc-ease),
             transform var(--fc-dur-tap) var(--fc-ease),
             opacity var(--fc-dur-tap) var(--fc-ease)}
.card--tap:hover,.card--tap.is-hover{background-color:var(--fc-bg2);box-shadow:var(--fc-sh-2)}
.card--tap:active,.card--tap.is-active{transform:scale(.995);background-color:var(--fc-bg3)}
.card--tap:disabled,.card--tap.is-disabled{opacity:.45;pointer-events:none}
.card--tap.is-loading{opacity:.6;pointer-events:none}
.card__title{font-size:var(--fc-fs-6);line-height:var(--fc-lh-snug)}
.card__meta{font-size:var(--fc-fs-3);color:var(--fc-fg2)}
.card__end{display:flex;gap:var(--fc-s-2);margin-top:var(--fc-s-4);flex-wrap:wrap}

.panel{
  background-color:var(--fc-bg);
  border-radius:var(--fc-r-panel);box-shadow:var(--fc-sh-pop);overflow:hidden;
}
.panel__head{
  display:flex;align-items:center;gap:var(--fc-s-3);
  min-height:var(--fc-hit);padding:var(--fc-s-2) var(--fc-s-4);
  border-bottom:var(--fc-bw) solid var(--fc-border);
}
.panel__body{padding:var(--fc-s-4)}
.panel__foot{padding:var(--fc-s-3) var(--fc-s-4);border-top:var(--fc-bw) solid var(--fc-border);
  background-color:var(--fc-canvas)}

/* ═════ TOOLBAR — quiet buttons on a hairline ════════════════════════════ */
.toolbar{
  display:flex;align-items:center;gap:var(--fc-s-1);
  min-height:var(--fc-hit);padding:var(--fc-s-1) var(--fc-s-2);
  background-color:var(--fc-bg);border-bottom:var(--fc-bw) solid var(--fc-border);
}
.toolbar--glass{background-color:var(--fc-glass);backdrop-filter:var(--fc-glass-fx)}
.toolbar--float{border:var(--fc-bw) solid var(--fc-border);border-radius:var(--fc-radius);
  box-shadow:var(--fc-sh-soft)}
.toolbar__sep{width:1px;height:20px;margin:0 var(--fc-s-2);background-color:var(--fc-border);flex:none}
.toolbar__gap{margin-left:auto}
.toolbar__title{font-size:var(--fc-fs-4);font-weight:var(--fc-fw-label);color:var(--fc-fg2)}

/* ═════ ROW — a list line: thumb, title, meta, actions ═══════════════════
   Selected shows a 2px rail as well as a wash, so the state survives a
   greyscale screen. Dropped is hatched and struck.
   ═══════════════════════════════════════════════════════════════════════ */
.row{
  display:flex;align-items:center;gap:var(--fc-s-3);
  min-height:var(--fc-hit);padding:var(--fc-s-2) var(--fc-s-3);
  border-radius:var(--fc-radius);width:100%;text-align:left;
  transition:background-color var(--fc-dur-tap) var(--fc-ease),
             opacity var(--fc-dur-tap) var(--fc-ease),
             transform var(--fc-dur-tap) var(--fc-ease);
}
.row:hover,.row.is-hover{background-color:var(--fc-bg2)}
.row:active,.row.is-active{transform:scale(.995)}
.row[aria-selected="true"],.row.is-on{
  background-color:var(--fc-ac-wash);box-shadow:inset 2px 0 0 var(--fc-ac);
}
.row.is-dropped{
  background-color:var(--fc-rj-wash);box-shadow:inset 2px 0 0 var(--fc-rj);
}
.row[aria-selected="true"]:hover,.row[aria-selected="true"].is-hover,.row.is-on:hover,.row.is-on.is-hover{background-color:var(--fc-ac-wash)}
.row.is-dropped:hover,.row.is-dropped.is-hover{background-color:var(--fc-rj-wash)}
.row.is-dropped .row__title{text-decoration:line-through;text-decoration-thickness:1px;color:var(--fc-fg2)}
.row.is-disabled,.row:disabled{opacity:.45;pointer-events:none}
.row.is-loading{pointer-events:none;opacity:.7}
.row__thumb{display:block;width:40px;height:40px;border-radius:var(--fc-r-sm);object-fit:cover;flex:none;background-color:var(--fc-bg2)}
.row__body{min-width:0;flex:1;display:flex;flex-direction:column;gap:var(--fc-s-0)}
.row__title{display:block;font-size:var(--fc-fs-4);line-height:var(--fc-lh-snug);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.row__meta{display:block;font-size:var(--fc-fs-2);color:var(--fc-fg2)}
.row__end{margin-left:auto;display:flex;align-items:center;gap:var(--fc-s-2);flex:none}

/* ═════ SIDEBAR TREE ROW ═════════════════════════════════════════════════
   Indent comes from --depth, so nesting never needs a new class.
   ═══════════════════════════════════════════════════════════════════════ */
.tree{display:flex;flex-direction:column;gap:var(--fc-s-0);padding:var(--fc-s-1)}
.tree__row{
  --depth:0;
  display:flex;align-items:center;gap:var(--fc-s-2);width:100%;
  min-height:var(--fc-hit);border-radius:var(--fc-radius);text-align:left;
  padding:0 var(--fc-s-2) 0 calc(var(--fc-s-2) + (var(--depth) * var(--fc-s-4)));
  font-size:var(--fc-fs-4);color:var(--fc-fg2);
  transition:background-color var(--fc-dur-tap) var(--fc-ease),
             color var(--fc-dur-tap) var(--fc-ease),
             opacity var(--fc-dur-tap) var(--fc-ease);
}
.tree__row:hover,.tree__row.is-hover{background-color:var(--fc-bg2);color:var(--fc-fg)}
.tree__row:active,.tree__row.is-active{background-color:var(--fc-bg3)}
.tree__row[aria-current="true"],.tree__row.is-on{
  background-color:var(--fc-bg3);color:var(--fc-fg);font-weight:var(--fc-fw-label);
  box-shadow:inset 2px 0 0 var(--fc-fg);
}
.tree__row:disabled,.tree__row.is-disabled{opacity:.45;pointer-events:none}
/* Same reason as .queue__key above: the count is a child with its own colour,
   so promoting the row's text did not reach it. */
.tree__row[aria-current="true"] .tree__count,
.tree__row.is-on .tree__count{color:var(--fc-fg2)}
.tree__row.is-loading{opacity:.6;pointer-events:none}
.tree__twist{
  width:16px;height:16px;flex:none;display:inline-flex;align-items:center;justify-content:center;
  color:var(--fc-fg3);
  transition:transform var(--fc-dur-tap) var(--fc-ease);
}
.tree__row[aria-expanded="true"] .tree__twist{transform:rotate(90deg)}
.tree__label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* When a row carries a checkbox as well, the row cannot be a <button>: an
   input inside a button is not a control, it is a parse error. Two controls
   instead, which is also the right answer — the box takes the files, the
   row takes you there. */
.tree__go{display:flex;align-items:center;gap:var(--fc-s-2);flex:1;min-width:0;
  min-height:var(--fc-hit);text-align:left;color:inherit;font:inherit;
  border-radius:var(--fc-r-sm);
  transition:color var(--fc-dur-tap) var(--fc-ease)}
.tree__go:hover,.tree__go.is-hover{color:var(--fc-fg)}
.tree__count{margin-left:auto;font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;
  font-size:var(--fc-fs-2);color:var(--fc-fg3);flex:none}

/* ═════ TABLE — scrolls inside itself, never sideways on the page ════════ */
.table-wrap{overflow-x:auto;overscroll-behavior-x:contain;
  border:var(--fc-bw) solid var(--fc-border);border-radius:var(--fc-r-box);background-color:var(--fc-bg)}
.table{width:100%;border-collapse:collapse;font-size:var(--fc-fs-4)}
.table th{
  text-align:left;padding:var(--fc-s-3);
  font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);font-weight:var(--fc-fw-label);
  letter-spacing:.01em;color:var(--fc-fg2);
  border-bottom:var(--fc-bw) solid var(--fc-border);white-space:nowrap;background-color:var(--fc-bg);
}
.table td{padding:var(--fc-s-3);border-bottom:var(--fc-bw) solid var(--fc-border);vertical-align:middle}
.table tr:last-child td{border-bottom:0}
.table tbody tr{transition:background-color var(--fc-dur-tap) var(--fc-ease)}
.table tbody tr:hover td,.table tbody tr.is-hover td{background-color:var(--fc-bg2)}
.table tbody tr[aria-selected="true"] td{background-color:var(--fc-ac-wash)}
.table tbody tr.is-dropped td{background-color:var(--fc-rj-wash);color:var(--fc-fg2)}
.table tbody tr.is-disabled td{opacity:.45}
.table .num{text-align:right}
.table__sort{display:inline-flex;align-items:center;gap:var(--fc-s-1);color:inherit;
  font:inherit;letter-spacing:inherit;text-transform:inherit}
.table__sort:hover,.table__sort.is-hover{color:var(--fc-fg)}

/* ═════ MODAL · SHEET ════════════════════════════════════════════════════
   A SHEET IS THREE ROWS, AND THE BOTTOM ONE NEVER MOVES.

   It used to be one box with `overflow:auto`, so at a 900px window the
   whole sheet scrolled as one block and the button that finishes the job
   sat below the fold. macOS hides its scrollbar until you have already
   scrolled, so what a person saw was a sheet with no button and no reason
   to think there was more. On export that hid 317px, including the last
   question and the entire footer.

   Now: the head holds still, the body is the only thing that scrolls, and
   the foot is pinned to the bottom of the sheet with a hairline above it.
   The primary button is on screen at every window height.

   The body also carries the cue that there is more. Two cover layers
   attached `local` sit on the content and two hairline gradients attached
   `scroll` sit on the box, so a shadow shows at exactly the edge that has
   something behind it and disappears when that edge is reached. Nothing
   animates and no script is involved.
   ═══════════════════════════════════════════════════════════════════════ */
.backdrop{
  position:fixed;inset:0;z-index:80;background:rgba(0,0,0,.44);
  animation:fc-fade var(--fc-dur) var(--fc-ease-out) both;
}
.modal{
  position:fixed;z-index:81;left:50%;top:50%;
  width:min(560px,calc(100vw - var(--fc-s-6)));max-height:calc(100dvh - var(--fc-s-7));
  display:grid;grid-template-rows:auto minmax(0,1fr) auto;overflow:hidden;
  transform:translate(-50%,-50%);
  background-color:var(--fc-bg);color:var(--fc-fg);
  border-radius:var(--fc-r-panel);
  box-shadow:var(--fc-sh-pop);
  animation:fc-rise var(--fc-dur-slow) var(--fc-ease-out) both;
}
/* Wider than the standard sheet, for a list read in three seconds rather
   than a question answered one field at a time. */
.modal--wide{width:min(840px,calc(100vw - var(--fc-s-6)))}

.modal__head{display:grid;gap:var(--fc-s-2);padding:var(--fc-s-5) var(--fc-s-5) var(--fc-s-3)}
.modal__scroll{
  min-height:0;overflow-y:auto;overscroll-behavior:contain;
  padding:var(--fc-s-2) var(--fc-s-5) var(--fc-s-5);
  background-color:var(--fc-bg);
  background-image:
    linear-gradient(var(--fc-bg) 40%,rgba(0,0,0,0)),
    linear-gradient(rgba(0,0,0,0),var(--fc-bg) 60%),
    linear-gradient(var(--fc-border),rgba(0,0,0,0)),
    linear-gradient(rgba(0,0,0,0),var(--fc-border));
  background-position:top,bottom,top,bottom;
  background-size:100% var(--fc-s-5),100% var(--fc-s-5),100% var(--fc-s-3),100% var(--fc-s-3);
  background-repeat:no-repeat;
  background-attachment:local,local,scroll,scroll;
}
.modal__foot{
  display:flex;align-items:center;flex-wrap:wrap;gap:var(--fc-s-2);
  justify-content:flex-end;
  padding:var(--fc-s-3) var(--fc-s-5);
  border-top:var(--fc-bw) solid var(--fc-border);
  background-color:var(--fc-bg);
}
/* The line that says why, on the left, taking whatever room the buttons
   leave. It wraps to its own row before it can push a button off the edge. */
.modal__foot .sp{flex:1 1 var(--fc-s-9);min-width:0}
.modal__foot p{margin:0;font-size:var(--fc-fs-3);color:var(--fc-fg2)}

.modal__title{font-size:var(--fc-fs-7);line-height:var(--fc-lh-head)}
.modal__body{color:var(--fc-fg2);text-wrap:pretty}
.modal__end{display:flex;gap:var(--fc-s-2);justify-content:flex-end;flex-wrap:wrap}
.modal.is-out,.backdrop.is-out{animation:fc-sink var(--fc-dur-out) var(--fc-ease-in) both}

@keyframes fc-fade{from{opacity:0}to{opacity:1}}
@keyframes fc-rise{from{opacity:0;transform:translate(-50%,calc(-50% + 8px)) scale(.99)}
                   to{opacity:1;transform:translate(-50%,-50%) scale(1)}}
@keyframes fc-sink{from{opacity:1}to{opacity:0;transform:translate(-50%,calc(-50% + 4px))}}

/* A sheet's body is a stack of labelled answers, one gap, so nothing
   drifts. One section in it is the question, and it is the only thing at
   the heading step: its own room, and a rule under it. Size and space,
   never weight, never colour, never a box. */
.sheet{display:grid;gap:var(--fc-s-5)}
.sheet section{display:grid;gap:var(--fc-s-2)}
.sheet section.sheet__q{gap:var(--fc-s-3);padding-bottom:var(--fc-s-5);
  border-bottom:var(--fc-bw) solid var(--fc-border)}
.sheet .sheet__q h3{font-size:var(--fc-fs-6)}
/* A section of a sheet is a grid, and a grid item stretches, so a two-option
   segmented control was rendering 510px wide inside a 560px sheet. It takes
   its content width, like every other segment in the kit. */
.sheet section > .seg{justify-self:start}

/* ═════ WELL — a block of a sheet that states rather than asks ═══════════
   It takes the page ground under the sheet's lifted white, and a hairline,
   at the one radius this system has.
   ═══════════════════════════════════════════════════════════════════════ */
.well{
  display:grid;gap:var(--fc-s-2);
  padding:var(--fc-s-4);
  background-color:var(--fc-canvas);
  border:var(--fc-bw) solid var(--fc-border);border-radius:var(--fc-r-box);
}
.well--tight{padding:var(--fc-s-3)}
.well p{margin:0}

/* ═════ PATH ROW — a path, and the control that changes it ═══════════════
   The path takes the room that is left, so a long one shortens instead of
   pushing the button off the sheet.
   ═══════════════════════════════════════════════════════════════════════ */
.pathrow{display:flex;align-items:center;gap:var(--fc-s-2);flex-wrap:wrap}
.pathrow .input{flex:1 1 var(--fc-s-9);min-width:0}

/* ═════ FACT LIST — a summing-up reads as facts, never as a paragraph ════ */
.facts{display:grid;gap:0}
.fact{display:flex;align-items:center;gap:var(--fc-s-3);
  padding:var(--fc-s-3) 0;border-bottom:var(--fc-bw) solid var(--fc-border)}
.fact:last-child{border-bottom:0}
.fact .nm{flex:1;min-width:0;color:var(--fc-fg2)}
.fact .num{font-size:var(--fc-fs-5)}
.fact__say{flex:none;color:var(--fc-fg);text-align:right}

/* ═════ THE MOVE WARNING — never hue alone ═══════════════════════════════
   A dashed edge, a cross and a plain sentence carry it as well as the
   vermilion does, so it still reads on a greyscale screen.
   ═══════════════════════════════════════════════════════════════════════ */
.gone{display:flex;gap:var(--fc-s-3);align-items:flex-start;
  padding:var(--fc-s-3);border:var(--fc-bw) dashed var(--fc-rj-line);
  border-radius:var(--fc-r-box);background-color:var(--fc-rj-wash);color:var(--fc-rj-ink)}
.gone__x{flex:none;width:20px;height:20px;display:grid;place-items:center;
  border-radius:var(--fc-r-sm);background-color:var(--fc-rj);color:var(--fc-rj-on);
  font-size:var(--fc-fs-2);line-height:1}
.gone p{margin:0;color:inherit;max-width:none}

/* ═════ MENU — the avatar drop, the right click ══════════════════════════ */
.menu{
  min-width:200px;padding:var(--fc-s-1);
  background-color:var(--fc-glass);backdrop-filter:var(--fc-glass-fx);
  border:var(--fc-bw) solid var(--fc-border);border-radius:var(--fc-r-box);
  box-shadow:var(--fc-sh-soft);
  animation:fc-pop var(--fc-dur) var(--fc-ease-out) both;
}
.menu__item{
  display:flex;align-items:center;gap:var(--fc-s-2);width:100%;
  min-height:var(--fc-hit);padding:0 var(--fc-s-3);
  border-radius:var(--fc-r-sm);font-size:var(--fc-fs-4);color:var(--fc-fg);text-align:left;
  transition:background-color var(--fc-dur-tap) var(--fc-ease),
             color var(--fc-dur-tap) var(--fc-ease),
             opacity var(--fc-dur-tap) var(--fc-ease);
}
.menu__item:hover,.menu__item.is-hover{background-color:var(--fc-bg2)}
.menu__item:active,.menu__item.is-active{background-color:var(--fc-bg3)}
.menu__item[aria-checked="true"]{color:var(--fc-ac-ink)}
.menu__item:disabled,.menu__item.is-disabled{opacity:.45;pointer-events:none}
.menu__item.is-loading{opacity:.6;pointer-events:none}
.menu__item--danger{color:var(--fc-rj-ink)}
.menu__item--danger:hover,.menu__item--danger.is-hover{background-color:var(--fc-rj-wash)}
.menu__key{margin-left:auto;font-family:var(--fc-font-mono);font-size:var(--fc-fs-2);color:var(--fc-fg3)}
.menu__sep{height:1px;margin:var(--fc-s-1) 0;background-color:var(--fc-border)}
@keyframes fc-pop{from{opacity:0;transform:translateY(-4px) scale(.99)}to{opacity:1;transform:none}}

/* ═════ TOOLTIP — ink block, 12px, one line ══════════════════════════════ */
.tip{position:relative;display:inline-flex}
.tip__bubble{
  position:absolute;bottom:calc(100% + 8px);left:50%;
  transform:translateX(-50%) translateY(4px);
  padding:var(--fc-s-1) var(--fc-s-2);
  background-color:var(--fc-fg);color:var(--fc-canvas);
  font-size:var(--fc-fs-2);line-height:18px;white-space:nowrap;
  border-radius:var(--fc-r-box);box-shadow:var(--fc-sh-soft);
  opacity:0;pointer-events:none;z-index:70;
  transition:opacity var(--fc-dur-out) var(--fc-ease-in),
             transform var(--fc-dur-out) var(--fc-ease-in);
}
.tip__bubble::after{
  content:"";position:absolute;top:100%;left:50%;width:6px;height:6px;
  background-color:var(--fc-fg);transform:translate(-50%,-50%) rotate(45deg);
}
.tip:hover .tip__bubble,.tip.is-hover .tip__bubble,.tip:focus-within .tip__bubble,.tip.is-on .tip__bubble{
  opacity:1;transform:translateX(-50%) translateY(0);
  transition:opacity var(--fc-dur) var(--fc-ease-out),transform var(--fc-dur) var(--fc-ease-out);
}
.tip.is-disabled .tip__bubble{opacity:0}

/* ═════ TABS · SEGMENTED — chrome, so greyscale ══════════════════════════ */
/* THE RING GOES INSIDE ON BOTH OF THESE. Both scroll sideways, and a box
   that scrolls on one axis clips the other, which was slicing the top and
   bottom off a 2px ring sitting 2px outside its control and leaving two
   vertical strokes. The tab cannot solve it with padding, because its
   underline has to stay welded to the container rule below it, so the ring
   moves inward instead. Same treatment on the segment, so the two agree. */
.tabs{display:flex;gap:var(--fc-s-1);border-bottom:var(--fc-bw) solid var(--fc-border);overflow-x:auto}
.tab{
  min-height:var(--fc-hit);padding:0 var(--fc-s-3);white-space:nowrap;
  color:var(--fc-fg2);font-size:var(--fc-fs-4);
  box-shadow:inset 0 -1px 0 transparent;
  transition:color var(--fc-dur-tap) var(--fc-ease),
             box-shadow var(--fc-dur-tap) var(--fc-ease),
             opacity var(--fc-dur-tap) var(--fc-ease);
}
.tab:focus-visible,.tab.is-focus{outline-offset:calc(var(--fc-bw2) * -1)}
.tab:hover,.tab.is-hover{color:var(--fc-fg)}
.tab:active,.tab.is-active{color:var(--fc-fg2)}
.tab[aria-selected="true"]{color:var(--fc-fg);font-weight:var(--fc-fw-label);
  box-shadow:inset 0 -2px 0 var(--fc-fg)}
.tab:disabled{opacity:.45;pointer-events:none}
.tab.is-loading{opacity:.6;pointer-events:none}

/* flex:none matters, and the reason generalises.

   A box that scrolls on an axis has an AUTOMATIC MINIMUM SIZE OF ZERO. In
   an ordinary flex item that floor is min-content, which is why a button
   with nowrap text cannot be crushed. A scroll container has no such floor,
   so inside a flex row it absorbs the whole row's deficit: measured at
   375px this rendered 4px wide against 140px of content, on ten screens,
   with Date taken and Name still inside it and scrolled out of sight.

   THE RULE: any element that scrolls on one axis and sits in a flex row
   must say what it does under pressure — flex:none to hold its content, or
   an explicit min-width if it is meant to give room up. Never neither.
   The others in this repo that answer to it: .table-wrap sits in a grid
   cell, .strip is a grid row, and the stage's .folders takes a min-width
   in proto.css. */
.seg{display:inline-flex;flex:none;padding:var(--fc-s-0);background-color:var(--fc-bg3);
  border-radius:var(--fc-r-pill);gap:var(--fc-s-0);
  max-width:100%;overflow-x:auto;overscroll-behavior-x:contain}
/* A segmented control with more options than a phone is wide scrolls inside
   itself. Nothing in this kit is allowed to push the page sideways. */
.seg__btn{
  min-height:var(--fc-ctrl-h-sm);padding:0 var(--fc-s-3);border-radius:var(--fc-r-pill);
  font-size:var(--fc-fs-3);color:var(--fc-fg2);
  transition:background-color var(--fc-dur-tap) var(--fc-ease),
             color var(--fc-dur-tap) var(--fc-ease),
             transform var(--fc-dur-tap) var(--fc-ease),
             opacity var(--fc-dur-tap) var(--fc-ease);
}
.seg__btn:focus-visible,.seg__btn.is-focus{outline-offset:calc(var(--fc-bw2) * -1)}
.seg__btn:hover,.seg__btn.is-hover{color:var(--fc-fg)}
.seg__btn:active,.seg__btn.is-active{transform:scale(var(--fc-press))}
.seg__btn[aria-pressed="true"],.seg__btn.is-on{
  background-color:var(--fc-bg);color:var(--fc-fg);font-weight:var(--fc-fw-label);box-shadow:var(--fc-sh-1);
}
.seg__btn:disabled{opacity:.45;pointer-events:none}
.seg__btn.is-loading{opacity:.6;pointer-events:none}

/* ═════ PROGRESS — ink, because a sort in flight is chrome ═══════════════ */
.bar{height:6px;border-radius:var(--fc-r-pill);background-color:var(--fc-bg3);overflow:hidden}
.bar__fill{height:100%;background-color:var(--fc-fg);border-radius:inherit;
  transform-origin:left;transition:transform var(--fc-dur) var(--fc-ease)}
.bar--kept .bar__fill{background-color:var(--fc-ac)}
.bar--dropped .bar__fill{background-color:var(--fc-rj)}
.bar.is-disabled{opacity:.45}
/* Indeterminate: a shuttle, moved by transform only. */
.bar--wait .bar__fill{width:36%;animation:fc-shuttle 1.2s var(--fc-ease) infinite}
@keyframes fc-shuttle{0%{transform:translateX(-100%)}100%{transform:translateX(320%)}}

/* ═════ SKELETON — shaped like the thing that is coming ══════════════════
   The sweep is a translated overlay, not an animated background-position,
   so the only property in flight is transform.
   ═══════════════════════════════════════════════════════════════════════ */
.skeleton{position:relative;overflow:hidden;background-color:var(--fc-bg2);border-radius:var(--fc-r-sm)}
.skeleton::after{
  content:"";position:absolute;inset:0;
  background:linear-gradient(90deg,transparent,var(--fc-bg3),transparent);
  transform:translateX(-100%);
  animation:fc-sweep 1.4s var(--fc-ease) infinite;
}
.skeleton--text{height:12px;margin:var(--fc-s-1) 0}
.skeleton--title{height:18px;width:52%}
.skeleton--thumb{aspect-ratio:3/2;width:100%;border-radius:var(--fc-r-sm)}
.skeleton--row{height:var(--fc-hit);border-radius:var(--fc-radius)}
@keyframes fc-sweep{to{transform:translateX(100%)}}
/* base.css honours reduced motion for the rest of the kit, and the app does
   not load base.css. This is the one animation here that starts on its own and
   never stops, so its guard lives with the component instead of with whichever
   stylesheet happens to be loaded. */
@media (prefers-reduced-motion:reduce){ .skeleton::after{animation:none} }

/* ═════ TOAST ════════════════════════════════════════════════════════════
   The whole stroke carries the status, never a bar down one side. A partial
   edge reads as a rendering fault rather than a signal.
   ═══════════════════════════════════════════════════════════════════════ */
.toast{
  display:flex;align-items:center;gap:var(--fc-s-3);
  padding:var(--fc-s-3) var(--fc-s-4);max-width:min(420px,calc(100vw - var(--fc-s-6)));
  background-color:var(--fc-glass);backdrop-filter:var(--fc-glass-fx);
  border:var(--fc-bw) solid var(--fc-border2);border-radius:var(--fc-r-box);
  box-shadow:var(--fc-sh-pop);font-size:var(--fc-fs-4);
  animation:fc-rise-sm var(--fc-dur) var(--fc-ease-out) both;
}
.toast--ok{border-color:var(--fc-ok)}
.toast--err{border-color:var(--fc-err)}
.toast--warn{border-color:var(--fc-warn)}
.toast--kept{border-color:var(--fc-ac)}
.toast--dropped{border-color:var(--fc-rj);border-style:dashed}
.toast__end{margin-left:auto;display:flex;gap:var(--fc-s-2);flex:none}
.toast.is-out{animation:fc-fade-out var(--fc-dur-out) var(--fc-ease-in) both}

/* ═════ SPOTLIGHT ═══════════════════════════════════════════════════════
   A hint lit on the control it is about. The hole is a transparent box
   over the control and its shadow is the dimming, so one element paints
   the whole overlay. The overlay ignores the pointer: a drag onto the lit
   control is still a drag. The callout beside it carries one line and a
   way out, and its arrow faces the hole. The dim is the backdrop's own
   value; the accent ring says which control is meant.
   ═══════════════════════════════════════════════════════════════════════ */
.spot{position:fixed;inset:0;pointer-events:none}
.spot__hole{
  position:absolute;border-radius:var(--fc-r-pop);
  box-shadow:0 0 0 9999px rgba(0,0,0,.44),0 0 0 var(--fc-bw2) var(--fc-ac);
}
.spot__call{
  position:absolute;pointer-events:auto;max-width:280px;
  display:flex;align-items:flex-start;gap:var(--fc-s-3);
  padding:var(--fc-s-3) var(--fc-s-3) var(--fc-s-3) var(--fc-s-4);
  background-color:var(--fc-bg);color:var(--fc-fg);
  border:var(--fc-bw) solid var(--fc-border2);border-radius:var(--fc-r-pop);
  box-shadow:var(--fc-sh-pop);font-size:var(--fc-fs-4);line-height:var(--fc-lh);
  animation:fc-rise-sm var(--fc-dur) var(--fc-ease-out) both;
}
.spot__call::before{
  content:'';position:absolute;width:var(--fc-s-3);height:var(--fc-s-3);
  background-color:var(--fc-bg);border:var(--fc-bw) solid var(--fc-border2);
  transform:rotate(45deg);
}
.spot__call[data-side="below"]::before{top:calc(var(--fc-s-3) / -2 - var(--fc-bw));left:var(--spot-ax,var(--fc-s-5));border-right:0;border-bottom:0}
.spot__call[data-side="above"]::before{bottom:calc(var(--fc-s-3) / -2 - var(--fc-bw));left:var(--spot-ax,var(--fc-s-5));border-left:0;border-top:0}
.spot__call[data-side="left"]::before{right:calc(var(--fc-s-3) / -2 - var(--fc-bw));top:var(--spot-ay,var(--fc-s-4));border-left:0;border-bottom:0}
.spot__text{min-width:0;text-wrap:pretty;padding-top:var(--fc-s-1)}
.spot__text kbd{
  font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);font-variant-numeric:tabular-nums;
  padding:var(--fc-s-0) var(--fc-s-2);border-radius:var(--fc-r-sm);
  background-color:var(--fc-bg3);color:var(--fc-fg2);
}
.spot__x{flex:none;margin-left:auto}
.spot.is-out{animation:fc-fade-out var(--fc-dur-out) var(--fc-ease-in) both}
.spot--demo{position:absolute}
@keyframes fc-rise-sm{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
@keyframes fc-fade-out{from{opacity:1}to{opacity:0;transform:translateY(4px)}}

/* ═════ EMPTY STATE ══════════════════════════════════════════════════════
   A real sentence and the action that fills it. Never a blank box.
   ═══════════════════════════════════════════════════════════════════════ */
.empty{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:var(--fc-s-3);padding:var(--fc-s-8) var(--fc-s-5);text-align:center;
}
.empty__icon{width:40px;height:40px;color:var(--fc-fg3);flex:none}
.empty__title{font-size:var(--fc-fs-6);color:var(--fc-fg);line-height:var(--fc-lh-snug)}
.empty__body{font-size:var(--fc-fs-4);color:var(--fc-fg2);max-width:44ch;text-wrap:pretty}
.empty__end{margin-top:var(--fc-s-2);display:flex;gap:var(--fc-s-2);flex-wrap:wrap;justify-content:center}
.empty--err .empty__icon{color:var(--fc-err)}

/* ═════ SPLIT PANE DIVIDER ═══════════════════════════════════════════════
   One hairline you can see, forty pixels you can grab.
   ═══════════════════════════════════════════════════════════════════════ */
.split{display:flex;min-height:0;width:100%}
.split__pane{min-width:0;flex:1;overflow:auto}
.split__grip{
  position:relative;flex:none;width:1px;background-color:var(--fc-border);
  cursor:col-resize;border:0;padding:0;
  transition:background-color var(--fc-dur-tap) var(--fc-ease),opacity var(--fc-dur-tap) var(--fc-ease);
}
.split__grip::before{
  content:"";position:absolute;top:0;bottom:0;left:50%;
  transform:translateX(-50%);width:var(--fc-hit);
}
.split__grip::after{
  content:"";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
  width:3px;height:24px;border-radius:var(--fc-r-pill);background-color:var(--fc-border2);
  opacity:0;transition:opacity var(--fc-dur-tap) var(--fc-ease);
}
.split__grip:hover,.split__grip.is-hover,.split__grip.is-on{background-color:var(--fc-border2)}
.split__grip:hover::after,.split__grip.is-hover::after,.split__grip:focus-visible::after,.split__grip.is-focus::after,.split__grip.is-on::after{opacity:1}
.split__grip:active,.split__grip.is-active{background-color:var(--fc-fg2)}
.split__grip:disabled{opacity:.4;cursor:default;pointer-events:none}
.split--row{flex-direction:column}
.split--row > .split__grip{width:auto;height:1px;cursor:row-resize}
.split--row > .split__grip::before{top:50%;bottom:auto;left:0;right:0;width:auto;
  height:var(--fc-hit);transform:translateY(-50%)}
.split--row > .split__grip::after{width:24px;height:3px}

/* ═════ THUMBNAIL TILE ═══════════════════════════════════════════════════
   The one place the whole system is aimed at. Kept: a solid rail, a flat
   corner mark, a tick, full brightness. Dropped: a dashed rail, a hatched
   veil, a cross, and the picture pulled back to 55%. Greyscale the screen
   and the two are still one glance apart.
   ═══════════════════════════════════════════════════════════════════════ */
.thumb{
  position:relative;display:block;width:100%;padding:0;border:0;
  background-color:var(--fc-bg2);border-radius:var(--fc-r-sm);overflow:hidden;
  transition:transform var(--fc-dur-tap) var(--fc-ease),opacity var(--fc-dur-tap) var(--fc-ease);
}
.thumb__frame{display:block;position:relative;aspect-ratio:3/2;overflow:hidden;background-color:var(--fc-photo)}
.thumb__img{width:100%;height:100%;object-fit:cover;display:block;
  transition:opacity var(--fc-dur) var(--fc-ease)}
.thumb__veil{position:absolute;inset:0;pointer-events:none;
  transition:opacity var(--fc-dur-tap) var(--fc-ease);opacity:0}
.thumb__rail{position:absolute;inset:0;pointer-events:none;border-radius:var(--fc-r-sm);
  box-shadow:inset 0 0 0 0 transparent;
  transition:box-shadow var(--fc-dur-tap) var(--fc-ease)}
.thumb__mark{
  position:absolute;top:var(--fc-s-2);left:var(--fc-s-2);
  width:20px;height:20px;display:none;align-items:center;justify-content:center;
  border-radius:var(--fc-r-sm);font-size:var(--fc-fs-2);line-height:1;
}
.thumb__cap{
  display:flex;align-items:center;gap:var(--fc-s-2);
  padding:var(--fc-s-2) var(--fc-s-2) 0;
  font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;
  font-size:var(--fc-fs-2);color:var(--fc-fg2);
}
.thumb:hover,.thumb.is-hover{transform:scale(1.01)}
.thumb:active,.thumb.is-active{transform:scale(var(--fc-press))}
.thumb:disabled,.thumb.is-disabled{opacity:.45;pointer-events:none;transform:none}
.thumb.is-loading .thumb__img{opacity:0}

.thumb.is-kept .thumb__rail{box-shadow:inset 0 0 0 2px var(--fc-ac)}
.thumb.is-kept .thumb__mark{display:flex;background-color:var(--fc-ac);color:var(--fc-ac-on)}
.thumb.is-kept .thumb__cap{color:var(--fc-ac-ink)}

.thumb.is-dropped .thumb__img{opacity:.55}
.thumb.is-dropped .thumb__veil{opacity:1;background-color:var(--fc-rj-wash)}
.thumb.is-dropped .thumb__rail{box-shadow:inset 0 0 0 2px var(--fc-rj)}
.thumb.is-dropped .thumb__mark{display:flex;background-color:var(--fc-rj);color:var(--fc-rj-on)}
.thumb.is-dropped .thumb__cap{color:var(--fc-rj-ink);text-decoration:line-through;
  text-decoration-thickness:1px}
/* The dashed rail is the shape half of the signal. A dashed 2px inset is
   not expressible as a box-shadow, so it is drawn as a real border. */
.thumb.is-dropped .thumb__veil{border:var(--fc-bw2) dashed var(--fc-rj);border-radius:var(--fc-r-sm)}

.thumb-grid{
  display:grid;gap:var(--fc-s-3);
  grid-template-columns:repeat(auto-fill,minmax(150px,1fr));
}

/* ═════ VISUALLY HIDDEN ══════════════════════════════════════════════════
   Read aloud, never drawn. For the heading a screen needs to have and a
   sighted person does not need to see — an app shell's <h1>, a landmark
   label. Not display:none, which removes it from the reading order too. */
.sr-only{
  position:absolute;width:1px;height:1px;
  overflow:hidden;clip-path:inset(50%);white-space:nowrap;
}

/* ═════ KEY CAP ══════════════════════════════════════════════════════════ */
.kbd{
  display:inline-flex;align-items:center;justify-content:center;
  min-width:22px;height:22px;padding:0 var(--fc-s-2);
  font-family:var(--fc-font-mono);font-size:var(--fc-fs-2);color:var(--fc-fg2);
  background-color:var(--fc-bg3);border:var(--fc-bw) solid var(--fc-border);border-radius:var(--fc-r-sm);
}

/* ═════ STAGGERED REVEAL — 40ms per item, one orchestrated load ══════════ */
.stagger > *{animation:fc-rise-sm var(--fc-dur) var(--fc-ease-out) both}
.stagger > *:nth-child(1){animation-delay:0ms}
.stagger > *:nth-child(2){animation-delay:calc(var(--fc-stagger) * 1)}
.stagger > *:nth-child(3){animation-delay:calc(var(--fc-stagger) * 2)}
.stagger > *:nth-child(4){animation-delay:calc(var(--fc-stagger) * 3)}
.stagger > *:nth-child(5){animation-delay:calc(var(--fc-stagger) * 4)}
.stagger > *:nth-child(6){animation-delay:calc(var(--fc-stagger) * 5)}
.stagger > *:nth-child(n+7){animation-delay:calc(var(--fc-stagger) * 6)}

/* ═════ TILE — one file in the grid ══════════════════════════════════════
   .thumb above is a bare frame: a picture in a strip, or in a mock of the
   app. .tile is the whole object the grid is made of — the frame, the
   name under it, the tick, where the file is going, and what was found in
   it — and it is the shape this entire product is aimed at.

   A grid, not a block. A <button> does not count a block child's height as
   its own, so laid out as a block the filename fell outside the tile and
   the next row landed on top of it.
   ═══════════════════════════════════════════════════════════════════════ */
.tile{
  position:relative;display:grid;grid-template-rows:auto auto;
  align-content:start;gap:var(--fc-s-1);
  border:0;padding:0;background:none;text-align:left;color:var(--fc-fg);cursor:pointer;
  transition:transform var(--fc-dur-tap) var(--fc-ease),
             opacity var(--fc-dur-tap) var(--fc-ease);
}
.tile__img{position:relative;aspect-ratio:3/2;overflow:hidden;
  border-radius:var(--fc-r-sm);background-color:var(--fc-bg3);
  outline:var(--fc-bw) solid var(--fc-img-edge);outline-offset:calc(var(--fc-bw) * -1);
  transition:outline-color var(--fc-dur-tap) var(--fc-ease)}
.tile__img img{width:100%;height:100%;object-fit:cover;display:block;
  transition:opacity var(--fc-dur) var(--fc-ease)}
.tile__name{display:block;
  font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);color:var(--fc-fg2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tile__meta{display:block;
  font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;
  font-size:var(--fc-fs-1);line-height:var(--fc-lh);color:var(--fc-fg3);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* SIX STATES. Focus-visible is base.css's ring, which lands on the tile
   itself because the tile is the control. */
.tile:hover .tile__img,.tile.is-hover .tile__img{outline-color:var(--fc-border2)}
.tile:active,.tile.is-active{transform:scale(var(--fc-press))}
.tile:disabled,.tile.is-disabled{opacity:.45;pointer-events:none;transform:none}
.tile.is-loading{pointer-events:none;cursor:default}

/* The tick is always drawn. It used to be a 20px circle that appeared on
   hover, which made the most repeated action in the product the least
   visible one. It is a mark, not a control: the tile is the control, and
   the tile is already well past 40px, so there is no second hit area. */
.tile__check{position:absolute;top:var(--fc-s-2);left:var(--fc-s-2);
  width:20px;height:20px;border-radius:var(--fc-r-sm);
  background-color:var(--fc-bg);border:var(--fc-bw) solid var(--fc-border2);
  display:grid;place-items:center;color:transparent;
  font-size:var(--fc-fs-2);line-height:1;
  transition:background-color var(--fc-dur-tap) var(--fc-ease),
             border-color var(--fc-dur-tap) var(--fc-ease),
             color var(--fc-dur-tap) var(--fc-ease)}

/* SELECTED IS INK. KEPT IS ULTRAMARINE. They used to be the same 2px
   ultramarine ring, told apart only by whether a 20px corner mark was
   filled, so a kept file and a selected file were one state at a glance.
   "You are here" is ink everywhere else in this kit, and it is ink here. */
.tile[aria-selected="true"] .tile__check,
.tile.is-on .tile__check{background-color:var(--fc-fg);border-color:var(--fc-fg);color:var(--fc-canvas)}
.tile[aria-selected="true"] .tile__img,
.tile.is-on .tile__img{outline:var(--fc-bw2) solid var(--fc-ring);outline-offset:var(--fc-bw)}

/* Where a file is going, written on the file. Sorting used to make a tile
   vanish, which read as the app eating the photograph. The text is a child
   span, because text-overflow only works on a block container and this box
   is a flex one, so the ellipsis was never drawn. */
.tile__to{position:absolute;left:var(--fc-s-2);bottom:var(--fc-s-2);
  display:inline-flex;align-items:center;gap:var(--fc-s-1);
  max-width:calc(100% - var(--fc-s-4));
  padding:var(--fc-s-0) var(--fc-s-2);border-radius:var(--fc-r-sm);
  background-color:var(--fc-bg);color:var(--fc-fg);
  font-size:var(--fc-fs-1);line-height:1.4;
  box-shadow:var(--fc-sh-1)}
.tile__to .k{flex:none;font-family:var(--fc-font-mono);color:var(--fc-fg3)}
.tile__to .nm{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* A FILE FACT, top right: what the file is. A FINDING, dashed, is what was
   noticed about it. Two kinds of information, two costumes, so "MP4 · 0:06"
   never reads as something the app is telling you to look at. */
.tile__badge{position:absolute;top:var(--fc-s-2);right:var(--fc-s-2);
  padding:var(--fc-s-0) var(--fc-s-1);border-radius:var(--fc-r-sm);
  background-color:var(--fc-bg);color:var(--fc-fg2);box-shadow:var(--fc-sh-1);
  font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;
  font-size:var(--fc-fs-1);line-height:1.5}
.tile__badge--found{border:var(--fc-bw) dashed var(--fc-border2);color:var(--fc-fg)}

/* The way to look closer, drawn on the frame and visible at rest. */
.tile__open{position:absolute;right:var(--fc-s-2);bottom:var(--fc-s-2);
  padding:var(--fc-s-0) var(--fc-s-2);border-radius:var(--fc-r-sm);
  background-color:var(--fc-bg);color:var(--fc-fg);box-shadow:var(--fc-sh-1);
  font-size:var(--fc-fs-1);line-height:1.5}

.tile--kept .tile__img{outline:var(--fc-bw2) solid var(--fc-ac);outline-offset:var(--fc-bw)}
.tile--kept .tile__to{color:var(--fc-ac-ink)}

/* DROPPED NEVER RELIES ON HUE: hatched and dimmed as well as vermilion.
   The dimming belongs to the photograph, not to the box around it. On the
   whole box it also faded the tick and the folder label, which took the
   label to 3.68:1 and the tick's edge to 1.87:1. The picture reads as
   dropped, the controls stay legible. */
.tile--dropped .tile__img img{opacity:.5}
.tile--dropped .tile__img::after{content:'';position:absolute;inset:0;
  background-image:repeating-linear-gradient(45deg,transparent 0 5px,var(--fc-rj-line) 5px 6px)}
.tile--dropped .tile__to{color:var(--fc-rj-ink)}

/* Loading: a skeleton shaped like the tile that is coming. */
.tile--wait{cursor:default}
.tile--wait .skeleton--text{width:64%;height:var(--fc-fs-3);margin:var(--fc-s-0) 0}

/* ═════ QUEUE — the folders you are sorting into ═════════════════════════
   Numbered, because the number is the keyboard shortcut, and the count is
   the only proof a keystroke did anything.

   A row is a <button>. It was a <div> with cursor:pointer, so tabbing
   through the grid gave twenty-six stops and not one of them was a folder:
   the entire right-hand panel, which is the point of the product, was
   mouse only.
   ═══════════════════════════════════════════════════════════════════════ */
.queue{display:flex;flex-direction:column;gap:var(--fc-s-1)}
.queue__row{
  display:flex;align-items:center;gap:var(--fc-s-2);width:100%;
  min-height:var(--fc-hit);padding:0 var(--fc-s-2);
  border-radius:var(--fc-r-sm);color:var(--fc-fg);text-align:left;
  font-size:var(--fc-fs-4);
  transition:background-color var(--fc-dur-tap) var(--fc-ease),
             color var(--fc-dur-tap) var(--fc-ease),
             opacity var(--fc-dur-tap) var(--fc-ease);
}
.queue__row:hover,.queue__row.is-hover{background-color:var(--fc-bg2)}
.queue__row:active,.queue__row.is-active{background-color:var(--fc-bg3)}
.queue__row[aria-current="true"],.queue__row.is-on{
  background-color:var(--fc-bg3);box-shadow:inset 2px 0 0 var(--fc-fg)}
.queue__row:disabled,.queue__row.is-disabled{opacity:.45;pointer-events:none}
/* --fc-fg3 measures 3.81:1 on --fc-bg3, and --fc-bg3 is exactly what the row
   fills with when it is the current one. tokens.css says nothing under 4.5
   letters anything in this kit, so the key promotes with its row. */
.queue__row[aria-current="true"] .queue__key,
.queue__row.is-on .queue__key{color:var(--fc-fg2)}
.queue__row.is-loading{opacity:.6;pointer-events:none}
.queue__key{width:18px;flex:none;text-align:center;
  font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);color:var(--fc-fg3)}
.queue__name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.queue__n{flex:none;font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;
  font-size:var(--fc-fs-2);color:var(--fc-fg2)}
/* The row a dragged file is over. Ultramarine, because it means kept. */
.queue__row--drop{background-color:var(--fc-ac-wash);outline:var(--fc-bw) dashed var(--fc-ac-line)}
/* The dropped folder. Vermilion on the key only, and it is also the one
   row with a dashed edge, so it still reads with the colour taken away. */
.queue__row--dropped .queue__key{color:var(--fc-rj-ink)}

/* ═════ FOUND — what a tool noticed, said once, above the work ═══════════
   Duplicates, look-alikes and soft frames used to be three modals that
   stopped the work to tell you a number. This is the line that replaced
   them, and it is a statement with actions, never a dialog.
   ═══════════════════════════════════════════════════════════════════════ */
.found{display:flex;align-items:center;gap:var(--fc-s-3);flex-wrap:wrap;
  padding:var(--fc-s-3)}
.found p{flex:1;min-width:var(--fc-s-9);margin:0;font-size:var(--fc-fs-3);color:var(--fc-fg2)}
.found strong{font-weight:var(--fc-fw);color:var(--fc-fg)}
.found small{display:block;color:var(--fc-fg3);font-size:var(--fc-fs-2)}
.found__acts{display:flex;gap:var(--fc-s-2);flex-wrap:wrap}
.found--stack{display:grid;gap:var(--fc-s-3)}
.found--stack .found__say{display:grid;grid-template-columns:minmax(0,1fr) auto;
  gap:var(--fc-s-3);align-items:start}
@media (max-width:700px){
  .found--stack .found__say{grid-template-columns:minmax(0,1fr)}
}

/* The running score under a finding. Mono, tabular, sentence case. */
.tally{display:flex;align-items:center;flex-wrap:wrap;gap:var(--fc-s-1) var(--fc-s-3);
  padding-top:var(--fc-s-3);border-top:var(--fc-bw) solid var(--fc-border);
  font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;
  font-size:var(--fc-fs-1);letter-spacing:.01em;
  color:var(--fc-fg3)}
.tally b{font-weight:var(--fc-fw);color:var(--fc-fg)}

/* ═════ BULK — the answer offered for a whole group at once ══════════════
   A sentence and the two moves worth a single press. Never a dialog.
   ═══════════════════════════════════════════════════════════════════════ */
.bulk{display:flex;align-items:center;gap:var(--fc-s-2);flex-wrap:wrap;
  font-size:var(--fc-fs-3);color:var(--fc-fg2)}
.bulk p{margin:0;text-wrap:pretty}
.bulk--stack{display:grid;gap:var(--fc-s-2)}
.bulk__acts{display:flex;gap:var(--fc-s-2);flex-wrap:wrap}

/* ═════ LICENCE KEY ══════════════════════════════════════════════════════
   A key is read back one character at a time, so it is mono, tabular, set
   large and allowed to break anywhere. Two shapes for one thing: alone on
   a line, and in a well with the control that copies it.
   ═══════════════════════════════════════════════════════════════════════ */
.keycode{
  font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;
  font-size:var(--fc-fs-8);line-height:var(--fc-lh-snug);
  letter-spacing:var(--fc-ls-caps);overflow-wrap:anywhere;color:var(--fc-fg);
}
.keycode--boxed{
  display:flex;align-items:center;gap:var(--fc-s-3);flex-wrap:wrap;
  padding:var(--fc-s-3);background-color:var(--fc-bg3);border-radius:var(--fc-r-sm);
  font-size:var(--fc-fs-5);
}
.keycode--boxed code{min-width:0;font-family:inherit;font-size:inherit;
  letter-spacing:inherit;overflow-wrap:anywhere}
