/* Shared controls and states for every robouse.ai page (after /tokens.css, /chrome.css, /prose.css, /surface.css):
   focus ring, selection, scrollbars, buttons, text buttons, inputs, checkboxes, the listbox that replaces every native
   <select> (/ui.js), skeletons, empty states, media. Page stylesheets add layout only. */

/* focus: one quiet ring in the accent, only for keyboard focus */
:focus{outline:none}
:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:var(--r-xs)}
input:focus-visible,textarea:focus-visible{outline-offset:0}

/* visually hidden, still read by screen readers */
.vh{position:absolute !important;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* a theme switch lands in one frame (theme.js adds .theme-swap for two frames) */
html.theme-swap *,html.theme-swap *::before,html.theme-swap *::after{transition:none !important}

/* selection and scrollbars */
::selection{background:var(--sel-bg);color:inherit}
html{scrollbar-color:var(--line-strong) transparent}
*{scrollbar-width:thin;scrollbar-color:var(--line-strong) transparent}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:var(--line-strong);border:3px solid transparent;border-radius:var(--r-pill);background-clip:padding-box}
::-webkit-scrollbar-thumb:hover{background-color:var(--faint)}
::-webkit-scrollbar-corner{background:transparent}

/* text: no orphans, tabular numbers where numbers align */
p,li,figcaption,dd{text-wrap:pretty}
h1,h2,h3,h4{text-wrap:balance}
.num,time,output{font-variant-numeric:tabular-nums}
html{-webkit-tap-highlight-color:transparent}

/* buttons: .btn (solid), .btn.quiet (hairline), .tbtn (text) */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-8);height:var(--ctl-lg);padding:0 var(--sp-16);border:0;border-radius:var(--ctl-r);
  background:var(--btn);color:var(--btn-ink);font:500 var(--fs-small)/1 var(--sans);white-space:nowrap;text-decoration:none;cursor:pointer;
  transition:background var(--t-fast),color var(--t-fast),transform var(--t-fast),box-shadow var(--t-fast)}
.btn:hover{background:var(--btn-hover)}
.btn:active{transform:translateY(1px)}
.btn.quiet{background:transparent;color:var(--ink);box-shadow:inset 0 0 0 1px var(--line-strong)}
.btn.quiet:hover{background:var(--hover);box-shadow:inset 0 0 0 1px var(--faint)}
.btn.sm{height:var(--ctl);padding:0 var(--sp-12);font-size:var(--fs-meta)}
.btn:disabled,.btn[aria-disabled="true"]{opacity:.4;cursor:default;transform:none}
.btn[aria-busy="true"]{color:transparent;position:relative;pointer-events:none}
.btn[aria-busy="true"]::after{content:"";position:absolute;width:14px;height:14px;border-radius:50%;border:1.5px solid var(--btn-ink);border-right-color:transparent;animation:ui-spin .7s linear infinite}
@keyframes ui-spin{to{transform:rotate(1turn)}}
.tbtn{display:inline-flex;align-items:center;gap:var(--sp-6);min-height:var(--ctl-sm);padding:0 var(--sp-6);margin:0 calc(-1 * var(--sp-6));border:0;border-radius:var(--r-sm);background:none;
  font:400 var(--fs-meta)/1 var(--sans);color:var(--muted);cursor:pointer;white-space:nowrap;text-decoration:none;transition:color var(--t-fast),background var(--t-fast)}
.tbtn:hover{color:var(--ink);background:var(--hover)}
.tbtn:active{background:var(--press)}
.tbtn[aria-pressed="true"]{color:var(--ink)}
.tbtn:disabled{color:var(--ghost);background:none;cursor:default}
.tbtn.done{color:var(--ok)}
[data-hold]{justify-content:center}

/* inputs: one field spec (height from the control scale, hairline, --ctl-r radius, accent border and halo on focus).
   Text sits on line-height:normal so descenders never clip; on touch screens fields are 44px tall and 16px text
   (iOS does not zoom into a field with 16px text). */
