/* ==========================================================================
   List building blocks — cards, secondary nav callout, filter bar.

   Imported from the "List Building Blocks" design project. Every value here
   resolves through variables.css; nothing invents a color, radius, shadow or
   type size that isn't already a token.

   LOAD ORDER — `stylesheet_link_tag :app` sorts logical paths alphabetically,
   so this file loads BEFORE main.css and utilities.css. Anything here that has
   to beat one of those rules must win on specificity, not on order. Two
   consequences, both handled below:

     - The base `nav.secondary-nav` list/link rules stay in main.css, which
       owns them. Only the additive callout surface lives here.
     - Rules that override utilities.css button styles are written against the
       same element+class selectors utilities.css uses, plus .card-actions /
       .nav-primary-action, so they carry higher specificity.
   ========================================================================== */


/* --------------------------------------------------------------------------
   1. LIST — the <ul> that holds cards.
   -------------------------------------------------------------------------- */

.card-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* A <turbo-frame> is inline by default; the cards it wraps are grids. Same
   fix line-items.css makes for framed rows. */
.card-list turbo-frame {
  display: block;
}

/* Indented to the card's content edge, not its outer boundary — the count
   has no card behind it, so without this it reads as misaligned against the
   titles below. */
.card-list-summary {
  margin: 0 0 var(--space-4);
  padding: 0 var(--space-6);
  font-size: var(--fs-meta);
  line-height: 1.6;
  color: var(--color-text-muted);
}

/* Following a filter bar the count needs air above it, or it reads as part of
   the bar rather than as a heading for the list. Left at 0 where the count
   leads the column instead — there it should line up with the nav beside it.

   The empty state takes the same gap for the same reason: with no results
   there is no count to space it, and the two white panels otherwise sit flush
   and read as one. Lists with no filter bar keep it flush to the top. */
.filter-bar + .card-list-summary,
.filter-bar + .list-empty {
  margin-top: var(--space-4);
}


/* --------------------------------------------------------------------------
   2. CARD — one CSS grid, five named areas.

   Only .card-media and .card-body are required. Omit any other child
   entirely and the grid closes up around it.

     <li>
       <article class="card">
         <div class="card-media">…</div>          <!-- required -->
         <div class="card-body">                  <!-- required -->
           <h3 class="card-title"><a …></a></h3>
           <p class="card-meta">…</p>
         </div>
         <div class="card-aside">…</div>          <!-- status + utility -->
         <div class="card-detail">…</div>         <!-- chips, avatars, prose -->
         <div class="card-actions">…</div>        <!-- buttons -->
       </article>
     </li>

   The card is NEVER a link. Navigation happens through .card-title a and the
   controls in .card-actions, so a click is never ambiguous.
   -------------------------------------------------------------------------- */

.card {
  background: var(--nieve);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-card);
  padding: 1.25rem var(--space-6);

  display: grid;
  grid-template-columns: auto 1fr auto;
  grid-template-areas:
    "media body   aside"
    "media detail aside"
    "actions actions actions";
  column-gap: 1.25rem;
  row-gap: var(--space-1);
  align-items: start;
}

.card-media  { grid-area: media; }
.card-body   { grid-area: body; }
.card-aside  { grid-area: aside; display: flex; align-items: center; gap: var(--space-2); }
.card-detail { grid-area: detail; margin-top: var(--space-2); }
.card-actions{ grid-area: actions; }

/* Cards with no leading media collapse the first column. */
.card:not(:has(.card-media)) {
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "body   aside"
    "detail detail"
    "actions actions";
}

/* --- Ghost: the space a deleted card leaves ------------------------------- */

/* A card removed from a list inside its own frame takes the row with it, and
   the list closes up as if nothing had been there — no flash can reach the
   page to say otherwise. This is the outline of what was: same footprint and
   radius, but drawn rather than built. Dashed and unfilled reads as absence
   the way the upload dropzone does, and the missing shadow drops it off the
   surface the real cards sit on.

   It is shorter than what it replaces — one line where there was a title, a
   meta line and a row of buttons — so the list visibly closes up around it.
   That is the honest shape: the record is gone, and only the note about it
   is still standing.

   No grid: there is one line of text and none of the card's areas to place
   it in. */
