/* App-local styles for Stash.
 *
 * The app is built from @ecosystem/design components; what lives here is only
 * what is genuinely specific to a bookmark library — the card, its icon, and
 * the two shapes that card comes in.
 *
 * Per packages/design/README.md this file may consume semantic tokens but must
 * not redefine any .ds-* component, and must not use a raw value where a token
 * exists. The three exceptions below are the accent definitions themselves,
 * which is exactly the supported way for an app to feel distinct.
 */

/* This app's accent lives in @ecosystem/design — css/app-themes.css, keyed on
   the `data-app` slug the shell puts on <body>. It moved there when the accents
   started being DERIVED from each app's logo rather than chosen per app: the
   ramp, the contrast check and the logo are one decision now, and splitting
   them across six stylesheets is how they drift apart. Repointing a semantic
   token is still the supported extension point — that file is just where this
   particular repointing happens. */

/* --- The composer --------------------------------------------------------- */

.composer { display: flex; flex-direction: column; gap: var(--ds-space-3); }

/* The extra fields are collapsed by default because the common case is
   pasting a link and pressing Save — the page supplies the title, so asking
   for one up front would be asking for work the app is about to do anyway. */
.composer__more > summary {
  font-size: var(--ds-text-sm);
  color: var(--ds-text-muted);
  cursor: pointer;
  user-select: none;
}
.composer__more > summary:hover { color: var(--ds-text); }
.composer__fields { margin-top: var(--ds-space-3); }

/* --- The shelf -------------------------------------------------------------
   A shelf, not a reader. The row carries the reference — title, where it came
   from, how it is filed — and nothing else; see docs/strategy/product-shape.md
   for why filling the card with content was the wrong answer to "it looks
   toy-like" and stripping it was the right one.

   Built on the design system's .ds-list__* primitives, which are already
   "dense row with a title, a meta line and actions". What is here is only what
   they don't do: the two container shapes, the leading checkbox and icon, the
   unread rule, and holding the verbs back until you point at a row.
-------------------------------------------------------------------------- */

.list-head { min-height: 1.5rem; }

/* One row template, two containers. In list view the shelf is a single object
   with hairlines between rows (.ds-list__item supplies those, and drops the
   last one); in grid view the rows separate into their own bordered cells. */
.shelf--list {
  background: var(--ds-surface);
  border: var(--ds-border-width) solid var(--ds-border);
  border-radius: var(--ds-radius-lg);
}
/* Rounded on the rows rather than with overflow:hidden on the container: the
   ⋯ menu is positioned inside a row, and a clipping ancestor would cut it off
   at the shelf's edge. */
.shelf--list > .shelf__row:first-child {
  border-start-start-radius: var(--ds-radius-lg);
  border-start-end-radius: var(--ds-radius-lg);
}
.shelf--list > .shelf__row:last-child {
  border-end-start-radius: var(--ds-radius-lg);
  border-end-end-radius: var(--ds-radius-lg);
}
.shelf--list > .shelf__row:hover { background: var(--ds-surface-hover); }

/* auto-fill rather than auto-fit: with two bookmarks in a wide window,
   auto-fit stretches them to half the screen each, which looks like a bug. */
.shelf--grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(22rem, 1fr));
  gap: var(--ds-space-3);
}
/* The one place this file overrides anything the component set: a grid cell
   needs a border on all four sides where a list row needs one underneath, and
   it has a fifth of the width to spend, so the row's generous inline padding
   has to come in. Scoped to Stash's own class, so .ds-list__item still means
   what it means everywhere else. */
.shelf--grid > .shelf__row {
  padding-inline: var(--ds-space-4);
  background: var(--ds-surface);
  border: var(--ds-border-width) solid var(--ds-border);
  border-radius: var(--ds-radius-lg);
  transition: border-color var(--ds-duration-fast) var(--ds-ease);
}
.shelf--grid > .shelf__row:hover { border-color: var(--ds-border-strong); }

