/* ============================================================================
   MOBILE LAYER — the phone contract. See docs/architecture/mobile.md.

   Loaded LAST, after system.css, on purpose: the desktop control contract
   (32px tall, 13px type) is WRONG on a touch screen, and on a phone mobile has
   to win. Nothing here applies on a desktop pointer.

   Phase 1 (foundation): tokens, touch primitives, keyboard-safe inputs,
   overscroll containment, safe areas on the existing fixed chrome.
   Phases 2-5 (shell, sheets, per-surface, taking down the wall) are in the doc.

   Two breakpoints only. A third one 8px away from either of these is a bug.
     phone  : max-width 600px
     tablet : max-width 900px
   ============================================================================ */

:root {
    /* Zero on any device without a notch or home indicator, and zero anywhere
       the viewport meta lacks viewport-fit=cover (base.html has it). Safe to
       add unconditionally — that is the point of the fallback. */
    --safe-top: env(safe-area-inset-top, 0px);
    --safe-right: env(safe-area-inset-right, 0px);
    --safe-bottom: env(safe-area-inset-bottom, 0px);
    --safe-left: env(safe-area-inset-left, 0px);

    /* Apple HIG says 44pt, Material says 48dp. 44 is the floor we enforce. */
    --tap-target: 44px;
    /* Below 16px, iOS Safari zooms the page when a field takes focus and never
       zooms back out. This is not a preference. */
    --tap-font-size: 16px;
    --mobile-gutter: 16px;
    /* 56px is the Material bottom-navigation height and the smallest that fits
       a 24px icon over an 11px caption while every cell stays a 44px target. */
    --tab-bar-height: 56px;
}

/* ---- Touch primitives ------------------------------------------------------
   `hover: none` + `pointer: coarse` is the real test for "a finger is driving
   this", not a width breakpoint: it is true on a phone in landscape and false
   on a narrow desktop window. */
@media (hover: none) and (pointer: coarse) {

    /* The ~300ms the browser waits to see whether a tap was a double-tap-to-zoom.
       `manipulation` removes it while leaving pan and pinch alone. */
    a, button, [role="button"], input, select, textarea, label,
    .menu-item, .dropdown-item, .sidebar-item, .row-kebab,
    .posts-filter-option, .applied-filter, .applied-filter-remove {
        touch-action: manipulation;
    }

    /* iOS/Android paint their own grey box over a tapped element. Removing it
       WITHOUT supplying a replacement is how a web app starts feeling dead, so
       the :active rule below is not optional — it ships with this one. */
    html {
        -webkit-tap-highlight-color: transparent;
    }
    a:active, button:active, [role="button"]:active,
    .menu-item:active, .dropdown-item:active, .sidebar-item:active,
    .row-kebab:active, .posts-filter-option:active, .applied-filter:active {
        /* No transition in: feedback has to land inside 100ms. The fade back
           out is what makes it read as a press rather than a flicker. */
        opacity: 0.55;
        transition: none;
    }

    /* Anything a finger has to hit is at least 44px in the direction it is
       hard to hit. The VISUAL control keeps its own size — only the box grows. */
    button, [role="button"], .menu-item, .dropdown-item, .sidebar-item,
    .row-kebab, .posts-filter-option,
    /* The public pages' language selector sets its own 34px min-height at
       class specificity, which beat the bare `button` above (measured 34px
       on /auth under a coarse pointer, 2026-09-03). */
    .lfs__trigger {
        min-height: var(--tap-target);
    }

    /* Same selector list as system.css's input contract, so this wins on order
       rather than on !important. 13px is correct on a mouse and unusable here:
       it triggers the iOS focus zoom on every single field in the app. */
    input[type="text"], input[type="search"], input[type="email"],
    input[type="url"], input[type="password"], input[type="number"],
    input[type="tel"], input[type="date"], input[type="time"],
    input[type="datetime-local"], textarea, select {
        font-size: var(--tap-font-size);
        min-height: var(--tap-target);
    }

    /* A hover state on a touch device sticks after the tap and reads as
       "selected". Anything that MUST react to touch defines an :active above. */
    .menu-item:hover, .dropdown-item:hover, .sidebar-item:hover {
        background-color: transparent;
    }
}

/* ---- A scroll container with nothing to scroll ---------------------------
   workspaces.css gives #workspace-settings-content `overflow: hidden;
   overflow-y: auto` and no height of its own, so it is a scroll container
   whose scrollHeight always equals its clientHeight: nothing to scroll, ever.
   That is exactly the shape the blanket `overscroll-behavior: contain` (below)
   bit on 2026-09-02: containment on a scroller sitting at its own boundary
   refuses to chain the wheel up to .page-content-wrapper, and Settings froze
   on a laptop. The rule is gone; this makes the box not a scroll container at
   all, so nothing can latch or contain on it again. `overflow-x: clip` keeps
   the sideways clipping WITHOUT creating a scroll container (that is the
   difference between clip and hidden). Applies at every width on purpose.

   Verification note: the Chrome-extension `scroll` action dispatches NO wheel
   events; it calls scrollBy on the nearest scrollable box under the cursor.
   A scrollTop of 0 measured over a horizontal-only scroller (the 2129px
   notifications table) is that tool picking the wrong box, not a user-facing
   bug. Real wheels chain normally unless `overscroll-behavior` says otherwise. */
#workspace-settings-content {
    overflow-x: clip !important;
    overflow-y: visible !important;
}

/* ---- Scrolling ------------------------------------------------------------
   NO blanket `overscroll-behavior: contain` on .app-scrollbar. That rule
   shipped for a few hours on 2026-09-02 and broke wheel scrolling on Settings:
   #workspace-settings-content is an overflow:auto box that never overflows
   (its height equals its content), so it is a scroll container with nothing
   to scroll, and `contain` on it swallowed the wheel instead of chaining it
   up to .page-content-wrapper, the real scroller. Containment is now applied
   only to the sheet, which genuinely sits over the page. */

@media (max-width: 600px) {

    /* ---- Safe areas on the chrome that already exists ---------------------
       responsive_base_html.css reserves the home-indicator strip under the
       bottom bar. These are the three edges it does not cover: the notch in
       portrait, and both rounded corners in landscape. */
    /* border-box, NOT content-box: #page-top-bar is width:100%, so content-box
       adds the two paddings OUTSIDE that width — measured at 411px it made the
       bar 443px wide and pulled its left edge to x=-16. */
    #page-top-bar {
        padding-top: var(--safe-top);
        padding-left: calc(var(--mobile-gutter) + var(--safe-left));
        padding-right: calc(var(--mobile-gutter) + var(--safe-right));
        box-sizing: border-box;
    }
    #sidebar {
        padding-left: var(--safe-left);
        padding-right: var(--safe-right);
    }

    /* A phone is one column. Any row that survived to here is a row that was
       never really two columns. */
    #applied-filters {
        /* The chip rail keeps its horizontal scroll, but on a phone it must be
           the thing that gives way, not the search field it sits beside. */
        flex: 1 1 auto;
    }
}


/* ============================ BOTTOM SHEET ==================================
   docs/architecture/mobile.md rules 12-15. Driven by static/js/mobile/sheet.js.

   Anchored to the bottom edge so the content grows UP into the stretch zone
   while the handle stays under the thumb. Height is set in px by the script
   (snap points are a fraction of the visual viewport, not of 100vh — the
   keyboard changes the first and not the second). */
.sheet-backdrop {
    position: fixed;
    inset: 0;
    z-index: 100060;
    background-color: rgba(3, 5, 12, 0.5);
    opacity: 0;
    transition: opacity var(--transition-slow) ease;
}
.sheet-backdrop.is-open {
    opacity: 1;
}
.sheet {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 100061;
    display: flex;
    flex-direction: column;
    box-sizing: border-box;
    /* Rounded at the top only: the bottom edge is the screen edge. */
    border-radius: var(--radius-big) var(--radius-big) 0 0;
    background-color: var(--surface);
    box-shadow: 0 -8px 40px rgba(3, 5, 12, 0.32);
    /* The home indicator lives under this. */
    padding-bottom: var(--safe-bottom);
    /* Closed position. Transform only — animating `height` or `bottom` would
       drop frames on a phone (rule 29). */
    transform: translateY(100%);
    transition: transform var(--transition-slow) cubic-bezier(0.22, 1, 0.36, 1);
    /* The sheet owns vertical gestures; the page behind must not scroll. */
    overscroll-behavior: contain;
    touch-action: none;
}
.sheet.is-open {
    transform: translateY(0);
}
/* While a finger is on it the height follows the finger, so no transition. */
.sheet.is-dragging {
    transition: none;
}