.card.ghost {
  display: block;
  box-shadow: none;

  /* The same pair forms.css gives the upload dropzone — brisa dashed over
     niebla pulled most of the way to white. That is already this codebase's
     mark for "nothing here yet"; a deleted row is the other end of the same
     idea, so it should not invent a second one. The pale field border was
     tried first and all but disappeared against the page. */
  border: 2px dashed var(--brisa);
  background: color-mix(in srgb, var(--niebla) 40%, var(--nieve));
}

.card.ghost .ghost-message {
  margin: 0;
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--color-text-muted);
}


/* --- Media: file thumbnail ------------------------------------------------

   Whatever the slot holds, it is a sheet of paper. A PDF puts its rendered
   first page in here; the formats Active Storage cannot preview put their name
   in here instead. Both are white with a hairline edge, so a list that mixes
   the two still reads as a column of documents rather than as pictures with
   gaps between them — which is what a mist-filled badge beside a page image
   looked like.

   The fill was mist before there was ever a picture to put here. Keeping it
   would have made every card without a preview read as the empty one.
   -------------------------------------------------------------------------- */

.card-thumbnail {
  width: 8rem;
  height: 5.25rem;
  display: grid;
  place-items: center;

  /* The row has to be definite. Left to `auto` it sizes to its content, so a
     page image's `height: 100%` resolves against the image's own intrinsic
     height instead of this box — the image lays out at full height, overflows,
     and `place-items: center` shows the middle of the sheet through
     `overflow: hidden`. `object-position` never gets a say, because nothing is
     being cropped by the image box at all. On a worksheet, whose content is all
     in the top third, that renders as a blank white rectangle. */
  grid-auto-rows: 100%;

  border: 3px solid var(--nieve);
  border-radius: var(--radius-md);
  background: var(--nieve);
  box-shadow: var(--shadow-thumbnail), var(--shadow-thumbnail-edge);
  overflow: hidden;
}

/* Top, not centre. A first page is portrait and this box is landscape, so
   `cover` alone crops to the middle of the sheet — which on a worksheet is a
   band of blank rules. Anchored to the top, the title and the level survive,
   and that is the only reason to show a picture at all. */
.card-thumbnail img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
}

/* The format, set where a page would carry its title: the word, then a rule
   under it. The rule is what keeps this from reading as a word floating in a
   box — it gives the sheet the same top-weighted anatomy the cropped page
   beside it has. Decorative, so it is drawn rather than marked up. */
.card-thumbnail-label {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4375rem;
  color: var(--crepusculo);
  font-size: 0.75rem;
  font-weight: var(--fw-bold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.card-thumbnail-label::after {
  content: "";
  width: 2.875rem;
  height: 2px;
  background: var(--brisa);
}

/* A .profile-dot (see main.css) drops straight into .card-media with no
   extra rule — it is already sized, bordered and centred. */


/* --- Body ----------------------------------------------------------------- */

.card-title {
  margin: 0;
  font-family: var(--font-family-display);
  font-weight: var(--fw-bold);
  font-size: 1.5rem;
  line-height: 1.3;
  color: var(--vn-heading);
}

.card-title a {
  color: inherit;
  text-decoration: none;
}

.card-title a:hover {
  color: var(--color-link);
  text-decoration: underline;
}

/* The meta line runs tighter than --lh-body on purpose: the brand's 180%
   body rhythm is right for prose and wrong for a 200-row list. */
.card-meta {
  margin: 0.125rem 0 0;
  font-size: var(--fs-meta);
  line-height: 1.5;
  color: var(--color-text-muted);
}

.card-description {
  margin: var(--space-2) 0 0;
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--color-text);
  text-wrap: pretty;
}


/* --- Detail: chip rows ---------------------------------------------------- */

.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
}

.chip {
  display: inline-block;
  padding: 0.125rem 0.625rem;
  border-radius: var(--radius-pill);
  background: var(--niebla);
  color: var(--crepusculo);
  font-size: 0.8125rem;
  line-height: 1.6;
}

