/* "Modern" theme (2026-08-06, new feature, not part of the port) - a new,
 * more contemporary/mobile-friendly visual identity, coexisting with the
 * classic one (kept as the default - see User.theme / SessionController.
 * getTheme() / preferences.xhtml). Toggled by "theme-modern" on <body>
 * (UIPage.encodeChildren()), same mechanism as dark-mode.css.
 *
 * Rolled out progressively, screen by screen (see the plan doc) - starting
 * here with the page shell (header/navigation, responsive down to mobile)
 * and the ticket dashboard (the hardest screen: dynamic, user-configurable
 * columns - see controlPanelController.getColumnLabel()/.mobile-field-label
 * below for how the mobile card view shows field names without hardcoding
 * which column is which).
 *
 * Direction agreed with the user: flat/material-ish (flat colors, rounded
 * corners, light shadows for depth), system font stack, blue accent kept
 * from the classic theme's own brand color (#1093d3) for continuity.
 *
 * Responsive tables (@media max-width:900px throughout this file) -
 * pick ONE of these per table, don't invent a fourth:
 *   1. Card with field labels - a data table where each row is one record
 *      and every column matters on mobile (ticket dashboard, search
 *      results, bookmarks/history). table/thead/tbody -> display:block,
 *      tbody tr -> a bordered/shadowed flex-wrap card, each td either
 *      carries a ".mobile-field-label" (reuse that exact class - it's
 *      already hidden-by-default outside this media query) or, if the
 *      columns are dynamic/user-configurable (dashboard), a single
 *      pre-formatted ".mobile-summary-column" cell replaces all the
 *      individual columns instead (see controlPanelController.
 *      getColumnLabel()).
 *   2. Plain stack, no labels - a layout table with no "records" of its
 *      own (an e:panelGrid used to lay out unrelated form elements/
 *      buttons side by side, not to display data - FAQ's action-button
 *      grids, the tree+content 3-column grid). table/tbody/tr/td ->
 *      display:block; width:100% - nothing needs a label because each
 *      cell's own content is already self-explanatory outside of a
 *      column header's context.
 *   3. Internal scroll (exception, last resort) - only when the content
 *      itself can't reasonably be restructured into 1 or 2 without
 *      rewriting the underlying component (categories.xhtml's e:tree2:
 *      each row mixes an indent level, an icon and several inline action
 *      icons that only make sense read left-to-right). max-width:100%;
 *      overflow-x:auto on a wrapper, page itself never overflows.
 */

:root {
  /* Reference design's button/accent blue - a calmer slate blue rather than
     the classic theme's brighter cyan (#1093d3, kept nowhere now - the
     status/priority palette already reused this exact tone for
     ticket-status-CLOSED, so this also brings the buttons in line with
     that). */
  --modern-accent: #3b6ea5;
  --modern-accent-dark: #2f5a87;
  /* button--primary's green, desaturated the same way --modern-accent above
     desaturates the classic theme's brighter blue (#1093d3) - kept apart
     from the blue accent since primary/default/secondary now have distinct
     meanings (see the button system comment further down). */
  --modern-accent-green: #4a8f5c;
  --modern-accent-green-dark: #3d7549;
  --modern-bg: #f7f8fa;
  --modern-surface: #ffffff;
  --modern-stripe: #f3f4f6;
  --modern-text: #1a1f28;
  --modern-text-muted: #6b7280;
  --modern-icon-neutral: #b0b7c0;
  --modern-border: #e5e7eb;
  --modern-radius: 10px;
  --modern-shadow: 0 1px 3px rgba(16, 24, 40, 0.08), 0 1px 2px rgba(16, 24, 40, 0.06);
  /* Single source of truth for the theme's font - every font-family
     declaration below reads this instead of hardcoding the name, so trying
     a different font (or reverting) is a one-line change here.
     TRIAL (2026-08-10): 'Barlow Semi Condensed' first - not self-hosted
     (no @font-face here, unlike Barlow Condensed below), so it only
     renders for visitors who already have it installed locally; falls
     back to Barlow Condensed (self-hosted/always available) otherwise.
     Revert by dropping the 'Barlow Semi Condensed', prefix. */
  --modern-font: 'Barlow Semi Condensed', 'Barlow Condensed', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

/* Dark mode (dark-mode.css) is an independent preference, orthogonal to the
   theme choice - it can be combined with either theme. Every rule below
   reads these custom properties rather than literal colors specifically so
   that redefining them here is enough to re-theme the whole modern theme
   for dark mode, without repeating body.dark-mode in every single selector.
   Colors reused from dark-mode.css's own palette (#121212 page bg, #1e1e1e/
   #262626 card surfaces, #cccccc text, #2e2e2e borders) for visual
   consistency with the rest of the app in dark mode. */
body.dark-mode.theme-modern {
  --modern-bg: #121212;
  --modern-surface: #1e1e1e;
  --modern-stripe: #262626;
  --modern-text: #cccccc;
  --modern-text-muted: #8a8a8a;
  --modern-icon-neutral: #565d68;
  --modern-border: #2e2e2e;
  --modern-shadow: 0 1px 3px rgba(0, 0, 0, 0.4), 0 1px 2px rgba(0, 0, 0, 0.3);
}

body.theme-modern {
  background-color: var(--modern-bg);
  color: var(--modern-text);
  font-family: var(--modern-font);
}

/* Plain inheritance from the rule above isn't enough: a lot of legacy CSS
   (portlet.css, base.style.css, form.style.css...) sets its own explicit
   font-family (Verdana, Roboto...) directly on specific elements, and an
   explicit declaration on an element always wins over an inherited value
   regardless of specificity (see the statistics-page dropdown/portlet-
   section-text case earlier). Forcing it here with !important on every
   descendant is the only way to guarantee --modern-font everywhere in this
   theme - FontAwesome icons are excluded since they rely on their own icon
   font to render glyphs via ::before, not text content. */
body.theme-modern *:not(.fas):not(.far):not(.fab):not(.fal) {
  font-family: var(--modern-font) !important;
}

body.theme-modern h1,
body.theme-modern h2,
body.theme-modern h3 {
  color: var(--modern-text);
}

/* ---------------------------------------------------------------------- */
/* Header                                                                  */
/* ---------------------------------------------------------------------- */

body.theme-modern .header .region {
  background-color: var(--modern-surface);
  border-bottom: 1px solid var(--modern-border);
  box-shadow: var(--modern-shadow);
}

/* Logos (header + footer) are white-on-transparent PNGs designed for the
   classic theme's blue header bar - invisible against this theme's white
   surface in light mode (dark mode's dark surface is fine as-is). Recolored
   without a new asset: hide the actual <img> (opacity, keeps its layout box)
   and paint a solid #131328 shape clipped to the PNG's own alpha channel via
   mask-image - an exact silhouette match, not a filter-based approximation. */
body.theme-modern:not(.dark-mode) .header .logo img,
body.theme-modern:not(.dark-mode) .footer .region-inner > div:first-child img {
  opacity: 0;
}

body.theme-modern:not(.dark-mode) .header .logo,
body.theme-modern:not(.dark-mode) .footer .region-inner > div:first-child {
  position: relative;
}

body.theme-modern:not(.dark-mode) .header .logo::after,
body.theme-modern:not(.dark-mode) .footer .region-inner > div:first-child::after {
  content: "";
  position: absolute;
  inset: 0;
  background-color: #131328;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: left center;
  mask-position: left center;
  -webkit-mask-size: contain;
  mask-size: contain;
}

body.theme-modern:not(.dark-mode) .header .logo::after {
  -webkit-mask-image: url(images/logo-header-large.png);
  mask-image: url(images/logo-header-large.png);
}

body.theme-modern:not(.dark-mode) .footer .region-inner > div:first-child::after {
  -webkit-mask-image: url(images/logo-footer-small.png);
  mask-image: url(images/logo-footer-small.png);
}

body.theme-modern .header .region .current-user,
body.theme-modern .header .region .portlet-section-text {
  color: var(--modern-text);
  font-family: var(--modern-font);
  font-size: 19px;
}

body.theme-modern .header .region .dark-mode-toggle,
body.theme-modern .header .region .logout-link,
body.theme-modern .header .region .current-user {
  border-left-color: var(--modern-border);
}

body.theme-modern .header .region .dark-mode-toggle .fas,
body.theme-modern .header .region .logout-link .fas {
  color: var(--modern-text-muted);
}

body.theme-modern .header .region .dark-mode-toggle:hover,
body.theme-modern .header .region .logout-link:hover {
  background-color: var(--modern-bg);
}

/* Hamburger toggle (_header.xhtml) - only meaningful on narrow screens;
   hidden by default (including in the classic theme, where it stays
   inert), shown here below a breakpoint. */
.nav-toggle {
  display: none;
  order: -1;
}

body.theme-modern .nav-toggle {
  display: none;
  order: -1;
  align-items: center;
  padding: 0 1em;
  font-size: calc(1.4em + 1px);
  color: var(--modern-text);
}

/* ---------------------------------------------------------------------- */
/* Navigation                                                              */
/* ---------------------------------------------------------------------- */

body.theme-modern .navigation {
  background-color: var(--modern-surface);
  border-right: 1px solid var(--modern-border);
  padding: 16px 12px;
  /* layout.style.css's ".navigation { flex-basis: 280px }" is a fixed
     classic-theme width; let this theme's sidebar size to its content
     instead. */
  flex-basis: auto;
}

/* dark-mode.css gives the <ul class="portlet-menu"> its own background
   (#262626), a different shade than .navigation's own (#1e1e1e) - each
   individual button is reset back to #1e1e1e by another dark-mode.css rule,
   so the net effect was the gaps between buttons reading as a visibly
   different-toned panel behind them. Light mode has no such conflicting
   rule, which is why only dark mode showed this. */
body.dark-mode.theme-modern .navigation .portlet-menu {
  background-color: transparent !important;
}

/* The collapse arrow (.manager/.user in custom.css) is positioned with a
   hardcoded "top: Xem; position: relative" tuned to the classic theme's
   menu height - the modern theme's menu is taller (section labels), so that
   offset landed in the wrong place. Center it properly against the menu's
   actual rendered height instead. */
body.theme-modern .navigation #navigationForm {
  align-items: center;
}

body.theme-modern .navigation .manager,
body.theme-modern .navigation .user {
  position: static;
  top: auto;
  /* custom.css's "margin-right: 1em" (classic theme) offset the icon toward
     the menu instead of centering it in its own space. */
  margin-right: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2em;
}

body.theme-modern .navigation .moreItems .fa-chevron-left:before {
  margin-left: 50%;
}

body.theme-modern .navigation .moreItems .fa-chevron-left {
  color: var(--modern-accent);
}

body.theme-modern .navigation .portlet-menu,
body.theme-modern .navigation ul.portlet-menu {
  list-style: none;
  margin: 0;
  padding: 0;
}

body.theme-modern .navigation .portlet-menu-item,
body.theme-modern .navigation .portlet-menu-item-selected {
  margin-bottom: 4px;
  /* portlet.css has an unscoped, always-active
     ".portlet-menu-item, .portlet-menu-item-selected { padding: 2px; }" -
     same leaked-box-model story as the input's margin above, but on the <li>
     itself this time. */
  padding: 0;
  border-radius: 0;
  overflow: hidden;
}

body.theme-modern .navigation .portlet-menu-item input,
body.theme-modern .navigation .portlet-menu-item-selected input {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  /* Classic theme's own rule (navigation.style.css) sets "margin: 2px 0" on
     every nav button and is never reset here - invisible against a
     transparent background, but shows as a gap above/below the selected
     item's solid blue fill specifically. That, not a focus ring, was the
     "margin around the selected item". */
  margin: 0;
  /* The selected item is whatever was last clicked to navigate here, so it
     keeps the browser's default focus ring - a border-like outline drawn
     just outside the button's box, which read as an unwanted "margin"
     around it specifically. */
  outline: none;
  padding: 10px 14px 10px 44px;
  border-radius: 0;
  color: var(--modern-text);
  font-family: var(--modern-font);
  font-size: calc(1.05em + 1px);
  font-weight: 500;
  letter-spacing: 0.02em;
  cursor: pointer;
  /* Per-item background-image (icon) set further below, by id - input[type=
     submit] can't reliably host ::before/child icons. */
  background-repeat: no-repeat;
  background-position: 14px center;
  background-size: 18px 18px;
}

/* One SVG-as-background-image per item (data URI, single neutral color -
   input[type=submit] can't reliably host ::before/child icons the way a
   real <button> could, so this avoids touching MenuItemRenderer.java).
   Selected by id since each icon is unique to its item; the shared
   position/size/padding lives on the existing .portlet-menu-item(-selected)
   input rule above. */
body.theme-modern #navigationForm\:welcome {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%3E%3Cpath%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20d=%22M3%2010.5%2012%203l9%207.5%22/%3E%3Cpath%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20d=%22M5%209.5V21h14V9.5%22/%3E%3C/svg%3E");
}

body.theme-modern #navigationForm\:controlPanel {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%3E%3Crect%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20x=%223%22%20y=%223%22%20width=%227%22%20height=%229%22/%3E%3Crect%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20x=%2214%22%20y=%223%22%20width=%227%22%20height=%225%22/%3E%3Crect%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20x=%2214%22%20y=%2212%22%20width=%227%22%20height=%229%22/%3E%3Crect%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20x=%223%22%20y=%2216%22%20width=%227%22%20height=%225%22/%3E%3C/svg%3E");
}

