/* =========================================================================
   osTicket agent console theme — Global Pathways Academy
   =========================================================================

   PRESENTATION LAYER for the staff control panel, focused on the ticket
   queue landing page (scp/tickets.php — which IS the agent dashboard;
   scp/index.php merely requires it). Loaded LAST from
   include/staff/header.inc.php so it wins every tie against scp.css,
   dropdown.css and the vendor sheets. Upstream scp.css stays pristine;
   reverting this whole redesign is deleting one <link>.

   No PHP logic, form attribute, or JS hook changes ride along with this
   file. The only template edits on this branch are label/wrapper additions,
   verified attribute-identical with tools/attr-inventory.sh.

   Design source — assets/default/css/theme.css (the client portal theme,
   itself copied from gp.academy's compiled stylesheet). The custom
   properties below are copied verbatim from there — theme.css never loads
   in the agent panel, so the tokens are restated, not re-invented. If the
   brand is ever re-tuned, change theme.css first and mirror here.

   Width. theme.css declares the column scale and states that nothing else
   may set a container width. The agent console uses the fourth declared
   rung, --gp-w-console: 100rem (1600px) — added to theme.css in the same
   pass as this file. The shell is fluid below the rung:
       width: min(100% - 2 gutters, var(--gp-w-console))
   so the queue uses a 4K display and scales to a phone with no horizontal
   scrolling (asserted by tools/preview/check.mjs).

   Known-value fallbacks. Queue columns are agent-configurable, and cells
   carry no semantic classes — only inline background-color styles from the
   stock priority palette (include/i18n/en_US/priority.yaml) and stable
   stock ids (queue.yaml, queue_column.yaml). Priority pills/stripes and
   responsive column drops are therefore KEYED TO THOSE STOCK VALUES and
   guarded with :has(), so an unrecognised custom column set simply falls
   back to plain text and an in-form horizontal scroll — never to broken
   layout.

   Scope discipline. Selectors for the queue itself hang off form#tickets
   and the queue templates' own hooks. Chrome shared with the rest of the
   agent/admin panel (#header, #nav, #sub_nav, buttons, dropdowns) is
   restyled deliberately — the interim mixed state on other scp pages is
   accepted; do not chase it from here.
   ========================================================================= */

/* --- Brand face ---------------------------------------------------------
   Self-hosted Hanken Grotesk, variable (400–700 continuous). GDPR: no
   visitor IP ever reaches a font CDN — see docs/PROJECT-CONTEXT.md §9. The
   same four files the client portal ships in assets/default/font/; the path
   is relative to scp/css/. Split by unicode-range so the Latin-ext half is
   fetched only when the page actually contains a character in that range,
   which for this panel is a European name or a Latin-ext help topic. There
   is no 300 weight any more: the old hairline headings are gone on purpose. */

@font-face {
  font-family: 'Hanken Grotesk';
  src: url('../../assets/default/font/HankenGrotesk-Variable.woff2') format('woff2-variations'),
       url('../../assets/default/font/HankenGrotesk-Variable.woff2') format('woff2');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                 U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                 U+FFFD;
}
@font-face {
  font-family: 'Hanken Grotesk';
  src: url('../../assets/default/font/HankenGrotesk-Variable-ext.woff2') format('woff2-variations'),
       url('../../assets/default/font/HankenGrotesk-Variable-ext.woff2') format('woff2');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
                 U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
                 U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
                 U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Hanken Grotesk';
  src: url('../../assets/default/font/HankenGrotesk-Variable-Italic.woff2') format('woff2-variations'),
       url('../../assets/default/font/HankenGrotesk-Variable-Italic.woff2') format('woff2');
  font-weight: 400 700;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                 U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                 U+FFFD;
}
@font-face {
  font-family: 'Hanken Grotesk';
  src: url('../../assets/default/font/HankenGrotesk-Variable-Italic-ext.woff2') format('woff2-variations'),
       url('../../assets/default/font/HankenGrotesk-Variable-Italic-ext.woff2') format('woff2');
  font-weight: 400 700;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
                 U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
                 U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
                 U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* The Stitch token set, copied forward from assets/default/css/theme.css
   (feature/portal-stitch-redesign). That file never loads in scp/, so this
   is a copy, not an import — keep the two in step by hand, and keep the
   comments: they are the rationale. The panel-only tokens are at the end. */
:root {
  /* Ink, primary and surfaces ------------------------------------------- */
  /* Stitch's `primary` is near-black: the redesign puts weight on the
     headline and the one filled CTA and lets colour mean something else
     entirely. Sage is demoted to `secondary` — it now appears as the Open
     status chip and the active nav pill, never as a generic button. */
  --gp-primary:        #14181c;   /* filled buttons, headlines */
  --gp-primary-strong: #000000;   /* hover on a filled button */
  --gp-ink:            #1b1c19;   /* body text        (14.9:1 on cream) */
  --gp-ink-2:          #44474a;   /* on-surface-variant  (9.2:1) */
  --gp-ink-3:          #75777b;   /* outline — the floor for small labels */

  /* Brand (gp.academy sage) — semantic, not decorative -------------------- */
  --gp-brand:        #3d674e;   /* secondary  (6.6:1 on cream) */
  --gp-brand-strong: #2c4c39;   /* hover/strong */
  /* Three rungs of sage, and they are not interchangeable. `tint` is the
     hover wash — it lands under body text on a row or a list item, so it has
     to stay near the page. `soft` is the active nav pill and `chip` the Open
     status chip: both are Stitch's saturated secondary-container/fixed, and
     both are only ever a background for a short bold label. */
  --gp-brand-tint:   #eef4f0;   /* hover wash */
  --gp-brand-soft:   #bcebcb;   /* secondary-container — the active nav pill */
  --gp-brand-soft-ink: #14321f; /* darkened from Stitch's #416b52 to clear
                                   4.5:1 on the pill it sits in */
  --gp-brand-chip:   #bfedce;   /* secondary-fixed — Open chips */
  --gp-brand-ink:    #002111;   /* on-secondary-fixed */
  --gp-brand-edge:   #cfdcd4;
  --gp-brand-ring:   rgba(61, 103, 78, 0.28);
  --gp-moss:         #99A87F;   /* accent hairline — decorative only */

  /* Blue. Stitch's `upload-blue`: focus rings, in-progress state, the one
     colour that means "something is happening". */
  --gp-blue:      #5A78A8;
  --gp-blue-deep: #3f5c8a;      /* 5.8:1 on white — the text-safe rung */
  --gp-blue-tint: #eef1f7;
  --gp-blue-edge: #c9d6ea;
  --gp-blue-ring: rgba(90, 120, 168, 0.28);

  /* Surfaces. Stitch's container ladder, low to high. Cards sit at
     `lowest` (white) on the `background` cream; the footer band and the
     heaviest chips sit at `highest`. */
  --gp-page:      #fbf9f4;   /* background / surface */
  --gp-surface:   #ffffff;   /* surface-container-lowest — cards */
  --gp-surface-1: #f5f3ee;   /* surface-container-low — insets, the rail */
  --gp-surface-2: #f0eee9;   /* surface-container */
  --gp-surface-3: #eae8e3;   /* surface-container-high */
  --gp-surface-4: #e4e2dd;   /* surface-container-highest — footer band */
  --gp-beige:     #EBE9E2;   /* surface-alt — every card and input border */

  --gp-line:      #EBE9E2;   /* surface-alt, as a hairline */
  --gp-line-soft: rgba(27, 28, 25, 0.06);
  --gp-outline:   #c5c6ca;   /* outline-variant — control borders */

  /* Semantic — meaning only, never decoration ---------------------------- */
  --gp-ok:        #3d674e;   /* resolved / open */
  --gp-ok-bg:     #bfedce;
  --gp-ok-edge:   #a3d1b3;
  --gp-warn:      #8a5300;
  --gp-warn-bg:   #faf3e2;
  --gp-warn-edge: #e7d3a8;
  --gp-err:       #ba1a1a;   /* Stitch `error` */
  --gp-err-bg:    #ffdad6;   /* error-container */
  --gp-err-edge:  #f0b8b3;
  --gp-err-ink:   #93000a;   /* on-error-container */
  --gp-info:      #3f5c8a;
  --gp-info-bg:   #eef1f7;
  --gp-info-edge: #c9d6ea;
  --gp-send:      #618C71;   /* success-green — the Post Reply fill */
  --gp-send-strong: #4e7460;

  /* Type ------------------------------------------------------------------ */
  --gp-sans: "Hanken Grotesk", ui-sans-serif, -apple-system, BlinkMacSystemFont,
             "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --gp-mono: ui-monospace, SFMono-Regular, "SF Mono", "Cascadia Mono",
             Menlo, Consolas, monospace;

  /* The Stitch type scale, verbatim. Weights are heavier than the previous
     pass across the board — headlines are 600/700 here where they were a
     300 hairline before, which is the single biggest change in the file.
     NOTE the panel's body stays at 14px (see §1 Base): scp.css sets the
     panel at 14 and every em-based upstream size hangs off that, so
     --gp-t-bmd is the *portal* body rung, not this panel's. Use --gp-t-bsm
     for panel body copy and --gp-t-lmd / --gp-t-lsm for controls. */
  --gp-t-hxl:   48px;  --gp-lh-hxl:   56px;   /* page h1, desktop */
  --gp-t-hlg:   32px;  --gp-lh-hlg:   40px;   /* section h1 */
  --gp-t-hlgm:  28px;  --gp-lh-hlgm:  36px;   /* h1, phone */
  --gp-t-hmd:   24px;  --gp-lh-hmd:   32px;   /* card heading */
  --gp-t-hsm:   18px;  --gp-lh-hsm:   24px;   /* rail heading */
  --gp-t-blg:   18px;  --gp-lh-blg:   28px;   /* lede */
  --gp-t-bmd:   16px;  --gp-lh-bmd:   24px;   /* body */
  --gp-t-bsm:   14px;  --gp-lh-bsm:   20px;   /* meta */
  --gp-t-lmd:   14px;  --gp-lh-lmd:   16px;   /* button and nav labels */
  --gp-t-lsm:   12px;  --gp-lh-lsm:   14px;   /* chips, eyebrows */

  /* Metrics ---------------------------------------------------------------- */
  /* Column scale — three golden-ratio rungs plus the console. Nothing else
     may set a container width; a surface that needs a value not on this
     list is a surface whose contents need rethinking. Full rationale in
     theme.css. The console rung is the one this panel is built on. */
  --gp-w-auth: 27rem;   /* 432px */
  --gp-w-form: 44rem;   /* 704px */
  --gp-w-page: 71rem;   /* 1136px, which is 1088 inside the page gutters */
  --gp-w-console: 100rem;  /* 1600px — the agent console; sits outside the
                              golden progression deliberately: it is sized by
                              the widest display the queue must fill, not by
                              its neighbours. */

  /* Shell. The rail and the bar are fixed; everything else is measured
     against them, so both are tokens rather than repeated magic numbers.
     The rail breakpoint is 981px and is a documented constant, not a token:
     custom properties cannot be used in media queries. */
  --gp-rail:   256px;   /* w-64 */
  --gp-bar-h:  64px;    /* h-16 */
  --gp-margin:      64px;   /* margin-desktop */
  --gp-margin-sm:   16px;   /* margin-mobile */

  --gp-pad:    24px;    /* gutter */
  --gp-gutter: 56px;    /* thread avatar gutter */
  --gp-r:      12px;    /* cards — rounded-xl */
  --gp-r-sm:   8px;     /* panels and buttons — rounded-lg */
  --gp-r-xs:   4px;     /* inputs and chips — rounded */
  --gp-r-pill: 999px;

  /* Card treatment. Two levels, and only two: a CARD is a standalone surface
     sitting on the page background; an INSET is a secondary surface nested in
     a page's flow. Boxes at the same width still look mismatched when one is
     padded 30 and its neighbour 22, which is most of what made the portal feel
     uneven — five different card paddings had accumulated. Stitch pads its
     cards on the 8px unit (p-8 / p-6), so both values move onto it. */
  --gp-card-pad:  32px;
  --gp-card-gap:  24px;   /* space above a card */
  --gp-inset-pad: 24px;

  /* Prose. Boxes fill their rung; the lines inside them do not. */
  --gp-measure:      68ch;   /* body copy in page-width content */
  --gp-measure-lede: 52ch;   /* the centred intro under a page heading */

  /* Motion — colours fast, transforms slightly slower, both ease-out ------- */
  --gp-t-color: 120ms ease-out;
  --gp-t-move:  160ms cubic-bezier(0.23, 1, 0.32, 1);

  /* Optical correction for Font Awesome 3.2.1 glyphs set beside text.
     FA's em box has ascent 1536 / descent 0, so a glyph's box centre does not
     coincide with its ink centre, and align-items:center lines up boxes, not
     ink — leaving the glyph visibly low against the text's cap band. Measured
     against General Sans by decoding rendered pixels: the text's cap band runs
     18.00-29.13px inside a 40px pill (centre 23.60) while the glyph ink centre
     landed at 22.69 — about 1px high. Nudging down by 1px at 15px text lands
     the plus, magnifier, house, book, and document glyphs on the cap-band
     centre. Expressed in em so it tracks font-size. Hanken Grotesk's cap
     height is within a hair of General Sans's, so the measured value carries
     over. */
  --gp-icon-nudge: 0.065em;

  /* Names kept so nothing downstream breaks while the file is being
     retuned. Both now resolve into the new palette. */
  --gp-navy:        var(--gp-blue-deep);
  --gp-navy-strong: #334970;
  --gp-navy-tint:   var(--gp-blue-tint);
  --gp-navy-edge:   var(--gp-blue-edge);
  --gp-gold-soft:   rgba(153, 168, 127, 0.22);  /* moss, decorative only */
  --gp-surface-2-legacy: #f0eee9;

  /* ---- Panel-only tokens (not in theme.css) ------------------------------ */

  /* The panel's interactive accent: every filled button, checked control and
     active tab in this file reads --gp-accent, 160-odd times. It is an alias
     so the decision lives in one line. It used to point at the brand sage;
     in this design sage MEANS STATE (the Open chip, the active rail pill), so
     a sage button would read as a status. The accent is therefore Stitch's
     near-black primary, and the ring — the one thing that must still say
     "something is happening" — is the blue ring, the same one every field
     takes on focus. Tint and edge are the neutral surface/outline rungs, so a
     hover wash under a near-black control never turns green. */
  --gp-accent:        var(--gp-primary);
  --gp-accent-strong: var(--gp-primary-strong);
  --gp-accent-tint:   var(--gp-surface-2);
  --gp-accent-edge:   var(--gp-outline);
  --gp-accent-ring:   var(--gp-blue-ring);

  /* Upload states. Two colours, and they are the only two the uploader is
     allowed: blue means IN FLIGHT, green means LANDED. They are the same
     values as --gp-blue / --gp-send above and are aliased rather than kept as
     four names for two colours; the uploader's own rules keep their names. */
  --gp-upload:      var(--gp-blue);
  --gp-upload-tint: rgba(90, 120, 168, 0.10);
  --gp-upload-edge: var(--gp-blue-edge);
  --gp-done:        var(--gp-send);

  /* Console-local metrics */
  --gp-scp-gutter: 32px;
  --gp-ctl-h:      34px;    /* secondary controls */
  --gp-ctl-h-lg:   40px;    /* the search pair */
}

/* =========================================================================
   1. Base
   ========================================================================= */

/* scp.css sets html to 14px, which quietly shrinks every rem — the 100rem
   console rung would render at 1400px. Restore the standard root; body keeps
   the panel's 14px so em-based sizing is unchanged. */
html {
  font-size: 100%;
  background: var(--gp-page);
}
body {
  background: var(--gp-page);
  color: var(--gp-ink);
  font-family: var(--gp-sans);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
input, select, textarea, button {
  font-family: var(--gp-sans);
}
a { color: var(--gp-blue-deep); }
a:hover { color: var(--gp-primary); }
h2 {
  color: var(--gp-ink);
  font-weight: 400;
  letter-spacing: -0.015em;
}

/* =========================================================================
   2. Shell — a fixed bar, a fixed rail, and one canvas beside them
   =========================================================================
   Stitch's shell: a 64px bar across the full width, a 256px rail under it
   down the left edge, and everything else on the canvas to the right. The
   template's only change is a <nav class="site-rail"> around the
   pre-existing <ul id="nav"> and the sub-nav include, plus the #nav-toggle
   checkbox the phone drawer runs on. #header stays where it was in the DOM
   and is simply fixed. The rail lives inside #pjax-container, so it is
   re-rendered on every pjax swap — which is why nothing here depends on
   state the rail keeps between pages. The rail breakpoint is 981px, a
   documented constant (custom properties do not work in media queries). */

#container {
  box-sizing: border-box;
  width: auto;
  min-height: 100vh;
  margin: 0;
  padding: var(--gp-bar-h) 0 0 var(--gp-rail);
}
/* Upstream pins the sub-nav strip to the old fixed shell. */
.jb-overflowmenu { width: auto; }

/* When scp.js pins a page bar it goes viewport-wide; it must start under the
   fixed top bar and right of the rail, and its content column must line up
   with the card underneath it. scp.js writes `top` inline, so the override
   is !important — the slide-in animation just snaps instead. */
.sticky.bar.fixed {
  top: var(--gp-bar-h) !important;
  left: var(--gp-rail);
  right: 0;
  width: auto;
  background: rgba(255, 255, 255, 0.97);
  box-shadow: 0 6px 24px -12px rgba(27, 28, 25, 0.35);
  border-bottom: 1px solid var(--gp-line);
}
.sticky.bar.fixed .content {
  box-sizing: border-box;
  width: min(100% - (var(--gp-scp-gutter) * 2), var(--gp-w-console));
  max-width: none;
  margin: auto;
}

/* The content sheet: one white card on the canvas, measured against the
   console rung, centred in whatever is left beside the rail. */
#content {
  box-sizing: border-box;
  clear: both;
  width: min(100% - (var(--gp-scp-gutter) * 2), var(--gp-w-console));
  margin: 16px auto 24px;
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r);
  padding: 22px 24px 28px;
}

#footer {
  width: min(100% - (var(--gp-scp-gutter) * 2), var(--gp-w-console));
  margin: 0 auto;
  color: var(--gp-ink-3);
  font-size: 12.5px;
  padding: 14px 10px 6px;
}
/* The system banners render before #header in the DOM; they read as a strip
   across the top of the canvas. */
#error_bar, #warning_bar, #notice_bar {
  width: min(100% - (var(--gp-scp-gutter) * 2), var(--gp-w-console));
  margin: 12px auto 0;
}

/* =========================================================================
   3. Site bar (#header) — the fixed top bar
   ========================================================================= */

#header {
  box-sizing: border-box;
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 50;
  height: var(--gp-bar-h);
  min-height: 0;
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: 12px;
  background: var(--gp-surface);
  border: 0;
  border-bottom: 1px solid var(--gp-line);
  border-radius: 0;
  box-shadow: none;
  padding: 0 var(--gp-pad) 0 16px;
}
/* The brand occupies the rail's width in the bar, so the wordmark and the
   rail items below it share one left edge. */
#header #logo {
  order: -1;              /* markup puts #info first; the brand reads first */
  box-sizing: border-box;
  flex: none;
  width: calc(var(--gp-rail) - 16px);
  height: auto;
  padding: 0;
  display: flex;
  align-items: center;
}
#header #logo img { max-height: 40px; }

/* The phone menu button. Hidden until the rail becomes a drawer. The input
   it drives sits at the top of #pjax-container (a sibling of the rail, so
   `~` reaches it); the label here reaches it by `for`, and the bar reads the
   state back through #container:has(). The input stays 1px and focusable —
   not display:none — so keyboard users can still open the drawer. */
#nav-toggle {
  position: fixed;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  opacity: 0;
  appearance: none;
  -webkit-appearance: none;
  border: 0;
}
.nav-toggle-btn {
  display: none;          /* shown by the media query at the end of §5 */
  order: -2;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  flex: none;
  border: 1px solid var(--gp-outline);
  border-radius: var(--gp-r-sm);
  color: var(--gp-primary);
  cursor: pointer;
  transition: background-color var(--gp-t-color), border-color var(--gp-t-color);
}
.nav-toggle-btn::before {
  font-family: FontAwesome;
  font-style: normal;
  font-weight: normal;
  content: "\f0c9";       /* reorder (hamburger) */
  font-size: 15px;
  line-height: 1;
}
.nav-toggle-btn:hover {
  background: var(--gp-surface-3);
  border-color: var(--gp-ink-3);
}
#container:has(#nav-toggle:checked) .nav-toggle-btn {
  background: var(--gp-primary);
  border-color: var(--gp-primary);
  color: #fff;
}
#container:has(#nav-toggle:checked) .nav-toggle-btn::before { content: "\f00d"; }  /* × */
#container:has(#nav-toggle:focus-visible) .nav-toggle-btn {
  outline: 2px solid var(--gp-blue);
  outline-offset: 2px;
}

/* The account cluster. The "|" separators are template text nodes — they
   inherit the muted paragraph colour and read as hairline dividers. */
#header p#info {
  float: none;
  order: 1;
  margin: 0 0 0 auto;
  max-width: none;
  width: auto;
  background: none;
  border: 0;
  padding: 0;
  text-align: right;
  color: var(--gp-ink-3);
  font-size: 13.5px;
  white-space: nowrap;
}
#header p#info strong { font-weight: 500; color: var(--gp-ink); }
/* Everything in the cluster shares one 30px line box, so bare text, the
   pill links and hover states all centre on the same axis. The template's
   literal pipe separators are wrapped (.gp-sep) and dropped here — the
   pills' own rhythm separates the links. */
#header p#info { line-height: 30px; }
#header p#info .gp-sep { display: none; }
#header p#info a {
  display: inline-block;
  height: 30px;
  line-height: 30px;
  color: var(--gp-ink-2);
  text-decoration: none;
  padding: 0 11px;
  margin: 0;
  border-radius: var(--gp-r-pill);
  transition: background var(--gp-t-color), color var(--gp-t-color);
}
#header p#info a:hover {
  background: var(--gp-surface-2);
  color: var(--gp-ink);
}
/* Admin Panel (agent side) / Agent Panel (admin side) is the bar's one
   emphasised link. */
#header p#info a[href$="scp/admin.php"],
#header p#info a[href$="scp/index.php"] {
  color: var(--gp-primary);
  font-weight: 500;
}
#header p#info a[href$="scp/admin.php"]:hover,
#header p#info a[href$="scp/index.php"]:hover {
  background: var(--gp-surface-2);
  color: var(--gp-primary-strong);
}

/* =========================================================================
   4. The rail and the primary nav (#nav) — full-width pills, one filled
   ========================================================================= */

/* Fixed under the bar, down the left edge, on the low container surface so
   it reads as a plane behind the content rather than a card on it. */
.site-rail {
  box-sizing: border-box;
  position: fixed;
  left: 0;
  top: var(--gp-bar-h);
  bottom: 0;
  z-index: 40;
  width: var(--gp-rail);
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 16px 12px var(--gp-pad);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--gp-surface-1);
  border-right: 1px solid var(--gp-line);
}

/* The tab list. Upstream lays it out as a horizontal strip with hover menus;
   here it is a column of pills. The nested <ul> upstream shows on hover is
   hidden outright: the section you are in lists its pages below (#sub_nav),
   and every destination in those menus has a landing page. */
#nav {
  order: 1;
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
  height: auto;
  line-height: normal;
  margin: 0;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  white-space: normal;
}
#nav .active, #nav .inactive {
  display: block;
  min-width: 0;
  height: auto;
  padding: 0;
  margin: 0;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  background: none;
  text-align: left;
  vertical-align: baseline;
  float: none;
}
#nav > li + li { margin-left: 0; }
#nav li.active, #nav li.inactive:hover {
  box-shadow: none;
  border-color: transparent;
  background: none;
}
#nav .active > a, #nav .inactive > a {
  box-sizing: border-box;   /* min-height is the whole pill, padding included:
                               content-box made a 44px pill render at 64px */
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 38px;
  padding: 8px 14px;
  margin: 0;
  border: 0;
  border-radius: var(--gp-r-pill);
  font-size: var(--gp-t-lmd);
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: 0;
  color: var(--gp-ink-2);
  text-decoration: none;
  background: none;
  transition: background-color var(--gp-t-color), color var(--gp-t-color),
              transform var(--gp-t-move);
}
/* Icons come from the Font Awesome build the page already loads, keyed off
   the href because the template puts no class on the anchor (the tab key is
   never emitted — class.nav.php:129-181 computes it and navigation.tmpl.php
   prints only desc and href). */
#nav > li > a::before {
  font-family: FontAwesome;
  font-weight: normal;
  font-style: normal;
  font-size: 1em;
  line-height: 1;
  width: 1.15em;
  text-align: center;
  color: var(--gp-ink-3);
  flex: none;
  transform: translateY(var(--gp-icon-nudge));
  transition: color var(--gp-t-color);
  content: "\f0c9";                                        /* fallback: reorder */
}
#nav > li > a[href$="dashboard.php"]::before,
#nav > li > a[href$="logs.php"]::before      { content: "\f0e4"; }  /* dashboard    */
#nav > li > a[href$="users.php"]::before,
#nav > li > a[href$="staff.php"]::before     { content: "\f0c0"; }  /* group        */
#nav > li > a[href$="tickets.php"]::before   { content: "\f0f6"; }  /* file-text    */
#nav > li > a[href$="internal.php"]::before  { content: "\f0e8"; }  /* sitemap      */
#nav > li > a[href$="tasks.php"]::before     { content: "\f0ae"; }  /* tasks        */
#nav > li > a[href$="issues.php"]::before    { content: "\f071"; }  /* warning-sign */
#nav > li > a[href$="mailings.php"]::before  { content: "\f003"; }  /* envelope     */
#nav > li > a[href$="kb.php"]::before        { content: "\f02d"; }  /* book         */
#nav > li > a[href$="apps.php"]::before,
#nav > li > a[href*="apps/"]::before         { content: "\f00a"; }  /* th           */
#nav > li > a[href$="settings.php"]::before  { content: "\f013"; }  /* cog          */
#nav > li > a[href$="manage.php"]::before    { content: "\f0ad"; }  /* wrench       */
#nav > li > a[href$="emails.php"]::before    { content: "\f003"; }  /* envelope     */
#nav > li > a[href$="reports.php"]::before   { content: "\f080"; }  /* bar-chart    */

#nav .inactive > a:hover {
  background: var(--gp-surface-3);
  color: var(--gp-primary);
}
#nav .inactive > a:hover::before { color: var(--gp-primary); }
/* Stitch nudges a rail item sideways on press rather than scaling it — the
   item is the full width of the rail, and scaling something that wide reads
   as the whole panel moving. */
#nav > li > a:active { transform: translateX(4px); }
/* The tab you are standing on: Stitch's secondary-container pill — the one
   sage-filled thing in the shell. */
#nav .active > a {
  background: var(--gp-brand-soft);
  color: var(--gp-brand-soft-ink);
  font-weight: 600;
  border-bottom: 0;
}
#nav .active > a::before { color: var(--gp-brand); }
#nav .active > a:hover { background: var(--gp-brand-soft); color: var(--gp-brand-soft-ink); }

/* Upstream's hover menus, hidden in the rail. Kept as a rule rather than
   removed from the template: the <ul> and its loop are byte-identical. */
#nav li > ul,
#nav li.inactive > ul,
#nav li.inactive:hover > ul { display: none !important; }
/* Upstream reserves 34px of padding for a gif sprite in each hidden row;
   the sprite must never come back even if the menu is shown again. */
#nav li > ul li a,
#nav .inactive li a { background-image: none !important; padding-left: 12px; }

/* =========================================================================
   5. The section's pages (#sub_nav) — a column under the tabs
   =========================================================================
   The sub-nav is whatever the active tab publishes: queue groups on
   Tickets, saved views on Tasks and Internal, plain page links elsewhere.
   scp-theme.js reverts the jb.overflowmenu enhancement and flattens the
   queue dropdowns into real <li> siblings (see flattenQueueNav), so the
   markup here is: each top-level queue as a row, its children as indented
   rows (inline for the group you are in, behind a caret for the others),
   and the "New …" item lifted to the top as the rail's one filled action.
   The saved-searches "Search" tab is hidden by request — re-enable by
   deleting one rule below. */

#customQ_nav,
nav.jb-overflowmenu,
nav:has(> #sub_nav) {
  order: 2;
  flex: 0 0 auto;
  display: block;
  background: none;
  border: 0;
  border-top: 1px solid var(--gp-line);
  border-radius: 0;
  margin: 6px 0 0;
  padding: 10px 0 0;
  height: auto;
  line-height: normal;
  width: auto;
}
#sub_nav {
  background: none;
  border: 0;
  border-radius: 0;
  height: auto;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  flex-wrap: nowrap;
  gap: 2px;
  width: auto;
  min-width: 0;
}
/* If the enhancement ever runs before the script reverts it, neutralise its
   geometry so nothing jumps. */
.jb-overflowmenu .jb-overflowmenu-helper-postion { position: static !important; }
.jb-overflowmenu .jb-overflowmenu-menu-primary {
  margin: 0;
  height: auto !important;
  padding: 0;
}
.jb-overflowmenu .jb-overflowmenu-container { display: none !important; }

#sub_nav > li,
#sub_nav li.item,
.jb-overflowmenu .jb-overflowmenu-menu-primary li.item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0;
  padding: 0;
  margin: 0;
  border: 0 !important;
  background: none;
  float: none;
  width: auto;
}
#sub_nav > li.top-queue + li.top-queue { margin-left: 0; }
#sub_nav > li > a { max-width: 100%; }
#customQ_nav .jb-overflowmenu-menu-primary li.item:hover {
  padding: 0;
  border: 0;
  background: none;
}

/* The row voice — one rule for top-level links and flattened children.
   A row is a quieter pill than a tab: 36px, no icon, the count at the far
   end. */
#sub_nav > li > a,
#sub_nav li.item > a {
  box-sizing: border-box;
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 34px;
  margin: 0;
  padding: 6px 12px;
  border-radius: var(--gp-r-sm);
  border: 1px solid transparent;
  background-image: none !important;   /* the old gif tab icons */
  font-size: 13.5px;
  font-weight: 500;
  line-height: 1.3;
  color: var(--gp-ink-2);
  text-decoration: none;
  max-width: none;
  transition: background var(--gp-t-color), color var(--gp-t-color),
              border-color var(--gp-t-color);
}
#sub_nav > li > a:hover,
#sub_nav li.item > a:hover {
  background: var(--gp-surface-3);
  color: var(--gp-primary);
}
#sub_nav a.active,
#sub_nav li.active > a,
#sub_nav li.child.active > a {
  background: var(--gp-surface);
  color: var(--gp-ink);
  border-color: var(--gp-line);
  box-shadow: 0 1px 2px rgba(27, 28, 25, 0.07);
  font-weight: 600;
}
/* No dropdowns — no carets from upstream. */
#sub_nav li a .icon-sort-down { display: none; }
#customQ_nav li.item .customQ-dropdown { display: none !important; }

/* --- the rail no longer lists pre-filtered views of the current list ----
   Every one of these rows is a link to the page you are already on with a
   filter applied, which is precisely what the facet panel below now does.
   Two controls for one job, a few hundred pixels apart, is the redundancy
   the filter panel created.

   What stays: anything that goes somewhere else. The "New …" action (always
   `?a=new` or a dialog, and always .newTicket), Mailing's Templates and
   Recipient Lists, and Tasks' Overdue and Completed - Tasks has no Status
   facet, so those two are not expressible in the panel and would become
   unreachable. That is the line: covered by a facet, it goes; not covered,
   it stays.

   Getting back to the unfiltered list is the section's own nav pill, which
   every one of these pages already has above.

   Targeted by href rather than by label, so translation cannot break it,
   and by :has() on the <li> so no empty row is left behind in the flex
   column. The ticket queue tree is matched by class instead - queue ids are
   per-install, so there is no href to write. NOTE: that hides EVERY
   top-level queue, so an admin-configured public queue whose criteria the
   facets cannot express would disappear with My Tickets and Closed.
   Personal saved searches are unaffected - they have their own group in the
   panel. Delete the .gp-qgroup rule to bring the tree back. */
#sub_nav > li.gp-qgroup,
#sub_nav > li.gp-qchild,
#sub_nav > li:has(> a[href="issues.php"]),
#sub_nav > li:has(> a[href^="internal.php?status="]),
#sub_nav > li:has(> a[href^="internal.php?assignee="]),
#sub_nav > li:has(> a[href^="internal.php?overdue="]),
#sub_nav > li:has(> a[href="tasks.php?status=open"]),
#sub_nav > li:has(> a[href="tasks.php?status=assigned"]),
#sub_nav > li:has(> a[href="users.php"]),
#sub_nav > li:has(> a[href="orgs.php"]) { display: none; }

/* NOTE on the two above. "User Directory" is the same self-link as "Issue
   Log" and goes for the same reason. "Organizations" is NOT - it is a
   different list, and the facet panel does not cover it. Removing it was
   asked for directly, so it is removed, but the cost should be visible:
   class.nav.php:211 is the ONLY route to the organizations LIST in the
   whole panel. Every other orgs.php link in the codebase carries an ?id=
   and lands on one organization - from a ticket, a user's profile, or the
   org page's own back chevron. With this rule the list is reachable by
   typing the URL, or by opening a user who has an organization.
   Delete the orgs.php line to put it back. */

/* …and when that empties the block completely, the block goes too.
   The sub-nav wrapper draws the rail's second hairline (border-top, §5).
   On the ticket queue every row above is now hidden and there is no "New
   Ticket" action to keep it company (scp/tickets.php:562 says so
   deliberately), so the wrapper was left as an 11px sliver drawing a
   divider above nothing - two rules apart from the filter panel's own
   divider, which read as a double line under Knowledgebase.

   The test is "does this sub-nav still hold a row that is not a queue
   row", so it hides on the queue and stays everywhere else: Issues keeps
   New Issue, Internal keeps New Internal Request, Tasks keeps Overdue,
   Completed and New Task. If a future surface ever has all of its rows
   hidden by the href rules above, it will need adding here - those rows
   carry no class to test for. */
nav:has(> #sub_nav):not(:has(#sub_nav > li:not(.gp-qgroup):not(.gp-qchild):not(.primary-only))) {
  display: none;
}

/* A hoisted child is the same row as its parent, one step in and quieter. */
#sub_nav > li.gp-qchild > a {
  margin-left: 16px;
  font-weight: 450;
  color: var(--gp-ink-2);
  border-left: 2px solid var(--gp-line);
  border-radius: 0 var(--gp-r-sm) var(--gp-r-sm) 0;
}
#sub_nav > li.gp-qgroup + li.gp-qchild { margin-left: 0; }
#sub_nav > li.gp-qgroup { margin-left: 0; }

/* --- group menus ---------------------------------------------------------
   The queue you are in shows its children inline; every other group keeps
   them behind a caret. In a rail the menu is not a popover: it expands in
   place below the group, so nothing is clipped by the rail's own scroll.
   The label still navigates to the group's own queue — only the caret opens
   the menu. */