.sheet-grabber {
    display: flex;
    align-items: center;
    justify-content: center;
    /* The bar is 4px; the drag target is 28px. Grow the box, not the graphic. */
    height: 28px;
    flex: 0 0 auto;
    cursor: grab;
    touch-action: none;
}
.sheet-grabber-bar {
    width: 36px;
    height: 4px;
    border-radius: var(--radius-pill);
    background-color: var(--border-color);
}
.sheet-header {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    min-height: 0;
    padding: 0 var(--mobile-gutter) 8px;
    touch-action: none;
}
.sheet-title {
    margin: 0;
    font-size: 16px;
    font-weight: 600;
    line-height: 1.2;
    color: var(--font-black);
    text-align: center;
}
.sheet-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    /* The body scrolls; the sheet itself does not. Containment here is what
       stops a flick at the end of the list from scrolling the app behind. */
    overscroll-behavior: contain;
    touch-action: pan-y;
    padding: 0 var(--mobile-gutter) var(--mobile-gutter);
}

/* ---- Action sheet: a short list of actions (rule 14) ---------------------- */
.sheet-actions {
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.sheet-action {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    min-height: var(--tap-target);
    padding: 0 12px;
    box-sizing: border-box;
    border: none;
    border-radius: var(--radius-small);
    background: transparent;
    color: var(--font-black);
    font-family: inherit;
    /* 16px, not the desktop 13px: this is a primary read on a phone. */
    font-size: 16px;
    font-weight: 500;
    line-height: 1;
    text-align: left;
    cursor: pointer;
}
.sheet-action:active {
    background-color: var(--dark-selected);
}
.sheet-action.is-destructive {
    color: var(--red-clear);
}
.sheet-action-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    color: var(--font-gray);
}
.sheet-action-icon svg {
    width: 18px;
    height: 18px;
}
.sheet-action-label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
    .sheet, .sheet-backdrop {
        transition: none;
    }
}


/* ============================ PHONE TAB BAR =================================
   docs/architecture/mobile.md rules 4-6. Hidden above the phone breakpoint —
   there is one markup, not a desktop copy and a phone copy. */
#mobile-tab-bar {
    display: none;
}

@media (max-width: 600px) {
    /* The desktop sidebar stops being navigation on a phone, but it MUST stay in
       the DOM: it owns every badge element, the create dropdown, the workspace
       selector and the brand lock, and the tab bar forwards its taps to the
       .navig-item anchors inside it. Hiding, never removing. */
    #sidebar {
        display: none;
    }
    /* ...which also retires the position:fixed workspace chip that was pinned
       over the top bar since the 1000px breakpoint and overlapped the search
       field (measured at 411px: the chip's right edge sat 114px into a field
       starting at x=0). The workspace switcher now lives in the More sheet. */
    #workspace-selector-div {
        position: static;
    }

    #mobile-tab-bar {
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        /* 25, deliberately BELOW every modal in the app (campaign 30, composer
           35, preview 38, bulk 39, brand 40, toasts 95). The first version sat
           at 100040 and painted over the composer's footer — and would have done
           the same to every one of those. Page content never declares a z-index
           this high, so the bar still sits above everything that scrolls, while
           any overlay covers it for free without an observer having to hide it.
           body.takeover-open below is the belt to this braces, for the modals
           that declare no z-index at all (#pricingModal). */
        z-index: 25;
        display: flex;
        align-items: stretch;
        justify-content: space-around;
        box-sizing: border-box;
        height: calc(var(--tab-bar-height) + var(--safe-bottom));
        /* Bonus padding on top of the normal spacing, never instead of it: the
           home indicator sits in this strip. */
        padding: 0 var(--safe-right) var(--safe-bottom) var(--safe-left);
        background-color: var(--surface);
        border-top: 1px solid var(--border-color);
    }

    /* The content ends where the tab bar begins. Replaces the old
       calc(100% - 90px), which was sized for the scrolling sidebar strip. */
    .content {
        height: calc(100% - var(--tab-bar-height) - var(--safe-bottom));
        width: 100%;
    }

    .mtab {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 3px;
        position: relative;
        flex: 1 1 0;
        /* Five equal cells, no horizontal scroll. The old bar put 6 of 11
           destinations off-screen, including Inbox at 99+ unread. */
        min-width: 0;
        /* Not a fixed height: the bar is border-box with a 1px top border, so a
           hard 56px cell overflowed it by exactly that pixel. Stretch instead. */
        align-self: stretch;
        padding: 0 2px;
        border: none;
        background: transparent;
        color: var(--font-gray);
        font-family: inherit;
        cursor: pointer;
        -webkit-tap-highlight-color: transparent;
        touch-action: manipulation;
    }
    .mtab.is-active {
        color: var(--accent-ui);
    }
    .mtab:active {
        opacity: 0.55;
        transition: none;
    }
    /* Every cell reserves the SAME icon box, sized to the largest glyph (the
       Create disc). Without this the 34px disc pushed Create's caption 5px
       below the other four — measured at 411px, baselines 824 vs 819. */
    .mtab-icon,
    .mtab-create-disc {
        display: flex;
        align-items: center;
        justify-content: center;
        flex: 0 0 auto;
        width: 34px;
        height: 34px;
    }
    .mtab-icon svg {
        width: 22px;
        height: 22px;
    }
    /* Deliberate carve-out from rule 27's 16px body minimum, written down in
       mobile.md: a tab caption is a LABELLED ICON, not body copy. The 44px
       target is the whole cell, and the icon carries the meaning. Five cells at
       375px leave ~71px each, which 11px fits in every one of the 41 locales. */
    .mtab-label {
        max-width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        font-size: 11px;
        font-weight: 500;
        line-height: 1;
    }
    .mtab.is-active .mtab-label {
        font-weight: 600;
    }

    /* Badges are mirrors of the sidebar's own, repainted by tabBar.js. */
    .mtab-badge {
        position: absolute;
        top: 4px;
        left: 50%;
        margin-left: 4px;
        min-width: 16px;
        height: 16px;
        padding: 0 4px;
        box-sizing: border-box;
        border-radius: var(--radius-pill);
        background-color: var(--red-clear);
        color: #fff;
        font-size: 10px;
        font-weight: 600;
        line-height: 16px;
        text-align: center;
    }
    .mtab-dot {
        position: absolute;
        top: 8px;
        left: 50%;
        margin-left: 8px;
        width: 7px;
        height: 7px;
        border-radius: 50%;
        background-color: var(--accent-ui);
    }

    /* Create keeps the brand gradient — the one identity moment in the chrome,
       and the only cell that is an action rather than a destination. */
    .mtab-create .mtab-create-disc {
        border-radius: var(--radius-small-medium);
        background: var(--accent-ui-gradient, var(--brand-gradient));
        color: #fff;
        box-shadow: 0 2px 10px var(--accent-ui-glow, rgba(0,0,0,0.18));
    }
    .mtab-create .mtab-create-disc svg {
        width: 18px;
        height: 18px;
    }
    .mtab-create .mtab-label {
        color: var(--font-gray);
    }

    /* ---- More sheet rows -------------------------------------------------- */
    .more-workspace {
        display: flex;
        align-items: center;
        gap: 10px;
        width: 100%;
        min-height: var(--tap-target);
        margin-bottom: 6px;
        padding: 0 12px;
        box-sizing: border-box;
        border: 1px solid var(--border-color);
        border-radius: var(--radius-small);
        background: var(--bg-blue-dark);
        color: var(--font-black);
        font-family: inherit;
        font-size: 15px;
        font-weight: 600;
        text-align: left;
        cursor: pointer;
    }
    .more-workspace-icon {
        width: 22px;
        height: 22px;
        border-radius: var(--radius-mini);
        object-fit: cover;
    }
    .more-workspace-name {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    .more-row .sheet-action-icon svg {
        width: 20px;
        height: 20px;
    }
    .more-row-badge {
        flex: 0 0 auto;
        min-width: 20px;
        padding: 0 6px;
        border-radius: var(--radius-pill);
        background-color: var(--red-clear);
        color: #fff;
        font-size: 11px;
        font-weight: 600;
        line-height: 18px;
        text-align: center;
    }
    .more-row-dot {
        flex: 0 0 auto;
        width: 7px;
        height: 7px;
        border-radius: 50%;
        background-color: var(--accent-ui);
    }
    .more-separator {
        height: 1px;
        margin: 8px 0;
        background-color: var(--border-color);
    }
}