.input,input[type=search],input[type=text],input[type=number]{height:var(--ctl);padding:0 var(--sp-12);border:var(--border);border-radius:var(--ctl-r);background:var(--panel);
  color:var(--ink);font:400 var(--fs-small)/normal var(--sans);transition:border-color var(--t-fast),box-shadow var(--t-fast)}
input::placeholder{color:var(--faint);opacity:1}
.input:hover,input[type=search]:hover,input[type=text]:hover,input[type=number]:hover{border-color:var(--faint)}
.input:focus,input[type=search]:focus,input[type=text]:focus,input[type=number]:focus{outline:none;border-color:var(--focus-line);box-shadow:var(--ring)}
input:disabled{opacity:.5;cursor:not-allowed}
input[type=search]{-webkit-appearance:none;appearance:none}
input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none;appearance:none}

/* Search: the one search and filter field on every page (hub hero, dataset and tag filters, file tree, runs, model filters).
     <label class="search [sm|lg|xl]">
       <span class="vh">Filter datasets</span>
       <input type="search" placeholder="Filter 34 datasets" autocomplete="off" spellcheck="false">
       <kbd class="search-k" aria-hidden="true">/</kbd>        optional shortcut hint, hidden on focus, when filled and on touch
     </label>
   /ui.js adds the magnifier (.search-i) and the clear button (.search-x, shown while there is text; it empties the field,
   fires "input" and refocuses it), and Esc in a filled field clears it. The box is the bordered element; the input inside
   is bare, so icon, text, placeholder, clear button and hint share one centre line at every size.
   Sizes: .sm 30 (inside panels and popovers) · default 34 (a toolbar beside .lb and .btn.sm) · .lg 40 (a filter bar beside
   .btn and .lb.lg) · .xl 48 (the page's hero search, as wide as the lede: --measure). One radius (--ctl-r) for all.
   Width: 100% of its container up to the size's max-width; inside .ctl-row it grows from a 240px basis. Below 640px it is
   always full width; on touch screens at least 44px tall. */
.search{--h:var(--ctl);--ic:15px;position:relative;display:flex;align-items:center;gap:var(--sp-8);width:100%;min-width:0;height:var(--h);margin:0;
  padding:0 var(--sp-4) 0 var(--sp-12);border:var(--border);border-radius:var(--ctl-r);background:var(--panel);color:var(--faint);
  font:400 var(--fs-small)/normal var(--sans);cursor:text;transition:border-color var(--t-fast),box-shadow var(--t-fast)}
.search:hover{border-color:var(--faint)}
.search:focus-within{border-color:var(--focus-line);box-shadow:var(--ring)}
.search input,.search input:hover,.search input:focus{flex:1 1 auto;align-self:stretch;min-width:0;width:auto;height:auto;min-height:0;margin:0;padding:0;border:0;border-radius:0;
  background:none;box-shadow:none;outline:none;color:var(--ink);font:inherit;text-overflow:ellipsis}
.search input::placeholder{color:var(--faint);opacity:1}
.search-i{flex:none;display:block;width:var(--ic);height:var(--ic);color:var(--faint);pointer-events:none}
.search:focus-within .search-i{color:var(--muted)}
/* disabled; loading (aria-busy on the .search: the magnifier becomes a spinner, for example while an index loads) */
.search:has(input:disabled){opacity:.5;cursor:not-allowed}
.search:has(input:disabled) input{cursor:not-allowed}
.search[aria-busy="true"] .search-i{visibility:hidden}
.search[aria-busy="true"]::before{content:"";position:absolute;left:calc(var(--sp-12) + 1px);top:50%;width:calc(var(--ic) - 3px);height:calc(var(--ic) - 3px);margin-top:calc((var(--ic) - 3px) / -2);
  border:1.5px solid var(--faint);border-right-color:transparent;border-radius:50%;animation:ui-spin .7s linear infinite}
.search.sm[aria-busy="true"]::before{left:calc(var(--sp-10) + 1px)}
.search.xl[aria-busy="true"]::before{left:calc(var(--sp-16) + 1px)}
.search-x{flex:none;display:none;place-items:center;width:calc(var(--h) - 10px);height:calc(var(--h) - 10px);margin:0;padding:0;border:0;border-radius:var(--r-sm);
  background:none;color:var(--muted);cursor:pointer;transition:background var(--t-fast),color var(--t-fast)}
