.content-shell {
  max-width: 1080px;
  margin: 0 auto;
  padding: 2rem;
}

.content-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.content-header h1 {
  margin: 0;
}

.subnav {
  display: flex;
  gap: 1.25rem;
  margin-bottom: 1.5rem;
  border-bottom: 1px solid var(--border);
}

.subnav__link {
  padding: 0.6rem 0.1rem;
  color: var(--muted);
  text-decoration: none;
  font-weight: 600;
  font-size: 0.95rem;
  border-bottom: 2px solid transparent;
}

.subnav__link:hover {
  color: var(--text);
}

.subnav__link--active {
  color: var(--primary);
  border-bottom-color: var(--primary);
}

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 1.25rem;
}

/* button_to renders a <form>, which is block-level by default — this app
   drops a follow-icon form inline next to a performer's name in all kinds
   of running text/spans, so it needs to flow inline everywhere. */
.follow-icon-form {
  display: inline-block;
  vertical-align: middle;
}

/* Same reasoning as .follow-icon-form, for a button_to that needs to sit
   inline mid-sentence instead (see
   ShowsHelper#cast_member_editable_fragment's "Remove" control) — plain
   `display: inline` rather than inline-block, since there's no icon
   sizing/alignment to preserve here, just flowing text. */
.inline-form {
  display: inline;
}

/* Resets a <button> to read as plain inline text (an underlined link),
   for a button_to that has to sit inside running prose rather than its
   own row — see .inline-form above. */
.text-button {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: var(--primary);
  text-decoration: underline;
  cursor: pointer;
}

/* Lets a follow-icon form sit as a sibling of a full-card <a>, positioned
   over its corner, without nesting a <form>/<button> inside the <a> —
   see performers/_results.html.erb. */
.card-wrapper {
  position: relative;
  height: 100%;
}

.card-wrapper > .card {
  height: 100%;
  box-sizing: border-box;
}

.card-wrapper > .follow-icon-form {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  z-index: 1;
}

.card {
  display: block;
  padding: 1.25rem;
  border: 1px solid var(--border);
  border-radius: 16px;
  background: var(--card-bg);
  text-decoration: none;
  color: var(--text);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.card:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 24px rgba(15, 23, 42, 0.1);
}

/* A search result "card" that submits an action instead of navigating
   (e.g. picking a piece to add to a program — see
   performance_pieces/_piece_results.html.erb) is a button_to, which
   always wraps its own <button> in a <form>. display: contents drops
   that form out of .card-grid's own layout so the button itself, not an
   extra empty wrapper, is what actually fills the grid cell — sized and
   positioned exactly like a plain .card <a> would be. The button then
   only needs its own browser-default chrome (border, font, background)
   reset before .card's shared rules above apply the same as they would
   to a link. */
.card-grid form:has(> .card-button) {
  display: contents;
}

.card-button {
  width: 100%;
  border: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.card h2,
.card h3 {
  margin: 0 0 0.4rem;
  font-size: 1.1rem;
  color: var(--text);
}

/* A year of composition sitting inline after a card's title (an h3, bold
   by default) — reset back to a normal weight so it reads as secondary
   information alongside the title, not part of it. */
.card-year {
  font-weight: 400;
  font-size: 0.9rem;
  color: var(--muted);
}

.card-meta {
  margin: 0;
  color: var(--muted);
  font-size: 0.9rem;
}

.category-badge {
  display: inline-flex;
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
  background: var(--secondary);
  color: var(--secondary-text);
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: capitalize;
  margin-bottom: 0.4rem;
}

.selected-tags {
  list-style: none;
  margin: 0 0 0.5rem;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.selected-tag {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.3rem 0.4rem 0.3rem 0.75rem;
  border-radius: 999px;
  background: var(--secondary);
  color: var(--secondary-text);
  font-size: 0.85rem;
  font-weight: 600;
}

.selected-tag button {
  border: none;
  background: none;
  color: var(--muted);
  cursor: pointer;
  font-size: 0.75rem;
  font-weight: 700;
  padding: 0.15rem 0.4rem;
  border-radius: 999px;
}

.selected-tag button:hover {
  background: rgba(15, 23, 42, 0.08);
  color: var(--text);
}

.selected-tag--quantity input[type="number"] {
  width: 3.5rem;
  padding: 0.15rem 0.3rem;
  border-radius: 999px;
  border: 1px solid var(--border);
}

.milestone-badge-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.4rem;
}

.milestone-badge {
  width: 32px;
  height: 32px;
  border-radius: 6px;
  object-fit: cover;
}

.milestone-badge--large {
  width: 96px;
  height: 96px;
}

.program-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.5rem;
}