/* ============================ PHONE APP BAR =================================
   docs/architecture/mobile.md rule 6: a title and at most one action. On a
   phone the sidebar no longer says where you are, so the bar has to. */
#app-bar-title {
    display: none;
}

@media (max-width: 600px) {
    #app-bar-title {
        display: block;
        margin: 0;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        /* 17px: the title is the largest thing in the chrome and the one piece
           of text that answers "where am I". */
        font-size: 17px;
        font-weight: 600;
        line-height: 1.2;
        color: var(--font-black);
    }

    /* The one action is the bell. Upgrade and the avatar both moved into the
       More sheet, which is what buys the app bar its single action. */
    #page-top-bar .update-plan-button-div,
    #page-top-bar #user-profile-top-bar {
        display: none;
    }

    /* Calendar is the one surface with a search of its own and no other home
       for it, so the bar wraps to a second full-width row there rather than
       cramming a second action next to the bell. Gmail and Slack do the same.
       Every other page has an empty second row, which collapses via :empty on
       the container's own `hidden` class. */
    /* The wrap happens INSIDE #page-top-bar-left, which is the flex container
       that actually holds the title, the search field and the chip rail.
       Putting flex-basis:100% on those while their parent was #page-top-bar
       made the search 100% of the LEFT column and squeezed the title to 0px. */
    #page-top-bar {
        height: auto;
        min-height: 50px;
        align-items: flex-start;
        padding-bottom: 8px;
    }
    #page-top-bar-left {
        flex-wrap: wrap;
        row-gap: 8px;
        padding-top: 9px;
    }
    #page-top-bar-right {
        flex: 0 0 auto;
        /* Align the one action with the title's optical centre, not with the
           top of a two-row column. */
        height: 50px;
    }
    #app-bar-title {
        order: 1;
        flex: 1 1 auto;
    }
    #search-bar-container {
        order: 2;
        /* Its own full-width row: 280px beside a title is a squeezed field AND
           a squeezed title. Calendar is the only surface that shows it. */
        flex: 1 0 100%;
        min-width: 0;
        max-width: none;
    }
    #applied-filters {
        order: 3;
        flex: 1 0 100%;
    }
}


/* ============================ CALENDAR (phone) ==============================
   #calendar-tools measured 840px inside a 391px shell: a toolbar you had to
   scroll sideways to reach Today, the view selector or export. Defaulting the
   calendar to LIST on a phone (calendarRendering.js) removes the heatmap
   toggle, timezone selector, arrows and month label from that row; wrapping
   handles whatever the chosen view still needs. */
@media (max-width: 600px) {
    /* Month/Week view wraps the toolbar to three rows (123px) but the bar
       kept a computed 102.7px, so the view selector spilled under the day
       header. Measured: selector bottom 204, bar bottom 183, days top 183. */
    .calendar-tools-bar {
        height: auto !important;
        max-height: none !important;
        min-height: 0;
        overflow: visible !important;
        flex: 0 0 auto;
    }
    #calendar-tools {
        height: auto !important;
    }
    #calendar-tools {
        flex-wrap: wrap;
        row-gap: 8px;
        column-gap: 8px;
        width: 100%;
        min-width: 0;
        padding: 8px var(--mobile-gutter);
        box-sizing: border-box;
    }
    /* The view selector is the one control that must never wrap mid-way: four
       segments reading Week/Month/Grid/List are a segmented control, and half a
       segmented control is nonsense. */
    #calendar-tools .calendar_selector {
        flex: 1 0 100%;
        justify-content: space-between;
    }
    #calendar-tools .calendar_selector > div {
        flex: 1 1 0;
        text-align: center;
    }
    /* The month title is the heading of the view, not a chip wedged between two
       arrows. */
    #calendar_actual_month {
        flex: 1 1 auto;
        min-width: 0;
        justify-content: center;
    }
    /* Row 1 is [period | filter | export], row 2 is the segmented control.
       Left to its own DOM order every control claimed a full row and the
       toolbar stood 132px tall on a 771px screen. `order` pulls export up
       beside the filter button so they share the row they can fit on. */
    #calendar-period-filter-tools { order: 1; flex: 1 1 auto; min-width: 0; }
    #search-bar-filter-button     { order: 2; flex: 0 0 auto; }
    #calendar-export-button       { order: 3; flex: 0 0 auto; }
    #calendar-tools .calendar_selector { order: 4; }
    #calendar-period-filter-tools .calendar-period-select {
        flex: 1 1 auto;
        min-width: 0;
    }

    /* ---- The list IS the phone's calendar --------------------------------
       calendar.css builds each row as a 9-column grid with `min-width: 930px`,
       so at 391px the whole table scrolled sideways: the date read "202...",
       the caption was clipped and the status column was off-screen entirely.
       Rule 26 — a table becomes cards.

       !important on the header: showListMode() reaches these nodes through
       jQuery .show(), which writes an inline `display: block` that no
       stylesheet rule can beat. Same trap that kept the calendar search field
       invisible until 2026-08-31 (f). */
    #calendar-list-top-bar {
        display: none !important;
    }
    #calendar-list-table {
        min-width: 0;
        width: 100%;
    }
    #calendar-list-scroll {
        overflow-x: hidden;
    }

    /* Four columns: tap target, thumbnail, the stack that carries the meaning,
       and the row's own kebab. Three rows, so the caption gets the full width
       instead of a 140px slice of it. */
    .post-list-preview {
        grid-template-columns: 36px 52px minmax(0, 1fr) 36px !important;
        min-width: 0 !important;
        column-gap: 10px !important;
        row-gap: 2px;
        align-items: center;
        padding: 10px var(--mobile-gutter) !important;
        height: auto !important;
    }
    .post-list-preview .post-list-checkbox-cell  { grid-column: 1; grid-row: 1 / span 3; }
    .post-list-preview .post-list-thumbnail-cell { grid-column: 2; grid-row: 1 / span 3; }
    .post-list-preview .post-list-caption-cell   { grid-column: 3; grid-row: 1; }
    .post-list-preview .post-list-accounts-cell  { grid-column: 3; grid-row: 2; }
    .post-list-preview .post-list-date-cell      { grid-column: 3; grid-row: 3; }
    .post-list-preview .post-list-status-cell    { grid-column: 4; grid-row: 3; justify-self: end; }
    .post-list-preview .post-list-actions-cell   { grid-column: 4; grid-row: 1; justify-self: end; }
    /* Campaign is "-" on almost every row and post type is already on the
       thumbnail badge: on a phone they are noise, not information. */
    .post-list-preview .post-list-campaign-cell,
    .post-list-preview .post-list-type-cell {
        display: none;
    }
    /* The caption is what you are actually scanning for, so it gets the weight
       the date used to have. */
    .post-list-preview .post-list-caption-cell {
        font-size: 14px;
        font-weight: 500;
        color: var(--font-black);
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        min-width: 0;
    }
    .post-list-preview .post-list-accounts-cell,
    .post-list-preview .post-list-date-cell {
        font-size: 12px;
        color: var(--font-gray);
        min-width: 0;
    }
    .post-list-preview .post-list-accounts-cell {
        overflow: hidden;
    }

    /* The two hover buttons become ONE tap target that opens an action sheet
       (rowActions.js). They are opacity:0 until :hover, which on a phone means
       Edit and Delete were unreachable, not just invisible. */
    .post-list-preview .post-list-actions-cell {
        position: relative;
        width: 36px;
        min-width: 36px;
        height: var(--tap-target);
    }
    .post-list-preview .post-list-actions-cell > * {
        display: none !important;
    }
    .post-list-preview .post-list-actions-cell::after {
        content: "\22EF";           /* midline horizontal ellipsis */
        position: absolute;
        inset: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        color: var(--font-gray);
        font-size: 18px;
        line-height: 1;
    }

    /* An empty thumbnail reserved 52px on every row. `auto` lets the column
       collapse when there is no media, which is most rows. */
    .post-list-preview {
        grid-template-columns: 36px auto minmax(0, 1fr) 36px !important;
    }
    .post-list-preview .post-list-preview-thumbnail:empty {
        display: none;
    }

    /* The caption is what you scan for. calendar.css styles the inner span at
       12px in --gray-clear, which is correct in a table cell and wrong as the
       primary line of a card. */
    .post-list-preview .post-list-caption-cell > * {
        font-size: 14px;
        font-weight: 500;
        color: var(--font-black);
    }

    /* Pagination sits above the tab bar, not behind it. */
    #calendar-list-div {
        padding-bottom: 4px;
    }
}