#sub_nav > li.gp-qgroup { position: static; }
#sub_nav .gp-qcaret {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 30px;
  height: 30px;
  margin: 0 0 0 2px;
  padding: 0;
  border: 0;
  border-radius: var(--gp-r-sm);
  background: none;
  color: var(--gp-ink-3);
  cursor: pointer;
  transition: background var(--gp-t-color), color var(--gp-t-color),
              transform var(--gp-t-move);
}
#sub_nav .gp-qcaret:hover,
#sub_nav li.gp-open > .gp-qcaret {
  background: var(--gp-surface-3);
  color: var(--gp-ink);
}
#sub_nav li.gp-open > .gp-qcaret i { transform: rotate(180deg); }
#sub_nav .gp-qcaret i {
  margin: 0;
  font-size: 11px;
  transition: transform var(--gp-t-move);
}
#sub_nav .gp-qcaret:focus-visible {
  outline: 2px solid var(--gp-blue);
  outline-offset: 2px;
}

#sub_nav .gp-qmenu {
  display: none;
  position: static;
  flex: 1 0 100%;
  width: 100%;
  min-width: 0;
  margin: 2px 0 4px;
  padding: 0;
  list-style: none;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
#sub_nav li.gp-open > .gp-qmenu { display: block; }
#sub_nav .gp-qmenu > li {
  display: flex;
  align-items: center;
  width: auto;
  margin: 0 0 0 16px;
  padding: 0;
  border-left: 2px solid var(--gp-line);
  border-radius: 0 var(--gp-r-sm) var(--gp-r-sm) 0;
}
#sub_nav .gp-qmenu > li:hover { background: var(--gp-surface-3); }
#sub_nav .gp-qmenu > li > a {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  min-height: 34px;
  padding: 6px 10px;
  border-radius: 0 var(--gp-r-sm) var(--gp-r-sm) 0;
  font-size: 13px;
  font-weight: 450;
  color: var(--gp-ink-2);
  white-space: normal;
  text-decoration: none;
}
#sub_nav .gp-qmenu > li:hover > a,
#sub_nav .gp-qmenu > li > a.active { color: var(--gp-primary); background: none; }
#sub_nav .gp-qmenu > li > span.queue-count {
  order: 2;                    /* the count trails its queue down the row */
  margin: 0 8px 0 auto;
  background: var(--gp-surface);
  border-color: transparent;
}
/* "Add personal queue" reads as a labelled row, not a bare green plus. */
#sub_nav .gp-qmenu > li.gp-qadd-row {
  margin-top: 4px;
  padding-top: 4px;
  border-top: 1px solid var(--gp-line-soft);
}
#sub_nav .gp-qmenu > li.gp-qadd-row > a {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 450;
  font-size: 13px;
  color: var(--gp-ink-2) !important;
  cursor: pointer;
}
#sub_nav .gp-qmenu > li.gp-qadd-row > a span {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  opacity: 1;
  padding: 0;
}
#sub_nav .gp-qmenu > li.gp-qadd-row i { color: var(--gp-ink-3); margin: 0; }

/* Ticket counts — tabular chips at the far end of the row. scp.js reveals
   every #queue-count-bucket when the counts ajax lands. */
#sub_nav .queue-count,
#sub_nav li > span.queue-count,
#sub_nav li:not(.personalQ) > span.queue-count,
#sub_nav li:hover > span.queue-count {
  float: none;
  order: 2;
  margin: 0 0 0 auto;
  font-weight: 500;
  font-family: var(--gp-mono);
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  color: var(--gp-ink-2);
  background: var(--gp-surface);
  border: 1px solid var(--gp-line-soft);
  border-radius: var(--gp-r-pill);
  padding: 1px 7px;
}
#sub_nav #queue-count-bucket {
  display: inline-flex;
  align-items: center;
  margin-left: auto;
}
#sub_nav #queue-count-bucket.hidden { display: none; }
#sub_nav #queue-count-bucket .queue-count { background: none; padding: 0; margin: 0; border: 0; }
#sub_nav #queue-count-bucket:not(.hidden) {
  background: var(--gp-surface);
  border: 1px solid var(--gp-line-soft);
  border-radius: var(--gp-r-pill);
  padding: 1px 7px;
}
#sub_nav li.active > a .queue-count,
#sub_nav li.active > a #queue-count-bucket:not(.hidden) { color: var(--gp-brand-soft-ink); }
#sub_nav li.active > a #queue-count-bucket:not(.hidden) { background: var(--gp-brand-chip); border-color: transparent; }
/* The stock Overdue queue (id 4 since install — include/i18n/en_US/
   queue.yaml). A renumbered custom queue just keeps the neutral chip. */
#sub_nav .queue-count[data-queue-id="4"],
#sub_nav li:not(.personalQ) > span.queue-count[data-queue-id="4"] {
  color: var(--gp-err);
  background: var(--gp-err-bg);
}
/* Wrapper spans hide the literal parentheses the template prints. */
#sub_nav .gp-paren { display: none; }

/* --- "New …" — the rail's one filled action ------------------------------
   Every section publishes its create action into the sub-nav with the
   `newTicket` icon class (New Ticket, New Task, New Internal Request …).
   It is lifted to the top of the section's list with `order`, drawn as a
   button at the small radius rather than a pill, and filled with the
   primary — the only filled thing in the rail. */
#sub_nav > li:has(> a.newTicket) {
  order: -1;
  margin: 0 0 8px;
}
#sub_nav > li > a.newTicket,
#sub_nav a#new-ticket {
  box-sizing: border-box;
  justify-content: center;
  min-height: 38px;
  padding: 8px 14px;
  border-radius: var(--gp-r-sm);
  background: var(--gp-primary);
  border-color: var(--gp-primary);
  color: #fff;
  font-weight: 600;
  margin-left: 0;
}
#sub_nav > li > a.newTicket::before {
  font-family: FontAwesome;
  font-weight: normal;
  content: "\f067";                                        /* plus */
  font-size: 0.9em;
  line-height: 1;
  transform: translateY(var(--gp-icon-nudge));
}
#sub_nav > li > a.newTicket:hover,
#sub_nav a#new-ticket:hover {
  background: var(--gp-primary-strong);
  border-color: var(--gp-primary-strong);
  color: #fff;
}
#sub_nav > li > a.newTicket:active { transform: scale(0.97); }

/* The saved-searches "Search" tab is hidden by request — the search bar
   and Advanced search cover it. Delete this rule to bring it back
   (saved searches are unreachable while it stands). */
#sub_nav > li.item.primary-only:has(> a.Ticket) { display: none; }

/* --- Below the rail breakpoint: a drawer ---------------------------------
   981px is where the rail stops earning its 256px: below it the canvas
   beside the rail is narrower than the form rung. The rail slides off-canvas
   and the bar grows a menu button; the #nav-toggle checkbox does the work,
   so no script is involved and nothing depends on scp-theme.js having run. */
@media (max-width: 980px) {
  #container { padding-left: 0; }
  #header { padding-left: 12px; gap: 10px; }
  #header #logo { width: auto; }
  .nav-toggle-btn { display: inline-flex; }
  .sticky.bar.fixed { left: 0; }
  .site-rail {
    width: min(300px, 86vw);
    transform: translateX(-100%);
    visibility: hidden;
    box-shadow: 24px 0 48px -32px rgba(20, 24, 28, 0.45);
    /* visibility is stepped, not interpolated: held until the slide-out
       finishes on the way closed, flipped at once on the way open. */
    transition: transform var(--gp-t-move),
                visibility 0s linear var(--gp-t-move);
  }
  #nav-toggle:checked ~ .site-rail {
    transform: none;
    visibility: visible;
    transition: transform var(--gp-t-move), visibility 0s;
  }
  /* The account cluster joins the foot of the drawer, which is where Stitch
     puts Profile and Sign Out. There is not room in a 64px bar for a logo, a
     menu button AND four account links, and the cluster is what overflowed.
     It cannot move there in the DOM without touching the template's
     conditionals, so it is positioned onto the drawer: both boxes are fixed
     and both take their width from the same expression. */
  #header p#info {
    position: fixed;
    left: 0;
    bottom: 0;
    z-index: 41;
    box-sizing: border-box;
    width: min(300px, 86vw);
    margin: 0;
    padding: 12px 16px;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
    line-height: 1.4;
    text-align: left;
    white-space: normal;
    font-size: 13px;
    background: var(--gp-surface-1);
    border-top: 1px solid var(--gp-line);
    border-right: 1px solid var(--gp-line);
    transform: translateX(-100%);
    visibility: hidden;
    transition: transform var(--gp-t-move),
                visibility 0s linear var(--gp-t-move);
  }
  #container:has(#nav-toggle:checked) #header p#info {
    transform: none;
    visibility: visible;
    transition: transform var(--gp-t-move), visibility 0s;
  }
  #header p#info a {
    display: block;
    height: auto;
    line-height: 1.4;
    padding: 8px 10px;
    border-radius: var(--gp-r-sm);
  }
  /* The drawer's foot must not cover the last rail rows. */
  .site-rail { padding-bottom: 200px; }
}
@media (prefers-reduced-motion: reduce) {
  .site-rail, #nav > li > a, #sub_nav .gp-qcaret, #sub_nav .gp-qcaret i { transition: none; }
}

/* =========================================================================
   6. Message banners — semantic tokens, no sprite icons
   ========================================================================= */

#msg_notice, .success-banner, .notice-banner,
#msg_warning, .warning-banner,
#msg_error, .error-banner,
#msg_info, .info-banner,
#notice_bar, #warning_bar, #error_bar {
  height: auto;
  line-height: 1.5;
  padding: 10px 16px;
  border-radius: var(--gp-r-sm);
  background-image: none;
  margin-bottom: 12px;
}
#msg_notice, .success-banner, .notice-banner, #notice_bar {
  background-color: var(--gp-ok-bg);
  border: 1px solid var(--gp-ok-edge);
  color: var(--gp-ok);
}
#msg_warning, .warning-banner, #warning_bar {
  background-color: var(--gp-warn-bg);
  border: 1px solid var(--gp-warn-edge);
  color: var(--gp-warn);
}
#msg_error, .error-banner, #error_bar {
  background-color: var(--gp-err-bg);
  border: 1px solid var(--gp-err-edge);
  color: var(--gp-err);
}
#msg_info, .info-banner {
  background-color: var(--gp-info-bg);
  border: 1px solid var(--gp-info-edge);
  color: var(--gp-info);
}

/* =========================================================================
   7. Controls — one pill voice for every button on the panel
   ========================================================================= */

.action-button,
.button,
input[type="button"],
input[type="reset"],
input[type="submit"],
button[type="submit"] {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  position: relative;
  height: var(--gp-ctl-h);
  min-height: var(--gp-ctl-h);
  line-height: 1;
  padding: 0 14px;
  margin: 0 4px;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-pill);
  box-shadow: none;
  background: var(--gp-surface);
  color: var(--gp-ink-2);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: -0.005em;
  white-space: nowrap;
  transition: background var(--gp-t-color), color var(--gp-t-color),
              border-color var(--gp-t-color), opacity var(--gp-t-color);
}
input[type="button"], input[type="reset"],
input[type="submit"], button[type="submit"] {
  display: inline-block;      /* replaced elements can't flex their text */
  line-height: calc(var(--gp-ctl-h) - 2px);
  padding: 0 16px;
}
.button:hover, .button:active,
.action-button:hover, .action-button:active,
input[type="button"]:hover, input[type="button"]:active,
input[type="reset"]:hover, input[type="reset"]:active {
  background: var(--gp-accent-tint);
  border-color: var(--gp-accent-edge);
  color: var(--gp-accent);
  box-shadow: none;
}
button[type="submit"], input[type="submit"], .primary.button {
  background: var(--gp-accent);
  border-color: var(--gp-accent);
  color: #fff;
  box-shadow: none;
}
button[type="submit"]:hover, input[type="submit"]:hover,
input[type="submit"]:active, .primary.button:hover {
  background: var(--gp-accent-strong);
  border-color: var(--gp-accent-strong);
  color: #fff;
  box-shadow: none;
}
.action-button:focus-visible, .button:focus-visible,
input[type="submit"]:focus-visible, button[type="submit"]:focus-visible {
  outline: 2px solid var(--gp-blue);
  outline-offset: 2px;
}
.action-button.muted { box-shadow: none; }
.action-button i.icon-caret-down,
.action-button i.icon-angle-down {
  float: none;
  order: 2;          /* the template writes it first and floats it; in a
                        flex button the float does nothing, so order puts it
                        back on the trailing edge where it belongs */
  opacity: 0.55;
}
/* Destructive actions state their nature before they're pressed. */
.red.button, .action-button.danger, a#tickets-delete {
  background: var(--gp-surface);
  border-color: var(--gp-err-edge);
  color: var(--gp-err);
}
.red.button:hover, .action-button.danger:hover, a#tickets-delete:hover {
  background: var(--gp-err);
  border-color: var(--gp-err);
  color: #fff;
  box-shadow: none;
}
/* Anchors styled as action buttons carry their own inner <a>; keep it
   inheriting the pill's colour.
   scp.css floats `.action-button span, .action-button a` LEFT — and since
   the icon <i> is not floated, every label was pulled in front of its own
   icon ("Change Status⚑"). Unfloat them and make the inner anchor its own
   icon+label row. */
.action-button a, .action-button > span {
  float: none;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
  /* The wrapper span opens the dropdown; the anchor inside runs the action.
     Left at its own size the anchor was a 13px target inside a 34px pill, so
     most of the pill silently did the other thing. Fill the pill — the caret
     stays outside it and keeps opening the menu. */
  align-self: stretch;
  flex: 1 1 auto;
  justify-content: center;
}
/* …and let it reach into the pill's own padding, so the whole pill runs the
   action rather than a ring of dead space around the icon. Where a caret
   shares the pill, only the left side extends — the caret keeps its side. */
.action-button > a {
  margin-left: -14px;
  padding-left: 14px;
}
.action-button:not(:has(> i.icon-caret-down)) > a {
  margin-right: -14px;
  padding-right: 14px;
}
.action-button a, .action-button a:hover {
  color: inherit;
  text-decoration: none;
}
/* Font Awesome 3's em box sits high against General Sans; nudge glyphs
   onto the text's cap-band centre (same correction the portal uses). */
.action-button i[class*="icon-"],
.button i[class*="icon-"],
.gp-refresh i[class*="icon-"],
.configureQ i[class*="icon-"] {
  position: relative;
  top: 0.06em;
  margin-right: 0;
}
.help-tip { color: var(--gp-ink-3); }

/* Text inputs panel-wide, matching the select below. The theme previously
   styled inputs only where it named them - #response_options, .form_table,
   the login box - so every screen added since has been rendering raw
   browser controls next to modernised ones. Anything scoped keeps winning
   on specificity, so this is a floor, not an override. */
input[type="text"],
input[type="search"],
input[type="email"],
input[type="url"],
input[type="tel"],
input[type="number"],
input[type="password"],
textarea {
  /* border-box or every width:100% control overflows its container by its
     own padding and border - 22px, which is exactly enough to stop anything
     lining up with anything. */
  box-sizing: border-box;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm);
  background: var(--gp-surface);
  color: var(--gp-ink);
  font-size: 13px;
  font-family: inherit;
}
input[type="text"],
input[type="search"],
input[type="email"],
input[type="url"],
input[type="tel"],
input[type="number"],
input[type="password"] {
  height: 32px;
  padding: 0 10px;
  line-height: normal;
}
textarea { padding: 8px 10px; line-height: 1.55; }

input[type="text"]:focus,
input[type="search"]:focus,
input[type="email"]:focus,
input[type="url"]:focus,
input[type="tel"]:focus,
input[type="number"]:focus,
input[type="password"]:focus,
textarea:focus {
  outline: 0;
  border-color: var(--gp-accent);
  box-shadow: 0 0 0 3px var(--gp-blue-ring);
}
input::placeholder, textarea::placeholder { color: var(--gp-ink-3); }

/* Native selects panel-wide: scp.css pins them to 24px, which clips any
   padded text. One modern control voice instead. */
select {
  height: 32px;
  line-height: normal;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm);
  background: var(--gp-surface);
  color: var(--gp-ink);
  padding: 0 8px;
  font-size: 13px;
}
/* A list box is as tall as its `size` says; the fixed height above is meant
   for the single-line control and crushes any <select multiple> to one row,
   which hides most of the options and all of the selected ones. */
select[multiple],
select[size]:not([size="0"]):not([size="1"]) {
  height: auto;
  padding: 4px;
}
select:focus {
  outline: 0;
  border-color: var(--gp-accent);
  box-shadow: 0 0 0 3px var(--gp-blue-ring);
}

/* Dropdown panels (jquery.dropdown + the hover menus). */
.action-dropdown, .noclick-dropdown {
  margin-top: 6px;
}
.action-dropdown ul, .noclick-dropdown ul {
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm);
  box-shadow: 0 14px 34px -18px rgba(27, 28, 25, 0.45);
  padding: 6px;
  min-width: 170px;
}
/* The rule above paints the <ul>, so a dropdown whose contents are a FORM
   rather than a list gets no surface at all - it renders transparent, with
   the page showing straight through it. Any menu without a list paints
   itself, so the next form-bearing dropdown does not have to remember. */
.action-dropdown:not(:has(ul)),
.noclick-dropdown:not(:has(ul)) {
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm);
  box-shadow: 0 14px 34px -18px rgba(27, 28, 25, 0.45);
  padding: 12px 14px;
  text-align: left;
  z-index: 60;
}
/* ...and a menu that mixes the two - demoted bar actions in a list, an inline
   form under them - fell between both rules. The <ul> took the surface and the
   form took none, so the form half rendered transparent and spilled over the
   page below the menu. Paint the CONTAINER and strip the list, so however a
   menu is built it is exactly one card. */
.action-dropdown:has(ul):has(form),
.noclick-dropdown:has(ul):has(form) {
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm);
  box-shadow: 0 14px 34px -18px rgba(27, 28, 25, 0.45);
  padding: 6px;
  min-width: 170px;
  z-index: 60;
}
/* The separator bleeds to the container's edge with a negative margin sized
   for the form-only menu's 14px padding. A mixed menu pads by 6, so the same
   rule overhung it by 8px a side. */
.action-dropdown:has(ul):has(form) .gp-ac-menu-sep,
.noclick-dropdown:has(ul):has(form) .gp-ac-menu-sep {
  margin-left: -6px;
  margin-right: -6px;
}
.action-dropdown:has(ul):has(form) > ul,
.noclick-dropdown:has(ul):has(form) > ul {
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding: 0;
  min-width: 0;
}

.action-dropdown ul li > a, .noclick-dropdown ul li > a {
  color: var(--gp-ink-2);
  border-radius: 7px;
  padding: 7px 10px;
  line-height: 1.3;
}
/* A menu row that has to POST is a <form><button>, not an <a> - the internal
   desk's status menu sets a status directly and needs the CSRF token. It has
   to be indistinguishable from the anchor rows beside it: a bare button
   arrives centred, with its own padding and its own chrome. */
.action-dropdown ul li > form { margin: 0; }
.action-dropdown ul li > form > button {
  display: block;
  width: 100%;
  box-sizing: border-box;
  text-align: left;
  background: none;
  border: 0;
  box-shadow: none;
  font: inherit;
  cursor: pointer;
  color: var(--gp-ink-2);
  border-radius: 7px;
  padding: 7px 10px;
  line-height: 1.3;
}
.action-dropdown ul li > form > button:hover {
  background-color: var(--gp-accent-tint);
  color: var(--gp-accent);
}
/* A hairline between groups inside a menu - the demoted bar actions above,
   the structural ones below. */
.action-dropdown ul li.gp-more-sep {
  height: 0;
  margin: 5px 4px;
  border-top: 1px solid var(--gp-line);
}

.action-dropdown ul li > a:hover,
.action-dropdown ul li.active > a:hover,
.noclick-dropdown ul li > a:hover,
.noclick-dropdown ul li.active > a:hover {
  background-color: var(--gp-accent-tint);
  color: var(--gp-accent) !important;   /* upstream: white!important on #08C */
}
.action-dropdown ul li.active > a {
  background-color: var(--gp-accent-tint);
  color: var(--gp-accent);
}
.action-dropdown ul li.danger > a:hover,
.customQ-dropdown ul li.danger > a:hover,
.noclick-dropdown ul li.danger > a:hover {
  background-color: var(--gp-err-bg);
  color: var(--gp-err) !important;
}

/* =========================================================================
   8. The find bar — #basic_search
   ========================================================================= */

/* Upstream paints #basic_search as a grey inset band (#f4f4f4, an inset
   shadow, a dotted rule and negative margins). The find bar is part of the
   content card here, so all of that comes off. */
#basic_search {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 12px;
  background: none;
  box-shadow: none;
  border: 0;
  border-radius: 0;
  margin: 0 0 16px;
  padding: 0;
}
#basic_search > div:not(.pull-right) { min-width: 0; flex: 1 1 auto; }
#basic_search form {
  order: -1;                /* markup puts the filters first */
  display: flex;
  align-items: center;
  flex-wrap: wrap;          /* on a phone, Advanced search wraps below */
  flex: 1 1 340px;
  min-width: 0;             /* shrink below the input's intrinsic width */
  gap: 10px;
}
#basic_search form .valign-helper { display: none; }
#basic_search .input.attached {
  display: flex;
  height: var(--gp-ctl-h-lg);
  flex: 1 1 240px;
  min-width: 0;             /* the input's size="30" must not set a floor */
  max-width: 560px;
  margin: 0;
}
#basic_search .input.attached input {
  flex: 1;
  min-width: 0;
  height: 100%;
  font-size: 14px;
  color: var(--gp-ink);
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-right: 0;
  border-radius: var(--gp-r-sm) 0 0 var(--gp-r-sm);
  padding: 0 14px;
  transition: border-color var(--gp-t-color), box-shadow var(--gp-t-color);
}
/* The focus ring belongs to the whole search control, not just the text box
   — the submit button is fused to it and was being left outside the glow. */
#basic_search .input.attached {
  border-radius: var(--gp-r-sm);
  transition: box-shadow var(--gp-t-color);
}
#basic_search .input.attached:focus-within {
  box-shadow: 0 0 0 3px var(--gp-blue-ring);
}
#basic_search .input.attached:focus-within input { border-color: var(--gp-accent); }
#basic_search .input.attached:focus-within .button.attached {
  border-color: var(--gp-accent);
}
#basic_search .input.attached input:focus {
  outline: 0;
  border-color: var(--gp-accent);
  box-shadow: none;
}
#basic_search .input.attached.focus { outline: none; }
#basic_search .button.attached {
  height: var(--gp-ctl-h-lg);
  min-height: var(--gp-ctl-h-lg);
  margin: 0;
  padding: 0 16px;
  border: 1px solid var(--gp-accent);
  border-radius: 0 var(--gp-r-sm) var(--gp-r-sm) 0;
  background: var(--gp-accent);
  color: #fff;
  box-shadow: none !important;   /* upstream: none!important too — keep */
  font-size: 13.5px;
}
#basic_search .button.attached:hover {
  background: var(--gp-accent-strong);
  border-color: var(--gp-accent-strong);
  color: #fff;
}
/* "Advanced search" — the bare bracketed link becomes a real secondary
   button. Targets the element, so it holds before the label pass lands. */
#basic_search form > a {
  display: inline-flex;
  align-items: center;
  height: var(--gp-ctl-h);
  padding: 0 14px;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-pill);
  background: var(--gp-surface);
  color: var(--gp-ink-2);
  font-size: 13px;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--gp-t-color), color var(--gp-t-color),
              border-color var(--gp-t-color);
}
#basic_search form > a:hover {
  background: var(--gp-accent-tint);
  border-color: var(--gp-accent-edge);
  color: var(--gp-accent);
}

/* Quick filter and Sort read as controls, not grey noise. The template
   already prints "Label: Choice", so this is pure skin. */
#basic_search > .pull-right {
  float: none;
  order: 1;
  height: auto !important;    /* the template inlines height:25px */
  flex: 0 1 auto;             /* may shrink so it fits a phone */
  min-width: 0;
  flex-wrap: wrap;
  margin-left: 0;             /* one left-aligned cluster on every list:
                                 search, then its filters, then sort — never
                                 a control floating at the far edge on one
                                 page and beside the search on the next */
  display: flex;
  align-items: center;
  gap: 8px;
}
#basic_search > .pull-right .valign-helper { display: none; }
#basic_search span.action-button.muted {
  margin: 0;
  color: var(--gp-ink-2);   /* the quick-filter/sort SPANS only — the
                               dashboard's Refresh is a real submit and
                               keeps the primary voice */
}

/* =========================================================================
   9. The queue bar — name, refresh, settings, mass actions
   ========================================================================= */

.sticky.bar .content {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 14px;
  /* scp.js measures the bar once and writes an inline width on .content;
     with this card's padding that overflowed the shell by 34px (and went
     stale on every resize). Inline styles lose to !important. */
  width: auto !important;
}
.sticky.bar:not(.fixed) .content {
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r);
  padding: 12px 16px;
}
.sticky.bar .content .pull-left,
.sticky.bar .content .pull-right {
  float: none;
}
.sticky.bar .content .flush-left {
  display: flex;
  align-items: center;
  gap: 4px;
  order: -1;   /* the ticket view emits .flush-right BEFORE .flush-left;
                  the title always reads first regardless */
}
.sticky.bar .content .flush-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  justify-content: flex-end;   /* a wrapped action row still hugs the right,
                                  so the "select to act" hint sits beside its
                                  buttons on every list, not at the far left */
  flex-wrap: wrap;
  gap: 6px;
}
.sticky.bar h2 {
  margin: 0;
  font-size: 19px;
  font-weight: 500;
  letter-spacing: -0.02em;
}
.sticky.bar h2 a {
  color: var(--gp-ink);
  text-decoration: none;
}
.sticky.bar h2 a:hover { color: var(--gp-accent); }
.sticky.bar h2 .icon-refresh,
.sticky.bar h2 .icon-filter {
  font-size: 0.7em;
  color: var(--gp-ink-3);
  vertical-align: 0.15em;
  margin-right: 5px;
}

/* The labelled Refresh button the template pass adds beside the name. */
.gp-refresh {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: var(--gp-ctl-h);
  padding: 0 13px;
  margin-left: 10px;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-pill);
  background: var(--gp-surface);
  color: var(--gp-ink-2) !important;
  font-size: 13px;
  font-weight: 500;
  text-decoration: none !important;
  vertical-align: middle;
  transition: background var(--gp-t-color), color var(--gp-t-color),
              border-color var(--gp-t-color);
}
.gp-refresh:hover {
  background: var(--gp-accent-tint);
  border-color: var(--gp-accent-edge);
  color: var(--gp-accent) !important;
}

/* Queue settings — the bare cog becomes a labelled button with a caret.
   The hover-dropdown contract (.configureQ > .noclick-dropdown) survives. */
.configureQ {
  float: none;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: var(--gp-ctl-h);
  padding: 0 13px;
  margin: 0;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-pill);
  background: var(--gp-surface);
  color: var(--gp-ink-2);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  position: relative;
  transition: background var(--gp-t-color), color var(--gp-t-color),
              border-color var(--gp-t-color);
}
.configureQ:hover {
  background: var(--gp-accent-tint);
  border-color: var(--gp-accent-edge);
  color: var(--gp-accent);
}
/* Click, not hover. Upstream drops this menu open when the pointer merely
   crosses the button (scp.css, .configureQ:hover > .noclick-dropdown);
   every other menu in the panel opens on click, and scp-theme.js toggles
   .gp-open. Both rules carry the same specificity as upstream's and come
   after it, so order alone decides: hover is silenced, .gp-open shows. */
.configureQ:hover > .noclick-dropdown { display: none; }
.configureQ.gp-open > .noclick-dropdown { display: block; }
.configureQ > .noclick-dropdown {
  top: 100%;
  margin-top: 2px;
  margin-left: -13px;
  padding-top: 6px;
}
.configureQ.gp-open {
  background: var(--gp-accent-tint);
  border-color: var(--gp-accent-edge);
  color: var(--gp-accent);
}
.configureQ.gp-open .gp-caret { transform: rotate(180deg); }
/* The caret the label pass adds after the label. */
.configureQ .gp-caret { opacity: 0.55; font-size: 0.85em; }

/* --- One order for every toolbar ---------------------------------------
   The queue lists its actions in DOM order; the ticket view relied on
   float:right, which reverses them — and floats do nothing in a flex row,
   so the pages disagreed about where Assign or Transfer lived. State
   changes lead, then the page's own verbs, then utilities. Each control
   names its verb with a .gp-verb-* class (ticket, task, internal and issue
   views alike), so the four bars agree without a per-page rule. The queue's
   mass-action bar keeps its id-keyed rules below — those buttons come from
   upstream partials and have no verb class yet. */
.sticky.bar .flush-right > * { order: 6; }
.sticky.bar .flush-right .gp-selcount-bar { order: 0; }
.sticky.bar .flush-right .gp-verb-status    { order: 1; }
.sticky.bar .flush-right .gp-verb-assign    { order: 2; }
.sticky.bar .flush-right .gp-verb-transfer  { order: 3; }
/* Referrals routes the record like Transfer does, so it sits beside it. */
.sticky.bar .flush-right .gp-verb-referrals,
.sticky.bar .flush-right .gp-verb-convert   { order: 4; }
.sticky.bar .flush-right .gp-verb-post,
.sticky.bar .flush-right .gp-verb-merge     { order: 5; }
.sticky.bar .flush-right .gp-verb-link,
.sticky.bar .flush-right .gp-verb-task,
.sticky.bar .flush-right .gp-verb-other     { order: 6; }
.sticky.bar .flush-right .gp-verb-print     { order: 7; }
.sticky.bar .flush-right .gp-verb-edit      { order: 8; }
.sticky.bar .flush-right .gp-verb-more      { order: 9; }
.sticky.bar .flush-right .gp-verb-delete    { order: 10; }
/* Inline forms wrapping a verb button (internal, issue) take the button's order. */
.sticky.bar .flush-right form:has(> .gp-verb-status)  { order: 1; }
.sticky.bar .flush-right form:has(> .gp-verb-delete)  { order: 10; }
/* The queue's mass-action bar (tickets-actions.tmpl.php, tasks-actions.tmpl.php). */
.sticky.bar .flush-right span.action-button:has(> a[href="#statuses"]) { order: 1; }
.sticky.bar .flush-right span.action-button:has(> a#tickets-assign) { order: 2; }
/* No rules for #tickets-transfer / #tickets-merge / #tickets-link: all three
   left the queue bar. Merge and Link are merge-family and destructive;
   Transfer routes a single record. A list bar carries only Change Status,
   Assign, Link and Delete — everything else lives on the record's own page.
   See docs/PRD-record-linking.md §7. */
.sticky.bar .flush-right span.action-button[data-dropdown="#action-dropdown-more"],
.sticky.bar .flush-right span.action-button:has(> a.tasks-action[href="#moreoptions"]) { order: 9; }
.sticky.bar .flush-right a#tickets-delete,
.sticky.bar .flush-right span.action-button:has(> a#tasks-delete) { order: 10; }

/* --- Mass actions: honest disabled state, pure CSS ----------------------
   tickets-actions.tmpl.php already no-ops on an empty selection
   (checkbox_checker($form, 1) bails) — this only makes that visible.
   The bar sits OUTSIDE form#tickets, so the shared ancestor is #content.
   No JS: pjax swaps #pjax-container wholesale, so a scripted class would
   desync from the real checkbox state on every reload. */
/* Only MASS-action controls grey out, and only on pages that actually have
   a mass-selection form (form#tickets with .ckb boxes — the queue). The
   ticket detail view has no #tickets form, so its toolbar — including the
   Change Status trigger, which carries .tickets-action even there — never
   freezes. */
/* Muted, not ghosted. Half opacity washed the whole row out until it read as
   a rendering fault rather than as "pick something first" — the labels were
   barely legible against the bar. A disabled control should still be
   readable; it is the colour that says it is not available yet. */
#content:has(form#tickets input.ckb):not(:has(form#tickets input.ckb:checked))
  .sticky.bar .flush-right
  :is(a.action-button.tickets-action, span.action-button:has(> a.tickets-action),
      span.action-button[data-dropdown]),
#content:has(form.gp-masslist input.ckb):not(:has(form.gp-masslist input.ckb:checked))
  .sticky.bar .flush-right :is(.gp-mass-action, span.action-button[data-dropdown]) {
  /* Unmistakably inactive: no fill, a hairline border and grey ink. The
     filled version read as a live button wearing a slightly paler label. */
  opacity: 1;
  background: none;
  border-color: var(--gp-line-soft);
  border-style: dashed;
  color: var(--gp-ink-3);
  box-shadow: none;
  pointer-events: none;
}
/* The queue's split buttons wrap an anchor that carries its own colour, and
   Delete carries the danger red; both mute with the rest. */
#content:has(form#tickets input.ckb):not(:has(form#tickets input.ckb:checked))
  .sticky.bar .flush-right
  :is(a.action-button.tickets-action, span.action-button:has(> a.tickets-action),
      span.action-button[data-dropdown]) :is(a, i),
#content:has(form.gp-masslist input.ckb):not(:has(form.gp-masslist input.ckb:checked))
  .sticky.bar .flush-right :is(.gp-mass-action, span.action-button[data-dropdown]) i {
  color: var(--gp-ink-3);
}
#content:has(form.gp-masslist input.ckb:checked) .gp-selcount { display: inline-flex; }

/* =========================================================================
   10. The queue table
   ========================================================================= */

/* The template hardcodes width="940" on the table; the attribute stays (a
   label-scope decision) and CSS overrides it. */
/* .gp-list shares the queue's treatment rather than imitating it. Any list
   that copies these numbers drifts from them the first time the queue is
   adjusted; sharing the selector means it cannot. */
#tickets table.list,
table.gp-list {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r);
  background: var(--gp-surface);
  margin: 0;
  font-size: 13.5px;
}

/* Uppercase tracked micro-labels for column headers; no gif sort arrows. */
#tickets table.list thead th,
table.gp-list thead th {
  background: var(--gp-surface-2);
  border: 0;
  border-bottom: 1px solid var(--gp-line);
  padding: 10px 12px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
  white-space: nowrap;
}
#tickets table.list thead th:first-child,
table.gp-list thead th:first-child { border-top-left-radius: var(--gp-r); }
#tickets table.list thead th:last-child,
table.gp-list thead th:last-child { border-top-right-radius: var(--gp-r); }
#tickets table.list thead th a {
  background: none;
  padding-right: 0;
  color: inherit;
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
#tickets table.list thead th a:hover { color: var(--gp-ink); }
#tickets table.list thead th a.asc::after,
#tickets table.list thead th a.desc::after {
  font-size: 8px;
  line-height: 1;
  color: var(--gp-accent);
}
#tickets table.list thead th a.asc::after { content: "\25B2"; }
#tickets table.list thead th a.desc::after { content: "\25BC"; }

/* Rows: quiet grid, hover state, checked tint. No zebra — the hairlines
   carry the rhythm. */
/* The row owns its background — every cell is transparent, including the
   priority cell whose inline background-color would otherwise stay white
   in a hovered or checked row. */
#tickets table.list tbody tr,
table.gp-list tbody tr { background: var(--gp-surface); }
#tickets table.list tbody td,
table.gp-list tbody td {
  background: none !important;
  border: 0;
  border-bottom: 1px solid var(--gp-line-soft);
  padding: 11px 12px;
  vertical-align: middle;
}
#tickets table.list tbody tr,
table.gp-list tbody tr { transition: background var(--gp-t-color); }
/* Hover is the cream wash; a checked row is a state, so it takes the sage
   tint — the only place sage lands under body text. */