/* Quiet — a fact about the record that is not a claim on the reader's
   attention. "Not in a class" is the only one so far: it exists to make a
   dormant student legible in the list, so that nobody reaches for Deactivate
   to tidy them out of it. Loud styling would defeat the point. */
.chip.quiet {
  background: transparent;
  border: 1px dashed var(--brisa);
  color: var(--color-text-muted);
}

/* Highlight — membership, "visible", anything worth a second look. */
.chip.highlight {
  background: var(--trigo);
  border: 1px solid var(--autobus-escolar);
  color: var(--chocolate);
}

/* Solid — a chip that is a control rather than a status, and has to read as
   picked. Used for the selections inside a collection picker. */
.chip.solid {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.1875rem 0.5625rem;
  background: var(--pajaro-azul);
  color: var(--trigo);
  font-weight: 500;
}

/* Attention — expired invite, missing file, needs review. */
.chip.attention {
  background: var(--nieve);
  border: 1px solid var(--color-alert-border);
  color: var(--color-alert-text);
}


/* --- Detail: avatar clusters (staff / students on a class card) ----------- */

.people-groups {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
  align-items: flex-start;
  margin-top: var(--space-4);
}

.people-group-label {
  margin: 0 0 0.375rem;
  font-size: var(--fs-meta);
  font-weight: var(--fw-bold);
  line-height: 1.4;
  color: var(--vn-heading);
}

.people-group-label .count {
  font-weight: var(--fw-regular);
  color: var(--color-text-muted);
}

.people-cluster {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}


/* --- Actions -------------------------------------------------------------- */

/* One primary button per card; everything after it is .secondary or
   .tertiary.

   Actions come in TWO groups, not one flat row:

     <div class="card-actions">
       <div class="action-group">…constructive…</div>
       <div class="action-group">…utility, archival, destructive…</div>
     </div>

   space-between holds the groups apart when they fit on one line, and lets
   the second group wrap as a unit when they don't. A lone `margin-left:auto`
   on the last button looks the same until it wraps, at which point it strands
   one right-aligned button under a gap — which happens on any content column
   under ~640px. A card's "Settings" link belongs in group two, not in
   .card-aside beside the status chip. */
.card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  align-items: center;
  justify-content: space-between;
  margin-top: 2rem;
}

.card-actions .action-group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  align-items: center;
}

/* When group one is empty — a document with no file to download, a staff
   member with no messaging routes — space-between has nothing to push
   against and strands the record group on the left, where every other card
   puts its primary action. Mark it so a lone record group still sits right. */
.card-actions > .action-group.record-actions:only-child {
  margin-left: auto;
}

/* utilities.css adds margin-left to adjacent buttons; the flex gap already
   handles spacing here, so cancel it. Mirrors utilities.css's own selector
   list one level deeper so it wins on specificity — a bare
   `.card-actions > * + *` loses to `A.button + A.button`. */
.card-actions A.button + A.button,
.card-actions A.button + A,
.card-actions A.button + FORM.button_to,
.card-actions BUTTON:not(.editor-button) + BUTTON:not(.editor-button),
.card-actions FORM.button_to + FORM.button_to,
.card-actions > * + *,
.card-actions .action-group > * + * {
  margin-left: 0;
}


/* --------------------------------------------------------------------------
   3. SECONDARY NAV — the callout-style sidebar.

   Extends the flat nav.secondary-nav in main.css with the brand's callout
   surface, group headings and a separated primary action. The base list and
   link rules (including the hover fill and the mobile link width) live in
   main.css, which loads after this file and owns those selectors.

     <nav class="secondary-nav" aria-label="Library">
       <p class="nav-group-label">Documents</p>
       <ul>
         <li><a class="selected">All documents</a></li>
         <li><a>Archived</a></li>
       </ul>
       <p class="nav-group-label">Organise</p>
       <ul><li><a>Manage labels</a></li></ul>
       <hr>
       <a class="button small nav-primary-action">Add a document</a>
     </nav>
   -------------------------------------------------------------------------- */