/* Positioned, so the ⋯ menu has something to hang off. */
.shelf__row { position: relative; }

/* Unread is marked with a rule down the leading edge rather than with colour
   alone, so it survives a colour-blind reader and a greyscale screen. An inset
   shadow rather than a border, so an unread row is not 3px narrower than the
   one above it. */
.shelf__row--unread { box-shadow: inset 3px 0 0 var(--ds-accent); }

/* Small and colourless. A tinted tile per row was what made this read as an
   app-icon grid rather than a library; the neutral tile behind the image
   stays, because a transparent favicon on a dark surface is invisible. */
.shelf__icon {
  flex: none;
  width: var(--ds-icon-sm);
  height: var(--ds-icon-sm);
  border-radius: var(--ds-radius-sm);
  object-fit: contain;
  background: var(--ds-surface-sunken);
}
.shelf__icon--letter {
  display: grid;
  place-items: center;
  color: var(--ds-text-subtle);
  font-size: var(--ds-text-xs);
  font-weight: var(--ds-weight-semibold);
  line-height: 1;
}

/* Left at .ds-list__title's own size on purpose. A denser shelf is tempting,
   but a 13px title over a 12px meta line is not a hierarchy — the quiet comes
   from removing ornament, not from shrinking the one thing you are reading. */
.shelf__title a { color: var(--ds-text); text-decoration: none; }
.shelf__title a:hover { color: var(--ds-accent); text-decoration: underline; }

/* Everything the row knows about a link, on one line, in reading order and
   separated by a middot supplied here rather than in the markup. Inline rather
   than flex on purpose: it is the inline layout that lets the whole line
   truncate with an ellipsis instead of clipping mid-word. */
.shelf__meta > * + *::before {
  content: "·";
  margin: 0 var(--ds-space-1);
  color: var(--ds-text-subtle);
}

/* Tags are filters, so they are buttons — but a row of pills is what this
   redesign removed, so they are set as the words they are. */
