/* GENERATED by design/ship.py from design/reader/surface.css -- DO NOT EDIT. That file is the master; run `python3 design/export.py --shell` and this one is rewritten. */
/* ENGINE · THE SEARCH SURFACE — the sheet, its two columns, and the three
   kinds of row · bench: bench-surface.html · mounted by: surface.js
   ====================================================================== */
/* SEARCH IS THE SURFACE, AND STUDIO LIVES IN IT (Osca, 7 September).
   This supersedes two things at once:

     - search as a WINDOW.  `desktop/src-tauri/src/tabs.rs::open_search`
       builds a 1280x800 window of its own with three panes side by side.
       A window is a place you GO; this is a thing that opens OVER what you
       are already on, and closes without moving you.
     - studio as a DESTINATION. The tab-strip plan gave Studio its own tab
       beside Library and Reader. It has no tab here. Pulling a result in
       IS the pipeline, so the pipeline is the surface's right-hand column
       and is on the screen the whole time, whether or not anything runs.

   TWO COLUMNS, ONE FIELD, ALWAYS THE SAME WIDTH.

       +--------------------------------------------------+
       |  the field, across both                          |
       +----------------------------+---------------------+
       |  RESULTS                   |  THE WORKS          |
       |  yours and out there, in   |  the running job,   |
       |  one list, told apart by   |  the queue, and the |
       |  the spine                 |  step grid of       |
       |                            |  whatever row is    |
       |  ...                       |  open               |
       |  Video   (a hole)          |                     |
       |  Search the web            |                     |
       +----------------------------+---------------------+

   THE SPINE IS THE WHOLE DISTINCTION, and it is why the two kinds can sit
   in one list rather than in two lanes:

       a book you HAVE            a full spine, in the book's own hue
       a book you are GETTING     a spine that fills as it arrives
       a book OUT THERE           no spine, and the gutter still reserved

   The gutter is reserved in all three, so every title has the same `left`
   whatever the row is -- that is the invariant the bench prints, and it is
   what makes one list read as one list.

   IT IS CHROME, SO EVERY NUMBER HERE IS FIXED (bar.css's rule). The book is
   fluid -- 14.5px + 0.75vw and the rest -- and nothing in this file scales
   with the column. It belongs to the window, like the title bar it drops
   from.  */

:root{
  /* --- the sheet ------------------------------------------------------- */
  --sf-w:        880px;   /* one width. The columns split it; it never grows */
  --sf-top:       64px;   /* from the top edge -- clear of the 28px bar      */
  --sf-bottom:    64px;   /* the sheet stops here; the list scrolls inside   */
  --sf-radius:    14px;
  --sf-pad:       18px;
  --sf-scrim:     .34;    /* how much of what you were on is put back        */
  --sf-blur:      18px;

  /* --- the split ------------------------------------------------------- */
  --sf-split:     62%;    /* results | the works                            */
  --sf-gap:       18px;

  /* --- the field ------------------------------------------------------- */
  --sf-field:     20px;   /* the query, and it is the biggest type here      */
  --sf-fieldh:    54px;

  /* --- a row ----------------------------------------------------------- */
  --sf-row:       46px;   /* the minimum; a row grows to its own two lines   */
  --sf-gutter:    16px;   /* reserved for the spine, in every kind of row    */
  --sf-spine:    2.5px;
  --sf-title:     15px;
  --sf-meta:     9.5px;
  --sf-head:     9.5px;   /* a section head                                  */

  /* --- the step ribbon -------------------------------------------------- */
  --sf-cell:       7px;
  --sf-cellgap:    3px;

  /* --- the video hole ---------------------------------------------------- */
  --sf-video:    300px;   /* when it is open. Closed it is 0 and the shell
                             hides the webview by itself (videos_rect: a box
                             under 1px high is None)                         */

  /* --- ITS OWN PALETTE, AND ITS OWN TYPE (G-SURF2, 10 Sep) -----------------
     Osca: *"Opened from the library it's grey-tinted and loose; from a reader
     it's yellow-tinted and tight -- it must be the same sheet regardless of
     what's behind."* It was reading the HOST's tokens -- `--ground`, `--ink`,
     `--dim`, `--rule`, `--ui`, `--serif` -- and the two hosts disagree about
     every one of them:
       reader   tokens.css, with the reader's paper WARMTH folded into
                `--ground` -- so the sheet took the page's cream (the yellow)
       library  tokens.css is NOT LOADED, so all six are undefined, and a
                `var()` of an undefined name makes its whole declaration
                invalid: the background went TRANSPARENT (the grey was the
                library itself, blurred through it), every `font:` shorthand
                fell back to the inherited 16px (the loose), and the border
                vanished.
     Measured before this, off the two real pages at 1440x900 with the same
     query: 192 of 195 shared nodes differed in computed style.
     So the sheet carries its own six, and nothing in this file reads a host
     token any more. The values are tokens.css's own at `--warm: 0` -- the
     near-neutral paper, the reader's own formula with the warmth taken out --
     because chrome does not follow the book's paper (bar.css's rule: chrome
     is fixed). Dark is the same swap tokens.css makes, on the same three
     selectors, so Settings' theme reaches the sheet exactly as it reaches a
     page. */
  --sf-ground-hsl: 48 14% 98.5%;
  --sf-ground:  hsl(var(--sf-ground-hsl));
  --sf-ink:     hsl(42 18% 7%);
  --sf-dim:     #7a776d;
  --sf-rule:    #dedcd4;
  --sf-ui:      -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --sf-serif:   "Iowan Old Style","Palatino Linotype",Palatino,Georgia,serif;
  /* HOW SOLID THE SHEET IS. 1 is opaque. It was 0 over the library (the
     background declaration was invalid, see above) and 1 over the reader;
     .97 lets 3 % of what is behind through the blur -- enough to say the
     sheet is OVER something, never enough to tint it. One number: the pixel
     proof (design/reader/pixelcheck/sheet.mjs) runs once at this value and
     once with it forced to 1, and at 1 the two hosts are identical inside the
     sheet to the pixel. */
  --sf-solid:   .97;
  --sf-sheet:   hsl(var(--sf-ground-hsl) / var(--sf-solid));
  /* the type every node inherits unless it says otherwise -- so an element
     with no size of its own takes 12px from HERE, not 16px from the library's
     body or 26.8px from the reader's fluid <html> */
  --sf-size:    12px;
  --sf-lh:      1.35;

  /* --- the phone --------------------------------------------------------- */
  --sf-narrow:   720px;   /* under this the sheet is the screen, one column  */
}