/* The mist fill sits on a page background that starts as the same mist and
   fades out, so the white border is what separates the card from the page.
   1px is too fine to read against that gradient — 2px holds the edge. */
nav.secondary-nav {
  padding: var(--space-4);
  background: var(--callout-box-background-color);
  border: 2px solid var(--nieve);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-card);
}

.nav-group-label {
  margin: 0 0 0.375rem;
  padding: 0 var(--space-3);
  font-size: 0.8125rem;
  font-weight: var(--fw-bold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  line-height: 1.6;
  color: var(--color-text-muted);
}

/* Space between one group's list and the next group's heading. */
nav.secondary-nav ul + .nav-group-label {
  margin-top: var(--space-4);
}

nav.secondary-nav hr {
  border: none;
  border-top: 1px solid var(--nieve);
  margin: var(--space-3) 0;
}

/* utilities.css sets A.button { display: inline-block }, so this needs the
   element+class form to take the block over. The colours are restated for the
   same reason in reverse: an action button inside this nav is also matched by
   `nav.secondary-nav a` in main.css, which is more specific than `A.button` and
   would otherwise paint the label link-blue on the blue fill. */
A.button.nav-primary-action,
BUTTON.nav-primary-action,
INPUT[type="submit"].nav-primary-action,
.nav-primary-action {
  display: block;
  width: 100%;
  color: var(--button-color);
}

A.button.nav-primary-action:hover,
BUTTON.nav-primary-action:hover,
INPUT[type="submit"].nav-primary-action:hover,
.nav-primary-action:hover {
  background: var(--button-background-hover-color);
  color: var(--button-hover-color);
}

/* Mobile: the nav card folds into an accordion. The primary action stays
   outside the fold. Wrap the groups in <details><summary>current item. */
@media (max-width: 48rem) {
  nav.secondary-nav summary {
    cursor: pointer;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-md);
    background: var(--nav-button-selected-background-color);
    color: var(--nav-button-selected-color);
    font-weight: var(--fw-bold);
    font-size: var(--fs-small);
    line-height: 1.5;
  }
}


/* --------------------------------------------------------------------------
   4. FILTER BAR — search plus an inline, variable set of filters.

     <form class="filter-bar">
       <input type="search" class="filter-search">
       <select class="filter-select">…</select>
       <details class="filter-multi">
         <summary>Labels <span class="count">· 1 selected</span></summary>
         <div class="filter-multi-panel">…checkboxes…</div>
       </details>
       <input type="submit" value="Search">
       <div class="applied-filters">…chips + clear-all…</div>
     </form>

   Every control is optional and the row wraps, so a screen can carry one
   filter or five without a layout change.
   -------------------------------------------------------------------------- */

/* Side padding matches .card so every control's left edge lines up with the
   card titles below it. */
.filter-bar {
  background: var(--nieve);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-card);
  padding: var(--space-4) var(--space-6);
  display: flex;
  flex-wrap: wrap;
  gap: 0.625rem;
  align-items: center;
}

/* Reset the global full-width input rule inside the bar. */
.filter-bar input[type="search"],
.filter-bar select {
  width: auto;
  max-width: none;
  padding: var(--space-2) var(--space-3);
  font-size: var(--fs-small);
  line-height: 1.5;
}

.filter-search {
  flex: 1 1 16rem;
  min-width: 0;
  max-width: 26rem;
}

.filter-select {
  flex: 0 0 auto;
}

/* Multi-select: a <details> popover of checkboxes. No JavaScript. */
.filter-multi {
  flex: 0 0 auto;
  position: relative;
}

.filter-multi > summary {
  list-style: none;
  cursor: pointer;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-input-border);
  border-radius: var(--radius-md);
  background: var(--nieve);
  font-size: var(--fs-small);
  line-height: 1.5;
  color: var(--color-text);
  white-space: nowrap;
}

.filter-multi > summary::-webkit-details-marker { display: none; }
.filter-multi > summary .count { color: var(--color-text-muted); }

