/* ═══════════════════════════════════════════════════════════════════════════
   RESPONSIVE — AUTHORITATIVE MOBILE / TABLET LAYER
   ───────────────────────────────────────────────────────────────────────────
   WHY THIS FILE LOADS LAST
   ------------------------
   base.html used to end its <head> with, in order:
       main.css → responsive.css → platform-mobile.css → navbar-mobile.css
       → responsive-complete.css → {% block extra_css %}

   Every platform page fills extra_css with platform.css (plus its own
   page_*.css), so the *desktop* stylesheet won the cascade against all four
   "mobile" stylesheets at equal specificity. Whole blocks of mobile CSS were
   dead on arrival — e.g. platform-mobile.css hides .top-search below 640px,
   but platform.css re-declares `.top-search { display: flex }` afterwards, so
   the search bar kept crowding the phone top bar and pushed the language
   toggle off screen.

   This file is linked AFTER extra_css, so it is the last word on small
   screens. Keep it that way: shell/navigation rules that must hold on phones
   and tablets belong here, not in the earlier four files.

   BREAKPOINTS
   -----------
     ≤ 767px                  phone
     768px – 1023px           tablet portrait  (iPad mini/Air/Pro portrait)
     1024px – 1200px          tablet landscape / small laptop window
     ≥ 1201px                 desktop — untouched by this file

   The sidebar's "drawer vs icon rail" split is pointer-aware rather than
   width-only: an icon-only rail depends on hover tooltips to be legible, and
   a touch device has no hover. See NAV DRAWER below.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  /* Single source of truth for the sticky top bar's height. Anything that
     sticks below it (settings tab strip, filter bars) offsets by this instead
     of hardcoding 56/52px and drifting out of sync. */
  --topbar-h: 56px;
}

/* ═══════════════════════════════════════════════════════════════════════════
   1. NAV DRAWER — sidebar legibility on touch devices
   ───────────────────────────────────────────────────────────────────────────
   platform.css collapses the sidebar to a 68px icon rail from 769px to 1200px
   and hides every text label. The only thing left explaining the icons is the
   links' `title` attribute — a hover tooltip. (Their `data-tooltip` attribute
   has no CSS anywhere in this codebase, so it explains nothing at all.)
   Portrait tablets (768/810/820/834px) and landscape iPads (1024px) all land
   inside that range, and none of them can hover — so the primary navigation
   became eight unlabeled glyphs with no way to identify them.

   Below, any viewport under 1024px, plus anything under 1200px on a coarse
   pointer, gets the full labelled sidebar as an overlay drawer instead.
   ═══════════════════════════════════════════════════════════════════════════ */