/* DARK IS THE SAME SWAP tokens.css MAKES, on the same selectors (prefs/ stamps
   html[data-theme]; "system" stamps nothing and leaves the media query in
   charge) -- and at warmth 0, like the light half. */
html[data-theme="dark"], html[data-dark]{
  --sf-ground-hsl: 240 7.3% 8.04%;
  --sf-ink: hsl(30 4.17% 90.59%); --sf-dim:#8c8a83; --sf-rule:#2e2e33;
}
@media (prefers-color-scheme: dark){
  html:not([data-theme="light"]){
    --sf-ground-hsl: 240 7.3% 8.04%;
    --sf-ink: hsl(30 4.17% 90.59%); --sf-dim:#8c8a83; --sf-rule:#2e2e33;
  }
}

/* ============================ THE SCRIM ================================= */
/* You are still ON something. The scrim says so and takes the dismiss. */
.sf-scrim{
  position:fixed; inset:0; z-index:8000;
  background:rgba(0,0,0,var(--sf-scrim));
  opacity:0; pointer-events:none; transition:opacity .16s ease;
}
.sf-scrim.on{ opacity:1; pointer-events:auto; }

/* ============================ THE SHEET ================================= */
.sf{
  position:fixed; z-index:8001;
  left:50%; top:var(--sf-top);
  width:var(--sf-w); max-width:calc(100vw - 32px);
  height:calc(100vh - var(--sf-top) - var(--sf-bottom));
  transform:translate(-50%, -8px); opacity:0; pointer-events:none;
  transition:opacity .16s ease, transform .16s ease;
  display:flex; flex-direction:column;
  box-sizing:border-box;
  background:var(--sf-sheet); color:var(--sf-ink);
  /* EVERY INHERITED PROPERTY, PINNED HERE, so no host reaches a node inside
     through inheritance: the library's 16px body, the reader's fluid rem, a
     page's letter-spacing or line-height. The list is what the two-host diff
     found and what could find it next. */
  font:400 var(--sf-size)/var(--sf-lh) var(--sf-ui);
  letter-spacing:normal; word-spacing:normal; text-transform:none;
  text-align:left; text-indent:0; white-space:normal;
  font-variant:normal; font-feature-settings:normal; font-kerning:auto;
  -webkit-font-smoothing:antialiased; text-rendering:auto;
  border:1px solid var(--sf-rule); border-radius:var(--sf-radius);
  box-shadow:0 26px 70px rgba(0,0,0,.28), 0 3px 10px rgba(0,0,0,.10);
  overflow:hidden;
  -webkit-backdrop-filter:blur(var(--sf-blur)); backdrop-filter:blur(var(--sf-blur));
}
.sf.on{ opacity:1; transform:translate(-50%, 0); pointer-events:auto; }
/* THE LIBRARY'S `* { box-sizing: border-box }` REACHED EVERY NODE HERE and
   the reader's page has no such rule -- 168 nodes measured apart, and the
   list and works columns a different width in each. `revert` hands each node
   back the browser's own value (content-box for a span, border-box where the
   UA says so), which is what the bench measured every rectangle against; the
   two rules below that want border-box say so themselves, later in the file. */
.sf *, .sf *::before, .sf *::after{ box-sizing:revert; }

/* ============================ THE FIELD ================================= */
/* One field for the whole surface. The bar upstairs carries only the door to
   here (Osca: "The BAR carries only the entry to it"), so this is the only
   place in the application a query is typed. */
.sf-field{
  flex:0 0 auto; height:var(--sf-fieldh);
  display:flex; align-items:center; gap:11px;
  padding:0 var(--sf-pad); box-sizing:border-box;
  border-bottom:1px solid var(--sf-rule);
}
/* the lookup ring, bar.css's own -- a ring with a hand, not a magnifier,
   because a lookup lands you at a PLACE IN A BOOK */
.sf-look{ flex:0 0 auto; width:15px; height:15px; border-radius:50%;
  box-shadow:inset 0 0 0 1.2px var(--sf-dim); position:relative; }
.sf-look::after{ content:""; position:absolute; left:7px; top:3.5px;
  width:1.2px; height:5px; background:var(--sf-dim); transform-origin:50% 100%;
  transform:rotate(38deg); }
.sf-field input{
  flex:1 1 auto; min-width:0; border:0; outline:0; background:transparent;
  font:var(--sf-field)/1.2 var(--sf-ui); letter-spacing:-.01em; color:var(--sf-ink);
}
.sf-field input::placeholder{ color:var(--sf-dim); }
.sf-esc{
  flex:0 0 auto; font:600 9px/1 var(--sf-ui); letter-spacing:.12em;
  text-transform:uppercase; color:var(--sf-dim);
  background:transparent; border:1px solid var(--sf-rule); border-radius:5px;
  padding:5px 7px; cursor:pointer;
}
.sf-esc:hover{ color:var(--sf-ink); }

/* ============================ THE COLUMNS =============================== */
.sf-cols{ flex:1 1 auto; display:flex; min-height:0; }
.sf-list{
  flex:0 0 var(--sf-split); min-width:0;
  padding:6px var(--sf-pad) var(--sf-pad);
  overflow-y:auto; overscroll-behavior:contain;
}
.sf-works{
  flex:1 1 auto; min-width:0;
  padding:6px var(--sf-pad) var(--sf-pad);
  border-left:1px solid var(--sf-rule);
  overflow-y:auto; overscroll-behavior:contain;
}

/* a section head, in both columns */
.sf-h{
  display:block; padding:14px 0 6px;
  font:600 var(--sf-head)/1 var(--sf-ui); letter-spacing:.14em;
  text-transform:uppercase; color:var(--sf-dim);
}
.sf-h:first-child{ padding-top:6px; }

