/* Contract: contracts/web/rules.md
 * header tools, project tabs, chips, endless scroll, pull to refresh
 *
 * Part of what was style.css — 1700 lines in one file. The parts load in a fixed
 * order and MUST stay in it: the cascade is decided by source order and
 * specificity exactly as it was, because this is a split and not a rewrite.
 *
 * There are deliberately no @layer rules. Layering is the tidier shape and it
 * would have changed behaviour — a later layer beats an earlier one regardless
 * of specificity, and this stylesheet has at least one pair, `body.app > .bar`
 * written above `.bar`, where the MORE specific rule comes first and wins on
 * specificity. Putting those two in different layers silently reverses them.
 * Splitting the file is worth doing on its own; changing the cascade is a
 * separate decision that wants somebody looking at the screen.
 */
/* ---------- header tools ---------- */
.brand {
  background: none; border: 0; padding: 0; cursor: pointer;
  color: inherit; font: inherit;
}
.brand:hover h1 { color: var(--accent); }

.headtools { display: flex; align-items: center; gap: 6px; flex: none; }

.iconbtn {
  display: inline-flex; align-items: center; gap: 5px;
  height: 32px; padding: 0 10px;
  background: transparent; color: var(--ink-dim);
  border: 1px solid var(--line); border-radius: 8px;
  font: inherit; font-size: 13px; cursor: pointer;
}
.iconbtn:hover { color: var(--ink); border-color: var(--ink-dim); }
.iconbtn .ico { font-size: 14px; line-height: 1; }
.iconbtn.named { max-width: 130px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.iconbtn.warnish { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 45%, transparent); }
.iconbtn[hidden] { display: none; }

/* The sync button turns while the back end is working, which is the whole
   point of it: the state of the queue is otherwise invisible. */
@keyframes spin { to { transform: rotate(360deg); } }
#syncbtn.spinning .ico { display: inline-block; animation: spin 1.1s linear infinite; }

.menuwrap { position: relative; }
/* The menu sizes to its longest item, which is fine until the item is a German
   sentence: "Check for anything waiting to be filed" becomes "Nach allem suchen,
   was noch abgelegt werden muss", and an unbounded menu takes that width
   straight off the side of a phone. Measured at 390px it reached 444px, so the
   last thing on it — Sign out — was off the screen with no way to scroll to it.
   Capped at the window, so a long line wraps inside the menu instead of moving
   the menu. See I18N-DESIGN.md, phase 3. */
.menu {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 40;
  min-width: 210px; max-width: min(340px, calc(100vw - 24px)); padding: 5px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 12px 34px rgba(0,0,0,.32);
}
.menu[hidden] { display: none; }
.menuname { padding: 7px 10px 5px; font-size: 12px; color: var(--ink-dim); }
.menuitem {
  display: block; width: 100%; text-align: left; padding: 7px 10px;
  background: none; border: 0; border-radius: 6px;
  color: var(--ink); font: inherit; font-size: 13px; cursor: pointer;
}
.menuitem:hover { background: var(--panel-2); }
.menuitem[hidden] { display: none; }
/* The language picker is a row in the same menu, but it is a control rather
   than a command: it says what it is on the left and takes the choice on the
   right, so it cannot be mistaken for something that happens when clicked. */
