/* Portrait Perspective: mobile responsive layer
 *
 * One home for every mobile style in the /os tree. See
 * RESPONSIVE_UI_STANDARDS.md in the repository root for the rules this file
 * implements and how to use the utilities it provides.
 *
 * THE DESKTOP RULE. Everything in this file sits inside
 * `@media (max-width: 1023.98px)`. At 1024px and above not one declaration
 * applies, so desktop rendering is byte-for-byte what it was before this file
 * existed. Never add a rule here outside that query, and never change a
 * desktop rule in another stylesheet to achieve a mobile result.
 *
 * Tokens only: every colour comes from --pos-* in portrait_os_tokens.css. No
 * new palette, no new fonts.
 *
 * The per-user root font-size control (base.html sets font-size on <html>)
 * is respected throughout: sizes that must not scale with it are in px, and
 * everything else is in rem so the control keeps working at every step.
 */

/* The single rule outside the breakpoint query, and the reason for it.
 *
 * base.html renders a drawer button in the header on every page. It is a new
 * element that does not belong to the desktop design, so it is hidden by
 * default and revealed only inside the mobile query below. Without this rule
 * the button would appear at 1440 and desktop would no longer be pixel
 * equivalent. Nothing else belongs out here.
 */
.pos-drawer-btn {
  display: none;
}

/* The second and last rule outside the breakpoint query, for the same reason.
   focus.html renders the age buckets a second time as chips, because the bar's
   own segments encode their counts as widths and some are four pixels wide.
   The chips are mobile-only; hidden here so desktop is unchanged. */
.focus-seg-chips {
  display: none;
}

