﻿/* ============================================================================
   shell.css — new chrome for the shell redesign.

   Load order: after tokens.css (everything here reads from it) and after
   default.css, so these rules win against the legacy stylesheet without needing
   !important. New chrome only; nothing here restyles an existing component.

   Phase 1: the Ctrl/Cmd-K palette.
   Phase 2 will add the unified rail here.
   ========================================================================= */

/* --------------------------------------------------------------------------
   BOX MODEL — read this before adding anything below.

   legacy-theme.css:77 deliberately resets the WHOLE DOCUMENT to content-box,
   because this app's markup was written against that baseline and border-box
   silently shrank every fixed width carrying padding or a border. It opts
   specific things back into border-box: the `.bs5` islands and the BS5 modal
   chrome, listed element by element.

   New shell chrome is authored border-box, so it has to opt in the same way.
   Without this, Bootstrap's `.dropdown-item { width: 100%; padding: 0 12px }`
   resolved to 100% PLUS 24px under content-box, and the hover highlight hung
   12px past each edge of the account menu. Any BS5 component used outside a
   `.bs5` island has the same problem waiting for it.

   Scoped to the shell's own roots rather than reverting the global reset —
   the global content-box is a deliberate decision, not an oversight.
   -------------------------------------------------------------------------- */
.tl-topbar, .tl-topbar *, .tl-topbar *::before, .tl-topbar *::after,
.tl-palette, .tl-palette *, .tl-palette *::before, .tl-palette *::after {
    box-sizing: border-box;
}

/* --------------------------------------------------------------------------
   COMMAND PALETTE
   -------------------------------------------------------------------------- */

.tl-palette[hidden] { display: none; }

.tl-palette {
    position: fixed;
    inset: 0;
    z-index: var(--tl-z-overlay);
    display: flex;
    justify-content: center;
    /* Sits high rather than centred: the list grows downward, so a centred box
       would drift as results arrive. A fixed top keeps the input still. */
    align-items: flex-start;
    padding: 10vh 16px 16px;
}

/* Scrim exists to signal dismissal, not decoration. */
.tl-palette-scrim {
    position: absolute;
    inset: 0;
    background: rgba(20, 33, 42, .45);
}

.tl-palette-box {
    position: relative;
    width: 100%;
    max-width: 640px;
    max-height: 74vh;
    display: flex;
    flex-direction: column;
    background: var(--tl-surface);
    border: 1px solid var(--tl-border-strong);
    border-radius: 6px;
    box-shadow: var(--tl-shadow-lg);
    overflow: hidden;
    font-family: var(--tl-font);
}

.tl-palette-field {
    display: flex;
    align-items: center;
    gap: var(--tl-space-3);
    padding: var(--tl-space-4) var(--tl-space-5);
    border-bottom: 1px solid var(--tl-border);
    background: var(--tl-surface);
}

.tl-palette-input {
    flex: 1;
    min-width: 0;
    border: 0;
    outline: 0;
    background: transparent;
    color: var(--tl-text);
    /* 16px: the one place in this dense app that deliberately uses the large step.
       It is the primary text entry of the whole shell, and 16px also stops mobile
       browsers auto-zooming the viewport on focus. */
    font-family: var(--tl-font);
    font-size: var(--tl-text-lg);
    line-height: var(--tl-leading-normal);
}
.tl-palette-input::placeholder { color: var(--tl-text-subtle); }

/* SCOPE CHIP — "searching inside this project". Sits in the field before the
   caret so it reads as part of what you are typing into, not a control above
   it. A button, not a span: clearing the scope is an action, so it has to be
   focusable and operable from the keyboard, and it carries its own aria-label
   saying what activating it does. Backspace on an empty box does the same. */
.tl-palette-chip {
    flex: none;
    max-width: 40%;
    display: inline-flex;
    align-items: center;
    gap: var(--tl-space-2);
    margin: 0;
    padding: 3px var(--tl-space-3);
    border: 1px solid var(--tl-brand);
    border-radius: 999px;
    background: var(--tl-brand-wash);
    color: var(--tl-brand);
    font: inherit;
    font-size: var(--tl-text-xs);
    font-weight: var(--tl-weight-medium);
    line-height: 1.3;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
}
.tl-palette-chip[hidden] { display: none; }
/* The clear affordance is generated, so the chip's text stays the project name
   alone and the accessible name comes from aria-label rather than "Demo x". */
.tl-palette-chip::after {
    content: "×";
    font-size: var(--tl-text-sm);
    line-height: 1;
    opacity: .7;
}
.tl-palette-chip:hover { background: var(--tl-surface-hover); }
.tl-palette-chip:hover::after { opacity: 1; }
.tl-palette-chip:focus-visible {
    outline: 2px solid var(--tl-focus-ring);
    outline-offset: 1px;
}

.tl-palette-esc {
    flex: none;
    border: 1px solid var(--tl-border-strong);
    border-radius: 4px;
    background: var(--tl-surface-raised);
    color: var(--tl-text-muted);
    font-family: var(--tl-font-mono);
    font-size: var(--tl-text-2xs);
    letter-spacing: .06em;
    padding: 3px 7px;
    cursor: pointer;
}
.tl-palette-esc:hover { color: var(--tl-text); border-color: var(--tl-text-subtle); }

.tl-palette-list {
    overflow-y: auto;
    overscroll-behavior: contain;   /* stop the page behind scrolling once this hits its end */
    padding: var(--tl-space-2) 0;
}

.tl-palette-grouphead {
    padding: var(--tl-space-3) var(--tl-space-5) var(--tl-space-1);
    font-family: var(--tl-font-mono);
    font-size: var(--tl-text-2xs);
    font-weight: var(--tl-weight-medium);
    letter-spacing: var(--tl-tracking-label);
    text-transform: uppercase;
    color: var(--tl-text-subtle);
}

/* --------------------------------------------------------------------------
   THE HEADING AS A REALM FILTER.

   For the seven scopable realms the heading is a <button> that narrows the
   search to that realm — the pointer equivalent of Shift+Tab on a row. It has
   to keep looking like the headings it sits among (Recents and Projects stay
   plain <div>s) while still reading as operable, so the button chrome is
   stripped back to the div's own appearance and the affordance is carried by
   hover, focus and a generated caret.

   `display:block` + `width:100%` because a button is inline-block by default
   and would otherwise shrink to its text, leaving a heading that is only
   clickable across half the row it appears to occupy. `text-align:left` for
   the same reason: a button centres its label, a div does not.
   -------------------------------------------------------------------------- */
.tl-palette-grouphead-btn {
    display: block;
    width: 100%;
    text-align: left;
    border: 0;
    background: none;
    cursor: pointer;
    /* font: inherit would take the LIST's font, not this rule's — the shared
       properties above are set on .tl-palette-grouphead, which this element
       also carries, so only the button's own UA overrides need naming. */
    font-family: inherit;
    font-size: inherit;
    font-weight: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    color: inherit;
}
.tl-palette-grouphead-btn::after {
    content: " ⌄";
    opacity: .55;
}
.tl-palette-grouphead-btn:hover {
    color: var(--tl-accent-ink);       /* 4.6:1 — the raw accent is 2.44:1 */
    background: var(--tl-surface-hover);
}
.tl-palette-grouphead-btn:hover::after { opacity: 1; }
.tl-palette-grouphead-btn:focus-visible {
    outline: 2px solid var(--tl-focus-ring);
    outline-offset: -2px;              /* inset: the heading is full-bleed */
}

/* The realm chip is the project chip in the accent hue, so two chips side by
   side read as two DIFFERENT filters rather than one wrapped label. Everything
   else — the pill shape, the generated ×, the hover and focus treatment — is
   inherited from .tl-palette-chip, which it also carries. */
.tl-palette-chip-realm {
    border-color: var(--tl-accent-ink);
    background: var(--tl-accent-wash);
    color: var(--tl-accent-ink);
}

.tl-palette-row {
    display: flex;
    align-items: center;
    gap: var(--tl-space-3);
    padding: var(--tl-space-3) var(--tl-space-5);
    text-decoration: none;
    color: var(--tl-text);
    cursor: pointer;
    /* Inset bar rather than a background swap alone: state stays visible to anyone
       who cannot separate the selected fill from the surface behind it. */
    box-shadow: inset 3px 0 0 transparent;
}
.tl-palette-row:visited { color: var(--tl-text); }

/* One class for keyboard AND pointer selection. The mouse moves the same cursor
   the arrow keys do, so there is never a second, competing highlight. */
.tl-palette-row.is-active {
    background: var(--tl-brand-wash);
    box-shadow: inset 3px 0 0 var(--tl-brand);
}
.tl-palette-row:focus-visible {
    outline: 2px solid var(--tl-focus-ring);
    outline-offset: -2px;
}