body.theme-modern #navigationForm\:search {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%3E%3Ccircle%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20cx=%2211%22%20cy=%2211%22%20r=%227%22/%3E%3Cline%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20x1=%2221%22%20y1=%2221%22%20x2=%2216.65%22%20y2=%2216.65%22/%3E%3C/svg%3E");
}

body.theme-modern #navigationForm\:bookmarks {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%3E%3Cpath%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20d=%22M6%203h12v18l-6-4-6%204V3z%22/%3E%3C/svg%3E");
}

body.theme-modern #navigationForm\:journal {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%3E%3Ccircle%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20cx=%2212%22%20cy=%2212%22%20r=%229%22/%3E%3Cpolyline%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20points=%2212%207%2012%2012%2015%2015%22/%3E%3C/svg%3E");
}

body.theme-modern #navigationForm\:faqs {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%3E%3Ccircle%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20cx=%2212%22%20cy=%2212%22%20r=%229%22/%3E%3Cpath%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20d=%22M9.5%209a2.5%202.5%200%200%201%205%200c0%201.7-2.5%202-2.5%203.5%22/%3E%3Ccircle%20cx=%2212%22%20cy=%2216.5%22%20r=%220.75%22%20fill=%22%236b7280%22%20stroke=%22none%22/%3E%3C/svg%3E");
}

body.theme-modern #navigationForm\:about {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%3E%3Ccircle%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20cx=%2212%22%20cy=%2212%22%20r=%229%22/%3E%3Cline%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20x1=%2212%22%20y1=%2211%22%20x2=%2212%22%20y2=%2216.5%22/%3E%3Ccircle%20cx=%2212%22%20cy=%227.5%22%20r=%220.75%22%20fill=%22%236b7280%22%20stroke=%22none%22/%3E%3C/svg%3E");
}

body.theme-modern #navigationForm\:preferences {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%3E%3Cline%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20x1=%224%22%20y1=%226%22%20x2=%2220%22%20y2=%226%22/%3E%3Ccircle%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20cx=%229%22%20cy=%226%22%20r=%222%22/%3E%3Cline%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20x1=%224%22%20y1=%2212%22%20x2=%2220%22%20y2=%2212%22/%3E%3Ccircle%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20cx=%2215%22%20cy=%2212%22%20r=%222%22/%3E%3Cline%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20x1=%224%22%20y1=%2218%22%20x2=%2220%22%20y2=%2218%22/%3E%3Ccircle%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20cx=%229%22%20cy=%2218%22%20r=%222%22/%3E%3C/svg%3E");
}

body.theme-modern #navigationForm\:logout {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%3E%3Cpath%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20d=%22M9%2021H5a2%202%200%200%201-2-2V5a2%202%200%200%201%202-2h4%22/%3E%3Cpolyline%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20points=%2216%2017%2021%2012%2016%207%22/%3E%3Cline%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20x1=%2221%22%20y1=%2212%22%20x2=%229%22%20y2=%2212%22/%3E%3C/svg%3E");
}

body.theme-modern #navigationForm\:responses {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%3E%3Crect%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20x=%223%22%20y=%225%22%20width=%2218%22%20height=%2212%22%20rx=%222%22/%3E%3Cpath%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20d=%22M8%2017v3l-3-3%22/%3E%3C/svg%3E");
}

body.theme-modern #navigationForm\:utils {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%3E%3Crect%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20x=%223%22%20y=%229%22%20width=%2218%22%20height=%2210%22%20rx=%221%22/%3E%3Cpath%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20d=%22M8%209V6a1%201%200%200%201%201-1h6a1%201%200%200%201%201%201v3%22/%3E%3Cline%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20x1=%223%22%20y1=%2213%22%20x2=%2221%22%20y2=%2213%22/%3E%3C/svg%3E");
}

body.theme-modern #navigationForm\:administrators {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%3E%3Cpath%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20d=%22M12%202%2020%206v6c0%205-3.5%208.5-8%2010-4.5-1.5-8-5-8-10V6l8-4z%22/%3E%3C/svg%3E");
}

body.theme-modern #navigationForm\:batchTasks {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%3E%3Ccircle%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20cx=%2212%22%20cy=%2212%22%20r=%229%22/%3E%3Cpath%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20d=%22M12%207v5l3%203%22/%3E%3C/svg%3E");
}

body.theme-modern #navigationForm\:departments {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%3E%3Crect%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20x=%224%22%20y=%222%22%20width=%2216%22%20height=%2220%22/%3E%3Crect%20x=%228%22%20y=%226%22%20width=%222%22%20height=%222%22%20fill=%22%236b7280%22%20stroke=%22none%22/%3E%3Crect%20x=%2214%22%20y=%226%22%20width=%222%22%20height=%222%22%20fill=%22%236b7280%22%20stroke=%22none%22/%3E%3Crect%20x=%228%22%20y=%2211%22%20width=%222%22%20height=%222%22%20fill=%22%236b7280%22%20stroke=%22none%22/%3E%3Crect%20x=%2214%22%20y=%2211%22%20width=%222%22%20height=%222%22%20fill=%22%236b7280%22%20stroke=%22none%22/%3E%3Crect%20x=%2210%22%20y=%2216%22%20width=%224%22%20height=%226%22%20fill=%22%236b7280%22%20stroke=%22none%22/%3E%3C/svg%3E");
}

body.theme-modern #navigationForm\:departmentSelection {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%3E%3Cpath%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20d=%22M1%2012s4-7%2011-7%2011%207%2011%207-4%207-11%207-11-7-11-7z%22/%3E%3Ccircle%20stroke=%22%236b7280%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20cx=%2212%22%20cy=%2212%22%20r=%223%22/%3E%3C/svg%3E");
}

body.theme-modern #navigationForm\:statistics {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%3E%3Crect%20x=%225%22%20y=%2212%22%20width=%223%22%20height=%228%22%20fill=%22%236b7280%22%20stroke=%22none%22/%3E%3Crect%20x=%2210.5%22%20y=%228%22%20width=%223%22%20height=%2212%22%20fill=%22%236b7280%22%20stroke=%22none%22/%3E%3Crect%20x=%2216%22%20y=%224%22%20width=%223%22%20height=%2216%22%20fill=%22%236b7280%22%20stroke=%22none%22/%3E%3C/svg%3E");
}

/* Firefox draws its own dotted focus indicator INSIDE a button as a
   pseudo-border, independent of (and not removed by) outline:none above -
   same "extra frame around the selected/last-clicked item" symptom. */
body.theme-modern .navigation .portlet-menu-item input::-moz-focus-inner,
body.theme-modern .navigation .portlet-menu-item-selected input::-moz-focus-inner {
  border: 0;
  padding: 0;
}

/* Section group labels (Tickets/Aide/Administration - _navigationItems.xhtml,
   _navigationItems2.xhtml) - default hidden regardless of theme, same
   reasoning as .mobile-field-label: the <li> exists in the markup under the
   classic theme too, which never loads the rule below that shows it. */
.nav-section-label {
  display: none;
}

body.theme-modern .navigation .nav-section-label {
  display: block;
  margin: 20px 8px 8px;
  padding: 10px 6px 0;
  font-size: calc(0.8em + 1px);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--modern-accent);
  border-top: 1px solid var(--modern-border);
}

body.theme-modern .navigation .nav-section-label:first-child {
  margin-top: 0;
  padding-top: 0;
  border-top: none;
}

body.theme-modern .navigation .portlet-menu-item input:hover {
  background-color: var(--modern-bg);
}

body.theme-modern .navigation .portlet-menu-item-selected input {
  /* Deliberately just a color swap - no font-weight/size/spacing change:
     those shifted the item's rendered box slightly (bold glyphs are wider),
     which nudged neighbouring items and read as the whole menu "jumping"
     depending on which item was current. */
  background-color: var(--modern-accent);
  color: #ffffff;
}

/* dark-mode.css forces a light gray text color on ALL nav items (readable
   on its own dark background) with !important, which also overrides the
   white text above once this item's background turns blue - restore it,
   also !important to win that fight. */
body.dark-mode.theme-modern .navigation .portlet-menu-item-selected input {
  /* dark-mode.css also forces a dark gray *background* on every nav item
     (".portlet-menu-item input { background-color: #1e1e1e !important }"),
     not just text color - needs overriding here too, else the blue never
     shows in dark mode regardless of the text-color fix. */
  background-color: var(--modern-accent) !important;
  color: #ffffff !important;
}

/* ---------------------------------------------------------------------- */
/* Dashboard - toolbar / header row                                       */
/* ---------------------------------------------------------------------- */

body.theme-modern .dashboard-header,
body.theme-modern .dashboard-header-wrapper {
  background-color: transparent;
}

/* Toolbar button icons (Créer un ticket / Modifier les colonnes / Accès
   direct) - same background-image data-URI technique as the nav menu (see
   there for why ::before on input[type=submit] isn't reliable). White
   strokes since these buttons have a solid accent-color fill. */
body.theme-modern #controlPanelForm\:addTicketButtonControlPanel {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%3E%3Cline%20stroke=%22white%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20x1=%2212%22%20y1=%225%22%20x2=%2212%22%20y2=%2219%22/%3E%3Cline%20stroke=%22white%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20x1=%225%22%20y1=%2212%22%20x2=%2219%22%20y2=%2212%22/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: 12px center;
  background-size: 16px 16px;
  padding-left: 36px;
  top: 2px;
}

/* editColumnsButton lives inside <e:dataTable id="data">'s header facet, an
   extra JSF naming container - its real client id needs that "data:"
   segment (unlike the other two toolbar buttons, which sit outside the
   dataTable). Styled like button--tertiary (neutral, not the accent fill
   used by the other two toolbar buttons) per the button system - the icon
   is a data-URI SVG (can't reference CSS custom properties for its stroke
   color), so it needs its own light/dark-mode variants to stay visible
   against button--tertiary's light/dark background. */
body.theme-modern #controlPanelForm\:data\:editColumnsButton {
  /* Not inside a .form-item (it's in the dataTable header facet's own
     .edit-columns wrapper), so the shared ".form-item input[type=submit]"
     radius:0 rule (below) never reached it either - form.style.css's own
     4px stayed in effect until overridden directly here. */
  background-color: var(--modern-surface);
  color: var(--modern-text);
  border: 1px solid var(--modern-border);
  border-radius: 0;
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%3E%3Crect%20stroke=%22%231a1f28%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20x=%224%22%20y=%224%22%20width=%227%22%20height=%2216%22/%3E%3Crect%20stroke=%22%231a1f28%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20x=%2213%22%20y=%224%22%20width=%227%22%20height=%2216%22/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: 12px center;
  background-size: 16px 16px;
  padding: 0 15px 0 36px;
  /* Not inside a .form-item either, so none of form.style.css's base
     input[type=submit] rules (font-size:1.1rem, font-weight:normal,
     min-height:30px, margin:7px) reached it - only the .edit-columns-
     specific uppercase/0.9em/8px-padding one above did, which is why it
     looked visibly smaller/shoutier than "Créer un ticket". Matching that
     base look here directly instead. */
  text-transform: none;
  font-size: calc(1.1rem + 1px);
  font-weight: 500;
  min-height: 30px;
  margin: 7px;
}

body.theme-modern #controlPanelForm\:data\:editColumnsButton:hover {
  border-color: var(--modern-accent);
  color: var(--modern-accent);
}

/* Dark mode: same icon, light stroke to stay visible on --modern-surface's
   dark background there (--modern-text's dark-mode value, #cccccc). */
body.dark-mode.theme-modern #controlPanelForm\:data\:editColumnsButton {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%3E%3Crect%20stroke=%22%23cccccc%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20x=%224%22%20y=%224%22%20width=%227%22%20height=%2216%22/%3E%3Crect%20stroke=%22%23cccccc%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20x=%2213%22%20y=%224%22%20width=%227%22%20height=%2216%22/%3E%3C/svg%3E");
}

body.theme-modern #controlPanelForm\:gotoTicketButton {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%3E%3Cline%20stroke=%22white%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20x1=%225%22%20y1=%2212%22%20x2=%2219%22%20y2=%2212%22/%3E%3Cpolyline%20stroke=%22white%22%20fill=%22none%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%20points=%2212%205%2019%2012%2012%2019%22/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: 12px center;
  background-size: 16px 16px;
  padding-left: 36px;
}

body.theme-modern .controlPanel-title h1 span {
  font-family: var(--modern-font);
  font-size: calc(1.6em + 1px);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

/* Redundant with the Utilisateur/Gestionnaire segmented control - see the
   comment in controlPanel.xhtml. */
body.theme-modern .dashboard-title-mode {
  display: none;
}

body.theme-modern .dashboard-search {
  color: var(--modern-text-muted);
}

body.theme-modern .dashboard-filter .form-item label {
  font-family: var(--modern-font);
}

/* Utilisateur/Gestionnaire segmented control (controlPanel.xhtml) - replaces
   the classic theme's single fa-toggle-on/off icon there. Unlike
   .mobile-field-label (new markup, needs default-hiding so it doesn't leak
   into the classic theme), .dashboard-toggle-icon is the classic theme's
   own pre-existing element - it must stay visible there by default and
   only be hidden under body.theme-modern, the reverse of that pattern. */
.interface-switch {
  display: none;
}

body.theme-modern .dashboard-toggle-icon {
  display: none;
}

body.theme-modern .interface-switch {
  display: inline-flex;
  border: 1px solid var(--modern-border);
}

body.theme-modern .interface-switch-item {
  padding: 6px 16px;
  font-family: var(--modern-font);
  font-size: calc(1em + 1px);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--modern-text-muted);
}

body.theme-modern .interface-switch-item.active {
  background-color: var(--modern-accent);
  color: #ffffff;
}