.menuselect { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.menuselect select {
  font: inherit; font-size: 13px; padding: 2px 4px;
  background: var(--panel-2); color: var(--ink);
  border: 1px solid var(--line); border-radius: 5px;
  /* "Diesem Gerät folgen" is half again as wide as "Follow this device", and a
     select sizes to its widest option. Without this it sets the width of the
     whole menu on its own. */
  max-width: 55%;
}
.menu hr { border: 0; border-top: 1px solid var(--line); margin: 5px 2px; }

/* The property tabs were here — a sticky strip of pills under the top bar with
   its own horizontal scroller and a dashed "+ Project" pinned to its right. The
   whole thing is a list of rows in the sidebar now, and a wall of pictures
   behind All projects; see .frow.project and .projectcard below. */

.filters[hidden] { display: none; }

/* ---------- card chips ---------- */
.chips {
  position: absolute; right: 7px; top: 7px;
  display: flex; gap: 4px; pointer-events: none;
}
.chip-type, .chip-size {
  padding: 2px 6px; border-radius: 5px;
  font-size: 10px; font-weight: 700; letter-spacing: .03em;
  background: color-mix(in srgb, var(--panel) 82%, transparent);
  color: var(--ink-dim); backdrop-filter: blur(6px);
}
.chip-type { color: var(--ink); }
.chip-size { font-weight: 600; font-variant-numeric: tabular-nums; }

/* ---------- endless scroll + live arrivals ---------- */
.gridmore { display: flex; justify-content: center; padding: 8px 0 48px; }
.gridmore[hidden] { display: none; }
.spinner {
  width: 18px; height: 18px; border-radius: 50%;
  border: 2px solid var(--line); border-top-color: var(--accent);
  opacity: 0; transition: opacity .2s ease;
}
.gridmore.busy .spinner { opacity: 1; animation: spin .8s linear infinite; }

/* Never injected into a filtered grid — the server owns which rows match, so
   this offers the count and re-runs the real query when clicked. */
/* The sticky row both pills sit in. The stickiness moved here from .newpill
   when a second pill arrived: two siblings both stuck to top:10px sit on top of
   each other as soon as the column scrolls, and "3 under way" hidden behind
   "1 new file filed" is the one arrangement that makes both useless.

   No padding and no margin of its own — an empty row must take up no space at
   all, and both children are hidden most of the time. */
.gridpills {
  position: sticky; top: 10px; z-index: 18;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  /* The row spans the column so its children can centre in it, which would
     otherwise swallow clicks aimed at the cards underneath. */
  pointer-events: none;
}
.gridpills > * { pointer-events: auto; }

.newpill {
  display: block; margin: 10px auto 0; padding: 7px 16px;
  background: var(--accent); color: #fff; border: 0; border-radius: 999px;
  font: inherit; font-size: 12.5px; font-weight: 560; cursor: pointer;
  box-shadow: 0 6px 18px rgba(0,0,0,.28);
}
.newpill[hidden] { display: none; }
/* Not the accent. The accent pill is news — something has arrived and there is
   something to do about it. This one is the archive saying it is busy, which is
   worth knowing and not worth interrupting anybody for, so it reads as chrome
   rather than as a notification. */
.newpill.making {
  background: var(--panel); color: var(--ink);
  border: 1px solid var(--line);
  box-shadow: 0 4px 12px rgba(0,0,0,.18); font-weight: 500;
}
/* Both up at once: the news sits under the status, because the news is the one
   somebody is going to click. Nearest the grid is nearest the thumb. */
.gridpills > .making + .newpill { margin-top: 0; }

/* ---------- pull to refresh ----------
   The pill above is for arrivals the page was told about. This is for
   everything else, on the one surface with no reload button of its own: the
   archive installed as an app on a phone. See web/pull-refresh.js.

   Sits above the top edge and is dragged into view, so at rest it is invisible
   without needing to be hidden — which keeps it out of the way of the sticky
   pill it shares this corner with. */
.ptr {
  position: absolute; top: -44px; left: 50%; z-index: 19;
  display: grid; place-items: center;
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--panel); border: 1px solid var(--line);
  box-shadow: 0 4px 14px rgba(0,0,0,.24);
  opacity: 0; pointer-events: none;
}
.ptr-fixed { position: fixed; }
/* Only on the way back up: following the finger has to be immediate. */
.ptr-settling { transition: transform .22s ease, opacity .22s ease; }

.ptr-spinner {
  width: 16px; height: 16px; border-radius: 50%;
  border: 2px solid var(--line); border-top-color: var(--accent);
  transition: transform .2s ease;
}
/* Past the threshold, before letting go: the one moment the gesture can still
   be called off, so it says "yes, this will refresh" rather than only spinning. */
.ptr-ready { border-color: var(--accent); }
.ptr-ready .ptr-spinner { transform: rotate(180deg); }
.ptr-busy .ptr-spinner { animation: spin .8s linear infinite; }

/* ============================================================
   Chip filters, the project bar's add button, and folders.
   ============================================================ */

/* ---------- chip filter groups ----------
   Replacing two <select>s. A dropdown shows one value and hides the vocabulary,
   which made "narrow to images and video" a feature only someone who already
   knew about it would ever find. */
.chipgroup { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.chipgroup-label {
  font-size: 11px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--ink-dim); margin-right: 2px;
}
.chip-toggle {
  height: var(--control-h); padding: 0 14px;
  background: var(--panel); color: var(--ink-dim);
  border: 1px solid var(--line); border-radius: 999px;
  font: inherit; font-size: 13.5px; cursor: pointer; white-space: nowrap;
  transition: background .12s, color .12s, border-color .12s;
}
.chip-toggle:hover { color: var(--ink); border-color: var(--ink-dim); }
.chip-toggle.on {
  background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 560;
}
/* "All" is the resting state, not a choice you made — it reads as quieter. */
.chipgroup .chip-toggle:first-of-type.on {
  background: var(--panel-2); border-color: var(--ink-dim); color: var(--ink);
}
.chip-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------- the eye: files in use, or what has been archived ----------
   An icon button that is a toggle rather than a command, so it needs an "on"
   that reads at a glance next to a row of chips wearing the same accent. It
   borrows the chips' lit state rather than inventing a third look. */