.search-x svg{width:12px;height:12px}
.search-x:hover{background:var(--hover);color:var(--ink)}
.search-x:active{background:var(--press)}
.search-x:focus-visible{outline-offset:-2px}
.search.filled .search-x{display:grid}
.search-k{flex:none;display:grid;place-items:center;min-width:20px;height:20px;margin-right:var(--sp-4);padding:0 var(--sp-4);border:var(--hairline);border-radius:var(--r-xs);
  background:var(--paper);font:400 var(--fs-caption)/1 var(--mono);color:var(--faint);pointer-events:none}
.search:focus-within .search-k,.search.filled .search-k{display:none}
.search.sm{--h:var(--ctl-sm);--ic:13px;gap:var(--sp-6);padding-left:var(--sp-10);font-size:var(--fs-meta)}
.search.sm .search-k{min-width:18px;height:18px;margin-right:var(--sp-2);font-size:var(--fs-micro)}
.search.sm .search-x svg{width:10px;height:10px}
.search.lg{--h:var(--ctl-lg);--ic:16px;font-size:var(--fs-ui)}
.search.xl{--h:var(--ctl-xl);--ic:18px;gap:var(--sp-12);max-width:var(--measure);padding:0 var(--sp-8) 0 var(--sp-16);font-size:var(--fs-body)}
.search.xl .search-k{min-width:22px;height:22px}
.search.xl .search-x svg{width:13px;height:13px}
/* a row of controls on one centre line and one height: Search, .lb, .btn.sm, .tbtn, counts */
.ctl-row{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-8)}
.ctl-row > .search{flex:1 1 240px;width:auto;max-width:360px}
.ctl-row > .push{margin-left:auto}
@media (max-width:640px){.search,.ctl-row > .search{width:100%;max-width:none;flex:1 1 100%}}
@media (hover:none),(max-width:640px){.search-k{display:none}}  /* no keyboard shortcut hint on phones, whatever the pointer reports */
@media (pointer:coarse){
  .search{min-height:var(--touch);font-size:var(--fs-body)}
  .search .search-x{width:36px;height:36px}
  .search-k{display:none}
  .input,input[type=search],input[type=text],input[type=number]{min-height:var(--touch);font-size:var(--fs-body)}
  .search input{min-height:0}
  .btn,.btn.sm,.tbtn{min-height:var(--touch)}
}

/* checkbox: a hairline square, ink when checked */
input[type=checkbox]{-webkit-appearance:none;appearance:none;flex:none;width:16px;height:16px;margin:0;border:var(--border);border-radius:var(--r-xs);background:var(--panel);
  display:inline-grid;place-content:center;vertical-align:-3px;cursor:pointer;transition:background var(--t-fast),border-color var(--t-fast)}
input[type=checkbox]::before{content:"";width:9px;height:5px;border:solid var(--paper);border-width:0 0 1.6px 1.6px;transform:translateY(-1px) rotate(-45deg) scale(0);transition:transform var(--t-fast) var(--ease)}
input[type=checkbox]:hover{border-color:var(--faint)}
input[type=checkbox]:checked{background:var(--ink);border-color:var(--ink)}
input[type=checkbox]:checked::before{transform:translateY(-1px) rotate(-45deg) scale(1)}
input[type=checkbox]:disabled{opacity:.35;cursor:not-allowed}

/* listbox: replaces every native <select> (/ui.js); same height, radius and hairline as inputs and chips */
select{height:var(--ctl);padding:0 var(--sp-8);border:var(--border);border-radius:var(--ctl-r);background:var(--panel);color:var(--ink);font:400 var(--fs-small)/1 var(--sans);max-width:100%}
.js-ui select.lb-native{position:absolute !important;width:1px !important;height:1px !important;padding:0 !important;margin:0 !important;overflow:hidden;clip:rect(0 0 0 0);border:0 !important;opacity:0;pointer-events:none}
.lb{position:relative;display:inline-flex;vertical-align:middle;max-width:100%;min-width:0}
.lb-btn{display:inline-flex;align-items:center;gap:var(--sp-10);width:100%;min-width:0;height:var(--ctl);padding:0 var(--sp-10) 0 var(--sp-12);border:var(--border);border-radius:var(--ctl-r);
  background:var(--panel);color:var(--ink);font:400 var(--fs-small)/1 var(--sans);text-align:left;cursor:pointer;transition:border-color var(--t-fast),background var(--t-fast),box-shadow var(--t-fast)}