#tickets table.list tbody tr:hover,
#tickets table.list tbody tr.highlight,
table.gp-list tbody tr:hover { background: var(--gp-page); }
#tickets table.list tbody tr:has(.ckb:checked),
#tickets table.list tbody tr:has(.ckb:checked):hover { background: var(--gp-brand-tint); }
/* Checkboxes are a primary control on a queue — 15px was a poor target.
   The whole cell is clickable (scp-theme.js toggles from anywhere in it). */
#content table.list input[type="checkbox"] { margin: 0; vertical-align: middle; }
/* The cell is the hit target, not just the 18px box. */
#content table.list tbody td:has(> input[type="checkbox"]),
#content table.list thead th:has(> input[type="checkbox"]) {
  cursor: pointer;
  padding-left: 14px;
  padding-right: 10px;
  width: 42px;
}
#tickets table.list tbody td a,
table.gp-list tbody td a {
  display: inline;
  color: var(--gp-ink);
  text-decoration: none;
}
#tickets table.list tbody td a:hover,
table.gp-list tbody td a:hover {
  color: var(--gp-accent);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Ticket numbers read as record locators; dates align down the column. */
#tickets tbody td a.preview,
#tickets tbody td time {
  font-family: var(--gp-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  font-size: 12.5px;
  color: var(--gp-ink-2);
}
/* date:full renders plain text — key the stock Last Updated / Date Created
   columns off their stable ids and positions (guarded, so a rearranged
   custom queue simply keeps proportional text). */
#tickets table.list:has(thead th[data-id="10"]:nth-child(3)) tbody td:nth-child(3),
#tickets table.list:has(thead th[data-id="2"]:nth-child(3)) tbody td:nth-child(3) {
  font-family: var(--gp-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  font-size: 12.5px;
  color: var(--gp-ink-2);
  white-space: nowrap;
}

/* Thread-count / attachment / collaborator annotations in the subject. */
#tickets tbody td .pull-right.faded,
#tickets tbody td small.faded { color: var(--gp-ink-3); }

/* --- upstream sprite decorations ---------------------------------------
   .Icon spans are 1990s gif sprites (ticket source, assignee kind, locks).
   Replace the sprite with a Font Awesome glyph so they scale, inherit
   colour, and survive a dark row tint. */
#content .Icon {
  background-image: none !important;
  padding-left: 0;
  min-height: 0;
}
#content .Icon[class*="Ticket"]::before,
#content .Icon.staffAssigned::before,
#content .Icon.teamAssigned::before {
  font-family: FontAwesome;
  font-style: normal;
  font-weight: normal;
  font-size: 0.9em;
  color: var(--gp-ink-3);
  margin-right: 6px;
  display: inline-block;
}
#content .Icon.webTicket::before    { content: "\f0ac"; }  /* globe */
#content .Icon.emailTicket::before  { content: "\f0e0"; }  /* envelope */
#content .Icon.phoneTicket::before  { content: "\f095"; }  /* phone */
#content .Icon.apiTicket::before,
#content .Icon.otherTicket::before  { content: "\f0ec"; }  /* exchange */
#content .Icon.staffAssigned::before { content: "\f007"; } /* user */
#content .Icon.teamAssigned::before  { content: "\f0c0"; } /* group */
#content .Icon.lockedTicket::before  { content: "\f023"; color: var(--gp-warn); }

/* On the tasks queue the overdue/locked classes ride on the title LINK
   (not an empty span): keep the link, swap the gif for a dot. */
#content a.Icon.overdueTicket,
#content a.Icon.lockedTicket {
  background-image: none;
  padding-left: 0;
  min-height: 0;
}
#content a.Icon.overdueTicket::before {
  content: '';
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--gp-err);
  margin-right: 6px;
  vertical-align: 0.05em;
}
#content a.Icon.lockedTicket::before {
  content: '';
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--gp-warn);
  margin-right: 6px;
  vertical-align: 0.05em;
}
/* Overdue: the OverdueFlagDecoration span becomes an unmissable dot.
   Upstream .Icon carries sprite metrics (padding-left:20px, min-height:16px)
   that must be zeroed or the dot renders as a blob. */
#tickets .Icon.overdueTicket {
  display: inline-block;
  width: 8px;
  height: 8px;
  min-height: 0;
  padding: 0;
  margin-right: 5px;
  border-radius: 50%;
  background: var(--gp-err) !important;
  vertical-align: 0.05em;
}

/* --- Priority: a stripe that scans, a pill that names --------------------
   Cells carry only inline background-color styles; the stock palette
   (priority.yaml) is the contract:  Low #DDFFDD · Normal #FFFFF0(A, an
   upstream typo browsers drop — the attribute text still matches) ·
   High/Emergency #FEE7E7 (upstream ships them identical).  Custom colours
   fall back to the admin's own cell tint, untouched. */

/* A stripe means "this one is urgent". Low priority gets none — a pale
   tick beside the checkbox read as a rendering artefact, not a signal. */
#tickets tbody tr:has(td[style*="#FEE7E7" i]) td:first-child {
  box-shadow: inset 3px 0 0 var(--gp-err);
}

/* The cell stops shouting; the pill inside names the level. */
#tickets tbody td[style*="#FEE7E7" i],
#tickets tbody td[style*="#DDFFDD" i],
#tickets tbody td[style*="#FFFFF0" i] {
  background-color: transparent !important;
}
#tickets tbody td[style*="#FEE7E7" i] > div,
#tickets tbody td[style*="#DDFFDD" i] > div,
#tickets tbody td[style*="#FFFFF0" i] > div {
  display: inline-block;
  padding: 2px 10px;
  border-radius: var(--gp-r-pill);
  border: 1px solid;
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
#tickets tbody td[style*="#FEE7E7" i] > div {
  background: var(--gp-err-bg) !important;
  border-color: var(--gp-err-edge);
  color: var(--gp-err);
}
#tickets tbody td[style*="#DDFFDD" i] > div {
  background: var(--gp-ok-bg) !important;
  border-color: var(--gp-ok-edge);
  color: var(--gp-ok);
}
#tickets tbody td[style*="#FFFFF0" i] > div {
  background: var(--gp-surface-2) !important;
  border-color: var(--gp-line);
  color: var(--gp-ink-2);
}

/* --- Table foot: select controls and the live selection count ----------- */
#tickets table.list tfoot td {
  background: var(--gp-surface-2);
  border: 0;
  border-top: 1px solid var(--gp-line);
  border-radius: 0 0 var(--gp-r) var(--gp-r);
  padding: 10px 14px;
  font-size: 12.5px;
  color: var(--gp-ink-3);
}
#tickets tfoot a {
  color: var(--gp-accent);
  font-weight: 500;
  text-decoration: none;
  padding: 4px 8px;
  border-radius: 7px;
}
#tickets tfoot a:hover { background: var(--gp-accent-tint); }

/* CSS counter over the checked boxes — the display element must follow
   them in document order, which is why the count lives in the foot. */
/* Hidden until something is checked, on every list rather than only the
   ticket queue -- the old rule named #tickets and so the count never showed
   on tasks at all. The number itself is painted by scp-theme.js. */
.gp-selcount { display: none; }
#content form:has(input.ckb:checked) .gp-selcount {
  display: inline-flex;
  align-items: center;
  padding: 3px 11px;
  border-radius: var(--gp-r-pill);
  background: var(--gp-accent-tint);
  color: var(--gp-accent);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.gp-selcount-n { margin-right: 4px; }

/* =========================================================================
   11. Pager & export
   ========================================================================= */

#tickets > div:last-of-type,
#tickets > div:last-child {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 10px;
  margin-top: 14px;
  font-size: 13px;
  color: var(--gp-ink-3);
}
#tickets > div .faded.pull-right {
  float: none;
  order: 9;
  margin-left: auto;
  font-family: var(--gp-mono);
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
}
/* Page numbers as buttons; the current page is the filled one. */
#tickets > div > a:not(#queue-export) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  height: 32px;
  padding: 0 9px;
  border-radius: var(--gp-r-sm);
  color: var(--gp-ink-2);
  text-decoration: none;
  font-variant-numeric: tabular-nums;
  transition: background var(--gp-t-color), color var(--gp-t-color);
}
#tickets > div > a:not(#queue-export) b { font-weight: 400; }
#tickets > div > a:not(#queue-export):hover {
  background: var(--gp-surface-2);
  color: var(--gp-ink);
}
/* class.pagenate.php span-wraps its historical [ ] so the theme can drop
   them; the client portal keeps rendering them. */
.gp-bracket { display: none; }
#tickets > div > b {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  height: 32px;
  padding: 0 9px;
  border-radius: var(--gp-r-sm);
  background: var(--gp-accent);
  color: #fff;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
/* Export says what it produces (label pass); meanwhile it reads as a
   secondary button either way. */
#tickets a#queue-export {
  display: inline-flex;
  align-items: center;
  height: 32px;
  padding: 0 13px;
  margin-left: 8px;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-pill);
  background: var(--gp-surface);
  color: var(--gp-ink-2);
  font-size: 12.5px;
  font-weight: 500;
  text-decoration: none;
  transition: background var(--gp-t-color), color var(--gp-t-color),
              border-color var(--gp-t-color);
}
#tickets a#queue-export:hover {
  background: var(--gp-accent-tint);
  border-color: var(--gp-accent-edge);
  color: var(--gp-accent);
}

/* =========================================================================
   12. Responsive — 2560 down to 320 with no horizontal scroll
   ========================================================================= */

/* Custom column sets that don't match the stock drops below scroll inside
   the form instead of pushing the page sideways. */
@media (min-width: 721px) and (max-width: 1240px) {
  #tickets, #tasks, #users-list, #orgs-list { display: block; overflow-x: auto; }
}
/* Any other list or stats table (admin pages, embeds) scrolls in place on a
   phone rather than pushing the page sideways. */
@media (max-width: 720px) {
  #content form:not(#tickets):not(#tasks):not(#users-list):not(#orgs-list):has(table.list) {
    display: block;
    overflow-x: auto;
  }
  #content .tab_content:has(> table.dashboard-stats) { overflow-x: auto; }
  /* jquery.dialog centres by writing an inline pixel `left`, computed for a
     desktop-width dialog — on a phone that lands off-screen (left:-130px for
     a 650px panel in a 390px viewport). Pin it to the gutters instead. */
  .dialog, #popup {
    left: 12px !important;
    right: 12px;
    width: auto !important;
    max-width: none;
    padding: 18px 16px;
  }

  /* Two-column layout tables (profile, several admin forms) stack rather
     than forcing the document sideways. */
  #content table.two-column,
  #content table.two-column > tbody,
  #content table.two-column > tbody > tr,
  #content table.two-column > tbody > tr > td {
    display: block;
    width: 100% !important;
  }

  /* The ticket view's metadata halves stack, and the label column gives
     its width back to the values (an email has nowhere to break). */
  table.ticket_info > tbody > tr { display: flex; flex-direction: column; }
  table.ticket_info > tbody > tr > td { width: 100%; }
  table.ticket_info table th,
  .ticket_info th {
    width: 96px;
    padding-right: 6px;
    font-size: 12px;
  }
  table.ticket_info.custom-data td:first-child { width: 96px; }
}

/* Columns drop in priority order, keyed to the STOCK column layout and
   guarded so a rearranged queue is left alone: From (User Name, id 4,
   5th cell counting the checkbox), then Last Updated (id 10, 3rd cell). */
@media (min-width: 721px) and (max-width: 1240px) {
  #tickets table.list:has(thead th[data-id="4"]:nth-child(5)) th:nth-child(5),
  #tickets table.list:has(thead th[data-id="4"]:nth-child(5)) tbody td:nth-child(5) {
    display: none;
  }
}
@media (min-width: 721px) and (max-width: 1040px) {
  #tickets table.list:has(thead th[data-id="10"]:nth-child(3)) th:nth-child(3),
  #tickets table.list:has(thead th[data-id="10"]:nth-child(3)) tbody td:nth-child(3) {
    display: none;
  }
}

@media (max-width: 900px) {
  :root { --gp-scp-gutter: 18px; }
  #content { padding: 16px 14px 22px; }
}

/* Below 860px button labels collapse to icons; the text stays for screen
   readers (visually hidden, not display:none). Tooltips are still there. */
@media (max-width: 860px) {
  .gp-btn-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
  /* The two identity-bearing labels stay visible. */
  #basic_search .button.attached .gp-btn-label,
  .configureQ .gp-btn-label {
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    clip: auto;
    clip-path: none;
    overflow: visible;
  }
  /* The nav is a drawer below the rail breakpoint (§5); nothing to do here. */
}

@media (max-width: 720px) {
  /* The queue list lives in the drawer below the rail breakpoint (§5). */

  /* Each row becomes a card. Every cell is shown again — the mid-width
     column drops only apply where the table is still a grid. */
  #content :is(#tickets, #tasks, #users-list, #orgs-list) table.list,
  #content :is(#tickets, #tasks, #users-list, #orgs-list) table.list thead,
  #content :is(#tickets, #tasks, #users-list, #orgs-list) table.list tbody,
  #content :is(#tickets, #tasks, #users-list, #orgs-list) table.list tfoot,
  #content :is(#tickets, #tasks, #users-list, #orgs-list) table.list tr,
  #content :is(#tickets, #tasks, #users-list, #orgs-list) table.list td {
    display: block;
    width: 100%;
    box-sizing: border-box;
  }
  #content :is(#tickets, #tasks, #users-list, #orgs-list) table.list { border: 0; background: none; }
  #content :is(#tickets, #tasks, #users-list, #orgs-list) table.list thead { display: none; }
  #content :is(#tickets, #tasks, #users-list, #orgs-list) table.list tbody tr {
    position: relative;
    background: var(--gp-surface);
    border: 1px solid var(--gp-line);
    border-radius: var(--gp-r-sm);
    margin-bottom: 8px;
    padding: 12px 14px 12px 40px;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 12px;
  }
  #content :is(#tickets, #tasks, #users-list, #orgs-list) table.list tbody td {
    width: auto;
    border: 0;
    padding: 0;
    background: none !important;
  }
  #content :is(#tickets, #tasks, #users-list, #orgs-list) table.list tbody tr:hover td { background: none !important; }
  #content :is(#tickets, #tasks, #users-list, #orgs-list) table.list tbody tr:hover {
    background: var(--gp-page);
  }
  #content :is(#tickets, #tasks, #users-list, #orgs-list) table.list tbody tr:has(.ckb:checked) {
    background: var(--gp-brand-tint);
  }
  /* The checkbox pins top-left; the priority stripe moves to the card. */
  #content :is(#tickets, #tasks, #users-list, #orgs-list) table.list tbody td:has(> .ckb) {
    position: absolute;
    left: 12px;
    top: 14px;
    width: auto;
    box-shadow: none !important;
  }
  #content #tickets tbody tr:has(td[style*="#FEE7E7" i]) {
    box-shadow: inset 3px 0 0 var(--gp-err);
  }
  /* Subject (stock position) takes the full card width and may wrap. */
  #content #tickets table.list:has(thead th[data-id="1"]:nth-child(2)) tbody td:nth-child(2) {
    flex: 0 0 auto;
    font-weight: 500;
  }
  #content #tickets table.list:has(thead th[data-id="10"]:nth-child(3)) tbody td:nth-child(3) {
    flex: 0 0 auto;
    margin-left: auto;
  }
  #content #tickets table.list:has(thead th[data-id="3"]:nth-child(4)) tbody td:nth-child(4) {
    flex: 1 1 100%;
    font-weight: 500;
    white-space: normal;
  }
  #content :is(#tickets, #tasks, #users-list, #orgs-list) table.list tbody td[style] > div { white-space: normal; }
  /* The column renderer stamps a server-computed max-width on truncated
     cells (max-width:275px for a subject). In a card that is wider than the
     card itself, so the subject clipped instead of wrapping. */
  #content table.list .truncate {
    display: inline;          /* an inline-BLOCK cannot share a line with the
                                 overdue dot, so it dropped beneath it */
    white-space: normal;
    overflow: visible;
  }
  #content :is(#tickets, #tasks, #users-list, #orgs-list) table.list tfoot td { border-radius: var(--gp-r-sm); }

  /* The queue bar and find bar stack. */
  .sticky.bar .content .flush-right {
    margin-left: 0;
    width: 100%;
  }
  #basic_search > .pull-right {
    margin-left: 0;
    flex-wrap: wrap;
  }
  #basic_search .input.attached { max-width: none; }
}

@media (max-width: 480px) {
  #header p#info a { padding: 0 7px; }
}

/* =========================================================================
   13. Motion preferences
   ========================================================================= */

@media (prefers-reduced-motion: reduce) {
  .action-button, .button, #nav a, #sub_nav a, #tickets tbody td,
  .gp-refresh, .configureQ, #basic_search .input.attached input {
    transition: none;
  }
}

/* =========================================================================
   14. The rest of the agent panel — one treatment for every list page
   =========================================================================
   Tickets got the first pass; tasks (scp/tasks.php), the user directory
   (scp/users.php), organizations (scp/orgs.php) and the dashboard reuse the
   same idioms with small markup deltas (mapped in git history):
     - tasks:      form#tasks,      table.list, ckb[name="tids[]"]
     - users:      form#users-list, table.list, ckb.mass (nameless)
     - orgs:       form#orgs-list,  table.list, ckb.mass (nameless)
     - dashboard:  one bare form, table.dashboard-stats, Raphael SVG chart
   Admin-panel lists share table.list and inherit this for free (accepted).
   ========================================================================= */

/* --- every list table gets the card treatment --------------------------- */
#content table.list {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r);
  background: var(--gp-surface);
  margin: 0 0 6px;
  font-size: 13.5px;
}
#content table.list caption {
  background: var(--gp-surface-2);
  border: 0;
  border-bottom: 1px solid var(--gp-line);
  padding: 10px 14px;
  font-weight: 500;
}
#content table.list thead th {
  background: var(--gp-surface-2);
  border: 0;
  border-bottom: 1px solid var(--gp-line);
  padding: 10px 12px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
  white-space: nowrap;
}
#content table.list thead th:first-child { border-top-left-radius: var(--gp-r); }
#content table.list thead th:last-child { border-top-right-radius: var(--gp-r); }
#content table.list thead th a {
  background: none;
  padding-right: 0;
  color: inherit;
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
#content table.list thead th a:hover { color: var(--gp-ink); }
#content table.list thead th a.asc::after,
#content table.list thead th a.desc::after {
  font-size: 8px;
  line-height: 1;
  color: var(--gp-accent);
}
#content table.list thead th a.asc::after { content: "\25B2"; }
#content table.list thead th a.desc::after { content: "\25BC"; }
#content table.list tbody tr {
  background: var(--gp-surface);
  transition: background var(--gp-t-color);
}
#content table.list tbody td {
  background: none !important;
  border: 0;
  border-bottom: 1px solid var(--gp-line-soft);
  padding: 11px 12px;
  vertical-align: middle;
}
/* Compact density on a column list. The card lists say "compact" by folding
   their second line away (.gp-rrow-sub, further down); a column list has no
   second line to fold, so it says it in the only place it can — the row
   height. :not(.gp-cards) keeps this off the card tables, which set their
   own padding on .gp-rrow-main and would otherwise inherit 5px here for any
   cell that is not one of the two they name. */
#content table.list.gp-compact:not(.gp-cards) tbody td {
  padding-top: 5px;
  padding-bottom: 5px;
}
#content table.list.gp-compact:not(.gp-cards) thead th {
  padding-top: 7px;
  padding-bottom: 7px;
}

/* The knowledgebase browser is a <ul id="kb">, not a table, so its compact
   mode is the card lists' fold rather than the column lists' row height: a
   category's prose description is its second line, and prose is what makes
   that page long. The heading, the count and the child categories stay -
   they are how you get anywhere from here. */
#kb.gp-compact li { padding-top: 6px; padding-bottom: 6px; }
#kb.gp-compact li .gp-kb-desc { display: none; }

#content table.list tbody tr:hover,
#content table.list tbody tr.highlight { background: var(--gp-page); }
#content table.list tbody tr:has(input.ckb:checked),
#content table.list tbody tr:has(input.ckb:checked):hover { background: var(--gp-brand-tint); }

#content table.list tbody td a {
  display: inline;          /* scp.css makes every <a> inline-block, which
                               stops a long subject from sharing a line with
                               its overdue dot or count */
  color: var(--gp-ink);
  text-decoration: none;
}
#content table.list tbody td a:hover {
  color: var(--gp-accent);
  text-decoration: underline;
  text-underline-offset: 3px;
}
#content table.list tfoot td {
  background: var(--gp-surface-2);
  border: 0;
  border-top: 1px solid var(--gp-line);
  border-radius: 0 0 var(--gp-r) var(--gp-r);
  padding: 10px 14px;
  font-size: 12.5px;
  color: var(--gp-ink-3);
}
#content table.list tfoot a {
  color: var(--gp-accent);
  font-weight: 500;
  text-decoration: none;
  padding: 4px 8px;
  border-radius: 7px;
}
#content table.list tfoot a:hover { background: var(--gp-accent-tint); }
/* Rows that a click can open (scp-theme.js follows the row's own link). */
#content table.list tbody tr:has(td a[href]),
#content table.list tbody tr[data-href] { cursor: pointer; }

/* The column renderer stamps a server-computed max-width on truncated cells
   (max-width:275px for a subject), sized for upstream's fixed 940px table.
   The console's table is fluid, so that cap clipped subjects while the
   column still had room — let the cell decide, and keep the ellipsis.
   The cell becomes a row so the truncating text shares its line with any
   decoration in front of it (an overdue dot, a lock) rather than being
   pushed underneath by a full-width inline-block. */
#content table.list td > div:has(> .truncate) {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
#content table.list .truncate {
  max-width: 100% !important;
  min-width: 0;
  flex: 0 1 auto;
}

/* Decoration wrappers in list cells are floated by upstream, which drops
   the subject onto the next line beside them. They are single glyphs — let
   them sit inline with the text they annotate. */
#content table.list td span.pull-left {
  float: none;
  display: inline;
}

/* Record numbers in list tables read as record locators — tickets and
   tasks only; on the user directory the preview link is a person's name. */
:is(#tickets, #tasks) table.list td a.preview {
  font-family: var(--gp-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  font-size: 12.5px;
  color: var(--gp-ink-2);
}

/* Selection counting works on every mass-select form. */
#content form#tasks,
#content form#users-list,
#content form#orgs-list { counter-reset: gp-checked; }
#content form input.ckb:checked { counter-increment: gp-checked; }

/* Mass actions on the tasks queue read exactly like the tickets queue and
   the two GP lists: muted, not ghosted, so the labels stay legible while
   they wait for a selection. */
#content:has(form#tasks input.ckb):not(:has(form#tasks input.ckb:checked))
  .sticky.bar .flush-right
  :is(a.action-button.tasks-action, span.action-button:has(> a.tasks-action),
      span.action-button[data-dropdown]) {
  opacity: 1;
  background: none;
  border-color: var(--gp-line-soft);
  border-style: dashed;
  color: var(--gp-ink-3);
  box-shadow: none;
  pointer-events: none;
}
#content:has(form#tasks input.ckb):not(:has(form#tasks input.ckb:checked))
  .sticky.bar .flush-right
  :is(a.action-button.tasks-action, span.action-button:has(> a.tasks-action),
      span.action-button[data-dropdown]) :is(a, i) {
  color: var(--gp-ink-3);
}
#content form:has(input.ckb:checked) .gp-selcount { display: inline-flex; }

/* The JS-painted count chip in the action bar. */
.gp-selcount-bar {
  display: inline-flex;
  align-items: center;
  padding: 4px 11px;
  border-radius: var(--gp-r-pill);
  background: var(--gp-accent-tint);
  color: var(--gp-accent);
  font-size: 12.5px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* --- pagers and exports, wherever they render --------------------------
   The pager row is identified by the current-page marker pagenate now
   emits (b > .gp-bracket) — one selector covers tickets, tasks, users,
   orgs and the org-view embed without knowing each wrapper. */
#content form > div:has(> b > .gp-bracket),
#content div:has(> b > .gp-bracket) {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 10px;
  margin-top: 14px;
  font-size: 13px;
  color: var(--gp-ink-3);
}
#content div:has(> b > .gp-bracket) > .faded.pull-right {
  float: none;
  order: 9;
  margin-left: auto;
  font-family: var(--gp-mono);
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
}
#content div:has(> b > .gp-bracket) > a:not(.export-csv):not(.no-pjax):not(.export) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  height: 32px;
  padding: 0 9px;
  border-radius: var(--gp-r-sm);
  color: var(--gp-ink-2);
  text-decoration: none;
  font-variant-numeric: tabular-nums;
  transition: background var(--gp-t-color), color var(--gp-t-color);
}
#content div:has(> b > .gp-bracket) > a:not(.export-csv):not(.no-pjax):not(.export) b { font-weight: 400; }
#content div:has(> b > .gp-bracket) > a:not(.export-csv):not(.no-pjax):not(.export):hover {
  background: var(--gp-surface-2);
  color: var(--gp-ink);
}
#content div:has(> b > .gp-bracket) > b {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  height: 32px;
  padding: 0 9px;
  border-radius: var(--gp-r-sm);
  background: var(--gp-accent);
  color: #fff;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
/* Export controls: tasks' a.export-csv, users/orgs' bare export links, the
   dashboard's button.link.button — all read as the same secondary pill. */
#content a.export-csv,
#content div:has(> b > .gp-bracket) a[href*="a=export"],
#content button.link.button {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 13px;
  margin-left: 8px;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-pill);
  background: var(--gp-surface);
  color: var(--gp-ink-2);
  font-size: 12.5px;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--gp-t-color), color var(--gp-t-color),
              border-color var(--gp-t-color);
}
#content a.export-csv:hover,
#content div:has(> b > .gp-bracket) a[href*="a=export"]:hover,
#content button.link.button:hover {
  background: var(--gp-accent-tint);
  border-color: var(--gp-accent-edge);
  color: var(--gp-accent);
}

/* --- page heading bars off the queues ----------------------------------
   users/orgs put a bare h2 + a button cluster in the sticky bar; the More
   span carries a /*DELME*\/ inline vertical-align the templates drop. */
.sticky.bar .content > .pull-right:not(.flush-right) {
  margin-left: auto;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
}
/* "green" is the template's word for the page's one primary action. In this
   design that is the filled near-black primary; sage means state. */
.sticky.bar .content .green.button {
  background: var(--gp-primary);
  border-color: var(--gp-primary);
  color: #fff;
}
.sticky.bar .content .green.button:hover {
  background: var(--gp-primary-strong);
  border-color: var(--gp-primary-strong);
  color: #fff;
  box-shadow: none;
}

/* --- flat tabs, everywhere ul.tabs renders ------------------------------
   (ticket thread/response tabs, dashboard statistics, dialogs) */
ul.tabs {
  height: auto;
  padding: 0 4px;
  background: none;
  border-bottom: 1px solid var(--gp-line);
  box-shadow: none;
}
ul.tabs li,
ul.tabs li:not(.active) {
  min-width: 0;
  height: auto;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  bottom: 0;
  font-weight: 400;
}
ul.tabs li.active {
  background: none;
  border: 0;
  border-top: 0;
  box-shadow: none;
  bottom: 0;
}
ul.tabs.clean li.active { background: none; }
ul.tabs li a {
  display: block;
  padding: 11px 14px 9px;
  font-size: 13.5px;
  font-weight: 500;
  line-height: 1.3;
  color: var(--gp-ink-2);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color var(--gp-t-color), border-color var(--gp-t-color);
}
ul.tabs li a:hover { color: var(--gp-ink); }
ul.tabs li.active a {
  font-weight: 500;
  color: var(--gp-accent);
  border-bottom-color: var(--gp-accent);
}
ul.tabs li.error a, ul.tabs li a.error { color: var(--gp-err); }

/* --- dashboard ----------------------------------------------------------
   css/dashboard.css loads INSIDE #content, after this file — chart rules
   here carry #content for specificity. The chart ink itself is Raphael
   SVG with JS-injected colours and stays as shipped. */
#content #line-chart-here {
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r);
}
#content #line-chart-legend span.label {
  border-radius: var(--gp-r-pill);
  font-size: 11.5px;
}
#content table.dashboard-stats {
  border-bottom: 1px solid var(--gp-line);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}
#content table.dashboard-stats tbody:first-child th {
  white-space: nowrap;
  border-bottom: 1px solid var(--gp-line);
  padding: 8px 10px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
}
#content table.dashboard-stats tbody:nth-child(2) tr:nth-child(odd) {
  background: var(--gp-surface);
}
#content table.dashboard-stats tbody:nth-child(2) th {
  border-right: 0;
  padding: 8px 10px;
  color: var(--gp-ink-2);
  font-weight: 400;
}
#content table.dashboard-stats tbody:nth-child(2) td {
  border-right: 0;
  border-bottom: 1px solid var(--gp-line-soft);
  padding: 8px 10px;
  font-family: var(--gp-mono);
  font-size: 12.5px;
  color: var(--gp-ink-2);
}
#content table.dashboard-stats tbody:nth-child(2) tr:hover {
  background: var(--gp-surface-2);
}
#content table.dashboard-stats tbody:nth-child(2) tr:hover th { color: var(--gp-ink); }
/* The timeframe row reads as one aligned control row. */
#basic_search label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--gp-ink-2);
  font-size: 13.5px;
}
/* jQuery UI inserts a raw cal.png button beside every .dp field
   (showOn:'both', so focusing the field still opens the picker). Swap the
   bitmap for a glyph inside the control. */
.ui-datepicker-trigger,
#basic_search img.ui-datepicker-trigger,
#content img.ui-datepicker-trigger { display: none !important; }
#basic_search label:has(input.dp) { position: relative; }
#basic_search label:has(input.dp)::after {
  content: "\f073";
  font-family: FontAwesome;
  position: absolute;
  right: 11px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 12px;
  color: var(--gp-ink-3);
  pointer-events: none;
}
#basic_search input.dp { padding-right: 30px; }
#basic_search input.dp,
#basic_search select {
  height: var(--gp-ctl-h);
  font: inherit;
  font-size: 13.5px;
  color: var(--gp-ink);
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm);
  padding: 0 10px;
}
#basic_search input.dp:focus,
#basic_search select:focus {
  outline: 0;
  border-color: var(--gp-accent);
  box-shadow: 0 0 0 3px var(--gp-blue-ring);
}

/* =========================================================================
   15. Ticket detail view (scp/tickets.php?id=N — ticket-view.inc.php)
   =========================================================================
   Same system as the queue: white cards on cream, warm insets, navy for
   actions, semantic tokens for state. The toolbar reuses the sticky-bar
   card from §9; .flush-left order:-1 already puts the title first (the
   template emits .flush-right before .flush-left).
   ========================================================================= */

/* --- subject ------------------------------------------------------------ */
.tixTitle {
  padding: 18px 2px 14px;
}
.tixTitle h3 {
  color: var(--gp-ink);
  font-size: var(--gp-t-hmd);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.2;
  margin: 0;
}
.has_bottom_border { border-bottom: 1px solid var(--gp-line); }

/* --- metadata: the pale-blue panels become warm insets ------------------ */
/* One rhythm between every metadata block. Upstream separates them with
   bare <br>s on top of mixed margins, which left the panels 33px apart and
   Additional Details / Consent almost touching. */
table.ticket_info {
  width: 100%;                        /* the template hardcodes width=940 */
  background: var(--gp-surface-2);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm);
  margin: 24px 0 0;
}
#content table.ticket_info + br,
#content br + br { display: none; }
#content table.ticket_info.custom-data { margin: 24px 0 0; }
table.ticket_info > tbody > tr > td { padding: 6px 10px; }
/* the label/value tables nested in each half */
table.ticket_info table th,
.ticket_info th {
  width: 130px;
  padding: 7px 10px;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--gp-ink-3);
  white-space: nowrap;
  vertical-align: top;
}
table.ticket_info table td {
  padding: 7px 10px;
  font-size: 13.5px;
  color: var(--gp-ink);
  overflow-wrap: anywhere;  /* raw email addresses must break on a phone */
}
table.ticket_info a { color: var(--gp-accent); text-decoration: none; }

/* --- the User row -------------------------------------------------------
   Upstream renders "icon Name (3) icon ( Manage Collaborators)" — two
   parenthesised fragments jammed against a link. The parens are wrapped in
   the template so they can come off here: the name leads, the ticket count
   becomes a chip, and collaborators read as a quiet secondary action. */
.ticket_info .gp-paren { display: none; }
.ticket_info td a > i.icon-user { color: var(--gp-ink-3); margin-right: 2px; }
.ticket_info a[data-dropdown="#action-dropdown-stats"] {
  display: inline-flex;
  align-items: center;
  margin-left: 6px;
  padding: 1px 8px;
  border-radius: var(--gp-r-pill);
  background: var(--gp-surface);
  border: 1px solid var(--gp-line-soft);
  font-family: var(--gp-mono);
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  color: var(--gp-ink-2);
  transition: background var(--gp-t-color), color var(--gp-t-color),
              border-color var(--gp-t-color);
}
.ticket_info a[data-dropdown="#action-dropdown-stats"] b { font-weight: 500; }
.ticket_info a[data-dropdown="#action-dropdown-stats"]:hover {
  border-color: var(--gp-accent-edge);
  background: var(--gp-accent-tint);
  color: var(--gp-accent);
  text-decoration: none;
}
.ticket_info a.manage-collaborators {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: 10px;
  font-size: 12.5px;
  color: var(--gp-ink-3);
}
.ticket_info a.manage-collaborators i { color: var(--gp-ink-3); }
.ticket_info a.manage-collaborators:hover,
.ticket_info a.manage-collaborators:hover i {
  color: var(--gp-accent);
  text-decoration: none;
}
.ticket_info a.manage-collaborators:hover span { text-decoration: underline; text-underline-offset: 3px; }
table.ticket_info a:hover { text-decoration: underline; text-underline-offset: 3px; }
table.ticket_info .faded { color: var(--gp-ink-3); }
/* custom-data forms ("Ticket Details", consent forms, …) */
table.ticket_info.custom-data thead th {
  background: none;
  border-bottom: 1px solid var(--gp-line);
  padding: 9px 10px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
}
.custom-data th, .custom-data td { padding: 7px 10px; }
/* inline-edit affordance: quiet until hovered */
.ticket_info a.inline-edit { color: inherit; }
.ticket_info a.inline-edit:hover { color: var(--gp-accent); text-decoration: none; }
.ticket_info a.inline-edit:hover span { text-decoration: underline; text-underline-offset: 3px; }

/* --- thread ------------------------------------------------------------- */
#ticket_thread, .tab_content { padding-top: 14px; }

.thread-entry { margin-bottom: 16px; }
/* Upstream floats a 48px avatar into a 60px gutter beside each entry, which
   leaves a placeholder square hanging outside the card. The header already
   names the author and the tint already says which side they are on, so the
   gutter comes back as width. (Inline avatars inside event descriptions
   stay — they read as part of the sentence.) */