/* ============================ INBOX (phone) =================================
   The worst surface measured in this pass. `.inbox-thread-list` is a
   `flex-direction: row` strip with `scrollWidth: 6257px` inside a 375px box, so
   you saw one and a half threads and had to scroll sideways through the rest;
   underneath it, a desktop detail pane took half the screen to say "Select a
   thread". Rules 25 (one column) and 26 (tables become cards). */
@media (max-width: 600px) {

    .inbox-queue-layout {
        display: flex !important;
        flex-direction: column;
        min-height: 0;
    }

    /* One column, scrolling the way a list scrolls. */
    .inbox-thread-list {
        flex-direction: column !important;
        flex-wrap: nowrap;
        overflow-x: hidden !important;
        overflow-y: auto;
        flex: 1 1 auto;
        min-height: 0;
    }
    .inbox-thread-list .inbox-thread-row {
        width: 100% !important;
        min-width: 0 !important;
        max-width: none !important;
        flex: 0 0 auto;
    }

    /* Nothing selected: the list owns the screen. An empty "Select a thread"
       panel is a desktop idea — on a phone the list IS the screen until you
       open something. */
    .inbox-queue-layout.without-detail .inbox-detail-column {
        display: none !important;
    }
    /* Something OPENED: the detail becomes the screen (rule 13) and the list
       steps aside rather than splitting a 411px viewport in two.
       Keyed on `is-phone-detail` (inboxPhone.js), not on the absence of
       `without-detail`: the thread stays selected and loaded while you are
       looking at the list, so going back costs nothing and returning to the
       thread is instant. */
    .inbox-queue-layout.is-phone-detail .inbox-thread-column {
        display: none !important;
    }
    .inbox-queue-layout.is-phone-detail .inbox-detail-column {
        display: flex !important;
        flex-direction: column;
        flex: 1 1 auto;
        min-height: 0;
    }
    /* A thread is selected but you navigated back to the list: keep the detail
       out of the way instead of stacking it under the list. */
    .inbox-queue-layout:not(.is-phone-detail) .inbox-detail-column {
        display: none !important;
    }

    /* On desktop the queue controls ride on the header line beside the tabs;
       a 375px screen has no room for both, so the header wraps and the toolbar
       takes its own full-width line under the tabs. */
    .inbox-page-header {
        flex-wrap: wrap;
        gap: 8px;
    }
    .inbox-header-tools {
        width: 100%;
        margin-left: 0;
        padding-bottom: 0;
    }
    .inbox-queue-toolbar {
        display: flex !important;
        width: 100%;
        gap: 8px;
    }
    .inbox-search-sort-bar {
        flex: 1 1 auto;
        width: auto;
        max-width: none;
    }
    /* The blanket 44px tap floor stretched a 16px checkbox and an 18px read
       toggle into 16x44 and 18x44 pills, one of each on every thread row. Keep
       the drawn box square and expand the hit area with an invisible child. */
    .inbox-thread-select,
    .inbox-thread-read-toggle {
        min-height: 0;
        position: relative;
    }
    .inbox-thread-select::before {
        content: "";
        position: absolute;
        inset: -14px;
    }
    .inbox-thread-read-toggle::before {
        content: "";
        position: absolute;
        inset: -10px;
    }

    /* A 72% bubble on a 375px screen is a column of three words. */
    .inbox-chat-message {
        max-width: 88%;
    }

    /* Four surfaces, one swipeable line. */
    .inbox-thread-type-tabs {
        display: flex !important;
        flex-wrap: nowrap;
        gap: 16px;
    }
    .inbox-thread-type-tabs > * {
        flex: 0 0 auto;
        min-width: 0;
    }

    /* The top tab bar (Inbox / Auto-reply / Health) is a real segmented
       control, not a sideways scroller. */
    .inbox-tabbar {
        overflow-x: visible;
        flex-wrap: nowrap;
        gap: 4px;
    }
    .inbox-tabbar .inbox-tab-btn {
        flex: 1 1 0;
        min-width: 0;
        justify-content: center;
        /* "Auto-reply" wrapped to two lines in a three-segment control. */
        white-space: nowrap;
        font-size: 14px;
        padding-left: 6px;
        padding-right: 6px;
    }

    /* Back to the list from an open thread, in the app bar (inboxPhone.js).
       Hidden until a thread is open; when it shows, the title steps aside. */
    #app-bar-back {
        display: none;
    }
    body.phone-back-visible #app-bar-back {
        display: inline-flex;
        order: 0;
        margin-right: 4px;
    }
    body.phone-back-visible #app-bar-title {
        flex: 1 1 auto;
    }
    .inbox-phone-back {
        display: inline-flex;
        align-items: center;
        gap: 6px;
        align-self: flex-start;
        min-height: var(--tap-target);
        padding: 0 10px 0 4px;
        margin-bottom: 4px;
        border: none;
        background: transparent;
        color: var(--accent-ui);
        font-family: inherit;
        font-size: 15px;
        font-weight: 600;
        cursor: pointer;
    }
    .inbox-phone-back svg {
        width: 18px;
        height: 18px;
    }
}


/* ==================== TAKEOVERS MUST BEAT THE TAB BAR =======================
   Regression introduced with the tab bar and caught by an adversarial check
   that rebuilt the composer against the real stylesheets: #mobile-tab-bar is
   fixed at z-index 100040 while #modal-new-publication is z-index 35, and
   nothing hid the bar when the editor opened. Measured at 411x823, the
   composer's footer sat at y=783..823 — all 40px of it behind the bar — which
   is where #modal-new-publication-errors-div renders, so the panel that says
   WHY a post cannot be published was permanently invisible, and a tap there
   navigated out of the editor instead.

      `body.takeover-open` is written by takeover.js from a narrow observer on the
   modal's own style attribute — it reads the SAME inline style both close
   branches clear, so no close path can leave the bar hidden and strand someone
   with no navigation. A `body:has(#modal[style*=...])` selector expressed this
   in one line and was verified to work, but :has() on body against a descendant
   attribute forces a document-wide style recalc on every inline-style write,
   and this app writes them constantly through jQuery. */
@media (max-width: 600px) {
    body.takeover-open #mobile-tab-bar,
    /* Native apps let the keyboard cover the tab bar. With
       interactive-widget=resizes-content the layout viewport shrinks and a
       fixed bar would ride up to sit on top of the keyboard, eating 56px of an
       already short form. takeover.js flips this class from --keyboard-inset. */
    body.keyboard-open #mobile-tab-bar {
        display: none !important;
    }

    /* Rule 2: the composer's bottom edge pays the safe-area tax too. Hiding the
       bar reveals the footer, but nothing in the composer referenced
       --safe-bottom, so on a device with a home indicator its lowest ~34px
       still sat under it. */
    #modal-new-publication-div,
    #modal-new-publication-footer {
        padding-bottom: var(--safe-bottom);
    }
}

/* ==================== NOTHING MAY BE HOVER-ONLY (rule 10) ===================
   Each of these is a control that simply does not exist on a phone: it is
   painted at opacity 0 / display none and revealed only by :hover, which never
   fires on a touch screen. */
@media (hover: none) {
    /* The caption AI toolbar — rewrite, hashtags, the whole
       #caption-ai-tools-group — revealed only by #option-caption:hover, with no
       touch route anywhere in the JS. */
    #caption-tools-bar {
        opacity: 1;
    }
    /* Remove / edit / alt-text on each media tile in the composer, revealed
       only by .media-item:hover — so a selected image could not be removed. */
    .media-item .action-buttons-container {
        display: flex;
    }
    /* The inbox row read toggle and its select checkbox. */
    .inbox-thread-read-toggle,
    .inbox-thread-select {
        opacity: 1;
        pointer-events: auto;
    }
    /* The app-wide .row-kebab (Brands rows and others) is display:none until
       its row is hovered. On a phone that is a row with no actions at all.
       Always shown; rowActions.js turns its menu into an action sheet. */
    .row-kebab {
        display: inline-flex !important;
        opacity: 1 !important;
        visibility: visible !important;
        pointer-events: auto !important;
    }
    .row-kebab-trigger {
        min-width: var(--tap-target);
        min-height: var(--tap-target);
    }
    /* And its desktop dropdown never opens on a phone: the sheet replaces it. */
    .row-kebab.is-open .row-kebab-menu {
        display: none !important;
    }
}

/* The .row-kebab rule above lives under (hover: none). A narrow window driven
   by a mouse still has no room for a hover-revealed control, so the phone
   breakpoint gets the same treatment. */