/* 16 -> 20, tracking the markers' own 25% growth (build-realm-icons.py). These
   sizes CAP the intrinsic size, so without moving them the palette and the
   pills would have been the two places the change did not land. */
.tl-palette-marker {
    flex: none;
    width: 20px;
    height: 20px;
    border: 0;
}

.tl-palette-label {
    flex: 1;
    min-width: 0;
    font-size: var(--tl-text-md);
    /* Legal descriptions and party names are long and arrive from data; let them
       ellipsis rather than wrap a row to two lines and break the rhythm. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tl-palette-secondary {
    flex: none;
    font-family: var(--tl-font-mono);
    font-size: var(--tl-text-xs);
    font-variant-numeric: tabular-nums;
    color: var(--tl-text-muted);
}

.tl-palette-project {
    flex: none;
    max-width: 34%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--tl-text-xs);
    color: var(--tl-text-subtle);
}
/* Results from the project the user is already in are ranked first by the server;
   this is the visible reason why. */
.tl-palette-project.is-current {
    color: var(--tl-brand);
    font-weight: var(--tl-weight-medium);
}

.tl-palette-status {
    padding: var(--tl-space-2) var(--tl-space-5) var(--tl-space-3);
    border-top: 1px solid var(--tl-border);
    background: var(--tl-surface-raised);
    font-size: var(--tl-text-xs);
    color: var(--tl-text-muted);
}

/* Page must not scroll behind the open dialog. */
body.tl-palette-lock { overflow: hidden; }

@media (max-width: 640px) {
    .tl-palette { padding-top: 4vh; }
    .tl-palette-project { display: none; }   /* row width is the scarce thing here */
}

/* --------------------------------------------------------------------------
   TOP BAR
   Replaces the pipe-separated link corner in #title_extra. That cell is
   absolutely positioned top-right by default.css:#title_extra, which this
   layout inherits rather than fights -- these rules only lay out its contents.
   -------------------------------------------------------------------------- */

.tl-topbar {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--tl-space-2);
    font-family: var(--tl-font);
}

.tl-topbar-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--tl-space-2);
    height: 28px;
    padding: 0 var(--tl-space-3);
    border: 1px solid transparent;
    border-radius: var(--tl-radius, 4px);
    background: transparent;
    color: var(--tl-text-muted);
    font-family: inherit;
    font-size: var(--tl-text-sm);
    line-height: 1;
    text-decoration: none;
    cursor: pointer;
}
.tl-topbar-btn:hover,
.tl-topbar-btn:focus-visible {
    background: var(--tl-surface-raised);
    border-color: var(--tl-border);
    color: var(--tl-text);
}
/* Never remove the ring: this bar is the first thing a keyboard user tabs into. */
.tl-topbar-btn:focus-visible {
    outline: 2px solid var(--tl-focus-ring);
    outline-offset: 1px;
}
.tl-topbar-btn svg { width: 15px; height: 15px; fill: currentColor; flex: none; }

/* Search reads as a field rather than a button, because that is what it opens. */
.tl-topbar-search {
    border-color: var(--tl-border);
    background: var(--tl-surface);
    color: var(--tl-text-subtle);
    padding-right: var(--tl-space-2);
}
.tl-topbar-search:hover { color: var(--tl-text); }
.tl-topbar-searchlabel { padding-right: var(--tl-space-5); }

.tl-topbar-kbd {
    font-family: var(--tl-font-mono);
    font-size: var(--tl-text-2xs);
    color: var(--tl-text-subtle);
    background: var(--tl-surface-raised);
    border: 1px solid var(--tl-border);
    border-radius: 3px;
    padding: 2px 5px;
    line-height: 1;
    white-space: nowrap;
}

.tl-topbar-icon { position: relative; padding: 0 var(--tl-space-3); }

/* Count badge. Semantic danger, NOT a realm colour -- realm hues never carry
   status, or a Units badge starts reading as an alert. */
.tl-topbar-badge {
    position: absolute;
    top: -2px;
    right: 0;
    min-width: 15px;
    padding: 0 3px;
    border-radius: 8px;
    background: var(--tl-status-danger);
    color: #fff;
    font-family: var(--tl-font-mono);
    font-size: var(--tl-text-2xs);
    font-variant-numeric: tabular-nums;
    line-height: 15px;
    text-align: center;
}

.tl-topbar-avatar { padding-left: var(--tl-space-2); padding-right: var(--tl-space-2); }
.tl-topbar-initials {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--tl-brand);
    color: var(--tl-on-brand);
    font-size: var(--tl-text-2xs);
    font-weight: var(--tl-weight-semibold);
    letter-spacing: .02em;
}
.tl-topbar-caret { width: 9px !important; height: 9px !important; opacity: .7; }

.tl-topbar-menu {
    min-width: 208px;
    font-size: var(--tl-text-base);
    text-align: left;
    border-color: var(--tl-border-strong);
    box-shadow: var(--tl-shadow-md);
}
.tl-topbar-who {
    padding: var(--tl-space-3) var(--tl-space-4) var(--tl-space-2);
    line-height: 1.35;
}
.tl-topbar-whoname { display: block; font-weight: var(--tl-weight-semibold); color: var(--tl-text); }
.tl-topbar-whouser {
    display: block;
    font-family: var(--tl-font-mono);
    font-size: var(--tl-text-2xs);
    color: var(--tl-text-subtle);
}
.tl-topbar-menu .dropdown-item { padding: var(--tl-space-2) var(--tl-space-4); }

/* Destructive and impersonation actions are separated from ordinary navigation
   BY POSITION (below a divider) and reinforced by colour -- never colour alone.

   SPECIFICITY, and why these selectors are not just `.tl-topbar-danger`:
   default.css sets `a:link { color: #346689 }`, which is (0,1,1). A bare class is
   (0,1,0) and LOSES, so both of these rendered brand blue -- measured in-browser,
   not guessed. Loading shell.css last does not help: order only breaks ties at
   equal specificity. `.tl-topbar-menu a.x` is (0,2,1) and wins without
   !important. The same trap is documented on .tl-grid3 in bs5-custom.css.

   ORDER WITHIN THIS BLOCK IS ALSO LOAD-BEARING. The generic `a.dropdown-item`
   rule below is (0,2,1) — the SAME specificity as the danger/caution rules — so
   whichever comes last wins. It is written first on purpose. Putting it after
   flattened all three to --tl-text and silently removed the distinction this
   whole block exists to make. Measured, not assumed. */

/* Ordinary items: pinned because a:link would otherwise make the menu a stack of
   brand-blue links rather than a normal menu. MUST stay above the two below. */
.tl-topbar-menu a.dropdown-item { color: var(--tl-text); }
.tl-topbar-menu a.dropdown-item:hover,
.tl-topbar-menu a.dropdown-item:focus { background: var(--tl-surface-hover); color: var(--tl-text); }

.tl-topbar-menu a.tl-topbar-caution {
    color: var(--tl-status-warning-ink);
    font-weight: var(--tl-weight-medium);
}
.tl-topbar-menu a.tl-topbar-caution:hover,
.tl-topbar-menu a.tl-topbar-caution:focus {
    background: var(--tl-status-warning-wash);
    color: var(--tl-status-warning-ink);
}
.tl-topbar-menu a.tl-topbar-danger {
    color: var(--tl-status-danger-ink);
    font-weight: var(--tl-weight-medium);
}
.tl-topbar-menu a.tl-topbar-danger:hover,
.tl-topbar-menu a.tl-topbar-danger:focus {
    background: var(--tl-status-danger-wash);
    color: var(--tl-status-danger-ink);
}

/* cursor:pointer, NOT help: this stopped being a hover tooltip and became a button that opens the
   statistics modal. It stays muted and small on purpose -- it is a SystemOp developer tool sitting in
   a menu of ordinary account links, and it should not compete with them. */
.tl-topbar-stats { cursor: pointer; color: var(--tl-text-muted); font-size: var(--tl-text-xs); }

/* The phone-cell twin of the above (#title_extra_phone in _Layout). It was a bare <img class="help">
   carrying a tooltip; it is a <button> now so it is focusable and works without hover, which is the
   whole point on a phone. Stripped back to look exactly like the icon it replaced -- no button chrome,
   no padding, baseline-aligned with the username link beside it. */
.tl-phone-stats-btn {
    background: none;
    border: 0;
    padding: 0;
    line-height: 1;
    vertical-align: baseline;
    cursor: pointer;
}
.tl-phone-stats-btn img { display: inline-block; vertical-align: baseline; }

/* Screen-reader-only, for the icon-only controls. Every icon button in this bar
   carries one -- an icon with no accessible name is unusable by a screen reader
   and unlabelled in the a11y tree. */