.lb-btn:hover{border-color:var(--faint)}
.lb-btn:active{background:var(--sunk)}
.lb-btn:focus-visible{outline:none;border-color:var(--focus-line);box-shadow:var(--ring)}
[role=listbox]:focus-visible,.lb-list:focus{outline:none}
.lb-btn[aria-expanded="true"]{border-color:var(--focus-line);box-shadow:var(--ring)}
.lb-btn:disabled{opacity:.45;cursor:default}
.lb-v{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lb-car{flex:none;width:8px;height:8px;margin-top:calc(-1 * var(--sp-2) - 1px);border:solid var(--muted);border-width:0 1.4px 1.4px 0;transform:rotate(45deg);transition:transform var(--t) var(--ease)}
.lb-btn[aria-expanded="true"] .lb-car{transform:translateY(3px) rotate(-135deg)}
.lb.sm .lb-btn{height:var(--ctl-sm);font-size:var(--fs-meta);padding:0 var(--sp-8) 0 var(--sp-10)}
.lb.lg .lb-btn{height:var(--ctl-lg)}
.lb-pop{position:absolute;left:0;top:calc(100% + var(--sp-6));z-index:40;width:max-content;min-width:100%;max-width:min(92vw,520px);padding:var(--sp-4);border:var(--border);border-radius:var(--r-lg);
  background:var(--panel);box-shadow:var(--shadow-2);transform-origin:top;animation:lb-in var(--t) var(--ease)}
.lb-pop.up{top:auto;bottom:calc(100% + var(--sp-6));transform-origin:bottom}
.lb-pop.end{left:auto;right:0}
.lb-pop[hidden]{display:none}
@keyframes lb-in{from{opacity:0;transform:translateY(-3px) scale(.99)}}
.lb-find{margin:0 0 var(--sp-4)}
.lb-list{max-height:min(320px,52vh);overflow-y:auto;overscroll-behavior:contain;margin:0;padding:0;list-style:none}
.lb-grp{padding:var(--sp-8) var(--sp-8) var(--sp-4);font:500 var(--fs-caption)/1.3 var(--sans);color:var(--faint)}
.lb-opt{display:flex;align-items:center;gap:var(--sp-10);min-height:var(--ctl-sm);padding:var(--sp-6) var(--sp-10) var(--sp-6) var(--sp-8);border-radius:var(--r-sm);
  font:400 var(--fs-small)/1.35 var(--sans);color:var(--ink2);cursor:pointer;scroll-margin:var(--sp-4)}
.lb-opt::before{content:"";flex:none;width:6px;height:6px;border-radius:50%;background:transparent}
.lb-opt[aria-selected="true"]{color:var(--ink);font-weight:500}
.lb-opt[aria-selected="true"]::before{background:var(--ink)}
.lb-opt.act{background:var(--hover);color:var(--ink)}
.lb-opt[aria-disabled="true"]{color:var(--ghost);cursor:default}
.lb-opt span{min-width:0;padding-right:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}  /* 1px: max-content rounds down at 2x and would cut the last glyph */
.lb-none{padding:var(--sp-10);font:400 var(--fs-meta)/1.4 var(--sans);color:var(--muted)}
.lb-static{display:inline-flex;align-items:center;height:var(--ctl);font:400 var(--fs-small)/1 var(--sans);color:var(--ink);font-variant-numeric:tabular-nums}
/* a label beside a control: one baseline */
.field{display:inline-flex;align-items:center;gap:var(--sp-8);font:400 var(--fs-small)/1 var(--sans);color:var(--muted)}
@media (pointer:coarse){.lb-opt{min-height:var(--touch)}.lb-btn{min-height:var(--touch)}}

/* pills: a filter toggle; 30px tall (--ctl-sm), pill radius, hairline; pressed is ink. On touch screens the hit area grows
   to 44px without changing the look. <div class="pills"><button class="pill" aria-pressed="true">All suites</button>…</div>
   (Static labels are the hub's .chip, 24px, not interactive.) */
.pills{display:flex;flex-wrap:wrap;gap:var(--sp-6)}
.pill{position:relative;display:inline-flex;align-items:center;gap:var(--sp-6);height:var(--ctl-sm);padding:0 var(--sp-12);border:var(--hairline);border-radius:var(--r-pill);
  background:transparent;color:var(--ink2);font:400 var(--fs-meta)/1 var(--sans);white-space:nowrap;text-decoration:none;cursor:pointer;transition:border-color var(--t-fast),background var(--t-fast),color var(--t-fast)}
.pill:hover{border-color:var(--line-strong);background:var(--hover);color:var(--ink)}
.pill:active{background:var(--press)}
.pill:disabled,.pill[aria-disabled="true"]{opacity:.4;cursor:default;background:transparent;border-color:var(--line)}
.pill:focus-visible{border-radius:var(--r-pill)}
.pill[aria-pressed="true"],.pill[aria-current]{border-color:var(--ink);background:var(--ink);color:var(--paper)}
.pill .n{color:var(--faint);font-variant-numeric:tabular-nums}
.pill[aria-pressed="true"] .n,.pill[aria-current] .n{color:inherit;opacity:.7}
@media (pointer:coarse){.pill::after{content:"";position:absolute;left:0;right:0;top:50%;height:var(--touch);transform:translateY(-50%)}}

/* tabs: text tabs on a hairline, the current one underlined in ink. <nav class="tabs"><a aria-current="page">…</a></nav>
   (or aria-selected="true" for in-page tabs). Scrolls sideways on narrow screens. The hairline and the underline are inset
   shadows inside the boxes: a scroller clips at its padding box, so a border pulled onto it with a negative margin vanishes. */
.tabs{display:flex;gap:var(--sp-28);margin:var(--sp-28) 0;border:0;box-shadow:inset 0 -1px 0 var(--line);overflow-x:auto;scrollbar-width:none}
.tabs::-webkit-scrollbar{display:none}
.tabs a,.tabs button{flex:none;margin:0;padding:0 0 var(--sp-12);border:0;border-radius:0;background:none;box-shadow:inset 0 -1px 0 transparent;
  font:400 var(--fs-ui)/1 var(--sans);color:var(--muted);text-decoration:none;white-space:nowrap;cursor:pointer;transition:color var(--t-fast),box-shadow var(--t-fast)}
.tabs a:hover,.tabs button:hover{color:var(--ink);box-shadow:inset 0 -1px 0 var(--line-strong)}
.tabs a:focus-visible,.tabs button:focus-visible{outline-offset:-2px;border-radius:var(--r-xs)}
.tabs button:disabled{color:var(--ghost);cursor:default;box-shadow:none}
.tabs [aria-current],.tabs [aria-selected="true"],.tabs [aria-current]:hover,.tabs [aria-selected="true"]:hover{color:var(--ink);box-shadow:inset 0 -1px 0 var(--ink)}
.tabs a span,.tabs .n{margin-left:var(--sp-4);color:var(--faint)}
@media (pointer:coarse){.tabs a,.tabs button{padding-top:var(--sp-14)}}

/* segmented control: 2-4 mutually exclusive views (Grid / List). One frame at --ctl with the field hairline; segments fill
   its full inner height; the pressed one is a raised panel. <div class="seg" role="group" aria-label="View">
   <button aria-pressed="true">Grid</button><button aria-pressed="false">List</button></div> (.sm 30, .lg 40) */
.seg{--h:var(--ctl);display:inline-flex;align-items:stretch;height:var(--h);padding:2px;gap:2px;border:var(--border);border-radius:var(--ctl-r);background:var(--sunk);vertical-align:middle}
.seg > button,.seg > a{display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-6);min-width:0;height:auto;margin:0;padding:0 var(--sp-10);border:0;border-radius:calc(var(--ctl-r) - 3px);
  background:none;color:var(--muted);font:400 var(--fs-small)/1 var(--sans);white-space:nowrap;text-decoration:none;cursor:pointer;transition:background var(--t-fast),color var(--t-fast),box-shadow var(--t-fast)}