@media (max-width: 1023.98px) {

  /* ---------------------------------------------------------------- *
   * 1. Foundations: no horizontal panning, ever.
   * ---------------------------------------------------------------- */

  /* No overflow-x: hidden here. It stops the page panning but it also clips
     content that is genuinely too wide, so a broken layout looks fine to the
     automated check while the user simply cannot see the right-hand column.
     Overflow is fixed at its source instead. */
  html, body {
    max-width: 100%;
  }

  /* Long unbroken strings (reference numbers, URLs, email addresses) are the
     most common cause of a page growing wider than the phone. */
  .pos-content {
    /* break-word, never `anywhere`: `anywhere` feeds into intrinsic sizing,
       so a flex row will squeeze a text child to one character wide and print
       it vertically. This breaks only a word that would actually overflow. */
    overflow-wrap: break-word;
  }

  /* Media never pushes the layout wider than the screen. */
  .pos-content img,
  .pos-content svg,
  .pos-content video,
  .pos-content canvas,
  .pos-content iframe {
    max-width: 100%;
    height: auto;
  }

  /* ---------------------------------------------------------------- *
   * 2. Shell: the sidebar becomes an off-canvas drawer.
   *
   * The same 48 links in the same six groups and the same order as desktop.
   * Nothing is removed or reordered for mobile.
   * ---------------------------------------------------------------- */

  .pos-shell {
    display: block;           /* the desktop flex row would squeeze the content */
    min-height: 100dvh;
  }

  .pos-sidebar {
    position: fixed;
    z-index: 1000;
    top: 0;
    left: 0;
    width: min(86vw, 320px);
    min-width: 0;
    height: 100dvh;
    /* Full height, scrollable, and clear of the notch and the home bar. */
    padding-top: env(safe-area-inset-top);
    padding-bottom: env(safe-area-inset-bottom);
    transform: translateX(-100%);
    transition: transform 0.22s ease;
    box-shadow: none;
    will-change: transform;
  }

  body.pos-drawer-open .pos-sidebar {
    transform: translateX(0);
    box-shadow: 0 0 40px rgba(0, 0, 0, 0.5);
  }

  /* Only the nav list scrolls, exactly as on desktop: the brand header and
     the user footer stay pinned. */
  .pos-sidebar .pos-nav {
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
  }

  .pos-scrim {
    position: fixed;
    inset: 0;
    z-index: 999;
    background: rgba(0, 0, 0, 0.55);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.22s ease;
  }

  body.pos-drawer-open .pos-scrim {
    opacity: 1;
    pointer-events: auto;
  }

  /* The body must not scroll behind an open drawer or sheet. */
  body.pos-drawer-open,
  body.pos-sheet-open {
    overflow: hidden;
  }

  .pos-main {
    width: 100%;
    min-width: 0;             /* lets flex/grid children shrink instead of overflow */
  }

  /* ---------------------------------------------------------------- *
   * 3. Header: hamburger, title, actions. Sticky and safe-area aware.
   * ---------------------------------------------------------------- */

  .pos-header {
    position: sticky;
    top: 0;
    z-index: 900;
    gap: 0.25rem;
    padding-top: calc(0.5rem + env(safe-area-inset-top));
    padding-left: max(0.5rem, env(safe-area-inset-left));
    padding-right: max(0.5rem, env(safe-area-inset-right));
    background: var(--pos-bg2);
    border-bottom: 1px solid var(--pos-line);
  }

  /* The drawer button only exists on mobile; base.html renders it always and
     this is what reveals it. */
  .pos-drawer-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    flex: 0 0 44px;
    background: transparent;
    border: 0;
    border-radius: 8px;
    color: var(--pos-text);
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
  }

  .pos-drawer-btn:active {
    background: var(--pos-nav-active);
  }

  .pos-header-title {
    font-size: 1rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
  }


  /* ---------------------------------------------------------------- *
   * 3b. The app-wide VOA banner and the header search.
   *
   * Both are flex rows built for a wide header. On a phone they stack, and
   * their text children are allowed to take the full width rather than being
   * squeezed to nothing by a sibling that will not shrink.
   * ---------------------------------------------------------------- */

  .pos-voa-banner {
    flex-wrap: wrap;
    gap: 0.375rem;
  }

  .pos-voa-banner > * {
    min-width: 0;
  }

  /* The row is its own flex container and does not wrap on desktop. Its
     controls carry margin-left:auto and flex:0 0 auto, so they refuse to
     shrink and push the row 60px past the screen. On a phone the controls
     take their own line instead. */
  .pos-voa-banner__row {
    flex-wrap: wrap;
    min-width: 0;
  }

  .pos-voa-banner__controls {
    margin-left: 0;
    flex: 1 1 100%;
    justify-content: flex-start;
    min-width: 0;
  }

  .pos-voa-banner__panel,
  .pos-voa-banner__form {
    flex-wrap: wrap;
    margin-left: 0;
    min-width: 0;
  }

  .pos-voa-banner__form input {
    width: 100%;
    min-width: 0;
  }

  .pos-voa-banner__msg {
    padding-left: 0;
  }

  .pos-voa-banner__text,
  .pos-voa-banner p,
  .pos-voa-banner span {
    flex: 1 1 100%;
    min-width: 0;
  }

  /* The header carries a hamburger, the page title, the VOA chip, global
     search, the role picker and four icon buttons. That does not fit on one
     390px row, and squeezing it leaves a 22px wide search box.
     So the header wraps: controls stay on the first row and the search takes
     the full width of a second row, where it is actually usable with the
     keyboard up and its results are reachable. Nothing is hidden or removed. */
  .pos-header {
    flex-wrap: wrap;
    row-gap: 0.375rem;
    /* portrait_os.css pins the header to a fixed --pos-header-h. Once the row
       wraps, the second row escapes that box and paints over the page below,
       which is what put the VOA banner underneath the role picker. The header
       has to be allowed to grow to its content. */
    height: auto;
    min-height: var(--pos-header-h);
    padding-bottom: 0.375rem;
    align-items: center;
  }

  .pos-header > * {
    min-width: 0;
  }

  .pos-search,
  #pos-search-wrap {
    flex: 1 1 100%;
    order: 10;              /* after the icon row, whatever the source order */
    min-width: 0;
    max-width: 100%;
  }

  #pos-global-search {
    width: 100%;
    min-width: 0;
  }

  /* Its results panel follows the full-width field. */
  .pos-search-drop {
    left: 0;
    right: 0;
    width: auto;
    max-height: 60dvh;
    overflow-y: auto;
  }

  /* The spacer has no job once the row wraps. */
  .pos-header-spacer {
    display: none;
  }

  /* The role picker shrinks so it shares a row with the icon buttons rather
     than pushing the theme toggle onto a row of its own. It stays wide enough
     to read the current role. */
  .pos-role {
    flex: 0 1 auto;
    min-width: 6rem;
    max-width: 11rem;
  }

  /* The VOA chip takes its own row; it is a status banner, not a control
     that should fight the icons for space. */
  .pos-conn-chip {
    flex: 1 1 100%;
    min-width: 0;
  }

  /* The assistant launcher is fixed over the bottom right corner, so the page
     needs room underneath its last line of content. */
  .pos-content {
    padding-bottom: calc(104px + env(safe-area-inset-bottom));
  }

  /* ---------------------------------------------------------------- *
   * 4. Touch targets: 44 x 44 minimum effective hit area.
   * ---------------------------------------------------------------- */

  .pos-nav-link {
    min-height: 44px;
    display: flex;
    align-items: center;
  }

  .pos-btn,
  .pos-iconbtn,
  .pos-sidebar-footer a,
  .pos-sidebar-footer button,
  button:not(.pos-drawer-btn),
  input[type="submit"],
  input[type="button"],
  .btn,
  .primary-button {
    min-height: 44px;
  }

  .pos-iconbtn {
    min-width: 44px;
  }

  /* A small icon control keeps its painted size but gains a full hit area. */
  .pos-tap {
    position: relative;
  }

  .pos-tap::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 44px;
    height: 44px;
    transform: translate(-50%, -50%);
  }


  /* ---------------------------------------------------------------- *
   * 4b. Targets found by the Phase 1 sweep.
   * ---------------------------------------------------------------- */

  /* The brand link at the top of the drawer. */
  .pos-brand a {
    min-height: 44px;
    display: flex;
    align-items: center;
  }

  /* A collapsible section header is a control. */
  summary {
    min-height: 44px;
    display: flex;
    align-items: center;
  }

  /* Sub-navigation chips: short labels still need a 44px wide target. */
  .pos-subnav-link,
  .pos-subnav a,
  .pos-chip,
  .pos-pill {
    min-width: 44px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  /* A link inside a table cell is how a row is opened, so it is a tap target.
     This does make rows taller, which is correct on a phone: a 16px tall link
     is not reachable by thumb. */
  td a,
  th a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }

  /* A cell holding only a link should not also add its own padding on top. */
  td:has(> a:only-child) {
    padding-top: 0;
    padding-bottom: 0;
  }


  /* List and card rows: the title link is how the row is opened, so it is the
     row's tap target. These carry inline styles in several templates, hence
     the override. */
  .pos-jcard-title a,
  .pos-jcard a,
  a.pos-badge,
  .pos-list-row a,
  .pos-card a.pos-title {
    min-height: 44px !important;
  }

  /* A link that is the only thing in its paragraph is an action, not prose.
     "Back to sign in" sitting alone in a <p> is a control and needs the
     target; a link inside a sentence still does not. */
  .pos-content p > a:only-child {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }

  /* A narrow button is still a 44px target. */
  .pos-btn,
  button.pos-btn {
    min-width: 44px;
  }

  /* Standalone links are controls and need a thumb-sized target.
     "Standalone" is expressed as a direct child of a layout container rather
     than a link sitting inside a sentence: prose links live inside p, span or
     td, and are deliberately not matched here so text keeps flowing normally.
     Many of these links carry inline font sizes, hence the override. */
  .pos-content div > a,
  .pos-content li > a,
  .pos-content nav > a,
  .pos-content span > a,
  .pos-content h1 > a,
  .pos-content h2 > a,
  .pos-content h3 > a,
  .pos-content strong > a,
  .pos-panel a,
  td > a,
  th > a {
    min-height: 44px !important;
    /* A flat 44px floor. min-content was tried here and is wrong: it drops
       short labels such as "FMT" and "Zapoi" below the 44px hit area, which
       is the rule this whole layer exists to keep. The cost is that a control
       squeezed inside a very tight flex row can still clip a long word; that
       is logged rather than traded for a broken touch target. */
    min-width: 44px;
    /* No display override here. Forcing one is too blunt: it collapsed the
       whitespace inside calendar chips, and it destroyed the portal journey
       stepper, whose steps declare their own `display: grid` to put the dot
       and the label in separate columns. The behaviour layer upgrades only
       those links that are genuinely inline, where min-height would otherwise
       do nothing. */
    /* Deliberately NOT flex-shrink:0. A case or task title is a long label,
       and stopping it shrinking pushed /os/queue 669px sideways. It wraps
       instead; only the short badge pills below hold their width. */
    white-space: normal;
    overflow-wrap: break-word;
  }


  /* Controls need 44px in BOTH dimensions. A short label like "Edit" or "Add"
     produced a 39px wide control that was tall enough and still too small to
     hit. Named broadly because these classes appear across the estate. */
  /* Size only. Do NOT force a display here.
     A button is already inline-block and a summary is a list-item, so
     min-height works on both as they are. Forcing inline-flex turned their
     inline children into flex items, and flex items lose the whitespace
     between them: the stacking plan started reading "Unit AOccupied". */
  button,
  summary,
  .pos-btn,
  a.pos-btn,
  .ghost-button,
  .pos-iconbtn,
  .pos-display-btn,
  [role="button"] {
    min-height: 44px;
    min-width: 44px;
  }

  /* An anchor is the exception. It is a non-replaced inline element, where
     min-height has no effect at all, so it needs a display that gives it one.
     Anchors have no inline children to space out, so nothing is lost. */
  a.pos-btn,
  a.ghost-button,
  a[role="button"] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  /* A details summary is a control wherever it appears. */
  details > summary {
    min-height: 44px;
    display: flex;
    align-items: center;
  }


  /* A control must not be squeezed narrower than its own label.
     On the weekly review the "Mark review" button was compressed to its 44px
     minimum by the title beside it, so the label overflowed the card. Action
     clusters keep their width and the row wraps instead, which it already
     does under the inline-flex rule above. */
  /* A control squeezed by its neighbour wraps its label rather than clipping
     it. Refusing to shrink was the first attempt and it pushed pages sideways
     wherever a label was long. */
  button,
  .pos-btn,
  a.pos-btn,
  .ghost-button {
    white-space: normal;
    overflow-wrap: break-word;
    height: auto;
  }


  /* Pill-shaped badges used as links.
     Giving a small pill a 44px minimum height turns it into a tall ellipse,
     and with the text on its first line it sat at the top and escaped the
     curve, colliding with the field above it. A badge holds a single piece of
     text, so centring it with inline-flex is safe here: there are no sibling
     inline children whose whitespace could be dropped. */
  .pos-content a.pos-badge,
  .pos-content a.pos-chip,
  .pos-content a.pos-pill,
  .pos-content a.pt-chip,
  .pos-content a.pos-badge,
  .pos-content a.pt-badge {
    display: inline-flex !important;
    align-items: center;
    justify-content: center;
    width: auto;
    /* The pill has to be wide enough for its own label, or the text sits
       proud of the rounded shape on both sides. */
    white-space: nowrap;
    padding-inline: 0.75rem;
    text-align: center;
  }


  /* ---------------------------------------------------------------- *
   * 5. Forms: no iOS zoom-jump, thumb-friendly controls.
   *
   * 16px is the threshold below which iOS Safari zooms the page on focus.
   * These are px deliberately: the per-user font control must not be able to
   * take an input back under the threshold.
   * ---------------------------------------------------------------- */

  /* !important is deliberate and is confined to this media query.
     Component styles in portrait_os.css carry class-level specificity
     (.pos-voa-banner__select and friends) that a bare element selector cannot
     beat. 16px is not a matter of taste here: below it, iOS Safari zooms the
     page on focus and the user has to pinch back out on every field. It is an
     accessibility floor, so it overrides component styling by design. */
  /* `html body` is here to win a cascade fight, not for decoration.
     pos_classic_compat.css is linked per page and therefore loads AFTER this
     file, and it carries `form input[type="text"] { font-size: 13px }` at
     specificity 0,1,2 plus `.toggle-row input { width: auto !important }`.
     A bare element selector loses both. These are the foundations; they have
     to win. */
  html body input:not([type="checkbox"]):not([type="radio"]),
  html body select,
  html body textarea {
    font-size: 16px !important;
    /* !important on the height for the same reason as the font size: scoped
       component rules such as `.cuh-layout select` set an explicit 42px that a
       bare element selector cannot beat, and 44px is the floor. */
    min-height: 44px !important;
    min-width: 44px;
    max-width: 100%;
    /* Padding and border must count inside the width, or a select set to the
       container width overflows it by its own padding. */
    box-sizing: border-box;
  }

  textarea {
    min-height: 88px;
  }

  /* A checkbox keeps its drawn size but gets a full 44px target. The
     transparent border expands the hit box without moving the glyph or
     changing how the control looks. */
  /* A checkbox is drawn at a comfortable 22px, not blown up to 44.
     Its hit area is its label: clicking a label toggles its control, so the
     label is the real target and that is what has to be 44px tall. Sizing the
     input itself to 44px makes a native control paint a 44px solid block,
     which is why that approach was reverted.
     !important is needed because these carry inline width/height from the
     templates. */
  html body input[type="checkbox"],
  html body input[type="radio"] {
    width: 22px !important;
    height: 22px !important;
    flex: none !important;
  }

  /* The label is the tap target. .pos-check is the shared hook for a
     standalone checkbox that has no visible label text of its own. */
  /* `html body` again: the general "everything in the content area shrinks"
     rule below sets min-width:0 on every label, and a checkbox's label is the
     one label that must not shrink, because it IS the tap target. */
  html body label:has(> input[type="checkbox"]),
  html body label:has(> input[type="radio"]),
  html body .pos-check {
    min-height: 44px;
    min-width: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
  }

  /* A checkbox row where the label is a sibling (<div class="pos-check"><input id>
     <label for>) leaves the real target only as tall as one line of text. The
     label is what a tap lands on, so it fills the row. */
  html body .pos-check > label,
  html body .pos-check label {
    min-height: 44px;
    display: flex;
    align-items: center;
  }

  html body .pos-check {
    min-height: 44px;
    display: flex;
    align-items: center;
    gap: 0.5rem;
  }

  /* A focused field must never sit under the sticky header. */
  /* The header wraps on a phone, so its height is not a constant: 194px on
     Perspective and 163px on the portal, against the 5rem (80px) guess that
     was here before. A field scrolled into view was landing up to 114px
     behind it. The behaviour layer measures the real header and publishes it
     as --pos-sticky-h; the fallback only applies if that has not run yet. */
  input, select, textarea {
    scroll-margin-top: calc(var(--pos-sticky-h, 5rem) + 0.75rem);
  }

  /* Forms laid out as desktop grids collapse to one column. */
  .pos-form-row,
  .pos-field-row {
    grid-template-columns: 1fr;
    display: grid;
  }


  /* A flex item defaults to min-width:auto, which stops it shrinking below its
     own min-content. A label wrapping a select therefore refuses to go
     narrower than the select's intrinsic width and pushes out of its flex row,
     which is what took the contracts form 22px past the screen edge. */
  .pos-content label,
  .pos-content form label,
  .pos-content fieldset {
    min-width: 0;
    max-width: 100%;
  }

  /* A control inside a label fills it rather than setting its own width. */
  .pos-content label > select,
  .pos-content label > input,
  .pos-content label > textarea {
    width: 100%;
  }

  /* ---------------------------------------------------------------- *
   * 6. Tables. Two strategies, chosen per table by content.
   *
   *   (a) .pos-table-scroll  - wrap a wide table in this to scroll it
   *       horizontally inside its own container, with a sticky first column
   *       and an edge fade showing there is more to the right.
   *
   *   (b) .pos-table-cards   - put this on the table itself to stack each row
   *       as a card whose cells become labelled lines. Every cell needs a
   *       data-label attribute carrying its column heading.
   *
   * The one-row ellipsis rule stays. Hover does not exist on a phone, so a
   * truncated cell expands on tap via .is-expanded (see pos_mobile.js).
   * ---------------------------------------------------------------- */

  .pos-table-scroll {
    position: relative;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
    max-width: 100%;
  }

  .pos-table-scroll > table {
    min-width: max-content;
  }

  .pos-table-scroll th:first-child,
  .pos-table-scroll td:first-child {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--pos-panel);
  }

  /* The fade is the affordance that says "this scrolls". It disappears once
     the table is scrolled to its right-hand end. */
  .pos-table-scroll::after {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    width: 28px;
    height: 100%;
    pointer-events: none;
    background: linear-gradient(to right, transparent, var(--pos-bg));
    opacity: 1;
    transition: opacity 0.15s ease;
  }

  .pos-table-scroll.is-scrolled-end::after {
    opacity: 0;
  }

  .pos-table-cards,
  .pos-table-cards thead,
  .pos-table-cards tbody,
  .pos-table-cards tr,
  .pos-table-cards td {
    display: block;
    width: 100%;
  }

  .pos-table-cards thead {
    display: none;            /* the labels move into each cell instead */
  }

  .pos-table-cards tr {
    margin: 0 0 0.625rem;
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--pos-line);
    border-radius: 10px;
    background: var(--pos-panel);
  }

  .pos-table-cards td {
    display: flex;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.375rem 0;
    border: 0;
    text-align: right;
  }

  .pos-table-cards td::before {
    content: attr(data-label);
    flex: 0 0 42%;
    text-align: left;
    color: var(--pos-text-dim, var(--pos-text));
    font-weight: 600;
  }

  .pos-table-cards td:empty {
    display: none;
  }

  /* Tap to expand a truncated value. */
  .pos-1row.is-expanded,
  .pos-ellipsis.is-expanded {
    white-space: normal;
    overflow: visible;
    max-width: none;
  }

  /* ---------------------------------------------------------------- *
   * 7. Modals, panels, drawers and in-place iframes become full-screen
   *    sheets. The estate's no-redirect rule means everything is completed
   *    in place, so on a phone "in place" has to mean a sheet.
   * ---------------------------------------------------------------- */

  .pos-modal,
  .pos-panel-overlay,
  .pos-drawer-panel,
  .pos-sheet {
    position: fixed;
    inset: 0;
    z-index: 1100;
    display: flex;
    flex-direction: column;
    width: 100vw;
    max-width: 100vw;
    height: 100dvh;
    max-height: 100dvh;
    margin: 0;
    border: 0;
    border-radius: 0;
    padding-top: env(safe-area-inset-top);
    padding-bottom: env(safe-area-inset-bottom);
    background: var(--pos-bg);
  }

  /* Its own header, so there is always a title and a way out. */
  .pos-sheet-header,
  .pos-modal-header {
    position: sticky;
    top: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex: 0 0 auto;
    min-height: 52px;
    padding: 0.5rem 0.75rem;
    background: var(--pos-bg2);
    border-bottom: 1px solid var(--pos-line);
  }

  .pos-sheet-title,
  .pos-modal-title {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 1rem;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .pos-sheet-close,
  .pos-modal-close {
    flex: 0 0 44px;
    width: 44px;
    height: 44px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: var(--pos-text);
    font-size: 20px;
    line-height: 1;
  }

  .pos-sheet-body,
  .pos-modal-body {
    flex: 1 1 auto;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    padding: 0.75rem;
  }

  /* An embedded page (the contract-signing iframe, the email composer) fills
     the sheet rather than sitting in a letterbox. */
  .pos-sheet-body > iframe,
  .pos-modal-body > iframe {
    width: 100%;
    height: 100%;
    min-height: 60dvh;
    border: 0;
  }

  /* A bottom action bar inside a sheet clears the home bar. */
  .pos-sheet-actions {
    flex: 0 0 auto;
    display: flex;
    gap: 0.5rem;
    padding: 0.625rem 0.75rem calc(0.625rem + env(safe-area-inset-bottom));
    background: var(--pos-bg2);
    border-top: 1px solid var(--pos-line);
  }

  .pos-sheet-actions > * {
    flex: 1 1 auto;
    min-height: 44px;
  }

  /* ---------------------------------------------------------------- *
   * 8. Tab bars are one horizontally scrollable row. They never wrap.
   * ---------------------------------------------------------------- */

  .pos-tabs,
  .pos-tabbar,
  .pos-subnav,
  .pos-hub-tabs {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    scroll-snap-type: x proximity;
    position: relative;
  }

  .pos-tabs::-webkit-scrollbar,
  .pos-tabbar::-webkit-scrollbar,
  .pos-subnav::-webkit-scrollbar,
  .pos-hub-tabs::-webkit-scrollbar {
    display: none;
  }

  .pos-tabs > *,
  .pos-tabbar > *,
  .pos-subnav > *,
  .pos-hub-tabs > * {
    flex: 0 0 auto;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    scroll-snap-align: start;
    white-space: nowrap;
  }

  /* Wrapper that adds the edge fade to a scrolling tab row. */
  .pos-tabs-wrap {
    position: relative;
  }

  .pos-tabs-wrap::after {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    width: 24px;
    height: 100%;
    pointer-events: none;
    background: linear-gradient(to right, transparent, var(--pos-bg));
  }

  .pos-tabs-wrap.is-scrolled-end::after {
    opacity: 0;
  }

  /* ---------------------------------------------------------------- *
   * 9. Content, cards and sticky chrome.
   * ---------------------------------------------------------------- */

  .pos-content {
    padding: 0.75rem max(0.75rem, env(safe-area-inset-left))
             calc(104px + env(safe-area-inset-bottom))
             max(0.75rem, env(safe-area-inset-right));
  }

  /* Any desktop multi-column grid collapses to a single column. Named
     explicitly rather than with a blanket rule so nothing unexpected moves. */
  .pos-grid,
  .pos-grid-2,
  .pos-grid-3,
  .pos-grid-4,
  .pos-cards,
  .pos-kpis,
  .pos-stats {
    grid-template-columns: 1fr !important;
  }

  .pos-row,
  .pos-toolbar,
  .pos-actions {
    flex-wrap: wrap;
  }

  /* A floating action or bottom bar respects the home bar. */
  .pos-fab,
  .pos-bottom-bar {
    bottom: calc(1rem + env(safe-area-inset-bottom));
  }




  /* Inline flex rows wrap on a phone.
     Templates across the estate declare `style="display:flex"` for a row of
     badges, figures or actions sized for a desktop column. None of them wrap,
     so each one pushes the page sideways. The scrolling rows are excluded:
     a tab bar or sub-nav must stay on one line and scroll instead. */
  [style*="display:flex"]:not(.pos-tabs):not(.pos-tabbar):not(.pos-subnav):not(.pos-nav):not(.pos-hub-tabs),
  [style*="display: flex"]:not(.pos-tabs):not(.pos-tabbar):not(.pos-subnav):not(.pos-nav):not(.pos-hub-tabs) {
    flex-wrap: wrap !important;
    min-width: 0;
  }

  /* ---------------------------------------------------------------- *
   * 9c. Two-column page layouts stack.
   *
   * The customer hub splits into a main column and a rail using flex with a
   * flex-basis of 460px on each child. At 390px wide that basis alone puts
   * the content 105px past the screen edge, which is what was clipping the
   * right-hand side of the customer journey and the recent-activity list.
   * ---------------------------------------------------------------- */

  .cuh-layout,
  .pos-split,
  .pos-two-col {
    flex-direction: column;
    flex-wrap: wrap;
  }

  .cuh-layout > *,
  .pos-split > *,
  .pos-two-col > * {
    flex: 1 1 auto !important;
    width: 100%;
    max-width: 100%;
    min-width: 0;
  }

  /* A scrolling row must be constrained to the screen, or it grows to fit its
     content and takes the page with it instead of scrolling inside itself. */
  .pos-tabs,
  .pos-tabbar,
  .pos-subnav,
  .pos-hub-tabs,
  .pos-table-scroll {
    max-width: 100%;
    min-width: 0;
  }

  /* ---------------------------------------------------------------- *
   * 9b. Inline grids.
   *
   * 73 templates declare their columns in an inline style, typically
   * `repeat(auto-fit, minmax(300px, 1fr))` or `2fr 1fr`. Every one of them
   * assumes desktop width, and at 390px they are the single largest cause of
   * horizontal overflow. On a phone they all become one column.
   *
   * !important is unavoidable: an inline style beats any stylesheet rule
   * without it, and the alternative is editing 73 templates by hand.
   * ---------------------------------------------------------------- */

  /* minmax(0, 1fr), not 1fr. A grid track sized 1fr still refuses to go below
     its item's min-content width, and a grid item defaults to min-width:auto,
     so a single-column grid holding a wide panel still pushed the page 280px
     sideways. minmax(0, ...) lets the track shrink, and the min-width:0 below
     lets the item inside it shrink too. */
  [style*="grid-template-columns"] {
    grid-template-columns: minmax(0, 1fr) !important;
  }

  /* Grid and flex items default to min-width:auto and will not shrink below
     their own min-content. That is the single most common structural cause of
     sideways scroll on this estate. */
  /* Targeting the inline-style containers alone was not enough: plenty of
     grids and flex rows are declared in a class (.assist-grid, .cuh-layout),
     and CSS cannot select on computed display. So every ordinary container in
     the content area is made shrinkable. min-width:0 is a no-op for a block
     element, which already shrinks, and is exactly what a grid or flex item
     needs to stop refusing to go below its own min-content. max-width keeps a
     column-flex child from sizing itself to its content.

     Tables are deliberately not in this list: one inside .pos-table-scroll
     needs min-width:max-content to scroll. */
  .pos-content div,
  .pos-content section,
  .pos-content article,
  .pos-content aside,
  .pos-content li,
  .pos-content label,
  .pos-content form,
  .pos-content fieldset {
    min-width: 0;
    max-width: 100%;
  }

  /* Same reasoning for inline fixed widths and min-widths that exceed the
     screen. */
  /* Controls are excluded. Stripping an inline min-width off a text input in
     a flex row let it shrink to 28px, which is narrower than the thumb it is
     meant to be typed into. Containers shrink; controls keep their floor. */
  [style*="min-width"]:not(input):not(select):not(textarea):not(button):not(a) {
    min-width: 0 !important;
  }


  /* ---------------------------------------------------------------- *
   * 9d. The focus page's category bars.
   *
   * Each row is a 150px right-aligned category link, a proportional bar and a
   * count. The bar's segments are links whose WIDTH is the data, so they run
   * from 4 to 37px and can never be 44px targets without destroying what the
   * bar shows. The age filter was reachable nowhere else, so on a phone it was
   * simply unreachable.
   *
   * The row stacks: the category becomes a full-width 44px target, the bar
   * keeps its exact proportions at a readable height, and the same age links
   * appear beneath as chips a thumb can hit.
   * ---------------------------------------------------------------- */

  .focus-bar-row {
    flex-wrap: wrap;
    align-items: stretch !important;
    gap: 0.25rem !important;
  }

  .focus-bar-cat {
    width: auto !important;
    flex: 1 1 auto !important;
    text-align: left !important;
    font-size: 0.8125rem !important;
  }

  .focus-bar-cat a {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }

  .focus-bar-count {
    width: auto !important;
    min-width: 44px;
    display: flex;
    align-items: center;
    justify-content: flex-end;
  }

  .focus-bar-track {
    flex: 1 1 100% !important;
    order: 5;
    height: 18px !important;
  }

  .focus-seg-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.375rem;
    flex: 1 1 100%;
    order: 6;
    margin-bottom: 0.5rem;
  }

  .focus-seg-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    min-height: 44px;
    padding: 0 0.625rem;
    border: 1px solid var(--pos-line2);
    border-radius: 999px;
    color: var(--pos-text);
    font-size: 0.8125rem;
    text-decoration: none;
    white-space: nowrap;
  }

  .focus-seg-dot {
    width: 10px;
    height: 10px;
    border-radius: 2px;
    flex: none;
  }

  /* The bar's own legend is indented to clear the 150px label column, which
     does not exist once the row stacks. */
  .focus-legend {
    padding-left: 0 !important;
  }

  /* ---------------------------------------------------------------- *
   * 9e. The weekly review card header.
   *
   * Title, progress and an action cluster on one row. The cluster carries
   * margin-left:auto and was compressed to 46px by the title beside it, so
   * "Mark reviewed" hung outside its own button. Scoped to this header, so it
   * cannot repeat the two earlier attempts that fixed it globally and pushed
   * other pages sideways.
   * ---------------------------------------------------------------- */

  .dv-panel-head {
    flex-wrap: wrap;
  }

  .dv-panel-head > [style*="margin-left:auto"] {
    margin-left: 0 !important;
    flex: 1 1 100%;
    justify-content: flex-start;
  }

  /* ---------------------------------------------------------------- *
   * 10. Assistant and chat widgets collapse to a compact launcher and open
   *     as a sheet. They must never cover page actions when closed.
   * ---------------------------------------------------------------- */

  .pos-assistant-launcher,
  .assistant-launcher,
  .staff-chatbot-launcher {
    width: 52px;
    height: 52px;
    right: max(0.75rem, env(safe-area-inset-right));
    bottom: calc(0.75rem + env(safe-area-inset-bottom));
  }

  .pos-assistant-panel,
  .assistant-panel,
  .staff-chatbot-panel {
    position: fixed;
    inset: 0;
    width: 100vw;
    max-width: 100vw;
    height: 100dvh;
    max-height: 100dvh;
    border-radius: 0;
    padding-bottom: env(safe-area-inset-bottom);
  }

  /* ---------------------------------------------------------------- *
   * 11. Maps, plans and other interactive media fit the viewport.
   * ---------------------------------------------------------------- */

  .pos-map,
  .pos-plan,
  #map,
  .map-container {
    width: 100%;
    max-width: 100%;
    height: 60dvh;
    touch-action: pan-x pan-y pinch-zoom;
  }
}