.tl-visually-hidden {
    position: absolute !important;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* --------------------------------------------------------------------------
   THE TAB BAR — global navigation, horizontal again.

   The left rail was built, tried and rejected (user decision). Tabs return, but
   NOT the 86px band they used to live in: that band existed to show the logo as
   a CSS background image, and retiring it was the part of the redesign worth
   keeping. This bar is 44px — the height the header had already collapsed to —
   with the brand as a real <img> beside the tabs.

   default.css pins #navigation_wrapper and #navigation_main at 86px with that
   background logo and a 4px bottom border. All of it is unwound HERE rather
   than edited there, so the anonymous layout — which still has the old header
   and never had the new shell — is untouched.

   Not a revert to RADPI's strip: the legacy `ul#navigation` stays hidden. Its
   markup carries inline styles and onclick="window.location=..." on the <li>,
   so its rows are not real links. _TabBar renders the same gated model as clean
   anchors.
   -------------------------------------------------------------------------- */

.tl-tabbar, .tl-tabbar *, .tl-tabbar *::before, .tl-tabbar *::after {
    box-sizing: border-box;
}

/* Unwind the legacy band. height:auto rather than a fixed value — the bar
   inside sets the height, so there is one number to change, not two. */
body.tl-shell div#navigation_wrapper,
body.tl-shell div#navigation_main {
    height: auto;
    min-height: 0;
    background-image: none;
    border-bottom: 0 !important;   /* beats the inline activeTab accent borders */
}
/* h1#title is the background-image logo; #title_extra was the pipe-separated
   link corner, now the top bar. mobileLogo was the phone-only mark — the tab
   bar carries the brand at every width, so a second one is a duplicate.
   An earlier attempt re-centred #title_extra with `top:50%`; it is
   position:absolute with NO positioned ancestor, so that resolved against the
   initial containing block and put the bell in the middle of the VIEWPORT. */
body.tl-shell h1#title,
body.tl-shell div#title_extra,
body.tl-shell a.mobileLogo { display: none; }

.tl-tabbar {
    display: flex;
    align-items: stretch;
    gap: var(--tl-space-4);
    height: 44px;
    padding: 0 var(--tl-space-4);
    border-bottom: 1px solid var(--tl-border);
    background: var(--tl-surface);
    font-family: var(--tl-font);
}

/* Brand. A real <img> with an accessible name, not a background image — that
   swap is what let the band shrink from 86px in the first place. */
.tl-tabbar a.tl-tabbar-brand,
.tl-tabbar a.tl-tabbar-brand:visited {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: var(--tl-space-2);
    color: var(--tl-text);
    font-size: var(--tl-text-md);
    font-weight: var(--tl-weight-medium);
    text-decoration: none;
    white-space: nowrap;
}
.tl-tabbar-brand img { display: block; border: 0; }

.tl-tabs {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: stretch;
    gap: 2px;
    /* A SHOULDER ABOVE THE TABS, so they read as tabs sitting on the bar rather
       than blocks butted against the top of the window. With the 5px top-corner
       radius already there, this is what makes the shape legible.

       On .tl-tabs, NOT on .tl-tabbar: padding the bar would push the brand and
       the tools down too, and those should stay centred in the full height —
       only the tabs want to sit low. The tabs still reach the BOTTOM edge, so
       they meet the bar's bottom border the way a tab meets its panel.

       COSTS NO HEIGHT. .tl-tabbar is a fixed 44px and .tl-tabbar * is
       border-box, so this eats into the tabs' own 44px rather than adding to
       the bar: the tabs become 36px tall inside an unchanged bar. */
    padding-top: var(--tl-space-3);
    /* Seven tabs plus dividers do not fit a narrow laptop beside the tools.
       Scroll the tabs rather than wrap the bar to two rows or shrink the type:
       the bar's height is the thing being held constant. */
    overflow-x: auto;
    scrollbar-width: thin;
}

/* Specificity: default.css `a:link` is (0,1,1) and beats a bare class — the
   same trap as the top bar and the pills. `.tl-tabs a.tl-tab` is (0,2,1). */
/* THE ORIGINAL TAB PALETTE, on the new geometry (user request).
   default.css:755-846 coloured the legacy strip:

       li            #000 bold, no fill
       li:hover      #0397d5 cyan fill, white text
       li.active     #44555f slate fill, white text
       li.admin      #0397d5 cyan text
       li.admin:hover / .admin.active   #8CBA4A green fill, white text

   All three fills are already tokens with byte-identical values — --tl-slate,
   --tl-status-info, --tl-status-success — so the hue identity is exact rather
   than approximated.

   TWO OF THE THREE CHANGED THEIR TEXT COLOUR, and only that. Measured:

       white on slate  #44555f   7.75:1   keep
       white on cyan   #0397d5   3.29:1   fails AA -> dark text, 4.99:1
       white on green  #8cba4a   2.27:1   fails AA -> dark text, 7.21:1

   The fill is what makes a colour recognisable, so the fills are untouched and
   only the text flips where white was unreadable on it. Parity is suspended for
   chrome in this plan, and reproducing a 2.27:1 label would be reproducing a
   defect rather than a design. */
.tl-tabs a.tl-tab,
.tl-tabs a.tl-tab:visited {
    display: inline-flex;
    align-items: center;
    padding: 0 var(--tl-space-4);
    /* Rounded TOP corners only, echoing the legacy 5px — a filled box with all
       four corners rounded reads as a button, not a tab. */
    border-radius: 5px 5px 0 0;
    color: var(--tl-text);
    /* 12 -> 16 (user request): the tabs are the app's primary navigation and
       were set at the chrome's small step, which made them recede.

       THE BAR DOES NOT GROW. Its 44px is a fixed height on .tl-tabbar, and each
       tab is a flex item centred in it with line-height:1 — so the type box is
       the font size itself, 16px inside 44px, and nothing here pushes on the
       bar. That is the whole reason the size could move without a redesign.

       It costs HORIZONTAL room, not vertical: wider tabs reach the scroll
       threshold sooner on a narrow window. .tl-tabs already scrolls, so the
       failure mode is a scroll rather than a wrap or a clip. 16px was tried
       first and stepped back to 14 to buy that width back. */
    font-size: var(--tl-text-md);
    /* BOLD BY DEFAULT (user request). RADPI's tabs were bold too
       (default.css:801 `ul#navigation li a { font-weight: bold }`), so this is
       the legacy weight back rather than a new emphasis. */
    font-weight: var(--tl-weight-bold);
    line-height: 1;
    text-decoration: none;
    white-space: nowrap;
}
/* HOVER: white text, no underline (user request).
   The background moved with it, and deliberately. White on the legacy hover
   cyan #0397d5 measures 3.29:1 — under the 4.5:1 AA needs, and 14px bold is
   not "large text" (that starts at 18.66px bold), so the exemption does not
   apply. The request named the TEXT colour, not the fill, so the fill steps to
   the token's own darker shade of the same hue: white on --tl-status-info-ink
   #0f6a94 is 5.98:1. Same colour family, legible text.
   The underline went with the legacy hover; the fill change is signal enough. */
.tl-tabs a.tl-tab:hover {
    background: var(--tl-status-info-ink);
    color: var(--tl-on-slate);           /* white on #0f6a94: 5.98:1 */
    text-decoration: none;
}
/* Active carries THREE cues, not colour alone: the fill, the text colour and
   aria-current on the element itself. Weight is NOT one of them any more — every
   tab is bold now, so it cannot distinguish one. */
.tl-tabs a.tl-tab.is-active,
.tl-tabs a.tl-tab.is-active:visited,
.tl-tabs a.tl-tab.is-active:hover {
    background: var(--tl-slate);
    color: var(--tl-on-slate);           /* white on slate: 7.75:1 */
    /* No weight of its own any more. It used to be medium against a normal
       base; with the base now BOLD that would have rendered the current tab
       LIGHTER than its neighbours. Its cues are the slate fill, the white text
       and aria-current — still three, still not colour alone. */
    text-decoration: none;
}

/* Site Operations kept its own colour in the legacy strip (li.admin), and it is
   worth keeping: it is the one destination that is not customer-facing. */
.tl-tabs a.tl-tab-admin,
.tl-tabs a.tl-tab-admin:visited { color: var(--tl-status-info-ink); }
/* TL Admin follows the same white-text rule, so hover is uniform across the
   strip. Its green needs the same treatment as the cyan: white on
   --tl-status-success #8cba4a is 2.27:1, white on the -ink shade #5f7f30 is
   4.60:1. Deeper green, same family, legible. */
.tl-tabs a.tl-tab-admin:hover,
.tl-tabs a.tl-tab-admin.is-active,
.tl-tabs a.tl-tab-admin.is-active:visited,
.tl-tabs a.tl-tab-admin.is-active:hover {
    background: var(--tl-status-success-ink);
    color: var(--tl-on-slate);           /* white on #5f7f30: 4.60:1 */
}
.tl-tabs a.tl-tab:focus-visible {
    outline: 2px solid var(--tl-focus-ring);
    outline-offset: -2px;   /* inset: the bar clips at its own edge */
}

