/* ===== DASHBOARD (home page) ===== */

.dashboard-wrap {
  display: flex;
  flex-direction: column;
  gap: 24px;
  /* Fills the available height of .grid-area (which has symmetric 24px top/bottom padding),
     so the widget grid below naturally lands flush with that bottom padding — matching the
     top margin above the hero without hardcoding a separate value. */
  min-height: 100%;
  max-height: 100%;
}

/* The dashboard is designed to fit exactly within the available height (fixed hero + a
   flex:1 widget grid below it) — it should never scroll, unlike every other view, which
   still needs normal overflow-y:auto for long lists. Scoped to just this view via :has(). */
.grid-area:has(.dashboard-wrap) {
  overflow: hidden;
}

/* On narrow/mobile widths the same widgets stack vertically instead of sitting side-by-side,
   so the "fits exactly" assumption above no longer holds — the stacked content is reliably
   taller than the viewport, and with overflow still hidden, everything past the fold was
   simply unreachable (reported as "the dashboard doesn't scroll" testing on an iPhone 16 Pro).
   Re-enable scrolling here, and let .dashboard-wrap grow past 100% instead of being clipped to
   it, only below the same 768px breakpoint used elsewhere in this codebase for mobile layout. */
@media (max-width: 768px) {
  .grid-area:has(.dashboard-wrap) {
    overflow-y: auto;
    /* Extra room at the very end of the scroll so the last widget's own content (e.g. the
       Profile widget) doesn't end up sitting directly behind the floating "+ Add Item" FAB
       (misc.css's .fab-add — 56px tall, 24px off the bottom) once the user scrolls all the way
       down, per request. Now the same
       --fab-clearance var (misc.css) every other mobile page's .grid-area gets by default as
       standard practice — this page still needs its own override since it's otherwise
       overflow:hidden (fixed-fit desktop layout, re-enabled to scroll only here on mobile). */
    padding-bottom: var(--fab-clearance);
  }
  .dashboard-wrap {
    max-height: none;
  }
}

/* --- Hero --- */
.dash-hero {
  position: relative;
  height: 220px;
  overflow: hidden;
}

.dash-hero-collage {
  position: absolute;
  inset: 0;
  overflow: hidden;
  display: flex;
  align-items: center;
  mask-image: linear-gradient(to right, transparent, black 15%, black 85%, transparent);
  -webkit-mask-image: linear-gradient(to right, transparent, black 15%, black 85%, transparent);
}

.dash-hero--no-collage .dash-hero-collage { display: none; }

.dash-hero-collage-track {
  display: flex;
  align-items: center;
  gap: 14px;
  width: max-content;
  animation: dash-marquee 45s linear infinite;
}

@keyframes dash-marquee {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); } /* track content is duplicated in JS for a seamless loop */
}

@media (prefers-reduced-motion: reduce) {
  .dash-hero-collage-track { animation: none; }
}

.dash-hero-thumb {
  width: 150px;
  height: 150px;
  border-radius: 12px;
  overflow: hidden;
  background: var(--surface); /* fills the square while its image is still loading, instead of showing empty space through to the page background */
  box-shadow: 0 4px 20px var(--shadow-card);
  transform: rotate(var(--rot));
  flex-shrink: 0;
  cursor: pointer;
  transition: transform 0.15s;
}
.dash-hero-thumb:hover { transform: rotate(var(--rot)) scale(1.05); }
.dash-hero-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

.dash-hero-content {
  position: relative;
  z-index: 1;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0 32px;
  background: linear-gradient(to right, var(--bg) 35%, transparent 90%);
}

.dash-hero-greeting { font-size: 26px; font-weight: 700; color: var(--text-primary); margin: 0; }
.dash-hero-sub { font-size: 14px; color: var(--text-secondary); margin: 4px 0 0; }

/* --- Widget grid --- */
.dash-widget-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: repeat(2, 1fr); /* both rows equal height, so all 4 cards match */
  gap: 16px;
  flex: 1; /* stretch to fill whatever vertical space is left after the hero */
}

