/* ============================================================
   Reaction ERP — every screen it might be opened on.
   ============================================================
   A site engineer photographs a pour on a phone, a project manager reads the
   critical path on a laptop, and the chairman opens the portfolio on
   whatever is nearest. The same application has to work on all three, and
   "works" means the buttons are reachable with a thumb and no two things ever
   sit on top of each other.

   Four sizes, and the reason each one exists:

     ≤ 1180px  the menu gives up its labels — the content column matters more
     ≤ 900px   the menu leaves the page entirely and becomes a drawer
     ≤ 680px   one column everywhere; rows of controls become stacks
     ≤ 420px   the smallest phone still in use; nothing may overflow it

   Written last in the cascade so it can correct anything above it without
   fighting specificity, and grouped by what it fixes rather than by selector.
   ============================================================ */

/* ============================================================
   ≤ 1180 — the menu stops spelling itself out
   ============================================================ */
@media (max-width: 1180px) {
  .sidebar { width: 66px; }
  .sidebar .sb-org { justify-content: center; }
  .sidebar .sb-org .txt,
  .sidebar .sb-group .txt,
  .sidebar .sb-item .txt,
  .sidebar .sb-user .info { display: none; }
  .sidebar .sb-group { padding: 11px 4px 3px; text-align: center; }
  .sidebar .sb-group::after { content: "·"; }
  .sidebar .sb-item { justify-content: center; padding: 10px 0; }
  .sidebar .sb-item.on::before { left: -6px; }
  .sidebar .sb-user { flex-direction: column; gap: 8px; }
  .view { padding: 16px 16px; --view-pad-top: 16px; }
}

/* ============================================================
   ≤ 900 — the menu becomes a drawer
   ------------------------------------------------------------
   Below this width a permanent menu is not a menu, it is a wall: it takes a
   fifth of the screen to show icons nobody can read. So it slides out over
   the content when it is asked for, and gets out of the way the moment a
   route is chosen.
   ============================================================ */
@media (max-width: 900px) {
  .sidebar {
    /* Below the top strip, not under it: the strip is above the drawer in
       the stacking order, so a drawer starting at the top of the screen had
       its own header — the logo and the practice's name — hidden beneath it
       (owner, 2026-09-16: "the menu panel must be seen; it may start from the
       lower edge of the top strip on a small screen"). */
    position: fixed; inset: 50px auto 0 0; z-index: 60;
    width: 236px; max-width: 84vw;
    transform: translateX(-100%);
    transition: transform .2s ease;
    box-shadow: 8px 0 28px rgba(10, 20, 32, .28);
  }
  /* Inside the drawer the labels come back — there is room for them now */
  .sidebar .sb-org { justify-content: flex-start; }
  .sidebar .sb-org .txt,
  .sidebar .sb-group .txt,
  .sidebar .sb-item .txt,
  .sidebar .sb-user .info { display: block; }
  .sidebar .sb-group { padding: 14px 9px 5px; text-align: left; }
  .sidebar .sb-group::after { content: none; }
  .sidebar .sb-item { justify-content: flex-start; padding: 11px 12px; }
  .sidebar .sb-user { flex-direction: row; gap: 10px; }

  #app-root.nav-open .sidebar { transform: translateX(0); }
  #app-root.nav-open .nav-scrim { opacity: 1; pointer-events: auto; }

  .nav-scrim {
    position: fixed; inset: 50px 0 0 0; z-index: 55;
    background: rgba(12, 20, 30, .5); backdrop-filter: blur(2px);
    opacity: 0; pointer-events: none; transition: opacity .2s ease;
    border: none; padding: 0; margin: 0;
  }

  /* The topbar carries the way back to the menu */
  .topbar { padding: 0 12px; gap: 8px; }
  .topbar .title { font-size: 14px; }
  .topbar .btn-refresh span,
  .topbar .btn-refresh { font-size: 0; padding: 7px 9px; }
  .topbar .btn-refresh svg { width: 16px; height: 16px; }
  /* The search takes the room the title gave up, and everything to the right
     of it keeps its size — a clipped language switch is worse than a short
     search box. */
  .gsearch { flex: 1 1 auto; min-width: 0; }
  .gsearch input { width: 100%; min-width: 0; }
  .tb-switches, .bell, .topbar .btn-refresh, #tb-collapse { flex: none; }
}
/* The scrim only exists on small screens */
@media (min-width: 901px) { .nav-scrim { display: none; } }

/* ============================================================
   ≤ 680 — one column, and every row of controls becomes a stack
   ============================================================ */