@media (max-width: 600px) {
    .row-kebab {
        display: inline-flex !important;
        opacity: 1 !important;
        visibility: visible !important;
        pointer-events: auto !important;
    }
    .row-kebab-trigger {
        min-width: var(--tap-target);
        min-height: var(--tap-target);
    }
    .row-kebab.is-open .row-kebab-menu {
        display: none !important;
    }
}

/* ==================== FILTER PANEL (phone) ==================================
   #search-bar-filters becomes a centred .floating-modal-phone sheet, but
   #search-bar-filters-content stays flex-direction: row with six columns and
   overflow-x, so five of the six facets were off the side of the sheet. */
@media (max-width: 600px) {
    #search-bar-filters {
        height: auto;
        max-height: 80svh;
    }
    #search-bar-filters-content {
        flex-direction: column;
        overflow-x: hidden;
        overflow-y: auto;
    }
    #search-bar-filters-content .search-bar-filters-column {
        width: 100%;
        min-width: 0;
        /* The column, not just its content box, carries the desktop grid's
           fixed height: measured 116px holding 12 platform rows. */
        height: auto !important;
        max-height: none !important;
        overflow: visible !important;
        flex: 0 0 auto;
    }
    /* ONE scroller (rule 17). Capping each facet at 190px gave six tiny
       nested scroll areas showing two rows each; the panel itself already
       scrolls, so the facets just take their height. */
    .search-bar-filters-column-content {
        /* !important: filters.css sizes these boxes with a fixed height for
           the six-column desktop grid, which clipped every facet at ~2 rows. */
        height: auto !important;
        max-height: none !important;
        overflow: visible !important;
    }
    #search-bar-filters {
        overflow-y: auto;
        padding-bottom: calc(16px + var(--safe-bottom));
    }
    #search-bar-filters-content .search-bar-filters-column-header {
        position: sticky;
        top: 0;
        z-index: 1;
        background-color: var(--bg-blue-dark-2);
    }
}

/* ==================== INBOX FIELDS (phone) ==================================
   Every inbox field is 13px, which triggers the iOS focus zoom. Re-declared at
   matching specificity so it wins on order rather than on !important. */
@media (hover: none) and (pointer: coarse) {
    .inbox-input, .inbox-select, .inbox-textarea, .inbox-inline-reply-input {
        font-size: var(--tap-font-size);
    }
    /* The composer's own controls, which calendar.css types at 13px. */
    .new-publication-option-content, .caption-input, .accounts-selector {
        font-size: var(--tap-font-size);
    }
}


/* The floating post preview is 455px wide by design. On a phone it gets the
   screen minus a gutter, which is what makes the clamp in previewPost()
   (handlePreview.js) able to keep it fully on screen. */
@media (max-width: 600px) {
    #preview-modal-post-content {
        width: calc(100vw - 24px);
        max-width: 455px;
    }
}


/* ==================== POST COMPOSER (phone) =================================
   Every value below was measured at 411px against the real stylesheets. */
@media (max-width: 600px) {
    /* The top rail held the mode tabs (317px) AND the action cluster (350px) in
       399px with overflow-x:auto, so Save and Close were 268px off the side —
       reachable only by knowing to swipe a toolbar that gives no sign it
       scrolls. Wrapping puts them on a second line, on screen. */
    #modal-new-publication-div-header-right {
        overflow-x: visible;
        flex-wrap: wrap;
        row-gap: 8px;
    }

    /* A stray wide child could turn the form into a two-axis pan: measured
       clientWidth 391 vs scrollWidth 448. */
    #modal-new-publication-options-container {
        overflow-x: hidden;
        /* The form is 1738px tall with one platform open and 6986px with all of
           them; it ended flush against the bottom edge with nothing below the
           last field. */
        padding: 10px 10px calc(var(--tab-bar-height) + var(--safe-bottom) + 24px);
    }

    /* Which platform am I editing? At 10 screens of scroll the section title
       has to stay on screen. */
    #modal-new-publication-options-container .options-box-title {
        position: sticky;
        top: 0;
        z-index: 2;
        background-color: var(--surface);
    }

    /* A 400px + 40px popover anchored inside a 391px column. */
    #dropdown-recommendations {
        width: auto;
        max-width: calc(100vw - 40px);
        left: 0;
        right: 0;
    }
    #caption-ai-tools-group {
        flex-wrap: wrap;
        height: auto;
        max-width: 100%;
    }

    /* The preview stage renders a hard 390px phone bezel inside a 381px shell,
       so the preview itself scrolled on both axes. On a phone the screen IS the
       device frame — the bezel is decoration that costs content width. */
    #post-preview-shell {
        --post-preview-phone-width: 100%;
    }
    .post-preview-phone {
        max-width: 390px;
        border-width: 0;
        border-radius: var(--radius-big);
    }
    .post-preview-stage {
        padding: 0;
    }
    /* A 1280px laptop frame cannot be made useful at 411px. */
    #post-preview-devices [data-device="laptop"] {
        display: none;
    }
}

/* Touch floors inside the composer: every <select> was 40px/14px and the
   checkboxes 13px, which is both under the 44px target and under the 16px
   iOS-zoom floor. */
@media (hover: none) and (pointer: coarse) {
    #modal-new-publication input[type="checkbox"],
    #modal-new-publication input[type="radio"] {
        width: 20px;
        height: 20px;
    }
}


/* The composer's footer rides above the on-screen keyboard. --keyboard-inset is
   published on <html> by takeover.js from window.visualViewport; it is 0px
   whenever the keyboard is closed, so this is inert the rest of the time. */
@media (max-width: 600px) {
    #modal-new-publication-footer {
        transform: translateY(calc(-1 * var(--keyboard-inset, 0px)));
        transition: transform var(--transition-fast) ease;
    }
}


/* ==================== SMALL DIALOGS + TOASTS (phone) ========================
   Rule 12: a centred modal becomes a bottom sheet. showConfirmModal() is
   awaited as a promise by ~40 call sites, so it keeps its markup and its
   handlers; on a phone its panel simply anchors to the bottom edge and takes
   the sheet's shape. Same feel, zero JS churn. */
@media (max-width: 600px) {
    #modal-confirm-choice {
        align-items: flex-end;
    }
    #modal-confirm-choice-content {
        width: 100%;
        max-width: none;
        margin: 0;
        border-radius: var(--radius-big) var(--radius-big) 0 0;
        padding-bottom: calc(16px + var(--safe-bottom));
        box-sizing: border-box;
    }
    #modal-confirm-choice-content button {
        min-height: var(--tap-target);
        font-size: 16px;
    }

    /* Toasts are top-anchored at 8px, which on a notched phone is under the
       status bar. Rule 2. */
    #notifications-box {
        top: calc(8px + var(--safe-top));
        left: var(--mobile-gutter);
        right: var(--mobile-gutter);
        width: auto;
        max-width: none;
    }
}

/* ==================== INBOX DETAIL (phone) ==================================
   Measured on a deep-linked thread at 411px. */
@media (max-width: 600px) {
    /* Thread / Team Chat / Details: 347px of tabs in a 341px row. Three equal
       thirds, no wrap. */
    .inbox-thread-tabs {
        display: flex;
        overflow-x: hidden;
    }
    .inbox-thread-tabs > button {
        flex: 1 1 0;
        min-width: 0;
        justify-content: center;
        white-space: nowrap;
        padding-left: 6px;
        padding-right: 6px;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    /* A caption that scrolled sideways (scrollWidth 726 in a 134px cell). */
    .inbox-detail-column .post-caption {
        white-space: normal;
        overflow: hidden;
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        max-width: 100%;
    }
    /* The detail card's own padding was sized for a 700px pane. */
    .inbox-detail-column .inbox-panel {
        padding-left: 12px;
        padding-right: 12px;
    }
}

/* ==================== HOME (phone) ==========================================
   #home-agent-chat-shell reserves min-height calc(100% - 170px) and centres
   its content, so with no conversation open the greeting + composer block
   (~215px) floated in a 341px shell with ~115px of nothing above it — on a
   771px screen. The centring is right once a chat is running (the composer
   pins to the bottom); before that, start at the top and let the board rise. */
@media (max-width: 600px) {
    #home-agent-page:not(.is-home-chat-active) #home-agent-chat-shell {
        min-height: 0;
        justify-content: flex-start;
        align-items: flex-start;
    }
    #home-agent-page:not(.is-home-chat-active) .ai-chat-main-div {
        justify-content: flex-start;
    }
    #home-agent-main {
        padding-top: 4px;
    }
}