body.theme-modern .dashboard-search-input {
  background-color: var(--modern-surface);
  border: 1px solid var(--modern-border);
  border-radius: 0;
  color: var(--modern-text);
}

body.theme-modern .dashboard-search-input:focus {
  outline: none;
  border-color: var(--modern-accent);
}

/* ==========================================================================
   Site-wide table row text (desktop)
   ==========================================================================
   Row text size was inconsistent from one table/page to the next: most rely
   on legacy portlet.css classes (.portlet-section-text, .portlet-table-text)
   that don't set their own font-size (so it fell back to whatever ancestor/
   browser default applied), and the ticket dashboard specifically had
   custom.css's ".dashboard tbody .oddRow/.evenRow td span" hardcoding 13px
   on top of that. Normalizes every table's row text to 16px regardless of
   which of those applies - the td rule for cells with no wrapping span, the
   span rule for the (majority) that do. Icons (<i class="fas ...">, not
   spans) are unaffected. ".content" as the second class (alongside
   "theme-modern") is what gives this enough specificity to beat the
   dashboard's own 2-class/3-element selector above (2 classes either way,
   but this one has more elements: body+table+tbody+td(+span) vs.
   tbody+td+span) without relying on source order. */
body.theme-modern .content table tbody td,
body.theme-modern .content table tbody td span {
  font-size: 17px;
}

/* ==========================================================================
   Button system
   ==========================================================================
   button--default and button--secondary: blue (var(--modern-accent), the
   modern-desaturated take on the classic theme's blue #1093d3) - the two
   are visually identical, kept as separate classes only because form.style.
   css already distinguishes them for the classic theme's own markup.
   button--primary: green (var(--modern-accent-green)), same desaturation
   logic as the blue, kept apart from secondary/default now that all three
   have distinct meanings.
   button--tertiary: neutral (var(--modern-surface)/var(--modern-text)) -
   white in light mode, dark grey in dark mode purely from those variables
   being redefined under body.dark-mode.theme-modern above, no separate dark
   rule needed here.
   All three cover both actual <input type="submit"> and the many places in
   this app where a <span>/h:panelGroup is styled as a button instead (see
   .form-item span.button--* base rules in form.style.css) so the two never
   look different depending on the underlying element. */
body.theme-modern input[type='submit'].button--default,
body.theme-modern input[type='submit'].button--primary,
body.theme-modern input[type='submit'].button--secondary,
body.theme-modern .form-item span.button--default,
body.theme-modern .form-item span.button--primary,
body.theme-modern .form-item span.button--secondary,
body.theme-modern .form-item input[type='submit'] {
  border-radius: 0;
  font-weight: 500;
}

body.theme-modern input[type='submit'].button--default,
body.theme-modern input[type='submit'].button--secondary,
body.theme-modern .form-item span.button--default,
body.theme-modern .form-item span.button--secondary {
  /* form.style.css hardcodes button--default/secondary's own background-
     color (#1093d3, the classic theme's blue) independently of anything
     here - never actually wired to var(--modern-accent) at all. */
  background-color: var(--modern-accent);
  border-color: var(--modern-accent);
  color: #ffffff;
}

body.theme-modern input[type='submit'].button--default:hover,
body.theme-modern input[type='submit'].button--secondary:hover,
body.theme-modern .form-item span.button--default:hover,
body.theme-modern .form-item span.button--secondary:hover {
  background-color: var(--modern-accent-dark);
  border-color: var(--modern-accent-dark);
}

body.theme-modern input[type='submit'].button--primary,
body.theme-modern .form-item span.button--primary {
  background-color: var(--modern-accent-green);
  border-color: var(--modern-accent-green);
  color: #ffffff;
}

body.theme-modern input[type='submit'].button--primary:hover,
body.theme-modern .form-item span.button--primary:hover {
  background-color: var(--modern-accent-green-dark);
  border-color: var(--modern-accent-green-dark);
}

body.theme-modern .form-item span.button--tertiary,
body.theme-modern input[type='submit'].button--tertiary {
  border-radius: 0;
  font-weight: 500;
  background-color: var(--modern-surface);
  border-color: var(--modern-border);
  color: var(--modern-text);
}

body.theme-modern .form-item span.button--tertiary:hover,
body.theme-modern input[type='submit'].button--tertiary:hover {
  border-color: var(--modern-accent);
  color: var(--modern-accent);
}

/* Native file input's "Choose file" button (ticketView.xhtml's attachment
   upload, among others) - a real, distinct native sub-element, styleable
   directly via ::file-selector-button (the "no file chosen" text next to it
   stays native/unstyled, only its font-size is set - see form.style.css).
   Styled like button--tertiary (the "Transférer" submit button next to it
   is the accent-colored one - see form.style.css/.button--secondary). */
body.theme-modern input[type='file']::file-selector-button,
body.theme-modern input[type='file']::-webkit-file-upload-button {
  border-radius: 0;
  border: 1px solid var(--modern-border);
  font-weight: 500;
  font-size: calc(1rem + 1px);
  min-height: 30px;
  padding: 0 15px;
  margin-right: 8px;
  background-color: var(--modern-surface);
  color: var(--modern-text);
  cursor: pointer;
}

body.theme-modern input[type='file']::file-selector-button:hover,
body.theme-modern input[type='file']::-webkit-file-upload-button:hover {
  border-color: var(--modern-accent);
  color: var(--modern-accent);
}

/* Filter dropdowns (nice-select.js, wired up in controlPanel.xhtml/custom.js -
   only active under this theme) and the manager filter's search field
   (input+datalist, also custom.js). Both restyled here to match the flat/
   material direction instead of nice-select.css's own plain defaults. */
body.theme-modern .nice-select {
  background-color: var(--modern-surface);
  border: 1px solid var(--modern-border);
  border-radius: 0;
  color: var(--modern-text);
  height: 38px;
  line-height: 36px;
  font-family: var(--modern-font);
  margin-right: 5px;
}

body.theme-modern .nice-select:hover {
  border-color: var(--modern-accent);
}

body.theme-modern .nice-select:active,
body.theme-modern .nice-select.open,
body.theme-modern .nice-select:focus {
  border-color: var(--modern-accent);
}

body.theme-modern .nice-select .current {
  color: var(--modern-text);
}

body.theme-modern .nice-select .list {
  background-color: var(--modern-surface);
  border-radius: 0;
  box-shadow: var(--modern-shadow);
  border: 1px solid var(--modern-border);
}

body.theme-modern .nice-select .option {
  color: var(--modern-text);
}

body.theme-modern .nice-select .option:hover,
body.theme-modern .nice-select .option.focus,
body.theme-modern .nice-select .option.selected.focus {
  background-color: var(--modern-bg);
}

body.theme-modern .nice-select .option.selected {
  color: var(--modern-accent);
  font-weight: 600;
}

body.theme-modern .datalist-input {
  background-color: var(--modern-surface);
  border: 1px solid var(--modern-border);
  border-radius: 0;
  color: var(--modern-text);
  height: 38px;
  min-width: 260px;
  padding: 0 12px;
  font-size: 17px;
  font-family: var(--modern-font);
  box-sizing: border-box;
  margin-right: 5px;
}

body.theme-modern .datalist-input:focus {
  outline: none;
  border-color: var(--modern-accent);
}

/* ---------------------------------------------------------------------- */
/* Dashboard - ticket table (desktop)                                     */
/* ---------------------------------------------------------------------- */

body.theme-modern .dashboard {
  border-collapse: separate;
  border-spacing: 0;
  background-color: var(--modern-surface);
  border-radius: var(--modern-radius);
  box-shadow: var(--modern-shadow);
  overflow: hidden;
  margin-top: 5px;
}

body.theme-modern .dashboard thead > tr .portlet-table-header {
  background-color: var(--modern-surface) !important;
  border-bottom: 2px solid var(--modern-border);
  color: var(--modern-text-muted) !important;
  text-transform: uppercase;
  font-family: var(--modern-font);
  font-weight: 600;
  letter-spacing: 0.03em;
}

/* actions-column only ever holds a single gear icon (plus its absolutely
   positioned dropdown, which doesn't affect layout) - nothing was
   constraining its width, so table-layout:auto let it grow arbitrarily.
   h:column's styleClass doesn't reach the <th> (only the body <td>), and
   it's always the 1st column, hence the nth-child(1) for the header.
   Scoped to #controlPanelForm - see comment above. */
body.theme-modern #controlPanelForm .dashboard thead tr:nth-child(2) th:nth-child(1),
body.theme-modern .dashboard td.actions-column {
  width: 40px;
}

/* custom.css's neutral (not-currently-sorted) column icon is a light gray
   (#8d8d8d) - went darker (var(--modern-text)) then medium
   (var(--modern-text-muted)) in earlier passes, still not enough contrast
   with the active icon's blue; --modern-icon-neutral is dedicated to this,
   lighter than --modern-text-muted on purpose. */
body.theme-modern .dashboard .column-header .sort-column .fa-sort-alpha-up,
body.theme-modern .dashboard .column-header .sort-column .fa-sort-alpha-down,
body.theme-modern .dashboard .column-header .sort-column .fa-sort-numeric-up,
body.theme-modern .dashboard .column-header .sort-column .fa-sort-numeric-down,
body.theme-modern .dashboard .column-header .fa-sort {
  color: var(--modern-icon-neutral) !important;
}

/* Plain up/down arrows instead of the alpha/numeric-flavoured glyphs
   (letters/digits baked into the icon) - same .fas font, just override which
   glyph the icon's ::before shows; classic theme keeps the original ones. */
body.theme-modern .dashboard .column-header .fa-sort-alpha-up::before,
body.theme-modern .dashboard .column-header .fa-sort-numeric-up::before {
  content: "\f0de";
}

body.theme-modern .dashboard .column-header .fa-sort-alpha-down::before,
body.theme-modern .dashboard .column-header .fa-sort-numeric-down::before {
  content: "\f0dd";
}

/* Foundation's base stylesheet (fondation.css) gives <thead>/<tbody>/<tfoot>
   their own "border: 1px solid #f1f1f1" - invisible against the classic
   theme's own light backgrounds, but shows as a stray line right above the
   header row inside this theme's rounded/shadowed table card. */
body.theme-modern .dashboard thead,
body.theme-modern .dashboard tbody,
body.theme-modern .dashboard tfoot {
  border: none;
}

body.theme-modern .dashboard tbody td {
  font-family: var(--modern-font);
  font-size: 17px;
  border-top: none !important;
  /* Deliberately near-zero: the cell content's own span already carries
     "padding: 3px 8px 3px 0" (custom.css), which this theme doesn't
     override - stacking a second padding here on top of it made desktop
     rows visibly taller than the classic theme's. */
  padding: 1px 0;
}

body.theme-modern .dashboard tbody tr.oddRow {
  background-color: var(--modern-surface);
}

body.theme-modern .dashboard tbody tr.evenRow {
  /* dark-mode.css has its own "!important" evenRow rule that already wins
     over this in dark mode - this only affects light mode, which had no
     such fallback and lost the odd/even alternation entirely (both rows
     were var(--modern-surface), i.e. identical). */
  background-color: var(--modern-stripe);
}

body.theme-modern .dashboard tbody tr.oddRow:hover,
body.theme-modern .dashboard tbody tr.evenRow:hover {
  background-color: var(--modern-accent) !important;
}

/* Status/priority badges keep their own color on hover (excluded here) -
   they're not affected by the row's text-color state, they're small
   filled/outlined pills with their own already-considered contrast. */
body.theme-modern .dashboard tbody tr:hover td span:not(.ticket-status):not(.ticket-priority) {
  color: #ffffff !important;
}

/* The per-row "..." actions dropdown (mark read/unread, bookmark) floats
   above the row (position:absolute, its own white background - see
   .ticketControlPanel-item-actions-wrapper below) rather than sitting on
   the blue hover fill, so it shouldn't inherit the white text above: white
   on the dropdown's own white background was unreadable.
   Not conditioned on :hover at all (2nd attempt was still hover-only and
   still lost the specificity race in some cases - clicking the gear keeps
   the row hovered, so the hover-white rule above was always the actual
   competitor) - this is deliberately over-specific (row class repeated +
   both wrapper levels) so it wins outright regardless of hover state or
   source order, instead of relying on a tie. */
body.theme-modern .dashboard tbody tr.oddRow td .ticketControlPanel-item-actions .ticketControlPanel-item-actions-wrapper span,
body.theme-modern .dashboard tbody tr.evenRow td .ticketControlPanel-item-actions .ticketControlPanel-item-actions-wrapper span {
  /* var(--modern-text), not -text-muted: needs to read as black in light
     mode specifically (the dropdown's background is plain white there),
     and var(--modern-text) is already the correct near-black/near-white
     value per mode (dark mode was already fine on its own). */
  color: var(--modern-text) !important;
}

/* Per-row actions dropdown + the header's bulk-actions dropdown - never
   themed before (hardcoded white background/dark gray text), so this was
   likely already looking out of place in dark mode even without the hover
   bug above. */
body.theme-modern .ticketControlPanel-item-actions-wrapper,
body.theme-modern .actions-list-wrapper {
  background-color: var(--modern-surface);
  border-color: var(--modern-border);
  box-shadow: var(--modern-shadow);
}

/* rem, not em: the per-row wrapper lives inside tbody td (font-size: 16px,
   set below) while the header wrapper lives inside thead, which never gets
   that same 16px - same "1.3em" therefore produced two different absolute
   sizes despite looking like the same rule. rem is always relative to the
   root <html> (16px, unmodified) so both end up identical regardless of
   their ancestors' font-size. */