/* Landscape phones: the shell chrome gets shorter so content keeps room. */
@media (max-width: 1023.98px) and (orientation: landscape) and (max-height: 480px) {
  .pos-header {
    min-height: 44px;
  }

  .pos-map,
  .pos-plan,
  #map,
  .map-container {
    height: 75dvh;
  }
}

/* ---------------------------------------------------------------------- *
 * 11. Run AI Assist (11 September 2026)
 *
 * The overlay is the estate's no-redirect rule taken to its conclusion: on a
 * phone "in place" has to mean a full-screen sheet, because a person clearing
 * their day on a 390px screen cannot also be navigating a shell. So the
 * overlay IS the screen, the header collapses to two scrollable rows, and the
 * card becomes one column with the actions on a bar the thumb can reach.
 *
 * Everything below is inside the breakpoint query. At 1024 and above not one
 * declaration applies.
 * ---------------------------------------------------------------------- */

@media (max-width: 1023.98px) {

  /* The button stays large and reachable, and the shimmer keeps working: it
   * is a transform on a composited layer, which is the cheap kind. */
  .aa-runbtn {
    width: 100%;
    max-width: 420px;
    justify-content: center;
    min-height: 56px;
    font-size: 1rem;
  }
  .aa-runbtn-wrap { display: block; width: 100%; }

  /* The overlay is the full sheet, safe areas included. */
  .aa-overlay {
    padding: 0;
    padding-bottom: env(safe-area-inset-bottom);
  }

  /* The header is the one place this design has to give: five stats, four
   * modes and three chips do not fit across 390px. Stats and modes each
   * become their own single scrollable row, which is the estate's tab-row
   * rule applied to a stat row. Neither ever wraps. */
  .aa-head {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 8px 10px;
    padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px));
    padding-top: max(10px, env(safe-area-inset-top));
  }
  .aa-head h1 { font-size: 1rem; }
  /* Everything in the header wraps. These two used to be scrolling strips
   * with a 22px edge fade, and the fade did not carry the message: the fourth
   * tab and the third chip were simply off the screen, and the tab strip is
   * the way into the whole relevance model. Two short rows beat one row with
   * half its contents hidden. */
  .aa-modes,
  .aa-chips {
    grid-column: 1 / -1;
    display: flex;
    flex-wrap: wrap;
    overflow: visible;
    margin-left: 0;
    row-gap: 6px;
  }

  /* The stats wrap rather than scroll. The tabs and the chips are things you
   * move through, so a scrolling strip with an edge fade reads correctly for
   * them; the stats are a readout, and the fifth of five was MINS SAVED --
   * the figure this run exists to make real -- permanently off the right of a
   * 390px screen behind a swipe nobody would know to make. */
  .aa-stats {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 10px 12px;
    margin-left: 0;
    overflow: visible;
  }
  .aa-stat { min-width: 0; }
  .aa-stat b { font-size: 1.1rem; }
  .aa-mode,
  .aa-chip { flex: 0 1 auto; min-height: 44px; }
  .aa-mode { padding: 0 12px; font-size: 0.82rem; }
  .aa-chip { min-height: 36px; padding: 0 10px; font-size: 0.76rem; }

  /* The close button is a target in its own right, pinned to the top row
   * beside the title, never inside a row that scrolls away under a thumb. */
  .aa-head > .aa-close {
    grid-row: 1;
    grid-column: 2;
    min-height: 44px;
    min-width: 64px;
    align-self: center;
  }

  /* Edge fades on the three scrolling rows, so it reads as scrollable rather
   * than as content that has been cut off. */


  /* The subtitle under the button carries a money phrase now, which is longer
   * than the bare number it replaced. Let it wrap rather than run off the
   * side. */
  .aa-runbtn-sub {
    display: block;
    max-width: 100%;
    white-space: normal;
    overflow-wrap: break-word;
  }

  /* On a phone the rail goes under the session rather than beside it: at
   * 390px a two-column split gives neither half enough room. */
  .aa-voa-split { grid-template-columns: 1fr; }
  .aa-voa-split .aa-rail { max-height: none; }

  /* One column. The side panel (plan, evidence, blockers) follows the
   * explanation rather than sitting beside it. */
  .aa-stage { padding: 14px 12px 24px; }
  .aa-card-body {
    grid-template-columns: 1fr;
    gap: 16px;
    padding: 16px 14px;
  }
  .aa-card-head { padding: 14px 14px 12px; }
  .aa-card-head h2 { font-size: 1.02rem; }
  .aa-meta { gap: 10px; font-size: 0.74rem; }

  /* Actions: the two that move you through the queue on one row, the four
   * that change the world on another, all of them 44px and none of them
   * wrapping into a shape that puts Skip under your thumb by accident. */
  .aa-actions {
    /* Not sticky on a phone. The overlay scrolls with the page here rather
       than inside its own stage, so `position: sticky` pinned the row to the
       viewport and it floated through the middle of the card, cutting the body
       text in half -- "WHAT THIS IS" above it and the rest of the sentence
       below. A row you scroll to is far better than a bar across the middle of
       what you are reading. */
    position: static;
    padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px));
    gap: 8px;
    display: grid;
    grid-template-columns: repeat(2, 1fr);
  }
  .aa-actions .spacer { display: none; }
  .aa-actions .aa-nav {
    grid-column: 1 / -1;
    justify-content: space-between;
  }
  .aa-actions .aa-btn { width: 100%; min-height: 44px; padding: 0 10px; }
  .aa-actions .aa-nav .aa-btn { width: auto; min-width: 96px; }
  /* The primary action takes the full width. Its label is a real sentence --
     "Claim the property on the gateway", "Update the project status" -- and in
     a half-width cell it wrapped to two lines while its neighbour stayed on
     one, leaving the row uneven. It is also the button being aimed at. */
  .aa-actions > #aa-primary { grid-column: 1 / -1; }
  .aa-count { min-width: 0; font-size: 0.74rem; }

  /* The background strip must not eat the screen on a phone. */
  .aa-strip { max-height: 26vh; padding: 8px 12px; }

  /* A document or a mounted panel is full width on a phone: side by side is
   * not a thing that exists at 390px. Taller, too, because a lease read
   * through a 200px window is not read. */
  .aa-viewer iframe,
  .aa-panel-frame iframe { height: 70dvh; }
  .aa-doc { gap: 8px; }
  .aa-doc .aa-btn { min-height: 44px; flex: 1 0 auto; }
  .aa-doc-name { flex: 1 0 100%; }
  .aa-picker { max-height: 46vh; }

  /* Fields: 16px in px, so iOS does not zoom the sheet on focus. */
  .aa-field input,
  .aa-field textarea,
  .aa-field select { font-size: 16px; }
  .aa-field textarea { min-height: 120px; }

  /* The landing page's own panels. */
  .aa-runbtn-sub { font-size: 0.8rem; }
}