.eyebtn {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--control-h); height: var(--control-h);
  background: var(--panel); color: var(--ink-dim);
  border: 1px solid var(--line); border-radius: 999px; cursor: pointer;
}
.eyebtn svg { width: 18px; height: 18px; }
.eyebtn:hover { color: var(--ink); border-color: var(--ink-dim); }
.eyebtn.on { background: var(--accent); border-color: var(--accent); color: #fff; }
.eyebtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------- the project wall ----------
   A grid of shows, in the space the grid of pictures normally has. The card is
   mostly its picture: the whole argument for this being a place rather than a
   dropdown is that a project is recognised faster than it is read. */
.projectview {
  min-width: 0; min-height: 0; overflow: hidden;
  flex-direction: column; background: var(--bg);
}
.projectview[hidden] { display: none; }
body.in-projects .projectview { display: flex; }
body.in-projects .results,
body.in-projects .requestview,
body.in-projects .workboard { display: none !important; }
body.in-projects .workspace { grid-template-columns: var(--sidebar-w) minmax(0, 1fr); }
body.in-projects .inspector { display: none; }
/* Nothing on this screen is an asset, so the row that narrows a search for one
   has nothing to act on. Same rule, same reason, as the two views in
   10-requests.css and 12-work-board.css. */
body.in-projects #filters,
body.in-projects .folderbar { display: none; }

.projectview-head {
  display: flex; align-items: center; gap: 18px; flex: none;
  padding: 16px 20px; border-bottom: 1px solid var(--line);
}
.projectview-head h2 { margin: 0; font-size: 17px; }
.projectview-head > div { margin-right: auto; min-width: 0; }
.projectcards {
  flex: 1; min-height: 0; overflow-y: auto;
  display: grid; gap: 14px; padding: 18px 20px 40px;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  align-content: start;
}
.projectcard {
  position: relative; display: flex; flex-direction: column;
  padding: 0; border: 1px solid var(--line); border-radius: 12px; overflow: hidden;
  background: var(--panel); color: var(--ink);
  font: inherit; text-align: left; cursor: pointer;
}
.projectcard:hover { border-color: var(--accent); }
.projectcard:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.projectcard.muted .projectcard-name { font-style: italic; color: var(--ink-dim); }
.projectcard-face {
  aspect-ratio: 4 / 3; background: var(--panel-2);
  display: grid; place-items: center; overflow: hidden;
}
.projectcard-face img { width: 100%; height: 100%; object-fit: cover; }
.projectcard-foot {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 12px; border-top: 1px solid var(--line);
}
.projectcard-name {
  flex: 1; min-width: 0; font-size: 13.5px; font-weight: 560;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* On the picture rather than beside the name: it is a decision about the show,
   not a fact about it, and the corner is the one place on this card that is
   never carrying information already. */
.projectpin {
  position: absolute; top: 8px; right: 8px;
  width: 30px; height: 30px; display: grid; place-items: center;
  border: 0; border-radius: 50%; cursor: pointer;
  background: rgba(20,20,26,.45); color: #fff; opacity: 0;
  transition: opacity .12s;
}
.projectcard:hover .projectpin, .projectpin.on, .projectpin:focus-visible { opacity: 1; }
.projectpin.on { background: var(--accent); }
.projectpin svg { width: 16px; height: 16px; }

/* ---------- header icon buttons ---------- */
.iconbtn .ico { display: inline-flex; }
/* A DRAWN icon needs a size given to it. Most `.ico` in this bar are a glyph in
   a <span>, which the font sizes; an <svg> carrying only a viewBox has no
   intrinsic size at all and collapses to nothing inside a flex row — visible as
   a button that is plainly there, plainly clickable, and plainly empty. Only
   .big set one, so every SVG icon button had to be a big one or be invisible. */
.iconbtn > svg.ico { width: 18px; height: 18px; }
.iconbtn.big { height: 36px; width: 36px; padding: 0; justify-content: center; }
/* Still well up from the 14px it started at. The sync control is the only thing
   in the bar that says whether the archive is busy, so it has to be legible at
   a glance and its spin has to read as motion — but at 26px it was crowding the
   account button beside it. */
.iconbtn.big .ico { width: 22px; height: 22px; }
#syncbtn.spinning .ico { animation: spin 1.1s linear infinite; }


/* The archive had to guess at a spelling.
   Beside the count rather than over the grid: it qualifies the answer, it does
   not replace it, and the rows underneath are worth looking at. Warm rather
   than red — nothing has gone wrong, the words were just not quite the ones on
   file. */
#near-matches.near {
  color: var(--warn);
  font-weight: 500;
}