body.theme-modern .ticketControlPanel-item-actions-wrapper > span,
body.theme-modern .actions-list-wrapper > input,
body.theme-modern .bookmarks .actions-list-wrapper > input {
  color: var(--modern-text-muted);
  font-family: var(--modern-font) !important;
  font-size: calc(1.1rem + 1px) !important;
}

/* e:text renders its label with the default "portlet-section-text" class
   (see UIText/TagsConfigurator), which sets its own font-family directly on
   that inner span - an explicit declaration on the element itself always
   wins over the inherited value from the rule above, so it must be
   overridden here too. */
body.theme-modern .ticketControlPanel-item-actions-wrapper .portlet-section-text {
  font-family: var(--modern-font) !important;
  font-size: calc(1.0rem + 1px) !important;
}

body.theme-modern .ticketControlPanel-item-actions-wrapper > span:hover,
body.theme-modern .actions-list-wrapper > input:hover {
  background-color: var(--modern-bg);
}

body.theme-modern .dashboard .ticketControlPanel-item-actions .fa-cog {
  font-size: calc(1em + 1px);
}

body.theme-modern .dashboard .actions-header-wrapper .fa-cog {
  font-size: calc(1.3em + 1px);
}

/* Status/priority pills - dropped custom.css's semantic red/orange/green/
   blue fills entirely for a calmer, less saturated palette (dark navy only
   for the genuinely urgent level, muted/outlined for the rest), matching
   the reference design. custom.css's ".dashboard tbody .oddRow/.evenRow td
   span" (higher specificity: extra element qualifiers) sets
   "padding: 3px 8px 3px 0" - the trailing 0 clobbered the badges' own
   left padding, hence !important below on padding too, not just colors. */
body.theme-modern .ticket-status,
body.theme-modern .ticket-priority.ticket-open.priority1,
body.theme-modern .ticket-priority.ticket-open.priority2,
body.theme-modern .ticket-priority.ticket-open.priority3,
body.theme-modern .ticket-priority.ticket-open.priority4,
body.theme-modern .ticket-priority.ticket-open.priority5 {
  border-radius: 0 !important;
  font-weight: 500;
  border: 1px solid transparent;
}

/* Status colors - keyed off ticket-status-<STATUS> (see TicketStatus.java /
   _controlPanelColumnContent.xhtml), since .ticket-open/.ticket-closed alone
   can't tell FREE from INPROGRESS etc. Logic: white/black (not the theme's
   surface/text vars) for the neutral/default state, increasingly present
   blue fills the further from "just sitting there free" a status is;
   negative/terminal outcomes get a muted outline instead of a fill so they
   don't visually compete with INPROGRESS. Colors are literal on purpose,
   deliberately identical in light and dark mode - no per-mode variants. */
/* Fully-qualified (.dashboard tbody tr td span.ticket-status, not just
   ".ticket-status") specifically to out-specificity dark-mode.css's
   ".dashboard tbody .oddRow/.evenRow td span { color: #e8e8e8 !important }"
   (custom.css) - a bare "body.theme-modern .ticket-status" (2 classes) lost
   to it (3 classes + more elements) in dark mode, even with !important on
   both sides; specificity, not !important, decides ties like this. */
body.theme-modern .dashboard tbody tr td span.ticket-status {
  background-color: #ffffff !important;
  border-color: #d7dbe0;
  color: #000000 !important;
}

body.theme-modern .dashboard tbody tr td span.ticket-status-INPROGRESS {
  background-color: #8ecae6 !important;
  border-color: #8ecae6 !important;
  color: #0b3d5c !important;
}

body.theme-modern .dashboard tbody tr td span.ticket-status-CLOSED,
body.theme-modern .dashboard tbody tr td span.ticket-status-APPROVED,
body.theme-modern .dashboard tbody tr td span.ticket-status-CONNECTED_TO_TICKET,
body.theme-modern .dashboard tbody tr td span.ticket-status-CONNECTED_TO_FAQ {
  background-color: #3b6ea5 !important;
  border-color: #3b6ea5 !important;
  color: #ffffff !important;
}

body.theme-modern .dashboard tbody tr td span.ticket-status-CANCELLED,
body.theme-modern .dashboard tbody tr td span.ticket-status-REFUSED,
body.theme-modern .dashboard tbody tr td span.ticket-status-EXPIRED,
body.theme-modern .dashboard tbody tr td span.ticket-status-ARCHIVED,
body.theme-modern .dashboard tbody tr td span.ticket-status-INCOMPLETE {
  background-color: #dbe7f0 !important;
  border-color: #dbe7f0 !important;
  color: #4a6580 !important;
}

body.theme-modern .ticket-priority.ticket-open.priority5 {
  background-color: #1e2a4a !important;
  color: #ffffff !important;
}

/* Postponed badge: placed after the generic .ticket-status rule above (same
   specificity, later wins) so it isn't left with that rule's black text.
   dark-mode.css's own ".ticket-status.ticket-postponed" rule only overrides
   background-color (higher specificity, 4 classes vs 3 here), never color -
   without this, dark mode fell back to this file's black text on its darker
   gray, an unreadable combination. */
body.theme-modern .dashboard tbody tr td span.ticket-status-POSTPONED,
body.theme-modern .dashboard tbody tr td span.ticket-status.ticket-postponed {
  background-color: #999999 !important;
  border-color: #999999 !important;
  color: #ffffff !important;
}

body.theme-modern .ticket-priority.ticket-open.priority4 {
  background-color: #5b9bd5 !important;
  color: #ffffff !important;
}

body.theme-modern .ticket-priority.ticket-open.priority3 {
  background-color: #ffffff !important;
  border-color: #d7dbe0;
  color: #000000 !important;
}

body.theme-modern .ticket-priority.ticket-open.priority2 {
  background-color: #e8edf3 !important;
  color: #5b6b7f !important;
}

body.theme-modern .ticket-priority.ticket-open.priority1 {
  background-color: #f4f6f8 !important;
  color: #8a97a6 !important;
}

/* Priority level icons - level number -> arrow "how urgent", low to high
   (see PriorityI18nKeyProvider/PriorityStyleClassProvider: priority1 =
   Très basse ... priority5 = Très élevée); double chevrons for the two
   extremes. .ticket-priority is a <span>, not an <input>, so ::before is
   reliable here (unlike the nav menu items). */
body.theme-modern .ticket-priority.ticket-open.priority1::before,
body.theme-modern .ticket-priority.ticket-open.priority2::before,
body.theme-modern .ticket-priority.ticket-open.priority3::before,
body.theme-modern .ticket-priority.ticket-open.priority4::before,
body.theme-modern .ticket-priority.ticket-open.priority5::before {
  font-family: "Font Awesome 5 Free";
  font-weight: 900;
  margin-right: 4px;
}

body.theme-modern .ticket-priority.ticket-open.priority1::before {
  content: "\f103";
}

body.theme-modern .ticket-priority.ticket-open.priority2::before {
  content: "\f078";
}

body.theme-modern .ticket-priority.ticket-open.priority3::before {
  content: "\f068";
}

body.theme-modern .ticket-priority.ticket-open.priority4::before {
  content: "\f077";
}

body.theme-modern .ticket-priority.ticket-open.priority5::before {
  content: "\f102";
}

/* Field labels shown next to each value on the mobile card view
   (_controlPanelColumnContent.xhtml's <e:text styleClass="mobile-field-label">) -
   irrelevant noise on desktop next to the table's own column headers, and
   MUST default to hidden regardless of theme (this bare, unscoped rule is
   intentional - .mobile-field-label elements exist in the markup even
   under the classic theme, which never even loads the rest of this
   stylesheet's body.theme-modern-scoped rules to hide them otherwise).
   !important is required: custom.css's ".dashboard tbody .oddRow td span"
   (and .evenRow variant) sets display:inline-block with a higher
   specificity (0,2,3) than a bare class selector (0,1,0) and would
   otherwise win and re-show the label. */
.mobile-field-label {
  display: none !important;
}

/* Fixed mobile ticket card (see controlPanel.xhtml's mobile-summary-column
   h:column) - hidden everywhere by default, same reasoning as
   .mobile-field-label above: exists in the DOM under the classic theme too,
   which never shows it. */
.mobile-summary-column {
  display: none !important;
}

/* mobile-card-id now lives in actions-column (controlPanel.xhtml), not
   inside mobile-summary-column, so it needs its own default-hide - it used
   to just inherit .mobile-summary-column's above. !important: custom.css's
   ".dashboard tbody .oddRow td span, .evenRow td span" (display:inline-
   block) is more specific than this bare class selector and would
   otherwise win on desktop - same trap called out elsewhere in this file. */
.mobile-card-id {
  display: none !important;
}

/* Filters accordion toggle (controlPanel.xhtml) - mobile-only feature,
   desktop keeps the filters always visible/uncollapsed. Unscoped (not
   body.theme-modern): this is new markup with no equivalent in the classic
   theme, and a theme-scoped display:none only hides it while
   body.theme-modern is present - the classic theme has no rule for it at
   all, so it fell back to the browser default (block, visible) there. */
.filters-toggle {
  display: none;
}

/* ---------------------------------------------------------------------- */
/* Responsive breakpoint - tablet/mobile                                  */
/* ---------------------------------------------------------------------- */