@media (max-width: 1023px), (max-width: 1200px) and (pointer: coarse) {
  .sidebar {
    width: min(288px, 86vw);
    transform: translateX(-100%);
    transition: transform .28s cubic-bezier(.16, 1, .3, 1);
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  .sidebar.open {
    transform: translateX(0);
    box-shadow: 24px 0 60px rgba(0, 0, 0, .35);
  }

  /* Undo the icon-rail treatment: in drawer mode there is room for labels and
     no hover to fall back on. These mirror platform.css's
     `.sidebar:not(.expanded) …` rail rules, which declare `display: none
     !important` — so matching that weight is the only way to win, selector
     specificity alone is not enough. */
  .sidebar:not(.expanded) .sb-label,
  .sidebar:not(.expanded) .sb-logo .brand-word { display: inline !important; }

  .sidebar:not(.expanded) .sb-section-label,
  .sidebar:not(.expanded) .sb-sub,
  .sidebar:not(.expanded) .sb-name { display: block !important; }

  .sidebar:not(.expanded) .sb-link {
    justify-content: flex-start;
    padding: .75rem .875rem;
  }

  .sidebar:not(.expanded) .sb-brand {
    padding: 1.125rem 1rem;
    text-align: left;
    justify-content: space-between;
  }

  .sidebar:not(.expanded) .sb-user-section { padding: .875rem .75rem; }
  .sidebar:not(.expanded) .sb-user-card { justify-content: flex-start; padding: .625rem; }

  /* The collapse chevron only makes sense for the rail. In drawer mode the
     overlay and the Esc key close it. */
  .sb-collapse-btn { display: none !important; }

  /* Drawer floats above the page; content never reserves space for it. */
  .main-content,
  .main-content.sb-expanded { margin-left: 0 !important; }

  .menu-toggle { display: flex !important; }

  .sidebar-overlay { display: block; }
  .sidebar-overlay.active { opacity: 1; pointer-events: auto; }

  /* Comfortable touch rows in the drawer. */
  .sb-link { min-height: 44px; }
}

/* Sidebar nav must scroll independently when the drawer is taller than a
   short (landscape phone) viewport — otherwise the lower links and the logout
   row are simply unreachable. */
@media (max-width: 1200px) {
  .sidebar { display: flex; flex-direction: column; }
  .sb-nav { flex: 1 1 auto; overflow-y: auto; min-height: 0; }
  .sb-user-section { flex-shrink: 0; }
}

/* The user's display name is clipped mid-word in the drawer ("María Fernanda
   R…" against a 143px box). Let it use the drawer's real width. */
@media (max-width: 1200px) {
  .sb-name,
  .sb-sub {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 100%;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   2. TOP BAR — fit the controls on a phone
   ───────────────────────────────────────────────────────────────────────────
   At 390px the inline search field consumed the row and left no space for the
   language toggle. (It was also being hidden outright below 420px by an
   unscoped rule in navbar-mobile.css meant for the landing page's nav pill —
   fixed there.) The field now collapses behind a search button and takes the
   row back when tapped, with the action cluster stepping aside for it; see
   responsive-nav.js.
   ═══════════════════════════════════════════════════════════════════════════ */

.top-search-toggle { display: none; }

@media (max-width: 767px) {
  .top-bar {
    position: sticky;
    height: var(--topbar-h);
    padding: 0 .75rem;
    gap: .5rem;
  }

  .top-search { display: none; }

  .top-search-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    flex-shrink: 0;
    border: 1px solid var(--border);
    border-radius: 9px;
    background: transparent;
    color: var(--ink-3);
    cursor: pointer;
    padding: 0;
  }

  .top-search-toggle .tst-close { display: none; }

  /* Expanded state: the field takes the row inline and the avatar/bell/language
     cluster steps aside. Laying it over the bar with absolute positioning
     instead would mean hardcoding an offset that clears the hamburger and the
     toggle — and getting that arithmetic wrong puts the close button
     underneath the field it is meant to close. */
  .top-bar.search-open .top-search {
    display: flex;
    flex: 1 1 auto;
    width: auto;
    min-width: 0;
    max-width: none;
    height: 38px;
  }

  .top-bar.search-open .top-actions { display: none; }
  .top-bar.search-open .top-search-toggle .tst-open { display: none; }
  .top-bar.search-open .top-search-toggle .tst-close { display: block; }

  .top-actions { gap: .375rem; margin-left: auto; }

  /* Keep every top-bar control at a real touch size. */
  .menu-toggle { width: 38px; height: 38px; }
  .notif-btn { width: 38px; height: 38px; }
  .top-av { width: 34px; height: 34px; }
  .pl-btn { padding: .3rem .5rem; font-size: .66rem; min-height: 32px; }
}

/* The top bar is the page's only escape hatch on a phone — it must never
   scroll away or be overlapped by page content. */
@media (max-width: 1023px) {
  .top-bar {
    position: sticky;
    top: 0;
    z-index: 45;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   3. DROPDOWN PANELS & MODALS — reachable content
   ───────────────────────────────────────────────────────────────────────────
   platform-mobile.css turns .dropdown-panel into a bottom sheet under 640px
   but never caps its height, so a notification list longer than the viewport
   ran off the top with nothing to scroll. The modals are handled further down
   this section — they cap at 90vh, which on iOS Safari measures the viewport
   with the toolbars retracted and so overshoots what is actually visible.
   ═══════════════════════════════════════════════════════════════════════════ */

@media (max-width: 767px) {
  .dropdown-panel {
    position: fixed !important;
    left: 0 !important;
    right: 0 !important;
    bottom: 0 !important;
    top: auto !important;
    width: 100% !important;
    max-width: none !important;
    max-height: 80dvh;
    display: flex;
    flex-direction: column;
    border-radius: 16px 16px 0 0 !important;
    transform: translateY(100%);
    z-index: 1200 !important;
  }

  .dropdown-panel.open { transform: translateY(0); }

  /* The scrollable region inside the sheet. Without an explicit min-height:0
     a flex child refuses to shrink below its content and the cap above does
     nothing. */
  .dropdown-panel .dp-content,
  .dropdown-panel .notif-list {
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
  }

  .dropdown-panel .dp-header,
  .dropdown-panel .pd-header,
  .dropdown-panel .dp-footer { flex-shrink: 0; }

  /* Sheets need a bit of breathing room under the home indicator. */
  .dropdown-panel { padding-bottom: env(safe-area-inset-bottom, 0); }

  .pd-link { min-height: 44px; }
  .dp-item { min-height: 48px; }
}

/* The real modals in this codebase are .auth-modal (landing),
   .details-modal-content (marketplace apply/details) and .grp-modal-inner
   (messages) — a bare `.modal` class is never rendered, so the rules keyed to
   it in responsive.css and platform-mobile.css are dead either way.

   All three cap themselves at 90vh. On iOS Safari `vh` is the *large* viewport
   — measured with the toolbars retracted — so a modal at 90vh extends under
   the browser chrome and its footer buttons cannot be tapped. `dvh` tracks the
   viewport that is actually visible. */
@media (max-width: 900px) {
  .auth-modal,
  .details-modal-content,
  .grp-modal-inner {
    max-height: 88dvh;
    display: flex;
    flex-direction: column;
  }

  /* A flex child will not shrink past its content unless told to, which would
     defeat the cap above and push the footer off screen again. */
  .details-modal-body,
  .auth-modal-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }

  .details-modal-header,
  .details-modal-footer { flex-shrink: 0; }

  .details-modal-footer {
    padding-bottom: calc(1rem + env(safe-area-inset-bottom, 0));
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
  }

  .details-modal-footer > * { flex: 1 1 auto; min-height: 44px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   4. SETTINGS — the eight panels were unreachable below 900px
   ───────────────────────────────────────────────────────────────────────────
   page_settings.css did `@media(max-width:900px){ .settings-nav{display:none} }`.
   The nav is the *only* control that swaps .settings-panel.active, so on every
   phone and most tablets the page was permanently stuck on "Cuenta" —
   Seguridad, Notificaciones, Privacidad, Métodos de pago, Facturación,
   Preferencias and Zona peligrosa had no route at all.

   Restored as a horizontally scrollable tab strip pinned under the top bar.
   ═══════════════════════════════════════════════════════════════════════════ */

@media (max-width: 900px) {
  .settings-shell { grid-template-columns: minmax(0, 1fr); }

  .settings-nav {
    display: flex;
    gap: .5rem;
    align-items: center;
    overflow-x: auto;
    overflow-y: hidden;
    padding: .5rem;
    border-radius: 14px;
    position: sticky;
    top: calc(var(--topbar-h) + .5rem);
    z-index: 20;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
  }

  .settings-nav::-webkit-scrollbar { display: none; }

  /* Group headings and rules only read as structure in a vertical list. */
  .sn-group-label,
  .sn-divider { display: none; }

  .sn-item {
    width: auto;
    flex: 0 0 auto;
    white-space: nowrap;
    border-radius: 10px;
    padding: .5rem .875rem;
    min-height: 44px;
    gap: .5rem;
  }

  /* The 3px active bar is anchored to a full-width row; as a pill it renders
     as a stray sliver on the left edge. */
  .sn-item.active::before { display: none; }

  .sn-item .sn-icon {
    width: 24px;
    height: 24px;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   5. MARKETPLACE — filters were unreachable from 0 to 1024px
   ───────────────────────────────────────────────────────────────────────────
   Both page_marketplace.css and platform.css hid .mp-filters below 900px, and
   page_marketplace.css hid it again for 641–1024px. Category, contract type,
   budget and duration were desktop-only, which is most of the point of a job
   board. The panel now opens as a bottom sheet from a "Filtros" button.

   A bottom sheet (rather than a left drawer) keeps it clear of the navigation
   drawer, which occupies the left edge at exactly these widths.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Declared before the media query, not after it: an unqualified rule later in
   the file would out-cascade the display it is given inside the query. */
.mp-filters-toggle { display: none; }
.mp-filters-backdrop { display: none; }
.mpf-close { display: none; }

@media (max-width: 1024px) {
  .mp-shell {
    grid-template-columns: minmax(0, 1fr);
    height: auto;
    overflow: visible;
  }

  .mp-filters-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .45rem;
    min-height: 42px;
    padding: .5rem 1rem;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: #fff;
    color: var(--ink-2);
    font-family: var(--font);
    font-size: .84rem;
    font-weight: 700;
    cursor: pointer;
    flex-shrink: 0;
  }

  .mp-filters-toggle .mpft-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 .3rem;
    border-radius: 999px;
    background: var(--accent);
    color: #fff;
    font-size: .65rem;
    font-weight: 800;
  }

  .mp-filters-toggle .mpft-count:empty { display: none; }

  .mp-sidebar-wrapper {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: auto;
    width: 100%;
    height: auto;
    max-height: 85dvh;
    display: flex;
    flex-direction: column;
    border-radius: 18px 18px 0 0;
    border-bottom: none;
    transform: translateY(100%);
    transition: transform .28s cubic-bezier(.16, 1, .3, 1);
    z-index: 1150;
    overflow: hidden;
    box-shadow: 0 -18px 50px rgba(0, 0, 0, .22);
  }

  .mp-sidebar-wrapper.open { transform: translateY(0); }

  .mp-filters {
    display: flex;
    flex-direction: column;
    height: auto;
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    padding: 0 1rem calc(1.25rem + env(safe-area-inset-bottom, 0));
  }

  /* The desktop panel paints its own scrollbar thumb against a fixed-height
     wrapper; inside a sheet the native scroller is the honest one. */
  .mpf-custom-scroll { display: none; }

  .mpf-hd {
    position: sticky;
    top: 0;
    z-index: 1;
    background: #fff;
    padding: 1rem 0 .75rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
  }

  .mpf-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border: none;
    border-radius: 8px;
    background: var(--surface-2);
    color: var(--ink-3);
    cursor: pointer;
    flex-shrink: 0;
    padding: 0;
  }

  .mp-filters-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, .45);
    z-index: 1140;
    opacity: 0;
    pointer-events: none;
    transition: opacity .28s;
  }

  .mp-filters-backdrop.active {
    opacity: 1;
    pointer-events: auto;
  }

  /* Checkbox/radio rows are the whole interaction here — give them a target. */
  .mpf-item {
    min-height: 42px;
    display: flex;
    align-items: center;
    gap: .6rem;
  }

  .mpf-item input[type="checkbox"],
  .mpf-item input[type="radio"] {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
  }

  .mpf-range { min-height: 32px; }

  /* The main column owns the scroll once the grid stops being a fixed-height
     two-pane layout. */
  .mp-main {
    height: auto;
    overflow: visible;
    min-width: 0;
  }
}

/* The header's control row is search (flex:1) + sort box + the new Filtros
   button on one line. At 390px that left the search field collapsed to an
   empty stub and pushed "Filtros" off the right edge. Give search its own row
   and let sort and Filtros share the next one. */
@media (max-width: 767px) {
  .mp-controls-bar {
    flex-wrap: wrap;
    gap: .625rem;
  }

  .mp-search-wrapper { flex: 1 0 100%; min-width: 0; }

  .mp-sort-box {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0 .875rem;
  }

  .mp-sort-sel { min-width: 0; }
  .mp-filters-toggle { flex: 0 0 auto; }
}

/* `repeat(3, 1fr)` is really `repeat(3, minmax(auto, 1fr))`: each card refuses
   to shrink below its own content, so the strip overflowed its container and
   the third card was cut off at the viewport edge. minmax(0, 1fr) lets them
   share the row honestly; stacking the icon keeps the labels readable at a
   third of a phone's width. */
@media (max-width: 1023px) {
  .mp-stats-row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .mp-stat-card { min-width: 0; }
}

@media (max-width: 560px) {
  .mp-stat-card {
    flex-direction: column;
    align-items: flex-start;
    gap: .4rem;
    padding: .75rem;
  }

  .mp-stat-lbl { font-size: .62rem; line-height: 1.25; }
  .mp-stat-val { font-size: 1.05rem; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   6. MESSAGES — a 64px avatar rail is not a conversation list
   ───────────────────────────────────────────────────────────────────────────
   page_messages.css shrinks .msg-panel to 64px under 760px and hides the
   contact name, search box, filter tabs and per-message actions. What is left
   is a column of anonymous avatars: you cannot tell who you are writing to,
   search for anyone, or start a conversation.

   Phones get the standard two-view pattern instead — full-width inbox, and
   the thread replaces it once a conversation is opened (responsive-nav.js
   toggles .msg-chat-open).
   ═══════════════════════════════════════════════════════════════════════════ */

.msg-back-btn { display: none; }

@media (max-width: 760px) {
  .msg-layout {
    height: calc(100dvh - var(--topbar-h) - 2rem);
    border-radius: 14px;
  }

  .msg-panel {
    width: 100%;
    flex: 1 1 auto;
    min-width: 0;
    border-right: none;
  }

  /* Everything page_messages.css collapsed away at this width. Each display
     is restated explicitly rather than with `revert`: revert drops to the
     user-agent value (block for a div), which would flatten the flex rows
     these components are built on. */
  /* `.panel-collapse` is on two elements that need different display values —
     .panel-head is a block that stacks its title row above the search box,
     .filter-tabs is a flex row. Restoring them through the shared class made
     .panel-head a flex container and laid the search box alongside the title,
     on top of it. So each is restored by its own selector. */
  .msg-panel .panel-head { display: block; }
  .msg-panel .panel-head-title { display: block; }
  .msg-panel .search-wrap { display: flex; }

  .msg-panel .filter-tabs {
    display: flex;
    overflow-x: auto;
    scrollbar-width: none;
  }

  .msg-panel .filter-tabs::-webkit-scrollbar { display: none; }

  /* Stays icon-only — it is a "+" with no label, so widening it just made a
     stretched pill that collided with the title. */
  .msg-panel .panel-compose-btn.icon-only {
    width: 40px;
    height: 40px;
  }

  .contact-item {
    padding: .625rem .75rem;
    justify-content: flex-start;
    min-height: 60px;
  }

  .c-info { display: block !important; }
  .c-meta { display: flex !important; }

  /* The per-conversation "…" menu is hover-revealed on desktop, so on touch
     it is unreachable unless it is always present. */
  .c-dots {
    display: inline-flex !important;
    opacity: 1;
  }

  .c-av { width: 42px; height: 42px; }

  /* Inbox and thread are two screens, not two columns. */
  .chat-area { display: none; }

  .msg-layout.msg-chat-open .msg-panel { display: none; }

  .msg-layout.msg-chat-open .chat-area {
    display: flex;
    flex: 1 1 auto;
    min-width: 0;
  }

  .msg-layout.msg-chat-open .msg-back-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    flex-shrink: 0;
    margin-right: .25rem;
    border: none;
    border-radius: 9px;
    background: var(--surface-2);
    color: var(--ink-2);
    cursor: pointer;
    padding: 0;
  }

  /* Per-message actions are hover-only on desktop; on touch they must be
     permanently visible or they do not exist. */
  .msg-actions {
    display: flex !important;
    opacity: 1 !important;
    position: static;
    gap: .25rem;
  }

  .msg-actions button { min-width: 34px; min-height: 34px; }

  .chat-actions { gap: .25rem; }
  .chat-actions button { min-width: 38px; min-height: 38px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   7. SIDE PANELS — stack instead of vanish
   ───────────────────────────────────────────────────────────────────────────
   Several pages drop their right-hand aside with display:none on narrow
   screens. That is right for purely decorative panels and wrong for the ones
   holding controls — the freelancer wallet's "Meta del mes" card, for
   instance, contains the only button that edits the monthly goal, and it
   disappeared on every device under 1100px.

   These stack under the main column instead.
   ═══════════════════════════════════════════════════════════════════════════ */

@media (max-width: 1100px) {
  .wal-grid,
  .prof-layout,
  .cont-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .wal-aside,
  .prof-right,
  .cont-aside {
    display: block;
    width: 100%;
    min-width: 0;
    margin-top: 1.25rem;
    position: static;
  }

  /* The client wallet aside is intentionally empty (two TODO placeholders);
     an empty block would just add dead space. */
  .wal-aside:empty { display: none; }
}

@media (max-width: 1024px) {
  .dash-shell,
  .frnet-layout {
    grid-template-columns: minmax(0, 1fr);
  }

  .dash-right,
  .frnet-right {
    display: block !important;
    width: 100%;
    min-width: 0;
    margin-top: 1.25rem;
    position: static;
    flex: 1 1 auto;
  }
}

@media (max-width: 900px) {
  .cp-layout { grid-template-columns: minmax(0, 1fr); }

  .cpl-aside {
    display: block;
    width: 100%;
    min-width: 0;
    margin-top: 1.25rem;
    position: static;
  }
}

@media (max-width: 700px) {
  /* page_mis_publicaciones.css hid the identity block's action row along with
     its decorative dividers — .mp-id-actions holds "Editar perfil" and
     "Ver perfil público". */
  .mp-id-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin-top: .875rem;
  }

  .mp-id-stats {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem 1.25rem;
    margin-top: .75rem;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   8. OVERFLOW GUARDS — content wider than its box
   ───────────────────────────────────────────────────────────────────────────
   A flex or grid child defaults to min-width:auto, i.e. it refuses to shrink
   below its intrinsic content width. Combined with an ancestor that clips
   overflow, the tail of the content becomes unreachable rather than
   scrollable. Measured cases at 390px before this file existed:

       #courseSearch          659px wide inside a 390px viewport
       .page-content (cursos) 732px of content in a 390px box
       .cp-item (proyectos)   771px of content in a 356px box

   The fix is min-width:0 on the containers that legitimately need to shrink.
   ═══════════════════════════════════════════════════════════════════════════ */

@media (max-width: 1200px) {
  .page-content,
  .main-content,
  .platform-layout > * {
    min-width: 0;
  }

  /* Text inputs inside flex rows: let them shrink to the row, not to their
     placeholder's length. */
  .ac-search,
  .mp-search-wrapper,
  .search-wrap,
  .top-search,
  .grp-search-wrap {
    min-width: 0;
  }

  .ac-search input,
  .mp-search-input,
  .search-wrap input,
  .top-search input,
  .grp-search-wrap input {
    min-width: 0;
    width: 100%;
  }
}

@media (max-width: 768px) {
  /* Wide, multi-column rows collapse to stacked blocks rather than being cut
     off at the viewport edge. */
  .cp-item,
  .cpi-main,
  .mp-header-top,
  .ac-toolbar > div {
    flex-wrap: wrap;
    min-width: 0;
  }

  .cp-item {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: .75rem;
  }

  .cpi-fin,
  .cpi-actions,
  .cpi-freelancers {
    width: 100%;
    min-width: 0;
  }

  .cpi-actions { display: flex; gap: .5rem; }
  .cpi-actions .cpi-btn { flex: 1 1 auto; }

  .cpi-title,
  .chat-name,
  .mpc-title {
    overflow-wrap: anywhere;
    white-space: normal;
  }
}

/* Headings that carry a person's name are clipped mid-word by a fixed-width
   box well past phone widths — measured 236px of text in a 138px box at 820,
   and still 236 in 178 at 1024. Let them wrap instead. */
@media (max-width: 1200px) {
  .mp-id-name,
  .prof-name,
  .chat-welcome {
    overflow-wrap: anywhere;
    white-space: normal;
    min-width: 0;
  }

  .mp-id-name { max-width: 100%; }
}

/* Long unbroken strings (URLs, emails, generated ids) are a common source of
   horizontal scroll on narrow screens. */
@media (max-width: 767px) {
  .page-content p,
  .page-content h1,
  .page-content h2,
  .page-content h3,
  .page-content td,
  .page-content li { overflow-wrap: anywhere; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   9. TOUCH TARGETS
   ───────────────────────────────────────────────────────────────────────────
   Measured on the rendered pages, the following all fell under 34px tall on
   phone and tablet: .mp-tab (28), .mp-sort-btn (23), .ac-filter-chip (26),
   .cpi-btn (31), .prof-banner-edit (29), .btn-publish (29), .filter-tab (31),
   .mpc-apply-btn (31), .pl-btn (22), .tool-btn (30).

   iOS asks for 44px and Android for 48dp. Scoped to coarse pointers so mouse
   users keep the tighter desktop rhythm.
   ═══════════════════════════════════════════════════════════════════════════ */

@media (pointer: coarse) {
  .sn-item,
  .mp-tab,
  .ac-tab,
  .ac-filter-chip,
  .mp-sort-btn,
  .filter-tab,
  .cpi-btn,
  .mpc-save-btn,
  .mpc-detail-btn,
  .mpc-apply-btn,
  .mp-btn-primary,
  .mp-btn-ghost,
  .prof-btn,
  .prof-banner-edit,
  .btn-publish,
  .tool-btn,
  .wc-link,
  .ac-btn-ghost,
  .panel-compose-btn,
  .pd-link,
  .sb-link {
    min-height: 40px;
  }

  /* Icon-only controls need width as well as height. */
  .tool-btn,
  .notif-btn,
  .menu-toggle,
  .top-av,
  .top-search-toggle,
  .mpf-close,
  .msg-back-btn { min-width: 38px; }

  /* The language switcher's segments measured 35x22 — the smallest hit area
     in the whole top bar, and the control a bilingual user reaches for most. */
  .pl-btn {
    min-height: 32px;
    min-width: 34px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  /* Log out sits behind a bare 14px lucide glyph in the sidebar user card. */
  .sb-user-card a,
  .sb-logo {
    min-height: 34px;
    display: inline-flex;
    align-items: center;
  }

  /* Native controls: 16px font also stops iOS Safari zooming on focus. */
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]),
  select,
  textarea { font-size: 16px; }

  select { min-height: 42px; }

  /* Checkboxes and radios are otherwise ~13px and very hard to hit. */
  input[type="checkbox"],
  input[type="radio"] { width: 18px; height: 18px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   10. HORIZONTAL TAB STRIPS
   ───────────────────────────────────────────────────────────────────────────
   Tab rows that fit on desktop wrap or clip on a phone. Make them scroll
   sideways with momentum, and hide the scrollbar so they read as a strip.
   ═══════════════════════════════════════════════════════════════════════════ */

@media (max-width: 1023px) {
  .ac-filter-tabs,
  .mp-tabs,
  .mp-tabs-wrap,
  .frnet-tabs,
  .filter-tabs {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    gap: .5rem;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
    padding-bottom: 2px;
  }

  .ac-filter-tabs::-webkit-scrollbar,
  .mp-tabs::-webkit-scrollbar,
  .mp-tabs-wrap::-webkit-scrollbar,
  .frnet-tabs::-webkit-scrollbar,
  .filter-tabs::-webkit-scrollbar { display: none; }

  .ac-filter-tabs > *,
  .mp-tabs > *,
  .mp-tabs-wrap > *,
  .frnet-tabs > *,
  .filter-tabs > * { flex: 0 0 auto; white-space: nowrap; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   11. TABLES
   ───────────────────────────────────────────────────────────────────────────
   Data tables cannot usefully reflow; give them their own scroll container so
   they never widen the page itself.
   ═══════════════════════════════════════════════════════════════════════════ */

@media (max-width: 1023px) {
  .table-wrapper,
  .ct-wrap,
  .tx-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    max-width: 100%;
  }

  .table-wrapper > table,
  .ct-wrap > table,
  .tx-wrap > table { min-width: 560px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   12. BODY SCROLL LOCK
   ───────────────────────────────────────────────────────────────────────────
   Set by responsive-nav.js whenever a drawer or sheet owns the screen, so the
   page behind does not scroll along with it.
   ═══════════════════════════════════════════════════════════════════════════ */

body.no-scroll {
  overflow: hidden;
  touch-action: none;
}