.thread-entry > .avatar { display: none; }
.thread-entry.avatar,
.thread-entry.message.avatar,
.thread-entry.response.avatar,
.thread-entry.note.avatar { margin-left: 0; margin-right: 0; }
/* speech-bubble arrows: gone (both pseudo pairs carry them upstream) */
.thread-entry .header::before,
.thread-entry .header::after,
.thread-entry::before,
.thread-entry::after { display: none; }
.thread-entry .header {
  padding: 9px 14px;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm) var(--gp-r-sm) 0 0;
  font-size: 13px;
  color: var(--gp-ink-2);
}
.thread-entry .header b { font-weight: 500; color: var(--gp-ink); }
.thread-entry .header time {
  font-family: var(--gp-mono);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--gp-ink-3);
}
.thread-entry .header a { color: inherit; text-decoration: none; }
/* who is speaking, told by tint: user = navy, agent reply = sage, internal
   note = warm gold, system = neutral */
.thread-entry.message .header { background: var(--gp-accent-tint); border-color: var(--gp-accent-edge); }
.thread-entry.response .header { background: var(--gp-brand-tint); border-color: var(--gp-brand-edge); }
.thread-entry.note .header { background: var(--gp-warn-bg); border-color: var(--gp-warn-edge); }
.thread-entry.system .header { background: var(--gp-surface-2); }
.thread-entry .thread-body {
  border: 1px solid var(--gp-line);
  border-top: none;
  border-radius: 0 0 var(--gp-r-sm) var(--gp-r-sm);
  padding: 14px;
}
.thread-body .attachments {
  background-color: var(--gp-surface-2);
  border-top: 1px dashed var(--gp-line);
  border-radius: 0 0 var(--gp-r-sm) var(--gp-r-sm);
  margin: 0 -14px;
  padding: 8px 14px;
}
.thread-entry .header .muted-button {
  background: none;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm);
  color: var(--gp-ink-3);
  padding: 2px 7px;
}
.thread-entry .header .muted-button:hover {
  background: var(--gp-surface);
  border-color: var(--gp-accent-edge);
  color: var(--gp-accent);
}
.thread-entry .header .label.label-bare {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
  background: none;
}
/* the event timeline between entries */
.thread-event { color: var(--gp-ink-3); }
.thread-event .type-icon {
  background: var(--gp-surface-2);
  border: 1px solid var(--gp-line-soft);
  border-radius: 50%;
}
.thread-event .type-icon::after { border-color: var(--gp-surface); }
.thread-event .faded.description { color: var(--gp-ink-3); }
.thread-event .description b { font-weight: 500; color: var(--gp-ink-2); }

/* --- response area ------------------------------------------------------ */
#response_options { margin-top: 20px; }
#response_options > form,
#response_options > form.tab_content {
  background: var(--gp-surface-2);
  border: 1px solid var(--gp-line);
  border-top: none;
  border-radius: 0 0 var(--gp-r-sm) var(--gp-r-sm);
  padding: 16px 18px;
}
#response_options td { font-size: 13.5px; overflow-wrap: anywhere; }
#response_options td label, #response_options label strong {
  font-weight: 500;
  color: var(--gp-ink-2);
}
#response_options input[type="text"],
#response_options select,
#response_options textarea:not(.richtext) {
  max-width: 100%;          /* long option text must not escape the fixed
                               layout table on a phone */
  font: inherit;
  font-size: 13.5px;
  color: var(--gp-ink);
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm);
}
#response_options input[type="text"],
#response_options textarea:not(.richtext) { padding: 6px 10px; }
#response_options select { height: 32px; padding: 0 10px; }
#response_options input[type="text"]:focus,
#response_options select:focus {
  outline: 0;
  border-color: var(--gp-accent);
  box-shadow: 0 0 0 3px var(--gp-blue-ring);
}
/* the editor chrome */
#response_options .redactor-toolbar { margin: 0; padding: 4px 6px; }
#response_options .redactor-toolbar li {
  display: inline-block;
  float: none;
  margin: 0;
}
#response_options .redactor-box {
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm);
  overflow: hidden;
  background: var(--gp-surface);
}
/* the uploader */
#response_options .filedrop .dropzone {
  border: 1px dashed var(--gp-line);
  border-radius: var(--gp-r-sm);
  background: var(--gp-surface);
  color: var(--gp-ink-3);
}
/* gold is decorative, never functional: the hardcoded .save.pending submit
   is the composer's send action, and takes Stitch's send-green — the one
   green that is an action rather than a state, kept for exactly this. */
input.save.pending, .save.pending {
  background-color: var(--gp-send);
  border-color: var(--gp-send);
  box-shadow: none;
  color: #fff;
}
input.save.pending:hover, .save.pending:hover {
  background-color: var(--gp-send-strong);
  border-color: var(--gp-send-strong);
  box-shadow: none;
  color: #fff;
}
.save.success { background-color: var(--gp-ok); border-color: var(--gp-ok); color: #fff; }
/* select2 widgets pick up the control voice */
#content .select2-container--default .select2-selection--single,
#content .select2-container--default .select2-selection--multiple {
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm);
}

/* The dropdown is appended to <body>, not inside #content, so these cannot
   be scoped the way the control above is. Stock select2 highlights with its
   own blue (#5897fb), which is the one colour on the page that belongs to
   no palette. */
/* Already-chosen options first, then the hover/keyboard highlight, because
   the highlighted option is usually also a selected one and whichever rule
   comes last wins at equal specificity. */
.select2-container--default .select2-results__option[aria-selected="true"],
.select2-container--default .select2-results__option[data-selected="true"] {
  background: var(--gp-surface-2);
  color: var(--gp-ink);
}
.select2-container--default .select2-results__option--highlighted,
.select2-container--default .select2-results__option--highlighted[aria-selected],
.select2-container--default .select2-results__option--highlighted[aria-selected="true"],
.select2-container--default .select2-results__option--highlighted[data-selected="true"] {
  background: var(--gp-accent);
  color: #fff;
}
.select2-container--default .select2-results__option--highlighted .gp-sig-held {
  color: rgba(255, 255, 255, 0.75);
}
.select2-dropdown {
  border-color: var(--gp-line);
  border-radius: var(--gp-r-sm);
  box-shadow: 0 12px 32px -12px rgba(27, 28, 25, 0.28);
}
.select2-container--default .select2-search--dropdown .select2-search__field {
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm);
  padding: 6px 8px;
}

/* =========================================================================
   16. Dialogs and shared form controls
   =========================================================================
   $.dialog panels (#popup and the inline .dialog nodes) carry every
   confirm, mass action, advanced search and column picker in the panel —
   agent AND admin — so they get the same surface language as a card.
   ========================================================================= */

.dialog, #popup {
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r);
  box-shadow: 0 30px 80px -30px rgba(27, 28, 25, 0.45),
              0 2px 8px rgba(27, 28, 25, 0.08);
  padding: 22px 24px;
}
.dialog h3, #popup h3 {
  color: var(--gp-ink);
  font-size: 19px;
  font-weight: 500;
  letter-spacing: -0.015em;
}
.dialog h3 i, #popup h3 i { color: var(--gp-ink-3); }
.dialog a, #popup a { color: var(--gp-accent); }
.dialog a.close, #popup a.close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  color: var(--gp-ink-3);
  transition: background var(--gp-t-color), color var(--gp-t-color);
}
.dialog a.close:hover, #popup a.close:hover {
  background: var(--gp-surface-2);
  color: var(--gp-ink);
}
.dialog hr, #popup hr {
  background: none;
  border-top: 1px solid var(--gp-line);
  height: 0;
}
.dialog th, .tip_box th {
  background: none;
  border: 0;
  border-bottom: 1px solid var(--gp-line);
  padding: 8px 10px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
}
.dialog .buttons, #popup .buttons { display: inline-flex; gap: 8px; }

/* --- checkboxes and radios, panel-wide ---------------------------------
   The list treatment is the panel treatment: dialogs, filter rules, the
   reply form's signature radios and every admin form share it. */
#content input[type="checkbox"],
.dialog input[type="checkbox"],
#popup input[type="checkbox"],
#content input[type="radio"],
.dialog input[type="radio"],
#popup input[type="radio"] {
  appearance: none;
  -webkit-appearance: none;
  box-sizing: border-box;
  width: 17px;
  height: 17px;
  margin: 0 4px 0 0;
  /* scp.css gives every input 3px 5px of padding. On a 17px border-box box
     that leaves a 5px-wide content area for a 6px dot, so the `auto` margins
     that were meant to centre it resolved to -1px and pushed it up and left.
     The dot is centred by the flexbox below rather than by margins, so it
     cannot drift again if the sizes change. */
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1.5px solid var(--gp-line);
  background: var(--gp-surface);
  cursor: pointer;
  /* `middle`, not a length: an inline-flex box takes its baseline from its
     first flex item, so with a length the control jumped 5.5px the moment a
     mark appeared inside it. `middle` ignores the box's own baseline, so a
     checkbox with a tick and a radio without one sit identically. */
  vertical-align: middle;
  transition: background var(--gp-t-color), border-color var(--gp-t-color);
}
#content input[type="checkbox"],
.dialog input[type="checkbox"],
#popup input[type="checkbox"] { border-radius: 5px; }
#content input[type="radio"],
.dialog input[type="radio"],
#popup input[type="radio"] { border-radius: 50%; }
#content input[type="checkbox"]:hover,
#content input[type="radio"]:hover,
.dialog input[type="checkbox"]:hover,
.dialog input[type="radio"]:hover { border-color: var(--gp-accent-edge); }
#content input[type="checkbox"]:checked,
.dialog input[type="checkbox"]:checked,
#popup input[type="checkbox"]:checked,
#content input[type="radio"]:checked,
.dialog input[type="radio"]:checked,
#popup input[type="radio"]:checked {
  background: var(--gp-accent);
  border-color: var(--gp-accent);
}
#content input[type="checkbox"]:checked::after,
.dialog input[type="checkbox"]:checked::after,
#popup input[type="checkbox"]:checked::after {
  content: "";
  display: block;
  width: 4px;
  height: 8px;
  /* nudged up by half its own border so the tick sits optically centred */
  margin: -1px 0 0 0;
  border: solid #fff;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
/* No inner dot: a checked radio is a filled circle. Besides being what was
   asked for, it keeps the control childless - see the note on vertical-align
   below, which is why the dot made it jump. */
#content input[type="checkbox"]:focus-visible,
#content input[type="radio"]:focus-visible,
.dialog input[type="checkbox"]:focus-visible,
.dialog input[type="radio"]:focus-visible {
  outline: 2px solid var(--gp-accent);
  outline-offset: 2px;
}
/* the queue's own boxes stay a touch larger — they are the primary control */
#content table.list input[type="checkbox"] { width: 18px; height: 18px; }

/* --- form tables ---------------------------------------------------------
   .form_table carries every settings and open-a-ticket form in both panels.
   Its section bands are grey #eee strips; they become the same warm
   micro-label the queue headers use, and the grid softens to hairlines. */
#content table.form_table,
.dialog table.form_table {
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm);
  border-collapse: separate;
  border-spacing: 0;
  overflow: hidden;
  margin-top: 12px;
}
#content table.form_table th,
#content div.section-break,
.dialog table.form_table th {
  background: var(--gp-surface-2);
  border: 0;
  border-bottom: 1px solid var(--gp-line);
  border-top: 1px solid var(--gp-line);
  padding: 9px 12px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
}
#content table.form_table tr:first-child th { border-top: 0; }
#content table.form_table th em,
#content table.form_table th h4 {
  font-size: 12px;
  font-style: normal;
  letter-spacing: 0;
  text-transform: none;
  font-weight: 400;
  color: var(--gp-ink-3);
  background: none;
  margin: 4px 0 0;
  padding: 0;
}
#content table.form_table td {
  border: 0;
  border-bottom: 1px solid var(--gp-line-soft);
  padding: 9px 12px;
  vertical-align: middle;
}
#content table.form_table tr:last-child td { border-bottom: 0; }
#content table.form_table .required { font-weight: 500; }
#content table.form_table tr i.help-tip { opacity: 1; color: var(--gp-ink-3); }
#content table.form_table input[type="text"],
#content table.form_table input[type="password"],
#content table.form_table input[type="email"],
#content table.form_table textarea {
  font: inherit;
  font-size: 13.5px;
  color: var(--gp-ink);
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm);
  padding: 6px 10px;
  max-width: 100%;
}
#content table.form_table input[type="text"]:focus,
#content table.form_table input[type="password"]:focus,
#content table.form_table textarea:focus {
  outline: 0;
  border-color: var(--gp-accent);
  box-shadow: 0 0 0 3px var(--gp-blue-ring);
}

/* --- widgets that size themselves in JS ---------------------------------
   select2 writes an inline pixel width (450px on the ticket-open user
   picker), and filedrop paints its own grey dropzone. Both must live inside
   the column they are given. */
#content .select2-container,
.dialog .select2-container { max-width: 100%; }
/* The attach bar. One row, one glyph, one voice: the template emits
   "<i class=icon-upload> Drop files here or <a>choose them</a>", and left to
   itself that renders as a glyph jammed against the prompt and a second,
   differently-coloured link jammed against its own. Laid out as a flex row
   with a real gap, the icon and the prompt share the muted ink and only the
   link carries the accent — so there is exactly one coloured thing in the
   row, and it is the one you can click. */
#content .filedrop .dropzone,
.dialog .filedrop .dropzone {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 11px 14px;
  background: var(--gp-surface);
  border: 1px dashed var(--gp-line);
  border-radius: var(--gp-r-sm);
  color: var(--gp-ink-3);
  font-size: 0.875rem;
  transition: background-color var(--gp-t-color), border-color var(--gp-t-color),
              color var(--gp-t-color);
}
#content .filedrop .dropzone i.icon-upload,
.dialog .filedrop .dropzone i.icon-upload {
  flex: none;
  margin: 0;
  color: inherit;
  font-size: 1rem;
  line-height: 1;
}
#content .filedrop .dropzone:hover,
.dialog .filedrop .dropzone:hover { border-color: var(--gp-accent-edge); }

/* --- Attachments: the upload row reports its own state -------------------
   The treatment the client portal ships (assets/default/css/theme.css, "The
   attached-file list"), brought to the agent and admin panels — both are
   served by include/staff/header.inc.php, so this one block covers every
   uploader behind scp/: the ticket reply, the new-ticket form, canned
   responses, FAQ articles, and the file fields on the admin pages.

   Every attachment is its own bordered row that REPORTS ITS STATE: in flight
   it takes a blue wash with a progress bar along its top edge and a blue
   "Uploading…" line; landed, it goes white with a green check at the leading
   edge. Upstream drew one 25px line of grey text for both, with a striped
   Bootstrap bar underneath that said "something is happening" without saying
   what — and an agent attaching a 30MB export had no way to tell a stalled
   upload from a slow one.

   THE STATE HOOK. filedrop.field.js drives the whole lifecycle by showing
   and hiding elements — no class is ever set — so the state has to be read
   off what is visible, and no JavaScript changes. That matters more here
   than anywhere else in this stylesheet: filedrop.field.js is the code path
   that carries a user's file to the server.

   The obvious selector is a :has() on the row, and it does not work.
   Chrome matches it correctly but will not repaint it when the inline style
   on the descendant changes — verified against these fixtures, where the row
   rendered its state on load and then froze. Frozen is the worst possible
   outcome for the one element on the page whose job is to change while you
   watch it, so the state is carried two other ways instead:

     the wash, the edge and the bar   .progress itself, stretched over the
                                      row — the script shows and hides it,
                                      so no ancestor selector is involved
     the green check                  .trash ~ .filetype — a sibling
                                      combinator, which DOES invalidate on
                                      an attribute change

   tools/preview/check.mjs mutates the inline display both ways and requires
   the row to follow, so a jQuery upgrade that changes the spelling, or a
   browser that stops invalidating, fails the suite instead of quietly going
   flat.

   Raised to the specificity below because css/filedrop.css is injected as
   field media and therefore loads after this file. */

#content .filedrop .files,
.dialog .filedrop .files { padding: 0; border: 0; }
#content .filedrop .files:not(:empty),
.dialog .filedrop .files:not(:empty) {
  border: 0;
  border-radius: 0;
  /* Equal on all four sides. Padded 8/8/0 the list sat 8px below the editor
     and flush against the attach bar, so the same 1px rule read as two
     different gaps. */
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
#content .filedrop .files:not(:empty) + .dropzone,
.dialog .filedrop .files:not(:empty) + .dropzone { border-top: 1px solid var(--gp-line); }

/* Upstream pins each row to height:25px, which crushes any thumbnail into a
   sliver. Rows size to their content instead, and become the design's card. */
#content .filedrop .files .file,
.dialog .filedrop .files .file {
  position: relative;      /* the overlay and the bar are pinned to this box */
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0 12px;
  height: auto;
  min-height: 0;
  padding: 10px 12px;
  margin: 0;
  overflow: hidden;        /* keeps the overlay inside the rounded corners */
  background: var(--gp-surface);
  border: 1px solid var(--gp-beige);
  border-radius: var(--gp-r-sm);
  transition: border-color var(--gp-t-color);
}
#content .filedrop .files .file:hover,
.dialog .filedrop .files .file:hover { border-color: var(--gp-ink-3); }

/* --- Leading state icon --------------------------------------------------
   addNode() emits an empty <div class="filetype"> and never puts a class on
   it. It is the one stable slot at the head of the row, so it carries the
   state glyph. Blue is the resting state: a row exists because a file is
   arriving. A thumbnail says more than a glyph, so an image that has a
   preview keeps the preview and drops the icon. */
#content .filedrop .files .filetype,
.dialog .filedrop .files .filetype {
  order: 0;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  font-size: 1rem;
  line-height: 1;
  color: var(--gp-upload);
}
#content .filedrop .files .filetype::before,
.dialog .filedrop .files .filetype::before {
  font-family: FontAwesome;
  font-style: normal;
  font-weight: normal;
  content: "\f016";        /* file */
}
#content .filedrop .files .file:has(.preview[src]:not([src=""])) .filetype,
.dialog .filedrop .files .file:has(.preview[src]:not([src=""])) .filetype { display: none; }

/* Landed, in one sibling hop. .trash is the only element that precedes
   .filetype AND tracks the settled state: addNode() prepends it,
   beforeEach() hides it while the bytes move, uploadFinished() shows it, and
   an attachment that was already on the record renders with it visible.
   Written sideways rather than downwards because a sibling combinator
   invalidates on an attribute change and :has() does not.

   Where the widget is not deletable there is no .trash and no row takes the
   check. That is the honest failure: a row that cannot say it has landed is
   better than one that says so before it has. */
#content .filedrop .files .file .trash:not([style*="display: none"]):not([style*="display:none"]) ~ .filetype,
.dialog .filedrop .files .file .trash:not([style*="display: none"]):not([style*="display:none"]) ~ .filetype {
  color: var(--gp-done);
}
#content .filedrop .files .file .trash:not([style*="display: none"]):not([style*="display:none"]) ~ .filetype::before,
.dialog .filedrop .files .file .trash:not([style*="display: none"]):not([style*="display:none"]) ~ .filetype::before {
  content: "\f058";        /* check-circle */
}

/* A picture should look like the picture. Was max 100x25. */
#content .filedrop .preview,
.dialog .filedrop .preview {
  order: 1;
  width: 42px;
  height: 42px;
  max-width: 42px;
  max-height: 42px;
  padding: 0;
  float: none;
  flex: none;
  object-fit: cover;
  border-radius: var(--gp-r-sm);
  background: var(--gp-surface-2);
}
/* Non-image uploads leave the <img> with no src; collapse it rather than
   showing a broken-image box. */
#content .filedrop .preview:not([src]),
.dialog .filedrop .preview:not([src]),
#content .filedrop .preview[src=""],
.dialog .filedrop .preview[src=""] { display: none; }

#content .filedrop .files .file .filename,
.dialog .filedrop .files .file .filename {
  order: 2;
  flex: 1 1 12rem;
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 10px;
  padding: 0;
  font-size: 0.9375rem;
  color: var(--gp-ink);
  overflow: hidden;
}
#content .filedrop .files .file .filename > a,
.dialog .filedrop .files .file .filename > a,
#content .filedrop .files .file .filename > span:first-child,
.dialog .filedrop .files .file .filename > span:first-child {
  flex: 1 1 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#content .filedrop .files .file .filesize,
.dialog .filedrop .files .file .filesize {
  margin: 0;
  flex: none;
  font-family: var(--gp-mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.75rem;
  color: var(--gp-ink-3);
}
/* The transfer rate is the second line while a file is moving, and it is the
   only text on the row that is blue. filedrop.field.js hides it on finish, so
   it says nothing once there is nothing to say. */
#content .filedrop .files .file .upload-rate,
.dialog .filedrop .files .file .upload-rate {
  margin: 0;
  float: none;
  flex: none;
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--gp-upload);
}
#content .filedrop .files .file .upload-rate:not(:empty)::before,
.dialog .filedrop .files .file .upload-rate:not(:empty)::before {
  content: "Uploading… ";
}

/* Remove/cancel controls sit at the far end; .pull-right's float is inert in
   a flex row, so they are pushed there instead. They must outrank the
   overlay, which covers the whole row. */
#content .filedrop .files .file .trash,
.dialog .filedrop .files .file .trash,
#content .filedrop .files .file .cancel,
.dialog .filedrop .files .file .cancel {
  order: 3;
  position: relative;
  z-index: 1;
  float: none;
  margin-left: auto;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  color: var(--gp-ink-3);
  cursor: pointer;
  transition: background-color var(--gp-t-color), color var(--gp-t-color);
}
#content .filedrop .files .file .trash:hover,
.dialog .filedrop .files .file .trash:hover,
#content .filedrop .files .file .cancel:hover,
.dialog .filedrop .files .file .cancel:hover {
  background: var(--gp-err-bg);
  color: var(--gp-err);
}
/* Everything the overlay sits over has to sit over it back, or the wash
   washes the text too. */
#content .filedrop .files .file .filetype,
.dialog .filedrop .files .file .filetype,
#content .filedrop .files .file .preview,
.dialog .filedrop .files .file .preview,
#content .filedrop .files .file .filename,
.dialog .filedrop .files .file .filename { position: relative; z-index: 1; }

/* --- In flight -----------------------------------------------------------
   .progress is stretched over the whole row: it paints the wash and the blue
   edge, and the bar runs along its top. The bar reads as the row filling up,
   which is what it measures. filedrop.field.js writes the bar's width as an
   inline percentage; against a full-row overlay that percentage is a
   percentage of the row, which is what it was always meant to mean. */
#content .filedrop .files .file .progress,
.dialog .filedrop .files .file .progress {
  position: absolute;
  inset: 0;
  height: auto;
  margin: 0;
  background: var(--gp-upload-tint);
  box-shadow: inset 0 0 0 1px var(--gp-upload-edge);
  border-radius: inherit;
  overflow: hidden;
  pointer-events: none;   /* the cancel button is underneath */
}
#content .filedrop .files .file .progress-bar,
.dialog .filedrop .files .file .progress-bar {
  position: absolute;
  top: 0;
  left: 0;
  height: 3px;
  background-color: var(--gp-upload);
  background-image: none;   /* upstream's candy stripes */
  box-shadow: none;
  animation: none;
  transition: width 140ms linear;
}

/* Queued, not uploading. filedrop.field.js parks a file at width:100% with
   .progress-bar-striped.active until a transfer slot frees, and strips both
   classes the moment bytes move. With the candy stripes gone, a queued file
   would otherwise read as a FINISHED one — a full blue bar — which is the
   most misleading thing the row could say. It gets an indeterminate sweep
   instead: motion means waiting, a filling bar means progress. */
#content .filedrop .files .file .progress-bar.progress-bar-striped,
.dialog .filedrop .files .file .progress-bar.progress-bar-striped {
  background-image: linear-gradient(90deg,
    var(--gp-upload-tint) 0%,
    var(--gp-upload) 45%,
    var(--gp-upload) 55%,
    var(--gp-upload-tint) 100%);
  background-size: 240% 100%;
  background-color: transparent;
  animation: gp-upload-wait 1.1s linear infinite;
}
@keyframes gp-upload-wait {
  from { background-position: 120% 0; }
  to   { background-position: -120% 0; }
}
@media (prefers-reduced-motion: reduce) {
  #content .filedrop .files .file .progress-bar.progress-bar-striped,
  .dialog .filedrop .files .file .progress-bar.progress-bar-striped { animation: none; }
}

/* --- The whole editor is the drop target --------------------------------
   filedrop.field.js widens the surface from the attach bar to the entire
   editor and adds .filedrop-over while a file is over it (see
   extendDropSurface). The bar alone was ~40px at the foot of a control ten
   times that, so the obvious place to drop a file was the one place that did
   not take one.

   The cue has to be unmistakable and must not move anything: an outline and
   a wash, both drawn inside the box, so nothing reflows under the pointer
   mid-drag. It borrows the uploader's own blue — a file is about to be in
   flight, which is exactly what that colour means everywhere else here. */
#content .redactor-box.filedrop-over,
.dialog .redactor-box.filedrop-over {
  border-color: var(--gp-upload) !important;
  box-shadow: inset 0 0 0 1px var(--gp-upload),
              inset 0 0 0 9999px var(--gp-upload-tint);
}
#content .redactor-box.filedrop-over .redactor-toolbar,
.dialog .redactor-box.filedrop-over .redactor-toolbar {
  background: transparent;
}

#content .filedrop.filedrop-over .dropzone,
.dialog .filedrop.filedrop-over .dropzone {
  border-color: var(--gp-upload);
  border-style: solid;
  background: var(--gp-upload-tint);
  color: var(--gp-upload);
}




/* The prompt is one flex item, not two. FileUploadWidget wraps the sentence
   in .dropzone-prompt so the row's gap separates the glyph from the words and
   nothing else; inside it, "Drop files here or choose them" is ordinary
   inline text and the space before the link is the same space as every other
   space in the line. letter-spacing is pinned to normal for the same reason:
   the row inherits from whatever field it was dropped into, and the prompt
   and its link have to be tracked alike to read as one sentence. */
#content .filedrop .dropzone .dropzone-prompt,
.dialog .filedrop .dropzone .dropzone-prompt {
  min-width: 0;
  letter-spacing: normal;
  word-spacing: normal;
}
/* The panel's dropzone link takes the panel's accent, not the portal's. */
#content .filedrop .dropzone a.manual,
.dialog .filedrop .dropzone a.manual {
  color: var(--gp-accent);
  font-weight: 500;
}
#content .filedrop .dropzone a.manual:hover,
.dialog .filedrop .dropzone a.manual:hover { color: var(--gp-accent-strong); }


/* A disabled field is still part of the palette. */
#content input:disabled,
#content textarea:disabled,
#content select:disabled,
.dialog input:disabled {
  background: var(--gp-surface-2);
  color: var(--gp-ink-3);
  cursor: not-allowed;
}

/* Legacy layout tables carry width="940" attributes; none of them may
   outgrow the column they sit in. */
#content table { max-width: 100%; }
/* The user and organization views head the page with a bare 940px table
   whose right cell holds the toolbar, so those buttons stopped 226px short
   of the card edge while every other page's toolbar reached it. */
#content > table:has(td.right_align),
#content > form > table:has(td.right_align) { width: 100%; }
/* The buttons inside stay floated, as upstream renders them — that is what
   puts More at the end of the run. A flex cell would leave the table's
   column model and collapse the header. */
#content td.right_align { text-align: right; vertical-align: middle; }
/* the reload glyph beside a record's name is an affordance, not a status */
#content h2 a > i.icon-refresh { color: var(--gp-ink-3); font-size: 0.72em; }
#content td.right_align > .action-button,
#content td.right_align > .button { margin-left: 6px; margin-right: 0; }
/* Selects size to their longest option, which on a narrow column pushes the
   page sideways. */
#content select, .dialog select { max-width: 100%; }

/* the injected scroll-up affordance in the pinned toolbar */
a.scroll-up, a.only.sticky.scroll-up {
  background: var(--gp-surface-2);
  border: 1px solid var(--gp-line);
  border-top: none;
  border-radius: 0 0 var(--gp-r-sm) var(--gp-r-sm);
  box-shadow: none;
  padding: 7px 10px 5px;
  color: var(--gp-ink-3) !important;
}
a.scroll-up:hover, a.only.sticky.scroll-up:hover {
  background: var(--gp-accent-tint);
  border-color: var(--gp-accent-edge);
  color: var(--gp-accent) !important;
}

/* =========================================================================
   17. Agent sign-in (scp/login.php)
   =========================================================================
   Its own header (include/staff/login.header.php) loads only css/login.css,
   so this file is linked there too and lands last. The page matters more
   than its traffic suggests: a session timeout drops an agent straight
   onto it mid-task.

   Shape: single sign-on leads, because that is the route most agents take;
   the username-and-password form is intact but folded behind a disclosure.
   The disclosure is a checkbox sibling of the form, so no script is needed
   and the form is never rebuilt — every field, id and handler is untouched.

   Three states must never hide the form, and all three are read off the
   markup rather than tracked in JS:
     - no external backend is configured (password is the only way in)
     - the form is carrying the 2FA step  (input[name=do][value=2fa])
     - a message is showing (a failed attempt, or "2FA Pending")
   ========================================================================= */

#loginBody {
  min-height: 100vh;
  margin: 0;
  padding: 40px 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: var(--gp-page);
  font-family: var(--gp-sans);
  color: var(--gp-ink);
}
/* the tiled wallpaper, its blur layer and the blend-mode overlay */
#brickwall, #blur, #background, #background-compat { display: none !important; }
#loginBox::after { content: none !important; }

#loginBox {
  position: static;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 27rem;              /* --gp-w-auth: the portal's sign-in column */
  margin: 0;
  padding: 36px 34px 30px;
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r);
  box-shadow: 0 30px 70px -34px rgba(27, 28, 25, 0.4),
              0 2px 6px rgba(27, 28, 25, 0.04);
  text-align: left;
  overflow: visible;
}

/* Single sign-on leads; the disclosure and the form follow it. Ordering in
   CSS keeps the template's own block order untouched. */
#loginBox > h1#logo         { order: 1; }
#loginBox > .gp-signin-title { order: 1; }   /* DOM order settles the tie */
#loginBox > h3#login-message { order: 2; }
#loginBox > div.banner      { order: 3; }
#loginBox > div.external-auth { order: 4; }
#loginBox > div.or          { order: 5; }
#loginBox > .gp-signin-alt  { order: 6; }
#loginBox > form#login      { order: 7; }
#loginBox > #loading        { order: 8; }
#loginBox > #company        { order: 9; }
#loginBox > br              { display: none; }

#loginBox h1#logo {
  margin: 0 0 22px;
  padding: 0;
  text-align: center;
}
#loginBox #logo a { display: inline-block; height: auto; }
#loginBox #logo img { max-height: 54px; max-width: 220px; width: auto; }

/* Says what the page is — it is also where a timed-out session lands. */
#loginBox .gp-signin-title {
  margin: 0 0 18px;
  font-size: 21px;
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1.2;
  color: var(--gp-ink);
  text-align: center;
}

/* The message slot carries two very different things: the standing prompt
   ("Authentication Required", rendered on every visit) and the failure text
   the ajax handler writes into it. It reads as a neutral notice by
   default, and as an error once a username has actually been submitted —
   which the field's own value attribute records. */
#loginBox h3#login-message {
  margin: 0;
  padding: 0;
  font-size: 13.5px;
  font-weight: 400;
  line-height: 1.45;
  color: var(--gp-ink-2);
  text-align: center;
}
#loginBox h3#login-message:not(:empty) {
  margin-bottom: 18px;
  padding: 10px 14px;
  background: var(--gp-surface-2);
  border-radius: var(--gp-r-sm);
}
#loginBox:has(input[name="userid"]:not([value=""])) h3#login-message:not(:empty) {
  background: var(--gp-err-bg);
  border: 1px solid var(--gp-err-edge);
  color: var(--gp-err);
}
/* The banner always ships a <small>, so it is never :empty even with
   nothing to say — test the child instead, or an empty strip renders. */
#loginBox div.banner { margin: 0; padding: 0; }
#loginBox div.banner:has(small:empty),
#loginBox div.banner:empty { display: none; }
#loginBox div.banner:not(:empty) {
  margin-bottom: 18px;
  padding: 10px 14px;
  background: var(--gp-surface-2);
  border-radius: var(--gp-r-sm);
  color: var(--gp-ink-2);
  font-size: 13px;
  line-height: 1.5;
  text-align: left;
}
#loginBox div.banner small { font-size: inherit; }

/* --- single sign-on ------------------------------------------------------
   Whatever backends are registered render through
   AuthenticationBackend::renderExternalLink(): an <a class="external-sign-in">
   wrapping either a branded image or an icon + label. Both shapes become the
   page's primary action. */
#loginBox div.external-auth { display: block; margin: 0 0 12px; padding: 0; }
#loginBox div.external-auth + div.external-auth { margin-top: 0; }
#loginBox a.external-sign-in {
  display: block;
  width: 100%;
  text-decoration: none;
}
#loginBox a.external-sign-in .external-auth-box {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  height: 44px;
  padding: 0 18px;
  border: 1px solid var(--gp-accent);
  border-radius: var(--gp-r-pill);
  background: var(--gp-accent);
  color: #fff;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -0.005em;
  transition: background var(--gp-t-color), border-color var(--gp-t-color);
}
#loginBox a.external-sign-in:hover .external-auth-box {
  background: var(--gp-accent-strong);
  border-color: var(--gp-accent-strong);
}
#loginBox a.external-sign-in:focus-visible .external-auth-box {
  outline: 2px solid var(--gp-accent);
  outline-offset: 2px;
}
/* login.css gives the icon a 30px box with a divider rule and the label a
   100px box with a 30px line-height and its own dark colour — sized for the
   old bordered chip, not a full-width button. */
#loginBox .external-auth-icon,
#loginBox .external-auth-name {
  display: inline-flex;
  align-items: center;
  width: auto;
  padding: 0;
  border: 0;
  line-height: 1.2;
  color: inherit;
  font-size: 14px;
}
#loginBox .external-auth-icon i { font-size: 16px; line-height: 1; }
/* a backend that supplies its own branded button keeps it, just sized */
#loginBox img.sign-in-image { display: block; max-width: 100%; margin: 0 auto; }

/* the hairline between single sign-on and the username route */
#loginBox div.or { margin: 4px 0 14px; padding: 0; }
#loginBox div.or hr {
  height: 0;
  margin: 0;
  border: 0;
  border-top: 1px solid var(--gp-line);
  background: none;
}

/* --- the username disclosure -------------------------------------------- */
#loginBox .gp-signin-toggle {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;          /* stays focusable — the label is its control */
  pointer-events: none;
}
#loginBox .gp-signin-alt {
  display: none;       /* only earns its place when there is an alternative */
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 44px;
  padding: 0 18px;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-pill);
  background: var(--gp-surface);
  color: var(--gp-ink-2);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  user-select: none;
  transition: background var(--gp-t-color), color var(--gp-t-color),
              border-color var(--gp-t-color);
}
#loginBox .gp-signin-alt:hover {
  background: var(--gp-accent-tint);
  border-color: var(--gp-accent-edge);
  color: var(--gp-accent);
}
#loginBox .gp-signin-alt i { color: var(--gp-ink-3); }
#loginBox .gp-signin-alt:hover i { color: var(--gp-accent); }
#loginBox .gp-signin-toggle:focus-visible + .gp-signin-alt {
  outline: 2px solid var(--gp-accent);
  outline-offset: 2px;
}