/* ShowDisabledTabs renders the section unavailable rather than hiding it. */
.tl-tabs .tl-tab.is-disabled {
    display: inline-flex;
    align-items: center;
    padding: 0 var(--tl-space-4);
    color: var(--tl-text-subtle);
    font-size: var(--tl-text-md);
    white-space: nowrap;
    cursor: default;
}

/* The rail's group headings become a hairline here. Work / Operations / Admin
   are not peers, and this keeps that true without spending a second row. */
.tl-tabs-divider {
    flex: none;
    align-self: center;
    width: 1px;
    height: 18px;
    margin: 0 var(--tl-space-2);
    background: var(--tl-border);
}

.tl-tabbar-tools {
    flex: none;
    display: flex;
    align-items: center;
    gap: var(--tl-space-3);
}

.tl-tabbar-search {
    display: inline-flex;
    align-items: center;
    gap: var(--tl-space-2);
    height: 28px;
    margin: 0;
    padding: 0 var(--tl-space-3);
    border: 1px solid var(--tl-border);
    border-radius: 999px;
    background: var(--tl-surface-sunken);
    color: var(--tl-text-muted);
    font: inherit;
    font-size: var(--tl-text-sm);
    line-height: 1;
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
}
.tl-tabbar-search:hover { border-color: var(--tl-text-subtle); color: var(--tl-text); }
.tl-tabbar-search:focus-visible { outline: 2px solid var(--tl-focus-ring); outline-offset: 1px; }
.tl-tabbar-search svg { width: 14px; height: 14px; fill: currentColor; flex: none; }

.tl-tabbar-kbd {
    border: 1px solid var(--tl-border-strong);
    border-radius: 3px;
    background: var(--tl-surface);
    color: var(--tl-text-subtle);
    font-family: var(--tl-font-mono);
    font-size: var(--tl-text-2xs);
    padding: 2px 5px;
    line-height: 1;
}

/* Narrow: the brand word and the search label go, the marks stay. The bar keeps
   its height at every width — that is the thing being held constant. */
@media (max-width: 1000px) {
    .tl-tabbar-brandname,
    .tl-tabbar-searchlabel,
    .tl-tabbar-kbd { display: none; }
}

/* --------------------------------------------------------------------------
   TOP ROW — breadcrumbs, entity search and the top bar on one line.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   WHICH AREA AM I IN — the active pill.

   This REPLACES the breadcrumb trail, which no longer renders on this layout.
   The trail answered "where am I" with a line of text in a band of its own;
   the pills answer it in space the band already occupies, by marking the one
   you are in. Which pill wins is decided in _RealmPills — longest path match,
   exact beating area, children counted so a menu pill lights up for the page
   inside it.

   THREE CUES, NOT COLOUR ALONE (`nav-state-active`, `color-not-only`): the
   fill, the border and the weight all change, and the element carries
   aria-current="page" so it is announced rather than merely seen.
   -------------------------------------------------------------------------- */

/* (0,3,1) — clears `.tl-pills a.tl-pill` (0,2,1) and the aria-expanded open
   state (0,3,0), so an open menu pill that is ALSO current still reads current. */
.tl-pills a.tl-pill.is-active,
.tl-pills a.tl-pill.is-active:visited,
.tl-pills button.tl-pill.is-active {
    background: var(--tl-brand-wash);
    border-color: var(--tl-brand);
    color: var(--tl-brand);
    font-weight: var(--tl-weight-medium);
}
.tl-pills a.tl-pill.is-active:hover,
.tl-pills button.tl-pill.is-active:hover {
    background: var(--tl-brand-wash);
    border-color: var(--tl-brand);
    color: var(--tl-brand);
}
/* The caret is one object with its split pill; it has to move with it or the
   pill reads as half-selected. */
.tl-pills a.tl-pill.is-active + button.tl-pill-caret {
    border-color: var(--tl-brand);
    color: var(--tl-brand);
    background: var(--tl-brand-wash);
}

/* The entry INSIDE an open menu, so the group says which one rather than only
   that you are somewhere in it.

   background-COLOR for the same reason as the hover rule further down: the
   shorthand would reset the item's legacy `background-image` icon to none. This
   one was worse than hover, because the current entry lost its icon for as long
   as the menu was open rather than only while the pointer was over it. */
.tl-pillmenu a.tl-pillmenu-item.is-active,
.tl-pillmenu a.tl-pillmenu-item.is-active:visited {
    background-color: var(--tl-brand-wash);
    color: var(--tl-brand);
    font-weight: var(--tl-weight-medium);
}
/* --------------------------------------------------------------------------
   REALM PILLS — the per-project collections, lifted out of the rail.

   One row of a fixed small set, directly under the top row. It is NOT a return
   to the four #navigation_sub blobs: those were four separate strips of mixed
   content, this is one strip of one kind of thing.
   -------------------------------------------------------------------------- */

.tl-pills, .tl-pills * { box-sizing: border-box; }

/* THE BAND is .tl-subnav, not .tl-pills. The pills and the entity search share this row, and the
   search is a layout SECTION — only a layout can render one — so _Layout owns the band and the
   pills partial is a flex child of it. Padding, border and background moved here from .tl-pills
   accordingly; putting them on both would double the border and inset the pills twice. */
.tl-subnav, .tl-subnav *, .tl-subnav *::before, .tl-subnav *::after { box-sizing: border-box; }

.tl-subnav {
    display: flex;
    align-items: center;
    /* WRAPS. Trail + eight realm pills + the entity picker does not fit one line at laptop
       widths. Wrapping costs a second line only on the pages that need it, where the old
       breadcrumb row cost one on every page. */
    flex-wrap: wrap;
    row-gap: var(--tl-space-2);
    gap: var(--tl-space-4);
    padding: var(--tl-space-3) var(--tl-space-4);
    border-bottom: 1px solid var(--tl-border);
    background: var(--tl-surface-sunken);
}

.tl-pills {
    flex: 1;
    min-width: 0;                    /* or a long pill row refuses to let the search sit beside it */
    display: flex;
    flex-wrap: wrap;                 /* wrap rather than scroll: a hidden pill is an unreachable one */
    align-items: center;
    gap: var(--tl-space-2);
}

/* Specificity: default.css `a:link` is (0,1,1) and beats a bare class — the same trap as the top
   bar, the rail items and the rail brand. `.tl-pills a.tl-pill` is (0,2,1) and wins. */
.tl-pills a.tl-pill,
.tl-pills a.tl-pill:visited {
    display: inline-flex;
    align-items: center;
    gap: var(--tl-space-2);
    padding: var(--tl-space-2) var(--tl-space-4);
    border: 1px solid var(--tl-border);
    /* 5px, THE SAME RADIUS AS THE MAIN TABS above (`.tl-tabs a.tl-tab`, rounded
       on their top corners only). A 999px capsule read as a different family of
       control from the bar it hangs under; matching the corner ties the two rows
       into one piece of chrome. Kept as a literal rather than a token because
       the tab rule states its own 5px the same way — one number, two places,
       and a token would hide that they are deliberately equal. */
    border-radius: 5px;
    background: var(--tl-surface);
    color: var(--tl-text);
    font-size: var(--tl-text-sm);
    line-height: 1.2;
    text-decoration: none;
    white-space: nowrap;
}
.tl-pills a.tl-pill:hover,
.tl-pills a.tl-pill:focus-visible {
    border-color: var(--tl-text-subtle);
    background: var(--tl-surface-hover);
    color: var(--tl-text);
}
.tl-pills a.tl-pill:focus-visible {
    outline: 2px solid var(--tl-focus-ring);
    outline-offset: 1px;
}
/* 14 -> 17.5, the same 25%. Fractional px is fine here: the marker is an SVG,
   so it scales cleanly rather than snapping to a device pixel. */
.tl-pills { --tl-pill-marker: 17.5px; }
.tl-pill-marker { width: var(--tl-pill-marker, 17.5px); height: var(--tl-pill-marker, 17.5px); flex: none; border: 0; }

/* EVERY PILL THE SAME HEIGHT (user request, 2026-09-29). A pill's height was whatever its content
   made it: the marker sets it (17.5px) where there is one, the 14.4px line where there is not, so a
   pill without a marker -- the blocked-mapping menu, a tool with no icon -- sat 3px shorter than
   its neighbours, centred in the row. The floor is the marker plus the pill's own padding and
   border (border-box, see the band rule), so a marked pill is unchanged and an unmarked one grows
   to it. Anchors and the menu-only buttons alike. */
.tl-pills .tl-pill {
    min-height: calc(var(--tl-pill-marker, 17.5px) + 2 * var(--tl-space-2) + 2px);
}

