/*
 * CoachLOGIC brand styling over Liferay's Classic theme.
 *
 * WHY CSS RATHER THAN A FORKED THEME. Classic keeps working — its markup,
 * its updates, its accessibility work — and this changes how it looks. A
 * forked theme would own all of that forever in exchange for a palette.
 *
 * WHAT THIS DOES AND DOES NOT REACH. Everything Liferay renders: the header,
 * the navigation, the page ground, type. It does NOT restyle the CoachLOGIC
 * widgets, which carry their own inline styles and are already built to this
 * palette. That is a deliberate boundary rather than an oversight: the widgets
 * are React components whose styling belongs with them, and overriding inline
 * styles from here would mean !important on every rule.
 *
 * Every colour is sampled from www.coachlogic.ai, so the application and the
 * marketing site are the same brand rather than two interpretations of it.
 */

:root {
  --cl-rail: #12202E;
  --cl-rail-deep: #0D141A;
  --cl-rail-line: #1E3247;
  --cl-rail-text: #DCE4EC;
  --cl-rail-dim: #93A7BB;
  /* Section labels sit quieter than the links they head, but still readable. */
  --cl-rail-label: #8DA2B8;

  --cl-ground: #F5F7F9;
  --cl-card: #FFFFFF;
  --cl-ink: #16212E;
  --cl-ink-soft: #5A6C7E;
  --cl-line: #E2E8ED;

  /* The legacy signature. Gold reads as performance and award, and keeps this
     from looking like every other blue analytics product. */
  --cl-accent: #C9A227;
  --cl-accent-soft: #F5EBCB;
  --cl-accent-ink: #7A6113;

  --cl-steel: #295680;
  --cl-good: #1B7F5C;
  --cl-warn: #B45309;
  --cl-weak: #B91C1C;

  /*
   * THE BANDED SURFACES: a message with a coloured edge and a tinted ground.
   * These were hardcoded in the widgets — #fca5a5 seventeen times, #fcd34d
   * thirteen — which is the drift the variables exist to stop, and the reason
   * a dark palette was impossible: a pale red stays pale red on a dark ground
   * and reads as a highlighter pen.
   */
  --cl-danger-line: #FCA5A5;
  --cl-good-line:   #86EFAC;
  --cl-warn-line:   #FCD34D;
  --cl-warn-wash:   #FFFBEB;
  --cl-danger-wash: #FEF2F2;

  /* A chip, a pill, a quiet inset panel. */
  --cl-chip: #F1F3F5;

  /* Text on a control that is not the primary one. */
  --cl-control-ink: #374151;

  /* Switched off, and unavailable. */
  --cl-disabled: #D1D5DB;

  /* White where white is the point — a card on navy, a logo backing. */
  --cl-on-dark: #FFFFFF;

  --cl-font: "DM Sans", "Avenir Next", "Segoe UI", system-ui, -apple-system, sans-serif;

  /* Clay's own variables, so components that read them come along too. */
  --brand-color-1: #C9A227;
  --brand-color-2: #12202E;
}

body,
.portlet-body,
.control-menu {
  font-family: var(--cl-font);
  color: var(--cl-ink);
}

/* The page ground. Light on purpose: legacy put data on a dark textured
   background, which is striking on a welcome screen and works against you on a
   forty-row ranking table. The chrome carries the brand; the numbers stay
   readable. */
body {
  background: var(--cl-ground);
}

/* ── Header ─────────────────────────────────────────────────────────────── */

.cadmin.navbar,
#banner,
.control-menu-container {
  background: var(--cl-rail);
  border-bottom: 1px solid var(--cl-rail-line);
}

#banner .site-title,
#banner .site-title a,
.control-menu a,
.control-menu .lexicon-icon {
  color: #fff;
}

/* ── Navigation ─────────────────────────────────────────────────────────── */

.navbar-site,
.site-navigation,
nav.navbar {
  background: var(--cl-rail);
  border: 0;
}

.navbar-site .nav-link,
.site-navigation .nav-link,
nav.navbar .nav-link {
  color: var(--cl-rail-text);
  font-size: 14px;
  letter-spacing: 0.01em;
}

.navbar-site .nav-link:hover,
.site-navigation .nav-link:hover,
nav.navbar .nav-link:hover {
  color: #fff;
  background: #182A3B;
}

/* The active page. A left border rather than a background so the marker
   survives on both the rail and a horizontal bar. */
.navbar-site .nav-item.active .nav-link,
.site-navigation .nav-item.active .nav-link,
nav.navbar .nav-item.selected .nav-link {
  color: #fff;
  background: #182A3B;
  box-shadow: inset 3px 0 0 var(--cl-accent);
  font-weight: 500;
}

/*
 * DMAIC section headings.
 *
 * A submenu item with no page behind it is a section label, and Liferay renders
 * it as an ordinary disabled link. Uppercase, letterspaced, with a rule running
 * off it — the structural signature the legacy application was recognised by.
 */
.site-navigation .nav-item > .nav-link[href="#"],
.site-navigation .dropdown-header,
nav.navbar .dropdown-header {
  color: var(--cl-rail-dim);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  display: flex;
  align-items: center;
  gap: 8px;
  pointer-events: none;
}

.site-navigation .nav-item > .nav-link[href="#"]::after,
.site-navigation .dropdown-header::after,
nav.navbar .dropdown-header::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--cl-rail-line);
}

/* ── Content ────────────────────────────────────────────────────────────── */

.portlet-decorate .portlet-content {
  background: var(--cl-card);
  border: 1px solid var(--cl-line);
  border-radius: 10px;
}