/* ==================== PAGE HEADINGS (phone) =================================
   The app bar already names the page. Each of these surfaces then repeats it
   as an H1 with a sentence of explanation — 90-140px of a 771px screen spent
   saying "Drops" twice. Inbox got this treatment first; same rule, same
   reason. Desktop keeps them: there is no app bar there. */
@media (max-width: 600px) {
    .automations-page-heading,
    .drops-header-titles,
    .mydrop-dashboard-intro-titles,
    .intelligence-header-titles,
    #workspace-settings-header {
        display: none;
    }
    /* Settings' content padding was 24px 30px, sized for a wide pane. */
    #workspace-settings-content {
        padding: 12px var(--mobile-gutter);
    }
}

/* ==================== DROPS (phone) ====================================
   The list already stacks under 700px (drops.css); on a coarse pointer the
   three verdict buttons need the tap floor, and the ghost Skip must not sit
   flush against Edit. */
@media (max-width: 600px) {
    .drops-card-actions .drops-btn-primary,
    .drops-card-actions .drops-btn-secondary,
    .drops-card-actions .drops-btn-ghost {
        min-height: var(--tap-target);
    }
    .drops-card-actions {
        gap: 10px;
    }
}

/* ==================== MONITORING (phone) ====================================
   .intelligence-cards is a two-column grid (measured 154px + 421px) inside a
   363px column, so the dashboard was 589px wide and the page panned sideways.
   One column; each card takes the width it has. */
@media (max-width: 600px) {
    .intelligence-cards {
        grid-template-columns: minmax(0, 1fr) !important;
    }
    .intelligence-cards .intel-card {
        width: auto !important;
        max-width: 100%;
        min-width: 0;
    }
    .intelligence-layout,
    .intelligence-dashboard {
        max-width: 100%;
        min-width: 0;
        overflow-x: hidden;
    }
}

/* ==================== TEAMS (phone) =========================================
   The channel strip (#workspace-conversations-sidebar) already collapses to a
   48px horizontal row, but it rendered BELOW the message composer with ~200px
   of nothing under it, while the thread itself got 527px. Channels go on top
   under the app bar; the thread takes everything else; the composer ends up
   directly above the tab bar, in the thumb zone. */
@media (max-width: 600px) {
    #workspace-conversations-page {
        display: flex;
        flex-direction: column;
        min-height: 0;
    }
    #workspace-conversations-sidebar {
        order: -1;
        flex: 0 0 auto;
    }
    #workspace-conversations-main {
        flex: 1 1 auto;
        min-height: 0;
    }
    #workspace-conversations-main-inner {
        height: 100%;
        min-height: 0;
    }
    #workspace-conversations-list {
        overscroll-behavior-x: contain;
    }
}

/* ==================== BRANDS (phone) ========================================
   Same shape as Teams: the brand switcher (#profiles-content-menu) rendered as
   a sideways strip at the BOTTOM, under the list, and the action bar (Create a
   Brand / Connect Profiles / search / Expired only) was 680px wide in 409. */
@media (max-width: 600px) {
    #profiles-content-div {
        display: flex;
        flex-direction: column;
    }
    #profiles-content-menu {
        order: -1;
        overscroll-behavior-x: contain;
    }
    #profiles-top-bar-div {
        flex-wrap: wrap;
        row-gap: 8px;
        overflow-x: visible;
    }
    #profiles-top-bar-div > * {
        min-width: 0;
    }
    #profiles-filter-bar {
        flex: 1 0 100%;
        width: 100%;
        min-width: 0;
    }
}

/* ==================== COMPOSER HEADER (phone) ===============================
   Measured with the composer open at 411px: #modal-new-publication-div-header
   is a hard 48px (height + min-height) while its wrapped content
   (#modal-new-publication-div-header-right) is 79px, so the second row — the
   Publish split button and the close × — overflowed the header and was painted
   half under the form. And the whole takeover sat at top: 20px, a grey band on
   a screen that has none to spare. Rule 13: a takeover covers everything. */
@media (max-width: 600px) {
    #modal-new-publication-div {
        top: 0 !important;
        height: 100% !important;
        padding-top: var(--safe-top);
    }
    #modal-new-publication-div-header {
        height: auto !important;
        min-height: 48px;
        padding-bottom: 6px;
    }
    #modal-new-publication-div-header-right {
        align-items: center;
    }
    /* The mode tabs and the action cluster each take a full row: the tabs are
       251px and the actions 182px, which never fit 399 side by side anyway. */
    #modal-new-publication-div-header-modes {
        flex: 1 0 100%;
        width: 100%;
        justify-content: center;
    }
    #modal-new-publication-div-header-actions {
        flex: 1 0 100%;
        width: 100%;
        justify-content: space-between;
    }
}

/* ==================== WORKSPACE SHEET (phone) ===============================
   The desktop switcher (#workspaces-list-div) lives inside the hidden sidebar,
   so it rendered at 0x0 on a phone. tabBar.js builds this instead. */
@media (max-width: 600px) {
    .workspace-sheet-current {
        display: flex;
        align-items: center;
        gap: 10px;
        min-height: var(--tap-target);
        padding: 0 12px;
        margin-bottom: 8px;
        border-radius: var(--radius-small);
        background: var(--bg-blue-dark);
        font-size: 15px;
        font-weight: 600;
        color: var(--font-black);
    }
    .workspace-sheet-current-name {
        flex: 1 1 auto;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    .workspace-sheet-current-plan {
        flex: 0 0 auto;
        font-size: 12px;
        font-weight: 500;
        color: var(--font-gray);
    }
    /* input.workspace-sheet-search (0,1,1) — system.css types input[type=search]
       at 13px with the same specificity, and this loads later, so it wins. */
    input.workspace-sheet-search {
        width: 100%;
        box-sizing: border-box;
        min-height: var(--tap-target);
        margin-bottom: 6px;
        padding: 0 12px;
        border: 1px solid var(--border-color);
        border-radius: var(--radius-small);
        background: var(--surface);
        color: var(--font-black);
        font-family: inherit;
        /* 16px: the iOS focus-zoom floor, and this IS a field you type in. */
        font-size: 16px;
    }
    .workspace-sheet-rows {
        max-height: 40svh;
        overflow-y: auto;
        overscroll-behavior: contain;
        margin-bottom: 8px;
        padding-bottom: 8px;
        border-bottom: 1px solid var(--border-color);
    }
    .workspace-sheet-more {
        padding: 8px 12px;
        font-size: 12px;
        color: var(--font-gray);
    }
}


/* ==================== PRICING MODAL (phone) =================================
   Full-screen and z 75 already; only the plan comparison table overran the
   viewport (right edge at 673px in a 411 screen) inside a container that did
   not scroll. Rule 26: a wide table scrolls in its own box, never the page. */
@media (max-width: 600px) {
    .pricing-feature-table {
        display: block;
        max-width: 100%;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        overscroll-behavior-x: contain;
    }
    #pricingModal {
        padding-bottom: var(--safe-bottom);
    }
}

/* ==================== BULK CREATE (phone) ===================================
   The wizard opens full-screen (z 39) and is usable; only its step rail
   (#bulk-modal .bulk-steps-nav: three pills + Next) measured 422px in a 411px
   viewport, so the Next button was clipped at the edge. */
@media (max-width: 600px) {
    #bulk-modal .bulk-steps-nav {
        flex-wrap: wrap;
        row-gap: 6px;
        column-gap: 4px;
        max-width: 100%;
        min-width: 0;
    }
    #bulk-modal .bulk-step-pill {
        padding-left: 8px;
        padding-right: 8px;
    }
    #bulk-modal .bulk-step-line {
        display: none;
    }
}

/* ==================== GALLERY (phone) =======================================
   Full-screen (z 40) and its 2-column grid works. Only the header button row
   (#gallery-header-buttons: Upload / Filter / Select All / Canva / Drive)
   measured 425px in a 369px box, with the import-service chips off the edge. */
@media (max-width: 600px) {
    #gallery-header,
    #gallery-header-buttons {
        flex-wrap: wrap;
        row-gap: 8px;
        overflow-x: visible;
        max-width: 100%;
    }
    #gallery-header-import-services {
        flex: 1 0 100%;
        min-width: 0;
        overflow-x: auto;
        overscroll-behavior-x: contain;
    }
    #gallery-body-folders-list {
        overscroll-behavior-x: contain;
    }
}

/* ==================== BRANDS ROW KEBAB (phone) ==============================
   .profile-container is a 271px + 64px grid and .profile-buttons (the kebab)
   is position:absolute over the 64px posts-used cell. Visible, the 44px
   kebab sat on top of "27 Posts used". Reserve its column on the right. */