@media (max-width: 680px) {
  .view { padding: 12px 12px 26px; --view-pad-top: 12px; }
  .section { padding: 13px 13px; border-radius: 12px; }
  .section > h2 { font-size: 14px; flex-wrap: wrap; gap: 7px; }
  .section > h2 .muted { flex-basis: 100%; }

  /* Cards: one per row, and big enough to read at arm's length */
  .cards, .cards.tight, .cards.evm-cards { grid-template-columns: 1fr 1fr; gap: 9px; }
  .cards .card { padding: 12px; }
  .card .big, .cards.tight .card .big { font-size: 19px; }
  .evm-card .big { font-size: 17px; }

  /* Tabs scroll sideways instead of stacking into five rows */
  .tabs {
    flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden;
    scrollbar-width: none; -webkit-overflow-scrolling: touch;
    margin: 0 -12px 14px; padding: 0 12px;
  }
  .tabs::-webkit-scrollbar { display: none; }
  .tab { flex: none; padding: 10px 12px; font-size: 12.5px; }

  /* The second tier scrolls the same way. Both rows bleed to the edge of the
     screen so a half-cut chip shows there is more to swipe to — a row that
     ends flush at the margin reads as the whole list. */
  .tabs.grouped { margin-bottom: 0; }
  .subtabs { margin: 9px 0 14px; }
  .subtab-row {
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    -webkit-overflow-scrolling: touch; margin: 0 -12px; padding: 2px 12px;
  }
  .subtab-row::-webkit-scrollbar { display: none; }
  .subtab { flex: none; min-height: 34px; }

  /* The header of a record */
  .detail-head { flex-wrap: wrap; gap: 10px; }
  .detail-head .detail-title h1 { font-size: 19px; }
  .detail-head .detail-actions { width: 100%; }
  .detail-head .detail-actions .btn { flex: 1; justify-content: center; }

  /* Anything that was a row of buttons beside text */
  .a-head { flex-wrap: wrap; }
  .a-actions { width: 100%; }
  .a-actions .btn { flex: 1 1 46%; justify-content: center; }

  .toolbar, .cf-range, .gt-tools, .gt-actions { flex-wrap: wrap; gap: 8px; }
  .gt-actions { width: 100%; }
  .gt-actions .btn { flex: 1 1 46%; justify-content: center; }
  .gt-actions .muted { flex: 1 1 100%; }
  .cf-dates { flex-wrap: wrap; }
  /* The programme's From/to share one row: our own date box is width:100%
     (theme.css .nui-date), so on a phone each date took a row of its own
     with its label on a third and fourth — the chart began 1.5 screens
     down (measured 2026-10-14, tools/uicheck/ganttTidy.mjs). The cash-flow
     page keeps its own layout, hence the .sched scope. */
  .sched .cf-dates { flex-wrap: nowrap; width: 100%; }
  .sched .cf-dates .nui-date { flex: 1 1 0; width: auto; }
  .sched .cf-dates .nui-date .nui-date-text { width: auto; }
  .seg { flex-wrap: wrap; }

  /* Tables stay tables — they scroll, and they never squash a number */
  :root { --tbl-head-pad: 8px 10px; --tbl-cell-pad: 8px 10px; --tbl-fs: 12.5px; }

  /* A modal on a phone is a sheet from the bottom, not a floating box */
  .modal-backdrop { align-items: flex-end; padding: 0; }
  .modal, .modal.wide {
    width: 100%; max-width: 100%; max-height: 92vh;
    border-radius: 16px 16px 0 0; margin: 0;
  }
  .modal-foot { flex-wrap: wrap; }
  .modal-foot .btn { flex: 1 1 40%; justify-content: center; }

  /* The pinned name column on the Gantt gives up half its width */
  .gt-wrap { --label-w: 150px !important; }
  .gt-label { font-size: 11px; padding: 0 6px; }
  .gt-who { display: none; }

  .chart-legend { gap: 10px; font-size: 11px; }
  .detail-grid { grid-template-columns: 1fr; }
  .hero { flex-wrap: wrap; gap: 12px; }
  .hero-stats { width: 100%; justify-content: space-between; }
  .mini-stats { grid-template-columns: 1fr 1fr; }
  .form-grid { grid-template-columns: 1fr; }
  .form-grid .span2 { grid-column: span 1; }

  /* Touch targets: 40px is the smallest a thumb hits reliably */
  .btn, .icon-btn, .sb-item, .tab { min-height: 38px; }
  .btn.small, .btn.sm { min-height: 34px; }
  input.input, select.input, textarea.input { font-size: 16px; }   /* iOS: no zoom-on-focus */
}