h1, h2, h3 {
  font-weight: 500;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

a {
  color: var(--cl-steel);
}

a:hover {
  color: var(--cl-accent-ink);
}

/* Numbers that line up in columns should line up. */
td, .table td, .value, .metric {
  font-variant-numeric: tabular-nums;
}

/* Primary actions carry the accent; everything else stays quiet, so one gold
   button per view means something. */
.btn-primary {
  background: var(--cl-accent);
  border-color: var(--cl-accent);
  color: var(--cl-rail);
  font-weight: 500;
}

.btn-primary:hover,
.btn-primary:focus {
  background: #B8931F;
  border-color: #B8931F;
  color: var(--cl-rail);
}

/* Keyboard focus stays visible against both the rail and the light ground. */
a:focus-visible,
button:focus-visible,
.nav-link:focus-visible {
  outline: 2px solid var(--cl-accent);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ────────────────────────────────────────────────────────────────────────
 * The navigation bar.
 *
 * Horizontal, not a rail. A fixed 252px column takes that width from every
 * screen behind it, and the screens here are ranking tables and index grids
 * that want all of it — the navigation is glanced at and the data is read.
 *
 * Scoped to the portlet id. Earlier rules targeted .navbar-site and
 * nav.navbar, which this portlet is not, so the links kept Liferay's default
 * grey (#6B6C7E) — on navy, the same colour as the wall behind them, which is
 * why the DMAIC labels looked missing when they were there all along.
 * ──────────────────────────────────────────────────────────────────────── */

/*
 * The navy belongs to the BAR, not to the portlet inside it.
 *
 * The portlet is only as wide as its items — 447px of a 1265px wrapper — so
 * colouring the portlet left the bar stopping a third of the way across. The
 * background goes on the navbar itself and the portlet fills it.
 */
.navbar.navbar-classic,
.navbar-site {
  background: var(--cl-rail);
  border-bottom: 1px solid var(--cl-rail-line);
}

#portlet_com_liferay_site_navigation_menu_web_portlet_SiteNavigationMenuPortlet,
#portlet_com_liferay_site_navigation_menu_web_portlet_SiteNavigationMenuPortlet > .portlet-content,
#portlet_com_liferay_site_navigation_menu_web_portlet_SiteNavigationMenuPortlet .portlet-content-container {
  background: transparent;
  width: 100%;
}

#portlet_com_liferay_site_navigation_menu_web_portlet_SiteNavigationMenuPortlet .nav-link {
  color: var(--cl-rail-text);
  font-size: 15px;
  padding: 14px 18px;
  border-bottom: 3px solid transparent;
}

#portlet_com_liferay_site_navigation_menu_web_portlet_SiteNavigationMenuPortlet .nav-link:hover {
  color: #fff;
  background: #182A3B;
}

#portlet_com_liferay_site_navigation_menu_web_portlet_SiteNavigationMenuPortlet .nav-item.active > .nav-link,
#portlet_com_liferay_site_navigation_menu_web_portlet_SiteNavigationMenuPortlet .nav-item.selected > .nav-link {
  color: #fff;
  background: #182A3B;
  border-bottom-color: var(--cl-accent);
  font-weight: 500;
}

/* The DMAIC label: the structural signature the legacy application was
   recognised by. A section is not a link, so it does not behave like one. */
/*
 * Liferay puts .text-truncate on every nav link, which combined with a flex
 * child set to flex:1 collapsed each label to 39px — "Define" rendered as a
 * few clipped pixels and read as missing. The rule is displayed as a block
 * with truncation explicitly off, and the separator moved to a border on the
 * item rather than a flexing pseudo-element that competes for the width.
 */
#portlet_com_liferay_site_navigation_menu_web_portlet_SiteNavigationMenuPortlet .dropdown-toggle {
  color: var(--cl-rail-label);
  font-size: 12.5px;
  font-weight: 700;
  /* Eased from 0.16em: the same tracking that reads as deliberate at 10.5px
     sprawls once the type is large enough to carry the emphasis itself. */
  letter-spacing: 0.1em;
  text-transform: uppercase;
  overflow: visible;
  text-overflow: clip;
  white-space: nowrap;
}

/* Dropdown panels, which the bar needs and the rail did not. */
#portlet_com_liferay_site_navigation_menu_web_portlet_SiteNavigationMenuPortlet .dropdown-menu {
  background: var(--cl-rail);
  border: 1px solid var(--cl-rail-line);
  border-radius: 0 0 8px 8px;
  padding: 4px 0;
  min-width: 210px;

  /*
   * Clay caps every dropdown at max-height: 295px and scrolls the rest. An
   * item here is about 40px — 14.5px type at 1.5, plus 9px of padding above
   * and below — so seven fit and the eighth scrolls. Define reached eight the
   * moment Admin joined it, and a menu that scrolls to reveal one entry reads
   * as a broken menu rather than a full one.
   *
   * 420px is ten items with the panel's own padding, so there is room for two
   * more before this needs thinking about again. The cap is kept rather than
   * removed: a menu long enough to run off the bottom of a laptop screen is a
   * worse failure than a scrollbar, and it should scroll when it gets there.
   *
   * Scoped to the site navigation portlet — every other dropdown in Liferay,
   * including the admin menus, keeps Clay's own limit.
   */
  max-height: 420px;
  /* Clay pairs its cap with max-width: 230px, which clips a label like
     "Athlete Test Editor" before the panel is wide enough to hold it. */
  max-width: none;
}

#portlet_com_liferay_site_navigation_menu_web_portlet_SiteNavigationMenuPortlet .dropdown-menu .dropdown-item {
  color: var(--cl-rail-text);
  padding: 9px 18px;
  font-size: 14.5px;
}

#portlet_com_liferay_site_navigation_menu_web_portlet_SiteNavigationMenuPortlet .dropdown-menu .dropdown-item:hover,
#portlet_com_liferay_site_navigation_menu_web_portlet_SiteNavigationMenuPortlet .dropdown-menu .dropdown-item:focus {
  color: #fff;
  background: #182A3B;
}