/* With single sign-on present the form folds away behind the disclosure. */
#loginBox:has(div.external-auth) .gp-signin-alt { display: inline-flex; }
#loginBox:has(div.external-auth) form#login { display: none; }
#loginBox:has(div.external-auth) .gp-signin-toggle:checked ~ form#login { display: block; }
#loginBox:has(div.external-auth) .gp-signin-toggle:checked ~ .gp-signin-alt { display: none; }
/* …but never when the form is the 2FA step, and never after an attempt has
   been made — the username field carries what was submitted, so a failed
   post comes back with the form open and the error above it. */
#loginBox:has(form#login input[name="do"][value="2fa"]) form#login,
#loginBox:has(form#login input[name="userid"]:not([value=""])) form#login { display: block; }
#loginBox:has(form#login input[name="do"][value="2fa"]) .gp-signin-alt,
#loginBox:has(form#login input[name="userid"]:not([value=""])) .gp-signin-alt { display: none; }

/* --- the form itself ----------------------------------------------------- */
#loginBox form#login { margin: 0; width: 100%; }
#loginBox fieldset {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0;
  padding: 0;
  border: 0;
}
#loginBox fieldset input[type="text"],
#loginBox fieldset input[type="password"] {
  width: 100%;
  height: 42px;
  margin: 0;
  padding: 0 14px;
  font: inherit;
  font-size: 14px;
  color: var(--gp-ink);
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm);
  box-shadow: none;
  transition: border-color var(--gp-t-color), box-shadow var(--gp-t-color);
}
#loginBox fieldset input::placeholder { color: var(--gp-ink-3); }
#loginBox input:focus,
#loginBox fieldset input[type="text"]:focus,
#loginBox fieldset input[type="password"]:focus {
  outline: 0;
  border-color: var(--gp-accent);
  box-shadow: 0 0 0 3px var(--gp-blue-ring);
}
#loginBox button.submit,
#loginBox button[type="submit"],
#loginBox .button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  order: 9;                 /* the action sits under the fields it submits */
  float: none;
  width: 100%;
  height: 44px;
  margin: 4px 0 0;
  padding: 0 18px;
  border: 1px solid var(--gp-accent);
  border-radius: var(--gp-r-pill);
  background: var(--gp-accent);
  color: #fff;
  font-size: 14px;
  font-weight: 500;
  box-shadow: none;
  cursor: pointer;
  transition: background var(--gp-t-color), border-color var(--gp-t-color);
}
#loginBox button.submit:hover,
#loginBox button[type="submit"]:hover,
#loginBox .button:hover {
  background: var(--gp-accent-strong);
  border-color: var(--gp-accent-strong);
  color: #fff;
}
#loginBox button[type="submit"]:disabled { opacity: 0.55; }
/* "Forgot my password" is a quiet link, not a heading */
#loginBox fieldset h3 {
  order: 10;
  margin: 2px 0 0;
  text-align: center;
  font-size: 13px;
  font-weight: 400;
}
#loginBox #reset-link { color: var(--gp-ink-3); text-decoration: none; }
#loginBox #reset-link:hover { color: var(--gp-accent); text-decoration: underline; text-underline-offset: 3px; }
#loginBox .hidden { display: none; }

/* the 2FA step renders a dynamic form inside the same <form> */
#loginBox form#login table { width: 100%; }
#loginBox form#login td { padding: 4px 0; }

/* --- footers -------------------------------------------------------------- */
#loginBox #company {
  position: static;
  left: auto;
  bottom: auto;
  width: auto;
  order: 9;
  margin: 22px 0 0;
  padding: 16px 0 0;
  border-top: 1px solid var(--gp-line-soft);
  color: var(--gp-ink-3);
  font-size: 12px;
  text-align: center;
}
/* login.css renders the copyright as a dark translucent chip with a text
   shadow — legible over the old wallpaper, not over a white card. */
#loginBox #company .content {
  display: block;
  padding: 0;
  background: none;
  border-radius: 0;
  box-shadow: none;
  text-shadow: none;
  color: inherit;
}
/* The way out. The page is otherwise a dead end: its logo points at
   scp/index.php, which bounces a signed-out visitor straight back here. */
#loginBody .gp-back-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 18px 0 0;
  padding: 8px 16px;
  border-radius: var(--gp-r-pill);
  color: var(--gp-ink-2);
  font-size: 13.5px;
  font-weight: 500;
  text-decoration: none;
  transition: background var(--gp-t-color), color var(--gp-t-color);
}
#loginBody .gp-back-link:hover {
  background: var(--gp-surface-2);
  color: var(--gp-ink);
}
#loginBody .gp-back-link:focus-visible {
  outline: 2px solid var(--gp-accent);
  outline-offset: 2px;
}
#loginBody .gp-back-link i { font-size: 12px; color: var(--gp-ink-3); }
#loginBody .gp-back-link:hover i { color: var(--gp-accent); }

/* login.css pins this to the viewport corner with a dark chip and text
   shadows meant for the old wallpaper. */
#loginBody #poweredBy {
  position: static;
  margin: 10px 0 0;
  padding: 0;
  background: none;
  border-radius: 0;
  box-shadow: none;
  text-shadow: none;
  color: var(--gp-ink-3);
  font-size: 11.5px;
  text-align: center;
}
#loginBody #poweredBy .osticket-logo {
  opacity: 0.45;
  vertical-align: middle;
  filter: none;
  -webkit-filter: none;
}

#loginBody #loading.dialog {
  position: fixed;
  inset: auto;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: auto;
  padding: 18px 22px;
}
#loginBody #loading h1 {
  margin: 0;
  font-size: 15px;
  font-weight: 500;
  color: var(--gp-ink-2);
}

@media (max-width: 480px) {
  #loginBody { padding: 24px 14px; }
  #loginBox { padding: 28px 20px 24px; }
}

/* =========================================================================
   18. Department access panel (include/staff/department.inc.php)
   =========================================================================
   One table answering "who can act in this department, and how". Rows come
   from three places - primary members, extended access, and agents
   inheriting through a parent - so Origin is a column rather than three
   separate tables with three separate headings.

   Each row carries a role select and four switches. Laid out inline that is
   five controls per row and unreadable at a glance, so the row shows a
   read-only summary and the controls live in a menu behind one cog. The
   menu is the panel's own .action-dropdown: it inherits the styling, and
   because it is only positioned rather than moved in the DOM, its inputs
   stay inside the form and post while hidden.
   ========================================================================= */

table.gp-access {
  border-collapse: separate;
  border-spacing: 0;
  table-layout: fixed;
}
table.gp-access th,
table.gp-access td {
  padding: 10px 12px;
  vertical-align: middle;         /* the cog and the name sit on one line */
  border-bottom: 1px solid var(--gp-line-soft);
}
table.gp-access thead th {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
  border-bottom: 1px solid var(--gp-line);
  text-align: left;
}
table.gp-access tbody tr:last-child td { border-bottom: 0; }

/* Fixed layout so the cog column cannot drift as names change length. */
/* Labels the second table on the agent Access tab. Matches the "Primary
   Department" heading above it - the two tables are peers and should
   announce themselves the same way. */
.gp-ac-heading {
  margin: 22px 0 6px;
  font-size: 18.2px;
  line-height: 27.3px;
  letter-spacing: 0.15px;
  color: var(--gp-ink);
}

.gp-ac-name   { width: 30%; }
.gp-ac-origin { width: 22%; }
.gp-ac-access { width: auto; }
.gp-ac-cog    { width: 56px; text-align: right; white-space: nowrap; }

.gp-ac-who { font-weight: 500; color: var(--gp-ink); }

/* Origin reads as a quiet tag, not a button: it is information, not a
   control. Inherited is tinted so a borrowed grant is distinguishable at a
   glance from one configured here. */
.gp-origin {
  display: inline-block;
  padding: 3px 9px;
  border-radius: var(--gp-r-pill);
  font-size: 11.5px;
  line-height: 1.5;
  white-space: nowrap;
  color: var(--gp-ink-2);
  background: var(--gp-surface-2);
  border: 1px solid var(--gp-line-soft);
}
.gp-origin-inherited {
  color: var(--gp-info);
  background: var(--gp-info-bg);
  border-color: var(--gp-info-edge);
}

.gp-ac-role { color: var(--gp-ink-2); }
.gp-ac-role.gp-ac-norole { color: var(--gp-err); }

.gp-ac-badges { display: inline; }
.gp-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 2px 8px;
  border-radius: var(--gp-r-pill);
  font-size: 11px;
  line-height: 1.6;
  white-space: nowrap;
  color: var(--gp-ink-3);
  background: var(--gp-surface-2);
  border: 1px solid var(--gp-line-soft);
}
.gp-badge-manager {
  color: var(--gp-brand);
  background: var(--gp-brand-tint);
  border-color: var(--gp-brand-edge);
  font-weight: 500;
}

/* The cog and the jump-to-source link share a footprint so the column keeps
   its rhythm whether a row is editable or inherited. */
.gp-ac-menu-btn,
.gp-ac-source,
.gp-ac-export {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: var(--gp-r-sm);
  color: var(--gp-ink-3);
  transition: background var(--gp-t-color), color var(--gp-t-color);
}
.gp-ac-menu-btn { cursor: pointer; border: 1px solid transparent; }
.gp-ac-menu-btn:hover,
.gp-ac-source:hover,
.gp-ac-export:hover {
  color: var(--gp-ink);
  background: var(--gp-surface-2);
}
.gp-ac-menu-btn.dropdown-open {
  color: var(--gp-ink);
  background: var(--gp-surface-2);
  border-color: var(--gp-line);
}
.gp-ac-menu-btn i,
.gp-ac-source i,
.gp-ac-export i { margin: 0; }

/* --- the menu ----------------------------------------------------------- */

/* The panel surface normally lives on `.action-dropdown ul`; this menu holds
   form controls rather than a list, so it carries the surface itself. Same
   tokens, so it reads as the same object as every other menu in the panel.
   z-index clears the table: rows after this one would otherwise paint over
   an open menu. text-align is reset because the cog cell is right-aligned
   and the menu would inherit it. */
.action-dropdown.gp-ac-menu {
  box-sizing: border-box;   /* padding inside the declared width */
  width: 320px;
  padding: 12px 14px;
  text-align: left;
  z-index: 60;
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm);
  box-shadow: 0 14px 34px -18px rgba(27, 28, 25, 0.45);
}
.gp-ac-menu-row {
  display: flex;
  align-items: center;
  gap: 10px;
}
/* Rhythm between rows. They were flush against each other, so a menu with a
   label, a field and a button read as one dense block. */
.gp-ac-menu-row + .gp-ac-menu-row { margin-top: 10px; }
.gp-ac-menu-row > textarea { flex: 1 1 auto; min-width: 0; margin: 0; }
/* A label above its field, not beside it, once the field is full width:
   beside works for a short select, not for a text box that needs the room. */
.gp-ac-menu-row:has(> textarea),
.gp-ac-menu-row:has(> input[type=text]) {
  display: block;
}
.gp-ac-menu-row:has(> textarea) > label,
.gp-ac-menu-row:has(> input[type=text]) > label {
  display: block;
  margin-bottom: 5px;
}
.gp-ac-menu-row > input[type=text] { width: 100%; margin: 0; }
/* The buttons at the foot of a menu need air above them and between them. */
.action-dropdown:not(:has(ul)) > form > button,
.action-dropdown:not(:has(ul)) > form > .action-button { margin: 12px 6px 0 0; }
.gp-ac-menu-row > label {
  flex: 0 0 auto;
  margin: 0;
  font-size: 12.5px;
  color: var(--gp-ink-3);
}
.gp-ac-menu-row > select {
  flex: 1 1 auto;
  min-width: 0;               /* a long role name must not widen the menu */
  margin: 0;
}
.gp-ac-menu-sep {
  height: 1px;
  margin: 10px -14px;
  background: var(--gp-line-soft);
}

/* Checkbox rows: the box is a fixed column so every label starts on the
   same x, and a wrapping label indents under its own text rather than
   under the box. */
label.gp-ac-check {
  display: grid;
  grid-template-columns: 16px 1fr;
  align-items: start;
  gap: 9px;
  margin: 0;
  padding: 5px 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--gp-ink-2);
  cursor: pointer;
}
label.gp-ac-check > input[type=checkbox] {
  margin: 1px 0 0;
  align-self: start;
}
label.gp-ac-check > span { min-width: 0; }
label.gp-ac-check:has(> input:disabled) {
  color: var(--gp-ink-3);
  cursor: default;
}
.gp-ac-manager-row > span { font-weight: 500; color: var(--gp-ink); }

a.gp-ac-remove {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  color: var(--gp-err);
}
a.gp-ac-remove:hover { color: var(--gp-err); text-decoration: underline; }

/* --- the settings tab's read-only manager line -------------------------- */

.gp-manager-note {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  color: var(--gp-ink);
}
.gp-manager-note em { color: var(--gp-ink-3); font-style: normal; }
a.gp-manager-jump { font-size: 12.5px; }

@media (max-width: 760px) {
  /* Origin stays: "via Support" is the answer to the question this table
     exists to answer, so it survives the narrow layout. The columns are let
     go instead, and the name and origin stack in one cell's worth of width. */
  table.gp-access { table-layout: auto; }
  table.gp-access th,
  table.gp-access td { padding: 9px 8px; }
  table.gp-access thead th.gp-ac-origin { display: none; }
  td.gp-ac-name { width: auto; }
  td.gp-ac-origin {
    width: 1%;
    padding-left: 0;
    white-space: nowrap;
  }
  .action-dropdown.gp-ac-menu { width: min(320px, calc(100vw - 32px)); }
}

/* The primary department's switches read as a list, not a paragraph: three
   inline labels wrapped mid-phrase and the second line looked orphaned. */
.gp-primary-switches {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 7px;
}
.gp-primary-switches > label.inline.checkbox {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  white-space: normal;
  /* scp.css gives label.checkbox a hanging indent (padding-left plus a
     negative text-indent) so the box hangs left of wrapped text. In a flex
     row that indent shifts the label under the checkbox and eats its first
     character - "Fall back..." rendered as "all back...". */
  padding-left: 0;
  text-indent: 0;
}
.gp-primary-switches > label.inline.checkbox > input { margin: 0; }

/* --- Redactor's reading measure ------------------------------------------
   redactor-osticket.js passes maxWidth: '950px' for .fullscreen editors, and
   Redactor writes it inline as `max-width: 950px; margin: auto`. It is meant
   as a reading measure for fullscreen composing, but it applies in every
   state. Upstream's 940px shell hid that - the cap was never reached, so the
   auto margins computed to zero. This console is wider, so the column started
   floating in the middle of its own box with the text out of line with the
   toolbar above it.

   Lift it only outside fullscreen, where the measure is doing nothing useful,
   and leave it intact where it was intended. Inline styles need !important. */
.redactor-box:not(.redactor-box-fullscreen) > .redactor-styles {
  max-width: none !important;
  margin-left: 0 !important;
  margin-right: 0 !important;
}

/* =========================================================================
   19. The close dialog's resolution section
   =========================================================================
   Rendered by templates/ticket-status.tmpl.php when the status change is a
   close. Each field lives in its own tbody so the conditional-visibility JS
   can slide the whole row; the widgets are the stock form widgets, so only
   the frame needs styling here. */

.gp-res-head td {
  padding-top: 14px;
}
.gp-res-title {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
  padding-bottom: 2px;
  border-bottom: 1px solid var(--gp-line-soft);
}
.gp-res-sub {
  margin-top: 6px;
  font-size: 12px;
  color: var(--gp-ink-3);
}
tbody.gp-res-field td {
  padding: 5px 0;
  vertical-align: middle;
}
td.gp-res-label {
  width: 150px;
  padding-right: 12px !important;
  font-size: 13px;
  color: var(--gp-ink-2);
  white-space: nowrap;      /* "Resolution summary" must not wrap */
}
/* One width for every control, so the column reads as a column. */
.gp-res-widget select,
.gp-res-widget input[type=text] {
  width: 100%;
  max-width: 360px;
  box-sizing: border-box;
  margin: 0;
}
.gp-res-widget .error {
  display: block;
  margin-top: 3px;
  font-size: 12px;
}

/* =========================================================================
   20. Resolution reports (scp/reports.php)
   =========================================================================
   Server-rendered: HTML bars for the Pareto, one inline SVG for the trend.
   Every number is a link into the underlying ticket list. */

.gp-rep-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 10px 14px;
  padding: 14px 16px;
  margin: 14px 0 18px;
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r);
}
.gp-rep-filters label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 0;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
}
.gp-rep-filters input[type=date],
.gp-rep-filters select {
  margin: 0;
  font-size: 13px;
  min-width: 130px;
}
.gp-rep-apply { align-self: flex-end; }
.gp-rep-exports {
  margin-left: auto;
  align-self: flex-end;
  display: flex;
  gap: 14px;
  font-size: 12.5px;
  white-space: nowrap;
}

/* --- headline cards ------------------------------------------------------ */
.gp-rep-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px;
  margin-bottom: 22px;
}
.gp-rep-card {
  padding: 14px 16px;
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r);
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.gp-rep-card.gp-rep-alert { border-color: var(--gp-warn-edge); background: var(--gp-warn-bg); }
.gp-rep-k {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
}
.gp-rep-v {
  font-family: var(--gp-mono);
  font-size: 24px;
  line-height: 1.1;
  color: var(--gp-ink);
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.gp-rep-delta { font-size: 12px; font-family: var(--gp-mono); }
.gp-rep-delta.good { color: var(--gp-ok); }
.gp-rep-delta.bad  { color: var(--gp-err); }

/* --- sections ------------------------------------------------------------ */
.gp-rep-section {
  padding: 18px 20px;
  margin-bottom: 18px;
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r);
}
.gp-rep-section > h3 {
  margin: 0 0 4px;
  font-size: 15px;
  font-weight: 600;
  color: var(--gp-ink);
}
.gp-rep-note { margin: 0 0 12px; font-size: 12.5px; color: var(--gp-ink-3); }
.gp-rep-cols {
  display: grid;
  grid-template-columns: 1fr 1.4fr;
  gap: 18px;
}
.gp-rep-cols .gp-rep-section { margin-bottom: 18px; }

/* --- the Pareto ---------------------------------------------------------- */
.gp-rep-bars { display: flex; flex-direction: column; gap: 6px; }
.gp-rep-bar {
  display: grid;
  grid-template-columns: 240px 1fr 44px 52px;
  align-items: center;
  gap: 10px;
  padding: 3px 6px;
  border-radius: var(--gp-r-sm);
  color: var(--gp-ink-2);
  transition: background var(--gp-t-color);
}
.gp-rep-bar:hover { background: var(--gp-accent-tint); color: var(--gp-accent); }
.gp-rep-bar-label {
  font-size: 13px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.gp-rep-bar-track {
  height: 14px;
  background: var(--gp-surface-2);
  border-radius: var(--gp-r-pill);
  overflow: hidden;
}
.gp-rep-bar-fill {
  display: block;
  height: 100%;
  background: var(--gp-accent);
  border-radius: var(--gp-r-pill);
}
.gp-rep-bar-n { font-family: var(--gp-mono); font-size: 13px; text-align: right; }
.gp-rep-bar-cum {
  font-family: var(--gp-mono);
  font-size: 11.5px;
  color: var(--gp-ink-3);
  text-align: right;
}
.gp-rep-others {
  margin-top: 14px;
  padding: 12px 14px;
  background: var(--gp-warn-bg);
  border: 1px solid var(--gp-warn-edge);
  border-radius: var(--gp-r-sm);
  font-size: 13px;
}
.gp-rep-others ul { margin: 8px 0 0 18px; }
.gp-rep-others li { margin: 2px 0; }

/* --- trend SVG ------------------------------------------------------------ */
.gp-rep-svg { width: 100%; max-width: 720px; height: auto; display: block; }
.gp-rep-svg-x { font-size: 11px; fill: var(--gp-ink-3); }
.gp-rep-svg-n { font-size: 11px; fill: var(--gp-ink-2); font-family: var(--gp-mono); }
.gp-rep-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
  margin-top: 8px;
  font-size: 12.5px;
  color: var(--gp-ink-2);
}
.gp-rep-legend i {
  display: inline-block;
  width: 10px; height: 10px;
  border-radius: 3px;
  margin-right: 6px;
}

/* --- tables ---------------------------------------------------------------- */
.gp-rep-table { border-collapse: collapse; width: 100%; }
.gp-rep-table th, .gp-rep-table td {
  padding: 7px 10px;
  border-bottom: 1px solid var(--gp-line-soft);
  font-size: 13px;
  text-align: left;
}
.gp-rep-table thead th {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
  border-bottom-color: var(--gp-line);
}
.gp-rep-table tbody tr:last-child td { border-bottom: 0; }
.gp-rep-table .gp-num { text-align: right; font-family: var(--gp-mono); }
.gp-rep-summary { max-width: 340px; }
.gp-rep-scroll { overflow-x: auto; }
.gp-rep-matrix td.faded { color: var(--gp-ink-3); }
.gp-rep-prevsplit {
  display: flex;
  gap: 18px;
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--gp-line-soft);
  font-size: 13px;
  flex-wrap: wrap;
}
.gp-rep-rowshead {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin: 4px 0 12px;
}
.gp-rep-crumbs { color: var(--gp-ink-3); font-size: 13px; }
.gp-rep-foot { font-size: 12px; margin-top: 4px; }

@media (max-width: 900px) {
  .gp-rep-cols { grid-template-columns: 1fr; }
  .gp-rep-bar { grid-template-columns: 130px 1fr 40px 48px; }
  .gp-rep-exports { margin-left: 0; }
}

/* --- report schedules (reports.php?view=schedules) ----------------------- */
/* Real links, not ul.tabs (scp.js preventDefaults those), styled to match
   the panel's tabs exactly so the difference is invisible. */
.gp-rep-nav {
  display: flex;
  gap: 2px;
  padding: 0 4px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--gp-line);
}
.gp-rep-nav > a {
  display: block;
  padding: 11px 14px 9px;
  font-size: 13.5px;
  font-weight: 500;
  line-height: 1.3;
  color: var(--gp-ink-2);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color var(--gp-t-color), border-color var(--gp-t-color);
}
.gp-rep-nav > a:hover { color: var(--gp-ink); }
.gp-rep-nav > a.active {
  color: var(--gp-accent);
  border-bottom-color: var(--gp-accent);
}
.gp-sched-cron {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  margin: 12px 0 16px;
  border-radius: var(--gp-r-sm);
  font-size: 13px;
}
.gp-sched-cron.ok   { background: var(--gp-ok-bg); border: 1px solid var(--gp-ok-edge); color: var(--gp-ok); }
.gp-sched-cron.warn { background: var(--gp-warn-bg); border: 1px solid var(--gp-warn-edge); color: var(--gp-warn); }
.gp-sched-paused td { opacity: 0.6; }
.gp-run-ok      { color: var(--gp-ok); font-weight: 500; }
.gp-run-failed  { color: var(--gp-err); font-weight: 500; }
.gp-run-running { color: var(--gp-warn); font-weight: 500; }
.gp-sched-actions form { display: flex; gap: 4px; margin: 0; }
button.gp-linklike {
  border: 0;
  background: none;
  padding: 4px 6px;
  cursor: pointer;
  color: var(--gp-ink-3);
  border-radius: var(--gp-r-sm);
  transition: background var(--gp-t-color), color var(--gp-t-color);
}
button.gp-linklike:hover { background: var(--gp-surface-2); color: var(--gp-ink); }
button.gp-linklike.danger:hover { background: var(--gp-err-bg); color: var(--gp-err); }

.gp-sched-form { display: flex; flex-direction: column; gap: 12px; }
.gp-sched-form label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 0;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
}
.gp-sched-form input[type=text],
.gp-sched-form input[type=number],
.gp-sched-form select { margin: 0; font-size: 13px; }
.gp-sched-row { display: flex; gap: 12px; flex-wrap: wrap; }
.gp-sched-row > label { flex: 1 1 120px; }
.gp-sched-dest {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  background: var(--gp-surface-2);
  border-radius: var(--gp-r-sm);
}
.gp-sched-check {
  flex-direction: row !important;
  align-items: center;
  gap: 8px !important;
  text-transform: none !important;
  letter-spacing: 0 !important;
  font-size: 13px !important;
  color: var(--gp-ink-2) !important;
  cursor: pointer;
}
.gp-sched-check input { margin: 0; }
.gp-sched-recips { margin-left: 24px !important; }
.gp-sched-privacy { font-size: 12.5px; margin: 0; }
.gp-sched-privacy.warn { color: var(--gp-warn); }

/* --- Google Drive settings (reports.php?view=drive) ----------------------- */
.gp-drive-models {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}
.gp-drive-model {
  display: flex !important;
  flex-direction: column;
  gap: 6px !important;
  padding: 14px 16px;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r);
  cursor: pointer;
  text-transform: none !important;
  letter-spacing: 0 !important;
  transition: border-color var(--gp-t-color), background var(--gp-t-color);
}
.gp-drive-model.active {
  border-color: var(--gp-accent);
  background: var(--gp-accent-tint);
}
.gp-drive-model input { position: absolute; opacity: 0; }
.gp-drive-model-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--gp-ink);
  display: flex;
  align-items: center;
  gap: 8px;
}
.gp-drive-model-desc {
  font-size: 12.5px;
  font-weight: 400;
  color: var(--gp-ink-2);
  line-height: 1.5;
}
.gp-drive-pane {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px;
  background: var(--gp-surface-2);
  border-radius: var(--gp-r-sm);
}
.gp-drive-pane textarea {
  font-family: var(--gp-mono);
  font-size: 12px;
  margin: 0;
}
.gp-drive-pane code {
  background: var(--gp-surface);
  padding: 2px 6px;
  border-radius: 4px;
  font-size: 12px;
}
.gp-drive-inlineform {
  display: inline-flex;
  gap: 8px;
  margin: 0 0 0 auto;
}
@media (max-width: 760px) {
  .gp-drive-models { grid-template-columns: 1fr; }
}
.gp-install-banner { align-items: flex-start; }
.gp-install-banner > div { flex: 1 1 auto; }
.gp-install-banner > div > div { margin-top: 3px; font-weight: 400; }
.gp-install-banner form { margin: 0; flex: 0 0 auto; }

/* =========================================================================
   21. The agent guide (templates/agent-guide.tmpl.php)
   =========================================================================
   A chaptered overlay with working mock-ups rather than a video: the agent
   clicks the same controls they will use, built from the same tokens, so
   the guide cannot drift away from the product's appearance.

   Rendered in the page header, outside #pjax-container, so it survives
   navigation. Everything is scoped under .gp-guide. */

.gp-guide-link i { margin-right: 4px; opacity: 0.75; }

.gp-guide[hidden] { display: none; }
.gp-guide {
  position: fixed;
  inset: 0;
  z-index: 9000;             /* above the sticky bars and any dropdown */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
body.gp-guide-open { overflow: hidden; }

.gp-guide-scrim {
  position: absolute;
  inset: 0;
  background: rgba(27, 28, 25, 0.55);
  backdrop-filter: blur(2px);
}

.gp-guide-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  width: min(1040px, 100%);
  max-height: min(720px, calc(100vh - 48px));
  background: var(--gp-surface);
  border-radius: var(--gp-r);
  box-shadow: 0 30px 70px -24px rgba(27, 28, 25, 0.5);
  overflow: hidden;
  outline: none;
}

/* --- header ------------------------------------------------------------- */
.gp-guide-head {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 18px 22px 14px;
  border-bottom: 1px solid var(--gp-line);
}
.gp-guide-head h2 {
  margin: 0;
  font-size: 17px;
  font-weight: 600;
  color: var(--gp-ink);
}
.gp-guide-sub { margin: 3px 0 0; font-size: 12.5px; color: var(--gp-ink-3); }
.gp-guide-x {
  margin-left: auto;
  border: 0;
  background: none;
  cursor: pointer;
  color: var(--gp-ink-3);
  padding: 6px 8px;
  border-radius: var(--gp-r-sm);
  transition: background var(--gp-t-color), color var(--gp-t-color);
}
.gp-guide-x:hover { background: var(--gp-surface-2); color: var(--gp-ink); }

/* --- body: rail + chapter ------------------------------------------------ */
.gp-guide-body {
  display: grid;
  grid-template-columns: 232px 1fr;
  min-height: 0;             /* lets the chapter pane scroll, not the page */
  flex: 1 1 auto;
}
.gp-guide-rail {
  border-right: 1px solid var(--gp-line);
  background: var(--gp-page);
  overflow-y: auto;
  padding: 10px 0;
}
.gp-guide-rail ol { list-style: none; margin: 0; padding: 0; }
.gp-guide-tab {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 16px;
  border: 0;
  background: none;
  text-align: left;
  cursor: pointer;
  font-size: 13px;
  color: var(--gp-ink-2);
  border-left: 2px solid transparent;
  transition: background var(--gp-t-color), color var(--gp-t-color);
}
.gp-guide-tab:hover { background: var(--gp-surface-2); color: var(--gp-ink); }
.gp-guide-tab.is-on {
  background: var(--gp-accent-tint);
  border-left-color: var(--gp-accent);
  color: var(--gp-accent);
  font-weight: 500;
}
.gp-guide-num {
  flex: 0 0 auto;
  width: 20px; height: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--gp-r-pill);
  background: var(--gp-surface-2);
  font-family: var(--gp-mono);
  font-size: 11px;
  color: var(--gp-ink-3);
}
.gp-guide-tab.is-on .gp-guide-num { background: var(--gp-accent); color: #fff; }
.gp-guide-tabname { flex: 1 1 auto; min-width: 0; }
.gp-guide-tick { opacity: 0; font-size: 11px; color: var(--gp-ok); }
.gp-guide-tab.is-done .gp-guide-tick { opacity: 1; }
.gp-guide-tab.is-done .gp-guide-num { background: var(--gp-ok-bg); color: var(--gp-ok); }

.gp-guide-main { overflow-y: auto; padding: 22px 26px 26px; min-width: 0; }
.gp-guide-eyebrow {
  margin: 0 0 6px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
}
.gp-guide-main h3 {
  margin: 0 0 4px;
  font-size: 21px;
  font-weight: 600;
  color: var(--gp-ink);
}
.gp-guide-lede { margin: 0 0 18px; font-size: 14px; color: var(--gp-ink-2); }
.gp-guide-cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 22px;
  align-items: start;
}
.gp-guide-prose p {
  margin: 0 0 11px;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--gp-ink-2);
}
.gp-guide-prose b { color: var(--gp-ink); font-weight: 600; }
.gp-guide-try {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 10px;
  font-size: 12.5px;
  color: var(--gp-brand);
}

/* --- footer -------------------------------------------------------------- */
.gp-guide-foot {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px 22px;
  border-top: 1px solid var(--gp-line);
  background: var(--gp-page);
}
.gp-guide-dots { display: flex; gap: 5px; }
.gp-guide-dots span {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--gp-line);
  transition: background var(--gp-t-color);
}
.gp-guide-dots span.is-on { background: var(--gp-accent); }
.gp-guide-actions { margin-left: auto; display: flex; gap: 8px; }
.gp-guide-actions .action-button[disabled] { opacity: 0.4; cursor: default; }
.gp-guide-next {
  background: var(--gp-accent);
  border-color: var(--gp-accent);
  color: #fff;
}
.gp-guide-next:hover { background: var(--gp-accent-strong); color: #fff; }

/* --- the mock-ups -------------------------------------------------------
   Built from the same tokens as the real panel so the guide cannot look
   like a different product. Inert: no form, no names, nothing navigable. */
.gp-gd {
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm);
  background: var(--gp-surface);
  overflow: hidden;
  font-size: 12.5px;
}
.gp-gd-tipline {
  padding: 9px 12px;
  border-top: 1px solid var(--gp-line-soft);
  background: var(--gp-page);
  color: var(--gp-ink-3);
  min-height: 34px;
  transition: color var(--gp-t-color);
}
.gp-gd-tipline.is-live { color: var(--gp-accent); }

/* nav + toolbar rows share the chip treatment */
.gp-gd-navbar, .gp-gd-tbar {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 10px 12px;
}
.gp-gd-navitem, .gp-gd-btn {
  padding: 5px 10px;
  border-radius: var(--gp-r-sm);
  border: 1px solid transparent;
  color: var(--gp-ink-2);
  cursor: default;
  white-space: nowrap;
  transition: background var(--gp-t-color), border-color var(--gp-t-color),
              color var(--gp-t-color);
}
.gp-gd-btn { border-color: var(--gp-line); background: var(--gp-surface); }
.gp-gd-navitem:hover, .gp-gd-btn:hover {
  background: var(--gp-accent-tint);
  border-color: var(--gp-accent-edge);
  color: var(--gp-accent);
}
.gp-gd-navitem.is-on { color: var(--gp-accent); font-weight: 500; }
.gp-gd-btn i { margin-right: 5px; opacity: 0.7; }

/* queue */
.gp-gd-strip {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 12px;
  background: var(--gp-surface-2);
  border-bottom: 1px solid var(--gp-line-soft);
  flex-wrap: wrap;
}
.gp-gd-chip {
  padding: 3px 10px;
  border-radius: var(--gp-r-pill);
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  color: var(--gp-ink-2);
}
.gp-gd-chip.is-on { border-color: var(--gp-accent); color: var(--gp-accent); }
.gp-gd-chip b { font-family: var(--gp-mono); margin-left: 3px; }
.gp-gd-search {
  margin-left: auto;
  color: var(--gp-ink-3);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-pill);
  padding: 3px 12px;
}
.gp-gd-search i { margin-right: 5px; }
table.gp-gd-list { width: 100%; border-collapse: collapse; }
.gp-gd-list th {
  text-align: left;
  padding: 7px 12px;
  font-size: 10.5px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
  border-bottom: 1px solid var(--gp-line-soft);
  font-weight: 500;
}
.gp-gd-list td {
  padding: 9px 12px;
  border-bottom: 1px solid var(--gp-line-soft);
  color: var(--gp-ink-2);
}
.gp-gd-list tbody tr { cursor: pointer; transition: background var(--gp-t-color); }
.gp-gd-list tbody tr:hover { background: var(--gp-accent-tint); }
.gp-gd-list tbody tr.is-hit { background: var(--gp-ok-bg); }
.gp-gd-list tbody tr:last-child td { border-bottom: 0; }
.gp-gd-pri {
  padding: 1px 8px;
  border-radius: var(--gp-r-pill);
  background: var(--gp-surface-2);
  border: 1px solid var(--gp-line-soft);
  font-size: 11px;
}
.gp-gd-pri-low { color: var(--gp-ink-3); }
.gp-gd-open {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  background: var(--gp-ok-bg);
  border-top: 1px solid var(--gp-ok-edge);
  color: var(--gp-ok);
}