/* --------------------------------------------------------------------------
   THE PROJECT PILL — which project am I in?

   Removing the breadcrumb trail left the project-scoped pages with nothing
   naming the project: the pills say which AREA you are in, but not which
   project the area belongs to. The row's first pill was already the project's
   own — it linked to the project and sat ahead of the realms — so it now wears
   the project's NAME instead of the literal word "Project". Nothing was added
   to the row; one label changed.

   Which makes this the ONE pill whose width is data-dependent, and that is what
   the rules below exist for. Every other label is a fixed word, so the realms
   have always started at the same x; a free-width project name would move them
   on every project, and the row is the orientation cue precisely because it is
   in the same place each time. Fixed width, ellipsis inside, full name on the
   title attribute.

   Two in-header arrangements were built and measured before this one, and both
   failed on long names. A float into the coloured band looked right until the
   entity name grew: the title span carries padding-left:30px and a background
   icon, so it is ONE BOX and cannot flow around a float — it dropped to a
   second row and stranded the chip alone on the first. Making the heading a
   flex row fixed that but then the two competed for one row's width, costing
   an extra wrapped line or truncating the project to "Eagl...". Out here
   nothing competes.
   -------------------------------------------------------------------------- */
.tl-pills a.tl-pill-project,
.tl-pills a.tl-pill-project:visited {
    width: 190px;
    /* flex:none or the shared `.tl-pills` gap/wrap logic would let it shrink and
       the fixed width would stop being fixed. */
    flex: none;
    justify-content: flex-start;
    /* A name, not a section label — it gets the weight so it reads as the thing
       the row is scoped TO rather than another place to go. That spends one of
       the four signals `is-active` uses (weight, fill, border, colour) on this
       pill, leaving three; measured against the lit Tracts pill, the active
       project pill still reads unambiguously active on all three.

       NO border or background of its own, though. Those are the signals that
       carry the active state most strongly, and a permanently "stronger" border
       would put an inactive pill in competition with the lit one. */
    font-weight: var(--tl-weight-medium);
}
/* An EDITOR gets the same pill as a split, with a caret hung off it (Edit
   Project / Edit Brokers / Edit Project Forms), and the caret is a sibling
   inside the wrap rather than part of the anchor. Fixing only the anchor would
   therefore make the whole unit 190 + caret for an editor and 190 for everyone
   else — a shuffle by PERMISSION rather than by project, which is the same
   defect in a different coat. The caret's min-width is 26px, so the anchor
   gives that back and the wrap measures 190 either way. */
.tl-pills a.tl-pill-project.tl-pill-split { width: 164px; }

.tl-pill-project-name {
    /* min-width:0 is what allows the ellipsis: a flex item will not shrink below
       its content width without it, so the name would push past the fixed width
       instead of truncating. */
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* --------------------------------------------------------------------------
   PILL MENUS — Project's edit actions and the GIS map list.

   These were flyouts in the legacy #navigation_sub strip and sub-headings in
   the rail. Here they are a disclosure hung off the pill they belong to, so a
   project-scoped control is never on a different surface from the project's
   other controls.
   -------------------------------------------------------------------------- */

.tl-pillwrap { position: relative; display: inline-flex; }

/* The menu-only pill (GIS Maps) is a <button> wearing the pill's clothes. Scoped
   with the element to out-rank any legacy `button` rule, and every UA button
   default that would show through is reset explicitly. */
.tl-pills button.tl-pill {
    display: inline-flex;
    align-items: center;
    gap: var(--tl-space-2);
    margin: 0;
    padding: var(--tl-space-2) var(--tl-space-4);
    border: 1px solid var(--tl-border);
    border-radius: 5px;              /* matches the tabs — see the anchor rule above */
    background: var(--tl-surface);
    color: var(--tl-text);
    font: inherit;
    font-size: var(--tl-text-sm);
    line-height: 1.2;
    white-space: nowrap;
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
}
.tl-pills button.tl-pill:hover {
    border-color: var(--tl-text-subtle);
    background: var(--tl-surface-hover);
}
.tl-pills button.tl-pill:focus-visible,
.tl-pills button.tl-pill-caret:focus-visible {
    outline: 2px solid var(--tl-focus-ring);
    outline-offset: 1px;
}

/* SPLIT PILL: label navigates, caret discloses. The two are one visual object,
   so the facing corners flatten and the shared edge is drawn once. */
.tl-pills a.tl-pill-split { border-top-right-radius: 0; border-bottom-right-radius: 0; }

.tl-pills button.tl-pill-caret {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* WCAG 2.2 target-size (24x24 minimum) — the glyph is small, the target is not. */
    min-width: 26px;
    min-height: 26px;
    margin: 0;
    padding: 0 var(--tl-space-2);
    border: 1px solid var(--tl-border);
    border-left: 0;                  /* the anchor already drew this edge */
    border-radius: 0 5px 5px 0;
    background: var(--tl-surface);
    color: var(--tl-text-subtle);
    font-size: 10px;
    line-height: 1;
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
}
.tl-pills button.tl-pill-caret:hover { background: var(--tl-surface-hover); color: var(--tl-text); }

.tl-pill-chev { font-size: 10px; color: var(--tl-text-subtle); }

/* Open state reads on the trigger, not only inside the popover — otherwise the
   only cue that a menu belongs to this pill is proximity. */
.tl-pills [aria-expanded="true"].tl-pill,
.tl-pills [aria-expanded="true"].tl-pill-caret {
    background: var(--tl-surface-raised);
    border-color: var(--tl-border-strong);
    color: var(--tl-text);
}

/* Divider between the collections and the tools. A real gap plus a hairline:
   the row wraps, and two adjacent pills on a wrapped line would otherwise read
   as siblings when they are not. */
.tl-pills-divider {
    width: 1px;
    align-self: stretch;
    min-height: 20px;
    margin: 0 var(--tl-space-2);
    background: var(--tl-border-strong);
}

.tl-pillmenu {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    z-index: var(--tl-z-topbar);
    display: flex;
    flex-direction: column;
    min-width: 100%;
    max-width: 320px;
    padding: var(--tl-space-2);
    border: 1px solid var(--tl-border);
    border-radius: 8px;
    background: var(--tl-surface);
    box-shadow: var(--tl-shadow-lg);
}
/* An author `display:flex` beats the UA's [hidden] rule, so closing it needs
   saying explicitly — without this the popovers render open on every page. */
.tl-pillmenu[hidden] { display: none; }

/* (0,2,1) to clear default.css `a:link` (0,1,1) — the same trap as the top bar,
   the rail items and the pills themselves. */
.tl-pillmenu a.tl-pillmenu-item,
.tl-pillmenu a.tl-pillmenu-item:visited {
    display: block;
    padding: var(--tl-space-2) var(--tl-space-3);
    border-radius: 4px;
    color: var(--tl-text);
    font-size: var(--tl-text-sm);
    line-height: 1.3;
    text-decoration: none;
    white-space: normal;             /* map names are user data and can be long */
}
/* ⚠ background-COLOR, never the `background` shorthand. A menu item carries the
   legacy icon class the gating already set — `project-background`,
   `form-background`, `map-background-html` — and default.css paints those with
   the LONGHAND `background-image: url(…)` at (0,1,1). These rules are (0,3,1),
   so the shorthand won and reset background-image to `none`: hovering Edit
   Project, or any GIS map entry, blanked its icon and left the 26px of
   padding-left behind as a gap. Nothing about the fill changes; the shorthand
   was simply carrying a component it had no business setting.

   Anything added here that paints a menu item must use the longhand too. */
.tl-pillmenu a.tl-pillmenu-item:hover,
.tl-pillmenu a.tl-pillmenu-item:focus-visible {
    background-color: var(--tl-surface-hover);
    color: var(--tl-text);
}
.tl-pillmenu a.tl-pillmenu-item:focus-visible {
    outline: 2px solid var(--tl-focus-ring);
    outline-offset: -2px;            /* inset: the popover clips at its border */
}

/* NEW-TAB CUE on a menu entry that opens in a new tab (the Project pill's edit pages; see
   _RealmPills). The Notification Log's "Go to it" arrow (NotificationLog/_showMessage.cshtml),
   restated because notification-log.css is page-scoped. NOT under .tl-subnav/.tl-pills: on a
   phone the open menu is moved to <body> (Pills.js portalOut) and leaves both. Stroked in
   currentColor so it follows hover and the active colour; it paints no background, so the
   longhand warning above does not apply. */
.tl-pillmenu .tl-newtab-icon {
    display: inline-block;
    width: 12px;
    height: 12px;
    margin-left: var(--tl-space-2);
    vertical-align: -1px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.5;
    stroke-linecap: round;
    stroke-linejoin: round;
}
/* The containing block for the entry's visually-hidden "(opens in a new tab)": an absolutely
   positioned label escapes an unpositioned box and can widen the page on a phone. */
.tl-pillmenu a.tl-pillmenu-item { position: relative; }

/* The last pill's menu would otherwise open past the right edge on a narrow
   viewport. Anchoring the tools' menus to their right edge keeps them on screen
   without measuring anything in script. */
.tl-pillwrap:last-child .tl-pillmenu { left: auto; right: 0; }

/* Page-scoped tools (Resources, Print) in the top row. The old #page_save_options corner is empty
   on this path — its three occupants went to the palette, the top row and the rail. */
body.tl-shell #page_save_options { display: none; }

.tl-pagetools {
    flex: none;
    display: flex;
    align-items: center;
    gap: var(--tl-space-3);
    font-size: var(--tl-text-xs);
}
/* Specificity: default.css `a:link` is (0,1,1) and beats a bare class — same trap as everywhere
   else in this file. */
.tl-pagetools a,
.tl-pagetools a:visited { color: var(--tl-text-muted); text-decoration: none; }
.tl-pagetools a:hover { color: var(--tl-text); text-decoration: underline; }
.tl-pagetools a:focus-visible { outline: 2px solid var(--tl-focus-ring); outline-offset: 2px; }

/* The palette's recents rows are mixed-realm, so some may have no marker; keep the label aligned
   with the rows that do. */
.tl-palette-row > .tl-palette-label:first-child { margin-left: 22px; }

/* --------------------------------------------------------------------------
   #navigation_sub IS EMPTY ON THIS PATH — hide the container, not just its
   children.

   default.css gives it `height: 2em` and a bottom border, so with every child
   hidden it still drew a 2em empty band with a rule under it, directly above
   the top row. All three of its occupants have now moved:

     the three link strips -> the rail and the realm pills
     navigation history    -> the Ctrl/Cmd-K palette's empty state
     Resources / Print     -> _PageTools, in the top row

   Hiding the container also removes any doubt about the per-child `hidden`
   attributes: `[hidden]` is a UA rule that ANY author `display` declaration
   silently beats, and default.css floats two of those children. One author rule
   at higher specificity cannot be beaten the same way.
   -------------------------------------------------------------------------- */
body.tl-shell #navigation_sub { display: none !important; }

/* --------------------------------------------------------------------------
   THE GO-TO SEARCH IS VISIBLE AGAIN, in the sub-nav band (user request).

   #entitySearch renders _goToAll: a msDropdown entity picker whose <select>
   carries eight data-image realm icons, plus a per-type autocomplete box.

   It was hidden here because in the top row's flex layout it collapsed into a
   squashed strip of eight icons beside the account menu. That was a SYMPTOM OF
   THE SLOT, not of the control — it was competing with the breadcrumb trail and
   the account menu for one line. Given the right-hand end of the sub-nav band it
   has room, and it sits with the other per-project navigation instead of in the
   page's own row.

   It still overlaps the ⌘K palette in purpose, and the palette is the better
   tool: no type to pick first, not confined to the current project, present on
   every page rather than only those opting into the EntitySearch section. But
   the overlap is a reason to let people choose, not to hide one of them.
   -------------------------------------------------------------------------- */

.tl-subnav #entitySearch {
    flex: none;
    /* Hard right whatever the pills do, including when they wrap to two lines. */
    margin-left: auto;
    /* The legacy inline float is gone from the markup; defaultResponsive.css still
       floats .entitySearchContainer at some widths, and a float in a flex row is
       ignored for layout but still fights the padding. */
    float: none !important;
    padding: 0 !important;
}
/* The picker is a fixed-width control; keep it from stretching the band's height. */
.tl-subnav #entitySearch select,
.tl-subnav #entitySearch input { vertical-align: middle; }