/*
 * The brand replaces Liferay's site name in the header.
 *
 * WHITE artwork: the header carries the same navy as the bar below it, so the
 * dark version — correct when the header was light — became dark ink on a dark
 * ground and all but disappeared. Replacing rather than hiding: the anchor
 * keeps its link to the home page and its accessible name, and only the
 * visible mark changes.
 */
#banner a.logo.default-logo {
  display: inline-block;
  width: 250px;
  height: 58px;
  background: url("/cl-static/cl-theme/logo-white.png") left center / contain no-repeat;
  overflow: hidden;
  text-indent: -9999px;
}

/* The anchor holds Liferay's own mark as an <img>, which sat on top of the
   background and left the wordmark reading "hLOGIC". The image goes; the
   anchor keeps its link and its accessible name. */
#banner a.logo.default-logo img,
#banner a.logo.default-logo svg {
  display: none;
}

/* ────────────────────────────────────────────────────────────────────────
 * Full-height layout.
 *
 * A page whose content is shorter than the window used to stop mid-screen with
 * the footer floating up behind it and a band of bare ground below — most
 * visible on the screens that start empty, which are exactly the ones a coach
 * sees first: a device list before any device, an import before a file.
 *
 * The chain from body down to the widget is made to grow, so the shortest page
 * still fills the window and the footer sits where a footer belongs. Long
 * pages are untouched: `flex: 1 0 auto` grows into spare space and never
 * shrinks past its own content, so a forty-row table still scrolls normally.
 * ──────────────────────────────────────────────────────────────────────── */

body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

#wrapper {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
}

#content,
#main-content {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
}

/* The portlet chain between the page and the widget. */
#main-content .portlet-layout,
#main-content .portlet-column,
#main-content .portlet-boundary,
#main-content .portlet,
#main-content .portlet-content,
#main-content .portlet-content-container,
#main-content .portlet-body {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
}

/*
 * Content pages wrap each widget in layout-structure items that are not portlet
 * classes, so the chain broke there: #content grew to fill the window while the
 * widget stayed its own height and the slack became bare ground between the two.
 *
 * Only the LAST item grows. Stretching every one would distribute the spare
 * space across a page of stacked widgets and space them apart like a form
 * nobody laid out; the page should fill from the bottom.
 */
#main-content .lfr-layout-structure-item-basic-component-container:last-child,
#main-content [class*="lfr-layout-structure-item"]:last-child,
#main-content [id^="fragment-"]:last-child {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
}

/* A custom element is inline by default, so it ignores a height given to its
   parent and the widget inside never learns how much room it has.

   NEVER APPLIED TO SCRIPT, STYLE, TEMPLATE OR LINK. `> *` reached those too,
   and `display: block` on a <script> makes the browser render its source as
   text on the page: Liferay's sign-in portlet ships its JavaScript inline, so
   the login screen printed a paragraph of AUI code under the form. The
   universal selector was only ever meant for the custom element the widget
   mounts into. */
#main-content .portlet-body > *:not(script):not(style):not(template):not(link) {
  display: block;
  flex: 1 0 auto;
}

footer {
  flex-shrink: 0;
}

/* ────────────────────────────────────────────────────────────────────────
 * Full width.
 *
 * Liferay caps the content container at 1248px (container-fluid-max-xl) and
 * again at the Bootstrap breakpoint width — sensible for a page of prose, and
 * wrong for a ranking table with twelve columns, which is what these screens
 * are. On a wide monitor the cap left the data squeezed into the middle with
 * empty ground either side.
 * ──────────────────────────────────────────────────────────────────────── */

#content.container,
#content.container-fluid,
#content .container-fluid-max-xl,
#main-content > .container,
#main-content > .container-fluid {
  max-width: none;
  width: 100%;
}

#main-content .portlet-layout.row,
#main-content .portlet-column,
#main-content .portlet-dropzone {
  max-width: none;
  width: 100%;
}

/* ────────────────────────────────────────────────────────────────────────
 * The footer names the product, not the platform.
 *
 * The markup is a bare text node plus a link to liferay.com, so there is
 * nothing to restyle — the text node is collapsed to zero and the replacement
 * set on the row itself.
 *
 * THE ::after IS A FALLBACK NOW, not the mechanism. cl-tour's global script
 * rewrites this footer into real markup so the Terms of Use can be a link, and
 * generated content cannot be clicked. This stays for the moment before that
 * script runs, and for the case where it does not run at all — without it the
 * footer would read "Powered by Liferay" instead.
 * ──────────────────────────────────────────────────────────────────────── */

#footer .col-md-12 {
  font-size: 0;
}

#footer .col-md-12 a {
  display: none;
}

#footer .col-md-12::after {
  content: "Powered by CoachLOGIC";
  font-size: 13px;
  color: var(--cl-rail-text);
  letter-spacing: 0.02em;
}

/*
 * Once the script has rewritten the footer, the fallback has to get out of the
 * way — otherwise the generated string and the real one both show, and the
 * blanket `a { display: none }` above would hide the Terms link that was the
 * entire point of rewriting it.
 */
#footer .col-md-12[data-cl-footer="true"]::after {
  content: none;
}

#footer .col-md-12[data-cl-footer="true"] a {
  display: inline;
  color: var(--cl-rail-text);
}

/* ── Sign in ──────────────────────────────────────────────────────────────
 *
 * Liferay's sign-in page uses a FULL-WIDTH layout, so there is no Bootstrap
 * .container upstream and nothing supplies a gutter — the heading and fields
 * sat flush against the left edge of the window, which read as a broken page
 * rather than a plain one. Every other page in the site has .container above
 * it and needs none of this.
 *
 * Scoped to the login portlet by its boundary class, so it cannot leak into
 * the widget pages.
 */
.portlet-login .portlet-content,
.portlet-boundary_com_liferay_login_web_portlet_LoginPortlet_ .portlet-content {
  max-width: 420px;
  margin: 40px auto;
  padding: 28px 30px 30px;
  background: var(--cl-card);
  border: 1px solid var(--cl-line);
  border-radius: 12px;
}