.program-list__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--card-bg);
}

/* A .program-list rendered as one shared card instead of one card per
   row — see shows/show.html.erb's Cast/Swing/Understudy/Orchestra
   sections, matching how the Program section's own songs already read
   (many rows, one card) — each row keeps its own padding but drops its
   individual border/radius/background, with a thin divider between rows
   instead of the usual gap. overflow: hidden clips the first/last row's
   now-square corners to the card's own rounded ones. */
.program-list--card {
  gap: 0;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--card-bg);
}

.program-list--card .program-list__row {
  border: none;
  border-radius: 0;
  background: none;
}

.program-list--card .program-list__row + .program-list__row {
  border-top: 1px solid var(--border);
}

/* .program-list__row's own main content span, previously anchored by its
   visible title/creator text alone — a flex item without flex-grow
   shrinks to that content's width, not the row's full width. That's
   invisible for an ordinary long title, but a row with little or no
   leading text (see hide_piece_summary: in this same partial) shrinks
   the span down to barely wider than its own nested movements list,
   pulling each movement row's own right-justified credit (see
   .program-list__movement-row) in from the card's real right edge —
   exactly the "justification moved to the middle" look. flex: 1 makes
   this span always fill the row, regardless of how much of its own text
   is showing. */
.program-list__row-main {
  flex: 1;
  min-width: 0;
}

.program-list__row-actions {
  display: flex;
  gap: 0.5rem;
  align-items: center;
}

/* A small grab handle at the front of a draggable row — see
   drag_reorder_controller.js. Only the handle itself initiates a drag
   (not the whole row, which holds several ordinary links/buttons of its
   own), while the row stays a valid drop target everywhere. */
.drag-handle {
  cursor: grab;
  color: var(--muted);
  font-size: 1.1rem;
  line-height: 1;
  padding: 0 0.15rem;
  flex-shrink: 0;
}

/* Drag feedback (see drag_reorder_controller.js's dragEnter/dragLeave) —
   applied to whatever row a drag is currently hovering over. Dropping
   here inserts the dragged row into the gap right before this one. */
.program-list__row.drag-over {
  outline: 3px dashed var(--primary);
  outline-offset: 2px;
}

/* Exists purely to be submitted by drag_reorder_controller.js — never
   shown or clicked directly. */
.program-list__move-to-form {
  display: none;
}

/* A run of sub_pieces sharing a Piece#section_label (e.g. "Part 1") —
   a plain divider, not a card, so it reads as a heading rather than
   another item in the list. */
.program-list__section-heading {
  font-weight: 700;
  color: var(--muted);
  padding: 0.5rem 0.25rem 0;
}

/* A book's own row for one of a numbered opus's individual pieces,
   grouped under that opus's own heading row (see Book#contents_groups) —
   or a piece's own page listing its sub_pieces (Movements/Parts/etc.),
   indented under the work's own title the same way. */
.program-list--indented {
  margin-left: 1.5rem;
}

.program-list__row--nested {
  margin-left: 1.5rem;
}

.program-list__movements {
  list-style: none;
  margin: 0.35rem 0 0;
  padding: 0;
  margin-left: 1.5rem;
  display: grid;
  gap: 0.15rem;
  font-size: 0.85rem;
  color: var(--text);
}

/* One movement/song's own title-and-credits line (see
   shared/_movement_entry.html.erb) — title on the left, whoever's
   credited on it pushed to the right edge of the same line, Playbill-style. */
.program-list__movement-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.75rem;
}

/* The role/character credit on a movement's own row (e.g. "Jenna and
   Company") reuses .card-meta for its font-size, but reads as regular
   program text here, not de-emphasized meta info the way .card-meta's
   own muted grey is everywhere else it's used (venue addresses, upload
   attribution...) — so it overrides that color back to plain text black,
   matching the title right next to it. */
