/* ═══════════════════════════════════════════════════════════════════
   Mobile Library View — 3-column thumbnail grid
   Only active at <=767px (mobile breakpoint)
   ═══════════════════════════════════════════════════════════════════ */

@media (max-width: 767px) {

  /* meticulous-tailoring-canvas S1 (2026-05-10) — Iron-Law class fix for
     Issue #1 (Media tab does not fit, "Library" heading clipped). The Media
     tab on iPhone mounts MobileLibraryView (.mobile-library) into #view-media,
     but the desktop .media-content sibling has an explicit
     height: calc(100vh - var(--nav-height) - 140px) reserving most of the
     viewport — pushing .mobile-library down behind the TabBar and producing
     the "large gap at the top of each tab" Joe described. CSS-layer hide
     mirrors the canonical mobile-stories-list pattern (race-free, persistent
     across re-mounts).
     ⛔ #mediaFilterPanel is hidden here too (Greg, 2026-10-01). It used to stay
     visible "since its mobile toggle (.fp-mobile-toggle) is the active filter UI
     per Issue #6 → D-A3". Measured on webkit-iphone (Home Screen emulation): that
     panel drives the DESKTOP curator's grid, which this very block hides. Applying
     "Documents" through it turned the pill to "Filters 1" and left the phone grid
     unchanged (25,956 items, the same tiles). A filter that announces itself and
     narrows nothing. The phone's real filter is MobileLibraryView's own Filter
     sheet (+ Ask Fergi / Say It in the bar), so that one stays and this one goes.
     Joe 2026-10-01 authorized simplifying mobile Browse Media around Fergi.
     Regression: tests/mobile/phone-media-one-filter.test.js. */
  #view-media > .media-content,
  #view-media > #mediaCount,
  #view-media > #mediaFilterPanel {
    display: none !important;
  }

  .mobile-library {
    display: flex;
    flex-direction: column;
    min-height: 100%;
    background: var(--color-bg, #f8f9fa);
    padding-bottom: calc(56px + env(safe-area-inset-bottom) + 16px);
  }

  /* ── header ──────────────────────────────────────────────────────── */

  .ml-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 16px 8px;
    position: sticky;
    top: 0;
    background: var(--color-bg, #f8f9fa);
    z-index: 10;
  }

  .ml-title {
    font-size: 22px;
    font-weight: 700;
    margin: 0;
    color: var(--color-text, #1a1a2e);
    /* crisp-anchoring-cradle S1 D1-A: in-body "Media" title is now redundant
       — the Universal Modal Chrome Contract centers the title in the top
       band (.mobile-view-title). Hide on mobile to avoid duplicate naming
       and free up vertical space at the top of the grid. The element
       remains in the DOM for desktop (mobile-library.css only loads inside
       the mobile @media wrapper). */
    display: none;
  }

  .ml-header-actions {
    display: flex;
    gap: 8px;
  }

  .ml-filter-btn,
  .ml-search-btn,
  .ml-size-btn {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    border: none;
    background: var(--color-bg-secondary, #e8e8ec);
    color: var(--color-text, #1a1a2e);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }

  .ml-filter-btn:active,
  .ml-search-btn:active,
  .ml-size-btn:active {
    background: var(--color-border, #d0d0d8);
  }

  /* ── thumbnail-size cycle button (S2 D2-B, Issue #4) ───────────────
     The button shows ONE of three icon variants based on its current
     data-size attribute. The icons visually mirror the resulting grid
     column count (3-up for small, 2-up with sidebar for medium, 2-up
     for large) so the affordance reads as "current size" not "size
     setting". All three icons render in the DOM; data-size-based CSS
     toggles which one is visible. Cycle order: small → medium → large
     → small. ───────────────────────────────────────────────────────── */

  .ml-size-btn {
    position: relative;
  }

  .ml-size-btn .ml-size-icon {
    display: none;
  }
  .ml-size-btn[data-size="small"]  .ml-size-icon-small  { display: block; }
  .ml-size-btn[data-size="medium"] .ml-size-icon-medium { display: block; }
  .ml-size-btn[data-size="large"]  .ml-size-icon-large  { display: block; }

  /* ── search bar ──────────────────────────────────────────────────── */

  .ml-search-bar {
    padding: 0 16px 8px;
  }

  .ml-search-input {
    width: 100%;
    padding: 10px 14px;
    border: 1px solid var(--color-border, #d0d0d8);
    border-radius: 10px;
    font-size: 15px;
    background: var(--color-bg-secondary, #fff);
    color: var(--color-text, #1a1a2e);
    box-sizing: border-box;
    -webkit-appearance: none;
  }

  .ml-search-input:focus {
    outline: none;
    border-color: var(--color-primary, #4f46e5);
    box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.12);
  }

  /* ── active filter chips ─────────────────────────────────────────── */

  .ml-active-filters {
    display: flex;
    gap: 8px;
    padding: 0 16px 8px;
    flex-wrap: wrap;
  }

  .ml-active-filters:empty {
    display: none;
  }

  .ml-active-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 10px;
    background: var(--color-primary, #4f46e5);
    color: #fff;
    border-radius: 16px;
    font-size: 13px;
    font-weight: 500;
  }

  .ml-active-chip button {
    background: none;
    border: none;
    color: inherit;
    font-size: 16px;
    line-height: 1;
    padding: 0 0 0 2px;
    cursor: pointer;
  }

  /* ── stats ───────────────────────────────────────────────────────── */

  .ml-stats {
    padding: 0 16px 8px;
    font-size: 13px;
    color: var(--color-text-light, #666);
  }

  .ml-stats:empty {
    display: none;
  }

  /* ── thumbnail grid ──────────────────────────────────────────────── */

  /* The grid column count is driven by the [data-size] attribute the
     view writes via _applyThumbSize(). Default of `repeat(3, 1fr)` keeps
     pre-S2-D2-B behavior for any grid rendered before the attribute
     lands (defense-in-depth — _applyThumbSize runs synchronously in
     init() right after appendChild, so attribute-less paint should
     never happen, but the default keeps the surface coherent if a
     future refactor reorders). reflective-swimming-ripple S2 D2-B. */
  .ml-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 2px;
    padding: 0 2px;
  }

  .ml-grid[data-size="small"]  { grid-template-columns: repeat(4, 1fr); }
  .ml-grid[data-size="medium"] { grid-template-columns: repeat(3, 1fr); }
  .ml-grid[data-size="large"]  { grid-template-columns: repeat(2, 1fr); }

  .ml-cell {
    position: relative;
    aspect-ratio: 1;
    overflow: hidden;
    background: var(--color-bg-secondary, #e8e8ec);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }

  .ml-cell:active {
    opacity: 0.8;
  }

  .ml-thumb {
    position: absolute;
    inset: 0;
  }

  .ml-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }

  .ml-thumb-placeholder {
    position: absolute;
    inset: 0;
    background: var(--color-bg-secondary, #e8e8ec);
  }

  .ml-thumb-error {
    background: var(--color-bg-secondary, #e8e8ec);
  }

  .ml-thumb-error::after {
    content: '?';
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 24px;
    color: var(--color-text-light, #999);
  }

  .ml-type-badge {
    position: absolute;
    top: 4px;
    right: 4px;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    font-size: 11px;
    padding: 2px 5px;
    border-radius: 4px;
    pointer-events: none;
    z-index: 1;
  }

  /* ── loading / empty ─────────────────────────────────────────────── */

  .ml-loading {
    display: flex;
    justify-content: center;
    padding: 24px;
  }

  .ml-spinner {
    width: 28px;
    height: 28px;
    border: 3px solid var(--color-border, #d0d0d8);
    border-top-color: var(--color-primary, #4f46e5);
    border-radius: 50%;
    animation: ml-spin 0.7s linear infinite;
  }

  @keyframes ml-spin {
    to { transform: rotate(360deg); }
  }

  .ml-empty {
    text-align: center;
    padding: 48px 16px;
    color: var(--color-text-light, #999);
    font-size: 15px;
  }

  /* ── skeleton (first-paint placeholder while cold backend warms up) ─
     meticulous-tailoring-canvas D-E4 (Issue #10). 12 cells in a 3-col
     grid mirror the real .ml-grid layout so the swap is visually
     stable when items arrive. JS toggles via inline style.display
     ('' = visible, 'none' = hidden); the CSS default is grid so the
     element renders only when init() opts in. ── */

  .ml-skeleton {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 2px;
    padding: 0;
  }

  /* S2 D2-B: skeleton mirrors the grid's size attribute so the
     placeholder count matches what the real grid will paint. */
  .ml-skeleton[data-size="small"]  { grid-template-columns: repeat(4, 1fr); }
  .ml-skeleton[data-size="medium"] { grid-template-columns: repeat(3, 1fr); }
  .ml-skeleton[data-size="large"]  { grid-template-columns: repeat(2, 1fr); }

  .ml-skel-cell {
    aspect-ratio: 1 / 1;
    background: linear-gradient(
      90deg,
      rgba(0, 0, 0, 0.04) 25%,
      rgba(0, 0, 0, 0.08) 50%,
      rgba(0, 0, 0, 0.04) 75%
    );
    background-size: 200% 100%;
    animation: ml-shimmer 1.4s ease-in-out infinite;
  }

  @keyframes ml-shimmer {
    0% { background-position: 200% 0; }
    100% { background-position: -200% 0; }
  }

  /* ── error pill (D-E3 + D-E4) ────────────────────────────────────── */

  .ml-error {
    padding: 16px;
  }

  .ml-error-pill {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    background: #fff5f5;
    border: 1px solid #fbbbbb;
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
  }

  .ml-error-icon {
    flex-shrink: 0;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: #d92d20;
    color: #fff;
    font-weight: 700;
    font-size: 14px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  .ml-error-text {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
  }

  .ml-error-title {
    color: #7a271a;
    font-size: 14px;
    font-weight: 600;
  }

  .ml-error-detail {
    color: #b42318;
    font-size: 12px;
  }

  .ml-error-detail:empty {
    display: none;
  }

  .ml-error-retry {
    flex-shrink: 0;
    background: #d92d20;
    color: #fff;
    border: none;
    border-radius: 8px;
    padding: 8px 14px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }

  .ml-error-retry:active {
    background: #b42318;
  }

  /* crisp-anchoring-cradle S4 D4-B: extend-timeout affordance.
     Shown only when prior fetch aborted on the 20s timeout AND the user
     has not yet opted in to the 60s extension. Retry stays primary (red
     CTA); Wait-longer is the secondary action. */
  .ml-error-extend {
    flex-shrink: 0;
    background: transparent;
    color: #7a271a;
    border: 1px solid #fbbbbb;
    border-radius: 8px;
    padding: 8px 12px;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }

  .ml-error-extend:active {
    background: #fbe7e7;
  }

  /* crisp-anchoring-cradle S4 D4-B: partial-load "Loading more…" footer.
     Surfaces between rendered grid and sentinel so a slow next-page fetch
     reads as progress, not stall. Hidden when hasMore=false or items=[]. */
  .ml-more-footer {
    padding: 16px;
    text-align: center;
    color: #667085;
    font-size: 12px;
    font-style: italic;
  }

  /* ── sentinel (infinite scroll trigger) ──────────────────────────── */

  .ml-sentinel {
    height: 1px;
    width: 100%;
  }

  /* ── filter bottom sheet content ─────────────────────────────────── */

  .ml-filter-content {
    padding: 8px 0;
  }

  .ml-filter-section {
    margin-bottom: 20px;
  }

  .ml-filter-heading {
    font-size: 14px;
    font-weight: 600;
    color: var(--color-text-light, #666);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin: 0 0 10px;
  }

  .ml-filter-chips {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
  }

  .ml-chip {
    padding: 8px 16px;
    border-radius: 20px;
    border: 1px solid var(--color-border, #d0d0d8);
    background: var(--color-bg, #fff);
    color: var(--color-text, #1a1a2e);
    font-size: 14px;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: background 0.15s, color 0.15s, border-color 0.15s;
  }

  .ml-chip:active {
    background: var(--color-bg-secondary, #e8e8ec);
  }

  .ml-chip-active {
    background: var(--color-primary, #4f46e5);
    color: #fff;
    border-color: var(--color-primary, #4f46e5);
  }

  .ml-filter-person-active {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 8px 12px;
    background: var(--color-bg-secondary, #f0f0f4);
    border-radius: 10px;
  }

  .ml-clear-person {
    border: none;
    background: none;
    color: var(--color-primary, #4f46e5);
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
  }

  .ml-filter-hint {
    font-size: 13px;
    color: var(--color-text-light, #999);
    margin: 0;
  }

  /* ── sweeping-unifying-canopy S3 D1 / Issue #7 ─────────────────────────
     One-screen Filter Media modal: 7 categories visible at a glance, each
     with a per-category popup qualifier (<details>/<summary> accordion).
     Footer Reset + Apply commit staged changes. */
  .ml-filter-onescreen {
    display: flex;
    flex-direction: column;
    padding: 0;
  }

  .ml-fq-list {
    display: flex;
    flex-direction: column;
    gap: 1px;
    background: var(--color-border, #d0d0d8);
  }

  .ml-fq-cat {
    background: var(--color-bg, #fff);
    overflow: hidden;
  }

  .ml-fq-summary {
    display: grid;
    grid-template-columns: 24px 1fr auto 16px;
    align-items: center;
    gap: 12px;
    padding: 14px 16px;
    cursor: pointer;
    list-style: none;
    -webkit-tap-highlight-color: transparent;
    min-height: 48px;
  }

  .ml-fq-summary::-webkit-details-marker {
    display: none;
  }

  .ml-fq-cat[open] .ml-fq-summary {
    background: var(--color-bg-secondary, #f4f4f8);
    border-bottom: 1px solid var(--color-border, #e0e0e6);
  }

  .ml-fq-icon {
    font-size: 18px;
    line-height: 1;
    width: 24px;
    text-align: center;
  }

  .ml-fq-label {
    font-size: 15px;
    font-weight: 600;
    color: var(--color-text, #1a1a2e);
  }

  .ml-fq-value {
    font-size: 14px;
    color: var(--color-text-light, #666);
    text-align: right;
    max-width: 60vw;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .ml-fq-chevron {
    font-size: 14px;
    color: var(--color-text-light, #999);
    transition: transform 0.15s;
  }

  .ml-fq-cat[open] .ml-fq-chevron {
    transform: rotate(180deg);
  }

  .ml-fq-popup {
    padding: 14px 16px 18px;
    background: var(--color-bg-secondary, #f4f4f8);
    border-bottom: 1px solid var(--color-border, #d0d0d8);
  }

  .ml-fq-input {
    width: 100%;
    padding: 10px 14px;
    border: 1px solid var(--color-border, #d0d0d8);
    border-radius: 10px;
    font-size: 16px;  /* iOS: prevent zoom on focus */
    background: var(--color-bg, #fff);
    color: var(--color-text, #1a1a2e);
    -webkit-appearance: none;
  }
  .ml-fq-input:focus {
    outline: none;
    border-color: var(--color-primary, #4f46e5);
    box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.18);
  }

  .ml-fq-chips {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
  }

  .ml-fq-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 8px 12px;
    background: var(--color-bg, #fff);
    border-radius: 10px;
  }

  .ml-fq-current {
    font-size: 14px;
    color: var(--color-text, #1a1a2e);
  }

  .ml-fq-clear {
    border: none;
    background: none;
    color: var(--color-primary, #4f46e5);
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    padding: 4px 8px;
  }

  .ml-fq-hint {
    font-size: 13px;
    color: var(--color-text-light, #6b7280);
    margin: 0;
    line-height: 1.5;
  }

  .ml-fq-date-row {
    display: flex;
    gap: 10px;
    margin-bottom: 10px;
  }

  .ml-fq-date-label {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 4px;
    font-size: 12px;
    color: var(--color-text-light, #6b7280);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    font-weight: 600;
  }

  .ml-fq-date-label input {
    padding: 8px 10px;
    border: 1px solid var(--color-border, #d0d0d8);
    border-radius: 8px;
    font-size: 16px;
    background: var(--color-bg, #fff);
    color: var(--color-text, #1a1a2e);
    -webkit-appearance: none;
  }

  .ml-fq-date-shortcuts {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
  }

  .ml-fq-date-shortcuts button {
    padding: 6px 12px;
    border: 1px solid var(--color-border, #d0d0d8);
    border-radius: 16px;
    background: var(--color-bg, #fff);
    color: var(--color-text, #1a1a2e);
    font-size: 13px;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .ml-fq-date-shortcuts button:active {
    background: var(--color-bg-secondary, #e8e8ec);
  }

  .ml-fq-footer {
    display: flex;
    gap: 10px;
    padding: 12px 16px;
    background: var(--color-bg-secondary, #f4f4f8);
    border-top: 1px solid var(--color-border, #d0d0d8);
    position: sticky;
    bottom: 0;
    /* The TabBar clearance this footer used to carry (embracing-iphone-
       immersion S1 D7, 2026-05-12 — Apply clipped under the bar) now lives in
       the primitive: `.bottom-sheet.sheet-fullscreen` anchors above the bar
       (mobile-bottom-sheet.css; converging-mobile-import S6). Re-adding a
       reservation here would leave a blank band above the bar. */
  }

  .ml-fq-reset,
  .ml-fq-apply {
    flex: 1;
    padding: 12px 16px;
    border-radius: 10px;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    border: 1px solid var(--color-border, #d0d0d8);
  }

  .ml-fq-reset {
    background: var(--color-bg, #fff);
    color: var(--color-text, #1a1a2e);
  }
  .ml-fq-reset:active {
    background: var(--color-bg-secondary, #e8e8ec);
  }

  .ml-fq-apply {
    background: var(--color-primary, #4f46e5);
    color: #fff;
    border-color: var(--color-primary, #4f46e5);
  }
  .ml-fq-apply:active {
    background: #3f37b8;
  }

  /* ── sweeping-unifying-canopy S3 D2 / Issue #8 ─────────────────────────
     Active filter chips become two-region: body re-opens the canonical
     Filter Media modal scrolled to the relevant category; ✕ clears just
     that filter. Routes chip-tap entry through the same canonical modal
     as the funnel button. */
  .ml-active-chip {
    display: inline-flex;
    align-items: center;
    background: var(--color-bg-secondary, #f0f0f4);
    border-radius: 16px;
    padding: 0;
    overflow: hidden;
    border: 1px solid var(--color-border, #d0d0d8);
  }

  .ml-active-chip .ml-chip-body {
    border: none;
    background: transparent;
    padding: 6px 10px 6px 12px;
    font-size: 13px;
    color: var(--color-text, #1a1a2e);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    max-width: 50vw;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .ml-active-chip .ml-chip-body:active {
    background: var(--color-bg-tertiary, #e0e0e6);
  }

  .ml-active-chip .ml-chip-clear {
    border: none;
    border-left: 1px solid var(--color-border, #d0d0d8);
    background: transparent;
    padding: 6px 10px;
    font-size: 16px;
    line-height: 1;
    color: var(--color-text-light, #6b7280);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .ml-active-chip .ml-chip-clear:active {
    background: var(--color-bg-tertiary, #e0e0e6);
  }

}