/* The portlet title is the form's heading here, so it carries the weight
   rather than the muted all-caps a widget header uses.

   The extra .portlet-layout on the front is specificity, not decoration:
   Clay ships `.portlet-layout .portlet .portlet-title-text` (three classes)
   and a two-class selector of ours simply loses to it. */
.portlet-layout .portlet-login .portlet .portlet-title-text,
.portlet-login .portlet-title-text {
  font-size: 20px;
  font-weight: 700;
  color: var(--cl-ink);
  text-transform: none;
  letter-spacing: 0;
}

/* Liferay nests the fields in a .panel-body that brings its own 20px inset,
   so the inputs sat indented from the heading above them and the card looked
   like two different alignments stacked. The card supplies the padding. */
.portlet-login .portlet-content .panel-body {
  padding-left: 0;
  padding-right: 0;
}

/* Fields fill the card. Left at their natural width they were about half of
   it, which made the card look mis-sized rather than the inputs look small. */
.portlet-login .portlet-content .form-group,
.portlet-login .portlet-content .input-text-wrapper,
.portlet-login .portlet-content input[type="text"],
.portlet-login .portlet-content input[type="email"],
.portlet-login .portlet-content input[type="password"] {
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
}

/* Room to breathe between the fields, the button and the two links under it. */
.portlet-login .btn-primary {
  margin-top: 4px;
}

.portlet-login .sign-in-links,
.portlet-login .taglib-icon-list {
  margin-top: 14px;
}

@media (max-width: 480px) {
  .portlet-login .portlet-content,
  .portlet-boundary_com_liferay_login_web_portlet_LoginPortlet_ .portlet-content {
    margin: 16px 12px;
    padding: 20px;
  }
}

/* ── Vertical space above the fold ────────────────────────────────────────
 *
 * Measured on the athlete dashboard: 232px of chrome before a coach sees any
 * of their own data, on a laptop where the whole window is 800. Where it went,
 * and what each of these takes back:
 *
 *   56px  Liferay's control menu — the portal's own bar. Left alone.
 *   91px  the brand band: a 58px logo with 16px above and below
 *   59px  the navigation bar
 *   18px  the gap between navigation and content
 *
 * The logo band and the navigation are ours to tighten. Roughly 50px comes
 * back — most of a card's worth of dashboard, and the difference between the
 * radar being cut off and being on screen.
 *
 * Nothing here shrinks text or touching targets: the nav links keep their own
 * height and only the padding around them gives way.
 */
/*
 * !important, reluctantly and specifically. Liferay puts Bootstrap's `py-3`
 * utility on the element itself, and that ships as `padding: 1rem !important`
 * — no selector can outrank it. Overriding a utility is the one case where
 * matching it is the only move available.
 */
#banner .navbar-classic,
#banner .navbar-top,
#banner .navbar-expand-md {
  padding-top: 8px !important;
  padding-bottom: 8px !important;
}

/* The mark is legible well below the size it was drawn at; contain keeps the
   proportions, so only the box changes. */
#banner a.logo.default-logo {
  width: 200px;
  height: 46px;
}

/* The navigation row: the links are 24px and were sitting in 59px of bar. */
#portlet_com_liferay_site_navigation_menu_web_portlet_SiteNavigationMenuPortlet .nav-link,
#portlet_com_liferay_site_navigation_menu_web_portlet_SiteNavigationMenuPortlet .dropdown-toggle {
  padding-top: 10px;
  padding-bottom: 10px;
}

/*
 * NOTHING GOES BACK IN HERE. An earlier attempt added 8px of padding to
 * #content to "separate" the bar from the first card, and measuring showed
 * there had never been a gap — the space was inside the banner all along, so
 * the padding simply gave 8px of the saving straight back.
 */

/*
 * THE SEARCH BOX IS FOR PEOPLE WHO CAN SEARCH.
 *
 * Search finds athletes, tests and indexes, and every one of them is
 * staff-only — the gateway answers a signed-out caller 401, so a visitor who
 * typed into the header got a sign-in wall for their trouble. Offering a
 * control that cannot work is worse than not offering it.
 *
 * `signed-out` is Liferay's own class on <body>, present on every page a guest
 * can reach. Hiding rather than removing, because the box belongs to the
 * theme's header rather than to any page we control — there is nothing to
 * delete without editing the theme itself.
 *
 * This is the affordance only. The endpoint refuses guests on its own, and
 * still would if this rule were deleted tomorrow.
 */
body.signed-out .portlet-boundary_com_liferay_portal_search_web_search_bar_portlet_SearchBarPortlet_ {
  display: none;
}

/* ── Dark mode ───────────────────────────────────────────────────────────
 *
 * Set by the switch in the app header, which writes data-theme on <html>.
 * `:root[data-theme="dark"]` and not a media query alone: a coach who wants
 * dark at midday should get it, and a wall screen in a bright hall should be
 * able to stay light at midnight. The switch honours prefers-color-scheme as
 * its DEFAULT and then does as it is told — see cl-shared/src/Appearance.tsx.
 *
 * NOTHING HERE IS AN INVERSION. Inverting the palette would give white text on
 * gold buttons, a "good" green that glows, and a score scale that stops
 * surviving colour blindness. Every value below is chosen for the job the
 * token names:
 *
 *   - The ground is a warm-shifted near-black rather than #000. Pure black
 *     against white text is the pairing that makes long numbers shimmer, and
 *     this product is columns of numbers.
 *   - Cards are LIGHTER than the ground, not darker. Elevation reads as light
 *     in the dark, the opposite of paper.
 *   - The banded surfaces keep their hue and lose their fill: a pale red wash
 *     that works on paper is a highlighter pen on a dark screen, so the border
 *     carries the meaning and the tint is barely there.
 *   - Gold stays gold. It is the brand, it has enough luminance to sit on a
 *     dark ground, and moving it would be a different product.
 */