.filter-multi-panel {
  position: absolute;
  z-index: 5;
  top: calc(100% + 0.375rem);
  left: 0;
  min-width: 14rem;
  padding: var(--space-3);
  background: var(--nieve);
  border: 1px solid var(--color-input-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-popover);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.filter-multi-panel label {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  font-weight: var(--fw-regular);
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--color-text);
  cursor: pointer;
}

.filter-multi-panel input[type="checkbox"] {
  width: auto;
  margin: 0;
  accent-color: var(--pajaro-azul);
}

/* Inline boolean toggle, styled to match the other controls. */
.filter-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-input-border);
  border-radius: var(--radius-md);
  background: var(--nieve);
  font-weight: var(--fw-regular);
  font-size: var(--fs-small);
  line-height: 1.5;
  color: var(--color-text);
  cursor: pointer;
}

.filter-toggle input[type="checkbox"] {
  width: auto;
  margin: 0;
  accent-color: var(--pajaro-azul);
}

/* Applied filters: a second row inside the same card. */
.applied-filters {
  flex: 1 0 100%;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
  margin-top: var(--space-1);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}

.applied-filters .count {
  font-size: var(--fs-meta);
  color: var(--color-text-muted);
}

.applied-filters .chip {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding-right: var(--space-2);
}

/* The chip's remove control. A GET filter is a link, not a button, so both
   forms are covered — and the button form needs resetting out of the global
   button style. */
.applied-filters .chip button,
.applied-filters .chip a {
  cursor: pointer;
  border: none;
  background: none;
  padding: 0;
  color: inherit;
  font-size: var(--fs-small);
  line-height: 1;
  text-decoration: none;
  box-shadow: none;
}

.clear-filters {
  margin-left: auto;
  font-size: var(--fs-meta);
}


/* --------------------------------------------------------------------------
   5. EMPTY STATE
   -------------------------------------------------------------------------- */

.list-empty {
  padding: 2.5rem var(--space-6);
  background: var(--nieve);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-card);
  text-align: center;
}

.list-empty img {
  display: block;
  width: 3.5rem;
  height: auto;
  margin: 0 auto var(--space-4);
}

.list-empty h2 {
  margin: 0 0 var(--space-1);
  font-size: var(--fs-h4);
  font-weight: var(--fw-bold);
}

.list-empty p {
  margin: 0 0 1.25rem;
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--color-text-muted);
}


/* --------------------------------------------------------------------------
   6. MOBILE
   -------------------------------------------------------------------------- */

@media (max-width: 48rem) {
  .card {
    padding: var(--space-4);
    column-gap: var(--space-4);
  }

  /* Media moves above the body rather than beside it. */
  .card,
  .card:not(:has(.card-media)) {
    grid-template-columns: 1fr;
    grid-template-areas:
      "media"
      "body"
      "aside"
      "detail"
      "actions";
  }

  .card-aside {
    margin-top: var(--space-2);
  }

  .card-thumbnail {
    width: 100%;
    height: 7rem;
  }

  .filter-search {
    flex: 1 1 100%;
    max-width: none;
  }

  /* Cards drop to 1rem padding on mobile; keep the bar and count with them. */
  .filter-bar {
    padding: var(--space-4);
  }

  .card-list-summary {
    padding: 0 var(--space-4);
  }

  /* Side-by-side people groups starve each other at phone width — five staff
     avatars leave a 25-student cluster four per row. Stacking gives each
     cluster the full card width, so 25 students land in three tidy rows. */
  .people-groups {
    flex-direction: column;
    gap: var(--space-4);
  }

  /* Stack the actions rather than letting them wrap — a wrapped row of
     part-width buttons reads as noise at phone width. Both groups stay in
     source order on a single vertical axis. */
  .card-actions,
  .card-actions .action-group {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-3);
  }

  /* The desktop right-align is an auto margin, and an auto cross-axis margin
     beats align-items: stretch — leaving a lone record group shrunk to its
     content instead of full width. Drop it once the column takes over. */
  .card-actions > .action-group.record-actions:only-child {
    margin-left: 0;
  }

  .card-actions .button,
  .card-actions button,
  .card-actions input[type="submit"] {
    display: block;
    width: 100%;
    text-align: center;
  }
}