/* THE SEARCH BOX IS NARROWER HERE THAN ITS PARTIAL DRAWS IT (user request, 2026-09-29): 180px, not
   300px, so the pills keep one row. At 300px the go-to took 445px of the band (the 140px type
   picker, 5px, the box) and the pills wrapped at 1440px wide whenever the tool segment was more
   than a pill or so: a project with several maps put "GIS Maps" alone on a second row. At 180px
   (325px in all) the realms plus GIS Maps fit from about 1360px wide. An administrator's
   single-map row carries "GIS Map (beta)" as well and needs about 1510px.
   !important because every go-to partial (_goToAll, _goToProject, _gotoProjectForm) sets
   `width:300px` INLINE, which no selector can outrank; one rule here instead of eleven edits keeps
   the choice with the rest of the band's layout. The full prompt stays in each box's title, and the
   results list is not tied to this width (jQuery UI sizes it to at least the box, wider if needed). */
.tl-subnav #entitySearch input[type="text"] { width: 180px !important; }

/* --------------------------------------------------------------------------
   PHONE (<= 800px) — the legacy responsive framework's breakpoint.

   defaultResponsive.css was written for the old header and reveals, at this
   width, a phone version of everything the tab bar now carries:

     a.mobileLogo / #navigation_main img.mobileLogo   a 200px brand
     div#title_extra_phone                            a second account corner
     .nav-phone  ->  a.nav-btn                        a drawer duplicating the tabs

   All three use `display: inherit !important`, so specificity alone cannot
   win — these need !important to answer them, which is the one place in this
   file that is true.

   THE TAB BAR IS THE PHONE NAV. It was built to survive here: the tabs scroll
   horizontally rather than wrapping, so the bar keeps its 44px at any width,
   and the tools group already drops its labels at 1000px. Keeping the drawer as
   well would mean two navigations, one of them hidden behind a button.

   NOT TOUCHED, because it is still the right pattern and column1 went back to
   the page: #column1-btn_Container and its .column1-btn, which reveal the
   entity nav on phone. That button carries .nav-phone but NOT .nav-btn, which
   is why the rule below can name .nav-btn precisely.
   -------------------------------------------------------------------------- */
@media (max-width: 800px) {
    body.tl-shell a.mobileLogo,
    body.tl-shell div#navigation_main div img.mobileLogo,
    body.tl-shell div#title_extra_phone,
    body.tl-shell a.nav-btn {
        display: none !important;
    }

    /* THE GO-TO SEARCH COMES OUT ON PHONE (user request). It is kept on the
       desktop deliberately — see the note above this media query: it overlaps
       the palette in purpose, and the overlap is a reason to let people choose.

       On a phone the choice is not free. The control is a 140px entity picker
       plus a 300px autocomplete box, and defaultResponsive.css floats
       .entitySearchContainer and forces it to 98% at this width, so inside the
       flex band it takes a full row of its own — roughly a third of the chrome
       above the page, to offer a search that is narrower than the one already
       in the bar: it makes you pick a type first and never leaves the current
       project.

       SAFE ONLY BECAUSE THE PALETTE IS STILL REACHABLE BY TAP. `.tl-tabbar-search`
       is a button, and the 1000px rule above drops its LABEL and its ⌘K hint but
       not the control — the magnifier stays in the bar at every width. If that
       ever changes, this rule has to go with it, or a phone user is left with no
       search at all. */
    body.tl-shell .tl-subnav #entitySearch { display: none !important; }

    /* …which can empty the band. It renders when there are pills OR a search, so
       a project page whose user has no realm access at all would otherwise be
       left with a padded strip containing nothing. `.tl-pills` is emitted only
       when the row is non-empty, so this is an exact test rather than a guess. */
    body.tl-shell .tl-subnav:not(:has(.tl-pills)) { display: none; }

    /* The band's own padding is generous for a phone; the pills carry their own. */
    .tl-subnav { padding: var(--tl-space-2) var(--tl-space-3); }
    .tl-tabbar { padding: 0 var(--tl-space-3); }

    /* THE PILLS SCROLL HERE INSTEAD OF WRAPPING.
       Wrapping is right on a desktop — a hidden pill is an unreachable one, and
       there is room for two rows. On a phone it scales badly in the one
       direction that matters: the row grows with the project. Eight realms plus
       the two GIS entries plus the Project caret wraps to SIX rows at 375px,
       about 250px of chrome before the page starts, and every pill added makes
       it worse.

       One scrolling row is a fixed ~40px however many pills there are, and
       nothing becomes unreachable — a horizontal strip that scrolls is the
       standard phone pattern, and it is what the tab bar above already does.
       This is not the `hidden pill` case that argued for wrapping: scrolled is
       not hidden.

       .tl-pillwrap must be flex:none or the pills compress to fit instead of
       overflowing, which is the failure that looks like nothing happened. */
    .tl-pills {
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: thin;
        -webkit-overflow-scrolling: touch;
    }
    .tl-pillwrap { flex: none; }

    /* ⚠ AN OPEN PILL MENU IS CLIPPED BY THE SCROLL ABOVE, and this rule does not
       fix it — it names the wrong element. The clipper is `.tl-pills`, which the
       `overflow-x: auto` two rules up just turned into a scroll container; making
       its PARENT visible changes nothing. And it clips vertically as well as
       horizontally: per the overflow spec, when one axis is not `visible` the
       other computes from `visible` to `auto`. The row is 40px tall, so the whole
       menu was outside it.

       Measured at 375px before the fix: all three menus reported opened=true with
       aria-expanded set, while document.elementFromPoint at each menu's own centre
       returned the page content BELOW the band. The GIS Maps menu was at x=569 —
       off-screen entirely, because its pill had scrolled out of view.

       FIXED IN Pills.js, not here, because it cannot be done in CSS without giving
       up the scroll: on open, a menu whose ancestors clip is switched to
       position:fixed and placed against the viewport. Kept as `overflow: visible`
       anyway — it is harmless and the band should not clip. */
    .tl-subnav { overflow: visible; }
}