.seg > button:hover,.seg > a:hover{color:var(--ink)}
.seg > button:active,.seg > a:active{background:var(--press)}
.seg > button:disabled{opacity:.4;cursor:default;color:var(--muted);background:none}
.seg > [aria-pressed="true"],.seg > [aria-current],.seg > [aria-selected="true"]{background:var(--panel);color:var(--ink);box-shadow:var(--shadow-1),0 0 0 1px var(--line)}
.seg > button:focus-visible,.seg > a:focus-visible{outline-offset:0}
.seg svg{width:14px;height:14px}
.seg.sm{--h:var(--ctl-sm)}
.seg.sm > button,.seg.sm > a{font-size:var(--fs-meta);padding:0 var(--sp-8)}
.seg.lg{--h:var(--ctl-lg)}
@media (pointer:coarse){.seg,.seg.sm{min-height:var(--touch)}}

/* pagination: previous, pages, next; every item a --ctl square or pill on one line */
.pager-n{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-4)}
.pager-n a,.pager-n button,.pager-n span{display:inline-flex;align-items:center;justify-content:center;min-width:var(--ctl);height:var(--ctl);padding:0 var(--sp-10);border:0;border-radius:var(--ctl-r);
  background:none;color:var(--ink2);font:400 var(--fs-small)/1 var(--sans);font-variant-numeric:tabular-nums;text-decoration:none;cursor:pointer}