/* compose (reply / note) */
.gp-gd-tabs {
  display: flex;
  gap: 2px;
  padding: 0 12px;
  border-bottom: 1px solid var(--gp-line);
  background: var(--gp-page);
}
.gp-gd-tabs span {
  padding: 9px 12px;
  color: var(--gp-ink-3);
  border-bottom: 2px solid transparent;
  cursor: pointer;
  transition: color var(--gp-t-color), border-color var(--gp-t-color);
}
.gp-gd-tabs span:hover { color: var(--gp-ink); }
.gp-gd-tabs span.is-on {
  color: var(--gp-accent);
  border-bottom-color: var(--gp-accent);
  font-weight: 500;
}
.gp-gd-field {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  border-bottom: 1px solid var(--gp-line-soft);
}
.gp-gd-field > label {
  flex: 0 0 116px;
  margin: 0;
  font-size: 11.5px;
  color: var(--gp-ink-3);
}
.gp-gd-field > select,
.gp-gd-field > input[type=text] {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  font-size: 12.5px;
}
.gp-gd-field > select.is-bad,
.gp-gd-field > input.is-bad { border-color: var(--gp-err); }
.gp-gd-static, .gp-gd-select {
  flex: 1 1 auto;
  color: var(--gp-ink-2);
}
.gp-gd-select {
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm);
  padding: 4px 10px;
  color: var(--gp-ink-3);
  background: var(--gp-surface);
}
.gp-gd-editor {
  padding: 16px 12px;
  min-height: 62px;
  color: var(--gp-ink-3);
  font-style: italic;
  border-bottom: 1px solid var(--gp-line-soft);
}
.gp-gd-editor-in { background: var(--gp-warn-bg); }
.gp-gd-outbound {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 8px 12px;
  font-size: 11.5px;
  color: var(--gp-info);
  background: var(--gp-info-bg);
}
.gp-gd-outbound.gp-gd-internal { color: var(--gp-warn); background: var(--gp-warn-bg); }

/* dialogs */
.gp-gd-dialog-head {
  padding: 11px 12px;
  border-bottom: 1px solid var(--gp-line);
  font-weight: 600;
  color: var(--gp-ink);
}
.gp-gd-aside {
  margin: 0;
  padding: 10px 12px;
  color: var(--gp-ink-3);
  background: var(--gp-page);
}
.gp-gd-req { color: var(--gp-err); }
.gp-gd-dialog-foot {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  background: var(--gp-page);
}
.gp-gd-msg { flex: 1 1 auto; font-size: 12px; color: var(--gp-ink-3); }
.gp-gd-msg.is-bad { color: var(--gp-err); }
.gp-gd-msg.is-ok  { color: var(--gp-ok); }
.gp-gd-dialog.is-done { border-color: var(--gp-ok-edge); }
.gp-gd-done {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 22px 16px;
  color: var(--gp-ok);
  background: var(--gp-ok-bg);
}
.gp-gd-done p { margin: 0; }
.gp-gd-done i { font-size: 20px; }

/* --- narrow screens ------------------------------------------------------
   The rail becomes a horizontal strip and the two columns stack, so the
   guide is usable on a phone rather than merely not broken. */
@media (max-width: 860px) {
  .gp-guide { padding: 0; }
  .gp-guide-panel { max-height: 100vh; border-radius: 0; width: 100%; }
  .gp-guide-body { grid-template-columns: 1fr; }
  .gp-guide-rail {
    border-right: 0;
    border-bottom: 1px solid var(--gp-line);
    overflow-x: auto;
    padding: 6px 8px;
  }
  .gp-guide-rail ol { display: flex; gap: 4px; }
  .gp-guide-tab {
    border-left: 0;
    border-bottom: 2px solid transparent;
    padding: 7px 10px;
    white-space: nowrap;
    border-radius: var(--gp-r-sm);
  }
  .gp-guide-tab.is-on { border-bottom-color: var(--gp-accent); }
  .gp-guide-tabname { display: none; }      /* numbers only when tight */
  .gp-guide-tab.is-on .gp-guide-tabname { display: inline; }
  .gp-guide-cols { grid-template-columns: 1fr; gap: 16px; }
  .gp-guide-main { padding: 18px 16px 20px; }
  .gp-guide-main h3 { font-size: 18px; }
}

/* A display rule beats the UA's [hidden] { display: none }, and several of
   the mock-ups set display:flex on elements the demo script toggles with
   the hidden property. Without this the "Root cause detail" row - the one
   that is supposed to appear only for Other - is visible from the start,
   which teaches the opposite of the lesson. */
.gp-guide [hidden] { display: none !important; }

/* Narrow: stack each field so a select never pushes the mock wider than
   the panel. .gp-gd clips its overflow, so an overspilling row is cut off
   rather than scrollable - it has to fit. */
@media (max-width: 860px) {
  .gp-gd-field { flex-wrap: wrap; gap: 4px; }
  .gp-gd-field > label { flex: 1 1 100%; }
  .gp-gd-field > select,
  .gp-gd-field > input[type=text],
  .gp-gd-static, .gp-gd-select { flex: 1 1 100%; }
  .gp-gd-dialog-foot { flex-wrap: wrap; }
}
/* The mock controls are spans and rows, so they carry tabindex; give them
   the same focus treatment the real panel uses. */
.gp-gd [tabindex="0"]:focus-visible {
  outline: 2px solid var(--gp-accent);
  outline-offset: 1px;
  border-radius: var(--gp-r-sm);
}

/* =========================================================================
   22. Agent signature library (include/staff/signature-editor.inc.php)

   A collapsed row per signature, so an agent with four of them sees four
   lines rather than four editors. The row header carries everything needed
   to answer "which one goes where" without opening anything: the name, the
   departments as chips, and which one is the fallback.
   ========================================================================= */

.gp-sig { max-width: var(--gp-w-console); }

/* The profile's Signature tab wraps this editor in the shared two-column
   table. Auto table layout sizes cells from their content, so a
   signature's unbroken base64 line (every logo here is a data: URI)
   pushed the cell - and the whole page - wider each time a row was
   opened; overflow:auto on the <pre> never engaged because the cell
   simply grew instead. Fixed layout pins the table at its declared 100%
   and lets the editor's own overflow rules do their job. */
#signature table.two-column { table-layout: fixed; width: 100%; }
.gp-sig-source-ro { max-width: 100%; }
.gp-sig-body { min-width: 0; }

.gp-sig-lede {
  margin: 0 0 14px;
  color: var(--gp-ink-2);
  font-size: 13.5px;
  line-height: 1.55;
}

.gp-sig-selfmanage {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 16px;
  /* scp.css gives label.checkbox a hanging indent (padding-left plus a
     negative text-indent) which, under flex, clips the first character. */
  padding-left: 0;
  text-indent: 0;
  white-space: normal;
}

.gp-sig-empty { color: var(--gp-ink-3); }

.gp-sig-list {
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-md, 10px);
  overflow: hidden;
  background: var(--gp-surface);
}
.gp-sig-list:empty { display: none; }

.gp-sig-item + .gp-sig-item { border-top: 1px solid var(--gp-line-soft); }

.gp-sig-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 14px;
  cursor: pointer;
}
.gp-sig-head:hover { background: var(--gp-surface-2); }

.gp-sig-grip {
  color: var(--gp-ink-3);
  transition: transform 0.12s ease;
  flex: 0 0 auto;
}
.gp-sig-item.is-open .gp-sig-grip { transform: rotate(90deg); }

.gp-sig-name {
  flex: 0 0 220px;
  font-weight: 500;
  /* Reset osTicket's global input width so the field sits in the row. */
  width: auto;
  margin: 0;
}
.gp-sig-name-text { flex: 0 0 220px; font-weight: 500; color: var(--gp-ink); }

/* Chips wrap rather than scroll: a signature used in nine departments should
   grow the row, not hide six of them behind an edge. */
.gp-sig-chips {
  flex: 1 1 auto;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 0;
  min-width: 0;
}

.gp-sig-default {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 5px;
  margin: 0;
  padding-left: 0;
  text-indent: 0;
  font-size: 12.5px;
  color: var(--gp-ink-2);
  white-space: nowrap;
}
.gp-sig-default input { margin: 0; }

.gp-sig-del {
  flex: 0 0 auto;
  border: 0;
  background: none;
  color: var(--gp-ink-3);
  cursor: pointer;
  padding: 4px 6px;
  border-radius: var(--gp-r-sm, 6px);
}
.gp-sig-del:hover { color: var(--gp-err); background: var(--gp-err-bg); }

.gp-sig-body {
  padding: 4px 14px 18px;
  border-top: 1px dashed var(--gp-line-soft);
  background: var(--gp-page);
}

.gp-sig-field { margin-top: 14px; }
.gp-sig-field > label {
  display: block;
  margin-bottom: 5px;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--gp-ink-2);
}
.gp-sig-field .faded { margin-top: 5px; font-size: 12px; }

/* The bare list box is the no-JS fallback; select2 replaces it with the tag
   field used elsewhere in the panel. Both are capped to the same width, and
   the cap has to be repeated on .select2-container because that is a sibling
   of the <select>, not a wrapper around it. */
.gp-sig-depts { width: 100%; max-width: 460px; min-height: 132px; }
/* #content-prefixed: an earlier rule sets `#content .select2-container
   { max-width: 100% }`, and an id beats two classes. */
#content .gp-sig-field .select2-container,
.gp-sig-field .select2-container { max-width: 460px; }

#content .gp-sig-field .select2-container--default .select2-selection--multiple {
  min-height: 38px;
  padding: 2px 4px;
  background: var(--gp-surface);
}
#content .gp-sig-field .select2-selection__choice {
  /* Same pill as the chips in the row header, so a department reads the
     same whether the row is open or closed. */
  background: var(--gp-accent-tint);
  border: 1px solid var(--gp-accent-edge);
  color: var(--gp-accent);
  border-radius: var(--gp-r-pill);
  padding: 2px 9px;
  margin: 3px 4px 3px 0;
  font-size: 12px;
  line-height: 1.6;
}
#content .gp-sig-field .select2-selection__choice__remove {
  color: var(--gp-accent);
  margin-right: 5px;
  opacity: 0.65;
}
#content .gp-sig-field .select2-selection__choice__remove:hover { opacity: 1; }

/* "· in Admissions" beside a department another signature holds. Muted: it
   is a note about the consequence of choosing it, not part of the name. */
.gp-sig-held { color: var(--gp-ink-3); font-size: 12px; }

.gp-sig-source,
.gp-sig-source-ro {
  width: 100%;
  font-family: var(--gp-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 12px;
  line-height: 1.5;
  white-space: pre;
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm, 6px);
}
.gp-sig-source-ro {
  margin: 0;
  padding: 10px;
  max-height: 260px;
  overflow: auto;
}

.gp-sig-preview {
  width: 100%;
  height: 240px;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm, 6px);
  background: #fff;
}

.gp-sig-size { font-weight: 400; margin-left: 6px; }

.gp-sig-add { margin-top: 12px; }

@media (max-width: 720px) {
  /* The header stops being a row: at this width a 220px name field and a
     wrapping chip list cannot share one line without one of them winning. */
  .gp-sig-head { flex-wrap: wrap; }
  .gp-sig-name,
  .gp-sig-name-text { flex: 1 1 100%; }
  .gp-sig-chips { flex: 1 1 100%; }
}

/* =========================================================================
   23. The issue log (scp/issues.php)

   Reuses the access-panel table treatment so the log reads as part of the
   same panel. Severity and status are the only new colour: everything an
   agent scans for at a glance is one of those two.
   ========================================================================= */

/* The internal desk shares this furniture rather than copying it. Joined to
   the selector instead of redeclared, so there is one rule to change and the
   two desks cannot drift apart. */
/* (Width and top padding used to be set here, per family. The page sheet
   #content now bounds every page and gp_page_bar() spaces the bar, so the
   two families start at the same place — see §25 .gp-shell.) */
/* The bar is its own band; the content under it needs air, the way the
   ticket view's title block has it. */

.gp-issues-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 16px;
}
.gp-issues-head h2 { margin: 0; }

/* The detail page's own heading, under the sticky bar - the bar carries the
   record locator and the actions, this carries what the issue actually is.
   Mirrors the ticket view's tixTitle. */
.gp-issue-head { margin-bottom: 16px; }
.gp-issue-headline {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.gp-issue-headline h2 { margin: 0; }

/* Sits in the bar, before the record locator, so it reads as "up one level"
   rather than as a link that lost its paragraph. */
/* The back chevron, identical on tickets, tasks, users and issues (rendered
   by gp_back_link in include/staff/gp-ui.inc.php).

   The padding is symmetric on purpose: the arrow used to sit hard against
   the divider while the bar's own padding gave it room on the left, so it
   read as belonging to the title rather than sitting between the two. */
a.gp-back {
  display: inline-flex;
  align-items: center;
  align-self: stretch;      /* the divider runs the height of the row */
  /* 19px lands the glyph 20px clear of the divider, matching the bar's own
     20px of padding on the other side of it. */
  margin-right: 19px;
  padding-right: 19px;
  border-right: 1px solid var(--gp-line);
  color: var(--gp-ink-3);
  text-decoration: none;
}
/* A detail page's bar heading is a row of unequal boxes — a chevron, a mono
   locator chip, sometimes a status pill. Baseline alignment lined up their
   text and left their boxes a few pixels apart; centring them lines up what
   the eye actually reads, the boxes. */
.sticky.bar .content h2,
.sticky.bar .content .flush-left h2 {
  display: inline-flex;
  align-items: center;
  gap: 0;
}
/* .sticky.bar h2 a sets ink, which would make the chevron in a bar darker
   than the identical one on a page without a bar. */
.sticky.bar h2 a.gp-back,
.sticky.bar .flush-left a.gp-back { color: var(--gp-ink-3); }
a.gp-back:hover,
.sticky.bar h2 a.gp-back:hover { color: var(--gp-accent); }
/* scp.css paints stock icons #0a0 and the theme only neutralises refresh and
   filter, so a chevron dropped into the bar arrives bright green. */
a.gp-back i { color: inherit; font-size: 0.8em; }

.gp-issues-empty {
  padding: 40px 24px;
  text-align: center;
  color: var(--gp-ink-2);
  border: 1px dashed var(--gp-line);
  border-radius: var(--gp-r-md, 10px);
}

/* ---- the numbers ------------------------------------------------------ */
.gp-issue-stats,
.gp-ir-stats {
  display: flex;
  flex-wrap: wrap;
  /* start, not stretch: otherwise the causes card - which is a list - drags
     every single-number card to its height and leaves them mostly empty. */
  align-items: flex-start;
  gap: 10px;
  margin-bottom: 18px;
}
.gp-stat {
  flex: 1 1 120px;
  padding: 12px 14px;
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-md, 10px);
}
.gp-stat-n { display: block; font-size: 22px; line-height: 1.2; color: var(--gp-ink); }
.gp-stat-l { display: block; margin-top: 2px; font-size: 11.5px;
             text-transform: uppercase; letter-spacing: 0.4px; color: var(--gp-ink-3); }
.gp-stat-causes { flex: 2 1 260px; }
.gp-stat-causes ol { margin: 6px 0 0; padding-left: 18px; }
.gp-stat-causes li {
  display: flex; justify-content: space-between; gap: 10px;
  font-size: 12.5px; color: var(--gp-ink-2); line-height: 1.7;
}

/* ---- filters ---------------------------------------------------------- */
.gp-issue-filters,
.gp-ir-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-bottom: 14px;
}
.gp-issue-filters input[type=search],
.gp-ir-filters input[type=search] { flex: 1 1 220px; min-width: 180px; margin: 0; }
/* Bounded, and #content-prefixed to outrank `#content select { max-width:
   100% }`: the cause list holds the longest option in the panel, and an
   unbounded select sized itself to it and squeezed everything else out. */
#content .gp-issue-filters select,
#content .gp-ir-filters select,
.gp-issue-filters select,
.gp-ir-filters select { margin: 0; max-width: 190px; }
.gp-issue-filters .action-button,
.gp-ir-filters .action-button { margin: 0; }

/* ---- the table -------------------------------------------------------- */

/* Header and cell must agree, column by column. A <th> defaults to centred
   and a <td> to start, which is why every heading sat over the middle of a
   column whose contents began at the left. Everything reads left, matching
   the ticket queue; the two numeric columns are centred on BOTH halves. */
.gp-issue-table th,
.gp-issue-table td { text-align: left; }
.gp-issue-table th.gp-iss-id,
.gp-issue-table td.gp-iss-id,
.gp-issue-table th.gp-iss-tickets,
.gp-issue-table td.gp-iss-tickets { text-align: center; }

/* The whole row opens the issue (see the handler in issues.inc.php), so it
   has to look like it does. */

.gp-iss-id      { width: 62px; }
.gp-iss-sev     { width: 96px; }
.gp-iss-tickets { width: 74px; }
.gp-iss-age     { width: 90px; }
.gp-iss-status  { width: 96px; }
.gp-iss-title a { font-weight: 500; color: var(--gp-ink); }
.gp-iss-title a:hover { color: var(--gp-accent); }
.gp-iss-id { font-family: var(--gp-mono); font-variant-numeric: tabular-nums;
             font-size: 12.5px; color: var(--gp-ink-3); }
.gp-iss-age { color: var(--gp-ink-2); white-space: nowrap; }

/* Severity and status are pills rather than words in colour: a coloured
   word next to a black one reads as a mistake, a pill reads as a label. */
.gp-sev, .gp-istat, .gp-irstat, .gp-pill {
  display: inline-block;
  padding: 2px 9px;
  border-radius: var(--gp-r-pill);
  font-size: 11.5px;
  line-height: 1.6;
  white-space: nowrap;
  border: 1px solid var(--gp-line-soft);
  background: var(--gp-surface-2);
  color: var(--gp-ink-2);
}
/* (.gp-irstat and .gp-pill share the body above; see §25.) */
.gp-sev-critical { background: var(--gp-err-bg);  border-color: var(--gp-err-edge);  color: var(--gp-err); }
.gp-sev-high     { background: var(--gp-warn-bg); border-color: var(--gp-warn-edge); color: var(--gp-warn); }
.gp-sev-medium   { background: var(--gp-info-bg); border-color: var(--gp-info-edge); color: var(--gp-info); }

.gp-istat-open    { background: var(--gp-info-bg); border-color: var(--gp-info-edge); color: var(--gp-info); }
.gp-istat-solved  { background: var(--gp-ok-bg);   border-color: var(--gp-ok-edge);   color: var(--gp-ok); }
.gp-istat-wontfix { background: var(--gp-surface-2); color: var(--gp-ink-3); }

.gp-issue-pager { margin-top: 14px; }

/* ---- one issue -------------------------------------------------------- */
.gp-issue-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 28px;
  padding: 14px 16px;
  margin-bottom: 16px;
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-md, 10px);
}
.gp-issue-meta > div { display: flex; flex-direction: column; gap: 3px; }
.gp-issue-meta span {
  font-size: 11px; text-transform: uppercase;
  letter-spacing: 0.4px; color: var(--gp-ink-3);
}
.gp-issue-meta b { font-weight: 500; color: var(--gp-ink); }

.gp-issue-desc,
.gp-issue-resolution {
  padding: 14px 16px;
  margin-bottom: 16px;
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-md, 10px);
  line-height: 1.6;
}
.gp-issue-resolution.is-solved  { background: var(--gp-ok-bg); border-color: var(--gp-ok-edge); }
/* Neutral, not green: nothing was fixed here, and the panel should not
   congratulate anybody for it. */
.gp-issue-resolution.is-wontfix { background: var(--gp-surface-2); border-color: var(--gp-line); }
.gp-issue-resolution p { margin: 8px 0 0; }

/* Inside the sticky bar now, so it inherits the ticket toolbar's spacing.
   Only the bits the bar does not already say. */
.gp-issue-actions form { display: inline; margin: 0; }
.gp-issue-actions .action-button { vertical-align: middle; }
.gp-danger { color: var(--gp-err); }
.gp-danger:hover { background: var(--gp-err-bg); border-color: var(--gp-err-edge); }

/* The close-out and task menus hold real forms, so they need more room than
   the access panel's menus and their controls must fill the width. */
#issue-solve-menu, #issue-assign-menu, #issue-task-menu,
#issue-merge-menu { box-sizing: border-box; width: 320px; }
#issue-solve-menu textarea,
#issue-solve-menu select,
#issue-assign-menu select,
#issue-task-menu input[type=text],
#issue-merge-menu select { width: 100%; margin: 0; }
#issue-solve-menu button, #issue-task-menu button,
#issue-assign-menu button, #issue-merge-menu button { margin: 14px 6px 0 0; }

/* Every row in these menus reads the same way: label above, field full
   width. One menu mixing a label-beside row with a label-above row looks
   like two menus stuck together. */
#issue-solve-menu .gp-ac-menu-row,
#issue-task-menu .gp-ac-menu-row,
#issue-assign-menu .gp-ac-menu-row,
#issue-merge-menu .gp-ac-menu-row { display: block; }
#issue-solve-menu .gp-ac-menu-row > label,
#issue-task-menu .gp-ac-menu-row > label,
#issue-assign-menu .gp-ac-menu-row > label,
#issue-merge-menu .gp-ac-menu-row > label { display: block; margin-bottom: 5px; }
#issue-solve-menu .gp-ac-menu-row + .gp-ac-menu-row,
#issue-task-menu .gp-ac-menu-row + .gp-ac-menu-row,
#issue-assign-menu .gp-ac-menu-row + .gp-ac-menu-row,
#issue-merge-menu .gp-ac-menu-row + .gp-ac-menu-row { margin-top: 12px; }
#issue-solve-menu .faded, #issue-task-menu .faded,
#issue-assign-menu .faded, #issue-merge-menu .faded { margin-top: 6px; font-size: 12px; }
/* Fields fill the menu. A select that sizes to its longest option leaves a
   131px control under a 290px one, which reads as a mistake. */
#content #issue-solve-menu select, #content #issue-task-menu select,
#content #issue-assign-menu select, #content #issue-merge-menu select,
#issue-solve-menu textarea, #issue-task-menu input[type=text] {
  width: 100%;
  max-width: none;
}

.gp-issue-prompt {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 14px;
  margin-bottom: 16px;
  background: var(--gp-info-bg);
  border: 1px solid var(--gp-info-edge);
  border-radius: var(--gp-r-md, 10px);
  color: var(--gp-info);
  font-size: 13px;
}

/* Tickets and tasks side by side above the history: they are the record of
   what this issue touched, and the history is the record of what people did
   about it. Different questions, so a row each rather than a column each. */
.gp-issue-related {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  align-items: flex-start;
  margin-bottom: 22px;
}
.gp-issue-panel { flex: 1 1 380px; min-width: 300px; }

.gp-issue-cols { display: block; }
.gp-issue-stream { min-width: 0; }
.gp-issue-panel h3, .gp-issue-stream h3 {
  margin: 0 0 8px; font-size: 13px; text-transform: uppercase;
  letter-spacing: 0.5px; color: var(--gp-ink-3);
}
/* A smaller queue: same treatment as the full list, tighter rows, because it
   sits inside a page rather than being the page. */
table.gp-mini-list { font-size: 13px; }
table.gp-mini-list thead th { padding: 8px 10px; }
table.gp-mini-list tbody td { padding: 9px 10px; }
table.gp-mini-list th,
table.gp-mini-list td { text-align: left; }
.gp-mini-num { width: 96px; white-space: nowrap; }
.gp-mini-num a { font-family: var(--gp-mono); font-variant-numeric: tabular-nums; }
.gp-mini-who { width: 116px; }
.gp-mini-state { width: 84px; }
.gp-mini-act { width: 40px; text-align: right; }
.gp-mini-act form { margin: 0; }
/* Two classes deep so it outranks `button[type="submit"]`, which would
   otherwise dress a row-level icon as the page's primary action. */
.gp-mini-act .gp-mini-unlink {
  border: 0;
  background: none;
  padding: 3px 5px;
  height: auto;
  min-width: 0;
  cursor: pointer;
  color: var(--gp-ink-3);
  border-radius: var(--gp-r-sm);
  box-shadow: none;
  font-size: 13px;
}
.gp-mini-act .gp-mini-unlink:hover {
  color: var(--gp-err);
  background: var(--gp-err-bg);
}
/* An empty panel keeps its columns; the row inside says why it is empty. */
.gp-mini-empty td { color: var(--gp-ink-3); font-style: italic; }
.gp-mini-empty:hover { background: var(--gp-surface) !important; }

/* Events sit quieter than notes: the stream is mostly history, and history
   that shouts drowns the two sentences somebody actually wrote. */
.gp-issue-entry { padding: 9px 0; border-bottom: 1px solid var(--gp-line-soft); }
.gp-issue-entry-head { display: flex; gap: 8px; font-size: 12px; margin-bottom: 2px; }
.gp-issue-entry-body { font-size: 13.5px; line-height: 1.6; color: var(--gp-ink); }
.gp-issue-event .gp-issue-entry-body { color: var(--gp-ink-3); font-size: 12.5px; }
/* The note box and its button were flush against each other, so the button
   read as part of the field rather than as the thing that sends it. */
.gp-issue-addnote { margin-top: 18px; }
.gp-issue-addnote textarea { width: 100%; display: block; }
.gp-issue-addnote button { margin-top: 12px; }
.gp-issue-addnote .error:not(:empty) { margin-top: 6px; }

/* ---- the form --------------------------------------------------------- */
.gp-issue-lede {
  margin: 0 0 18px;
  color: var(--gp-ink-2);
  font-size: 13.5px;
  line-height: 1.55;
  max-width: 62ch;
}
.gp-if-row { margin-bottom: 14px; }
.gp-if-row > label {
  display: block; margin-bottom: 5px;
  font-size: 12.5px; font-weight: 500; color: var(--gp-ink-2);
}
/* One measure for the whole form. The title input was capped at 620px while
   the three selects below ran the full width of the card, so nothing lined
   up with anything: constrain the form instead of each control. */
.gp-issue-form form { max-width: 820px; }
.gp-if-row input[type=text],
.gp-if-row textarea,
#content .gp-if-row select,
.gp-if-row select { width: 100%; max-width: none; margin: 0; }
.gp-if-cols { display: flex; flex-wrap: wrap; gap: 14px; }
.gp-if-cols .gp-if-row { flex: 1 1 180px; margin-bottom: 0; }
.gp-if-cols { margin-bottom: 14px; }
.gp-if-actions { display: flex; gap: 8px; margin-top: 20px; }

.gp-issue-tablewrap { overflow-x: auto; }

@media (max-width: 720px) {
  .gp-issues-head, .gp-issue-top { flex-direction: column; align-items: stretch; }
  .gp-issues-head .action-button { align-self: flex-start; }
  /* Full width, not the 190px cap the desktop row needs - a select alone on
     a row that stops a third of the way across looks broken. */
  .gp-issue-filters > * { flex: 1 1 100%; }
  #content .gp-issue-filters select,
  .gp-issue-filters select,
  .gp-issue-filters input[type=search] { max-width: none; width: 100%; }
  .gp-issue-filters .action-button { flex: 0 0 auto; }
  /* Cause and owner are the first things to go: the title, severity and
     status are what somebody scans a phone-width list for. */
  .gp-iss-cause, .gp-iss-owner, .gp-iss-dept { display: none; }
  .gp-stat { flex: 1 1 100%; }
}

/* The issue-log chapter's demo: two radio questions and a verdict line. */
.gp-gd-radio { display: inline-flex; align-items: center; gap: 5px; margin-right: 14px; }
.gp-gd-radio input { margin: 0; }
/* The verdict belongs inside the mock dialog, not hanging off its edge. */
[data-gd-issue-demo] .gp-gd-msg {
  display: block;
  margin: 10px 0 0;
  padding: 8px 10px;
  border-radius: var(--gp-r-sm, 6px);
  font-size: 12.5px;
  background: var(--gp-ok-bg);
  color: var(--gp-ok);
}
[data-gd-issue-demo] .gp-gd-msg.is-bad { background: var(--gp-surface-2); color: var(--gp-ink-3); }

/* Google Drive destination chooser (reports-drive.inc.php). */
.gp-drive-dest {
  margin: 18px 0;
  padding: 16px 18px;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-md, 10px);
  background: var(--gp-surface);
}
.gp-drive-dest legend {
  padding: 0 6px;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--gp-ink-2);
}
/* This block sits inside .gp-sched-form, whose labels are uppercase 11px
   flex columns - right for a field caption, wrong for a sentence explaining
   a permission trade-off. Reset it here rather than loosening that rule,
   which every schedule field depends on. */
.gp-drive-dest label,
.gp-drive-dest legend {
  text-transform: none;
  letter-spacing: normal;
  font-size: 13px;
}
.gp-drive-dest .gp-drive-dest-field,
.gp-drive-dest .gp-drive-dest-name {
  font-size: 11px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}
.gp-drive-dest .gp-drive-dest-field input,
.gp-drive-dest .gp-drive-dest-name input,
.gp-drive-dest .gp-rep-note {
  text-transform: none;
  letter-spacing: normal;
  font-size: 13px;
}
.gp-drive-dest .gp-rep-note { font-size: 12.5px; }

/* The box is a fixed column so every label starts on the same line, and the
   explanation sits under its own heading rather than under the radio. */
.gp-drive-dest .gp-drive-choice {
  display: grid;
  grid-template-columns: 17px 1fr;
  align-items: start;
  gap: 10px;
  margin: 0 0 12px;
  padding: 0;
  text-indent: 0;
  cursor: pointer;
}
.gp-drive-dest .gp-drive-choice input { margin-top: 2px; }
.gp-drive-dest .gp-drive-choice b { display: block; font-weight: 500; color: var(--gp-ink); }
.gp-drive-dest .gp-drive-choice em {
  display: block;
  margin-top: 2px;
  font-style: normal;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--gp-ink-3);
}
.gp-drive-dest-field,
.gp-drive-dest-name { display: block; margin-top: 14px; }
.gp-drive-dest input[type=text] { width: 100%; max-width: 520px; }
.gp-drive-dest .gp-rep-note { margin-top: 6px; }
.gp-drive-scopenote { margin-top: 14px; }

/* The reconnect warning on the Drive settings: a predictable mismatch said
   before the test reports it, not a general note. */
.gp-drive-reconnect {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-top: 14px;
  padding: 10px 12px;
  background: var(--gp-warn-bg);
  border: 1px solid var(--gp-warn-edge);
  border-radius: var(--gp-r-sm, 6px);
  color: var(--gp-warn);
}

/* =========================================================================
   24. The report composer and the run-once page
   ========================================================================= */

.gp-comp { margin: 18px 0; }

/* Presets are cards, not a dropdown: the point is to see what each one
   contains before choosing, and a <select> hides exactly that. */
.gp-comp-presets {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 10px;
  margin-bottom: 16px;
}
.gp-comp .gp-comp-preset {
  display: block;
  margin: 0;
  padding: 11px 13px;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-md, 10px);
  background: var(--gp-surface);
  cursor: pointer;
  text-transform: none;
  letter-spacing: normal;
  transition: border-color var(--gp-t-color), background var(--gp-t-color);
}
.gp-comp .gp-comp-preset:hover { border-color: var(--gp-accent-edge); }
.gp-comp .gp-comp-preset.is-on {
  border-color: var(--gp-accent);
  background: var(--gp-accent-tint);
}
.gp-comp .gp-comp-preset input { position: absolute; opacity: 0; pointer-events: none; }
.gp-comp .gp-comp-preset b {
  display: block;
  font-size: 13px;
  font-weight: 500;
  color: var(--gp-ink);
}
.gp-comp .gp-comp-preset em {
  display: block;
  margin-top: 3px;
  font-style: normal;
  font-size: 12px;
  line-height: 1.45;
  color: var(--gp-ink-3);
}

.gp-comp-blocks {
  padding: 14px 16px;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-md, 10px);
  background: var(--gp-page);
}
.gp-comp-blocks-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 10px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
}
.gp-comp-count { text-transform: none; letter-spacing: normal; font-size: 12px; }

/* Fixed first column so every label starts on the same line, whether or not
   the block carries the personal-data tag. */
.gp-comp .gp-comp-block {
  display: grid;
  grid-template-columns: 17px 1fr;
  align-items: start;
  gap: 10px;
  margin: 0 0 9px;
  padding: 0;
  text-indent: 0;
  text-transform: none;
  letter-spacing: normal;
  cursor: pointer;
}
.gp-comp .gp-comp-block:last-of-type { margin-bottom: 0; }
.gp-comp .gp-comp-block input { margin-top: 2px; }
.gp-comp .gp-comp-block b {
  display: block;
  font-size: 13px;
  font-weight: 500;
  color: var(--gp-ink);
}
.gp-comp .gp-comp-block em {
  display: block;
  margin-top: 2px;
  font-style: normal;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--gp-ink-3);
}
.gp-comp-tag {
  display: inline-block;
  margin-left: 7px;
  padding: 1px 7px;
  border-radius: var(--gp-r-pill);
  background: var(--gp-warn-bg);
  border: 1px solid var(--gp-warn-edge);
  color: var(--gp-warn);
  font-size: 10.5px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  vertical-align: 1px;
}

/* [hidden] loses to display:flex, so the warning showed for every report
   whether or not it carried personal data - which is the fastest way to
   teach people to ignore it. */
.gp-comp [hidden] { display: none !important; }
.gp-comp-privacy {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 12px 0 0;
  padding: 10px 12px;
  background: var(--gp-warn-bg);
  border: 1px solid var(--gp-warn-edge);
  border-radius: var(--gp-r-sm, 6px);
  color: var(--gp-warn);
  font-size: 12.5px;
  line-height: 1.5;
}

/* ---- run once --------------------------------------------------------- */
.gp-runonce { max-width: var(--gp-w-console); }
.gp-runonce h3 { margin: 0 0 4px; }

.gp-run-dest {
  margin: 18px 0 0;
  padding: 14px 16px;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-md, 10px);
  background: var(--gp-surface);
}
.gp-run-dest-head {
  display: block;
  margin-bottom: 10px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
}
.gp-run-dest .gp-run-choice {
  display: grid;
  grid-template-columns: 17px 1fr;
  align-items: start;
  gap: 10px;
  margin: 0 0 10px;
  padding: 0;
  text-indent: 0;
  text-transform: none;
  letter-spacing: normal;
  cursor: pointer;
}
.gp-run-dest .gp-run-choice input { margin-top: 2px; }
.gp-run-dest .gp-run-choice b { display: block; font-size: 13px; font-weight: 500; color: var(--gp-ink); }
.gp-run-dest .gp-run-choice em {
  display: block; margin-top: 2px; font-style: normal;
  font-size: 12.5px; line-height: 1.45; color: var(--gp-ink-3);
}
/* A destination that is not set up yet stays visible and says why, rather
   than disappearing and leaving somebody wondering where it went. */
.gp-run-dest .gp-run-choice.is-off { opacity: 0.65; cursor: default; }

.gp-run-dest .gp-run-recips {
  display: block;
  margin: 0 0 12px 27px;
  max-width: 460px;
  text-transform: none;
  letter-spacing: normal;
}
.gp-run-dest .gp-run-recips input { width: 100%; }
.gp-run-actions { margin-top: 18px; }

@media (max-width: 720px) {
  .gp-comp-presets { grid-template-columns: 1fr; }
}

/* =========================================================================
   Mailing panel (scp/mailings.php, mailtemplates.php, maillists.php,
   admin scp/mailing.php - docs/mailing-implementation-plan.md §10).
   Follows the issue log's card/stat/pill idiom above; tokens only, no
   new colours.
   ========================================================================= */

/* .gp-mailing: width and top padding come from the page sheet and the shared
   bar now (§25). */

.gp-mailing-card {
  padding: 16px 18px;
  margin-bottom: 16px;
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-md, 10px);
}
.gp-mailing-card h3 { margin: 0 0 8px; }
.gp-mailing-card h4 { margin: 14px 0 6px; }

.gp-mailing-empty {
  padding: 40px 24px;
  text-align: center;
  color: var(--gp-ink-2);
  border: 1px dashed var(--gp-line);
  border-radius: var(--gp-r-md, 10px);
}