.dash-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-height: 0; /* lets content that should grow (slideshow, mini-board) actually grow, not overflow */
  min-width: 0; /* without this, a card whose content wants to be wide (e.g. the curated-lists
                   carousel strip, before it starts scrolling) forces its whole grid column
                   wider, squeezing the other column — grid tracks default to an auto minimum
                   equal to content's max-content size, not 0 */
}

/* padding-bottom: 5px is extra room below the title/button row, per direct request — applied to
   every widget's header (was scoped to just Queue Kanban's at first) so the spacing stays
   consistent across all of them, per direct request. */
.dash-card-header { display: flex; align-items: center; justify-content: space-between; padding-bottom: 5px; }
.dash-card-title { font-size: 15px; font-weight: 600; color: var(--text-primary); }

/* Same box recipe as .btn-add-item (base.css) — width now 20px narrower than that button's own
   148px, per direct request — still one identical width shared by Sort/Add +/Open regardless of
   their own (shorter, differing) label lengths. */
.dash-card-header-btn {
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  width: 128px;
  padding: 7px 22px;
  box-sizing: border-box;
  border-radius: 24px;
  border: none;
  background: var(--primary);
  color: #fff;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  /* "Open ▶"'s own space could otherwise wrap onto a second line at a narrow width — .btn-add-
     item doesn't need this itself (a plain inline-block button naturally shrinks its box instead
     of wrapping), but this one has an explicit fixed width above, which can't grow to keep single-
     line text from wrapping the way an auto-width box would. overflow: hidden alongside it clips
     rather than spilling past the fixed width if the label still doesn't fit — matching
     .dash-fav-category-btn (Sort), which already gets both of these for free from its own
     .btn-board-filter base class (kanban.css) and was rendering narrower as a result. */
  white-space: nowrap;
  overflow: hidden;
  cursor: pointer;
  transition: background 0.12s;
}

.dash-card-header-btn:hover,
.dash-card-header-btn:active {
  background: #fff;
  color: var(--primary);
}

/* Label stretches to fill the button, pinning any trailing icon flush against the right padding
   edge — same mechanism .btn-board-filter span already uses for Sort (kanban.css), so all three
   widget-header buttons (Sort/Add +/Open) share one spacing recipe and start their text flush at
   the same left position, instead of the divergent per-button transform hacks this went through
   getting here live. Targeted by class, not a bare `span` descendant selector, since Open's
   button has a SECOND span (.dash-kanban-open-arrow) that must stay fixed-width, not also
   stretch. Applies at every width — nothing here is mobile/desktop-scoped, so the same rule
   reflows naturally at the narrower mobile size instead of needing its own copy. */
.dash-fav-category-label,
.dash-kanban-open-label,
.dash-curated-add-label {
  flex: 1;
}

/* Kanban widget — a scaled-down peek at the real board. .dash-kanban-open is now just a marker
   class for the click handler (dashboard.js) — its actual look comes from .dash-card-header-btn,
   the same purple pill style "Sort"/"Add +" already use elsewhere in this widget grid, so all
   three widget-header buttons read consistently instead of this one being a plain text link. */
.dash-kanban-open-arrow {
  display: inline-block;
  /* Stops the arrow itself from being compressed by its stretching sibling label above. */
  flex-shrink: 0;
}
/* Was a plain "▶" text glyph — swapped for an SVG (dashboard.js) with sharp-pointed corners
   (a simple 3-point path) per direct request, since a system font's glyph rendering of that
   character isn't something CSS can reshape directly the way an SVG path can. */

/* Curated Lists' "Add +" — the "+" used to be part of the same text run as "Add" (no separate
   icon element), so it sat immediately after the label instead of pinned to the right edge like
   Sort's chevron/Open's triangle, the actual mismatch reported live. Split into its own span
   (dashboard.js) so it can get the same flex-shrink: 0 treatment as .dash-kanban-open-arrow. */
.dash-curated-add-icon {
  display: inline-block;
  flex-shrink: 0;
  font-size: 1.2em; /* 20% larger than the button's own font-size, per direct request */
}