/* ============================================================
   ≤ 420 — the smallest phone still in use
   ============================================================ */
@media (max-width: 420px) {
  .cards, .cards.tight, .cards.evm-cards, .mini-stats { grid-template-columns: 1fr; }
  .topbar .title { display: none; }
  .tb-switches .lang-seg { display: none; }
  /* the drawing's own language pair is the only one it has — the More menu's
     copy was removed as a duplicate — so it stays on the bar even here */
  .tk-top .tk-lang button { padding: 0 8px; }
  .a-actions .btn { flex: 1 1 100%; }
  /* The programme's six actions stay two to a row even here — six full-width
     buttons put the chart itself below the first screen and a half
     (measured 2026-10-14); the ≤680 rule's 46 % holds. Its two segmented
     controls scroll sideways in one row instead of wrapping into two, the
     way the sub-tab row does. */
  .sched .seg { flex-wrap: nowrap; overflow-x: auto; max-width: 100%; scrollbar-width: none; }
  .sched .seg::-webkit-scrollbar { display: none; }
  .sched .seg button { flex: none; white-space: nowrap; padding: 6px 10px; font-size: 12px; }
  .gt-chip { padding: 4px 8px; }
  /* Cancel and Save stay side by side even here: stacking them costs a row of
     screen on the smallest device, which is where rows cost most. */
  .modal-foot .btn { flex: 1 1 42%; }
  .detail-head .detail-avatar { width: 42px; height: 42px; }
  .gt-wrap { --label-w: 124px !important; }
  /* On the narrowest screen the group's explanation is a paragraph in the way
     of the page. The tab it hangs under still carries it as a tooltip. */
  .tab-note { display: none; }
}

/* ============================================================
   Pointer-coarse: phones and tablets, whatever their width
   ------------------------------------------------------------
   A tooltip that appears on hover is invisible on a touch screen, and a
   hover state that sticks after a tap looks like a selection. Both go.
   ============================================================ */
@media (hover: none) {
  .table.data tbody tr:hover, table.table tbody tr:hover { background: inherit; }
  .btn:hover, .icon-btn:hover { transform: none; }
}

/* ============================================================
   Short windows — a laptop with the browser half-height
   ============================================================ */
@media (max-height: 560px) {
  .modal { max-height: 94vh; }
  .aon-scroll { max-height: 52vh; }
}

/* ============================================================
   ≤ 680 — a table on a phone is a list of cards
   ------------------------------------------------------------
   A twelve-column register scrolled sideways on a 390px screen is a table
   nobody reads: the identifying column disappears the moment you scroll to
   the column you wanted. So below this width each row becomes a card, the
   first column becomes its heading, and every other cell wears the name of
   its column. Same DOM, same sorting, same search — only the shape changes.
   ============================================================ */