.program-list__movement-row .card-meta {
  color: var(--text);
}

/* The "Not all movements were performed" checklist a piece's editable
   row reveals — a plain checkbox-per-movement list, distinct from
   .program-list__movements above (that one's the always-visible,
   read-only movement listing under a piece's own title). */
.program-list__movement-checklist {
  list-style: none;
  margin: 0.5rem 0;
  padding: 0;
  display: grid;
  gap: 0.35rem;
}

details > summary {
  cursor: pointer;
  font-weight: 700;
  color: var(--text);
  margin: 1.5rem 0 1rem;
}

details[open] > summary {
  margin-bottom: 1rem;
}

.program-list__row-actions a,
.program-list__row-actions button {
  font-size: 0.85rem;
  color: var(--primary);
  background: none;
  border: none;
  cursor: pointer;
  text-decoration: none;
  font-weight: 600;
  padding: 0;
}

/* Same "form is block-level" problem #follow-icon-form solves, for the
   inline Edit/Remove controls a chamber show's compact instrument line
   (a running sentence, not a row list — see ShowsHelper#cast_member_fragment)
   appends after a name. */
.cast-inline-action-form {
  display: inline-block;
  vertical-align: middle;
}

.cast-inline-action,
.cast-inline-action-form button {
  font-size: 0.85rem;
  color: var(--primary);
  background: none;
  border: none;
  cursor: pointer;
  text-decoration: none;
  font-weight: 600;
  padding: 0;
}

.follow-icon {
  font-size: 1.1rem;
  line-height: 1;
  color: var(--muted);
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  cursor: pointer;
  padding: 0.25rem 0.5rem;
}

.follow-icon--active {
  color: #b45309;
  border-color: #b45309;
}

.empty-state {
  padding: 2rem;
  text-align: center;
  color: var(--muted);
  border: 1px dashed var(--border);
  border-radius: 16px;
}

/* A chamber group's compact "Emily and Clara, violins" musician line
   (see shows/show.html.erb) — a div, not a p, since a logged-in
   viewer's inline Edit/Remove controls include a <form>. */
.chamber-instrument-line {
  margin: 0 0 0.5rem;
}

.pagy.nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 1.5rem;
  justify-content: center;
}

.pagy.nav a,
.pagy.nav span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.2rem;
  padding: 0.4rem 0.6rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  color: var(--text);
  text-decoration: none;
  font-size: 0.9rem;
}

.pagy.nav a:hover {
  background: var(--secondary);
}

.pagy.nav .current {
  background: var(--primary);
  border-color: var(--primary);
  color: #ffffff;
  font-weight: 700;
}

.review-row {
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 1rem;
  margin-bottom: 1rem;
  display: grid;
  gap: 0.75rem;
}

input.field-missing {
  border-color: var(--danger-text);
  background: var(--danger-bg);
}

.field-missing-note {
  margin: 0.2rem 0 0;
  color: var(--danger-text);
  font-size: 0.85rem;
}

.raw-json {
  margin: 0.75rem 0 2rem;
  padding: 1rem 1.25rem;
  border: 1px solid var(--border);
  border-radius: 16px;
  background: var(--card-bg);
  overflow-x: auto;
  font-family: "SF Mono", "Menlo", "Consolas", monospace;
  font-size: 0.85rem;
  line-height: 1.5;
  white-space: pre;
}

.table-scroll {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: 16px;
  background: var(--card-bg);
}

.table-scroll table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.table-scroll th,
.table-scroll td {
  padding: 0.75rem 1rem;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--border);
}

.table-scroll th {
  color: var(--muted);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  white-space: nowrap;
}

.table-scroll tbody tr:last-child td {
  border-bottom: none;
}

.table-scroll tbody tr:hover {
  background: var(--secondary);
}

.table-scroll .program-list {
  min-width: 220px;
}

@media (max-width: 640px) {
  .content-shell {
    padding: 1.25rem;
  }

  .content-header {
    flex-direction: column;
    align-items: flex-start;
  }
}