/* REAL BUG, found and fixed: the vertical divider (::after, .dash-kmini-col:first-child) and the
   horizontal one under each title (border-bottom, .dash-kmini-col-header, extended into the gap
   via negative margins) were two independent elements in two different boxes' own coordinate
   spaces — getting them to actually cross required both to agree on a shared position that
   neither could see the other's. In practice they just met/stopped instead of crossing, per
   direct request ("the lines should cross"). Rebuilt as a single pair of pseudo-elements on this
   shared grid container instead — position: relative here makes both ::before (vertical) and
   ::after (horizontal) below position relative to the exact same box, so guaranteeing a real
   crossing is just "make sure the vertical's top/bottom range includes the horizontal's top." */
.dash-kanban-mini-board {
  position: relative;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-auto-rows: 1fr;
  gap: 8px;
  flex: 1;
  min-height: 0;
}
/* Vertical divider — starts at the very top (row 0), well above the horizontal line below, and
   runs down to bottom: 5px (see that rule's own comment on the 46px card / 36px thumbnail gap),
   so it passes straight through the horizontal line's position instead of stopping at it. */
.dash-kanban-mini-board::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 5px;
  left: 50%;
  transform: translateX(-50%);
  width: 3px; /* 2px thicker than the original 1px border, per direct request */
  background: var(--border);
}
/* Horizontal divider, under the titles — full width (both columns and the gap between them, so
   it runs straight through the vertical line above instead of stopping at each column's own
   edge), positioned to land just under the title row's text. */
.dash-kanban-mini-board::after {
  content: '';
  position: absolute;
  top: 19px; /* 3px lower, per direct request */
  left: 0;
  right: 0;
  height: 1px;
  background: var(--border);
}
/* Sat too close to the title text on desktop specifically (reported live, screenshot confirmed)
   — dropped another 4px there only, leaving mobile's own 19px (already adjusted separately)
   untouched. */
@media (min-width: 769px) {
  .dash-kanban-mini-board::after {
    top: 23px;
  }
}

/* Padding trimmed from 8px down to 3px — no longer needs to be a background-filled box with
   breathing room around it (removed below), so that space goes to the Spotify-style cards
   themselves instead, per direct request. */
.dash-kmini-col {
  /* border-radius removed — leftover from when this had its own background fill (no longer
     does). With none, it was rounding the corners the divider's border-right rides along below,
     making the line curve in at the top/bottom instead of running straight, per direct request. */
  padding: 3px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}
.dash-kmini-col:first-child {
  padding-right: 10px;
}

.dash-kmini-col-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--text-secondary);
  /* Left padding matches .dash-kmini-thumb's own 5px margin-left below, so the title text lines
     up with the thumbnails in its column, per direct request — both are additive on top of
     whatever this column's own base padding happens to be (it differs slightly between the
     first/last mini-column on mobile), so matching the two values directly keeps them aligned
     regardless. */
  padding: 0 2px 4px 5px;
  /* Own border-bottom removed — .dash-kanban-mini-board::after above replaces it with a single
     line spanning both columns, so the two no longer break in the middle. */
}
/* A true gray circle behind the count, per direct request — was a pill (asymmetric padding, not
   equal width/height) before. */
.dash-kmini-col-header span {
  width: 18px;
  height: 18px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: var(--hover-bg);
  color: var(--text-secondary);
  border-radius: 50%;
  font-weight: 600;
  /* Per direct request ("bring the circle and count number up 3px"). */
  position: relative;
  top: -3px;
}

.dash-kmini-col-cards { display: flex; flex-direction: column; gap: 6px; }

/* Restyled after Spotify's small library cards (referenced live, screenshot shown) — a flat,
   borderless pill with a true square thumbnail, tighter and rounder than the previous version's
   thin 36px-tall strip with a 44px-wide (not-quite-square) thumb. */
.dash-kmini-card {
  display: flex;
  align-items: center;
  height: 46px; /* taller than the single-line version's 40px — needed to fit the title's two lines below */
  flex-shrink: 0;
  gap: 9px;
  background: var(--surface);
  border-radius: 0; /* square corners, per direct request */
  padding: 0 9px 0 0; /* left edge comes from .dash-kmini-thumb's own margin below instead, now that it's an inset tile rather than flush */
  cursor: pointer;
  transition: background 0.15s;
}
.dash-kmini-card:hover { background: var(--hover-bg); }