.pager-n a:hover,.pager-n button:hover{background:var(--hover);color:var(--ink)}
.pager-n a:active,.pager-n button:active{background:var(--press)}
.pager-n [aria-current]{box-shadow:inset 0 0 0 1px var(--line-strong);color:var(--ink);font-weight:500}
.pager-n span{cursor:default;color:var(--faint)}
.pager-n :disabled{color:var(--ghost);background:none;cursor:default}
@media (pointer:coarse){.pager-n a,.pager-n button,.pager-n span{min-width:var(--touch);height:var(--touch)}}

/* disclosure: <details class="disc"><summary>Modifiers</summary>…</details> (also any <details> without a class). The site
   chevron instead of the browser triangle: points right when closed, down when open. */
:is(details.disc,details:not([class])) > summary{display:flex;align-items:center;gap:var(--sp-8);min-height:var(--ctl-sm);list-style:none;cursor:pointer;color:var(--ink2);transition:color var(--t-fast)}
:is(details.disc,details:not([class])) > summary::-webkit-details-marker{display:none}
:is(details.disc,details:not([class])) > summary::before{content:"";flex:none;width:6px;height:6px;margin:0 var(--sp-2);border:solid var(--muted);border-width:0 1.4px 1.4px 0;transform:rotate(-45deg);transition:transform var(--t) var(--ease)}
:is(details.disc,details:not([class]))[open] > summary::before{transform:translateY(-2px) rotate(45deg)}
:is(details.disc,details:not([class])) > summary:hover{color:var(--ink)}
@media (pointer:coarse){:is(details.disc,details:not([class])) > summary{min-height:var(--touch)}}

/* cards: one radius (--r-lg), one hairline, the panel colour, 16px padding (20px with .lg); a media card puts its image
   flush at the top and pads only its body (.card-b). Hover lifts the border, never the layout. */
.card{min-width:0;padding:var(--sp-16);border:var(--hairline);border-radius:var(--r-lg);background:var(--panel)}
.card.lg{padding:var(--sp-20)}
.card.media{padding:0;overflow:hidden}
.card.media > .card-b{padding:var(--sp-12) var(--sp-16) var(--sp-16)}
a.card,.card > a:only-child{color:inherit;text-decoration:none;transition:border-color var(--t-fast),box-shadow var(--t-fast)}
a.card:hover{border-color:var(--line-strong);box-shadow:var(--shadow-1)}
.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:var(--sp-16)}