/* Landscape phone: the header would otherwise take a third of the screen. */
@media (max-width: 1023.98px) and (orientation: landscape) and (max-height: 480px) {
  .aa-head { padding-top: 6px; padding-bottom: 6px; }
  .aa-stats { display: none; }
  .aa-strip { max-height: 20vh; }
}

/* Shared work drawer: full phone sheet, with a stable close target beside long titles. */
@media (max-width: 1023.98px) {
  .pos-drawer { width: 100%; border-left: 0; }
  .pos-drawer-head { flex-shrink: 0; }
  .pos-drawer-title { min-width: 0; overflow-wrap: anywhere; }
  .pos-drawer-close, .pos-frame-close { flex: 0 0 auto; min-width: 72px; white-space: nowrap; }
}

/* Desktop headers retain every control when connection status or user text grows. */
@media (min-width: 1024px) {
  .pos-header {
    flex-wrap: wrap;
    height: auto;
    min-height: var(--pos-header-h);
    padding-top: 8px;
    padding-bottom: 8px;
    row-gap: 8px;
  }
  .pos-header > * { max-width: 100%; }
  .pos-header-title { flex: 1 1 140px; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .pos-header-spacer { display: none; }
  .pos-header .pos-search { flex: 1 1 220px; min-width: 180px; }
  .pos-header .pos-iconbtn { flex: 0 0 34px; min-width: 34px; }
  .pos-header .pos-conn-chip { flex-shrink: 0; white-space: normal; }
}
@media (min-width: 1024px) and (max-width: 1440px) {
  .pos-header-title { flex-basis: 100px; }
  .pos-header .pos-conn-chip { max-width: 240px; }
  .pos-header .pos-search { order: 10; flex-basis: 100%; min-width: 0; }
}

/* Native postal designer and record cards: section 11.9. */
@media (max-width: 1023.98px) {
  .pl-designer-grid { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .pl-edit-panel, .pl-library-panel, .pl-history, .pl-preview-heading { padding: 16px; }
  .pl-designer .pos-btn, [data-postal-library] .pos-btn, [data-postal-despatch] .pos-btn { min-height: 44px; min-width: 44px; white-space: normal; }
  .pl-fields, .pl-filter { flex-direction: column; align-items: stretch; }
  .pl-insert { display: grid; grid-template-columns: minmax(0, 1fr); }
  .pl-toolbar { gap: 8px; }
  .pl-toolbar .pos-btn { flex: 0 1 auto; }
  .pl-body { padding: 14px; min-height: 320px; }
  .pl-template-grid { grid-template-columns: minmax(0, 1fr); }
  .pl-library-heading { align-items: stretch; }
  .pl-library-heading label { flex: 1 1 100%; }
  .pl-preview-panel iframe { height: 640px; }
  .pl-table, .pl-table tbody, .pl-table tr, .pl-table td { display: block; width: 100%; }
  .pl-table thead { display: none; }
  [data-postal-library] .pl-review-confirm, [data-postal-despatch] .pl-review-confirm { min-height: 44px; align-items: center; }
  .pl-table tr { border: 1px solid var(--pos-line); border-radius: 12px; margin-bottom: 12px; padding: 12px; }
  .pl-table td { padding: 7px 0; border: 0; }
  .pl-table td[data-label]::before { content: attr(data-label); display: block; font-size: 12px; font-weight: 700; color: var(--pos-dim); margin-bottom: 3px; }
  .pl-table td a { display: inline-flex; min-height: 44px; align-items: center; }
  .pl-conflict { padding: 12px; }
  .pl-conflict .pos-btn { width: 100%; }
}

@media (max-width: 1023.98px) {
  .pos-content .pl-server-body[data-bc-body] a { display: inline; min-height: 0 !important; min-width: 0; padding: 0; }
  .pl-body table, .pl-server-body table { display: block; overflow-x: auto; table-layout: auto; }
  .pl-body td, .pl-body th, .pl-server-body td, .pl-server-body th { min-width: 100px; }
}

/* Complete postal pack review: stack metadata and full-width printed pages. */
@media(max-width:1023px){.pl-review-grid{grid-template-columns:minmax(0,1fr)}.pl-record .pl-heading{align-items:flex-start}.pl-record .pl-edit-panel{padding:16px}.pl-record .pos-btn{max-width:100%;white-space:normal;min-height:44px}.pl-summary{grid-template-columns:minmax(0,.7fr) minmax(0,1fr)}}

@media(max-width:1023px){.pl-compose-grid{grid-template-columns:minmax(0,1fr)}.pl-compose .pl-edit-panel{padding:16px}.pl-compose .pos-btn{min-height:44px;max-width:100%;white-space:normal}.pl-customer-search{flex-wrap:wrap}.pl-customer-search label{flex-basis:200px}.pl-compose .pos-input{font-size:16px}}

@media (max-width: 1023.98px) {
  .pl-record .pl-options a[download] { display: flex; align-items: center; min-height: 44px; min-width: 44px; padding: 6px 0; overflow-wrap: anywhere; }
  .pl-record .pl-options { scroll-margin-top: calc(var(--pos-sticky-h, 5rem) + 0.75rem); }
}

/* Retained contract references use the shared mobile document cards. */
@media (max-width: 1023.98px) {
  .pos-contract-documents td { white-space: normal !important; }
  .pos-contract-documents td > span { min-width: 0; overflow-wrap: anywhere; }
}

/* Payment review uses cards and vertical collection lists, with no data grid. */
@media (max-width: 1023.98px) {
  .billing-review-card {padding:16px;}
  .billing-review-head {gap:10px;}
  .billing-review h1 {font-size:1.5rem;}
  .billing-review-fields {display:flex;flex-direction:column;align-items:stretch;gap:6px;}
  .billing-review-fields>label {width:100%;min-width:0;}
  .billing-review .pos-btn {max-width:100%;}
  .billing-review-facts {grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;}
}

/* Recurring billing: mobile cards and full-screen invoice sheets. */
@media (max-width: 1023.98px) {
  .subscription-workspace>header.subscription-head { flex-direction:column;align-items:stretch; }
  .subscription-workspace>header.subscription-head>.subscription-actions { justify-content:flex-start; }
  .subscription-workspace .subscription-stats { grid-template-columns:repeat(2,minmax(0,1fr)); }
  .subscription-workspace .subscription-stats>div { padding:12px;gap:4px; }
  .subscription-workspace .subscription-stats strong { font-size:1.4rem;line-height:1.2; }
  .subscription-workspace .subscription-stats span { font-size:.8rem;line-height:1.3; }
  .subscription-workspace .subscription-card { padding:16px; }
  .subscription-workspace .subscription-facts { grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:14px; }
  .subscription-workspace .subscription-filters>label,.subscription-workspace .subscription-fields>label { flex-basis:100%; }
  .subscription-workspace .subscription-actions { align-items:stretch; }
  body .subscription-dialog { position:fixed;inset:0;margin:0;width:100%;max-width:100%;height:100dvh;max-height:100dvh;border-radius:0; }
  body .subscription-dialog header { padding:calc(12px + env(safe-area-inset-top)) 14px 12px; }
  body .subscription-dialog iframe { padding-bottom:env(safe-area-inset-bottom);box-sizing:border-box; }
}

/* Prepared property correspondence is an account panel, including on phones. */
@media (max-width: 1023.98px) {
  .ppa .ppa-card { padding: 16px; }
  .ppa .ppa-lanes { grid-template-columns: minmax(0, 1fr); }
  .ppa .ppa-lane { padding: 16px; }
  .ppa .ppa-toolbar { align-items: stretch; }
  .ppa .ppa-ack { padding: 10px 0; }
  .ppa .ppa-ack input { min-height: 24px; }
}

/* Native customer composer: responsive additions are scoped to mobile. */
@media (max-width: 1023.98px) {
  .pos-composer #ce-root { padding: 16px 16px max(24px, env(safe-area-inset-bottom)); }
  .pos-composer .ce-heading h1 { font-size: 1.25rem; }
  .pos-composer .ce-actions .ce-button { flex: 1 1 140px; }
  .pos-composer .ce-fields input { font-size: max(16px, 1rem); }
  .pos-composer #email-body { padding: 0; }
  .pos-composer #email-body > table { table-layout: fixed; max-width: 100%; }
  .pos-composer #email-body table { max-width: 100%; }
  .pos-composer #email-body td { overflow-wrap: anywhere; }
  .pos-composer .ce-provenance dl > div { grid-template-columns: 1fr; gap: 4px; }
  .pos-composer .ce-provenance summary { display: list-item; }
  .pos-composer .ce-provenance summary span { display: block; margin: 0; }
  .pos-composer .ce-empty { margin: 32px auto; }
}

@media (max-width: 1023.98px) {
  #ce-overlay .ce-host-dialog { width: 100%; height: 100dvh; max-width: none; border-radius: 0; border: 0; }
  #ce-overlay .ce-host-header { padding: max(12px, env(safe-area-inset-top)) 16px 12px; gap: 8px; }
  #ce-overlay .ce-host-actions { width: 100%; }
  #ce-overlay .ce-host-actions .ce-button { flex: 1; padding: 10px 8px; }
}

/* Header notification controls remain reachable on small screens. */
@media (max-width:1023.98px) {
  #pos-bell-items > a, #pos-notifsw-types > label { min-height:44px; box-sizing:border-box; }
  #pos-notifsw-pop select, #pos-notifsw-save, #pos-notifsw-pop a { min-height:44px; font-size:16px; }
  #pos-notifsw-pop a { display:inline-flex; align-items:center; }
  #pos-notifsw-pop input[type="checkbox"] { width:24px; height:24px; margin:0 2px 0 12px; flex:none; }
}

/* In-place viewer actions retain complete labels and phone-sized targets. */
@media (max-width: 1023.98px) {
  .pos-frame-head { flex-shrink: 0; }
  .pos-frame-title { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
  .pos-frame-actions { flex: 0 0 auto; }
  .pos-frame-link, .pos-frame-close { display: inline-flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; white-space: nowrap; }
}

/* Notification centres: filters, read controls and preference labels. */
@media (max-width:1023.98px) {
  #notification-centre button, #notification-centre .pos-btn, #notification-centre .nc-title { min-height:44px; }
  #notification-centre .nc-title { display:inline-flex; align-items:center; }
  #notification-centre select { font-size:16px; min-height:44px; }
  #notification-centre input[type="checkbox"] { width:24px; height:24px; flex:none; }
  #notification-centre label { min-height:44px; }
  .nc-filters { align-items:stretch; }
  .nc-filters .pos-input { flex:1 1 180px; min-width:0; }
  .nc-row { gap:10px; padding:14px; }
  #nc-switches { padding:16px; }
}

/* Keep each Landlord switch beside its description on small screens. */
@media (max-width:1023.98px) {
  #ll-customer .ll-switch { flex-wrap:nowrap !important; justify-content:flex-start; }
  #ll-customer .ll-switch > span { min-width:0; flex:1; }
}

@media (max-width:1023.98px) {
  .px-filter input[type="search"] { flex-basis:100%; width:100%; }
  .px-filter select { flex:1 1 160px; }
  .px-import-form { width:100%; }
  #px-status, #px-workspace h1, #px-editor, #px-open, #px-results { scroll-margin-top:210px; }
}

@media (max-width:1023.98px) {
  .px-table > .pos-table-cards { min-width:0; width:100%; }
  .px-table .pos-table-cards td { display:block; max-width:none; min-width:0; white-space:normal; text-align:left; overflow-wrap:anywhere; }
  .px-table .pos-table-cards td::before { display:block; margin-bottom:4px; }
  .px-table .pos-table-cards td:first-child { position:static; }
  .px-table .pos-table-cards td[data-label="Actions"] { display:flex; flex-wrap:wrap; justify-content:flex-start; }
  .px-table .pos-table-cards td[data-label="Actions"]::before, .px-table::after { display:none; }
}

/* D1250: survey controls stack without changing desktop layout. */
@media (max-width: 1023.98px) {
  #survey-workspace #sv-map-wrap { grid-template-columns:minmax(0,1fr)!important; }
  #survey-workspace form[method="get"] { flex-wrap:wrap;min-width:0;max-width:100%; }
  #survey-workspace form[method="get"] input { min-width:0!important;width:100%; }
  #survey-workspace .pos-panel { min-width:0; }
  #survey-workspace #sv-newtask form,#survey-workspace #sv-newfp form { grid-template-columns:minmax(0,1fr)!important; }
  #survey-workspace .pos-panel>div { flex-wrap:wrap; }
  #survey-workspace .pos-table th,#survey-workspace .pos-table td { position:static!important; }
  #survey-workspace button,#survey-workspace .pos-btn,#survey-workspace input,#survey-workspace select { min-height:44px; }
  #survey-workspace input[type="checkbox"] { min-width:44px; }
  #survey-workspace #sv-draw-controls { max-width:calc(100% - 20px);flex-wrap:wrap; }
  #survey-workspace #sv-draw-controls span { white-space:normal!important; }
}

@media (max-width:1023.98px){#survey-workspace .sv-filters>form{flex:1 1 100%;width:100%}#survey-workspace .sv-filters>form input{flex:1 1 100%}#survey-workspace .sv-filters>form select{flex:1}#survey-workspace .sv-filters>span{display:none}}
@media (max-width:1023.98px){#survey-workspace{padding-bottom:110px}}

#survey-workspace #sv-map-retry[hidden]{display:none!important}

/* Customer survey records stack as labelled cards. */
@media(max-width:1023.98px){
  .sc-workspace{padding-bottom:90px}
  .sc-workspace .pos-table th,.sc-workspace .pos-table td{position:static}
  .sc-workspace .cv-form{min-width:0;gap:12px}
  .sc-workspace .cv-label{max-width:100%}
  .sc-workspace a[href^="/property/"]{display:inline-flex;align-items:center;min-height:44px;min-width:44px}
  .sc-workspace .cv-form{display:grid;grid-template-columns:minmax(0,1fr)}
  .sc-workspace .cv-label,.sc-workspace .cv-input{width:100%!important;min-width:0!important}
  .sc-workspace .pos-btn,.sc-workspace .cv-input,.sc-workspace summary{min-height:44px;min-width:44px;font-size:16px}
  .sc-workspace .pos-table thead{display:none}
  .sc-workspace .pos-table,.sc-workspace .pos-table tbody,.sc-workspace .pos-table tr{display:block;width:100%;min-width:0}
  .sc-workspace .pos-table tr{padding:8px 12px;border-bottom:1px solid var(--pos-line);box-sizing:border-box}
  .sc-workspace .pos-table td{display:block;max-width:100%;white-space:normal!important;text-align:left!important;padding:6px 0;overflow-wrap:anywhere}
  .sc-workspace .pos-table td::before{content:attr(data-label);display:block;color:var(--pos-dim);font-size:11px;font-weight:600}
  .sc-workspace .pos-table td[data-label=Actions]{display:flex;gap:8px;flex-wrap:wrap}
  .sc-workspace .pos-table td[data-label=Actions]::before{display:none}
}

@media(max-width:1023.98px){.cuh-am{max-width:100%;min-width:0}.cuh-am summary{overflow-wrap:anywhere}.cuh-am-form select{width:100%;min-width:0!important}}

/* Appointment delivery must remain readable beside every record on phones. */
@media (max-width: 1023.98px) {
  .ap-delivery-table.pos-table thead{display:none}
  .ap-delivery-table.pos-table,.ap-delivery-table.pos-table tbody,.ap-delivery-table.pos-table tr{display:block;width:100%;min-width:0}
  .ap-delivery-table.pos-table tr{padding:8px 12px;border-bottom:1px solid var(--pos-line);box-sizing:border-box}
  .ap-delivery-table.pos-table td{display:block;position:static;max-width:100%;white-space:normal!important;text-align:left!important;padding:6px 0;overflow-wrap:anywhere}
  .ap-delivery-table.pos-table td::before{content:attr(data-label);display:block;color:var(--pos-dim);font-size:11px;font-weight:600}
  .ap-delivery-table.pos-table td[data-label=Actions]::before{display:none}
  .ap-delivery-table.pos-table .pos-badge{max-width:100%;box-sizing:border-box;white-space:normal}
  .ap-delivery-table.pos-table .pos-btn{min-width:44px;min-height:44px}
}

@media (max-width: 1023.98px) {
  [data-ap-editor] .ap-proposal-grid{grid-template-columns:minmax(0,1fr)!important}
  [data-ap-editor] [data-ap-proposal-field]{width:100%;min-width:0;box-sizing:border-box;font-size:16px!important;min-height:44px}
  [data-ap-editor] .pos-btn{min-width:44px;min-height:44px}
  [data-ap-editor] .ap-email-preview{height:440px}
}

@media (max-width: 1023.98px) {
  [data-ap-bulk] .pos-btn{min-height:44px;min-width:44px}
  [data-ap-bulk] #ap-bulk-results a{display:inline-flex;align-items:center;min-height:44px;min-width:44px}
}

@media (max-width: 1023.98px) {
  [data-ap-lifecycle]{max-width:100%}
  [data-ap-lifecycle] input{font-size:16px!important;min-height:44px;min-width:0;max-width:100%;box-sizing:border-box}
  .ap-lifecycle-feedback .pos-btn{min-height:44px;min-width:44px}
}

/* D1268: the approval queue remains readable and actionable on narrow screens. */
@media(max-width:1023px){
 .ap-queue-table,.ap-queue-table tbody,.ap-queue-table tr,.ap-queue-table td{display:block;width:auto!important;max-width:100%;box-sizing:border-box}
 .ap-queue-table thead{display:none}
 .ap-queue-table tr{border:1px solid var(--pos-line);border-radius:12px;margin:12px 0;padding:8px}
 .ap-queue-table td{position:static!important;padding:8px!important;border:0!important;text-align:left!important}
 .ap-queue-table td[data-label]::before{content:attr(data-label);display:block;font-size:12px;font-weight:700;color:var(--pos-dim);margin-bottom:4px}
 .ap-queue-actions input{font-size:16px!important;min-height:44px}
 [data-ap-queue] .pos-btn{min-width:44px;min-height:44px}
 .ap-queue-actions form{max-width:100%}
}

/* D1270: labelled appointment type creation and narrow-screen service cards. */
@media(max-width:1023px){
 .ap-types-table,.ap-types-table tbody,.ap-types-table tr,.ap-types-table td{display:block;width:auto!important;max-width:100%!important;box-sizing:border-box}
 .ap-types-table thead{display:none}
 .ap-types-table tr{border:1px solid var(--pos-line);border-radius:12px;margin:12px 0;padding:8px}
 .ap-types-table td{position:static!important;padding:8px!important;border:0!important;text-align:left!important}
 .ap-types-table td[data-label]::before{content:attr(data-label);display:block;font-size:12px;font-weight:700;color:var(--pos-dim);margin-bottom:4px}
 .ap-types-form input{font-size:16px!important;min-height:44px}
 [data-ap-types] .pos-btn{min-width:44px;min-height:44px}
}

/* D1280: public appointment actions remain readable and reachable on phones. */
@media (max-width:1023.98px) {
  .ap-public-body .wrap {margin:20px auto;padding:0 max(16px,env(safe-area-inset-left)) max(24px,env(safe-area-inset-bottom));}
  .ap-public-body .card {padding:22px 18px;border-radius:16px;}
  .ap-public-body h1 {font-size:1.5rem;}
  .ap-public-body .slot {padding:16px 18px;}
  .ap-public-body .actions {flex-direction:column;align-items:stretch;}
  .ap-public-body .actions > .btn {width:100%;}
  .ap-public-header {padding:16px;}
}

/* D1282: complete appointment list filters and labelled record cards. */
@media (max-width:1023.98px) {
  [data-ap-list-filter] {align-items:stretch;}
  [data-ap-list-filter] label {width:100%;}
  [data-ap-list-filter] .ap-list-search {min-width:0;flex-basis:100%;}
  [data-ap-list-filter] > .pos-btn {flex:1;}
  .ap-list-nav nav {width:100%;}
  .ap-list-nav nav > a {flex:1;}
}

/* D1301: complete Files controls and filters at every narrow viewport. */
@media (max-width:1023.98px) {
  .fs-controls-dialog{margin:0;width:100vw;max-width:100vw;height:100dvh;max-height:100dvh;border-radius:0;}
  .fs-controls-dialog>header{padding:10px 14px;padding-top:calc(10px + env(safe-area-inset-top));}
  [data-fs-dialog-body]{padding:14px;padding-bottom:calc(14px + env(safe-area-inset-bottom));}
  [data-fs-record] .files-ai-editform{grid-template-columns:minmax(0,1fr);}
  [data-fs-record] .files-ai-editform input,[data-fs-record] .files-ai-editform select,[data-fs-record] .files-ai-editform textarea{min-height:44px;font-size:16px;}
  [data-fs-record] summary{min-height:44px;display:flex;align-items:center;}
  [data-fs-record] label:has([type=checkbox]){flex-direction:row;align-items:center;min-height:44px;}
  .fs-control-feedback dl{grid-template-columns:minmax(0,1fr);gap:6px;}
  .fs-control-feedback dd{margin-bottom:10px;}
  [data-fs-panel] .fs-filters{flex-wrap:wrap;overflow:visible;}
  [data-fs-panel] .fs-search,[data-fs-panel] .fs-prop{flex:1 1 100%;min-width:0;width:100%;max-width:none;}
  [data-fs-panel] .fs-filter-sep{display:none;}
  [data-fs-panel] td[data-label=Property] a{min-height:44px;display:flex;align-items:center;}
}

@media (max-width:1023.98px) {
  .fv .fv-btn{min-width:44px;width:44px;height:44px;flex:0 0 44px;}
  .fv .fv-tools{flex-wrap:wrap;justify-content:flex-start;gap:4px;}
}

/* Shared in-place email composer: mobile uses the full available viewport. */
#fmt-mailer-close{color:#fff;background:#263449;border-color:#657489}
@media(max-width:1023.98px){#fmt-mailer{padding:0!important}#fmt-mailer>div{width:100%!important;height:100dvh!important;border:0!important;border-radius:0!important}#fmt-mailer>div>div{padding-top:calc(8px + env(safe-area-inset-top))!important}#fmt-mailer iframe{min-height:0}}

/* D1309: Files rail upload and contract register. */
@media(max-width:1023.98px){
 .fs-rail [data-fu-form]{grid-template-columns:minmax(0,1fr);}
 .fs-contract-table thead{position:absolute;width:1px;height:1px;clip-path:inset(50%);overflow:hidden;}
 .fs-contract-table,.fs-contract-table tbody,.fs-contract-table tr,.fs-contract-table td{display:block;width:auto;}
 .fs-contract-table tr{padding:12px 16px;border-top:1px solid var(--pos-line);}
 .fs-contract-table td{padding:6px 0;border:0;}
 .fs-contract-table td::before{content:attr(data-label);display:block;margin-bottom:4px;color:var(--pos-dim);font-size:12px;}
}
/* D1310: signature editing and preview stay within the narrow page. */
@media(max-width:1023.98px){
 .sig-columns{grid-template-columns:minmax(0,1fr);padding:16px;}
 .sig-card>header{padding:16px;}
 .sig-feedback{margin:16px 16px 0;}
 .signature-manager .sig-preview{scroll-margin-top:210px;}
 .signature-manager .sig-preview iframe{height:480px;}
 .sig-card[data-sig-editable="false"] .sig-preview{grid-row:1;}
}

/* Staff Tasks: retain full forms and history within a narrow workspace. */
@media (max-width:1023.98px){
 .tk-workspace #tk-form{grid-template-columns:minmax(0,1fr)!important;}
 .tk-workspace .pos-kpi-row{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));}
 .tk-workspace [data-tk-overview]{grid-template-columns:minmax(0,1fr)!important;}
 .tk-workspace .tk-row{display:grid!important;grid-template-columns:minmax(0,1fr);}
 .tk-workspace .tk-row>div:last-child{align-items:flex-start!important;}
 .tk-workspace [data-tk-filter]{display:grid!important;grid-template-columns:minmax(0,1fr);}
 .tk-workspace [data-tk-filter]>*{width:100%;min-width:0!important;}
 .tk-workspace #tk-modal{padding:0!important;height:100dvh;}
 .tk-workspace #tk-modal>.pos-panel{width:100%;height:100dvh;max-height:100dvh!important;border-radius:0;}
 .tk-workspace #tk-modal>.pos-panel>div{padding:14px!important;}
 .tk-workspace #tk-modal .tk-reassign{min-width:0!important;width:100%;}
 .tk-workspace #tk-modal div:has(>#tk-comment){display:grid!important;grid-template-columns:minmax(0,1fr);}
 .tk-workspace #tk-modal div:has(>span:nth-child(3)){flex-wrap:wrap;}
}

@media (max-width: 1023.98px) {
  .wr-workspace .pos-kpi-row{grid-template-columns:repeat(2,minmax(0,1fr))!important;gap:10px;}
  .wr-workspace .pos-kpi{min-width:0;}
  .wr-workspace .wr-savings-grid{grid-template-columns:minmax(0,1fr)!important;}
  .wr-workspace .pos-table,.wr-workspace .pos-table tbody{display:block;width:100%;}
  .wr-workspace .pos-table thead{display:none;}
  .wr-workspace .pos-table tbody tr{display:block;border-bottom:1px solid var(--pos-line);padding:12px;}
  .wr-workspace .pos-table td{display:grid;grid-template-columns:95px minmax(0,1fr);gap:10px;border:0;padding:7px 0;text-align:left;font-size:14px!important;}
  .wr-workspace .pos-table td::before{content:attr(data-label);font-size:12px;font-weight:600;color:var(--pos-text-strong);}
  .wr-workspace .pos-table td:has(.ra-confirm){display:block;}
  .wr-workspace .pos-table td:has(.ra-confirm)::before{display:block;margin-bottom:8px;}
  .wr-workspace .wt-row{flex-direction:column;}
  .wr-workspace .wt-row>div{min-width:0;max-width:100%!important;text-align:left!important;}
  .wr-workspace #wt-q,.wr-workspace #wt-urgency,.wr-workspace #wt-type,.wr-workspace #wt-owner{flex:1 1 100%;width:100%;}
  .wr-workspace .wr-pane>div:first-child{align-items:stretch!important;}
  .wr-workspace .wr-pane input:not([type=radio]):not([type=checkbox]){width:100%!important;}
  .wr-workspace .pos-table td>div{grid-column:2;}
  .wr-workspace .pos-table td>a,.wr-workspace .pos-table td>span{min-width:0;max-width:100%;}
}

/* My Queue: complete task pages and team blockers, D1323. */
@media (max-width:1023.98px){
 .q-workspace .q-heading>.pos-btn{margin-top:0}
 .q-workspace .q-kpis{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}
 .q-workspace .q-columns{grid-template-columns:minmax(0,1fr)}
 .q-workspace .q-row{display:block}.q-workspace .q-row-actions{flex-direction:row;flex-wrap:wrap;max-width:none;margin-top:10px}
 .q-workspace .q-row-actions>*{flex:1 1 150px}.q-workspace .q-toolbar{display:block}
 .q-workspace .q-toolbar form{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));margin-top:12px}.q-workspace .q-toolbar label{width:100%;grid-column:1/-1}
 .q-workspace .q-toolbar select{width:100%;grid-column:1/-1}.q-workspace .q-toolbar button{width:100%}
}

@media (max-width: 1023.98px) {
  body.aa-embed .aa-overlay { height:auto !important; min-height:100dvh; }
  body.aa-embed .aa-stage { overflow:visible; flex:none; min-width:0; }
  body.aa-embed .aa-window-controls { padding:10px 12px; gap:8px; }
  body.aa-embed .aa-window-controls span { flex-basis:100%; order:-1; }
  body.aa-embed .aa-workspace-note { padding:10px 12px; }
  body.aa-embed .aa-mode { white-space:normal; }
  body.aa-embed .aa-head, body.aa-embed .aa-card { min-width:0; }
}

/* Reviewed native customer invoices: data tables scroll locally; the review
   fills the viewport while the customer record remains behind it. */
@media(max-width:1023.98px){
 .invoice-review-dialog{width:100%;max-width:100%;height:100dvh;max-height:100dvh;margin:0;border-radius:0;padding:calc(16px + env(safe-area-inset-top)) 16px calc(20px + env(safe-area-inset-bottom))}
 [data-invoice-panel] .invoice-table-scroll>table{min-width:580px}
 [data-invoice-panel] .invoice-table-scroll>table>tbody>tr>td:first-child:not([colspan]),[data-invoice-panel] .invoice-table-scroll>table>thead>tr>th:first-child{position:sticky;left:0;z-index:1;max-width:150px}
 [data-invoice-panel] td[colspan] .invoice-table-scroll>table{min-width:350px}
}

@media(max-width:1023.98px){
 [data-invoice-panel] .invoice-table-scroll>.invoice-native-table{min-width:0;display:block}
 [data-invoice-panel] .invoice-native-table>thead{display:none}
 [data-invoice-panel] .invoice-native-table>tbody,[data-invoice-panel] .invoice-native-table>tbody>tr{display:block}
 [data-invoice-panel] .invoice-native-table>tbody>tr>td{display:block;position:static!important;max-width:none!important;text-align:left!important;width:auto}
 [data-invoice-panel] .invoice-native-table>tbody>tr>td[data-label]::before{content:attr(data-label);display:block;color:var(--pos-dim);margin-bottom:4px}
 [data-invoice-panel] .invoice-native-table>tbody>tr>td[colspan]{padding:12px}
}

/* Customer Tasks tab: list records become labelled cards; desktop unchanged. */
@media(max-width:1023.98px){
 .cuh-task-records,.cuh-task-records .pos-panel{min-width:0;max-width:100%;overflow-wrap:anywhere}
 .cuh-task-records .cuh-task-table,.cuh-task-records .cuh-task-table tbody{display:block;width:100%}
 .cuh-task-records .cuh-task-table thead{display:none}
 .cuh-task-records .cuh-task-table tr{display:block;padding:12px;border-bottom:1px solid var(--pos-line)}
 .cuh-task-records .cuh-task-table td{display:block;width:auto;padding:6px 0;white-space:normal!important;border:0;color:var(--pos-text)!important}
 .cuh-task-records .cuh-task-table td::before{content:attr(data-label);display:block;font-weight:600;color:var(--pos-text-strong);margin-bottom:3px}
 .cuh-task-records .cuh-task-table .pos-dim2{color:var(--pos-text)!important}
 .cuh-task-records .pos-badge{color:var(--pos-text-strong)!important;background:var(--pos-panel2)!important;white-space:normal}
 .cuh-task-records a,.cuh-task-records button{display:inline-flex;align-items:center;min-height:44px;min-width:44px;white-space:normal}
}

/* Account task actions retain labelled cards at every mobile/tablet width. */
@media(max-width:1023.98px){
 .cv-customer-tasks table,.cv-customer-tasks tbody,.cv-customer-tasks tr,.cv-customer-tasks td{display:block;width:100%;box-sizing:border-box}
 .cv-customer-tasks thead{display:none}
 .cv-customer-tasks tr{border-bottom:1px solid var(--pos-line)}
 .cv-customer-tasks td::before{content:attr(data-label);display:block;font-weight:600;color:var(--pos-text-strong)}
 .cv-customer-tasks .cv-taskform input[name=note]{width:100%}
 .cv-customer-tasks .pos-dim2{color:var(--pos-text)!important}
 .cv-customer-tasks .pos-badge{background:var(--pos-panel2)!important;color:var(--pos-text-strong)!important}
}

/* D1502 Legacy invoice history: labelled record cards. */
@media(max-width:1023.98px){
 [data-perfex-ledger] table,[data-perfex-ledger] tbody,[data-perfex-ledger] tbody>tr{display:block;width:100%}
 [data-perfex-ledger] thead{display:none}
 [data-perfex-ledger] tbody>tr{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);border-bottom:2px solid var(--pos-line)}
 [data-perfex-ledger] td{display:block;position:static!important;box-sizing:border-box;max-width:100%;width:auto!important;text-align:left!important}
 [data-perfex-ledger] td[data-label]::before{content:attr(data-label);display:block;color:var(--pos-dim);margin-bottom:4px}
 [data-perfex-ledger] td[colspan]{grid-column:1/-1}
 .legacy-money,.legacy-records{padding:12px}
}

/* D1503 Legacy sync review is an owned full-screen sheet on mobile. */
@media(max-width:1023.98px){
 dialog.perfex-sync-review{margin:0;width:100vw;height:100dvh;max-height:100dvh;border-radius:0;padding:16px 16px calc(16px + env(safe-area-inset-bottom))}
 .perfex-sync-review header{position:sticky;top:-16px;padding-block:16px;z-index:2}
 .perfex-sync-review .pfs-actions button,.perfex-sync-review .pfs-history button{width:100%}
}

/* Ticket184: give the title its own readable row on smaller screens. */
@media (max-width: 1023.98px) {
  .br-create .br-create-fields { display:block; }
  .br-picked li { flex-direction:row-reverse; }
}

@media (max-width: 1023.98px) {
  .map-datasets {padding:16px;gap:16px;}
  .map-datasets .datasets-map {height:360px;}
}

@media (max-width: 1023.98px) {
  .weather-review {padding:16px;gap:16px;}
}

@media(max-width:767px){.pos-panel.airquality-review{padding:24px!important}}

@media(max-width:767px){.pos-panel.roads-review{padding:24px!important}}

@media(max-width:767px){.pos-panel.population-review{padding:24px!important}}

@media(max-width:767px){.pos-panel.insights-review{padding:24px!important}}

@media(max-width:767px){.pos-panel.earth-review{padding:24px!important}}

@media (max-width: 1023.98px) {
  .pos-panel.research-review {padding:24px!important;}
  .research-topic {padding:16px;}
}

@media (max-width:1023.98px){.pos-panel.aggregate-review{padding:16px;gap:16px}.aggregate-review .aggregate-map{height:320px}.aggregate-review .aggregate-notice{padding:14px}.aggregate-review .aggregate-metrics{grid-template-columns:minmax(0,1fr)}.aggregate-actions>*{flex:1 1 170px}}

@media(max-width:1023.98px){
 .comps-wrap .comps-catchment{padding:14px}
 .comps-wrap .catchment-fields{grid-template-columns:repeat(auto-fit,minmax(min(100%,240px),1fr));gap:12px}
 .comps-wrap .catchment-actions{flex-direction:column}
 .comps-wrap .catchment-actions button{width:100%}
 .comps-wrap .catchment-map{height:300px}
}