/* ============================== A ROW =================================== */
.sf-row{
  position:relative; display:flex; align-items:flex-start; gap:10px;
  min-height:var(--sf-row);
  padding:9px 0 9px var(--sf-gutter);
  border-bottom:1px solid var(--sf-rule);
  color:inherit; text-decoration:none;
  border-radius:6px;
}
.sf-row:last-child{ border-bottom:0; }
.sf-row .spine{
  position:absolute; left:0; top:9px; width:var(--sf-spine);
  height:0; border-radius:var(--sf-spine);
  background:var(--sf-dim); transition:height .28s ease;
}
.sf-row .body{ flex:1 1 auto; min-width:0; }
.sf-row .t{
  display:block; font-family:var(--sf-serif); font-size:var(--sf-title);
  line-height:1.24; letter-spacing:-.02em; color:var(--sf-ink);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
/* THE SECOND LINE IS A ROW, NOT A STRING (Round 2 item 5).
   It was one span with `white-space:nowrap; text-overflow:ellipsis`, holding
   `author · source` joined — so the ellipsis ate the SOURCE off the right end
   whenever the author was long, and the one word a person needs to judge a
   result was the first thing to go. Measured on the mock's own long author
   ("Benedictus de Spinoza, translated from the Latin by R. H. M. Elwes, with
   an introduction"): the source was not on the row at all.
   Now it is a flex row: the source, the language and the text/audio marker are
   `flex:0 0 auto` and CANNOT be cut; the author is the only `1 1 auto`, so it
   is the thing that truncates. A WORK row still puts a plain string in here
   and that keeps working — a lone text node in a flex row lays out as one
   item, and `min-width:0` lets it shrink. */
.sf-row .s{
  display:flex; align-items:baseline; gap:7px; margin-top:.45em;
  font:var(--sf-meta)/1.3 var(--sf-ui); letter-spacing:.14em;
  text-transform:uppercase; color:var(--sf-dim);
  overflow:hidden; white-space:nowrap; min-width:0;
}
.sf-row .s > .sf-who{
  flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis;
}
.sf-row .s > .sf-src-tag,
.sf-row .s > .sf-lang,
.sf-row .s > .sf-ta{ flex:0 0 auto; }
.sf-row .s > .sf-src-tag{ color:var(--sf-ink); font-weight:600; }
.sf-row .s > .sf-lang{ opacity:.7; }

/* ITEM 6 · TEXT, AUDIO, OR BOTH — three states and no fourth. Both is the
   only one that is good news, so it is the only one with any weight. */
.sf-ta{ letter-spacing:.1em; }
.sf-ta.ta-both{ color:#3f7a4a; }
.sf-ta.ta-text{ opacity:.75; }
.sf-ta.ta-audio{ color:#a8532a; }

/* ITEM 4 · the facts, on their own wrapping line — *"don't drop data because
   it's long"*, so they wrap rather than truncate */
.sf-facts{
  display:flex; flex-wrap:wrap; gap:4px 10px; margin-top:.4em;
  font:var(--sf-meta)/1.4 var(--sf-ui); letter-spacing:.1em;
  text-transform:uppercase; color:var(--sf-dim);
}
.sf-facts i{ font-style:normal; }
.sf-preview{
  display:block; margin-top:.45em; max-width:60ch;
  font:12px/1.5 var(--sf-serif); color:var(--sf-dim);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}

/* G-SURF2 · WHERE IT CAME FROM, on the row. One line under the second:
   the origin, then the file. Lowercase and monospaced -- both are names you
   may have to type or find, and letter case is part of a path, which the
   meta line's small capitals would have thrown away. Each half truncates on
   its own, the origin first; the whole of both is in each half's title and,
   selected, in THE WORKS. */
.sf-prov{
  display:flex; align-items:baseline; gap:6px; margin-top:.4em; min-width:0;
  font:10px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
  color:var(--sf-dim); white-space:nowrap; overflow:hidden;
}
.sf-prov .po{ flex:0 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; }
.sf-prov .pf{ flex:0 1 auto; min-width:3em; overflow:hidden; text-overflow:ellipsis;
  color:var(--sf-ink); opacity:.78; }
.sf-prov .sep{ flex:0 0 auto; opacity:.6; }
.sf-prov .po:empty, .sf-prov .po:empty + .sep{ display:none; }
/* an import that did not come in: studio's own sentence, whole, wrapping */
.sf-prov.err{ display:block; white-space:pre-wrap; overflow-wrap:anywhere;
  max-height:6.8em; overflow:auto; color:#a8532a; }
.sf-provpane .sf-path, .sf-provpane .sf-origin{ padding-top:5px; }
.sf-provpane .sf-origin{ color:var(--sf-ink); }

/* ITEM 8 · the whole row is a target, and it looks like one */
.sf-row.r-out{ cursor:pointer; }
.sf-row.r-out:hover{ background:rgba(127,127,127,.07); }
.sf-row.r-out:hover .t{ text-decoration:underline; text-underline-offset:2px; }
.sf-row.r-out.sel{ background:rgba(127,127,127,.10); }

/* ITEMS 1+2 · the WORKING band owns running, and its header owns Hold all */
.sf-h.sf-workhead{
  display:flex; align-items:center; gap:10px; flex-wrap:wrap;
}
.sf-h.sf-workhead .wl{ flex:1 1 auto; }
.sf-h.sf-workhead .sf-verb{ flex:0 0 auto; }
.sf-heldwhy{
  flex:1 0 100%; text-transform:none; letter-spacing:0;
  font:11px/1.4 var(--sf-ui); color:#a8532a;
}
.sf-rowacts{ display:inline-flex; align-items:center; gap:5px; }
.sf-row.r-queued .spine{ opacity:.45; height:calc(100% - 26px); }
.sf-row.r-queued .t{ opacity:.8; }

/* ITEM 7 · the audition, in the panel */
.sf-peekpane{ border-top:1px solid var(--sf-rule); margin-top:8px; }
.sf-peekaudio{ padding:6px 0; }
.sf-peekaudio audio{ width:100%; height:32px; }
.sf-peektext, .sf-candtext{
  margin:6px 0 10px; padding:0; max-height:200px; overflow:auto;
  white-space:pre-wrap; word-break:break-word;
  font:11.5px/1.5 var(--sf-serif); color:var(--sf-dim);
}
.sf-pair{
  margin:2px 0 8px; font:10.5px/1.45 var(--sf-ui); color:var(--sf-dim);
}
.sf-row .end{
  flex:0 0 auto; display:flex; align-items:center; gap:7px;
  padding-top:2px;
  font:var(--sf-meta)/1.3 var(--sf-ui); letter-spacing:.12em;
  text-transform:uppercase; color:var(--sf-dim);
}

/* --- ON THE SHELF: the row IS the door, and the spine is full ----------- */
/* §5.8, 8 Sep -- EVERY ROW ON THE LEFT IS A TARGET, and one rule says so.
   Osca: *"Right now hovering a book on the left does nothing -- only ADD is a
   target."* It was three rules with three different answers: `.r-shelf` was
   a pointer, `.sf-hit` was a pointer, and `.r-out` -- the fetchable result,
   the row that complaint is about -- had neither cursor nor hover, so four
   fifths of it was dead space above a button. This is the whole of it now,
   and the per-kind rules below say only what is still DIFFERENT about a kind
   (the spine's height), never whether it can be clicked. */
.sf-row:not(.sf-query){ cursor:pointer; }
.sf-row:not(.sf-query):hover{ background:rgba(127,127,127,.07); }
.sf-row.sel{ background:rgba(127,127,127,.10); }
/* the selected row is named by the spine as well as the wash, so it is still
   legible at the top of a long list with the pointer somewhere else */
.sf-row.sel .spine{ opacity:1; }

.sf-row.r-shelf{ cursor:pointer; }
.sf-row.r-shelf .spine{ height:calc(100% - 18px); }
/* folded into `.sf-row:not(.sf-query):hover` above (8 Sep, §5.8) */

/* --- OUT THERE: no spine, and one verb --------------------------------- */
.sf-row.r-out .spine{ height:0; }
.sf-row.r-out .t{ color:var(--sf-ink); }

/* --- WORKING: the spine fills, and a hairline runs under the row -------- */
.sf-row.r-work{ cursor:pointer; }
.sf-row.r-work .spine{ height:calc((100% - 18px) * var(--p, 0)); }
.sf-row.r-work .under{
  position:absolute; left:0; right:0; bottom:-1px; height:1px;
  background:linear-gradient(to right,
    var(--hue) 0, var(--hue) calc(var(--p, 0) * 100%),
    transparent calc(var(--p, 0) * 100%));
}

/* WHAT A SHELF ROW SAYS AT ITS RIGHT, and the second target on it. The row
   is the door -- it opens the book -- so "show me its steps" cannot also be
   the row; it is this, and it stops the click from reaching the door. */
.sf-row .end .cnt{ white-space:nowrap; }
.sf-peek{
  flex:0 0 auto; display:flex; align-items:center; justify-content:center;
  width:18px; height:18px; padding:0; margin-right:-4px;
  background:none; border:0; border-radius:4px; cursor:pointer; opacity:0;
  transition:opacity .12s ease;
}
.sf-row:hover .sf-peek, .sf-row.sel .sf-peek, .sf-peek:focus-visible{ opacity:1; }
.sf-peek:hover{ background:rgba(127,127,127,.12); }
.sf-peek .chev{ margin:0; transform:rotate(-45deg) translate(-1px,1px); }

/* the one verb, the library's drawn read button at this row's size */
.sf-verb{
  font:600 9px/1 var(--sf-ui); letter-spacing:.10em; text-transform:uppercase;
  color:var(--sf-ground); background:var(--sf-ink);
  border:0; border-radius:999px; padding:.62em 1.15em; cursor:pointer;
  white-space:nowrap;
}
.sf-verb[disabled]{ background:transparent; color:var(--sf-dim);
  box-shadow:inset 0 0 0 1px var(--sf-rule); cursor:default; }

/* ========================= THE STEP RIBBON ============================== */
/* studio/state.py STEPS, in its order and its length: parse, interpret,
   speak, restitch, align, prosody, dict, export. Eight cells at a row's
   scale is a lot of furniture, so the bench carries a three-cell coarse
   version too (parse / voice / ready) -- one switch, and Osca picks. */
.sf-steps{ display:flex; gap:var(--sf-cellgap); align-items:center; }
.sf-steps i{
  display:block; width:var(--sf-cell); height:var(--sf-cell);
  border-radius:1.5px; background:transparent;
  box-shadow:inset 0 0 0 1px var(--sf-rule);
}
.sf-steps i.ok  { background:var(--hue, var(--sf-ink)); box-shadow:none; }
.sf-steps i.run { background:var(--hue, var(--sf-ink)); opacity:.45;
                  animation:sfPulse 1.1s ease-in-out infinite; box-shadow:none; }
.sf-steps i.bad { background:#a8532a; box-shadow:none; }
@keyframes sfPulse{ 0%,100%{ opacity:.28 } 50%{ opacity:.72 } }

/* ============================ THE WORKS ================================= */
/* studio/studio.html's cockpit, folded in. The card, the queue and the grid
   are the same three things it draws, at this width. */
.sf-card{ padding:2px 0 10px; border-bottom:1px solid var(--sf-rule); }
.sf-card .l{ display:flex; justify-content:space-between; gap:8px;
  font:11px/1.35 var(--sf-ui); color:var(--sf-ink); }
.sf-card .l b{ font-weight:600; }
.sf-card .l span:last-child{ color:var(--sf-dim); font-variant-numeric:tabular-nums; }
.sf-bar{ display:block; height:3px; margin:8px 0 7px; border-radius:2px;
  background:var(--sf-rule); overflow:hidden; }
.sf-bar i{ display:block; height:100%; background:var(--hue, var(--sf-ink));
  width:calc(var(--p, 0) * 100%); transition:width .3s ease; }
.sf-bar.indeterminate i{ width:36%; animation:sfSlide 1.5s ease-in-out infinite; }
@keyframes sfSlide{ 0%{ margin-left:-36% } 100%{ margin-left:100% } }
.sf-st{ font:10px/1.3 var(--sf-ui); color:var(--sf-dim); }
.sf-lnk{ background:none; border:0; padding:0; font:inherit; color:var(--sf-dim);
  text-decoration:underline; text-underline-offset:2px; cursor:pointer; }
.sf-lnk:hover{ color:var(--sf-ink); }
.sf-lnk[disabled]{ opacity:.45; cursor:default; text-decoration:none; }

.sf-q{ display:flex; align-items:center; gap:7px; padding:6px 0;
  font:10.5px/1.3 var(--sf-ui); color:var(--sf-ink); border-bottom:1px solid var(--sf-rule); }
.sf-q .qn{ flex:1 1 auto; min-width:0; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap; }
.sf-q .qv{ flex:0 0 auto; color:var(--sf-dim); }
.sf-q .qx{ flex:0 0 auto; background:none; border:0; padding:2px; color:var(--sf-dim);
  cursor:pointer; opacity:0; transition:opacity .12s ease; }
.sf-q:hover .qx{ opacity:1; }
.sf-q .qx:hover{ color:var(--sf-ink); }

/* the grid: one line per chapter, its eight cells at the right */
.sf-grid{ display:block; }
.sf-gr{ display:flex; align-items:center; gap:8px; padding:5px 0;
  font:10.5px/1.3 var(--sf-ui); color:var(--sf-ink); border-bottom:1px solid var(--sf-rule); }
.sf-gr .gid{ flex:0 0 auto; color:var(--sf-dim); font-variant-numeric:tabular-nums; }
.sf-gr .gt{ flex:1 1 auto; min-width:0; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap; }

.sf-note{ display:block; padding:8px 0; font:10.5px/1.45 var(--sf-ui); color:var(--sf-dim); }

/* ======================= VIDEO, AND THE WEB ============================= */
/* The two lanes that are not books keep their place -- at the foot of the one
   list rather than in panes of their own. Video is CLOSED until asked: the
   hole is 0 high, and a box under a pixel is `None` to `videos_rect`, so the
   shell's second webview is hidden without this page saying anything. */
.sf-video{ height:0; overflow:hidden; transition:height .2s ease;
  border-radius:6px; background:transparent; }
.sf-video.open{ height:var(--sf-video); }
.sf-video iframe{ display:block; width:100%; height:var(--sf-video); border:0; }
.sf-row .chev{ display:inline-block; width:7px; height:7px; margin-left:2px;
  border-right:1.2px solid var(--sf-dim); border-bottom:1.2px solid var(--sf-dim);
  transform:rotate(45deg) translate(-1px,-1px); transition:transform .2s ease; }
.sf-row.open .chev{ transform:rotate(-135deg) translate(-2px,-2px); }
.sf-row .q{ display:block; margin-top:.45em; font:var(--sf-meta)/1.4 var(--sf-ui);
  letter-spacing:0; text-transform:none; color:var(--sf-dim); white-space:normal;
  overflow-wrap:anywhere; }

/* ============================== THE PHONE =============================== */
/* One column, the whole screen. The works is not a second column here -- it
   is a band at the TOP of the one column, and only when something runs,
   because that is the only time it beats a result for the room. */
@media (max-width:720px), (pointer:coarse){
  .sf{ left:0; top:0; width:100%; max-width:none; height:100%;
       transform:translate(0, 10px); border-radius:0; border:0;
       padding-top:env(safe-area-inset-top, 0px); }
  .sf.on{ transform:translate(0,0); }
  .sf-cols{ flex-direction:column; }
  .sf-list{ flex:1 1 auto; order:2; }
  .sf-works{ flex:0 0 auto; order:1; border-left:0;
             border-bottom:1px solid var(--sf-rule); max-height:38vh; }
  .sf-works.empty{ display:none; }
  :root{ --sf-title:17px; --sf-meta:10.5px; --sf-row:56px; --sf-field:19px; }
}


/* ==========================================================================
   8 SEPTEMBER — WHAT EACH SOURCE DID, THE ELSEWHERE HITS, AND A COLUMN THAT
   ACTS. Nothing here changes a row's geometry: the gutter, the spine and the
   title's left are untouched, because `titleLefts` being a set of size ONE is
   the invariant the whole list rests on and every new row goes through the
   same `rowShell()`.
   ====================================================================== */

/* --- the per-source report, AT THE TOP OF RESULTS (G-SURF2, 10 Sep) -----
   It was a row of chips under the list, drawn once the answer was in, and
   while the search was out the page had one grey "Searching…" note -- which
   a redraw a microtask later took away. Now it is the first thing under the
   Results head for as long as there is a query: ASKING while `/search` is
   out (every source by name, the seconds, a bar filling against studio's
   25 s), ANSWERED after (what each source did). Same element both times, so
   nothing jumps when the answer lands.

   WHILE ASKING IT IS STICKY AT BOTH EDGES -- `top` AND `bottom` -- so a long
   WORKING band above it cannot push it off either end of the list: scrolled
   down, it holds the top; pushed below the fold, it holds the bottom. A wait
   you cannot see is the thing this exists to end. Opaque there, because rows
   pass under it. */
.sf-src{ display:flex; flex-wrap:wrap; align-items:center; gap:5px;
         padding:8px 0 10px; border-bottom:1px solid var(--sf-rule); }
.sf-srchead{ flex:1 0 100%; display:flex; align-items:baseline; gap:10px; min-width:0;
  font:11px/1.4 var(--sf-ui); color:var(--sf-dim); }
.sf-srchead .hl{ flex:1 1 auto; min-width:0; overflow-wrap:anywhere; }
.sf-srchead .ck{ flex:0 0 auto; font-variant-numeric:tabular-nums; }
.sf-src.asking{ position:sticky; top:0; bottom:0; z-index:2;
                background:var(--sf-ground); }
.sf-src.asking .sf-srchead{ font:600 13px/1.35 var(--sf-ui); color:var(--sf-ink); }
.sf-src.asking .sf-srchead .ck{ font-weight:400; color:var(--sf-dim); }
.sf-srcbar{ flex:1 0 100%; display:block; height:2px; margin:1px 0 3px;
  border-radius:1px; background:var(--sf-rule); overflow:hidden; }
.sf-srcbar i{ display:block; height:100%; width:0; background:var(--sf-ink);
  transition:width .5s linear; }
.sf-srcsub{ flex:1 0 100%; padding-top:2px; font:10.5px/1.45 var(--sf-ui);
  color:var(--sf-dim); }
/* asked, not yet answered: dashed, with a dot that breathes on the step
   cells' own clock -- present and plainly unfinished */
.sf-chip.s-ask{ border-style:dashed; }
.sf-chip.s-ask i::before{ content:""; display:inline-block; width:5px; height:5px;
  margin-right:4px; vertical-align:1px; border-radius:50%; background:currentColor;
  animation:sfPulse 1.1s ease-in-out infinite; }
.sf-chip{ display:inline-flex; align-items:baseline; gap:5px; cursor:default;
          padding:2px 7px; border-radius:10px; border:1px solid var(--sf-rule);
          font:var(--sf-meta)/1.5 var(--sf-ui); color:var(--sf-dim); }
.sf-chip b{ font-weight:600; color:var(--sf-ink); }
.sf-chip i{ font-style:normal; font-variant-numeric:tabular-nums; }
/* the four words, and only two of them are bad news */
.sf-chip.s-n{ border-color:color-mix(in srgb, var(--sf-ink) 30%, transparent); }
.sf-chip.s-n i{ color:var(--sf-ink); }
.sf-chip.s-down, .sf-chip.s-failed{ cursor:pointer;
  border-color:#a8532a; color:#a8532a; }
.sf-chip.s-down b, .sf-chip.s-failed b{ color:#a8532a; }
.sf-chip.s-off{ cursor:pointer; opacity:.72; }
.sf-chip.s-nothing{ opacity:.62; }
.sf-chip.s-drop{ opacity:.72; }
/* SURFACE-PRESS (26 Sep): a chip with rows is a FILTER -- pointer, and the
   one that is on is filled; `filtered` too. A still-running source is the
   fifth word: its rows so far, and a breathing "still asking" after them. */
.sf-chip.s-n, .sf-chip.s-drop, .sf-chip.s-still{ cursor:pointer; }
.sf-chip.on{ background:var(--sf-ink); border-color:var(--sf-ink); color:var(--sf-ground); }
.sf-chip.on b, .sf-chip.on i{ color:var(--sf-ground); }
.sf-chip.s-still{ border-style:dashed;
  border-color:color-mix(in srgb, var(--sf-ink) 30%, transparent); }
.sf-chip.s-still i:first-of-type{ color:var(--sf-ink); }
.sf-chip.s-still i.still::before{ content:""; display:inline-block; width:5px; height:5px;
  margin-right:4px; vertical-align:1px; border-radius:50%; background:currentColor;
  animation:sfPulse 1.1s ease-in-out infinite; }
.sf-chip.s-rejoined::after{ content:"↺"; font-size:.9em; opacity:.7; }
.sf-srcwhy{ flex:1 0 100%; padding:5px 0 0; color:var(--sf-dim);
            font:var(--sf-meta)/1.5 var(--sf-ui); }
/* the sentence under a row: why it was dropped, why it has no Add */
.sf-row .sf-why{ display:block; padding-top:2px; font:var(--sf-meta)/1.45 var(--sf-ui);
  color:var(--sf-dim); overflow-wrap:anywhere; }
.sf-row .sf-borrow{ color:#a8532a; }
.sf-row .sf-open-ptr{ display:block; padding-top:2px; font:var(--sf-meta)/1.45 var(--sf-ui);
  color:var(--sf-dim); }
.sf-row .sf-open-ptr i{ font-style:normal; }
.sf-row.sf-flash{ background:color-mix(in srgb, var(--sf-ink) 10%, transparent); }

/* --- a hit out there is still a row, and it still lines up --------------- */
.sf-row.sf-hit{ cursor:pointer; }
/* folded into `.sf-row:not(.sf-query):hover` above (8 Sep, §5.8) */
.sf-row.sf-query .t{ color:var(--sf-dim); }
.sf-row.sf-find{ cursor:pointer; }
/* folded into `.sf-row:not(.sf-query):hover` above (8 Sep, §5.8) */
.sf-row.sf-ch .spine{ height:calc(100% - 22px); opacity:.8; }
.sf-row .end .sf-steps{ margin-right:6px; }
/* the second verb on a row: present, and plainly the quieter of the two */
.sf-verb.ghost{ background:transparent; border:1px solid var(--sf-rule); color:var(--sf-dim); }
.sf-verb.ghost:hover{ color:var(--sf-ink); }

/* --- the works column, acting ------------------------------------------- */
.sf-acts{ display:flex; flex-wrap:wrap; gap:6px; padding:8px 0 2px; }
.sf-acts .sf-verb{ flex:0 0 auto; }
.sf-q.pick{ cursor:pointer; }
.sf-q.pick:hover{ background:rgba(127,127,127,.07); }
.sf-gr.pick{ cursor:pointer; }
.sf-gr.pick:hover{ background:rgba(127,127,127,.07); }
.sf-gr .sf-verb{ flex:0 0 auto; padding:1px 6px; font-size:9.5px; }
/* G-RUN: the row's own small verb -- the surgical door, never a second bar */
.sf-gr .sf-verb.sf-cell{ font-size:8.5px; letter-spacing:.06em; padding:2px 7px; }
.sf-runrow{ padding:6px 0 4px; }
.sf-runrow .sf-run{ max-width:100%; white-space:normal; text-align:left; line-height:1.35; }
.sf-log{ border-top:1px solid var(--sf-rule); margin-top:8px; }
.sf-logtext{ margin:0; padding:6px 0 10px; max-height:220px; overflow:auto;
             white-space:pre-wrap; word-break:break-word;
             font:10px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace;
             color:var(--sf-dim); }

/* ==========================================================================
   8 SEPTEMBER, §4 — THE WORKS IS THE FULL STUDIO
   Four choices above the step grid: the cloned voice, WHERE it runs, the
   model, and the thirty seconds that proves all three. `studio/studio.html`'s
   own rows at this width — a label on the left, pills on the right, one
   sub-line under them. Nothing here touches `.sf-row`: the gutter, the spine
   and the title's left are untouched, so `titleLefts` being a set of size ONE
   is exactly as true as it was before this landed.

   A PILL THAT CANNOT BE PICKED IS DASHED, NEVER ABSENT (Osca, 8 Sep). It is a
   <span class="off">, not a button, because the server would refuse to write
   it — and the dashes are the whole visual difference, so an unusable Kaggle
   and an unusable engine read as the same kind of fact.
   ========================================================================== */
.sf-studio{ display:block; padding:2px 0 4px; }

.sf-prow{ display:flex; align-items:flex-start; gap:10px; padding:7px 0;
  border-bottom:1px solid var(--sf-rule); }
.sf-prow:last-child{ border-bottom:0; }
.sf-prow .pl{ flex:0 0 54px; padding-top:4px;
  font:600 var(--sf-head)/1.2 var(--sf-ui); letter-spacing:.12em;
  text-transform:uppercase; color:var(--sf-dim); }
.sf-prow .pc{ flex:1 1 auto; min-width:0; }

.sf-pills{ display:flex; flex-wrap:wrap; gap:5px; }
.sf-pill{
  font:10.5px/1.25 var(--sf-ui); color:var(--sf-ink);
  background:transparent; border:1px solid var(--sf-rule); border-radius:999px;
  padding:3px 9px; cursor:pointer; white-space:nowrap;
  display:inline-flex; align-items:center; gap:5px;
}
.sf-pill:hover{ border-color:var(--sf-ink); }
.sf-pill[aria-selected="true"]{ background:var(--sf-ink); color:var(--sf-ground);
  border-color:var(--sf-ink); }
.sf-pill[disabled]{ opacity:.45; cursor:default; }
/* the dashed one: present, plainly not a choice, and carrying its reason */
.sf-pill.off{ border-style:dashed; color:var(--sf-dim); cursor:default;
  white-space:normal; max-width:100%; }
.sf-pill.imp{ border-style:dashed; color:var(--sf-dim); }
.sf-pill.imp:hover{ color:var(--sf-ink); border-color:var(--sf-ink); }
/* the reference's play/pause, INSIDE the selected pill and nothing else moves */
.sf-pill .pp{ font-style:normal; font-size:9px; line-height:1; cursor:pointer;
  opacity:.8; }
.sf-pill .pp:hover{ opacity:1; }

.sf-vsub{ display:block; }
.sf-sub{ display:block; padding:5px 0 0; font:10.5px/1.45 var(--sf-ui); color:var(--sf-dim); }
.sf-sub b{ color:var(--sf-ink); font-weight:600; }
.sf-refslot{ display:block; }
.sf-refslot audio{ display:none; }   /* the pill's glyph IS the control */

/* `voice/port.py::limits()`, verbatim, ABOVE the GPU-minutes on purpose */
.sf-limits{ display:block; padding:6px 0 0; font:10px/1.5 var(--sf-ui); color:var(--sf-dim); }
.sf-limits b{ display:block; color:var(--sf-ink); font-weight:600; }
.sf-limits span{ display:block; }

.sf-sample{ display:block; }
.sf-sample audio{ display:block; width:100%; margin-top:8px; height:30px; }
.sf-est{ display:flex; flex-wrap:wrap; gap:4px 16px; padding:6px 0 0;
  font:10.5px/1.4 var(--sf-ui); color:var(--sf-ink); }
.sf-est .k{ color:var(--sf-dim); }
.sf-est b{ font-weight:600; font-variant-numeric:tabular-nums; }

/* one column: the label stacks over its pills rather than eating the width */
@media (max-width:720px), (pointer:coarse){
  .sf-prow{ flex-direction:column; gap:4px; }
  .sf-prow .pl{ flex:0 0 auto; padding-top:0; }
}

/* --- the addendum: a multi-file item, answered here --------------------- */
/* `stage_for_ingest` answered `{status:"choose", files:[…]}` — IA's own file
   list. It is the queue's own row shape, because it is the same kind of thing:
   a short list of names you press one of. */
.sf-choose{ display:block; border-bottom:1px solid var(--sf-rule); padding-bottom:6px; }
.sf-choose .sf-q .qn{ font-variant-numeric:tabular-nums; }


/* ==========================================================================
   8 SEPTEMBER, §5.8 — WHAT A RESULT IS, WHEN IT IS NOT A BOOK YET
   Clicking a fetchable result or a recording cannot fill the step grid: there
   are no steps, because there is no book. So THE WORKS answers the question
   the click asked — what IS this — out of the candidate's own fields, and
   puts the verbs that can act on it in the same place.
   ========================================================================== */
.sf-cand{ display:block; border-bottom:1px solid var(--sf-rule); padding-bottom:8px; }
.sf-cand .ct{ font:600 13px/1.35 var(--sf-ui); color:var(--sf-ink); padding:2px 0 6px; }
.sf-fields{ display:grid; grid-template-columns:auto 1fr; gap:3px 10px;
  font:10.5px/1.4 var(--sf-ui); }
.sf-fields .k{ color:var(--sf-dim); white-space:nowrap; }
.sf-fields .v{ color:var(--sf-ink); min-width:0; overflow-wrap:anywhere; }
.sf-url{ display:block; padding:7px 0 0; font:10px/1.4 ui-monospace, SFMono-Regular,
  Menlo, monospace; color:var(--sf-dim); overflow-wrap:anywhere; }


/* ==========================================================================
   14 SEPTEMBER, SURFACE STAGE 1 — EVERY RENDER, ALL BOOKS
   `studio.html::panelQueue` was a modal over the host's own front page. It is
   a band at the top of THE WORKS now: the machine, above the book, shut until
   it is asked for. Its head borrows `.sf-workhead`'s layout for the reason
   the two are the same kind of thing — a list title with the controls that
   belong to the LIST rather than to any row in it.
   ========================================================================== */
.sf-all{ display:block; border-bottom:1px solid var(--sf-rule); padding-bottom:6px; }
.sf-h.sf-allhead{ display:flex; align-items:baseline; gap:8px; }
.sf-h.sf-allhead .wl{ flex:1 1 auto; }
.sf-h.sf-allhead .sf-verb{ flex:0 0 auto; }
/* A GROUP IS NOT A BOOK ON A SHELF, and the row says so by weight rather
   than by a badge — the spine rule this surface keeps (told apart by the
   spine, never by a badge and never by being in two different lanes). */
.sf-row.r-all .t{ font-weight:500; }
.sf-row.r-all .s{ font-variant-numeric:tabular-nums; }
/* the three states a person scans for: out now, finished, and gone wrong */
.sf-row.r-all.rfailed .spine, .sf-row.r-all.rstopped .spine{ opacity:.55; }
.sf-row.r-all.rvoiced .spine, .sf-row.r-all.rpulled .spine{ opacity:.75; }
.sf-row.r-all.rheld .spine{ opacity:.45; }
/* A CHAPTER WITH AUDIO AND NO WORD TIMINGS. It is a fact, not a failure, so
   it is said in the row's own voice rather than in the red one -- and the
   ribbon's align cell says the same, or the row would give two answers. */
.sf-gr .sf-notime{ flex:0 0 auto; color:#a8532a; font-style:normal; }

/* ==========================================================================
   14 SEPTEMBER — ON THE DRIVE, THE THIRD STATE A ROW CAN BE IN
   `studio.html::panelSources` was a modal list of buttons. A file in
   TTS_DATA/sources/ is neither yours (no book) nor out there (nothing to
   fetch): it is here, and one press parses it in place. It is told apart the
   way every other row is — by the spine, never by a badge.
   ========================================================================== */
.sf-row.r-drive .spine{ opacity:.6; }
.sf-row.r-drive .t{ font-weight:500; }
.sf-row.r-drive .sf-prov{ font:10px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; }


/* ==========================================================================
   G-DROPDOWN (16 Sep) — THE COLUMN IS HEADED BY THE BOOK; THE PICKERS ARE
   DROPDOWNS. Osca: "drop-downs in the selectors ... let me scroll through
   them, if large ... Select in the bottom right, then when selected, just
   show those on the studio side." One primitive (`dropdown()` in surface.js)
   draws Voice, Language, Where and Model: a shut FACE naming the choice, a
   list that scrolls at nine rows, radio or checkbox rows, Select bottom
   right. A greyed row is the dashed pill's rule kept: present, plainly not a
   choice, carrying its reason.
   ========================================================================== */
/* the head: the selected book's title, the author under it in its hue */
.sf-h.sf-subj{
  text-transform:none; letter-spacing:-.02em;
  font:600 15px/1.25 var(--sf-serif); color:var(--sf-ink);
  padding:6px 0 0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.sf-subja{ display:block; padding:2px 0 8px; font:10.5px/1.3 var(--sf-ui);
  color:var(--sf-dim); border-bottom:1px solid var(--sf-rule); }
.sf-h.sf-subj + .sf-note{ padding-top:6px; }

.sf-ddrow{ display:flex; align-items:center; gap:6px; min-width:0; }
.sf-ddrow .sf-dd{ flex:1 1 auto; min-width:0; }
/* the reference's play/pause, BESIDE the face, for the chosen voice only */
.sf-pp{ flex:0 0 auto; font:11px/1 var(--sf-ui); color:var(--sf-ink);
  background:transparent; border:1px solid var(--sf-rule); border-radius:999px;
  width:22px; height:22px; cursor:pointer; padding:0; }
.sf-pp:hover{ border-color:var(--sf-ink); }

.sf-dd{ position:relative; display:block; min-width:0; }
.sf-ddface{
  display:flex; align-items:center; gap:8px; width:100%; min-width:0;
  font:11px/1.25 var(--sf-ui); color:var(--sf-ink); text-align:left;
  background:transparent; border:1px solid var(--sf-rule); border-radius:8px;
  padding:5px 9px; cursor:pointer;
}
.sf-ddface:hover{ border-color:var(--sf-ink); }
.sf-ddface[aria-expanded="true"]{ border-color:var(--sf-ink); }
.sf-ddface[disabled]{ opacity:.55; cursor:default; }
.sf-ddface .fx{ flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.sf-ddface .cx{ flex:0 0 auto; font-size:9px; color:var(--sf-dim); }

/* the open list: over the column, not in its flow, so the rows below do not
   jump when it opens; nine rows tall then it scrolls */
.sf-ddpop{
  position:absolute; left:0; right:0; top:calc(100% + 3px); z-index:5;
  background:var(--sf-sheet, var(--sf-ground)); border:1px solid var(--sf-rule);
  border-radius:8px; box-shadow:0 8px 24px rgba(0,0,0,.14);
  overflow:hidden;
}
.sf-ddpop[hidden]{ display:none; }
.sf-ddlist{ display:block; max-height:234px; overflow:auto; overscroll-behavior:contain;
  padding:3px 0; outline:none; }
.sf-opt{
  display:flex; align-items:baseline; gap:8px; width:100%; min-width:0;
  font:11px/1.3 var(--sf-ui); color:var(--sf-ink); text-align:left;
  background:transparent; border:0; padding:5px 9px; cursor:pointer;
}
.sf-opt:hover, .sf-opt.focus{ background:hsl(var(--sf-ground-hsl) / .5); box-shadow:inset 3px 0 0 var(--sf-rule); }
.sf-opt.focus{ box-shadow:inset 3px 0 0 var(--sf-ink); }
.sf-opt .tk{ flex:0 0 12px; font-style:normal; font-size:10px; color:var(--sf-dim); }
.sf-opt[aria-selected="true"] .tk{ color:var(--sf-ink); }
.sf-opt[aria-selected="true"] .tx{ font-weight:600; }
.sf-opt .tx{ flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.sf-opt .sx{ flex:0 0 auto; font-size:10px; color:var(--sf-dim); font-variant-numeric:tabular-nums; }
.sf-opt[disabled]{ opacity:.45; cursor:default; }
/* G-RUN: an action row (All / None) -- it rewrites the ticks, it is not one */
.sf-opt.act{ padding-left:29px; color:var(--sf-dim); text-transform:uppercase;
  font-size:9.5px; letter-spacing:.08em; }
.sf-opt.act:hover, .sf-opt.act.focus{ color:var(--sf-ink); }
/* the greyed row: not a choice, and it says why in its title */
.sf-opt.off{ color:var(--sf-dim); cursor:default; white-space:normal; padding-left:29px; }
.sf-ddfoot{ display:flex; align-items:center; justify-content:flex-end; gap:8px;
  padding:5px 7px; border-top:1px solid var(--sf-rule); }
.sf-ddfoot .hx{ flex:1 1 auto; font:9.5px/1.3 var(--sf-ui); color:var(--sf-dim); }
.sf-ddfoot .sf-ddsel{ flex:0 0 auto; }

/* the model's cost note: shut to its first line, "more ▸" opens it in place */
.sf-limits{ position:relative; }
.sf-limits.shut span:nth-of-type(n+2){ display:none; }
.sf-more{ display:inline-block; margin-top:2px; padding:0; border:0; background:transparent;
  font:10px/1.5 var(--sf-ui); color:var(--sf-dim); cursor:pointer; text-decoration:underline dotted; }
.sf-more:hover{ color:var(--sf-ink); }

/* the phone: the same dropdown, full width, the list in the flow so a thumb
   can scroll it without the sheet scrolling underneath */
@media (max-width:720px), (pointer:coarse){
  .sf-ddpop{ position:static; box-shadow:none; margin-top:4px; }
  .sf-ddlist{ max-height:min(234px, 40vh); }
  .sf-opt{ padding:8px 10px; font-size:12px; }
  .sf-ddface{ padding:8px 10px; font-size:12px; }
}
/* ===================================================== G-SET: chips + verb row */
.sf-chips{ display:flex; flex-wrap:wrap; gap:6px; padding:4px 0 8px; }
.sf-chips .sf-chip{ display:inline-flex; align-items:center; gap:4px;
  padding:3px 8px; border-radius:4px; font-size:12px; cursor:default;
  background:color-mix(in srgb, var(--hue, #888) 18%, transparent);
  border:1px solid color-mix(in srgb, var(--hue, #888) 30%, transparent); }
.sf-chipx{ all:unset; cursor:pointer; font-size:14px; line-height:1; opacity:.6;
  padding:0 2px; }
.sf-chipx:hover{ opacity:1; }
.sf-setverbs{ padding:8px 0; display:flex; flex-wrap:wrap; gap:6px; }

/* ============================================================ SOURCE-DOOR
   (D9, 26 Sep) the origin half of a provenance line is a BUTTON when there is
   an origin: same type, same colour, underlined on hover so it reads as the
   door it is. The band above the list is the one row the door opens on. */
.sf-prov .po.sf-door{ background:none; border:0; padding:0; margin:0; font:inherit;
  color:inherit; cursor:pointer; text-align:left; text-decoration:underline dotted; }
.sf-prov .po.sf-door:hover{ color:var(--sf-ink); text-decoration:underline; }
.sf-doorband{ margin:0 0 6px; padding:0 0 6px; border-bottom:1px solid var(--sf-rule); }
.sf-doorband .sf-row.r-door{ border-left:2px solid var(--sf-ink); }
.sf-provpane .sf-fate{ padding-top:6px; font-size:11px; line-height:1.4; color:var(--sf-dim); }