:root[data-theme="dark"] {
  --cl-ground:  #10151B;
  --cl-card:    #19212B;
  --cl-ink:     #E7EDF3;
  --cl-ink-soft:#9BAEC1;
  --cl-line:    #2A3644;

  /* The rail was already dark, and is nudged apart from the new ground so the
     chrome does not merge into the page. */
  --cl-rail:      #0C1219;
  --cl-rail-deep: #080C11;
  --cl-rail-line: #24313F;
  --cl-rail-text: #E7EDF3;
  --cl-rail-dim:  #93A7BB;
  --cl-rail-label:#8DA2B8;

  /* Gold reads on both grounds; the soft tint and the ink on it do not. */
  --cl-accent:      #D4AC2E;
  --cl-accent-soft: #2A2413;
  --cl-accent-ink:  #E8C766;

  --cl-steel: #5A8CBF;
  --cl-good:  #46B98C;
  --cl-warn:  #E0A64A;
  --cl-weak:  #F08A8A;

  /* Border carries the meaning; the wash is a hint, not a highlighter. */
  --cl-danger-line: #B4535B;
  --cl-good-line:   #2F7D5C;
  --cl-warn-line:   #8A6A22;
  --cl-warn-wash:   #221B0C;
  --cl-danger-wash: #241315;

  --cl-chip:        #222C38;
  --cl-control-ink: #C6D2DE;
  --cl-disabled:    #3A4756;
  --cl-on-dark:     #FFFFFF;

  /*
   * Charts. Same hues in the same lightness order — blue, teal, amber, deep
   * orange — lifted so they carry on a dark ground. A mid-tone blue on
   * near-black reads as a hole in the screen rather than a line.
   */
  --cl-chart-1: #6AA6FF;
  --cl-chart-2: #45C2AE;
  --cl-chart-3: #F0AE4A;
  --cl-chart-4: #A98BFF;
  --cl-chart-5: #F27272;
  --cl-chart-6: #9CCB57;
  --cl-chart-7: #4FC3DE;
  --cl-chart-8: #E07AE8;

  /* Red -> amber -> green, lifted for a dark ground. NOT the light values
     inverted: the hue order carries the meaning and must not move, so what
     changes is luminance. See cl-shared/src/charts/theme.ts for why this scale
     replaced the blue -> orange one, and what it costs. */
  --cl-score-low:     #FF8A8A;
  --cl-score-below:   #F5B457;
  --cl-score-average: #F3E07B;
  --cl-score-good:    #A8DB63;
  --cl-score-high:    #2FB683;

  --cl-chart-grid:        #263141;
  --cl-chart-axis:        #8095A9;
  --cl-chart-reference:   #6B7F93;
  --cl-chart-band:        rgba(107, 127, 147, 0.22);
  --cl-chart-provisional: #3E4C5C;
}

/* The page itself, which Liferay paints before any widget mounts. */
:root[data-theme="dark"] body,
:root[data-theme="dark"] .portlet-body {
  background: var(--cl-ground);
  color: var(--cl-ink);
}

/* Liferay's own chrome: cards, tables and inputs it draws for us. */
:root[data-theme="dark"] .card,
:root[data-theme="dark"] .panel,
:root[data-theme="dark"] .sheet {
  background: var(--cl-card);
  border-color: var(--cl-line);
  color: var(--cl-ink);
}

/*
 * THE MODAL, AND THE SIGN-IN DIALOGUE IN PARTICULAR.
 *
 * The generic input and label rules below darken every field and every label on
 * the page. Liferay draws its modals on a Clay surface this theme never
 * claimed, so the sign-in dialogue came out half-converted: near-black inputs
 * sitting on a white panel, with labels painted in the light ink meant for a
 * dark background and therefore all but invisible on it.
 *
 * It went unnoticed because the dark-mode work was done while signed in, and
 * this is the one dialogue you only ever see when you are not. Whoever it hits
 * is by definition a person who cannot yet get into the application.
 *
 * The surface is claimed here rather than by exempting inputs from the dark
 * treatment: the modal belongs to the dark page it opens over, not to a light
 * island in the middle of it.
 */
:root[data-theme="dark"] .modal-content,
:root[data-theme="dark"] .modal-header,
:root[data-theme="dark"] .modal-body,
:root[data-theme="dark"] .modal-footer {
  background: var(--cl-card);
  border-color: var(--cl-line);
  color: var(--cl-ink);
}

/* The heading and the close cross, which Clay colours independently. */
:root[data-theme="dark"] .modal-title,
:root[data-theme="dark"] .modal-header .close {
  color: var(--cl-ink);
}

:root[data-theme="dark"] input,
:root[data-theme="dark"] select,
:root[data-theme="dark"] textarea {
  background: var(--cl-card);
  color: var(--cl-ink);
  border-color: var(--cl-line);
}

/*
 * NEITHER DOES A LABEL. Clay sets `label { color: #272833 }` — a hardcoded
 * near-black, in clay.css rather than main.css, which is why searching the
 * theme's own stylesheet found nothing. Any text inside a <label> was
 * therefore invisible in dark mode, while a span beside it that set its own
 * colour read perfectly: the pickers on Offline Testing showed their units and
 * not their athlete names.
 *
 * An inline colour still wins, so labels that deliberately go quiet stay quiet.
 */
:root[data-theme="dark"] label {
  color: var(--cl-ink);
}

/*
 * BUTTONS DO NOT INHERIT COLOUR. A button with no `color` of its own uses the
 * browser's `buttontext` system colour, which is near-black and takes no
 * notice of the page around it — so Export and Print kept dark labels on a
 * dark background while every other word on the screen had flipped.
 *
 * Only the colour, and only where the button has not stated one: a primary
 * button setting white on gold is more specific and still wins.
 */
:root[data-theme="dark"] button {
  color: var(--cl-ink);
}