@media (max-width: 900px) {
  body.theme-modern .nav-toggle {
    display: flex;
  }

  /* No room for it next to the hamburger button/user icons at this width. */
  body.theme-modern .header .logo {
    display: none;
  }

  /* Both sized for desktop (h1's own browser default ~2em, further bumped to
     1.3em above, plus a 2.2em toggle icon) - way too large once the sidebar
     stacks away and the content column is this narrow. custom.css's own
     "margin: 10px 0" (h1) plus .content-inner's own padding stacked into a
     bigger gap than intended above the title - collapsed to 0 here. */
  body.theme-modern .controlPanel-title h1 {
    font-size: 1.1rem;
    margin: 0;
    white-space: nowrap;
  }

  body.theme-modern .controlPanel-title h1 span {
    font-size: 1em;
  }

  body.theme-modern .dashboard-toggle .fa-toggle-on,
  body.theme-modern .dashboard-toggle .fa-toggle-off {
    font-size: 1.4em;
  }

  body.theme-modern .columns {
    position: relative;
  }

  body.theme-modern .navigation {
    position: fixed;
    top: 68px;
    left: 0;
    bottom: 0;
    width: 260px;
    z-index: 1000;
    transform: translateX(-100%);
    transition: transform 0.2s ease;
    box-shadow: var(--modern-shadow);
    overflow-y: auto;
  }

  body.theme-modern.nav-open .navigation {
    transform: translateX(0);
  }

  /* min-width:0: flex items default to min-width:auto, i.e. they won't
     shrink below their content's own intrinsic width - a single wide
     descendant anywhere in .content (a size="N" input, a wide table, an
     unwrapped flex row...) can therefore hold this whole column, and the
     page, wider than the screen even where that descendant itself has its
     own "max-width: 100%". Safety net underneath the per-page fixes
     elsewhere in this file (departmentEdit/categories/faqView/bookmarks/
     ldap-search above) rather than a replacement for them: this alone
     doesn't reflow a table's columns or wrap a flex row, it just stops the
     unshrinkable element from dragging the whole page wider than the
     viewport (last resort is a horizontal scrollbar inside .content
     instead of the page itself). */
  body.theme-modern .content {
    flex-basis: 100%;
    min-width: 0;
  }

  body.theme-modern .content-inner {
    padding: 12px;
  }

  /* .dashboard-header-wrapper (items-count / search / manage-columns) is
     "display:flex; justify-content:space-between" (custom.css) with no
     wrapping - on a narrow screen its children just got squeezed/overflowed
     instead of shrinking. Stack them instead. */
  /* Matches the ticket cards' own 12px padding below, so the item count and
     search bar line up with the same left/right edges as the cards. */
  body.theme-modern .dashboard-header-wrapper {
    flex-wrap: wrap;
    gap: 8px;
    padding: 8px 12px;
    box-sizing: border-box;
  }

  body.theme-modern .dashboard-header-wrapper .items-count {
    flex: 1 1 100%;
    text-align: center;
  }

  body.theme-modern .dashboard-header-wrapper .dashboard-search {
    flex: 1 1 100%;
    width: 100%;
  }

  /* Full width (like the table below it), text/placeholder centered within
     it rather than left-aligned. */
  body.theme-modern .dashboard-search-input {
    flex: 1 1 auto;
    width: 100%;
    min-width: 0;
    text-align: center;
  }

  /* "Modifier les colonnes" edits which desktop table columns show - the
     mobile card below (.mobile-summary-column) ignores that entirely (fixed
     field set), so the button would do nothing here. */
  body.theme-modern .manage-columns {
    display: none;
  }

  /* ---------------------------------------------------------------------- */
  /* Toolbar reorder: Titre + switch -> Créer un ticket / Accès direct (2   */
  /* colonnes) -> filtres (accordéon) -> recherche/tableau (déjà dans le    */
  /* bon ordre, rien à faire de plus).                                     */
  /*                                                                        */
  /* .dashboard-header/.controlPanel-title/.dashboard-filter are 3 sibling  */
  /* wrappers under #controlPanelForm, each grouping pieces that need to    */
  /* end up interleaved in a different order - display:contents unwraps     */
  /* them (keeps their children in the DOM, drops their own box) so those   */
  /* children become direct flex items of the form and can be reordered    */
  /* individually via "order", crossing the original wrapper boundaries.    */
  /* ---------------------------------------------------------------------- */
  /* flex-direction: row (not column!) + wrap is what actually makes equal
     "order" values land on the same visual row - in a column-direction flex
     container every item gets its own line regardless of "order" ties (that
     only groups items when the container wraps horizontally), which is why
     title/switch and créer-un-ticket/accès-direct kept ending up stacked
     instead of side by side despite sharing an order value. Each "row
     group" below has flex-grow on at least one item so it fills 100% width
     and reliably pushes the next group onto a new line. */
  body.theme-modern #controlPanelForm {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
  }

  body.theme-modern .dashboard-header,
  body.theme-modern .controlPanel-title,
  body.theme-modern .dashboard-filter {
    display: contents;
  }

  /* Row 1: title (grows to fill remaining space) + switch. */
  body.theme-modern .controlPanel-title h1 {
    order: 1;
    flex: 1 1 auto;
    margin: 0;
  }

  body.theme-modern .dashboard-toggle {
    order: 1;
    flex: 0 0 auto;
  }

  /* Smaller so it has a chance to actually fit next to the title. */
  body.theme-modern .interface-switch-item {
    padding: 5px 10px;
    font-size: 0.85em;
  }

  /* Row 2: Créer un ticket (left) / N° de ticket + Accès direct, horizontal,
     on the right - form-goto-ticket keeps its own normal (row) internal
     layout, just placed as the 2nd half here. If a long translation
     doesn't fit both halves side by side, flex-wrap just drops the second
     one to its own line underneath, which is fine. */
  /* Different order values (not tied) so which one is left/right doesn't
     depend on DOM-order tie-breaking - goto-ticket (2) paints first (left),
     add-ticket-btn (3) second (right); nothing forces a line break between
     them so they still end up on the same row. */
  body.theme-modern .form-goto-ticket {
    order: 2;
    flex: 1 1 calc(50% - 6px);
    justify-content: flex-end;
    gap: 4px;
  }

  /* margin-left: auto pushes it to the right edge of its row even when it
     ends up alone on its own line (goto-ticket's content didn't leave room
     for it on the same row) - without it, flex-start would leave it
     stranded on the left in that case. */
  body.theme-modern .dashboard-filter .add-ticket-btn {
    order: 3;
    flex: 1 1 calc(50% - 6px);
    margin-left: auto;
  }

  /* input[type=submit] is inline-block/replaced, so margin-left:auto on it
     alone is unreliable across browsers - making its wrapping .form-item a
     flex row guarantees the auto margin is honored (flex items always
     respect auto margins). */
  body.theme-modern .dashboard-filter .add-ticket-btn .form-item {
    display: flex;
  }

  /* Both compacted (smaller padding/font, narrower ticket-number input) to
     maximize the odds "Créer un ticket" and "Accès direct" actually fit
     side by side at typical phone widths. The margin-left:auto on the
     surrounding .add-ticket-btn div only pushes that div's box to the
     right - the submit input itself, being just an inline-ish block left
     inside that (wider) div, still needs its own margin-left:auto to hug
     the div's right edge. */
  body.theme-modern .dashboard-filter .add-ticket-btn input[type='submit'],
  body.theme-modern .form-goto-ticket input[type='submit'] {
    padding: 0 6px;
    font-size: 0.78em;
    min-height: 26px;
    margin-left: auto;
  }

  body.theme-modern .form-goto-ticket input[type='text'] {
    padding: 0 4px;
  }

  /* custom.css nudges this button down 3px ("top: 3px") to align it with
     the rest of its row in the desktop toolbar - on mobile it's alone in a
     shorter/differently-aligned row, so the same nudge throws it off
     center. Cancel it out here instead of touching the shared desktop
     rule. */
  body.theme-modern #controlPanelForm\:addTicketButtonControlPanel {
    top: 0;
  }

  /* Accordion toggle (see .filters-toggle in controlPanel.xhtml) - filters
     start collapsed, tap to expand. flex: 1 1 100% guarantees this always
     starts its own row, however row 2 above happened to wrap. */
  body.theme-modern .filters-toggle {
    order: 4;
    flex: 1 1 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    cursor: pointer;
    padding: 10px 4px;
    font-family: var(--modern-font);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: var(--modern-text);
    border-top: 1px solid var(--modern-border);
    border-bottom: 1px solid var(--modern-border);
  }

  body.theme-modern .filters-toggle .fa-chevron-down {
    transition: transform 0.15s ease;
  }

  body.filters-open .filters-toggle .fa-chevron-down {
    transform: rotate(180deg);
  }

  body.theme-modern .dashboard-filter > .form-item,
  body.theme-modern .dashboard-filter > .hideme {
    order: 5;
    display: none;
  }

  body.filters-open .dashboard-filter > .form-item {
    display: block;
  }

  /* Gap before the ticket list. */
  body.theme-modern #controlPanelForm > .dashboard {
    order: 6;
    margin-top: 16px;
  }

  /* Filters as a 2-column grid instead of one per line - each takes ~half
     the width, label above control (already the case - see the "label
     above" rule below, since the inline label+control layout from desktop
     is too cramped at this width for some labels e.g. "Gestionnaire"). */
  body.theme-modern .dashboard-filter > .form-item {
    flex: 1 1 calc(50% - 6px);
  }

  body.theme-modern .dashboard-filter > .form-item label {
    display: block;
    margin-bottom: 4px;
  }

  /* The manager filter's search field (custom.js/controlPanel.xhtml) has a
     desktop-oriented "min-width: 260px" - wider than half (or even all) of
     a phone screen, which is what was forcing the whole page into
     horizontal scrolling. */
  body.theme-modern .dashboard-filter .nice-select,
  body.theme-modern .dashboard-filter .datalist-input {
    width: 100%;
    min-width: 0;
  }

  /* Ticket dashboard: table -> stacked cards. Each row becomes a card; each
     cell becomes a labelled block inside it (see .mobile-field-label, shown
     again here - it is what makes this readable without hardcoding which
     dynamic column holds what, since columns are user-reorderable) - laid
     out with flex-wrap instead of one-per-line, so short fields (ID, state,
     priority, dates...) pack two-plus to a row instead of each eating a
     full-width line; a long field (e.g. the subject) just wraps taller
     without breaking the row's alignment, since flex-wrap rows are
     independent (unlike a strict grid). */
  body.theme-modern #controlPanelForm .dashboard,
  body.theme-modern #controlPanelForm .dashboard thead,
  body.theme-modern #controlPanelForm .dashboard tbody {
    display: block;
    width: 100% !important;
  }

  /* Only the actual per-column header labels (thead's 2nd <tr> - the
     dataTable's table-level "header" facet, i.e. the toolbar row with
     items-count/search/manage-columns, is the 1st <tr> and must stay). */
  body.theme-modern #controlPanelForm .dashboard thead > tr:nth-child(2) {
    display: none;
  }

  body.theme-modern #controlPanelForm .dashboard tbody tr {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    border: 1px solid var(--modern-border);
    border-radius: var(--modern-radius);
    margin-bottom: 12px;
    padding: 0;
    box-shadow: var(--modern-shadow);
  }

  body.theme-modern #controlPanelForm .dashboard tbody td {
    flex: 1 1 45%;
    min-width: 130px;
    width: auto !important;
    border-top: none !important;
    padding: 4px 6px 4px 0;
  }

  body.theme-modern .mobile-field-label {
    display: block !important;
    font-size: 0.72em;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--modern-text-muted);
    margin-top: 6px;
  }

  body.theme-modern .mobile-field-label:first-child {
    margin-top: 0;
  }

  /* Fixed-field mobile card: the dynamic per-column cells (desktop's
     user-configurable "Modifier les colonnes") are replaced entirely by
     .mobile-summary-column - hide every other cell, keep just that one and
     the actions gear (bookmark/mark read - still useful on mobile). */
  body.theme-modern #controlPanelForm .dashboard tbody td:not(.actions-column):not(.mobile-summary-column) {
    display: none !important;
  }

  /* Gear icon + ticket # stacked in their own narrow column, top-left -
     taken out of the flex flow (tr is already position:relative) so
     mobile-summary-column's row 1 (the title) can sit to its right on the
     same visual lines instead of below it; .mobile-card-row1's
     padding-left below just clears this column's reserved width. */
  body.theme-modern #controlPanelForm .dashboard tbody td.actions-column {
    position: absolute;
    top: 8px;
    left: 8px;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    width: 32px;
    padding: 0;
  }

  body.theme-modern #controlPanelForm .dashboard tbody td.mobile-summary-column {
    order: 2;
    display: block !important;
    flex: 1 1 100%;
    width: 100% !important;
    padding: 0;
    font-family: var(--modern-font);
  }

  /* The clickable wrapper (controlPanel.xhtml's <h:panelGroup
     onclick="selectTicket(...)">) renders as a <span> - inline by default,
     so it only sized itself to its content instead of the full column. */
  body.theme-modern #controlPanelForm .dashboard tbody td.mobile-summary-column > span {
    display: block;
    width: 100%;
  }

  /* !important throughout this card: custom.css's ".dashboard tbody .oddRow
     td span, .evenRow td span" (display:inline-block, color:#000, padding)
     is more specific (extra element qualifiers) than a bare class selector
     and would otherwise win, same trap hit earlier with .ticket-status/etc. */
  body.theme-modern .mobile-card-row1,
  body.theme-modern .mobile-card-row2,
  body.theme-modern .mobile-card-row3,
  body.theme-modern .mobile-card-row4 {
    display: flex !important;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
  }

  /* padding-left clears the absolutely positioned actions-column (gear +
     ticket #, 32px wide + 8px left offset) sitting to this row's left -
     rows 2-4 don't need it, that column is only as tall as the icon+number
     stack, well within row 1's own (now 2-line-capable) height. Single
     child now (subject only, the ticket # moved into actions-column), so
     justify-content:space-between from the shared rule above is moot. */
  body.theme-modern .mobile-card-row1 {
    align-items: flex-start;
    margin-bottom: 6px;
    padding-left: 44px;
    min-height: 40px;
  }

  /* min-width:0 (flex items default to min-width:auto, i.e. their own
     unwrapped text width as a floor, which a bare number has no natural
     break point to shrink below) + overflow-wrap let a longer ticket #
     wrap/clip to the 32px column instead of forcing it wider and
     overlapping the title next to it - same class of bug as the earlier
     filters/select overflow issues. */
  body.theme-modern .mobile-card-id {
    display: block !important;
    width: 100%;
    min-width: 0;
    box-sizing: border-box;
    overflow-wrap: anywhere;
    text-align: left;
    color: var(--modern-text-muted) !important;
    font-weight: 600;
    font-size: 0.85em;
  }

  body.theme-modern .mobile-card-subject {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
    font-size: 1.1em;
    font-weight: 600;
    color: var(--modern-text);
    margin-top: -6px;
  }

  body.theme-modern .mobile-card-row2,
  body.theme-modern .mobile-card-row3 {
    font-size: 0.85em;
    color: var(--modern-text-muted);
    margin-bottom: 4px;
  }

  /* Same min-width:0 trap as .mobile-card-id/.mobile-card-subject above,
     applied to every remaining flex child of rows 2-3: a long department/
     category/display name has no natural break point (or a real device's
     WebKit resolves the flex-basis differently from Chrome DevTools'
     device-toolbar emulation, which stays on Blink) and was found to
     shrink-collapse the whole card instead of wrapping. */
  body.theme-modern .mobile-card-service-category,
  body.theme-modern .mobile-card-owner,
  body.theme-modern .mobile-card-date,
  body.theme-modern .mobile-card-manager {
    min-width: 0;
    overflow-wrap: anywhere;
  }

  /* Right column (owner/manager, and the priority badge in row 4) aligned
     to the right edge. */
  body.theme-modern .mobile-card-owner,
  body.theme-modern .mobile-card-manager,
  body.theme-modern .mobile-card-row4 .ticket-priority {
    text-align: right;
  }

  body.theme-modern .mobile-card-row4 {
    margin-top: 8px;
  }
}

/* ==========================================================================
   Statistics page (statistics.xhtml)
   ==========================================================================
   e:panelGrid/e:dataTable render as plain <table>s with no styleClass of
   their own by default - every one of them (filters, chart zoom controls,
   result tables) falls back to the same old portlet-table-header/-text/
   -alternate/-footer look (blue/red, Verdana) with no way to tell them
   apart by class. Rather than fragile structural selectors, the 4 real
   result <e:dataTable>s got styleClass="statistics-table" and the filter
   <e:panelGrid> got styleClass="statistics-filters" (see statistics.xhtml) -
   the un-classed panelGrids (chart zoom control layout) are just left with
   the neutral reset below, since they're pure layout, not real tables. */
/* --modern-font is now forced globally (see body.theme-modern *
   near the top of this file), so no statistics-specific font-family rule
   is needed here anymore. */
body.theme-modern .statistics .portlet-table-header,
body.theme-modern .statistics .portlet-table-footer,
body.theme-modern .statistics .portlet-table-text,
body.theme-modern .statistics .portlet-table-alternate {
  background: transparent;
  color: var(--modern-text);
}

body.theme-modern .statistics-filters {
  border-collapse: collapse;
}

body.theme-modern .statistics-filters td {
  padding: 4px 16px 12px 0;
  vertical-align: top;
}