@media (max-width: 680px) {
  .table-wrap, .table-scroll { overflow-x: visible; border: none; background: none; box-shadow: none; }

  /* `.table-scroll table.data` carries min-width 520px for the scrolling
     layout; it outranks the bare selector, so the card layout has to repeat
     itself at the same strength or every card is 520px wide with its values
     pushed off the right edge of the screen. */
  table.data, .table-scroll table.data, .table-wrap table.data { min-width: 0; }
  table.data { display: block; background: none; }
  table.data thead { display: none; }
  table.data tbody { display: flex; flex-direction: column; gap: 9px; }

  table.data tbody tr {
    display: block; background: var(--surface);
    border: 1px solid var(--border); border-radius: 12px;
    padding: 11px 12px; box-shadow: var(--shadow-sm);
  }
  table.data tbody tr:hover { background: var(--surface); }

  table.data tbody td {
    display: flex; gap: 10px; align-items: baseline; justify-content: space-between;
    border: none; padding: 3px 0; max-width: none; white-space: normal;
    font-size: 12.5px; text-align: right;
  }
  table.data tbody td::before {
    content: attr(data-label);
    flex: none; color: var(--text-dim); font-size: 11px; font-weight: 700;
    text-transform: uppercase; letter-spacing: .04em; text-align: left;
  }
  /* An empty cell earns no line at all */
  table.data tbody td:empty { display: none; }

  /* The first column is the record's name, so it reads as the card's title */
  table.data tbody td.cell-primary {
    display: block; text-align: left; font-size: 14.5px; font-weight: 700;
    color: var(--heading); padding: 0 0 6px; margin-bottom: 5px;
    border-bottom: 1px solid var(--border);
  }
  table.data tbody td.cell-primary::before { content: none; }

  table.data tbody td.cell-actions {
    justify-content: flex-end; gap: 6px; padding-top: 9px; margin-top: 6px;
    border-top: 1px solid var(--border);
  }
  table.data tbody td.cell-actions::before { margin-right: auto; }
  table.data tbody td.cell-actions .icon-btn { width: 36px; height: 36px; }

  table.data tbody tr.empty-row { text-align: center; }
  table.data tbody tr.empty-row td { display: block; text-align: center; }
  table.data tbody tr.empty-row td::before { content: none; }

  /* The total row is a card of its own under the others, its figures named
     the way the rows' are (round 8b: a roll-up's foot on a phone). */
  table.data tfoot { display: block; margin-top: 9px; }
  table.data tfoot tr {
    display: block; background: var(--surface-2);
    border: 1px solid var(--border-strong); border-radius: 12px; padding: 11px 12px;
  }
  table.data tfoot td {
    display: flex; gap: 10px; align-items: baseline; justify-content: space-between;
    border: none; box-shadow: none; padding: 3px 0; background: none; white-space: normal; text-align: right;
  }
  table.data tfoot td::before {
    content: attr(data-label);
    flex: none; color: var(--text-dim); font-size: 11px; font-weight: 700;
    text-transform: uppercase; letter-spacing: .04em; text-align: left;
  }
  table.data tfoot td:empty { display: none; }
  table.data tfoot td.cell-primary { display: block; text-align: left; padding: 0 0 6px; margin-bottom: 5px;
    border-bottom: 1px solid var(--border); position: static; }
  table.data tfoot td.cell-primary::before { content: none; }
  table.data tbody td.wrap { max-width: none; }

  /* The report tables (compact, totals at the foot) keep their shape but get
     out of the way of the thumb: they scroll inside their own box. */
  .table-scroll { overflow-x: auto; }
  table.table { min-width: 560px; }
}

/* ============================================================
   Reachable without a mouse, and calm for people who ask for calm
   ============================================================ */

/* Everything that can be clicked shows where the keyboard is. :focus-visible
   rather than :focus, so a mouse click does not leave a ring behind it.
   No border-radius here: it used to be 6px, and every text field (which
   matches :focus-visible even on a click) snapped from 10px corners to 6px
   and wore two rings at once. A field already draws its own focus ring. */
:focus-visible {
  outline: 2px solid var(--c-main);
  outline-offset: 2px;
}
.input:focus-visible, .rte-body:focus-visible { outline: none; }
tr.clickable:focus-visible,
.gt-label:focus-visible,
.wx-day:focus-visible { outline-offset: -2px; }

/* A row that can be opened says so on hover as well as on focus */
table.table tbody tr[role="link"] { cursor: pointer; }

/* Icon buttons stay hittable: 24px is the smallest square WCAG 2.2 accepts,
   and a thumb wants more than the minimum. */
.icon-btn { min-width: 28px; min-height: 28px; }
@media (max-width: 680px) { .icon-btn { min-width: 38px; } }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ============================================================
   Real devices — iPhone, iPad, Android, and older Safari
   ------------------------------------------------------------
   Everything above reasons about widths. These are the things a width does
   not tell you: how much of the screen is really visible, whether the
   pointer is a finger, and what an older Safari cannot draw.
   ============================================================ */

/* 100vh on a phone is the height with the address bar HIDDEN, so the last
   rows of every page sat under Safari's toolbar and the page scrolled twice.
   dvh is the height actually on screen. */
@supports (height: 100dvh) {
  #app-root { height: 100dvh; }
}

/* The modal layer follows the visible area (shell.js writes --vvh/--vvt from
   visualViewport): with the iOS keyboard up, the sheet ends above the
   keyboard instead of behind it, and its Save button stays in reach. The vh
   fallback is what browsers without visualViewport keep. */
#modal-root { top: var(--vvt, 0px); bottom: auto; height: var(--vvh, 100%); }
.modal, .modal.wide { max-height: calc(var(--vvh, 100vh) * .92); }
@media (max-height: 560px) { .modal, .modal.wide { max-height: calc(var(--vvh, 100vh) * .94); } }