/* ---- status pills (one vocabulary everywhere) — body shared, §23 ------ */
.gp-pill-ok   { background: var(--gp-ok-bg);   border-color: var(--gp-ok-edge);   color: var(--gp-ok); }
.gp-pill-warn { background: var(--gp-warn-bg); border-color: var(--gp-warn-edge); color: var(--gp-warn); }
.gp-pill-err  { background: var(--gp-err-bg);  border-color: var(--gp-err-edge);  color: var(--gp-err); }
.gp-pill-info { background: var(--gp-info-bg); border-color: var(--gp-info-edge); color: var(--gp-info); }
.sticky.bar h2 .gp-pill { margin-left: 10px; vertical-align: middle; }

/* ---- archive ----------------------------------------------------------- */
.gp-mailing-resume {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  padding: 10px 14px;
  margin-bottom: 14px;
  background: var(--gp-info-bg);
  border: 1px solid var(--gp-info-edge);
  border-radius: var(--gp-r-md, 10px);
  color: var(--gp-ink-2);
}
.gp-mailing-resume form { margin: 0; display: inline; }

.gp-mailing-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-bottom: 14px;
}
.gp-mailing-filters input[type=search] { flex: 1 1 220px; min-width: 180px; margin: 0; }
.gp-mailing-check { white-space: nowrap; cursor: pointer; }

.gp-mailing-table td { vertical-align: top; }
.gp-mailing-audiencecell {
  max-width: 260px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The one place a bar earns its keep: 12/78 vs 78/78 at a glance. */
.gp-mailing-bar {
  height: 3px;
  margin-top: 4px;
  background: var(--gp-line-soft);
  border-radius: var(--gp-r-pill);
  overflow: hidden;
}
.gp-mailing-bar span {
  display: block;
  height: 100%;
  background: var(--gp-accent);
  transition: width 0.4s ease;
}
.gp-mailing-bar-lg { height: 8px; margin: 4px 0 8px; }

.gp-mailing-pagination { margin: 10px 0; color: var(--gp-ink-3); }

/* ---- compose ----------------------------------------------------------- */
.gp-mailing-columns {
  display: flex;
  gap: 16px;
  align-items: flex-start;
}
.gp-mailing-col {
  flex: 1 1 380px;
  min-width: 0;
  padding: 16px 18px;
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-md, 10px);
}
.gp-mailing-message { flex: 3 1 480px; }
.gp-mailing-audience { flex: 2 1 320px; }

.gp-mailing-field { margin-bottom: 14px; }
.gp-mailing-field label { display: block; margin-bottom: 4px; color: var(--gp-ink-2); }
.gp-mailing-inlineopt { margin-left: 14px; }
.gp-mailing-inlineopt label { display: inline; margin-right: 10px; }
.gp-mailing-editorwarn { margin-top: 5px; font-size: 12px; }

.gp-mailing-audience details {
  border: 1px solid var(--gp-line-soft);
  border-radius: var(--gp-r-sm, 8px);
  margin-bottom: 8px;
  background: var(--gp-page);
}
.gp-mailing-audience summary {
  padding: 9px 12px;
  cursor: pointer;
  color: var(--gp-ink);
  user-select: none;
}
.gp-mailing-block { padding: 4px 12px 12px; }
.gp-mailing-block label { display: block; margin: 8px 0 3px; color: var(--gp-ink-2); }
.gp-mailing-block select[multiple] { width: 100%; }
.gp-mailing-chipcount {
  float: right;
  min-width: 22px;
  text-align: center;
  padding: 0 7px;
  border-radius: var(--gp-r-pill);
  background: var(--gp-accent-tint);
  border: 1px solid var(--gp-accent-edge);
  color: var(--gp-accent-strong);
  font-size: 11.5px;
}
.gp-mailing-chips { margin: 6px 0; }
.gp-mailing-chip {
  display: inline-block;
  margin: 0 6px 6px 0;
  padding: 2px 9px;
  border-radius: var(--gp-r-pill);
  background: var(--gp-surface-2);
  border: 1px solid var(--gp-line-soft);
  font-size: 12.5px;
  color: var(--gp-ink-2);
}
.gp-mailing-chip .gp-chip-x { margin-left: 4px; color: var(--gp-ink-3); text-decoration: none; }
.gp-mailing-chip .gp-chip-x:hover { color: var(--gp-err); }

.gp-mailing-fromline {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--gp-line-soft);
  color: var(--gp-ink-2);
}

.gp-mailing-actions { margin: 16px 0; display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.gp-mailing-primary { font-weight: 600; }

/* Danger is for the button that mails real families. */
.gp-mailing-danger,
button.gp-mailing-danger,
input.gp-mailing-danger {
  background: var(--gp-err) !important;
  border-color: var(--gp-err) !important;
  color: #fff !important;
}
.gp-mailing-danger[disabled] { opacity: 0.45; cursor: not-allowed; }
.gp-mailing-dangerform { margin: 8px 0 24px; }

.gp-mailing-linkbutton {
  background: none;
  border: none;
  padding: 0;
  color: var(--gp-accent-strong);
  text-decoration: underline;
  cursor: pointer;
  font: inherit;
}
.gp-mailing-barform { display: inline; margin: 0 0 0 6px; }
.gp-mailing-baractions form { display: inline; }

/* ---- confirm ------------------------------------------------------------ */
.gp-mailing-meta div { margin-bottom: 4px; }
.gp-mailing-meta b { display: inline-block; min-width: 70px; color: var(--gp-ink-3); font-weight: 500; }

.gp-mailing-countcard {
  margin: 12px 0;
  padding: 12px 14px;
  background: var(--gp-page);
  border: 1px solid var(--gp-line-soft);
  border-radius: var(--gp-r-sm, 8px);
}
.gp-mailing-countline { font-size: 17px; color: var(--gp-ink); margin-bottom: 3px; }
.gp-mailing-countcard details { margin-top: 6px; }
.gp-mailing-countcard summary { cursor: pointer; color: var(--gp-warn); }
.gp-mailing-skips { margin: 6px 0 0 14px; font-size: 12.5px; color: var(--gp-ink-2); }
.gp-mailing-skips td { padding: 2px 12px 2px 0; }

.gp-mailing-ok { color: var(--gp-ok); }
.gp-mailing-warn { color: var(--gp-warn); margin: 6px 0; }
.gp-mailing-gaps {
  margin: 12px 0;
  padding: 12px 14px;
  background: var(--gp-warn-bg);
  border: 1px solid var(--gp-warn-edge);
  border-radius: var(--gp-r-sm, 8px);
}
.gp-mailing-gaps table { margin: 6px 0 0 8px; }
.gp-mailing-gaps td { padding: 2px 14px 2px 0; }
.gp-mailing-ack { display: block; margin-top: 8px; cursor: pointer; color: var(--gp-ink); }

.gp-mailing-previewwrap { margin: 14px 0; }
.gp-mailing-preview {
  margin-top: 8px;
  padding: 14px 16px;
  background: var(--gp-page);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm, 8px);
  overflow-x: auto;
}
.gp-mailing-preview-subject {
  margin-bottom: 10px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--gp-line-soft);
}
.gp-mailing-preview-toggle { margin-top: 12px; }
.gp-mailing-preview-toggle summary { cursor: pointer; color: var(--gp-ink-2); }

.gp-mailing-whenrow {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: 14px 0 4px;
}
.gp-mailing-sendform { margin-top: 4px; }

/* ---- detail -------------------------------------------------------------- */
.gp-mailing-metagrid {
  display: grid;
  grid-template-columns: 110px 1fr;
  gap: 4px 14px;
  margin: 0;
}
.gp-mailing-metagrid dt { color: var(--gp-ink-3); }
.gp-mailing-metagrid dd { margin: 0; color: var(--gp-ink); }
.gp-mailing-progressline { color: var(--gp-ink-2); }
.gp-mailing-assent summary { cursor: pointer; color: var(--gp-ink-2); }

.gp-mailing-chipsrow { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.gp-mailing-filterchip {
  display: inline-block;
  padding: 3px 11px;
  border-radius: var(--gp-r-pill);
  border: 1px solid var(--gp-line-soft);
  background: var(--gp-surface-2);
  color: var(--gp-ink-2);
  text-decoration: none;
}
.gp-mailing-filterchip.gp-on {
  background: var(--gp-accent-tint);
  border-color: var(--gp-accent-edge);
  color: var(--gp-accent-strong);
}
.gp-mailing-filterchip b { font-weight: 600; }
.gp-mailing-why { display: block; font-size: 12px; }
.gp-mailing-rowactions form { display: inline; margin-left: 8px; }

/* ---- lists / import ------------------------------------------------------ */
.gp-mailing-importreport {
  margin-top: 10px;
  padding: 10px 12px;
  border-radius: var(--gp-r-sm, 8px);
  background: var(--gp-ok-bg);
  border: 1px solid var(--gp-ok-edge);
  color: var(--gp-ink-2);
}
.gp-mailing-importreport.gp-mailing-importpreview {
  background: var(--gp-info-bg);
  border-color: var(--gp-info-edge);
}
.gp-mailing-importreport table { margin: 6px 0 0 8px; font-size: 12.5px; }
.gp-mailing-importreport td { padding: 2px 12px 2px 0; }
.gp-mailing-inlineform { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-bottom: 10px; }
.gp-mailing-inlineform label { margin: 0; color: var(--gp-ink-2); }
.gp-mailing-rowform { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--gp-line-soft); }
.gp-mailing-rowform label { display: inline-block; margin: 0 4px 0 10px; color: var(--gp-ink-2); }
.gp-mailing-rowform label:first-of-type { margin-left: 0; }

/* ---- admin --------------------------------------------------------------- */
.gp-mailing-settings label { display: block; margin: 10px 0 4px; color: var(--gp-ink-2); }
.gp-mailing-limits td { padding: 8px 14px 8px 0; vertical-align: top; }
.gp-mailing-limits .faded { font-size: 12.5px; max-width: 480px; }
.gp-mailing-installlog { margin: 10px 0 0 18px; color: var(--gp-ink-2); }
.gp-mailing-varhelp { margin: 10px 0; }
.gp-mailing-varhelp summary { cursor: pointer; color: var(--gp-ink-2); }
.gp-mailing-varhelp table { margin: 8px 0 0 8px; font-size: 12.5px; }
.gp-mailing-varhelp td { padding: 2px 14px 2px 0; }

@media (max-width: 900px) {
  .gp-mailing-columns { flex-direction: column; }
  .gp-mailing-metagrid { grid-template-columns: 1fr; }
  .gp-mailing-metagrid dt { margin-top: 6px; }
  .gp-mailing-audiencecell { max-width: none; white-space: normal; }
}

/* Mailing: the per-variable fallback input on the confirm screen */
.gp-mailing-fallback { display: inline-flex; align-items: center; gap: 6px;
  margin-left: 10px; white-space: nowrap; color: var(--gp-ink-2); }
.gp-mailing-fallback input { margin: 0; }

/* Mailing letterhead: the current logo and its controls */
.gp-mailing-logo-now {
  display: flex; align-items: center; gap: 16px;
  margin: 10px 0 14px; padding: 12px 14px;
  background: var(--gp-page); border: 1px solid var(--gp-line-soft);
  border-radius: var(--gp-r-sm, 8px);
}
.gp-mailing-logo-now img {
  max-width: 220px; max-height: 80px; height: auto;
  background: #fff; padding: 6px; border-radius: 4px;
}
.gp-mailing-logo-now .gp-mailing-check { margin-top: 6px; }

/* =========================================================================
   Referred tickets
   -------------------------------------------------------------------------
   A referral hands an agent the ticket without handing them the assignment,
   and the queue used to render both identically. The rail and the tint mark
   the row; the badge names the reason. Navy, not sage: this is information
   about the row, not an action on it, and it must not read as "selected".
   ========================================================================= */

#tickets table.list tbody tr.gp-referred { background: var(--gp-info-bg); }
#tickets table.list tbody tr.gp-referred:hover { background: #e3eaf6; }
#tickets table.list tbody tr.gp-referred > td:first-child {
  box-shadow: inset 3px 0 0 var(--gp-info);
}
/* A checked row reads as checked first - selection outranks provenance. */
#tickets table.list tbody tr.gp-referred:has(.ckb:checked),
#tickets table.list tbody tr.gp-referred:has(.ckb:checked):hover {
  background: var(--gp-brand-tint);
}

/* Rides at the end of the first column. Wrapping under a narrow ticket
   number is the intended fallback, not a defect. */
.gp-referred-flag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-left: 8px;
  padding: 1px 8px;
  border: 1px solid var(--gp-info-edge);
  border-radius: var(--gp-r-pill);
  background: var(--gp-surface);
  color: var(--gp-info);
  font-family: var(--gp-sans);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  vertical-align: middle;
  cursor: default;
}
.gp-referred-flag i { font-size: 10px; line-height: 1; opacity: 0.85; }
/* ---- mailing: the audience pickers ------------------------------------
   The four audience selects are select2 multi-selects now. Stock select2
   chips are small grey pills with a × that is a 9px hit target; on a
   control whose whole job is "take this one back out", that is the part
   that has to be comfortable. */
.gp-mailing-chipcount.is-empty { display: none; }

.gp-mailing-block label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}
.gp-multi-clear {
  font-size: 11.5px;
  color: var(--gp-ink-3);
  text-decoration: none;
  white-space: nowrap;
}
.gp-multi-clear:hover { color: var(--gp-err); text-decoration: underline; }

/* A picker with nothing in it is one line tall; one with nine agents in it
   grows and then scrolls, rather than pushing the From line off-screen. */
.gp-mailing-block .select2-container--default .select2-selection--multiple {
  background: var(--gp-surface);
  min-height: var(--gp-ctl-h);
  max-height: 190px;
  overflow-y: auto;
  padding: 3px 4px;
}
/* Chips and the filter field share one flow. Stock select2 floats both
   the chips and the search box; the moment the chips stop floating - and
   they must, to become flex rows with the x after the label - the float
   on the search box drops it onto a line of its own and leaves a band of
   dead space under every picker. */
.gp-mailing-block .select2-container--default
    .select2-selection--multiple .select2-selection__rendered {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  margin: 0;
  padding: 0 4px;
}
.gp-mailing-block .select2-container--default .select2-search--inline {
  float: none;
  /* Stock hands this item the full row, which wraps it under the chips
     however small the field itself is. As a flex item it takes what is
     left of the chip line instead, and drops to the next line only when
     there is genuinely no room. */
  flex: 1 1 70px;
  width: auto;
  min-width: 0;
}
.gp-mailing-block .select2-container--default
    .select2-selection--multiple .select2-selection__choice {
  display: inline-flex;
  flex-direction: row-reverse;   /* the × sits after the name, not before */
  align-items: center;
  gap: 7px;
  margin: 3px 4px 3px 0;
  padding: 3px 10px;
  border: 1px solid var(--gp-accent-edge);
  border-radius: var(--gp-r-pill);
  background: var(--gp-accent-tint);
  color: var(--gp-ink);
  font-size: 12.5px;
  line-height: 1.5;
}
.gp-mailing-block .select2-container--default
    .select2-selection--multiple .select2-selection__choice__remove {
  margin: 0;
  padding: 0 2px;
  border: 0;
  background: none;
  color: var(--gp-ink-3);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
}
.gp-mailing-block .select2-container--default
    .select2-selection--multiple .select2-selection__choice__remove:hover {
  color: var(--gp-err);
}
/* The filter field rides on the same line as the chips. Left at the
   panel's standard 34px control height it wraps to its own line and the
   picker reads as an empty box under the chips - which is exactly what
   the first cut looked like. */
.gp-mailing-block .select2-container--default
    .select2-search--inline .select2-search__field {
  height: 24px;
  margin: 3px 0;
  padding: 0 2px;
  border: 0;
  background: none;
  font-family: var(--gp-sans);
  font-size: 13px;
  line-height: 24px;
}
/* Room for a real list: the stock 200px shows four agents of nineteen. */
.select2-container--default .select2-results > .select2-results__options {
  max-height: 320px;
}

/* =========================================================================
   Inline attachment viewer  (js/attachment-viewer.js)
   -------------------------------------------------------------------------
   Reading an attachment used to mean downloading it. This is the surface it
   opens in instead: a full-bleed overlay holding the image or the PDF and
   nothing else — a filename, a close button, and the file.

   There is deliberately no download control, no toolbar and no visible URL.
   That is a deterrent and not a control; see the note at the top of
   js/attachment-viewer.js for what it does and does not achieve.
   ========================================================================= */
.gp-viewer {
  position: fixed;
  inset: 0;
  z-index: 4000;          /* over #overlay (1000) and #loading (3000) */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.gp-viewer[hidden] { display: none; }
.gp-viewer-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(20, 24, 28, 0.72);
  opacity: 0;
  transition: opacity var(--gp-t-color);
}
.gp-viewer--on .gp-viewer-backdrop { opacity: 1; }

.gp-viewer-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  width: min(1100px, 100%);
  height: min(88vh, 100%);
  background: var(--gp-surface);
  border-radius: var(--gp-r);
  overflow: hidden;
  box-shadow: 0 24px 64px rgba(20, 24, 28, 0.35);
  opacity: 0;
  transform: translateY(8px) scale(0.99);
  transition: opacity var(--gp-t-color), transform var(--gp-t-move);
}
.gp-viewer--on .gp-viewer-panel { opacity: 1; transform: none; }

.gp-viewer-bar {
  display: flex;
  align-items: center;
  gap: 16px;
  flex: none;
  padding: 12px 12px 12px 20px;
  border-bottom: 1px solid var(--gp-beige);
  background: var(--gp-surface);
}
.gp-viewer-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--gp-ink);
}
.gp-viewer-close {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  min-height: 0;
  padding: 0;
  background: transparent;
  border: 1px solid var(--gp-beige);
  border-radius: 50%;
  color: var(--gp-ink-2);
  cursor: pointer;
}
.gp-viewer-close:hover {
  background: var(--gp-err-bg);
  border-color: var(--gp-err-bg);
  color: var(--gp-err);
}

/* The stage is the file and nothing else. */
.gp-viewer-stage {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--gp-surface-2);
}
.gp-viewer-stage--image { padding: 16px; }
.gp-viewer-stage img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  /* A dragged-out image is a saved image on most desktops. */
  -webkit-user-drag: none;
  user-select: none;
}
.gp-viewer-stage iframe {
  width: 100%;
  height: 100%;
  border: 0;
  background: var(--gp-surface-2);
}
.gp-viewer-fail {
  margin: 0;
  padding: 32px;
  text-align: center;
  color: var(--gp-ink-2);
}

@media (max-width: 640px) {
  .gp-viewer { padding: 0; }
  .gp-viewer-panel { width: 100%; height: 100%; border-radius: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .gp-viewer-backdrop,
  .gp-viewer-panel { transition: none; }
}

/* =========================================================================
   The internal desk (scp/internal.php)

   Almost everything here is shared with the issue log by joining its
   selectors above rather than by copying its rules: the page width, the
   stats strip, the filter bar, the .gp-if-* form grid, the table wrapper and
   the pager are all the same furniture. What follows is only what is
   genuinely new - the status chips, the overdue mark, and the parts of the
   detail page that have no issue-log equivalent.

   Colour: internal takes --gp-info, the deep blue the token block already
   describes as "kept for informational use". Tickets keep the brand green.
   No new custom properties are introduced.
   ========================================================================= */

/* ---- the line that does the teaching ---------------------------------- */
.gp-ir-legend {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 10px 14px;
  margin-bottom: 16px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--gp-info);
  background: var(--gp-info-bg);
  border: 1px solid var(--gp-info-edge);
  border-radius: var(--gp-r-md, 10px);
}
.gp-ir-legend i { margin-top: 2px; }
.gp-ir-legend b { font-weight: 600; }

/* ---- the table -------------------------------------------------------- */

/* Header and cell must agree, column by column - a <th> defaults to centred
   and a <td> to start. Same fix the issue table carries. */
.gp-ir-table th,
.gp-ir-table td { text-align: left; }

/* The whole row opens the request (handler in internal.inc.php). */

.gp-ir-num    { width: 110px; font-family: var(--gp-mono);
                font-variant-numeric: tabular-nums; font-size: 12.5px;
                color: var(--gp-ink-3); white-space: nowrap; }
.gp-ir-pri    { width: 84px; }
.gp-ir-due    { width: 104px; white-space: nowrap; }
.gp-ir-age    { width: 90px; color: var(--gp-ink-2); white-space: nowrap; }
.gp-ir-status { width: 104px; }
.gp-ir-title a { font-weight: 500; color: var(--gp-ink); }
.gp-ir-title a:hover { color: var(--gp-accent); }

/* ---- status chips ----------------------------------------------------- */

/* Pills rather than coloured words, for the reason the issue log gives: a
   coloured word beside a black one reads as a mistake, a pill reads as a
   label. Five statuses, and the two terminal ones are visually quieter than
   the live ones - a closed queue should not shout. */
.gp-irstat-open        { background: var(--gp-info-bg);   border-color: var(--gp-info-edge);  color: var(--gp-info); }
.gp-irstat-in_progress { background: var(--gp-brand-tint); border-color: var(--gp-brand-edge); color: var(--gp-brand-strong); }
.gp-irstat-waiting     { background: var(--gp-warn-bg);   border-color: var(--gp-warn-edge);  color: var(--gp-warn); }
.gp-irstat-resolved    { background: var(--gp-ok-bg);     border-color: var(--gp-ok-edge);    color: var(--gp-ok); }
.gp-irstat-cancelled   { background: var(--gp-surface-2); color: var(--gp-ink-3); }

/* Overdue marks the due cell only, never the row: a queue where a third of
   the rows shout is a queue nobody reads. */
.gp-overdue { color: var(--gp-err); font-weight: 600; }

/* ---- one request ------------------------------------------------------ */
.gp-ir-head { margin-bottom: 18px; }
.gp-ir-headline {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 14px;
}
.gp-ir-headline h2 { margin: 0; }

.gp-ir-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.gp-ir-meta dl {
  flex: 1 1 200px;
  margin: 0;
  padding: 12px 14px;
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-md, 10px);
}
.gp-ir-meta dt {
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--gp-ink-3);
}
.gp-ir-meta dd {
  margin: 2px 0 10px;
  font-size: 13.5px;
  color: var(--gp-ink);
}
.gp-ir-meta dd:last-child { margin-bottom: 0; }

.gp-ir-outcome,
.gp-ir-issue {
  margin-top: 12px;
  padding: 10px 14px;
  font-size: 13px;
  line-height: 1.6;
  border-radius: var(--gp-r-md, 10px);
}
.gp-ir-outcome { background: var(--gp-surface-2); color: var(--gp-ink-2); }
.gp-ir-issue {
  background: var(--gp-warn-bg);
  border: 1px solid var(--gp-warn-edge);
  color: var(--gp-warn);
}
.gp-ir-issue a { color: inherit; text-decoration: underline; }

/* ---- linked tasks ----------------------------------------------------- */
.gp-ir-tasks {
  margin-bottom: 18px;
  padding: 12px 14px;
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-md, 10px);
}
.gp-ir-tasks h3 { margin: 0 0 8px; font-size: 13px; color: var(--gp-ink-2); }
.gp-ir-tasks ul { margin: 0; padding: 0; list-style: none; }
.gp-ir-tasks li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 5px 0;
  font-size: 13px;
  border-top: 1px solid var(--gp-line-soft);
}
.gp-ir-tasks li:first-child { border-top: 0; }
.gp-ir-tasks li > span:nth-child(2) { flex: 1 1 auto; color: var(--gp-ink); }

/* A prompt, never an automatic action: a finished task does not always mean
   the thing somebody asked for has happened. */
.gp-ir-prompt {
  margin-top: 10px;
  padding: 8px 12px;
  font-size: 13px;
  background: var(--gp-ok-bg);
  border: 1px solid var(--gp-ok-edge);
  border-radius: var(--gp-r-sm);
  color: var(--gp-ok);
}
.gp-ir-prompt .faded { color: var(--gp-ink-3); }

/* ---- the composer ----------------------------------------------------- */

/* One box. No tabs, because there is no reply/note distinction to draw. */
.gp-ir-composer {
  margin-top: 20px;
  padding: 16px;
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r);
}
.gp-ir-composer h3 { margin: 0 0 4px; font-size: 14px; }
.gp-ir-safe {
  margin: 0 0 12px;
  font-size: 12.5px;
  color: var(--gp-ink-3);
}
.gp-ir-composer-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 12px;
}
.gp-ir-composer-actions label {
  font-size: 12.5px;
  color: var(--gp-ink-2);
  display: flex;
  align-items: center;
  gap: 6px;
}
#content .gp-ir-composer-actions select { margin: 0; max-width: 190px; }
.gp-ir-composer-actions input[type=submit] { margin: 0; margin-left: auto; }

/* ---- the create form -------------------------------------------------- */
.gp-ir-lede {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 11px 14px;
  margin: 0 0 18px;
  font-size: 13px;
  line-height: 1.65;
  color: var(--gp-info);
  background: var(--gp-info-bg);
  border: 1px solid var(--gp-info-edge);
  border-radius: var(--gp-r-md, 10px);
}
.gp-ir-lede i { margin-top: 3px; }

/* The bar's forms must not push the buttons onto their own line. */
.gp-ir-inlineform { display: inline-block; margin: 0; }
.gp-ir-actions form { display: inline-block; margin: 0; }

/* ---- narrow ----------------------------------------------------------- */
@media (max-width: 700px) {
  .gp-ir-meta dl { flex: 1 1 100%; }
  .gp-ir-composer-actions input[type=submit] { margin-left: 0; }
}

/* ---- guide: the two-desks sorter -------------------------------------- */

/* A drill rather than a diagram. Reuses .gp-gd-chip and .gp-gd-list from the
   other demos; only the wrong-answer state and the verdict column are new,
   because no existing demo has a wrong answer to show. */
.gp-gd-chip[role=button] { cursor: pointer; user-select: none; }
.gp-gd-chip.is-bad {
  border-color: var(--gp-err-edge);
  color: var(--gp-err);
  background: var(--gp-err-bg);
}
.gp-gd-sorter-choices { white-space: nowrap; width: 1%; }
.gp-gd-sorter-choices .gp-gd-chip + .gp-gd-chip { margin-left: 4px; }
.gp-gd-sorter-verdict {
  width: 1%;
  padding-left: 8px;
  font-weight: 700;
  color: var(--gp-ink-3);
}
.gp-gd-sorter-verdict.is-ok  { color: var(--gp-ok); }
.gp-gd-sorter-verdict.is-bad { color: var(--gp-err); }
.gp-gd-sorter .gp-gd-msg.is-bad {
  background: var(--gp-err-bg);
  border-color: var(--gp-err-edge);
  color: var(--gp-err);
}

/* The convert button on a ticket that does not qualify. Visible but plainly
   inert, rather than hidden - an agent told the feature exists should be able
   to find it and read why it will not run here. */
.gp-ir-convert-na { opacity: 0.55; }
.gp-ir-convert-na:hover { opacity: 1; }

/* The recorded resolution on a request. */
.gp-ir-outcome dl { margin: 0; }
.gp-ir-outcome dt {
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--gp-ink-3);
}
.gp-ir-outcome dd { margin: 2px 0 8px; color: var(--gp-ink); }
.gp-ir-outcome dd:last-child { margin-bottom: 0; }

/* The nudge on the new-ticket form. */
.gp-ir-nudge {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 10px 14px;
  margin: 0 0 14px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--gp-info);
  background: var(--gp-info-bg);
  border: 1px solid var(--gp-info-edge);
  border-radius: var(--gp-r-md, 10px);
}
.gp-ir-nudge i { margin-top: 2px; }
.gp-ir-nudge a { color: inherit; text-decoration: underline; white-space: nowrap; }

/* The installer's quiet state. Once a feature is installed its banner
   disappears, which is right - but the button has to stay reachable, or an
   instruction to "re-run the installer" points at something that is no
   longer on the page. */
.gp-install-rerun {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 14px;
  padding: 8px 12px;
  font-size: 12.5px;
  background: var(--gp-surface-2);
  border: 1px solid var(--gp-line-soft);
  border-radius: var(--gp-r-sm);
}
.gp-install-rerun form {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  width: 100%;
}
.gp-install-rerun .faded { flex: 1 1 auto; }
.gp-install-rerun .action-button { margin: 0; flex: 0 0 auto; }


/* =========================================================================
   25. Shared page furniture — the partials in include/staff/gp-ui.inc.php
   =========================================================================
   One rule per concept. A surface that wants a page bar, a record headline,
   a pill, a pager, an empty state or a stat strip calls the partial and
   inherits these; it does not copy the numbers. */

/* --- Page shell ---------------------------------------------------------
   Every list and detail page is one column at the console rung with one top
   padding. Replaces the inline `margin-bottom:20px; padding-top:5px` spacer
   the upstream templates carried and the 1180px max-width the GP pages had
   (which is why the two families started at different heights). */
.gp-shell { padding-top: 4px; }
.gp-shell > .clear { display: none; }

/* --- Page bar (gp_page_bar) ------------------------------------------- */
.gp-page-bar { margin: 0 0 16px; }
.gp-page-bar .sticky.bar .content .flush-left h2 {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}
.gp-bar-count {
  display: inline-flex;
  align-items: center;
  min-height: 22px;
  padding: 0 8px;
  border-radius: var(--gp-r-pill);
  background: var(--gp-surface-2);
  color: var(--gp-ink-2);
  font-family: var(--gp-mono);
  font-size: 12px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
}

/* --- Record locator and headline (gp_bar_locator, gp_record_title) ------
   The bar's <h2> carries the back chevron and a small mono locator; the
   record's own title sits under the bar as the page headline — the biggest,
   boldest text on the page, which is what "make the name more prominent"
   asked for. */
.sticky.bar h2 .gp-locator {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 2px 10px;
  border-radius: var(--gp-r-pill);
  background: var(--gp-surface-2);
  color: var(--gp-ink-2);
  font-family: var(--gp-mono);
  font-size: 13px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  text-decoration: none;
  transition: background var(--gp-t-color), color var(--gp-t-color);
}
.sticky.bar h2 a.gp-locator:hover { background: var(--gp-surface-3); color: var(--gp-primary); }
.sticky.bar h2 .gp-locator i { font-size: 0.85em; color: var(--gp-ink-3); }

.gp-record-head { padding: 18px 2px 14px; border-bottom: 1px solid var(--gp-line); margin-bottom: 16px; }
.gp-record-headline {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
}
.gp-record-title,
.gp-record-headline h2 {
  margin: 0;
  color: var(--gp-ink);
  font-size: var(--gp-t-hmd);
  line-height: calc(var(--gp-lh-hmd) / var(--gp-t-hmd));
  font-weight: 600;
  letter-spacing: -0.02em;
  overflow-wrap: anywhere;
}
.gp-record-meta { margin-top: 6px; color: var(--gp-ink-3); font-size: 13px; }

/* --- Pills (gp_pill) ----------------------------------------------------
   Pills rather than coloured words: a coloured word beside a black one reads
   as a mistake, a pill reads as a label. The issue log's .gp-istat / .gp-sev
   and the internal desk's .gp-irstat are the same pill and share this rule;
   their state variants stay where they are. */
.gp-pill-neutral { background: var(--gp-surface-2); border-color: var(--gp-line-soft); color: var(--gp-ink-2); }
.gp-pill-brand   { background: var(--gp-brand-chip); border-color: transparent; color: var(--gp-brand-ink); }
.gp-pill-open    { background: var(--gp-brand-chip); border-color: transparent; color: var(--gp-brand-ink); }
.gp-pill-closed  { background: var(--gp-surface-3); border-color: transparent; color: var(--gp-ink-2); }
.gp-record-headline .gp-pill { font-size: 12px; padding: 3px 10px; }