/* e:panelGrid columns="9" always reserves all 9 columns (label row + its
   matching control row) even for statistics types that only use a handful
   of filters - the unused columns' <h:panelGroup> wrappers still render
   (they have no "rendered" of their own, only their single child does),
   leaving a permanently empty, padded cell - "as if there were a max
   number of columns" that never adapts. Moving "rendered" up to the
   wrapper itself isn't safe: panelGrid wraps to a new row every N *actually
   rendered* children using the static columns="9" - if fewer than 9
   render, the wrap point no longer falls between the label row and the
   control row, mixing them together. So the wrapper stays unconditionally
   rendered (untouched layout/column math), and statistics.xhtml instead
   sets its styleClass to "filter-cell-empty" whenever its single child
   won't render (same condition, negated) - purely a CSS hook, :has()
   collapses that cell's padding so it stops looking like a broken empty
   box, without touching how many columns exist. */
body.theme-modern .statistics-filters td:has(.filter-cell-empty) {
  padding: 0;
}

body.theme-modern .statistics-filters label {
  display: block;
  margin-bottom: 4px;
  font-weight: 600;
  text-transform: uppercase;
  font-size: 14px;
  letter-spacing: 0.03em;
  color: var(--modern-text-muted);
}

body.theme-modern .statistics-filters select {
  background-color: var(--modern-surface);
  border: 1px solid var(--modern-border);
  border-radius: 0;
  color: var(--modern-text);
  font-family: inherit;
  /* Explicit px, not em/inherit: the legacy portlet-table-* classes these
     controls sit near default to 12px (portlet.css), so an em-relative bump
     would still have landed tiny - matches the dashboard row text size
     (also an explicit 16px, same reasoning) instead. */
  font-size: 16px;
  padding: 6px 8px;
}

body.theme-modern .statistics-filters select:focus {
  outline: none;
  border-color: var(--modern-accent);
}

body.theme-modern .statistics-table {
  border-collapse: collapse;
  border: 1px solid var(--modern-border);
  background-color: var(--modern-surface);
}

/* The table-wide header/footer facets are just a lone <hr/> (see
   statistics.xhtml) - a leftover separator that doesn't fit the flat card
   look, the border below already does that job. */
body.theme-modern .statistics-table hr {
  display: none;
}

body.theme-modern .statistics-table .portlet-table-header {
  background-color: var(--modern-accent);
  color: #ffffff;
  font-weight: 600;
  text-transform: uppercase;
  font-size: 14px;
  letter-spacing: 0.03em;
  padding: 8px 12px;
  border-bottom: 1px solid var(--modern-border);
}

body.theme-modern .statistics-table .portlet-table-text td,
body.theme-modern .statistics-table .portlet-table-alternate td {
  padding: 6px 12px;
  font-size: 16px;
}

body.theme-modern .statistics-table .portlet-table-alternate {
  background-color: var(--modern-stripe);
}

/* Chart resize controls (statistics.xhtml) - were <h:graphicImage> pointing
   at old bitmap icons (media/images/*.png, since removed), now plain <i>
   FontAwesome icons wired to the same hidden command buttons. */
body.theme-modern .chart-resize-icon {
  cursor: pointer;
  color: var(--modern-text-muted);
  font-size: calc(1.2em + 1px);
  padding: 6px;
}

body.theme-modern .chart-resize-icon:hover {
  color: var(--modern-accent);
}

@media (max-width: 900px) {
  /* e:panelGrid columns="9" renders a real 2-row x 9-col HTML table: <tr 1>
     holds the 9 labels, <tr 2> holds the 9 matching controls directly below
     them (same column index links a label to its control) - it's NOT 9
     rows of (label, control) pairs. A naive display:block stack therefore
     showed all 9 labels first, then all 9 controls, instead of alternating.
     Same display:contents + explicit order trick as the dashboard mobile
     toolbar: unwrap <tbody>/both <tr> so all 18 <td> become direct flex
     items of the table, then explicitly pair label[n] (row 1, col n) with
     control[n] (row 2, col n) via matching order values so they land next
     to each other, one pair per line (flex: 1 1 100%). (A "detached native
     select dropdown" was suspected here at one point and chased through
     several rewrites - turned out to be a Chrome DevTools mobile-emulation
     artifact, not a real bug on an actual phone, so this is back to the
     original, simplest version.) */
  body.theme-modern .statistics-filters {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    width: 100%;
  }

  body.theme-modern .statistics-filters tbody,
  body.theme-modern .statistics-filters tr {
    display: contents;
  }

  body.theme-modern .statistics-filters td {
    flex: 1 1 100%;
    /* Flex items default to min-width:auto, i.e. their content's own
       intrinsic width (a <select> with long department names) can still
       force the row wider than the container despite flex-basis:100% -
       this is what was overflowing past the header/footer width. */
    min-width: 0;
    box-sizing: border-box;
    padding: 4px 0 12px 0;
  }

  /* Same empty-column collapse as the desktop rule above, mobile just
     needed its own padding override since this block redeclares it. */
  body.theme-modern .statistics-filters td:has(.filter-cell-empty) {
    padding: 0;
    flex-basis: 0;
  }

  body.theme-modern .statistics-filters tr:nth-child(1) > td:nth-child(1) { order: 1; }
  body.theme-modern .statistics-filters tr:nth-child(2) > td:nth-child(1) { order: 2; }
  body.theme-modern .statistics-filters tr:nth-child(1) > td:nth-child(2) { order: 3; }
  body.theme-modern .statistics-filters tr:nth-child(2) > td:nth-child(2) { order: 4; }
  body.theme-modern .statistics-filters tr:nth-child(1) > td:nth-child(3) { order: 5; }
  body.theme-modern .statistics-filters tr:nth-child(2) > td:nth-child(3) { order: 6; }
  body.theme-modern .statistics-filters tr:nth-child(1) > td:nth-child(4) { order: 7; }
  body.theme-modern .statistics-filters tr:nth-child(2) > td:nth-child(4) { order: 8; }
  body.theme-modern .statistics-filters tr:nth-child(1) > td:nth-child(5) { order: 9; }
  body.theme-modern .statistics-filters tr:nth-child(2) > td:nth-child(5) { order: 10; }
  body.theme-modern .statistics-filters tr:nth-child(1) > td:nth-child(6) { order: 11; }
  body.theme-modern .statistics-filters tr:nth-child(2) > td:nth-child(6) { order: 12; }
  body.theme-modern .statistics-filters tr:nth-child(1) > td:nth-child(7) { order: 13; }
  body.theme-modern .statistics-filters tr:nth-child(2) > td:nth-child(7) { order: 14; }
  body.theme-modern .statistics-filters tr:nth-child(1) > td:nth-child(8) { order: 15; }
  body.theme-modern .statistics-filters tr:nth-child(2) > td:nth-child(8) { order: 16; }
  body.theme-modern .statistics-filters tr:nth-child(1) > td:nth-child(9) { order: 17; }
  body.theme-modern .statistics-filters tr:nth-child(2) > td:nth-child(9) { order: 18; }

  body.theme-modern .statistics-filters select {
    width: 100%;
    min-width: 0;
    box-sizing: border-box;
  }

  /* Chart + its zoom controls (statistics-chart, the columns="2" panelGrid
     around <c:chart>) - simple top-to-bottom stack is fine here (width
     icons, reset icon, chart, height icons already read sensibly in DOM
     order), no need for the label/control reordering trick above. The
     <img> itself is rendered by ChartRenderer with explicit width/height
     HTML attributes sized for desktop (statisticsController.chartWidth/
     Height, adjusted server-side by the zoom buttons) - those attributes
     set the image's intrinsic/native size, but max-width:100% + height:auto
     still scales it down to fit while keeping its aspect ratio. */
  body.theme-modern .statistics-chart,
  body.theme-modern .statistics-chart tbody,
  body.theme-modern .statistics-chart tr,
  body.theme-modern .statistics-chart td {
    display: block;
    width: 100%;
  }

  body.theme-modern #statisticsForm\:chart {
    max-width: 100%;
    height: auto;
  }

  /* Result tables can still legitimately need horizontal scrolling (up to
     7 columns on the tickets table) - scroll rather than let them overflow
     the page. display:block directly on <table> was the wrong way to get
     that: it turns the <table> into a plain block box (which does stretch
     to 100%), but its <tr>/<td> children keep their own table-row/
     table-cell display, so the browser fabricates an anonymous inner table
     to still lay them out - that anonymous table sizes itself to its
     content (shrink-to-fit), not to the outer, now-blockified <table> box,
     which is exactly why the rows ended up narrower than the table.
     statistics.xhtml now wraps each <e:dataTable> in a plain
     .statistics-table-wrapper div - the wrapper scrolls, the table itself
     stays a real, un-blockified table (default display:table) so
     width:100% applies to the actual table formatting context and rows
     genuinely span it. */
  body.theme-modern .statistics-table-wrapper {
    max-width: 100%;
    overflow-x: auto;
  }

  body.theme-modern .statistics-table {
    width: 100%;
  }

  /* "Plus longs temps de réaction"/"résolution" (STATISTICS_FILTER.
     CHARGE_TICKETS/CLOSURE_TICKETS, both rendered by this same #ticketsData
     table, just with a different last column) - 7 real columns plus a
     hidden action-button one is too much even scrolled horizontally, so
     this reflows each row into a card with 2 fields per line instead. */
  body.theme-modern #statisticsForm\:ticketsData thead {
    display: none;
  }

  body.theme-modern #statisticsForm\:ticketsData,
  body.theme-modern #statisticsForm\:ticketsData tbody {
    display: block;
    width: 100%;
  }

  body.theme-modern #statisticsForm\:ticketsData tbody tr {
    display: flex;
    flex-wrap: wrap;
    border: 1px solid var(--modern-border);
    border-radius: var(--modern-radius);
    margin-bottom: 12px;
    padding: 8px;
    box-shadow: var(--modern-shadow);
  }

  /* Last column only ever holds a hidden (style="display:none") "view"
     command button, triggered by the row's own onclick - never has visible
     content, so it'd otherwise leave an empty half-line in the card. */
  body.theme-modern #statisticsForm\:ticketsData tbody td:last-child {
    display: none;
  }

  body.theme-modern #statisticsForm\:ticketsData tbody td {
    display: block;
    flex: 1 1 50%;
    width: auto !important;
    box-sizing: border-box;
    padding: 4px 6px;
    font-size: 13px;
    border: none !important;
  }
}

/* ==========================================================================
   Services page (departments.xhtml)
   ==========================================================================
   Shares the .dashboard table class with the ticket control panel, but its
   own column layout (label/label long/enabled/redirection/move-arrows) is
   much simpler - no card conversion needed, just keep it a real table that
   scrolls horizontally instead of overflowing the viewport, and hide the
   least essential column (label long) to leave more room for the rest. */

/* The last column only ever holds a hidden (style="display:none") command
   button used by the row's own onclick (selectDepartment(index) in
   departments.xhtml) to submit which row was clicked - it can't be removed
   from the markup (the button has to exist somewhere in the row for that
   click-to-select-row pattern to work), but it never has any visible
   content, so the empty header/cell can just be collapsed away here.
   Desktop and mobile both, :last-child rather than nth-child since this
   column's position shifts (5th vs 9th) depending on whether the current
   user can reorder services (4 extra move-arrow columns). */
body.theme-modern .departments .dashboard thead tr:nth-child(2) th:last-child,
body.theme-modern .departments .dashboard tbody td:last-child {
  display: none;
}

@media (max-width: 900px) {
  body.theme-modern .departments-table-wrapper {
    max-width: 100%;
    overflow-x: auto;
  }

  body.theme-modern .departments .dashboard thead tr:nth-child(2) th:nth-child(2),
  body.theme-modern .departments .dashboard tbody td:nth-child(2) {
    display: none;
  }

  /* Desktop's .dashboard-header-wrapper (custom.css) is a 3-column grid
     (count / filter / page-size+sort+reorder) - too cramped side by side
     on a phone, so this stacks them instead: filter first, then sort
     (bundled with page size + the reorder button, already one visual
     block in the markup), then the item count last, right above the
     table. */
  body.theme-modern .departments .dashboard-header-wrapper {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
  }

  body.theme-modern .departments .dashboard-header-wrapper .department-filter {
    order: 1;
  }

  body.theme-modern .departments .dashboard-header-wrapper .items-sort {
    order: 2;
  }

  body.theme-modern .departments .dashboard-header-wrapper .items-count {
    order: 3;
    text-align: left;
  }

  /* Filter row (label, then input + "Appliquer" together) - the label
     ("Filtrer la liste des services :") and the input/button pair are two
     separate .form-item wrappers in the markup (label+input in the first,
     button+cancel-button in the second), but the wanted layout cuts across
     that grouping: label alone on its own line, then input+button together
     on the next one. display:contents unwraps both .form-item boxes so
     their children become direct items of this flex row, and the label's
     flex-basis:100% forces everything after it to wrap onto a new line. */
  body.theme-modern .departments .department-filter .block.form-block {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
  }

  body.theme-modern .departments .department-filter .form-item {
    display: contents;
  }

  body.theme-modern .departments .department-filter label {
    flex-basis: 100%;
    white-space: normal;
  }

  body.theme-modern .departments .department-filter input[type='text'] {
    flex: 1 1 auto;
    min-width: 0;
  }

  /* Table still didn't fit - shrink its text too, on top of the
     label-long column already being hidden above. */
  body.theme-modern .departments .dashboard thead th,
  body.theme-modern .departments .dashboard tbody td {
    font-size: 13px;
  }

  /* Still slightly too wide - the 4 move (first/up/down/last) columns are
     icon-only, no need for as much padding/width as a text column. */
  body.theme-modern .departments .dashboard th.move,
  body.theme-modern .departments .dashboard td.move {
    width: 22px;
    padding-left: 2px;
    padding-right: 2px;
  }
}