/*
 * THE PORTLET PANEL, which Liferay paints white from its own stylesheet.
 *
 * Roster, Index Ranking and Test Ranking showed a white page in dark mode
 * while newer screens did not, because those three render Liferay's portlet
 * decoration — a topper and a header — around the widget.
 *
 * THE FIRST ATTEMPT SCOPED THIS WITH :not(.portlet-borderless) AND MATCHED
 * NOTHING. Those pages carry BOTH classes: borderless on the boundary and a
 * topper inside it, so the guard excluded exactly the pages that needed the
 * rule. Checking the markup rather than assuming what "borderless" implies is
 * what found it.
 *
 * So the panel is simply made transparent. The page ground already supplies
 * the colour, every widget here draws its own cards, and a portlet that paints
 * nothing cannot paint the wrong thing — in either palette.
 */
/*
 * LIFERAY'S OWN VARIABLES, which is what actually paints the panel.
 *
 * Reading its stylesheet rather than guessing found the mechanism:
 *   .portlet .portlet-content   background-color: var(--portlet-bg, transparent)
 *   .portlet-topper             background-color: var(--portlet-topper-bg, …)
 *   .portlet-content-editable   border-color: var(--portlet-topper-border, …)
 *
 * Setting those is worth more than any number of overrides on the classes:
 * Liferay is already asking what colour to use, so the theme answers instead
 * of arguing.
 */
:root[data-theme="dark"] {
  /*
   * The frame around the content column, and the last white thing standing.
   * Liferay paints #wrapper with var(--body-bg, #fff), so a dark body still
   * had a white sheet inside it — visible as a band down both sides of every
   * screen.
   */
  --body-bg: var(--cl-ground);

  --portlet-bg: transparent;
  --portlet-topper-bg: var(--cl-card);
  --portlet-topper-border: var(--cl-line);
  --portlet-content-border-radius: 10px;
}

/*
 * THE HEADER SEARCH BAR, which is Liferay's own control.
 *
 * Its input takes the dark treatment from the generic input rule above, but
 * the button sitting inside the group does not: it is an inset item with its
 * own light surface, which showed as a pale block on the end of an otherwise
 * dark field.
 *
 * Scoped to the search portlet rather than styling `.input-group` globally —
 * that class appears throughout Liferay's admin screens, and a theme has no
 * business repainting those.
 */
:root[data-theme="dark"] .portlet-boundary_com_liferay_portal_search_web_search_bar_portlet_SearchBarPortlet_ .form-control,
:root[data-theme="dark"] .portlet-boundary_com_liferay_portal_search_web_search_bar_portlet_SearchBarPortlet_ .input-group-item,
:root[data-theme="dark"] .portlet-boundary_com_liferay_portal_search_web_search_bar_portlet_SearchBarPortlet_ .input-group-inset-item,
:root[data-theme="dark"] .portlet-boundary_com_liferay_portal_search_web_search_bar_portlet_SearchBarPortlet_ .btn {
  background: var(--cl-rail);
  border-color: var(--cl-rail-line);
  color: var(--cl-rail-text);
}

/* The placeholder, which inherits nothing. */
:root[data-theme="dark"] .portlet-boundary_com_liferay_portal_search_web_search_bar_portlet_SearchBarPortlet_ .form-control::placeholder {
  color: var(--cl-rail-dim);
}

/* Directly as well, in case a future Liferay stops asking. */
:root[data-theme="dark"] #wrapper {
  background: var(--cl-ground);
}

:root[data-theme="dark"] .portlet,
:root[data-theme="dark"] .portlet-boundary,
:root[data-theme="dark"] .portlet-content,
:root[data-theme="dark"] .portlet-content-container {
  background: transparent;
  color: var(--cl-ink);
  /*
   * THE OUTLINE, WHICH IS THE PART THAT SHOWED. Making the panel transparent
   * dealt with the fill and left Liferay's pale border drawing a bright
   * rectangle around the whole widget on a dark page — the white frame in the
   * report. The shadow goes too: a drop shadow tuned for a white page is a
   * grey halo on a dark one.
   */
  border-color: var(--cl-line);
  box-shadow: none;
}

/* The decoration itself, where a page still shows one. */
:root[data-theme="dark"] .portlet-topper,
:root[data-theme="dark"] .portlet-header {
  background: transparent;
  border-color: var(--cl-line);
}

:root[data-theme="dark"] .portlet-title-text,
:root[data-theme="dark"] .portlet-topper a,
:root[data-theme="dark"] .portlet-options {
  color: var(--cl-ink-soft);
}

/* A date picker's own glyph is drawn by the browser and is black on black
   without this. */
:root[data-theme="dark"] input[type="date"]::-webkit-calendar-picker-indicator {
  filter: invert(1) opacity(.7);
}


/* ─────────────────────────────────────────────────────────────────────────
 * GENERATED by build.mjs — do not edit dist/theme.css. Edit src/theme.css and
 * run `npm run build`.
 *
 * Every dark rule above, applied when the SYSTEM asks for dark and the coach
 * has not said otherwise. It exists because CSS arrives with the document and
 * the script that reads the stored choice does not: without this, a dark
 * machine would show a white page on every navigation before flipping.
 *
 * `:not([data-theme="light"])` is what keeps the choice winning — someone on
 * a dark machine who picked light stays light.
 * ───────────────────────────────────────────────────────────────────────── */