/* A finger, whatever the width of the screen it is on */
@media (pointer: coarse) {
  /* iOS zooms the whole page into any field under 16px and leaves it zoomed.
     The ≤680px rule missed landscape phones (844–932px wide) and lost to
     every more specific small-field rule; this one is keyed to the finger
     and outranks them. */
  .input, textarea.input, select.input, .input.tiny, .cf-dates .input, .fee-calc .input,
  .dsr-lines input.sm, .dsr-lines select.sm, .date-range .input[type="date"],
  .gsearch input, .rte-body,
  /* the board composer's painted layer must grow with the box it mirrors,
     or the two wrap at different widths and the caret leaves the letters */
  .pb-ta-mirror { font-size: 16px !important; }
  .input.tiny { height: auto; min-height: 36px; }

  /* 40px is what a thumb hits reliably; tablets kept the mouse sizes */
  .icon-btn { min-width: 40px; min-height: 40px; }
  .btn.small, .btn.sm { min-height: 36px; }
  .lang-seg button { min-height: 34px; padding-inline: 11px; }
  .sb-user .logout { width: 40px; height: 40px; }
  .auth-vis-x { min-width: 32px; min-height: 32px; }
  .am-step, .am-shield { width: 40px; height: 36px; }
  .cal-more { padding: 6px 7px; }
  .th-grip { width: 18px; right: -9px; }

  /* iOS draws an empty date field shorter than a text field and centres the
     date inside it */
  input[type="date"].input { min-height: 40px; -webkit-appearance: none; appearance: none; }
}
input[type="date"]::-webkit-date-and-time-value { text-align: left; }

/* iPad landscape and small laptops: the icon rail opens into the full menu on
   the menu button, instead of leaving the names to a tooltip that never shows
   on a touch screen.

   It used to slide over the page as a drawer and shut itself the moment a page
   was chosen. Since 2026-09-16 the choice sticks (owner: "if it is expanded
   keep it expanded"), and a panel that stays has to be part of the page rather
   than a sheet over it — so here the open menu is a column like it is on a
   wide screen, the content moves aside for it, and there is no scrim to dim
   what the person is reading. */
@media (min-width: 901px) and (max-width: 1180px) {
  #app-root.nav-open .sidebar { width: 236px; }
  #app-root.nav-open .sidebar .sb-org { justify-content: flex-start; }
  #app-root.nav-open .sidebar .sb-org .txt,
  #app-root.nav-open .sidebar .sb-group .txt,
  #app-root.nav-open .sidebar .sb-item .txt,
  #app-root.nav-open .sidebar .sb-user .info { display: block; }
  #app-root.nav-open .sidebar .sb-group { padding: 14px 9px 5px; text-align: left; }
  #app-root.nav-open .sidebar .sb-group::after { content: none; }
  #app-root.nav-open .sidebar .sb-item { justify-content: flex-start; padding: 11px 12px; }
  #app-root.nav-open .sidebar .sb-user { flex-direction: row; gap: 10px; }
  #app-root.nav-open .nav-scrim { display: none; }
}

/* The topbar title gives way to the search box earlier than it used to: at
   430px (the big iPhones) the search was left 40–70px wide. */
/* Measured on a 390px and a 430px phone the search box was still 70–76px
   wide. On a phone two things give way to it: the Refresh button (data now
   keeps itself up to date — js/live.js pulls only what changed — so the
   button is a spare), and the two-button language switch, which becomes one
   button showing the other language's name (shell.js explains why). */
@media (max-width: 560px) {
  .topbar .title { display: none; }
  .topbar .btn-refresh { display: none; }
  .tb-switches .lang-seg { display: none; }
}
.tb-switches .lang-one { display: none; font-size: 12px; font-weight: 700; }
@media (max-width: 560px) { .tb-switches .lang-one { display: inline-flex; } }

/* Safari before 16.2 drops a whole declaration that uses color-mix(). Most
   are decoration; these carry meaning (a reached rung, the chosen level, the
   first-visit hint), so they get a plain colour to fall back on. */
@supports not (color: color-mix(in srgb, red 50%, blue)) {
  .am-step.reached { background: rgba(0, 112, 192, .12); border-color: rgba(0, 112, 192, .35); }
  .rb-row.on { background: rgba(0, 112, 192, .12); }
  .first-tip { background: rgba(246, 192, 42, .18); border: 1px solid rgba(246, 192, 42, .55); }
  .form-file-strip { background: rgba(0, 112, 192, .05); }
  .form-file-strip.drag-over { background: rgba(0, 112, 192, .07); }
}

/* Safari up to 17 needs the prefixed spellings */
.topbar { -webkit-backdrop-filter: blur(8px); }
.modal-backdrop { -webkit-backdrop-filter: blur(3px); }
.nav-scrim { -webkit-backdrop-filter: blur(2px); }
table.data thead th, .cat-stage-head { -webkit-user-select: none; }