/* ==========================================================================
   Preferences pages (preferences.xhtml, managerPreferences.xhtml)
   ==========================================================================
   Both share styleClass="page-wrapper preferences". */
body.theme-modern .preferences fieldset legend {
  font-size: calc(1.3em + 1px);
}

@media (max-width: 900px) {
  /* form.style.css's ".form-item label" is 1.15rem - fine on desktop, a
     bit large next to the (also enlarged) 1.3em legend on a narrow phone
     screen. */
  body.theme-modern .preferences .form-item label {
    font-size: 1em;
  }
}

/* ==========================================================================
   FAQ tree/content 3-column layouts (faqs.xhtml, and department/category FAQ
   link editing - _departmentViewFaqLinks.xhtml, categoryFaqLinks.xhtml)
   ==========================================================================
   The tree/content 3-column layout (columns="3": tree | spacer | content)
   uses an old-school invisible-spacer-image trick to force the tree
   column's minimum width - <h:graphicImage value="trans.png" height="1"
   width="400" /> - a literal HTML width="400" attribute, wider than most
   phones on its own, which is what was pushing the whole page wider than
   its header/footer. Purely a spacing hack (1px tall, invisible), so just
   drop it on mobile - the content still lays out fine as a stack. Not
   page-scoped: any current or future occurrence of this exact hack should
   get the same treatment. */
@media (max-width: 900px) {
  body.theme-modern img[width="400"] {
    display: none;
  }
}

/* ==========================================================================
   Search page (search.xhtml)
   ==========================================================================
   Advanced search's field groups (tokens / manager-owner-user / date-type-
   department-sort) are laid out side by side via layout.style.css's
   ".advancedSearch-filter { display:flex; flex-wrap:wrap; }", and its text
   inputs use a fixed HTML size="50" attribute - together too wide for a
   phone screen, pushing the page wider than its header/footer. Stack the
   groups (each already a vertical stack of .form-item internally) and cap
   input/select widths to the container instead. */

/* Fixed mobile result card (see search.xhtml's search-mobile-card h:column)
   - hidden everywhere by default, same reasoning as controlPanel.xhtml's
   .mobile-summary-column above: exists in the DOM under the classic theme
   too, which never shows it. */
.search-mobile-card {
  display: none !important;
}

@media (max-width: 900px) {
  body.theme-modern .advancedSearch-filter {
    display: block;
  }

  body.theme-modern .advancedSearch-filter .form-item {
    margin-right: 0;
  }

  body.theme-modern .advancedSearch-filter .form-item input[type='text'],
  body.theme-modern .advancedSearch-filter .form-item input[type='date'],
  body.theme-modern .advancedSearch-filter .form-item select {
    box-sizing: border-box;
    max-width: 100%;
  }

  /* Card layout instead of a many-column table: the real columns (score,
     date, title, department, owner, manager - 6, plus the row-click and
     hidden-action-button columns either side) don't fit a phone width even
     scrolled, so hide the per-column header row and every real column, and
     show only search-mobile-card below instead. */
  body.theme-modern #searchForm .dashboard thead tr:nth-child(2) {
    display: none;
  }

  body.theme-modern #searchForm .dashboard tbody td:not(.search-mobile-card) {
    display: none !important;
  }

  body.theme-modern #searchForm .dashboard tbody tr {
    display: block;
    border: 1px solid var(--modern-border);
    border-radius: var(--modern-radius);
    margin-bottom: 12px;
    box-shadow: var(--modern-shadow);
  }

  /* Safety net in case some content still doesn't fit - scrolls inside the
     wrapper instead of stretching the page. */
  body.theme-modern .search-table-wrapper {
    max-width: 100%;
    overflow-x: auto;
  }

  body.theme-modern #searchForm .dashboard tbody td.search-mobile-card {
    display: block !important;
    width: 100% !important;
    box-sizing: border-box;
    border-top: none !important;
    padding: 8px;
  }

  body.theme-modern .search-mobile-card-row1,
  body.theme-modern .search-mobile-card-row3 {
    display: flex;
    justify-content: space-between;
    gap: 8px;
    font-size: 0.85em;
    color: var(--modern-text-muted);
  }

  body.theme-modern .search-mobile-card-row1 {
    margin-bottom: 4px;
  }

  body.theme-modern .search-mobile-card-row2 {
    font-size: 1.05em;
    font-weight: 600;
    color: var(--modern-text);
    margin-bottom: 4px;
  }

  body.theme-modern .search-mobile-card-row3 {
    margin-top: 4px;
  }
}

/* ==========================================================================
   Ticket view page (ticketView.xhtml)
   ==========================================================================
   First pass: eliminate horizontal overflow only (the fixed-pixel column
   layouts below stack to full width instead) - content/spacing inside each
   stacked block is intentionally left as-is for now, to be revisited
   separately. */
@media (max-width: 900px) {
  /* Set once the header sticks to the top of the viewport on scroll (see
     custom.js) - assumes a 300px classic-theme sidebar next to it that
     doesn't exist as a fixed column on mobile (the nav is the collapsible
     hamburger menu instead - see .nav-toggle elsewhere in this file). */
  body.theme-modern .view-ticket_header.sticky.fixed .region-inner > div:nth-child(1) {
    margin-left: 0;
  }

  /* .region-inner (display:flex, no wrap) tries to fit both the title/
     actions/return block AND the ticket-navigation block (4 icon buttons +
     the "go to ticket" text input + button, navigation.style.css - easily
     300px+ on its own) side by side on one unbreakable line - let it wrap
     the nav block onto its own line below instead. */
  body.theme-modern .view-ticket_header .region-inner {
    flex-wrap: wrap;
  }

  body.theme-modern .tickets-navigation {
    flex-wrap: wrap;
  }

  body.theme-modern .form-goto-ticket {
    flex-wrap: wrap;
  }

  body.theme-modern .form-goto-ticket .form-item:first-of-type {
    margin: 4px 8px 4px 0;
  }

  /* Ticket property rows (status/category/owner/manager/scope/see-also):
     label (120px) + value (300px) + actions (min-width 110px) + info
     (max-width 50%) never fit a phone width side by side. */
  body.theme-modern .ticket-property-inner {
    flex-direction: column;
  }

  body.theme-modern .ticket-property-inner > div:nth-child(1),
  body.theme-modern .ticket-property-inner > div:nth-child(2),
  body.theme-modern .ticket-property-inner > div:nth-child(3),
  body.theme-modern .ticket-property-inner > div:nth-child(4) {
    flex-basis: auto;
    min-width: 0;
    max-width: 100%;
    width: 100%;
    padding: 2px 0;
  }

  /* Extended properties tab (scope/priority/computer/spent time/origin/
     dates): same fixed-150px-label-column issue. */
  body.theme-modern .view-ticket_secondary_properties .form-block {
    flex-direction: column;
  }

  body.theme-modern .view-ticket_secondary_properties .form-block > div:first-of-type {
    width: 100%;
  }

  body.theme-modern .view-ticket_secondary_properties .ticket-property_edit-inner {
    flex-wrap: wrap;
  }

  /* History tab: date (fixed 150px) next to the action content. */
  body.theme-modern .view-ticket_history .action--header {
    flex-direction: column;
  }

  body.theme-modern .view-ticket_history .action--date {
    width: 100%;
  }

  /* Files/invitations tables: columns are already percentage-based, but
     long unbroken filenames/labels could still force the table wider than
     the viewport - scroll horizontally inside the table itself rather than
     the page, same technique as the search results table above. */
  body.theme-modern .view-ticket_files .files_array,
  body.theme-modern .view-ticket_invitations .invite_array {
    display: block;
    overflow-x: auto;
  }

  /* Tabs row (Historique/Propriétés/Fichiers attachés/Invités/Surveillance/
     Derniers tickets du propriétaire - 6 inline-block items) fights a
     float:right sibling (.button-right, the "+/- d'options" toggle,
     custom.css) for the same line - the float doesn't wrap away on a phone
     width, it just forces the tabs into an ever-narrower remaining strip.
     Un-float it to its own line below instead, and let the tabs wrap. */
  body.theme-modern .view-ticket_extended-properties .button-right {
    float: none;
    margin-top: 4px;
  }

  body.theme-modern ul.tabs li {
    overflow-wrap: break-word;
  }

  /* Safety net: guarantee no page-level horizontal scrollbar even if some
     inner element (further content pass to come) still doesn't fully fit -
     scrolls/clips inside the page instead of stretching header/footer. */
  body.theme-modern .ticketView .content {
    overflow-x: hidden;
  }
}

/* ==========================================================================
   Department properties/categories edit pages
   ==========================================================================
   Reached from departmentView.xhtml's Propriétés/Catégories tabs ("Modifier
   les propriétés" navigates to departmentEdit.xhtml, "Voir/modifier les
   catégories" to categories.xhtml). */
@media (max-width: 900px) {
  /* departmentEdit.xhtml has a couple of HTML size="80" text inputs
     (xlabel, monitoring email) - same fixed-character-width overflow
     source as advancedSearch-filter's size="50" ones above. */
  body.theme-modern .departmenEdit .form-item input[type='text'] {
    box-sizing: border-box;
    max-width: 100%;
  }

  /* categories.xhtml's "Retour au service" button sits next to the
     actions dropdown in a "display:flex" row (layout.style.css's
     ".categories .form-header") with no wrap, sized for desktop. */
  body.theme-modern .categories .form-header {
    flex-wrap: wrap;
    row-gap: 8px;
  }

  /* categories.xhtml's tree (myfaces tomahawk tree2, e:tree2) - each node
     is a table row with several inline action icons (edit/delete/move/add
     child), too much to reflow into a card without restructuring the
     component itself, so this scrolls horizontally inside its own box
     instead of overflowing the page - same technique as the search/
     departments results tables above. */
  body.theme-modern .categories .treeview {
    max-width: 100%;
    overflow-x: auto;
  }
}

/* ==========================================================================
   Bookmarks/history page (bookmarks.xhtml)
   ==========================================================================
   Historique tab's table (n°/Sujet/Service/Propriétaire/Gestionnaire) was
   plain HTML width:100% with no responsive handling: a bare "#tab-tickets-
   history" id selector doesn't match here (JSF prefixes the rendered id
   with the form's, e.g. "bookmarksForm:tab-tickets-history" - same trap as
   custom.js's tab-switching code, which is why that one uses an
   ends-with attribute selector instead of a bare #id). Targets the
   dataTable's own styleClass (bookmarks.xhtml's "mobile-card-table") instead,
   and reuses the same table -> stacked cards recipe as the ticket dashboard
   above (see .mobile-field-label) instead of just scrolling horizontally. */
@media (max-width: 900px) {
  body.theme-modern .bookmarks .mobile-card-table,
  body.theme-modern .bookmarks .mobile-card-table tbody {
    display: block;
    width: 100% !important;
  }

  body.theme-modern .bookmarks .mobile-card-table thead {
    display: none;
  }

  body.theme-modern .bookmarks .mobile-card-table tbody tr {
    display: flex;
    flex-wrap: wrap;
    border: 1px solid var(--modern-border);
    border-radius: var(--modern-radius);
    margin-bottom: 12px;
    padding: 0;
    box-shadow: var(--modern-shadow);
  }

  body.theme-modern .bookmarks .mobile-card-table tbody td {
    flex: 1 1 45%;
    min-width: 130px;
    width: auto !important;
    border-top: none !important;
    padding: 4px 6px 4px 0;
  }
}

/* ==========================================================================
   Invite a person - LDAP search (ticketInvite.xhtml, ldapResults.xhtml,
   ticketAssign.xhtml)
   ========================================================================== */
@media (max-width: 900px) {
  /* ticketInvite.xhtml's "search LDAP id" row: a plain flex row (input +
     search button) with no wrap, sized for desktop. */
  body.theme-modern .ticketInvite .ldap-search-form {
    flex-wrap: wrap;
  }

  body.theme-modern .ticketInvite .ldap-search-form input {
    width: 100%;
    box-sizing: border-box;
  }

  /* ldapResults.xhtml/ticketAssign.xhtml: each search result row (photo/
     name/status/composante/service + "Sélectionner" button,
     layout.style.css's .formated-user-data) is "display:flex;
     justify-content:space-between" with no wrap - forced the whole row
     (and page) wider than the screen instead of stacking the button under
     the user's details. Unscoped (not under a single page-wrapper class):
     the same .formated-user-data/.search-result-data pair is reused by
     both the "invite" and "assign manager" LDAP search screens. */
  body.theme-modern .formated-user-data {
    flex-wrap: wrap;
    row-gap: 8px;
  }

  body.theme-modern .search-result-data {
    width: 100% !important;
    table-layout: fixed;
  }
}

/* ==========================================================================
   FAQ edit mode (faqs.xhtml: _faq.xhtml, _faqSubFaqs.xhtml)
   ========================================================================== */