@media (prefers-color-scheme: dark) {
  /* ── Dark mode ───────────────────────────────────────────────────────────
   *
   * Set by the switch in the app header, which writes data-theme on <html>.
   * `:root:not([data-theme="light"])` and not a media query alone: a coach who wants
   * dark at midday should get it, and a wall screen in a bright hall should be
   * able to stay light at midnight. The switch honours prefers-color-scheme as
   * its DEFAULT and then does as it is told — see cl-shared/src/Appearance.tsx.
   *
   * NOTHING HERE IS AN INVERSION. Inverting the palette would give white text on
   * gold buttons, a "good" green that glows, and a score scale that stops
   * surviving colour blindness. Every value below is chosen for the job the
   * token names:
   *
   *   - The ground is a warm-shifted near-black rather than #000. Pure black
   *     against white text is the pairing that makes long numbers shimmer, and
   *     this product is columns of numbers.
   *   - Cards are LIGHTER than the ground, not darker. Elevation reads as light
   *     in the dark, the opposite of paper.
   *   - The banded surfaces keep their hue and lose their fill: a pale red wash
   *     that works on paper is a highlighter pen on a dark screen, so the border
   *     carries the meaning and the tint is barely there.
   *   - Gold stays gold. It is the brand, it has enough luminance to sit on a
   *     dark ground, and moving it would be a different product.
   */
  :root:not([data-theme="light"]) {
    --cl-ground:  #10151B;
    --cl-card:    #19212B;
    --cl-ink:     #E7EDF3;
    --cl-ink-soft:#9BAEC1;
    --cl-line:    #2A3644;
  
    /* The rail was already dark, and is nudged apart from the new ground so the
       chrome does not merge into the page. */
    --cl-rail:      #0C1219;
    --cl-rail-deep: #080C11;
    --cl-rail-line: #24313F;
    --cl-rail-text: #E7EDF3;
    --cl-rail-dim:  #93A7BB;
    --cl-rail-label:#8DA2B8;
  
    /* Gold reads on both grounds; the soft tint and the ink on it do not. */
    --cl-accent:      #D4AC2E;
    --cl-accent-soft: #2A2413;
    --cl-accent-ink:  #E8C766;
  
    --cl-steel: #5A8CBF;
    --cl-good:  #46B98C;
    --cl-warn:  #E0A64A;
    --cl-weak:  #F08A8A;
  
    /* Border carries the meaning; the wash is a hint, not a highlighter. */
    --cl-danger-line: #B4535B;
    --cl-good-line:   #2F7D5C;
    --cl-warn-line:   #8A6A22;
    --cl-warn-wash:   #221B0C;
    --cl-danger-wash: #241315;
  
    --cl-chip:        #222C38;
    --cl-control-ink: #C6D2DE;
    --cl-disabled:    #3A4756;
    --cl-on-dark:     #FFFFFF;
  
    /*
     * Charts. Same hues in the same lightness order — blue, teal, amber, deep
     * orange — lifted so they carry on a dark ground. A mid-tone blue on
     * near-black reads as a hole in the screen rather than a line.
     */
    --cl-chart-1: #6AA6FF;
    --cl-chart-2: #45C2AE;
    --cl-chart-3: #F0AE4A;
    --cl-chart-4: #A98BFF;
    --cl-chart-5: #F27272;
    --cl-chart-6: #9CCB57;
    --cl-chart-7: #4FC3DE;
    --cl-chart-8: #E07AE8;
  
    /* Red -> amber -> green, lifted for a dark ground. NOT the light values
       inverted: the hue order carries the meaning and must not move, so what
       changes is luminance. See cl-shared/src/charts/theme.ts for why this scale
       replaced the blue -> orange one, and what it costs. */
    --cl-score-low:     #FF8A8A;
    --cl-score-below:   #F5B457;
    --cl-score-average: #F3E07B;
    --cl-score-good:    #A8DB63;
    --cl-score-high:    #2FB683;
  
    --cl-chart-grid:        #263141;
    --cl-chart-axis:        #8095A9;
    --cl-chart-reference:   #6B7F93;
    --cl-chart-band:        rgba(107, 127, 147, 0.22);
    --cl-chart-provisional: #3E4C5C;
  }

  /* The page itself, which Liferay paints before any widget mounts. */
  :root:not([data-theme="light"]) body,
  :root:not([data-theme="light"]) .portlet-body {
    background: var(--cl-ground);
    color: var(--cl-ink);
  }

  /* Liferay's own chrome: cards, tables and inputs it draws for us. */
  :root:not([data-theme="light"]) .card,
  :root:not([data-theme="light"]) .panel,
  :root:not([data-theme="light"]) .sheet {
    background: var(--cl-card);
    border-color: var(--cl-line);
    color: var(--cl-ink);
  }

  /*
   * THE MODAL, AND THE SIGN-IN DIALOGUE IN PARTICULAR.
   *
   * The generic input and label rules below darken every field and every label on
   * the page. Liferay draws its modals on a Clay surface this theme never
   * claimed, so the sign-in dialogue came out half-converted: near-black inputs
   * sitting on a white panel, with labels painted in the light ink meant for a
   * dark background and therefore all but invisible on it.
   *
   * It went unnoticed because the dark-mode work was done while signed in, and
   * this is the one dialogue you only ever see when you are not. Whoever it hits
   * is by definition a person who cannot yet get into the application.
   *
   * The surface is claimed here rather than by exempting inputs from the dark
   * treatment: the modal belongs to the dark page it opens over, not to a light
   * island in the middle of it.
   */
  :root:not([data-theme="light"]) .modal-content,
  :root:not([data-theme="light"]) .modal-header,
  :root:not([data-theme="light"]) .modal-body,
  :root:not([data-theme="light"]) .modal-footer {
    background: var(--cl-card);
    border-color: var(--cl-line);
    color: var(--cl-ink);
  }

  /* The heading and the close cross, which Clay colours independently. */
  :root:not([data-theme="light"]) .modal-title,
  :root:not([data-theme="light"]) .modal-header .close {
    color: var(--cl-ink);
  }

  :root:not([data-theme="light"]) input,
  :root:not([data-theme="light"]) select,
  :root:not([data-theme="light"]) textarea {
    background: var(--cl-card);
    color: var(--cl-ink);
    border-color: var(--cl-line);
  }

  /*
   * NEITHER DOES A LABEL. Clay sets `label { color: #272833 }` — a hardcoded
   * near-black, in clay.css rather than main.css, which is why searching the
   * theme's own stylesheet found nothing. Any text inside a <label> was
   * therefore invisible in dark mode, while a span beside it that set its own
   * colour read perfectly: the pickers on Offline Testing showed their units and
   * not their athlete names.
   *
   * An inline colour still wins, so labels that deliberately go quiet stay quiet.
   */
  :root:not([data-theme="light"]) label {
    color: var(--cl-ink);
  }

  /*
   * BUTTONS DO NOT INHERIT COLOUR. A button with no `color` of its own uses the
   * browser's `buttontext` system colour, which is near-black and takes no
   * notice of the page around it — so Export and Print kept dark labels on a
   * dark background while every other word on the screen had flipped.
   *
   * Only the colour, and only where the button has not stated one: a primary
   * button setting white on gold is more specific and still wins.
   */
  :root:not([data-theme="light"]) button {
    color: var(--cl-ink);
  }

  /*
   * THE PORTLET PANEL, which Liferay paints white from its own stylesheet.
   *
   * Roster, Index Ranking and Test Ranking showed a white page in dark mode
   * while newer screens did not, because those three render Liferay's portlet
   * decoration — a topper and a header — around the widget.
   *
   * THE FIRST ATTEMPT SCOPED THIS WITH :not(.portlet-borderless) AND MATCHED
   * NOTHING. Those pages carry BOTH classes: borderless on the boundary and a
   * topper inside it, so the guard excluded exactly the pages that needed the
   * rule. Checking the markup rather than assuming what "borderless" implies is
   * what found it.
   *
   * So the panel is simply made transparent. The page ground already supplies
   * the colour, every widget here draws its own cards, and a portlet that paints
   * nothing cannot paint the wrong thing — in either palette.
   */
  /*
   * LIFERAY'S OWN VARIABLES, which is what actually paints the panel.
   *
   * Reading its stylesheet rather than guessing found the mechanism:
   *   .portlet .portlet-content   background-color: var(--portlet-bg, transparent)
   *   .portlet-topper             background-color: var(--portlet-topper-bg, …)
   *   .portlet-content-editable   border-color: var(--portlet-topper-border, …)
   *
   * Setting those is worth more than any number of overrides on the classes:
   * Liferay is already asking what colour to use, so the theme answers instead
   * of arguing.
   */
  :root:not([data-theme="light"]) {
    /*
     * The frame around the content column, and the last white thing standing.
     * Liferay paints #wrapper with var(--body-bg, #fff), so a dark body still
     * had a white sheet inside it — visible as a band down both sides of every
     * screen.
     */
    --body-bg: var(--cl-ground);
  
    --portlet-bg: transparent;
    --portlet-topper-bg: var(--cl-card);
    --portlet-topper-border: var(--cl-line);
    --portlet-content-border-radius: 10px;
  }

  /*
   * THE HEADER SEARCH BAR, which is Liferay's own control.
   *
   * Its input takes the dark treatment from the generic input rule above, but
   * the button sitting inside the group does not: it is an inset item with its
   * own light surface, which showed as a pale block on the end of an otherwise
   * dark field.
   *
   * Scoped to the search portlet rather than styling `.input-group` globally —
   * that class appears throughout Liferay's admin screens, and a theme has no
   * business repainting those.
   */
  :root:not([data-theme="light"]) .portlet-boundary_com_liferay_portal_search_web_search_bar_portlet_SearchBarPortlet_ .form-control,
  :root:not([data-theme="light"]) .portlet-boundary_com_liferay_portal_search_web_search_bar_portlet_SearchBarPortlet_ .input-group-item,
  :root:not([data-theme="light"]) .portlet-boundary_com_liferay_portal_search_web_search_bar_portlet_SearchBarPortlet_ .input-group-inset-item,
  :root:not([data-theme="light"]) .portlet-boundary_com_liferay_portal_search_web_search_bar_portlet_SearchBarPortlet_ .btn {
    background: var(--cl-rail);
    border-color: var(--cl-rail-line);
    color: var(--cl-rail-text);
  }

  /* The placeholder, which inherits nothing. */
  :root:not([data-theme="light"]) .portlet-boundary_com_liferay_portal_search_web_search_bar_portlet_SearchBarPortlet_ .form-control::placeholder {
    color: var(--cl-rail-dim);
  }

  /* Directly as well, in case a future Liferay stops asking. */
  :root:not([data-theme="light"]) #wrapper {
    background: var(--cl-ground);
  }

  :root:not([data-theme="light"]) .portlet,
  :root:not([data-theme="light"]) .portlet-boundary,
  :root:not([data-theme="light"]) .portlet-content,
  :root:not([data-theme="light"]) .portlet-content-container {
    background: transparent;
    color: var(--cl-ink);
    /*
     * THE OUTLINE, WHICH IS THE PART THAT SHOWED. Making the panel transparent
     * dealt with the fill and left Liferay's pale border drawing a bright
     * rectangle around the whole widget on a dark page — the white frame in the
     * report. The shadow goes too: a drop shadow tuned for a white page is a
     * grey halo on a dark one.
     */
    border-color: var(--cl-line);
    box-shadow: none;
  }

  /* The decoration itself, where a page still shows one. */
  :root:not([data-theme="light"]) .portlet-topper,
  :root:not([data-theme="light"]) .portlet-header {
    background: transparent;
    border-color: var(--cl-line);
  }

  :root:not([data-theme="light"]) .portlet-title-text,
  :root:not([data-theme="light"]) .portlet-topper a,
  :root:not([data-theme="light"]) .portlet-options {
    color: var(--cl-ink-soft);
  }

  /* A date picker's own glyph is drawn by the browser and is black on black
     without this. */
  :root:not([data-theme="light"]) input[type="date"]::-webkit-calendar-picker-indicator {
    filter: invert(1) opacity(.7);
  }
}