@media (max-width: 600px) {
    .profile-container {
        position: relative;
        padding-right: 48px !important;
        box-sizing: border-box;
    }
    .profile-container .profile-buttons {
        right: 0 !important;
        top: 50% !important;
        transform: translateY(-50%);
    }
}

/* ==================== POST STATE MENU (phone) ===============================
   The composer's "Publish now ▾" split button opens .post-state-selector-
   dropdown right-aligned to its trigger. On a phone the trigger sits at the
   left edge, so the ~300px menu opened at x=-152 with only "8:31 PM" and a
   chevron visible. Pinned to the left edge and capped to the viewport. */
@media (max-width: 600px) {
    .post-state-selector-dropdown {
        left: 0 !important;
        right: auto !important;
        max-width: calc(100vw - 32px);
        min-width: 0;
    }
}

/* ==================== SETTINGS NOTIFICATIONS (phone) ========================
   Rule 26: a table wider than the screen becomes cards. The notifications
   matrix is 18 columns / 2129px inside a 303px scroller; on a phone it read as
   two rows of unlabeled checkboxes with the labels scrolled off to the left.
   Each member becomes one card and each column one 44px "Label ....... [x]"
   row; the label comes from the cell's data-label (written by the renderer in
   workspaces.js from the same locale string the desktop header shows). The
   desktop table is untouched: everything below is phone-only. */
@media (max-width: 600px) {
    #workspace-notifications-table-container {
        overflow: visible;
        padding-bottom: 0;
    }
    #workspace-notifications-table,
    #workspace-notifications-table tbody,
    #workspace-notifications-table tr,
    #workspace-notifications-table td {
        display: block;
        width: 100%;
        box-sizing: border-box;
    }
    #workspace-notifications-table thead {
        display: none;
    }
    #workspace-notifications-table tr {
        margin-bottom: 12px;
        border: 1px solid var(--border-color);
        border-radius: var(--radius-big);
        background-color: var(--bg-blue-dark);
        overflow: hidden;
    }
    #workspace-notifications-table td.member-column {
        min-width: 0;
        padding: 12px;
        background-color: var(--surface);
        border-bottom: 1px solid var(--border-color);
    }
    #workspace-notifications-table td.member-column .member-info {
        padding: 0;
        flex-wrap: wrap;
    }
    #workspace-notifications-table td:not(.member-column) {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 12px;
        min-height: var(--tap-target);
        padding: 0 12px;
        text-align: left;
        border-top: 1px solid var(--border-color);
    }
    #workspace-notifications-table td:not(.member-column):first-of-type {
        border-top: 0;
    }
    #workspace-notifications-table td:not(.member-column)::before {
        content: attr(data-label);
        flex: 1 1 auto;
        min-width: 0;
        font-size: 14px;
        line-height: 1.3;
        color: var(--font-black);
    }
    #workspace-notifications-table input[type="checkbox"] {
        flex: 0 0 auto;
        width: 22px;
        height: 22px;
        margin: 0;
        accent-color: var(--accent-ui, var(--purple-dark));
    }
}

/* ==================== FLOATING PHONE MODALS ARE SHEETS ======================
   responsive_base_html.css turns every `.floating-modal-phone` dropdown (the
   filter panel, the timezone picker, the profile selectors, the inbox filter
   menu, the share menu, the reminder editor) into a centred card below
   1000px. A centred card is a desktop dialog shrunk; on a phone a picker is a
   bottom sheet (rules 12-13): anchored to the thumb, rounded at the top only,
   sitting on the keyboard when a search field inside it takes focus. Their
   open/close logic is untouched; only the resting shape changes. z-index 60:
   above the tab bar (25) and the takeovers (30-40), below the confirm dialog
   (85) and the toasts (95). */
@media (max-width: 600px) {
    .floating-modal-phone {
        top: auto !important;
        bottom: var(--keyboard-inset, 0px) !important;
        left: 0 !important;
        right: 0 !important;
        transform: none !important;
        width: 100% !important;
        max-width: none !important;
        max-height: calc(100svh - var(--keyboard-inset, 0px) - 48px) !important;
        box-sizing: border-box;
        border-radius: var(--radius-big) var(--radius-big) 0 0 !important;
        border-left: 0 !important;
        border-right: 0 !important;
        border-bottom: 0 !important;
        box-shadow: 0 -8px 40px rgba(3, 5, 12, 0.32) !important;
        padding-bottom: var(--safe-bottom);
        z-index: 60 !important;
    }
    /* The close row doubles as the grabber strip: bar centred, x at the right.
       width/align-self: #search-bar-filters is a column flexbox that centres
       its children, which shrank the row to the x and put the x under the
       bar in the middle of the sheet. */
    .floating-modal-phone-close-row {
        position: relative;
        width: 100%;
        align-self: stretch;
        /* flex: 0 0 auto: the profile picker is a column flexbox whose
           content is taller than the sheet, and the row was the first thing
           to shrink (measured 40px under a 46px button, which then sat on
           top of the search field). */
        flex: 0 0 auto;
        box-sizing: border-box;
        justify-content: flex-end;
        align-items: flex-start;
        min-height: 60px;
        padding-top: 12px;
    }
    .floating-modal-phone-close-row::before {
        content: '';
        position: absolute;
        top: 8px;
        left: 50%;
        width: 36px;
        height: 4px;
        margin-left: -18px;
        border-radius: var(--radius-pill);
        background-color: var(--border-color);
    }
    /* The filter panel opens by class with a fade + a downward slide from its
       desktop anchor. As a sheet it rises from the bottom edge (rule 29:
       transform only). The id beats .floating-modal-phone's transform: none. */
    #search-bar-filters {
        transform: translateY(100%) !important;
    }
    #search-bar-filters.is-open {
        transform: translateY(0) !important;
    }
    /* Rule 20: 44px is the floor for anything a thumb has to hit. */
    .floating-modal-phone-close {
        width: var(--tap-target);
        height: var(--tap-target);
        font-size: 14px;
    }
}

/* ==================== PERFORMANCE REPORT HISTORY (phone) ===================
   Below 1000px responsive_base_html.css lays the report history out as ONE
   horizontal row: 121 cards, 27,340px, in a 358px strip. A horizontal strip is
   for a handful of peers, not an archive. On a phone the list is vertical
   again and templates.js shows the latest six with a "Show all" row (rule 18:
   progressive disclosure beats an endless rail). */
@media (max-width: 600px) {
    #analytics-report-schedules-list,
    #analytics-report-runs-list {
        flex-direction: column !important;
        overflow-x: visible !important;
    }
    .analytics-schedule-card,
    .analytics-report-run-item {
        min-width: 0 !important;
        width: 100%;
    }
    .analytics-report-run-item {
        min-height: var(--tap-target);
        justify-content: center;
    }
    .analytics-report-runs-more {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 100%;
        min-height: var(--tap-target);
        border: 1px dashed var(--border-color);
        border-radius: var(--radius-small);
        background: transparent;
        color: var(--accent-ui, var(--purple-dark));
        font-size: 14px;
        font-weight: 500;
        cursor: pointer;
    }
}

/* ==================== COMPOSER: PICKERS ABOVE THE HEADER ====================
   Measured 2026-09-03: the "Post to" picker opens as a bottom sheet (z 60)
   but its top 30px sat UNDER the composer's header actions. The sheet lives
   inside #modal-content-new-publication, whose desktop open animation leaves
   `transform` + `will-change: opacity, transform` on it, and either of those
   makes it a stacking context of its own: the sheet's z-index only counts
   inside that box, and the header, painted later in the same modal, wins.
   On a phone the composer is a full-screen takeover with no slide-in to
   protect, so the box stops being a stacking context and every picker inside
   it competes with the header on plain z-index, which it wins. */
@media (max-width: 600px) {
    #modal-content-new-publication {
        transform: none !important;
        will-change: auto !important;
    }
}

/* ==================== HOME CHAT + KEYBOARD (phone) ==========================
   Android Chrome honours interactive-widget=resizes-content (base.html), so
   the layout viewport shrinks, #home-agent-page's height: 100% follows it and
   the composer at the bottom of the shell rises by itself; --keyboard-inset
   computes to ~0 there. iOS Safari ignores that meta: the layout viewport
   keeps its height, only the visual viewport shrinks, and --keyboard-inset is
   the keyboard's height. So the lift below is a no-op on Android and exactly
   the keyboard's height on iOS, same trick the post composer's footer uses.
   takeover.js additionally pins the chat to its latest message on open. */