@media (max-width: 900px) {
  /* _faq.xhtml's faqTitle: same fixed-character-width (size="80") overflow
     source as departmentEdit's xlabel/monitoringEmail above. */
  body.theme-modern .faqView .faq-content input[type='text'] {
    box-sizing: border-box;
    max-width: 100%;
  }

  /* _faq.xhtml's Modifier/Déplacer/Supprimer row and _faqSubFaqs.xhtml's
     sub-FAQ-list/"Ajouter une question" row are both e:panelGrid (a plain
     HTML <table>) with 2-3 side-by-side columns and no responsive handling
     - stack them into a single column instead of leaving them side by
     side. _faq.xhtml's grid has no styleClass of its own but is the only
     table under .faq-content; _faqSubFaqs.xhtml's got one added
     (sub-faqs-grid) since it sits outside .faq-content, next to the FAQ
     tree's own (unrelated, must stay untouched) tables. */
  body.theme-modern .faqView .faq-content table,
  body.theme-modern .faqView .faq-content table tbody,
  body.theme-modern .faqView .faq-content table tr,
  body.theme-modern .faqView .faq-content table td,
  body.theme-modern .faqView .sub-faqs-grid,
  body.theme-modern .faqView .sub-faqs-grid tbody,
  body.theme-modern .faqView .sub-faqs-grid tr,
  body.theme-modern .faqView .sub-faqs-grid td {
    display: block;
    width: 100% !important;
  }

  /* columnClasses="colLeftNowrap,colCenterNowrap,..." (_faq.xhtml) and
     "colLeft,colRight" (_faqSubFaqs.xhtml) still apply commons.css's
     text-align:center/right and, worse, white-space:nowrap - display:block
     above doesn't touch either. Left-aligned/wrapping text was rendering
     pinned to the right edge with empty space to its left (text-align），
     and on top of that a couple of these (icon + several words each) were
     wide enough under nowrap to force the actual horizontal overflow this
     was all meant to fix in the first place. */
  body.theme-modern .faqView .faq-content table td,
  body.theme-modern .faqView .sub-faqs-grid td {
    text-align: left !important;
    white-space: normal !important;
  }

  /* The real culprit: faqs.xhtml (and _departmentViewFaqLinks.xhtml, used
     from departmentView.xhtml/departmentFaqLinks.xhtml) wrap the FAQ tree,
     a 20px spacer and the content in an outer 3-column e:panelGrid
     (columns="3", styleClass added: "faq-layout-grid") laid out side by
     side - tree | spacer | content, none of the fixes above reach outside
     of the content column, so this outer row was still forcing the whole
     page wider than the screen regardless. Stack tree above content
     instead, and drop the spacer column entirely (it's a fixed 20px gap,
     meaningless once no longer side by side). Unscoped (not under
     .faqView): reused by two other page-wrapper classes with the exact
     same layout bug. */
  body.theme-modern .faq-layout-grid,
  body.theme-modern .faq-layout-grid tbody,
  body.theme-modern .faq-layout-grid tr,
  body.theme-modern .faq-layout-grid td {
    display: block;
    width: 100% !important;
  }

  body.theme-modern .faq-layout-grid td.colCenter {
    display: none;
  }

  /* h:graphicImage's own indentation-reserving spacer (400px wide,
     1px-tall transparent trans.png) - harmless side by side with the tree
     it precedes, but as wide as the whole tree column once stacked above
     it by the "display:block" rule just above. */
  body.theme-modern .faq-tree-spacer {
    max-width: 100%;
    height: auto;
  }

  /* The actual root cause of the editor (and everything else on this page)
     still being too wide despite every width:100%/min-width:0 above:
     faqs.xhtml wraps the whole tree+editor block in a <fieldset>, and
     <fieldset> has its own browser-default min-width based on its content
     (historically "min-content" in Chrome/Firefox) that a flex ancestor's
     min-width:0 (.content, above) does not override - fieldsets opt out of
     shrinking below their content's natural width unless told to
     explicitly, regardless of anything set on their children. */
  body.theme-modern .faqView fieldset {
    min-width: 0;
  }
}

/* ==========================================================================
   Ticket creation - category filter (ticketAdd.xhtml, "étape 1")
   ========================================================================== */
@media (max-width: 900px) {
  /* layout.style.css's ".category-filter .form-block { display:flex;
     align-items:center }" put the label, the input and the Appliquer
     button all on one unwrapped row - stack the label's row above the
     input+button row instead (the two form-item children of .form-block:
     the label alone, then the input+button together - see ticketAdd.xhtml). */
  body.theme-modern .category-filter .form-block {
    flex-direction: column;
    align-items: stretch;
  }

  body.theme-modern .category-filter .form-block .form-item {
    display: flex;
    flex-wrap: wrap;
    /* form.style.css's ".form-item input[type='submit'] { margin: 7px }"
       offsets the button vertically within the row (the text input has no
       such margin) - align-items:stretch (the flex default) doesn't cancel
       that out, since each item's own margin still shifts it within the
       line. */
    align-items: center;
    margin-bottom: 8px;
  }

  body.theme-modern .category-filter .form-block .form-item:last-child {
    margin-bottom: 0;
  }

  body.theme-modern .category-filter .form-block input[type='text'] {
    flex: 1 1 auto;
    min-width: 0;
  }
}

/* ==========================================================================
   Home page tiles (welcome.xhtml)
   ==========================================================================
   layout.style.css's own flex-wrap already stops the tiles from
   overflowing, but "flex-basis: 20%" still fits 2+ tiny tiles per row
   instead of wrapping (wrap only triggers once items don't fit at their
   basis size, and a % basis always "fits" by shrinking) - one full-width
   tile per row instead. */
@media (max-width: 900px) {
  body.theme-modern .welcome .content .region:nth-child(1) .region-inner .block {
    flex-basis: 100%;
  }
}

/* ==========================================================================
   Department visibility (departmentSelection.xhtml)
   ==========================================================================
   Main 2-column e:panelGrid (columns="2", styleClass added: "dept-sel-grid")
   - tree/rules on the left, the operations sidebar (download/edit/upload
     config) on the right - side by side with no responsive handling at all.
   Stack them instead. */
@media (max-width: 900px) {
  body.theme-modern .dept-sel-grid,
  body.theme-modern .dept-sel-grid tbody,
  body.theme-modern .dept-sel-grid tr,
  body.theme-modern .dept-sel-grid td {
    display: block;
    width: 100% !important;
  }
}

/* ==========================================================================
   +1px desktop font-size bump, part 2: classic-theme-origin declarations
   ==========================================================================
   Part 1 (above) bumped modern-theme.css's own already-customized font-sizes.
   This part covers every remaining font-size that modern theme pages still
   inherit unmodified from the shared/classic-origin stylesheets (commons.css,
   custom.css, base.style.css, layout.style.css, form.style.css,
   treeview.style.css, navigation.style.css, calendar.style.css,
   nice-select.css - all loaded on every page regardless of theme, see
   tags.xml). Buttons (input[type=submit], .button--*, the file-input's
   ::file-selector-button) are deliberately excluded - text and form controls
   (labels, inputs, selects/.nice-select/option) only. Desktop-only, same as
   part 1: wrapped in @media (min-width: 901px) so the mobile value (which
   these shared files don't otherwise vary by width) is left untouched.
   A few selectors from those files are skipped here on purpose because
   modern-theme.css already overrides them elsewhere (already bumped as part
   of part 1, or superseded by the ".content table tbody td/span"
   normalization rule above - every page wraps its content in
   <main class="content">, so any "<section> table tbody td/span" rule from
   a shared file - bookmarks/ticketView/administrators/departmentFaqLinks/
   faqView's own table text rules - never actually applies under modern
   theme in the first place). */
@media (min-width: 901px) {
  /* commons.css */
  body.theme-modern .form-submit-popup {
    font-size: 25px;
  }

  /* custom.css */
  body.theme-modern .faq h2 .faq-header {
    font-size: calc(0.8em + 1px);
  }
  body.theme-modern .dashboard.edit-columns .move-columns .fas {
    font-size: calc(2.1em + 1px);
  }
  body.theme-modern .dashboard .column-header .portlet-section-text {
    font-size: calc(1.1em + 1px);
  }
  body.theme-modern .dashboard .column-header .fa-sort-amount-up,
  body.theme-modern .dashboard .column-header .fa-sort-amount-down,
  body.theme-modern .dashboard .column-header .fa-sort-alpha-up,
  body.theme-modern .dashboard .column-header .fa-sort-alpha-down,
  body.theme-modern .dashboard .column-header .fa-sort-numeric-up,
  body.theme-modern .dashboard .column-header .fa-sort-numeric-down {
    font-size: calc(1.3em + 1px);
  }
  body.theme-modern .dashboard .column-header .fa-sort {
    font-size: calc(1.6em + 1px);
  }
  body.theme-modern .dashboard-toggle .fa-toggle-on,
  body.theme-modern .dashboard-toggle .fa-toggle-off {
    font-size: calc(2.2em + 1px);
  }
  body.theme-modern .dashboard-search-input {
    font-size: calc(1rem + 1px);
  }
  body.theme-modern .dashboard-paginator .fas {
    font-size: calc(2.2em + 1px);
  }
  body.theme-modern .dashboard-paginator a {
    font-size: calc(1.3em + 1px);
  }
  body.theme-modern .dashboard-paginator .current-page {
    font-size: calc(1.3em + 1px);
  }
  body.theme-modern .dept-sel-icon {
    font-size: calc(1.1em + 1px);
  }
  body.theme-modern .app-icon {
    font-size: calc(1.1em + 1px);
  }

  /* base.style.css */
  body.theme-modern .content h1,
  body.theme-modern .content h1 > span {
    font-size: calc(1.6rem + 1px);
  }
  body.theme-modern .content h2,
  body.theme-modern .content h2 > span {
    font-size: calc(1.45rem + 1px);
  }
  body.theme-modern .far.fa-2x,
  body.theme-modern .fas.fa-2x {
    font-size: calc(1.8rem + 1px);
  }
  body.theme-modern .message .portlet-msg-error,
  body.theme-modern .message .portlet-msg-info,
  body.theme-modern .message .portlet-msg-alert {
    font-size: calc(1.2em + 1px);
  }
  body.theme-modern .tooltip .tooltiptext {
    font-size: calc(0.85rem + 1px);
  }

  /* layout.style.css */
  body.theme-modern .header .region .ent-link a,
  body.theme-modern .header .region .logout-link a {
    font-size: calc(1.4em + 1px);
  }
  body.theme-modern .header .region .dark-mode-toggle .fas,
  body.theme-modern .header .region .logout-link .fas {
    font-size: calc(1.4em + 1px);
  }
  body.theme-modern .block--card .fas {
    font-size: calc(4em + 1px);
  }
  body.theme-modern .block.accordion h2 {
    font-size: calc(1rem + 1px);
  }
  body.theme-modern .block.accordion h2 > span {
    font-size: calc(1.2rem + 1px);
  }
  body.theme-modern .block.accordion h2 .fas {
    font-size: calc(1.2rem + 1px);
  }
  body.theme-modern .bookmarks .tab-link.history .fa-cog {
    font-size: calc(1.2rem + 1px);
  }
  body.theme-modern .view-ticket_title h1 {
    font-size: calc(1.45rem + 1px);
  }
  body.theme-modern .view-ticket_history .action--header {
    font-size: calc(0.95rem + 1px);
  }
  body.theme-modern .history-container .action-alert-recipient {
    font-size: calc(1em + 1px);
  }
  body.theme-modern .view-ticket_files .files_array .column.file-name {
    font-size: calc(1.2rem + 1px);
  }
  body.theme-modern .view-ticket_files .files_array .column.file-info {
    font-size: calc(0.95rem + 1px);
  }
  body.theme-modern .user--display-name {
    font-size: calc(1.2rem + 1px);
  }
  body.theme-modern .service-preference .form-block h2 {
    font-size: calc(1.3rem + 1px);
  }
  body.theme-modern .ticketAssign .selected-manager {
    font-size: calc(1.15rem + 1px);
  }
  body.theme-modern .departments .dashboard .move .fas {
    font-size: calc(1.2rem + 1px);
  }
  body.theme-modern .categories .treeview .properties .portlet-section-text {
    font-size: calc(0.95rem + 1px);
  }
  body.theme-modern .categories .treeview .category .no-members {
    font-size: calc(0.95rem + 1px);
  }
  body.theme-modern .categories .virtualCategories-list,
  body.theme-modern .categories .members-list {
    font-size: calc(1em + 1px);
  }

  /* form.style.css */
  body.theme-modern .form-item label {
    font-size: calc(1.15rem + 1px);
  }
  body.theme-modern .form-item .input-checkbox input[type="checkbox"] + .checkbox:before {
    font-size: calc(1.5rem + 1px);
  }
  body.theme-modern .form-item input[type='text'],
  body.theme-modern .form-item input[type='password'] {
    font-size: calc(1rem + 1px);
  }
  body.theme-modern .form-item input[type='file'] {
    font-size: calc(1rem + 1px);
  }
  body.theme-modern .form-select.scrollable select option {
    font-size: calc(1.1rem + 1px);
  }
  body.theme-modern .form-item select {
    font-size: calc(1rem + 1px);
  }
  body.theme-modern .form-category .category-lib {
    font-size: calc(1.15rem + 1px);
  }
  body.theme-modern .form-select .actions-header-inner span {
    font-size: calc(0.9rem + 1px);
  }

  /* treeview.style.css */
  body.theme-modern .department.leaf {
    font-size: calc(1.12rem + 1px);
  }
  body.theme-modern .category.leaf {
    font-size: calc(1.15em + 1px);
  }
  body.theme-modern .extra-cell {
    font-size: calc(1.8em + 1px);
  }
  body.theme-modern .treeview .form-item table span {
    font-size: calc(1.1rem + 1px);
  }

  /* navigation.style.css */
  body.theme-modern .navigation .portlet-menu .moreItems {
    font-size: calc(2em + 1px);
  }
  body.theme-modern .tickets-navigation .nav-item i {
    font-size: calc(2.2rem + 1px);
  }

  /* calendar.style.css */
  body.theme-modern .calendar {
    font-size: calc(1rem + 1px);
  }

  /* nice-select.css */
  body.theme-modern .nice-select {
    font-size: 17px;
  }
  body.theme-modern .nice-select.small {
    font-size: 13px;
  }
}