.shelf__tag {
  /* inline, not the button default of inline-block: the meta line's ellipsis
     only lands cleanly when everything on it is in one inline flow. */
  display: inline;
  border: 0;
  padding: 0;
  background: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.shelf__tag:hover { color: var(--ds-text); text-decoration: underline; }

/* Your own words, set apart from the page's — which is the whole reason they
   are separate columns. Full-strength text in a muted line does that without
   costing the row a second one. */
.shelf__notes { color: var(--ds-text); }

/* The verbs. Held back until hover on a device that has one, so a shelf of
   twenty bookmarks reads as twenty titles rather than sixty buttons — but
   always present for keyboard and touch, where there is no hover to wait for
   and a hidden control is simply a missing one.
   The slot is in the layout either way, so revealing it never moves the row
   under the cursor that revealed it. */
/* .ds-btn--sm is padded for a word; these hold a single glyph, and three of
   them at word-width is a slot wide enough to squeeze the title out of a grid
   cell. Narrowed here rather than promoted to a .ds-btn--icon variant: the
   system does arguably have that gap — the shell's menu button and the account
   app's power button live with the same padding — but it is a change to every
   app's chrome and does not belong in a Stash redesign. */
.shelf__action { padding-inline: var(--ds-space-2); }

/* The read toggle carries its state, because one glyph in two meanings is what
   an icon-only toggle gets wrong. Not the only signal for "unread" — that is
   the rule down the row's leading edge, which is a position rather than a
   colour — so this is the control agreeing with the row, not replacing it. */
.shelf__action[aria-pressed="true"] {
  color: var(--ds-text);
  background: var(--ds-surface-hover);
}

@media (hover: hover) {
  .shelf__actions {
    opacity: 0;
    transition: opacity var(--ds-duration-fast) var(--ds-ease);
  }
  .shelf__row:hover .shelf__actions,
  .shelf__row:focus-within .shelf__actions { opacity: 1; }

  /* Same trigger as the actions, deliberately: two reveals on one row that
     disagree about when to appear is worse than either choice. Outside the
     media query this used to hide the checkbox on touch devices entirely,
     where :hover never fires. */
  .shelf__select input { opacity: 0; }
  .shelf__row:hover .shelf__select input,
  .shelf__select input:focus-visible,
  .shelf__select input:checked { opacity: 1; }
}
.shelf__select { display: flex; align-items: center; flex: none; }

/* Edit and Delete: the two that are not one click and not reversible. Behind
   a menu, which is also what puts a second gesture in front of Delete. */
.shelf__more { position: relative; }
.shelf__more > summary { list-style: none; }
.shelf__more > summary::-webkit-details-marker { display: none; }
.shelf__more-menu {
  position: absolute;
  inset-inline-end: 0;
  top: calc(100% + var(--ds-space-1));
  z-index: var(--ds-z-sticky);
  min-width: 9rem;
  padding: var(--ds-space-1);
  background: var(--ds-surface);
  border: var(--ds-border-width) solid var(--ds-border);
  border-radius: var(--ds-radius-md);
  box-shadow: var(--ds-shadow-lg);
}
.shelf__more-item {
  display: flex;
  align-items: center;
  gap: var(--ds-space-2);
  width: 100%;
  padding: var(--ds-space-2) var(--ds-space-3);
  border: 0;
  border-radius: var(--ds-radius-sm);
  background: none;
  font: inherit;
  font-size: var(--ds-text-sm);
  color: var(--ds-text);
  text-align: start;
  cursor: pointer;
}
.shelf__more-item:hover { background: var(--ds-surface-hover); }
.shelf__delete:hover { color: var(--ds-danger); }

/* --- Chrome --------------------------------------------------------------- */

.view-toggle { display: flex; gap: 2px; }
.view-toggle [aria-pressed="true"] {
  background: var(--ds-accent-subtle);
  color: var(--ds-accent);
}

/* Search lives in the topbar, so it has to stay usable when the topbar is
   also holding a menu button and a title. Shrinks rather than pushing the
   title off the edge. */
.search { width: min(20rem, 35vw); }

@media (max-width: 48rem) {
  .view-toggle { display: none; }  /* one column at this width regardless */
}
@media (max-width: 30rem) {
  .search { width: 8rem; }
}

.ds-nav__empty {
  padding: var(--ds-space-1) var(--ds-space-2);
  margin: 0;
  font-size: var(--ds-text-xs);
  color: var(--ds-text-subtle);
}
.ds-nav__item--muted { color: var(--ds-text-muted); }

.toast {
  position: fixed;
  inset-inline: auto var(--ds-space-4);
  inset-block-end: var(--ds-space-4);
  z-index: var(--ds-z-toast);
  max-width: 28rem;
  box-shadow: var(--ds-shadow-lg);
}

/* --- Bulk selection --------------------------------------------------------
   The feature that separates a bookmark manager from a list: forty links get
   one tag in one gesture. See runBulk() in app.js and
   docs/strategy/competitive.md.
-------------------------------------------------------------------------- */

/* A rule down the leading edge rather than a filled amber panel. The bar only
   appears when something is selected, so it never needed to shout to be
   noticed — and a block of accent above a deliberately quiet shelf undoes the
   shelf. */
.bulk {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-2);
  padding: var(--ds-space-2) var(--ds-space-3);
  border: var(--ds-border-width) solid var(--ds-border);
  border-inline-start: 3px solid var(--ds-accent);
  border-radius: var(--ds-radius-md);
  background: var(--ds-surface);
  /* Follows the list rather than scrolling away with it: a selection made at
     the bottom of a long page must not need a scroll back up to act on. */
  position: sticky;
  top: var(--ds-space-2);
  z-index: var(--ds-z-sticky);
  box-shadow: var(--ds-shadow-sm);
}
.bulk__count {
  font-size: var(--ds-text-sm);
  font-weight: var(--ds-weight-semibold);
}