/* skeletons and empty states */
.skel{display:block;border-radius:var(--r-xs);background:linear-gradient(100deg,var(--sunk) 30%,var(--line) 50%,var(--sunk) 70%) 0 0/250% 100%;animation:ui-sk 1.3s linear infinite}
@keyframes ui-sk{to{background-position:-250% 0}}
.empty-state{padding:var(--sp-32) var(--sp-20);border:1px dashed var(--line-strong);border-radius:var(--r-lg);text-align:center;color:var(--muted);font:400 var(--fs-small)/1.6 var(--sans)}
.empty-state b{display:block;margin:0 0 var(--sp-4);color:var(--ink);font-weight:500}

/* media: reserve space, a quiet frame, a styled native player */
video{accent-color:var(--accent);border-radius:var(--r-md);background:var(--media-bg)}
video:focus-visible{outline-offset:3px}
img{max-width:100%}

/* truncation: one line with an ellipsis (ui.js adds the full text as a tooltip when it is cut) */
.trunc{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}

@media (prefers-reduced-motion: reduce){*,*::before,*::after{animation-duration:.001ms !important;animation-iteration-count:1 !important;transition-duration:0s !important;scroll-behavior:auto !important}}

/* model comparison set (/modelset.js): the quiet control, its popover, hidden pairs and dot strips */
.ms-off{display:none !important}
[data-modelset]{position:relative;display:inline-flex;align-items:center;gap:var(--sp-8);font:400 var(--fs-meta)/1 var(--sans);color:var(--muted);font-variant-numeric:tabular-nums}
[data-modelset] .ms-sum::after{content:"·";margin-left:var(--sp-8);color:var(--ghost)}
.ms-pop{position:absolute;left:0;top:calc(100% + var(--sp-8));z-index:45;width:min(380px,92vw);border:var(--border);border-radius:var(--r-lg);background:var(--panel);
  box-shadow:var(--shadow-2);animation:lb-in var(--t) var(--ease);text-align:left}
.ms-pop.end{left:auto;right:0}
/* phones: a bottom sheet over a backdrop (modelset.js moves it into <body> and locks the page scroll) */
.ms-scrim{position:fixed;inset:0;z-index:69;background:var(--backdrop);animation:ms-fade var(--t) var(--ease)}
@keyframes ms-fade{from{opacity:0}}
html.ms-lock,html.ms-lock body{overflow:hidden}
.ms-pop.sheet{position:fixed;left:0;right:0;top:auto;bottom:0;z-index:70;width:auto;max-height:86vh;display:flex;flex-direction:column;border-width:1px 0 0;
  border-radius:var(--r-xl) var(--r-xl) 0 0;padding-bottom:env(safe-area-inset-bottom);animation:ms-up var(--t-slow) var(--ease)}
.ms-pop.sheet::before{content:"";display:block;width:36px;height:4px;margin:var(--sp-8) auto 0;border-radius:var(--r-pill);background:var(--line-strong)}
.ms-pop.sheet .ms-list{flex:1;min-height:0;max-height:none}
.ms-pop.sheet:focus{outline:none}
@keyframes ms-up{from{transform:translateY(24px);opacity:0}}
.ms-top{padding:var(--sp-10) var(--sp-10) var(--sp-6);border-bottom:var(--hairline)}