/* --------------------------------------------------------------------------
   THE FLOATING PILL MENU — set by Pills.js, and only when an ancestor clips.

   Deliberately NOT inside the phone media query. The condition is "something
   above me scrolls", not "the viewport is narrow"; the script tests the computed
   overflow directly, so any future scroll container gets the same treatment and
   the desktop keeps its original absolute positioning untouched.

   position/left/top come from the script. Everything here is the part that must
   NOT be inline: the base rule's `min-width: 100%` would resolve against the
   VIEWPORT once the element is fixed and blow the menu up to full screen width,
   so the script pins a real min-width in pixels instead and this clears the
   percentage out of the way.
   -------------------------------------------------------------------------- */
.tl-pillmenu.is-floating {
    position: fixed;
    min-width: 0;
    /* Never wider than the screen it is being clamped into. */
    max-width: min(320px, calc(100vw - 16px));
}

/* ⚠ AND IT TAKES ITS BOX MODEL WITH IT. While open the menu is moved to <body> (see Pills.js), which
   puts it outside `.tl-subnav, .tl-subnav *`, the rule that was opting it into border-box — so it
   would silently fall back to the document-wide content-box from legacy-theme.css:77 and grow by its
   own padding and border, throwing off the pixel min-width and the viewport clamp the script
   computes. Restated here so the move cannot change the geometry. */
.tl-pillmenu.is-floating,
.tl-pillmenu.is-floating * {
    box-sizing: border-box;
}

/* --------------------------------------------------------------------------
   .icon CARRYING A REALM MARKER

   default.css's generic `.icon` pads 20px and paints the background at 2px,
   which was right for a 16px glyph: the icon ended at 18px and the text started
   at 20px. The realm markers are 20px wide now (build-realm-icons.py), so they
   end at 22px and ran UNDER the first character of the link — visible on the
   Desktop's two Recently Viewed lists.

   NOT fixed by bumping `.icon` itself. 179 call sites across the views use that
   class, and every one of them is still a 16px PNG; they would all gain 6px of
   dead space to fix two lists. The two JS-built lists that DO use a realm
   marker now add .icon-realm, and only when the marker actually is one.

   `!important` because the rule it answers is itself `!important`.
   -------------------------------------------------------------------------- */
a.icon-realm { padding-left: 26px !important; }

/* --------------------------------------------------------------------------
   AND THE MARKER MUST FIT INSIDE THE ANCHOR'S BOX.

   A background image is painted in the element's padding box and clipped by it.
   These anchors are inline, so their box is the line box — measured at exactly
   20px — while the marker is painted at `background-position: 2px 2px` and is
   itself 20px tall. It therefore needed 22px and lost its bottom 2px. Visible
   as every chip in the Desktop lists looking shaved along the bottom edge.

   Two changes, both needed:
     * a real box at least as tall as the marker (inline-block + min-height),
       because an inline box cannot be given a height at all;
     * vertical CENTRING instead of a 2px offset, so the marker cannot hang off
       the bottom however tall the box ends up. For the 16px PNG markers that
       share this naming, centring in a 20px box puts them at y=2 — exactly
       where they already were, so they do not move.

   NOT scoped to the realm markers: every a.*-background rule in default.css
   uses the same 2px offset, so they all clip the same way.

   Deliberately NOT overriding display where the page already sets it:
   `div#column1 a` is (1,0,1) and beats this (0,1,1), so the entity nav's block
   links stay block.
   -------------------------------------------------------------------------- */
a[class*="-background"],
a.icon-realm {
    display: inline-block;
    min-height: 20px;
    line-height: 20px;
    background-position: 2px center !important;
}

/* --------------------------------------------------------------------------
   RECENT — the "Recently Viewed" marker.

   The Desktop's "Recently Viewed Entities" heading had no marker of its own and
   borrowed abstract-background, so a list of mixed entities was wearing the
   Abstracts chip. It now has its own.

   Defined HERE rather than in default.css because it is new, not a port: there
   was no recent.png to repoint. Padding matches the sibling rules there after
   the 20px markers landed — 30px for the h2/h3 form at background-position 5px,
   26px for the anchor form at 2px.
   -------------------------------------------------------------------------- */
h2 span.recent-background,
h3 span.recent-background,
h4 span.recent-background {
    background-image: url(/site_images/icons/realm/recent.svg);
    background-repeat: no-repeat;
    background-position: 5px;
    padding-left: 30px !important;
}
a.recent-background,
span.recent-background {
    background-image: url(/site_images/icons/realm/recent.svg);
    background-repeat: no-repeat;
    background-position: 2px 2px !important;
    padding-left: 26px !important;
}

/* --------------------------------------------------------------------------
   MARKER HEADINGS NEED A LINE BOX AS TALL AS THE MARKER.

   Measured: an h3 span carrying a *-background marker is 18px tall with a
   line-height of 18.22px, while the marker painted behind it is now 20px. The
   icon therefore overflowed its own line box by ~2px top and bottom and
   collided with whatever sat above or below — visible on the Desktop's
   Recently Viewed headings.

   Applies to EVERY marker heading, not just that one: all eight realm markers
   and the four new ones grew together, so they all overflow the same way.

   [class*="-background"] rather than a list of twelve selectors, so a marker
   added later is covered without anyone remembering this rule. It also catches
   the handful of 16px-PNG markers that use the same naming (siteResources,
   map), which is harmless — a taller line box costs them nothing.
   -------------------------------------------------------------------------- */
h2 span[class*="-background"],
h3 span[class*="-background"],
h4 span[class*="-background"] {
    display: inline-block;
    min-height: 20px;
    line-height: 20px;
    /* CENTRED, AND !important, for the same reason the anchors are — a 20px
       marker at a 2px offset in a 20px box runs to 22 and loses its bottom 2px.
       The !important is not decoration here: the heading rules set 5px (which
       IS centred and would have been fine), but the sibling `span.*-background`
       rules set `2px 2px !important`, and those match a heading span too. An
       !important declaration beats specificity, so the more specific heading
       rule was losing to it — measured at 2px..22px inside a 20px span. Only
       the Y axis is forced, so each marker keeps its own horizontal inset. */
    background-position-y: center !important;

    /* AND A CONSISTENT HEIGHT. Measured across all twelve marker headings, they
       came out in two groups exactly 10px apart:

         H=30   contract, instrument, party, tract, units, wells
                  — their `span.*-background` rule sets `padding: 5px 0 5px 30px`
         H=20   abstract, contractParty, contractPartyPayment, project, recent,
                report — theirs sets `padding-left` only, with no vertical

       Nothing chose that split; it is a port artifact, six rules written one way
       and six the other. It shows up wherever marker headings sit side by side —
       reported on the Reports page, where the cards in a row started at
       different heights depending on which marker their header carried.

       Normalised to the taller form, which is the majority and the one RADPI's
       own spacing implies. !important because the six shorthand rules are
       themselves !important and would otherwise win. */
    padding-top: 5px !important;
    padding-bottom: 5px !important;
}

/* --------------------------------------------------------------------------
   ENTITY HEADER BAND — title on the left, "go to" picker on the right.

   The User and FormManagement layouts used to capture their goToUser/goToForm
   pickers into the EntitySearch section, which _Layout emits in the sub-nav
   band beside the realm pills. That reads well on a project-scoped page, where
   the picker ends a full row of pills. Those two layouts have no project
   context, so _RealmPills emitted nothing and the band became a full-width
   strip holding one lonely autocomplete. Moving the pickers into the entity
   header puts them next to the title and lets the empty band collapse.

   (`body.tl-has-rail #entitySearch { display: none }` above is DEAD — the rail
   was replaced by the horizontal tab bar and nothing sets tl-has-rail on <body>
   any more. It never suppressed these pickers.)

   flex-wrap so a long entity title — "Editing <contact>'s (<username>) User
   Information" plus the client link — drops the picker to its own line rather
   than squeezing it. align-items:center keeps the input on the H2's baseline
   band at one line.

   The trailing 15px belongs to the BAND, not to the H2 inside it. default.css
   gives h2 `margin: 0 0 15px`, and align-items:center centres each flex item's
   MARGIN box — so that 15px hung below the heading, pushed the heading's own
   text up, and left the picker sitting 7.5px lower than the title it is meant
   to line up with (measured, not reasoned about). Moving the space out to the
   wrapper and zeroing it on the heading aligns the two and keeps the gap below
   the header identical to what the H2 used to contribute. 15px is deliberately
   literal rather than a --tl-space token: no token is 15px, and the point is to
   reproduce the legacy heading margin exactly.
   -------------------------------------------------------------------------- */