@media (max-width: 600px) {
    /* Only while a chat is running: that is when the composer is pinned to
       the bottom edge. Before the first message it sits under the greeting
       near the top, above where any keyboard reaches, and lifting it there
       threw it off the top of the screen (measured: bottom -51px). */
    body.keyboard-open #home-agent-page.is-home-chat-active .agent-home-chat-surface .ai-chat-footer-row {
        transform: translateY(calc(-1 * var(--keyboard-inset, 0px)));
        transition: none;
    }
    body.keyboard-open #home-agent-page.is-home-chat-active #home-agent-chat-shell {
        padding-bottom: var(--keyboard-inset, 0px);
    }
}

/* ==================== THREE MORE TAKEOVERS (phone) ==========================
   Measured 2026-09-03 with each one open at 339px: the tab bar stayed on
   screen under the onboarding wizard (root z 20 < tab bar 25), the media
   preview and the link editor. takeover.js hides it for the first two; the
   link editor is created on open and removed on close, so :has() does it. */
@media (max-width: 600px) {
    body:has(#mydrop-link-editor-modal) #mobile-tab-bar {
        display: none;
    }
    /* id + class: onboarding.css is loaded lazily, after this file, and its
       own .onboarding-main-root { z-index: 20 } would otherwise win on order. */
    #onboarding-main-root.onboarding-main-root {
        z-index: 30;
    }
}

/* ==================== ONBOARDING WIZARD (phone) =============================
   The wizard's own stylesheet already gives it one scroller and 46px
   buttons. Three things were off at 339px: the brand preview's image strip
   spilled past its card with nothing to scroll it (432px in 235px), and the
   two "open your portal / bio" CTAs were 39-41px. */
@media (max-width: 600px) {
    .onboarding-main-frame {
        padding-left: 14px;
        padding-right: 14px;
        padding-bottom: calc(14px + var(--safe-bottom));
    }
    .onboarding-brand-preview-images {
        overflow-x: auto;
        overscroll-behavior-x: contain;
        -webkit-overflow-scrolling: touch;
        scroll-snap-type: x proximity;
        padding-bottom: 4px;
    }
    .onboarding-brand-preview-images > * {
        flex: 0 0 auto;
        scroll-snap-align: start;
    }
    .onboarding-brand-portal-cta {
        min-height: var(--tap-target);
    }
}

/* ==================== PLATFORM INFO DIALOGS (phone) =========================
   The "about Facebook / Instagram / LinkedIn / YouTube" explainers open a
   <dialog id="...InfoModal"> whose width is hard-coded inline to 500px.
   !important is the only thing that beats an inline style. */
@media (max-width: 600px) {
    dialog[id$="InfoModal"] {
        width: calc(100vw - 32px) !important;
        max-width: calc(100vw - 32px) !important;
        max-height: calc(100svh - 32px);
        box-sizing: border-box;
        padding: 16px !important;
        padding-bottom: calc(16px + var(--safe-bottom)) !important;
        overflow-y: auto;
        border-radius: var(--radius-big);
    }
    dialog[id$="InfoModal"] button {
        min-height: var(--tap-target);
    }
}

/* ==================== MEDIA PREVIEW OVERLAY (phone) =========================
   preview.css caps #gallery-media-preview-box at calc(100vh - 48px) and lets
   the media fill it, so at 339x704 the details were cut and the four actions
   sat at y 733, below the screen, unreachable (rule 3: nothing lives under
   the fold that the page cannot scroll to). The media is capped instead, the
   box grows with its content, the container scrolls, and the actions become
   a bottom bar that stays put. */
@media (max-width: 600px) {
    #gallery-media-preview-box-container {
        padding: 8px 0 calc(12px + var(--safe-bottom));
        overflow-x: hidden;
        overflow-y: auto;
    }
    #gallery-media-preview-box {
        width: calc(100% - 16px);
        max-height: none;
        height: auto;
        overflow: visible;
    }
    #gallery-media-preview-box img,
    #gallery-media-preview-box video {
        max-height: 45svh;
        max-width: 100%;
        width: auto;
        object-fit: contain;
    }
    #gallery-media-preview-actions {
        position: sticky;
        bottom: 0;
        z-index: 2;
        justify-content: space-around;
        padding: 10px 8px calc(10px + var(--safe-bottom));
        background-color: var(--surface);
        border-top: 1px solid var(--border-color);
    }
    #gallery-media-preview-actions-container {
        width: 100%;
        justify-content: space-around;
    }
    .gallery-media-preview-action {
        min-width: var(--tap-target);
        min-height: var(--tap-target);
    }
    .gallery-media-scheduled-box {
        width: calc(100% - 16px);
    }
    /* The "scheduled for" tiles are one nowrap row of 246px tiles whose
       date title itself refuses to wrap (measured right edge 562 in 339). */
    .gallery-media-scheduled-box,
    .gallery-media-scheduled-box [class^="gallery-media-scheduled"] {
        flex-wrap: wrap;
        min-width: 0;
        max-width: 100%;
    }
    .gallery-media-scheduled-box h2 {
        white-space: normal;
    }
}

/* ==================== LINK IN BIO EDITOR (phone) ============================
   Measured 2026-09-03 at 339px: the editor grid sized its single column to
   the content's min-content width, 553px, because the URL row keeps a 170px
   domain prefix and a 285px slug field on one line and every section is a
   two-column grid. The header row then put Save, Delete and the close
   button at x 378-543: off the screen. Everything below is min-width: 0
   plus wrapping; the editor's own logic is untouched. */
@media (max-width: 600px) {
    #mydrop-link-editor-modal .mydrop-link-editor {
        grid-template-columns: minmax(0, 1fr);
        width: 100%;
    }
    #mydrop-link-editor-modal .mydrop-link-editor-header,
    #mydrop-link-editor-modal .mydrop-link-editor-body,
    #mydrop-link-editor-modal .mydrop-link-editor-controls,
    #mydrop-link-editor-modal .mydrop-link-editor-panel,
    #mydrop-link-editor-modal .mydrop-link-editor-section {
        min-width: 0;
        max-width: 100%;
    }
    #mydrop-link-editor-modal .mydrop-link-editor-header {
        flex-wrap: wrap;
        height: auto;
        gap: 8px 12px;
        padding: 10px 12px;
    }
    #mydrop-link-editor-modal .mydrop-link-editor-title {
        flex: 1 1 auto;
        min-width: 0;
    }
    #mydrop-link-editor-modal .mydrop-link-editor-right {
        flex: 1 0 100%;
        justify-content: flex-end;
        flex-wrap: wrap;
    }
    #mydrop-link-editor-modal .mydrop-link-editor-right > * {
        min-height: var(--tap-target);
    }
    #mydrop-link-editor-modal .mydrop-link-editor-close {
        min-width: var(--tap-target);
    }
    #mydrop-link-editor-modal .mydrop-link-editor-controls {
        padding-left: 12px;
        padding-right: 12px;
        padding-bottom: calc(24px + var(--safe-bottom));
        overflow-x: hidden;
    }
    #mydrop-link-editor-modal .mydrop-link-editor-tab {
        min-height: var(--tap-target);
    }
    #mydrop-link-editor-modal .mydrop-link-editor-section {
        grid-template-columns: minmax(0, 1fr);
        padding: 12px;
    }
    #mydrop-link-editor-modal .mydrop-link-url-row {
        flex-wrap: wrap;
    }
    #mydrop-link-editor-modal .mydrop-link-url-row input[type="text"] {
        flex: 1 1 140px;
        width: auto;
        min-width: 0;
    }
    #mydrop-link-editor-modal .link-editor-field,
    #mydrop-link-editor-modal .mydrop-link-domain-card,
    #mydrop-link-editor-modal .mydrop-link-foldable-body,
    #mydrop-link-editor-modal .mydrop-link-foldable-header {
        max-width: 100%;
        min-width: 0;
    }
    #mydrop-link-editor-modal .mydrop-link-fold-toggle,
    #mydrop-link-editor-modal .link-editor-drag-handle,
    #mydrop-link-editor-modal .link-editor-duplicate-item,
    #mydrop-link-editor-modal .link-editor-remove-item,
    #mydrop-link-editor-modal .link-editor-card-fold-toggle,
    #mydrop-link-editor-modal [class^="link-editor-remove-social"] {
        min-width: var(--tap-target);
        min-height: var(--tap-target);
    }
    #mydrop-link-editor-modal .link-editor-select-audio,
    #mydrop-link-editor-modal .link-editor-add-item,
    #mydrop-link-editor-modal [class*="mydrop-link-font-select-bu"] {
        min-height: var(--tap-target);
    }
}