/* A distinct rounded-square icon tile inset within the card — per direct request/reference image
   (a bookmark icon in a rounded green square) — rather than the old flush thumbnail bleeding
   into and clipped by the card's own (now square) corners. */
.dash-kmini-thumb {
  width: 36px;
  height: 36px;
  margin: 5px 0 5px 5px;
  border-radius: 4px; /* slightly rounded, per direct request */
  object-fit: cover;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 700;
  color: #fff;
}

.dash-kmini-info { min-width: 0; display: flex; flex-direction: column; justify-content: center; gap: 2px; padding: 5px 9px 5px 0; }
/* Two lines instead of one (per direct request, matching Spotify's own small library cards) —
   -webkit-line-clamp hard-caps it there regardless of title length, with a matching line-height
   tuned so two lines fit .dash-kmini-card's own 46px height comfortably. */
.dash-kmini-title {
  font-size: 12px;
  font-weight: 500;
  line-height: 1.3;
  color: var(--text-primary);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
.dash-kmini-demo-badge {
  align-self: flex-start;
  background: var(--primary-bg);
  color: var(--primary);
  font-size: 12px;
  font-weight: 700;
  border-radius: 11px;
  padding: 1px 7px;
}

.dash-kmini-more, .dash-kmini-col-empty {
  font-size: 10px;
  color: var(--text-muted);
  text-align: center;
  padding: 2px;
}

/* Shared thumbnail-carousel style — used by both Favorites Spotlight and Curated Lists so they
   read as the same visual language: a horizontally scrollable row of landscape thumbnails with
   a caption below each one (ESPN-style "shows strip" reference). */
.dash-carousel {
  position: relative;
  display: flex;
  align-items: center;
  gap: 4px;
  flex: 1; /* fills the remaining height below the header, keeping the title pinned to the top */
}
/* Tightened for just these two widgets (gap 4px -> 0, strip's own left padding 2px -> 0 below) —
   .dash-carousel/.dash-carousel-strip are shared with Shared Saves/Embed Builder/Curated landing
   rows, which still have a right arrow and shouldn't be affected. With the right arrow gone here
   (per direct request), the strip reaches the widget's right edge directly, and the left side's
   still-noticeable inset (arrow + this gap + the strip's own left padding) stood out by
   comparison. */
.dash-card--favorites .dash-carousel,
.dash-card--curated .dash-carousel {
  gap: 5px; /* space between the arrow and the strip, per direct request */
  /* Right edge only, per direct request — left keeps .dash-card's normal 20px padding (the
     arrow still lives there). REAL BUG, found and fixed: removing the right arrow (earlier) let
     .dash-carousel-strip's flex: 1 claim the space the arrow used to occupy, but .dash-card's own
     20px right padding was still there underneath it the whole time — the strip only ever
     reached that padding's inner edge, not the card's true edge, so it read as "flush" only by
     contrast with the wider gap the arrow used to leave. */
  margin-right: -20px;
}
/* Nudged 4px left, per direct request. Scoped to just these two widgets, same reasoning as the
   rules above — .dash-carousel-prev is shared with pages that still have both arrows. */
.dash-card--favorites .dash-carousel-prev,
.dash-card--curated .dash-carousel-prev {
  margin-left: -4px;
  /* .dash-carousel's own align-items: center centers each child against the *row's* full height
     — which is .dash-thumb-card's height (image + caption below it), not just the image, so the
     arrow sat noticeably lower than the image's own center. align-self: flex-start + matching
     .dash-thumb-art's own 112px height instead centers the arrow (via its own
     align-items/justify-content, already set below) against just that image height, per direct
     request. */
  align-self: flex-start;
  height: 112px;
  margin-top: 20px; /* dropped down 20px from image-centered, per direct request */
}
.dash-card--favorites .dash-carousel-strip,
.dash-card--curated .dash-carousel-strip {
  padding-left: 0;
}

/* Sat noticeably higher than Open/Add's own position in their header rows (reported live,
   screenshot confirmed). Several flex-based attempts (margin-top, align-self) had no visible
   effect, so this bypasses whatever's actually eating the flex-centering math with a direct
   offset instead — this wrap already has position: relative from its shared .board-filter-wrap
   base rule (kanban.css). */
.dash-fav-category-wrap {
  position: relative;
  top: 8px;
}

/* Favorites Spotlight's category filter button — reuses the Kanban board's .btn-board-filter
   structure/positioning but recolored to the SaveCraft purple, scoped to this class only so the
   real board's filter button (surface-colored) elsewhere is unaffected. */
.dash-fav-category-btn {
  /* Overrides .btn-board-filter's own base 148px (kanban.css) down to the same 128px
     .dash-card-header-btn uses, so Sort/Add +/Open keep sharing one identical width. */
  width: 128px;
  padding: 7px 22px;
  box-sizing: border-box;
  background: var(--primary);
  border-color: var(--primary);
  color: #fff;
  text-transform: uppercase;
  /* .btn-board-filter (kanban.css) never sets justify-content, so this was already flex-start by
     inherited default — declared explicitly so the shared recipe with .dash-card-header-btn
     (which now matches this) is self-documenting, not implicit. */
  justify-content: flex-start;
}
.dash-fav-category-btn svg { opacity: 0.85; fill: #fff; }
/* Sort's own dropdown sits near the right edge of its widget card, unlike the real Kanban
   board's filter button that .board-filter-dropdown (kanban.css) is really positioned for — its
   left: 0 anchor ran the dropdown off the right edge of the screen on mobile (reported live,
   screenshot confirmed: "All Categorie[s]" cut off). Scoped to this dashboard-specific class
   (not the shared .board-filter-dropdown base rule) so the real board's own filter dropdown,
   which doesn't have this problem, is unaffected. */
.dash-fav-category-dropdown {
  left: auto;
  right: 0;
}
.dash-fav-category-btn:hover,
.dash-fav-category-btn:active {
  background: #fff;
  border-color: #fff;
  color: var(--primary);
}
.dash-fav-category-btn:hover svg,
.dash-fav-category-btn:active svg {
  opacity: 1;
  fill: var(--primary);
}

.dash-carousel-strip {
  flex: 1;
  min-width: 0; /* lets the strip actually shrink/scroll instead of forcing the arrows off the edges */
  display: flex;
  gap: 14px;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  padding: 4px 2px 8px;
  scrollbar-width: none;
  /* Click-and-drag panning (dashboard.js's _wireCarouselArrows) — grab/grabbing affordance so a
     mouse user can tell this is draggable, same as the arrows already imply prev/next is. */
  cursor: grab;
}
.dash-carousel-strip::-webkit-scrollbar { display: none; }
/* Shared with .category-carousel-strip (cards.css, the category-landing carousel) — both use the
   same _wireCarouselArrows drag mechanic (dashboard.js) and previously carried byte-for-byte
   identical copies of these two rules under their own selector; /simplify pass merged them into
   one shared declaration instead of two files that could silently drift out of sync. */
.dash-carousel-strip.carousel-dragging,
.category-carousel-strip.carousel-dragging {
  cursor: grabbing;
  user-select: none;
}
/* REAL BUG, found and fixed: browsers make <img> (and text) natively draggable by default —
   starting the click-and-drag panning above directly on a card's own photo/label instead
   triggered the browser's own native "drag this image out of the page" ghost gesture instead,
   which doesn't fire ordinary mousemove events the same way, breaking the drag entirely from
   that starting point (reported live: "i want to be able to grab any card and drag it"). Turned
   off for every element inside the strip, not just <img>, since card text is just as grabbable.
   Shared with .category-carousel-strip — see the comment on the rule just above. */
.dash-carousel-strip *,
.category-carousel-strip * {
  -webkit-user-drag: none;
  user-drag: none;
  user-select: none;
}

.dash-thumb-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  background: none;
  border: none;
  cursor: pointer;
  flex-shrink: 0;
  width: 170px;
  scroll-snap-align: start;
}

.dash-thumb-art {
  position: relative;
  width: 170px;
  height: 112px;
  border-radius: 10px;
  overflow: hidden;
  background: var(--hover-bg);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 34px;
  transition: box-shadow 0.15s;
}
.dash-thumb-art img { width: 100%; height: 100%; object-fit: cover; display: block; }

.dash-thumb-card:hover .dash-thumb-art {
  box-shadow: 0 0 0 2px var(--primary);
}

.dash-thumb-badge {
  position: absolute;
  top: 6px; left: 6px;
  background: var(--primary);
  color: #fff;
  font-size: 9px;
  font-weight: 700;
  border-radius: 10px;
  padding: 2px 6px;
}

.dash-thumb-label {
  font-size: 12px;
  font-weight: 500;
  color: var(--text-secondary);
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

/* .dash-carousel-next removed from these two dashboard widgets specifically (their markup no
   longer renders it, per direct request) — kept here in the shared selector since Shared Saves/
   Embed Builder/Curated landing rows (sharedSaves.js, embedBuilder.js, renderCuratedPages.js)
   still render and rely on this same class for their own carousels. */
.dash-carousel-prev, .dash-carousel-next {
  flex-shrink: 0;
  border: none;
  background: none;
  color: var(--text-secondary);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 25px; /* bare arrow glyph, ~40% larger than the old circle-button size */
  line-height: 1;
  padding: 4px 2px;
  z-index: 2;
  transition: color 0.15s;
}
.dash-carousel-prev:hover, .dash-carousel-next:hover { color: var(--primary); }

/* No carousel rendered in the empty state — center the message in the remaining space instead
   of leaving it stranded at the top, without pulling the title down off the top-left corner. */
.dash-fav-empty {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
  font-size: 13px;
}

/* Profile widget (decorative-only) */
.dash-card--profile:hover { border-color: var(--primary); }
/* The header title ("Profile") is pinned top-left, same as every other widget's header — the
   card itself no longer centers its whole column; only .dash-profile-body (avatar/name/sub)
   below the header centers within the remaining space. */
.dash-profile-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 12px;
}
.dash-profile-avatar.cat-icon { width: 56px; height: 56px; border-radius: 50%; }
.dash-profile-avatar.cat-icon::after { border-radius: 50% 50% 0 0; }
/* The person-icon SVG hardcodes 24px height/width, which reads tiny inside a 56px circle —
   override it here rather than in the SVG string, since that markup is otherwise reusable. */
.dash-profile-avatar.cat-icon svg { width: 40px; height: 40px; }
.dash-profile-name { font-weight: 600; font-size: 14px; color: var(--text-primary); }
.dash-profile-sub { font-size: 12px; color: var(--text-muted); }

/* Every widget-header pill button (Sort/Add +/Open ▶) shares one width on desktop too (see the
   base .dash-card-header-btn/.dash-fav-category-btn rules above, and .btn-board-filter in
   kanban.css) — narrowed separately here on mobile, per direct request.
   REAL BUG, found and fixed: this override used to live in the @media block near the top of this
   file, BEFORE the base rules above it in source order. Those base rules have no media-query
   guard of their own (they're meant to apply on desktop, but nothing stops them applying on
   mobile too) — with equal selector specificity, the LATER rule in the cascade wins a tie
   regardless of which one is wrapped in @media, so the unconditional desktop width (128px) was
   winning here even on mobile, and every previous "make these narrower" request silently had no
   effect at all. Moving this override down here, after both base rules, is what actually makes
   it win. */
@media (max-width: 768px) {
  .dash-card-header-btn,
  .dash-fav-category-btn {
    width: 70px;
    height: 26px;
    /* Was 0 — with the flex:1 label-stretch mechanism (this file, near the top) pinning icons to
       the right edge, zero padding left them flush against the bare box edge, which several
       rounds of per-button transform hacks (now removed) were band-aiding instead of fixing.
       Real (small) padding gives breathing room at this narrower width, same job desktop's own
       22px already does at its size — a width change, not a mechanism change, so this reflows
       from the shared base rules above with nothing button-specific needed here anymore. */
    padding: 0 8px;
    font-size: 11px;
  }
}