.tl-entity-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--tl-space-3, 12px);
    margin-bottom: 15px;
}

/* Descendant, not child: the title slot holds a header partial in the detail
   layouts (_userHeader, _formHeader) and a <view-h2> tag helper on the list
   pages, so the heading is not always a direct child. */
.tl-entity-head-title h1,
.tl-entity-head-title h2,
.tl-entity-head-title h3 {
    margin-bottom: 0;
}

/* The title must take the slack, or space-between pushes a short H2 to the far
   left and strands the picker mid-row. min-width:0 lets it shrink instead of
   forcing the flex line wider than the column. */
.tl-entity-head-title {
    flex: 1 1 auto;
    min-width: 0;
}

.tl-entity-head-goto {
    flex: 0 0 auto;
}

/* The picker inputs carry an inline width:300px from their partials (ported
   verbatim from RADPI). Let them shrink on a narrow viewport rather than
   forcing the header to scroll. */
.tl-entity-head-goto input {
    max-width: 100%;
}

/* NO SIDEWAYS SCROLL AT 800px AND BELOW (2026-10-16). There,
   defaultResponsive.css gives every autocomplete `width: 90% !important`,
   which beats the pickers' inline 300px. In this header that 90% has nothing
   real to be 90% of: the box above is flex: 0 0 auto, so it shrink-wraps the
   input's default size (162px for the project picker), and the input is
   content-box, so its icon padding (30px from input.project-background in
   default.css, 25px for the user picker) went on top of the 90%. The input
   came out 18px wider than its box. Whenever the picker shares the title's
   row it sits flush with the right edge of the content column, so the page
   scrolled sideways by about a scrollbar's width: the Project Management
   pages, Map Builder and the User list, measured at 552 and 768px. The
   max-width above could not stop it: on a content-box input it caps the text
   area, not the padding beside it.

   border-box makes the width include the padding, and width: 100% fills the
   box exactly (it outranks the 90% by coming later at the same specificity,
   so it needs the same !important). flex-grow gives the box the row's spare
   width, so the placeholder still fits: 162px would cut "Search for
   Project's name." short. That makes it about 320px beside the title at
   768px, and a full row of its own on a phone, where the title already
   pushes it under. The desktop picker is untouched: the 90% rule, and so
   this answer to it, only applies at this width. */
@media (max-width: 800px) {
    .tl-entity-head-goto {
        flex-grow: 1;
    }

    .tl-entity-head-goto input {
        box-sizing: border-box;
        width: 100% !important;
    }
}

/* --------------------------------------------------------------------------
   THE PAGE-MENU TOGGLE (.column1-btn) — the phone control that reveals #column1.

   It used to be `<a class="btn btn-brown">` wrapping contextmenu.png: a raster
   glyph at one fixed size, on a legacy button whose brown gradient belongs to
   no palette this shell uses, and with nothing announcing what it did or
   whether the menu was open.

   Rebuilt from the same parts as the rest of the chrome — the tab bar's 5px
   corner, --tl-border, --tl-surface, the shared focus ring — so it reads as
   part of the shell rather than as something left over from the old theme.

   THE BARS ARE ONE ELEMENT. The span is the middle bar and its ::before and
   ::after are the outer two, so the markup stays a single empty aria-hidden
   span rather than three divs.

   THE OPEN STATE IS DRIVEN FROM aria-expanded, not from a JS-toggled class.
   That is deliberate: the attribute screen readers depend on is then the same
   one the animation depends on, so an accurate control and a correct-looking
   control cannot drift apart. If the bars ever stop turning into an X, the
   aria is wrong and the bug is worth having surfaced.
   -------------------------------------------------------------------------- */
.tl-navtoggle {
    display: inline-flex;
    align-items: center;
    gap: var(--tl-space-2);
    /* 36px: comfortably past the 24px WCAG 2.2 minimum target without matching
       the 44px tab bar, which would make a secondary control look primary. */
    min-height: 36px;
    padding: 0 var(--tl-space-3);
    border: 1px solid var(--tl-border);
    border-radius: 5px;              /* the tabs' and pills' corner */
    background: var(--tl-surface);
    color: var(--tl-text);
    font: inherit;
    font-size: var(--tl-text-sm);
    line-height: 1;
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
}
.tl-navtoggle:hover {
    background: var(--tl-surface-hover);
    border-color: var(--tl-text-subtle);
}
.tl-navtoggle:focus-visible {
    outline: 2px solid var(--tl-focus-ring);
    outline-offset: 1px;
}
/* Open reads like an active pill, because that is what it is: a thing currently
   showing something. Same three tokens the active pill uses. */
.tl-navtoggle[aria-expanded="true"] {
    background: var(--tl-brand-wash);
    border-color: var(--tl-brand);
    color: var(--tl-brand);
}

.tl-navtoggle-bars,
.tl-navtoggle-bars::before,
.tl-navtoggle-bars::after {
    display: block;
    width: 16px;
    height: 2px;
    border-radius: 1px;
    background: currentColor;
    transition: transform .18s ease, background-color .18s ease;
}
.tl-navtoggle-bars {
    position: relative;
    flex: none;
}
.tl-navtoggle-bars::before,
.tl-navtoggle-bars::after {
    content: "";
    position: absolute;
    left: 0;
}
.tl-navtoggle-bars::before { top: -5px; }
.tl-navtoggle-bars::after  { top: 5px; }

/* Middle bar goes transparent rather than display:none, so the two that remain
   can cross through the space it leaves. */
.tl-navtoggle[aria-expanded="true"] .tl-navtoggle-bars { background: transparent; }
.tl-navtoggle[aria-expanded="true"] .tl-navtoggle-bars::before { transform: translateY(5px) rotate(45deg); }
.tl-navtoggle[aria-expanded="true"] .tl-navtoggle-bars::after  { transform: translateY(-5px) rotate(-45deg); }

.tl-navtoggle-label { white-space: nowrap; }

@media (prefers-reduced-motion: reduce) {
    .tl-navtoggle-bars,
    .tl-navtoggle-bars::before,
    .tl-navtoggle-bars::after { transition: none; }
}

/* ── AND THE ROW IT SITS IN ───────────────────────────────────────────────────────────────────
   The container used to carry an inline `padding-bottom: 35px`. That was not spacing, it was a
   float-clearing hack: .column1-btn is `float: right` (defaultResponsive.css:387), a float
   contributes no height to its parent, and 35px happened to be a little more than the OLD button
   needed. The new button is 36px tall plus its 10px bottom margin, so it overflowed the container
   and pushed into the entity header underneath -- which is the crowding visible on the tract page.

   Fixed by not floating it. `text-align: right` puts an inline-flex button on the right just as
   well, the container then gets REAL height from its child, and the space below is a stated margin
   instead of a number tuned to one button's dimensions. Anything that changes the button's height
   now just works.

   float:none needs to beat `.column1-btn { float:right }` (0,1,0); `#column1-btn_Container .column1-btn`
   is (1,1,0). The container's own display is left alone -- .nav-phone/#column1-btn_Container are
   `display: inherit !important` at this width and cannot be overridden anyway. */
#column1-btn_Container {
    text-align: right;
    /* ONE source of spacing below the button, deliberately. The button also inherits
       `margin-bottom: 10px` from .column1-btn (defaultResponsive.css:387); left in place the two
       stacked to 22px, which read as a gap rather than as breathing room. The button's own margin is
       zeroed just below and the gap is stated once, here. */
    margin-bottom: var(--tl-space-3);
}
#column1-btn_Container .column1-btn {
    float: none;
    margin-bottom: 0;
}

/* ── A ROW OF CREATE ACTIONS THAT SURVIVES A NARROW SCREEN ────────────────────────────────────
   The legacy shape for these is a run of `float:left; width:NNNpx` divs, which is why several of
   them ended up behind `hidden-phone`: two fixed widths cannot sit side by side on a 375px screen,
   so the phone answer was to delete the controls rather than to wrap them.

   A wrapping flex row does the same job at every width -- side by side while there is room, stacked
   when there is not -- with no fixed width to be wrong and no breakpoint of its own. Used by
   Billing/Overview's Create New Billing Item / Create New Billing Invoice pair.

   The children are deliberately unstyled: they are plain divs holding an .icon-create anchor, and
   the anchor keeps its own legacy icon and padding. */
.tl-actionrow {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--tl-space-3) var(--tl-space-5);
}