.ms-pre{display:flex;flex-wrap:wrap;gap:0 var(--sp-12);margin:var(--sp-6) 0 0 var(--sp-6)}
.ms-list{max-height:min(360px,50vh);overflow-y:auto;overscroll-behavior:contain;padding:var(--sp-4) var(--sp-6) var(--sp-8)}
.ms-fam{margin:0;padding:0;border:0}
.ms-fam legend{padding:var(--sp-10) var(--sp-8) var(--sp-4);font:500 var(--fs-caption)/1.3 var(--sans);color:var(--faint)}
.ms-row{display:grid;grid-template-columns:16px minmax(0,1fr) auto;align-items:center;gap:var(--sp-10);min-height:var(--ctl);padding:var(--sp-4) var(--sp-8);border-radius:var(--r-sm);cursor:pointer}
.ms-row:hover{background:var(--hover)}
.ms-row[hidden],.ms-fam[hidden]{display:none}
.ms-l{display:flex;flex-direction:column;gap:var(--sp-2);min-width:0}
.ms-n{font:400 var(--fs-small)/1.3 var(--sans);color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ms-h{font:400 var(--fs-caption)/1.3 var(--sans);color:var(--muted)}
.ms-r{font:400 var(--fs-caption)/1 var(--mono);color:var(--muted);font-variant-numeric:tabular-nums}
.ms-none{margin:var(--sp-12) var(--sp-8);font:400 var(--fs-meta)/1.4 var(--sans);color:var(--muted)}
.ms-foot{display:flex;align-items:center;justify-content:space-between;padding:var(--sp-8) var(--sp-10);border-top:var(--hairline);font:400 var(--fs-caption)/1 var(--sans);color:var(--muted)}
@media (pointer:coarse){.ms-row{min-height:var(--touch)}}
/* a dot per pair, sorted by result: filled solved, half some seeds, ring not solved, dashed infrastructure */
[data-res]{display:inline-flex;align-items:center;gap:var(--sp-8);min-width:0;font:400 var(--fs-meta)/1 var(--sans);color:var(--ink2);font-variant-numeric:tabular-nums}
[data-res] .ds{display:inline-flex;flex-wrap:wrap;gap:3px;max-width:176px}
[data-res] .d{display:block;width:8px;height:8px;border-radius:50%;box-sizing:border-box}
[data-res] .d.ok{background:var(--ink2)}
[data-res] .d.some{background:linear-gradient(90deg,var(--ink2) 50%,transparent 50%);box-shadow:inset 0 0 0 1.2px var(--ink2)}
[data-res] .d.no{box-shadow:inset 0 0 0 1.2px var(--faint)}
[data-res] .d.infra{box-shadow:inset 0 0 0 1.2px var(--ghost);border:1px dashed var(--ghost)}
[data-res] .dt{white-space:nowrap;color:var(--muted);font-size:var(--fs-caption)}

@media (pointer:coarse){input[type=checkbox]{width:22px;height:22px}}

/* a scroller that hides content to its right fades at that edge (ui.js sets .fade-r while more is there) */
.fade-r{-webkit-mask-image:linear-gradient(90deg,#000 calc(100% - var(--sp-48)),transparent);mask-image:linear-gradient(90deg,#000 calc(100% - var(--sp-48)),transparent)}
/* a code block whose lines run past its right edge fades there, inside the frame (ui.js sets .more on .code or pre's wrapper) */
.code.more::after{content:"";position:absolute;top:1px;right:1px;bottom:1px;width:var(--sp-48);border-radius:0 var(--r-md) var(--r-md) 0;background:linear-gradient(90deg,transparent,var(--panel));pointer-events:none}
.code.more .copy{z-index:1}
/* inline code that wraps keeps its frame on both lines */
code{-webkit-box-decoration-break:clone;box-decoration-break:clone}
/* a proportion bar for aggregates over many pairs (modelset.js above 12 pairs) */
[data-res] .pb{position:relative;display:block;width:64px;height:4px;border-radius:var(--r-2xs);background:var(--line)}
[data-res] .pb i{position:absolute;left:0;top:0;bottom:0;border-radius:var(--r-2xs);background:var(--ink2)}

/* model mark: the provider's icon beside a model or policy name, never on harnesses (spec: tools/models.json `icon`,
   site/icons/models/SOURCES.md). One em square, drawn in
   the text colour through a mask so it matches the greys in light and dark. A provider without a mark gets an empty slot of the same width.
   aria-hidden: the name beside it is the text. */
.model-mark{display:inline-block;flex:none;width:1em;height:1em;margin-right:.4em;vertical-align:-.16em;color:inherit;
  background:currentColor;-webkit-mask:var(--mm) center/contain no-repeat;mask:var(--mm) center/contain no-repeat}
.model-mark.none{background:none}  /* no mark: the slot stays, so names line up in a column */