/* --- Identifier chip (gp_chip) ---------------------------------------- */
.gp-chip {
  display: inline-flex;
  align-items: center;
  padding: 1px 7px;
  border-radius: var(--gp-r-xs);
  background: var(--gp-surface-2);
  color: var(--gp-ink-2);
  font-family: var(--gp-mono);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* --- Avatar (gp_avatar) -------------------------------------------------
   Two initials on a hue from the name. Eight hues, all muted, all with the
   ink at 4.5:1 or better. */
.gp-avatar {
  --gp-av: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--gp-av);
  height: var(--gp-av);
  border-radius: 50%;
  font-size: calc(var(--gp-av) * 0.42);
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1;
  color: var(--gp-ink);
  background: var(--gp-surface-3);
  vertical-align: middle;
  user-select: none;
}
.gp-avatar-h0 { background: #d9e6dc; color: #1f3a2a; }
.gp-avatar-h1 { background: #dfe6f1; color: #24385a; }
.gp-avatar-h2 { background: #f1e5d3; color: #5a3a0a; }
.gp-avatar-h3 { background: #ecdcdc; color: #5a1f1f; }
.gp-avatar-h4 { background: #e4dfee; color: #3a2a5a; }
.gp-avatar-h5 { background: #d9ebe9; color: #1f4a45; }
.gp-avatar-h6 { background: #e9e6d6; color: #4a4410; }
.gp-avatar-h7 { background: #e2e2e2; color: #2a2a2a; }
.gp-avatar-img img { width: var(--gp-av); height: var(--gp-av); border-radius: 50%; display: block; }

/* --- Pager (gp_pager) --------------------------------------------------- */
.gp-pager {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 18px;
  margin-top: 14px;
  color: var(--gp-ink-2);
  font-size: 13px;
}
/* Page numbers are navigation, not a headline: small, quiet, and the page
   you are on marked by weight and a hairline rather than a filled block. */
.gp-pager-pages { display: inline-flex; align-items: center; gap: 2px; flex-wrap: wrap; }
.gp-pager-pages a, .gp-pager-pages > b, .gp-pager-pages strong {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  height: 24px;
  padding: 0 6px;
  border-radius: var(--gp-r-xs);
  border: 1px solid transparent;
  font-size: 12.5px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  text-decoration: none;
  color: var(--gp-ink-3);
  transition: background var(--gp-t-color), color var(--gp-t-color),
              border-color var(--gp-t-color);
}
.gp-pager-pages a:hover { background: var(--gp-surface-2); color: var(--gp-primary); }
.gp-pager-pages > b {
  background: var(--gp-surface);
  border-color: var(--gp-line);
  color: var(--gp-ink);
  font-weight: 600;
}
.gp-pager-pages > b b { font-weight: inherit; }
.gp-pager-pages .gp-bracket { display: none; }
.gp-pager-extra { display: inline-flex; align-items: center; gap: 8px; }
.gp-pager-showing {
  margin-left: auto;
  font-family: var(--gp-mono);
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  color: var(--gp-ink-3);
}

/* --- Empty state (gp_empty) -------------------------------------------- */
.gp-empty,
.gp-issues-empty,
.gp-mailing-empty {
  padding: 40px 24px;
  text-align: center;
  color: var(--gp-ink-2);
  border: 1px dashed var(--gp-line);
  border-radius: var(--gp-r-sm);
  background: var(--gp-page);
}
.gp-empty-title { margin: 0; font-size: 15px; font-weight: 500; color: var(--gp-ink); }
.gp-empty-hint { margin: 6px 0 0; font-size: 13px; }
.gp-empty-action { margin: 14px 0 0; }

/* --- Stat strip (gp_stat_strip) ----------------------------------------- */
.gp-stats,
.gp-issue-stats,
.gp-ir-stats {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 10px;
  margin-bottom: 18px;
}
.gp-stats .gp-stat {
  flex: 1 1 120px;
  display: block;
  padding: 12px 14px;
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm);
  color: inherit;
  text-decoration: none;
  transition: border-color var(--gp-t-color), box-shadow var(--gp-t-color);
}
.gp-stats a.gp-stat:hover { border-color: var(--gp-blue); box-shadow: 0 2px 8px -4px rgba(27, 28, 25, 0.25); }
.gp-stats .gp-stat-n {
  display: block;
  font-family: var(--gp-mono);
  font-size: 22px;
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
  color: var(--gp-ink);
}
.gp-stats .gp-stat-err .gp-stat-n { color: var(--gp-err); }
.gp-stats .gp-stat-warn .gp-stat-n { color: var(--gp-warn); }
.gp-stats .gp-stat-ok .gp-stat-n { color: var(--gp-ok); }

/* --- The micro-label voice --------------------------------------------
   One uppercase eyebrow everywhere: table heads, stat labels, metadata
   terms. 12px / 600 / 0.1em / ink-3. */
.gp-stats .gp-stat-l,
.gp-eyebrow {
  display: block;
  margin-top: 2px;
  font-size: var(--gp-t-lsm);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
}

/* --- Record row (gp_record_row) -----------------------------------------
   The answer to "make the name more prominent". One renderer for tickets,
   tasks, internal requests and issues: the title is the largest, boldest
   text in the row; the number is a mono chip beside it; requester, chips
   and age sit on a quieter second line; assignee, due and status stand in a
   right-hand cluster under micro-labels. Rows are <tr>s inside the same
   table.list and form the mass actions read, so nothing about selection
   changes. table.gp-cards is the mode; .gp-compact collapses to one line. */
#content table.list.gp-cards {
  border-spacing: 0;
  /* Fixed layout, or the auto algorithm sizes the main column to the widest
     meta line (one nowrap line, so max-content is enormous) and the row's
     right-hand cluster is pushed off the card. */
  table-layout: fixed;
  width: 100%;
}
/* --- The selection bar (gp_select_bar) ---------------------------------
   One control above the rows, inside the form, where the old foot bar's
   "Select: All / None / Toggle" used to be three links below them. It caps
   the list's card, and its checkbox lines up with the rows' own. */
.gp-selectbar {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 36px;
  padding: 5px 14px 5px 12px;
  /* White, not the header tint: on a list that still has a column header
     row a second tinted strip above it reads as two headers. */
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-bottom: 0;
  border-radius: var(--gp-r) var(--gp-r) 0 0;
}
.gp-selectbar + table.list thead th,
.gp-selectbar + .gp-issue-tablewrap > table.list thead th { border-top: 1px solid var(--gp-line); }
/* The table below it is the same card, minus its own top corners. The
   #content prefix is load-bearing: `#content table.list` sets the radius,
   and a plain class selector loses to it, which left a notch where the
   bar's square corner met the table's round one. */
#content .gp-selectbar + table.list,
#content .gp-selectbar + .gp-issue-tablewrap > table.list,
#content .gp-selectbar + form > table.list {
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}
.gp-selectall {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  cursor: pointer;
  font-size: 13px;
  font-weight: 500;
  color: var(--gp-ink-2);
  user-select: none;
}
.gp-selectall:hover { color: var(--gp-primary); }
.gp-selectall input { margin: 0; flex: none; }
.gp-selectall-label { line-height: 1; }

/* No heading strip: the values below say what they are, and a row of
   labels that named only the right-hand third read as a table half-built.
   The widths stay, so the three values still line up column-wise row to
   row -- they are just not announced. */
.gp-rrow-cell-assignee { width: 156px; }
.gp-rrow-cell-due      { width: 132px; }
.gp-rrow-cell-extra    { width: 100px; }
.gp-rrow-cell-status   { width: 104px; }

#content table.list.gp-cards tbody td.gp-rrow-ckb {
  width: 32px;
  padding: 16px 0 0 12px;
  vertical-align: top;
}
#content table.list.gp-cards tbody td.gp-rrow-main {
  width: auto;
  padding: 13px 16px 13px 10px;
  vertical-align: top;
}
.gp-rrow-inner {
  display: flex;
  align-items: flex-start;
  gap: 12px 24px;
}
#content table.list.gp-cards tbody tr.gp-rrow { border-bottom: 1px solid var(--gp-line); }
.gp-rrow-body { flex: 1 1 320px; min-width: 0; }
/* Inline, not flex: a long title wraps with the dot at the head of its
   first line and the priority pill flowing after its last word, instead of
   a dot orphaned above the title and a pill dropped under it. */
.gp-rrow-lead {
  display: block;
  min-height: 24px;
  line-height: 24px;
}
.gp-rrow-lead > * { vertical-align: middle; }
.gp-rrow-lead .gp-pill, .gp-rrow-lead .gp-rrow-flags { margin-left: 8px; }
.gp-dot {
  display: inline-block;
  flex: none;
  width: 8px;
  height: 8px;
  margin-right: 10px;
  border-radius: 50%;
  background: var(--gp-ink-3);
}
.gp-dot-open, .gp-dot-ok, .gp-dot-brand { background: var(--gp-brand); }
.gp-dot-warn { background: var(--gp-warn); }
.gp-dot-err  { background: var(--gp-err); }
.gp-dot-info { background: var(--gp-blue); }
.gp-dot-closed, .gp-dot-neutral { background: var(--gp-outline); }
#content table.list.gp-cards tbody td a.gp-rrow-title {
  font-size: 15px;
  font-weight: 500;
  line-height: 1.35;
  color: var(--gp-ink);
  text-decoration: none;
  overflow-wrap: anywhere;
}
#content table.list.gp-cards tbody td a.gp-rrow-title:hover { color: var(--gp-primary); text-decoration: underline; }
.gp-rrow-unread a.gp-rrow-title { font-weight: 700; }
.gp-rrow-num { font-size: 12px; }
.gp-rrow-flags {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--gp-ink-3);
  font-size: 12px;
}
.gp-rrow-flags small, .gp-rrow-flags .faded-more { color: var(--gp-ink-3); }
.gp-rrow-flags .Icon { display: inline-block; }
/* The meta line: number · requester · department · topic · age. Plain
   muted text with a dot between the parts — one line to read, not a row of
   pills competing with the status. */
.gp-rrow-sub {
  display: block;
  margin-top: 4px;
  padding-left: 18px;          /* under the title, past the state dot */
  color: var(--gp-ink-2);
  font-size: 12.5px;
  line-height: 1.5;
  /* Wraps rather than truncating. An ellipsis that ate the department and
     the age to save a few pixels lost more than the tidy edge was worth. */
  overflow-wrap: anywhere;
}
.gp-rrow-sub > * { vertical-align: middle; }
.gp-rrow-sub > * + *::before {
  content: "·";
  margin: 0 7px;
  color: var(--gp-outline);
  font-family: var(--gp-sans);
}
.gp-rrow-excerpt {
  flex: 1 1 100%;
  color: var(--gp-ink-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gp-rrow-excerpt::before { content: none; }
.gp-rrow-chip { color: var(--gp-ink-2); }
.gp-rrow-who { color: var(--gp-ink); font-weight: 500; }
.gp-rrow-when {
  font-family: var(--gp-mono);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--gp-ink-3);
}
/* The cluster: fixed columns, no labels (the head names them). */
.gp-rrow-aside {
  flex: 0 0 auto;
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 16px;
  min-height: 24px;
}
.gp-rrow-cell { display: flex; align-items: center; min-width: 0; }
.gp-rrow-val {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  font-size: 13px;
  color: var(--gp-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.gp-rrow-val .gp-overdue { color: var(--gp-err); font-weight: 600; }
.gp-rrow-val .gp-pill { font-size: 12px; }
#content table.list.gp-cards tbody tr.gp-rrow-overdue { box-shadow: inset 3px 0 0 var(--gp-err); }

/* Compact: one line per record for bulk triage. */
#content table.list.gp-cards.gp-compact tbody td.gp-rrow-ckb { padding-top: 10px; }
#content table.list.gp-cards.gp-compact tbody td.gp-rrow-main { padding: 7px 16px 7px 10px; }
#content table.list.gp-cards.gp-compact .gp-rrow-inner { align-items: center; }
#content table.list.gp-cards.gp-compact .gp-rrow-sub { display: none; }
#content table.list.gp-cards.gp-compact .gp-rrow-lead { min-height: 0; }
#content table.list.gp-cards.gp-compact tbody td a.gp-rrow-title { font-size: 14px; }

/* Narrow: the cluster drops under the body and the head's column names go
   (they would no longer sit over anything); each value regains its label. */
@media (max-width: 1100px) {
  .gp-rrow-inner { flex-wrap: wrap; }
  .gp-rrow-aside { flex: 1 1 100%; margin-left: 0; padding-left: 18px; flex-wrap: wrap; gap: 6px 18px; }
  .gp-rrow-cell { width: auto; }
  .gp-rrow-cell:empty { display: none; }
}

/* --- View toggle (gp_view_toggle) -------------------------------------- */
.gp-viewtoggle {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-pill);
  background: var(--gp-surface);
}
.gp-viewtoggle .gp-vt {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 28px;
  padding: 0 10px;
  border-radius: var(--gp-r-pill);
  color: var(--gp-ink-2);
  font-size: 12.5px;
  font-weight: 500;
  text-decoration: none;
  transition: background var(--gp-t-color), color var(--gp-t-color);
}
.gp-viewtoggle .gp-vt:hover { background: var(--gp-surface-2); color: var(--gp-primary); }
.gp-viewtoggle .gp-vt-on { background: var(--gp-primary); color: #fff; }
.gp-viewtoggle .gp-vt-on:hover { background: var(--gp-primary-strong); color: #fff; }
.gp-viewtoggle .gp-vt i { font-size: 12px; }
.gp-viewtoggle .gp-vt-sep { width: 1px; height: 18px; margin: 0 4px; background: var(--gp-line); }

/* --- Find bar and Sort (gp_find_bar, gp_sort_menu) ----------------------
   The find bar is the search field alone now. Advanced Search became the
   rail's facet panel (section 28) and Sort moved onto the selection plane,
   which leaves the search pair the whole row - that width is the point of
   the move, not a side effect of it. */
#basic_search form > a.action-button { margin: 0; }

/* Sort: the same pill as the ticket queue's, wherever it renders. */
#basic_search .gp-sortbtn { margin: 0; }
#basic_search .pull-right .action-dropdown { text-align: left; }
.action-dropdown li.active > a { font-weight: 600; color: var(--gp-primary); }
.action-dropdown li > a > i.icon-long-arrow-up,
.action-dropdown li > a > i.icon-long-arrow-down,
.action-dropdown li > a > i.icon-ok { color: var(--gp-brand); }

/* --- Composer footer (every reply / update / note form) -----------------
   One row: the submit leads, Reset beside it, anything else (the internal
   desk's status select) at the far end. Replaces three centred <p>s and one
   inline button. The inputs keep their .save.pending hooks; scp.js still
   relocates the real submit on click, which is why nothing here positions
   by sibling order. */
.gp-composer-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin: 14px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--gp-line-soft);
  text-align: left;
}
.gp-composer-actions input[type="submit"],
.gp-composer-actions button[type="submit"] { margin: 0; order: 0; }
.gp-composer-actions input[type="reset"] { margin: 0; order: 1; }
.gp-composer-actions label { margin-left: auto; order: 2; }
/* User page: the avatar block lost its inline margin/width. */
.gp-user-avatar { margin: 10px; width: 80px; }

/* --- Form footer (create / edit forms) -----------------------------------
   One row for Submit / Reset / Cancel on every full-page form, replacing
   three centred <p style="text-align:center"> rows and two ad-hoc flex
   divs. The submit leads; scp.js relocates the real input on click, so
   nothing here positions by sibling order. */
.gp-form-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin: 18px 0 0;
  padding-top: 14px;
  border-top: 1px solid var(--gp-line-soft);
  text-align: left;
}
.gp-form-actions input, .gp-form-actions button, .gp-form-actions a { margin: 0; }
.gp-form-actions input[type="submit"], .gp-form-actions button[type="submit"] { order: 0; }
.gp-form-actions input[type="reset"], .gp-form-actions button[type="reset"] { order: 1; }
.gp-form-actions input[name="cancel"], .gp-form-actions button[name="cancel"],
.gp-form-actions a.action-button { order: 2; }
/* A .sticky.bar inside a form is the form's header; give it the list bar's gap. */
form > div > .sticky.bar, form .sticky.bar:not(.stop) { margin-bottom: 16px; }
/* Admin › Reports: the per-surface stat-strip switches. */
.gp-stat-toggles form { flex-wrap: wrap; gap: 10px 18px; }
.gp-stat-toggles label { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--gp-ink-2); }
.gp-stat-toggles .faded { flex: 1 1 100%; }

/* =========================================================================
   26. Detail pages — one facts card, one composer, one Delete
   =========================================================================
   Ticket, task, user and org views render upstream's table.ticket_info; the
   internal desk renders .gp-ir-meta and .gp-ir-outcome (.gp-facts); the
   issue log renders .gp-issue-meta. Four markups, one look: a white card, a
   grid of cells, a small uppercase label over a plain value. No grey wells,
   no colons, no per-page rhythm. Rules here come last in the file on
   purpose, so they win every tie with the per-page blocks above. */

/* --- the card ------------------------------------------------------------ */
#content table.ticket_info,
.gp-ir-meta,
.gp-ir-outcome,
.gp-issue-meta,
.gp-facts {
  display: block;
  box-sizing: border-box;
  width: 100%;
  margin: 0 0 12px;
  padding: 6px 8px;
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-md, 10px);
  color: var(--gp-ink);
}
#content table.ticket_info.custom-data { margin: 0 0 12px; }
#content table.ticket_info + br { display: none; }
.gp-record-head { margin-bottom: 14px; }

/* --- a title row (Ticket Details, Request Details) ----------------------- */
table.ticket_info.custom-data > thead,
table.ticket_info.custom-data > thead > tr,
.gp-facts-title { display: block; }
table.ticket_info.custom-data > thead th,
.gp-facts-title {
  display: block;
  margin: 4px 10px 2px;
  padding: 0 0 7px;
  border: 0;
  border-bottom: 1px solid var(--gp-line);
  background: none;
  text-align: left;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
}
table.ticket_info.custom-data > thead th { margin: 4px 10px 2px; }

/* --- how the cells lay out: a grid that fills the card --------------------- */
.gp-facts dl,
.gp-issue-meta,
table.ticket_info table > tbody,
table.ticket_info.custom-data > tbody {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(190px, 100%), 1fr));
  gap: 0;
  margin: 0;
  padding: 0;
}
.gp-issue-meta { padding: 6px 8px; }
#content table.ticket_info.custom-data > tbody { display: grid; }   /* outranks the
                                                   block rule on the outer tbody */
.gp-facts dl { background: none; border: 0; flex: none; }
/* upstream table.ticket_info: two halves side by side, each half a grid */
table.ticket_info table { display: block; width: 100% !important; }
#content table.ticket_info:not(.custom-data) > tbody { display: block; }
#content table.ticket_info:not(.custom-data) > tbody > tr { display: flex; flex-wrap: wrap; }
#content table.ticket_info:not(.custom-data) > tbody > tr > td {
  display: block;
  flex: 1 1 50%;
  min-width: min(280px, 100%);   /* never wider than the card on a phone */
  box-sizing: border-box;
  width: auto !important;
  padding: 0;
  vertical-align: top;
}

/* --- the cell: label over value ------------------------------------------ */
.gp-fact,
table.ticket_info table > tbody > tr,
table.ticket_info.custom-data > tbody > tr,
.gp-issue-meta > div {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
  padding: 8px 10px;
}
.gp-fact dt,
table.ticket_info table th,
.ticket_info th,
table.ticket_info.custom-data > tbody > tr > td:first-child,
.gp-issue-meta > div > span {
  display: block;
  width: auto !important;
  margin: 0;
  padding: 0;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
  white-space: normal;
  text-align: left;
}
.gp-fact dd,
table.ticket_info table td,
table.ticket_info.custom-data > tbody > tr > td,
.gp-issue-meta > div > b {
  display: block;
  margin: 0;
  padding: 0;
  font-size: 13.5px;
  font-weight: 400;
  line-height: 1.45;
  color: var(--gp-ink);
  overflow-wrap: anywhere;
}
.gp-issue-meta > div > b.gp-sev { display: inline-flex; align-self: flex-start; font-weight: 500; }
.gp-fact dd.gp-overdue { color: var(--gp-err); font-weight: 600; }
table.ticket_info table td .faded,
.gp-fact dd .faded { color: var(--gp-ink-3); }
/* Inline-edit links stay quiet until hovered, as before */
table.ticket_info a.inline-edit { color: inherit; }

/* --- the locator chips: a task can carry its parent ticket beside it ------ */
.sticky.bar h2 .gp-locator + .gp-locator { margin-left: 6px; }
.sticky.bar h2 .gp-locator-parent { background: none; border: 1px solid var(--gp-line); }

/* --- Delete: one voice — red outline, last in the row, never filled -------- */
.sticky.bar .flush-right button[type="submit"].action-button {
  background: var(--gp-surface);
  color: var(--gp-ink);
  border: 1px solid var(--gp-line);
}
.sticky.bar .flush-right button[type="submit"].action-button:hover {
  background: var(--gp-surface-2);
  color: var(--gp-primary);
}
.sticky.bar .flush-right .action-button.gp-danger,
.sticky.bar .flush-right button[type="submit"].action-button.gp-danger,
.sticky.bar .flush-right a.red.button.action-button {
  background: var(--gp-surface);
  color: var(--gp-err);
  border-color: var(--gp-err-edge);
}
.sticky.bar .flush-right .action-button.gp-danger:hover,
.sticky.bar .flush-right button[type="submit"].action-button.gp-danger:hover,
.sticky.bar .flush-right a.red.button.action-button:hover {
  background: var(--gp-err-bg);
  color: var(--gp-err);
}

/* --- the composer: one card, one tab strip, one footer ------------------- */
#response_options,
#task_response_options,
.gp-ir-composer,
.gp-issue-addnote {
  box-sizing: border-box;
  margin: 20px 0 0;
  padding: 14px 16px 16px;
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-md, 10px);
}
#response_options > form,
#response_options > form.tab_content,
#task_response_options > form,
#task_response_options > form.tab_content {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
}
#response_options ul.tabs,
#task_response_options ul.tabs { margin: -4px 0 14px; }
.gp-ir-composer h3,
.gp-issue-addnote h3 { margin: 0 0 4px; font-size: 14px; font-weight: 600; }
.gp-issue-addnote textarea { box-sizing: border-box; width: 100%; }
.gp-composer-actions,
.gp-ir-composer-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 14px 0 0;
}
.gp-composer-actions input[type="reset"],
.gp-composer-actions button[type="reset"] { margin: 0; }

/* --- Linked records (internal desk) -------------------------------------
   The panel that lists what a request is linked to. Same row shape as the
   linked-tasks panel beside it; the unlink control is a quiet glyph, not a
   button, because removing a link is a correction rather than an action. */
.gp-ir-links li { display: flex; align-items: center; gap: 10px; }
.gp-ir-links li > span:nth-of-type(1) { flex: 1 1 auto; min-width: 0; }
.gp-ir-links form.gp-ir-inlineform { display: inline; margin: 0; }
button.gp-ir-unlink {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  margin: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  box-shadow: none;
  color: var(--gp-ink-3);
  cursor: pointer;
  transition: background var(--gp-t-color), color var(--gp-t-color);
}
button.gp-ir-unlink:hover { background: var(--gp-err-bg); color: var(--gp-err); }
button.gp-ir-unlink i { font-size: 12px; }


/* --- §27 Record linking -------------------------------------------------
   The Linked records panel and the picker dialog. One partial and one
   dialog serve all four record types, so these are the only rules needed.
   See docs/PRD-record-linking.md §6. */

.gp-links-panel {
  background: #fff;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-radius);
  padding: 14px 16px 6px;
  margin: 0 0 18px;
}
.gp-links-panel h3 {
  margin: 0 0 10px;
  font-size: 13px;
  font-weight: 600;
  color: var(--gp-ink-2);
}
.gp-links-group { margin: 0 0 10px; }
.gp-links-grouphead {
  font-size: 11px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
  padding: 4px 0;
}

/* The whole row is the hit target, not just the locator — the small number
   was too tight to aim at. The unlink control sits outside the anchor so it
   is not swallowed by it. */
.gp-links-row {
  display: flex;
  align-items: center;
  border-top: 1px solid var(--gp-line-2, var(--gp-line));
}
.gp-links-row:first-of-type { border-top: 0; }
.gp-links-hit {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  padding: 9px 2px;
  text-decoration: none;
  color: inherit;
}
.gp-links-hit:hover { background: var(--gp-surface-2, #f7f7f5); }

.gp-links-dot {
  flex: 0 0 auto;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--gp-ink-4, #c9c9c4);
}
.gp-links-dot.is-open   { background: var(--gp-accent-open, #3c7d50); }
.gp-links-dot.is-closed { background: var(--gp-ink-4, #c9c9c4); }

.gp-links-loc {
  flex: 0 0 auto;
  font-family: var(--gp-mono);
  font-size: 12px;
  color: var(--gp-ink-2);
}
.gp-links-title {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 13px;
}
.gp-links-status {
  flex: 0 0 104px;
  text-align: right;
  font-size: 12px;
  color: var(--gp-ink-3);
}

/* Restricted: type and number only, never a title, and nothing to click. */
.gp-links-restricted {
  padding: 9px 2px;
  gap: 10px;
  color: var(--gp-ink-3);
}
.gp-links-restricted .gp-links-title { font-style: italic; }

.gp-links-unlink {
  flex: 0 0 auto;
  padding: 6px 8px;
  color: var(--gp-ink-4, #c9c9c4);
  opacity: 0;
  transition: opacity .12s ease, color .12s ease;
}
.gp-links-row:hover .gp-links-unlink { opacity: 1; }
.gp-links-unlink:hover { color: var(--gp-danger, #b3261e); }
/* Keyboard users never hover. */
.gp-links-unlink:focus { opacity: 1; }

/* --- the picker dialog -------------------------------------------------- */

.gp-link-picker .gp-lp-body { padding: 4px 0 0; }
.gp-link-picker .gp-lp-search {
  width: 100%;
  box-sizing: border-box;
  margin: 0 0 10px;
}
.gp-link-picker .gp-lp-types {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0 0 10px;
}
.gp-lp-type-chip {
  font-size: 12px;
  padding: 3px 10px;
  border: 1px solid var(--gp-line);
  border-radius: 999px;
  color: var(--gp-ink-2);
  text-decoration: none;
}
/* #gp-link-picker, not the bare class: the dialog's own anchor colour is more
   specific than a single class, so the label came out near-black on the
   near-black fill and the chip read as an empty blob. */
#gp-link-picker .gp-lp-type-chip.gp-on {
  background: var(--gp-primary);
  border-color: var(--gp-primary);
  color: #fff;
}

.gp-lp-results {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 320px;
  overflow-y: auto;
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-radius);
}
.gp-lp-row { border-top: 1px solid var(--gp-line-2, var(--gp-line)); }
.gp-lp-row:first-child { border-top: 0; }
.gp-lp-row label {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  margin: 0;
  cursor: pointer;
}
.gp-lp-row label:hover { background: var(--gp-surface-2, #f7f7f5); }
.gp-lp-type {
  flex: 0 0 96px;
  font-size: 10px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
}
.gp-lp-loc {
  flex: 0 0 auto;
  font-family: var(--gp-mono);
  font-size: 12px;
}
.gp-lp-title {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 13px;
}
.gp-lp-status { flex: 0 0 auto; font-size: 11px; color: var(--gp-ink-3); }
.gp-lp-empty { padding: 14px; color: var(--gp-ink-3); font-size: 13px; }

.gp-lp-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 0 2px;
}
.gp-lp-actions { display: flex; gap: 8px; }

/* =========================================================================
   28. Faceted filters — the panel in the left rail
   =========================================================================
   See docs/agent-faceted-filters-plan.md.

   Every row in here is an <a>, not an input. It is drawn as a checkbox
   because that is the affordance an agent already reads as "pick some of
   these", but it navigates, and the whole state lives in the query string.
   The box glyph is a pseudo-element on .gp-facet-box, so it costs no icon
   font and no markup beyond one empty <i>.

   Where it lives: the panel is rendered by the page, under the find bar,
   and scp-theme.js moves it into .site-rail. It has to be that way round —
   header.inc.php is required BEFORE the list template, so the rail is on
   the wire before the page knows what its facets are. With script the
   in-place copy is never painted (html.gp-js below); without script it
   stays where it is and still works, because every row is a link.
   ------------------------------------------------------------------- */

/* The visually-hidden helper the group headings use to say "one at a
   time" without putting it on screen. */
.gp-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --- where the panel is, and is not -------------------------------------
   With script, the in-place copy never paints: scp-theme.js sets .gp-js on
   <html> from the <head>, before <body> is parsed, so there is no flash of
   a panel in the wrong place. Without script the rule never matches and the
   panel simply stays under the search bar. */
html.gp-js #content .gp-facets { display: none; }

.site-rail .gp-facets {
  display: flex;
  flex-direction: column;
  order: 3;
  /* The rail's other two blocks are their natural height and this one takes
     what is left, so the tabs and the queue tree stay put and only the
     filters scroll. min-height:0 is what lets a flex child actually shrink
     enough to scroll at all. */
  flex: 1 1 auto;
  min-height: 0;
  margin: 6px 0 0;
  padding: 10px 0 0;
  border-top: 1px solid var(--gp-line);
}
.site-rail .gp-facets-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* room for the scrollbar so rows do not shuffle when one appears */
  padding-right: 2px;
}

.gp-facets-title {
  flex: 0 0 auto;
  margin: 0 0 6px;
  padding: 0 8px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
}

/* --- a group ------------------------------------------------------------ */
.gp-facet-group { border: 0; }
.gp-facet-group + .gp-facet-group { margin-top: 2px; }

.gp-facet-sum {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 32px;
  padding: 0 8px;
  border-radius: var(--gp-r-sm);
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
  color: var(--gp-ink-2);
  list-style: none;
  user-select: none;
}
/* Safari still paints its own triangle without this pair. */
.gp-facet-sum::-webkit-details-marker { display: none; }
.gp-facet-sum::marker { content: ''; }

.gp-facet-sum:hover { background: var(--gp-surface-2); }
.gp-facet-sum:focus-visible {
  outline: 2px solid var(--gp-brand);
  outline-offset: -2px;
}
.gp-facet-sumlabel { flex: 1 1 auto; min-width: 0; }

.gp-facet-caret {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translate(-1px, -1px);
  opacity: 0.55;
  transition: transform var(--gp-t-move);
}
.gp-facet-group[open] > .gp-facet-sum .gp-facet-caret {
  transform: rotate(-135deg) translate(-2px, -2px);
}

.gp-facet-list {
  margin: 2px 0 8px;
  padding: 0;
  list-style: none;
}
.gp-facet-row { margin: 0; padding: 0; list-style: none; }

/* A long group scrolls inside itself rather than pushing every group below
   it off the rail. Organizations and help topics run to hundreds on a real
   install; this keeps all of them reachable without a cap, a "show more"
   or a line of script. */
.gp-facet-scroll {
  max-height: 232px;          /* ~7 rows, so it is visibly cut off */
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* --- a row -------------------------------------------------------------- */
.gp-facet {
  display: flex;
  align-items: center;
  gap: 9px;
  min-height: 30px;
  padding: 3px 8px;
  border-radius: var(--gp-r-sm);
  color: var(--gp-ink-2);
  font-size: 13px;
  text-decoration: none;
  cursor: pointer;
}
.gp-facet:hover {
  background: var(--gp-surface-2);
  color: var(--gp-ink);
  text-decoration: none;
}
.gp-facet:focus-visible {
  outline: 2px solid var(--gp-brand);
  outline-offset: -2px;
  text-decoration: none;
}
.gp-facet-on { color: var(--gp-ink); font-weight: 600; }

.gp-facet-label {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The count is decorative: the real one rides in the link's aria-label,
   because pseudo-element and icon text is not reliably exposed. */
.gp-facet-n {
  flex: 0 0 auto;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: var(--gp-ink-3);
}
.gp-facet-on .gp-facet-n { color: var(--gp-ink-2); }

/* The glyph. A square for a facet that toggles, a circle for one in a
   group where the choices are exclusive — so the shape tells you whether
   picking a second one adds to your selection or replaces it, before you
   click and find out. */
.gp-facet-box {
  flex: 0 0 auto;
  position: relative;
  box-sizing: border-box;
  width: 15px;
  height: 15px;
  border: 1.5px solid var(--gp-ink-3);
  border-radius: 3px;
  background: var(--gp-surface);
  transition: background var(--gp-t-move), border-color var(--gp-t-move);
}
.gp-facet-radio .gp-facet-box { border-radius: 50%; }

.gp-facet:hover .gp-facet-box { border-color: var(--gp-ink-2); }

.gp-facet-on .gp-facet-box {
  background: var(--gp-brand);
  border-color: var(--gp-brand);
}
/* The tick, drawn rather than fetched. */
.gp-facet-check.gp-facet-on .gp-facet-box::after {
  content: '';
  position: absolute;
  left: 4px;
  top: 1px;
  width: 4px;
  height: 8px;
  border: solid #fff;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.gp-facet-radio.gp-facet-on .gp-facet-box::after {
  content: '';
  position: absolute;
  left: 3px;
  top: 3px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #fff;
}

/* --- the custom date range --------------------------------------------- */
.gp-facet-custom { margin-top: 2px; }
.gp-facet-rangesum {
  display: flex;
  align-items: center;
  min-height: 30px;
  padding: 3px 8px 3px 32px;
  border-radius: var(--gp-r-sm);
  color: var(--gp-ink-3);
  font-size: 12px;
  cursor: pointer;
  list-style: none;
}
.gp-facet-rangesum::-webkit-details-marker { display: none; }
.gp-facet-rangesum::marker { content: ''; }
.gp-facet-rangesum:hover { background: var(--gp-surface-2); color: var(--gp-ink-2); }
.gp-facet-rangesum:focus-visible { outline: 2px solid var(--gp-brand); outline-offset: -2px; }

.gp-facet-rangeform {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 2px 0 6px;
  padding: 8px;
  background: var(--gp-surface-2);
  border-radius: var(--gp-r-sm);
}
.gp-facet-rangefield {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin: 0;
  font-size: 11px;
  color: var(--gp-ink-3);
}
.gp-facet-rangeform input[type="date"] {
  box-sizing: border-box;
  width: 100%;
  height: var(--gp-ctl-h);
  margin: 0;
  padding: 0 8px;
  font-size: 12px;
  background: var(--gp-surface);
  border: 1px solid var(--gp-line);
  border-radius: var(--gp-r-sm);
  color: var(--gp-ink);
}
.gp-facet-rangeform input[type="date"]:focus-visible {
  outline: 2px solid var(--gp-brand);
  outline-offset: -1px;
}
.gp-facet-rangego {
  justify-content: center;
  width: 100%;
  margin: 2px 0 0;
}

/* --- the panel foot: Tickets' way back to the criteria builder ---------- */
.gp-facets-foot {
  flex: 0 0 auto;
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid var(--gp-line);
}
.gp-facets-more {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 32px;
  padding: 0 8px;
  border-radius: var(--gp-r-sm);
  color: var(--gp-ink-2);
  font-size: 13px;
  text-decoration: none;
}
.gp-facets-more:hover { background: var(--gp-surface-2); color: var(--gp-ink); text-decoration: none; }
.gp-facets-more:focus-visible { outline: 2px solid var(--gp-brand); outline-offset: -2px; }

/* --- the chip bar, above the list -------------------------------------- */
.gp-chipbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin: 0 0 10px;
}
.gp-chipbar-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--gp-ink-3);
}
.gp-fchip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 26px;
  padding: 0 8px 0 10px;
  background: var(--gp-brand-soft);
  border-radius: var(--gp-r-pill);
  color: var(--gp-brand-soft-ink);
  font-size: 12px;
  font-weight: 500;
  text-decoration: none;
}
.gp-fchip:hover { background: var(--gp-brand); color: #fff; text-decoration: none; }
.gp-fchip:focus-visible { outline: 2px solid var(--gp-brand); outline-offset: 1px; }
.gp-fchip-x { font-size: 11px; opacity: 0.7; }
.gp-fchip:hover .gp-fchip-x { opacity: 1; }

.gp-chipbar-clear {
  margin-left: 2px;
  color: var(--gp-ink-3);
  font-size: 12px;
  text-decoration: underline;
}
.gp-chipbar-clear:hover { color: var(--gp-ink); }
.gp-chipbar-clear:focus-visible { outline: 2px solid var(--gp-brand); outline-offset: 2px; }

/* --- Sort, now on the selection plane ---------------------------------- */
/* .gp-selectbar keeps its name because the adjacency selectors in §25 hang
   off it; .gp-listbar is the added half. */
.gp-listbar-sort {
  position: relative;   /* the sort menu's .anchor-right hangs off this */
  margin-left: auto;
}
/* The bar can now exist with sort and no select-all (an empty list, or an
   agent without mass actions), and it must not collapse to nothing. */
.gp-listbar:not(:has(.gp-selectall)) { justify-content: flex-end; }

/* --- below the rail breakpoint ----------------------------------------
   The rail becomes a drawer behind #nav-toggle down here, and filters must
   not go behind a hamburger — that buries them worse than the <details>
   panel this replaced. So scp-theme.js leaves the panel in #content, and
   these rules turn it into one collapsible block under the search bar. */
@media (max-width: 980px) {
  html.gp-js #content .gp-facets { display: block; }
  .site-rail .gp-facets { display: none; }

  #content .gp-facets {
    margin: 0 0 12px;
    padding: 0;
    border: 1px solid var(--gp-line);
    border-radius: var(--gp-r-sm);
    background: var(--gp-surface);
  }
  #content .gp-facets-title { display: none; }
  #content .gp-facets-body { padding: 6px; }
  #content .gp-facets-foot { padding: 0 6px 6px; border-top: 0; }

  /* The toggle scp-theme.js inserts, and the collapsed state it drives. */
  .gp-facets-toggle {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    min-height: 40px;
    padding: 0 12px;
    background: none;
    border: 0;
    border-radius: var(--gp-r-sm);
    color: var(--gp-ink);
    font: inherit;
    font-size: 13px;
    font-weight: 600;
    text-align: left;
    cursor: pointer;
  }
  .gp-facets-toggle:focus-visible { outline: 2px solid var(--gp-brand); outline-offset: -2px; }

  /* Two columns of facets rather than one tall ribbon at tablet width. */
  #content .gp-facets-body {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 0 12px;
    align-items: start;
  }

  /* #content-prefixed deliberately, and it has to be: the grid rule above
     is (1 id, 1 class) and a bare .gp-facets-collapsed > .gp-facets-body is
     (0 ids, 2 classes), so it loses and the panel stays open however many
     times you press the toggle. Caught by the bench, not by reading. */
  #content .gp-facets-collapsed > .gp-facets-body,
  #content .gp-facets-collapsed > .gp-facets-foot { display: none; }
}

@media (max-width: 560px) {
  #content .gp-facets-body { grid-template-columns: 1fr; }
  .gp-chipbar-clear { margin-left: 0; flex: 1 0 100%; }
}

/* Above the breakpoint the toggle has no job. */
@media (min-width: 981px) {
  .gp-facets-toggle { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .gp-facet-caret,
  .gp-facet-box { transition: none; }
}
