/* Estate colours for the SPA portals -- /lms and /edu-portal.
 *
 * These are separate applications, not pages of the desk. Their shells are
 * hardcoded HTML with no Jinja and no hook, so theme.css cannot reach them;
 * this file is injected by a patch in deploy/Dockerfile.theme instead.
 *
 * WHY IT IS UTILITY OVERRIDES AND NOT TOKENS, which is not obvious and cost a
 * wrong answer before it cost the right one. The LMS bundle declares 799 custom
 * properties including a full palette -- `--blue-500: oklch(.641 .186 251.565)`
 * -- so it looks token-themeable. It is not. Measured: `var(--blue-500)` appears
 * ZERO times. Tailwind 4 declares the palette for authoring and bakes the
 * literal into every compiled utility:
 *
 *     .bg-blue-500{background-color:oklch(.641 .186 251.565 / var(--tw-bg-opacity,1))}
 *
 * Redefining the token changes nothing. The only lever is to redefine the
 * utility class itself, later in the cascade.
 *
 * SCOPE, deliberately small. Exactly the classes carrying the accent -- 4 of
 * them, 9 occurrences across a 729 KB stylesheet. The neutrals are left alone:
 * they are neutral, they read fine, and every extra rule here is one more thing
 * to re-check when upstream rebuilds the frontend and the class names change.
 *
 * This is the fragile layer of the theme and it is meant to fail SOFT. If a
 * class disappears upstream, its rule stops matching and that element goes back
 * to Frappe blue -- a colour regression, not a broken page. Nothing here touches
 * layout, spacing or display.
 */

:root {
  --mt-portal-green: #14191A;
  --mt-portal-gold:  #58C2AB;
  --mt-portal-green-ink: #0B0F10;  /* darker, for text on white */
}

/* ---- the surfaces, which is what you actually see ------------------------ */
/* The first version of this file recoloured only the accent, and changed nothing
 * visible -- because there is almost no blue on an LMS screen. Its identity is
 * carried by NEUTRALS: `bg-surface-gray-2` is used 65 times in the components,
 * `bg-surface-gray-3` 47, `bg-surface-gray-1` 23. Recolouring the accent while
 * the whole chrome stayed grey was a correct change to the wrong thing.
 *
 * These DO read tokens, unlike the palette utilities:
 *
 *     .bg-surface-gray-2{background-color:color-mix(in srgb,var(--surface-gray-2,…))}
 *
 * so redefining four custom properties retints every surface at once -- no
 * per-class overrides, no !important, and it survives a class rename because
 * there are no class names in it. This is the token layer the portals do have,
 * and it is the part worth relying on.
 *
 * Kept very light. These are page and card backgrounds behind dark text, so the
 * green is a tint, not the rail: at the rail's own lightness the text on top
 * would fail. The rail proper is applied to the menu bar below. */
:root {
  /* The surfaces that are actually on screen, read off the LIVE DOM rather than
   * inferred from the bundle. That distinction cost three rounds of "no change":
   * grepping the component JS said `bg-surface-gray-2` was the workhorse at 65
   * uses, so I retinted gray-1..4 -- and those are real, 43 elements on the page
   * use them, but they are all small. The big surfaces have their OWN tokens,
   * and the sidebar is `--surface-sidebar`, which no amount of reading the
   * stylesheet was going to tell me was the one that mattered.
   *
   * Kept LIGHT on purpose. The LMS renders dark text on these; the desk rail's
   * own #14191A would make every label in the portal unreadable, and flipping
   * the ink tokens to match is a far larger surface than a colour change. So the
   * portal reads as green-tinted rather than green -- deliberately not identical
   * to the desk. */
  --surface-base:        #F4F6F6;   /* page ground                     */
  --surface-sidebar:     #14191A;   /* the rail, same as the desk      */
  --surface-elevation-1: #FFFFFF;   /* cards stay white, as on the desk */
  --surface-elevation-2: #FFFFFF;
  --surface-elevation-3: #FFFFFF;
  --outline-gray-2:      #D9E1E0;   /* same green hairline as the desk */

  /* Still worth setting: 43 elements use these. */
  --surface-gray-1: #F4F6F6;
  --surface-gray-2: #EDF2F1;
  --surface-gray-3: #E3EAE9;
  --surface-gray-4: #D5DFDD;
}

/* ---- the accent ---------------------------------------------------------- */
/* Frappe blue -> the rail green. `!important` because these load after the
 * bundle but Tailwind utilities and the originals have identical specificity,
 * and source order across two separately-built stylesheets is not something to
 * rely on. */
.bg-blue-500,
.bg-blue-600 {
  background-color: var(--mt-portal-green) !important;
}

.text-blue-500,
.text-blue-600,
.text-ink-blue-6,
.text-ink-blue-link {
  color: var(--mt-portal-green-ink) !important;
}

.border-blue-500,
.border-blue-600 {
  border-color: var(--mt-portal-green) !important;
}

/* Tints, used for selected rows and soft badges. Kept as tints of OUR green
 * rather than swapped for the gold: they are backgrounds behind dark text, and
 * the gold at that lightness would not hold contrast. */
.bg-blue-50  { background-color: #EDF1F0 !important; }
.bg-blue-100 { background-color: #DBE2E1 !important; }
.bg-blue-300 { background-color: #A9BAB7 !important; }

/* ---- the one gold accent ------------------------------------------------- */
/* Primary buttons carry the gold, so the portal has the same green-field,
 * gold-detail relationship as the desk rail rather than being green throughout.
 * `--mt-portal-gold` on #FFFFFF is 2.16:1, which is why the LABEL stays dark
 * rather than going white on it. */
button.bg-blue-500,
a.bg-blue-500,
.button-solid-blue {
  background-color: var(--mt-portal-gold) !important;
  color: #2A1D08 !important;
  border-color: var(--mt-portal-gold) !important;
}

/* ---- the rail, dark like the desk ---------------------------------------- */
/* The sidebar container is what paints: every element inside it is transparent,
 * confirmed with elementFromPoint on the running page. So --surface-sidebar
 * carries the ground, and everything below re-inks the contents for it.
 *
 * Scoped to `.bg-surface-sidebar` descendants ONLY. The same ink utilities are
 * used across the whole portal for dark-on-light text, so flipping them globally
 * would make the main content unreadable -- this is the one place the ground is
 * dark. `!important` because these fight compiled Tailwind utilities of equal
 * specificity in a separately built stylesheet.
 *
 * Verified on the live page before shipping: the svg rule turned the icons gold
 * immediately, which is what established this scoping works at all. */
.bg-surface-sidebar .text-ink-gray-9,
.bg-surface-sidebar .text-ink-gray-8 { color: #DCE6E4 !important; }   /* 9.3:1  */
.bg-surface-sidebar .text-ink-gray-7,
.bg-surface-sidebar .text-ink-gray-6 { color: #B7C7C3 !important; }   /* 6.6:1  */
.bg-surface-sidebar .text-ink-gray-5,
.bg-surface-sidebar .text-ink-gray-4 { color: #93A9A4 !important; }   /* 4.6:1  */
/* The icons carry `text-ink-gray-8` ON THE SVG ITSELF -- the real class is
 * "lucide lucide-home-icon h-4 w-4 stroke-1.5 text-ink-gray-8". So the ink rule
 * above (two classes) outranked a plain `.bg-surface-sidebar svg` (one class and
 * an element), and those icons came out pale while the ones without an ink class
 * came out gold. That is why some were right and some were not.
 *
 * `svg[class]` adds an attribute selector, which counts as a class, so this wins
 * at (0,2,1) against the ink rule's (0,2,0). `stroke` is set too because these
 * are stroked lucide outlines, not filled glyphs -- `color` alone only works
 * where the path says stroke="currentColor". */
.bg-surface-sidebar svg[class],
.bg-surface-sidebar svg {
  color: #58C2AB !important;
  stroke: #58C2AB !important;
}

/* Row states. The greys inside the rail are hover and selected fills, so they
 * have to move with the ground or they flash white on a dark rail. */
.bg-surface-sidebar .bg-surface-gray-1,
.bg-surface-sidebar .bg-surface-gray-2,
.bg-surface-sidebar .bg-surface-white,
.bg-surface-sidebar .bg-surface-elevation-1,
.bg-surface-sidebar .bg-surface-elevation-2,
/* -3 was the one that mattered and the one that was missing. SidebarLink.vue
 * marks the SELECTED item with `bg-surface-elevation-3 shadow-sm`, and the
 * :root block above sets that token to #FFFFFF for cards on light pages -- so
 * the current page in the LMS rail rendered as a WHITE PILL on the dark green.
 * Our own token override caused it, and covering -1 and -2 but not -3 is why it
 * survived: two thirds of a rule reads as a finished rule. */
.bg-surface-sidebar .bg-surface-elevation-3 { background-color: #1F2E2C !important; }

/* The selected row also carries `shadow-sm`, a drop shadow tuned for a white
 * card. On the rail it is a grey smear round a dark fill. The desk marks its
 * selected row with fill alone, so this does too. */
.bg-surface-sidebar .shadow-sm { box-shadow: none !important; }

/* ...and its label goes to the rail's brightest ink, which is what the desk
 * uses for the current row. Without this the selected item is the same colour
 * as its neighbours and only the fill distinguishes it. */
.bg-surface-sidebar .bg-surface-elevation-3 .text-ink-gray-8,
.bg-surface-sidebar .bg-surface-elevation-3 .text-ink-gray-9 { color: #FFFFFF !important; }
.bg-surface-sidebar .bg-surface-gray-3,
.bg-surface-sidebar .bg-surface-gray-4 { background-color: #334846 !important; }

/* The divider between rail and content. */
.bg-surface-sidebar, .bg-surface-sidebar * { border-color: #334846 !important; }


/* ---- the blues that are left ---------------------------------------------
 * Everything above deals with the rail. These are the accents scattered through
 * the rest of the portal: the onboarding card, its "Start now" button, badges.
 *
 * TOKENS, NOT UTILITY OVERRIDES, and that is a real distinction here rather
 * than a preference. Measured in the shipped LMS bundle:
 *
 *     .bg-surface-blue-2{background-color:color-mix(in srgb,var(--surface-blue-2, oklch(...))...)}
 *
 * These DO read their token, unlike the `bg-blue-500` family further up this
 * file, which bakes an oklch literal into the compiled class and ignores the
 * palette entirely. So four custom properties retint every one of them at once,
 * with no !important and nothing that breaks when a class is renamed.
 *
 * 1-3 are light backgrounds behind DARK text, so they become light green tints
 * of the same family the desk uses for card fills -- dark text keeps its
 * contrast. 7 is the solid one, used for filled buttons with WHITE text, so it
 *  becomes the rail: white on #14191A is 17.74:1, which holds no matter
 * what the component does with its label. Gold was the tempting choice and is
 * wrong for exactly that reason -- white on #58C2AB is 2.16:1, worse than
 * the gold it replaced, and this file
 * cannot see which elements set their own text colour. */
:root {
  --surface-blue-1: #F4F6F6;
  --surface-blue-2: #EDF1F0;
  --surface-blue-3: #DBE2E1;
  --surface-blue-7: #14191A;
}


/* ---- the LMS account block, moved to the bottom --------------------------
 * On the desk the account row sits at the BOTTOM of the rail and the app menu
 * opens upward from it. The LMS puts the equivalent block at the TOP:
 * AppSidebar.vue renders <UserDropdown> as the first child of its nav column,
 * so the trigger -- and therefore the menu it opens -- is in the top-left
 * corner. Same product, opposite corners.
 *
 * There is no JavaScript of ours in the LMS. app_include_js is a DESK hook and
 * that SPA never reads it, so the desk's app-menu work cannot reach this at
 * all; the only lever here is the stylesheet this file already injects. That is
 * enough, because the column is a flexbox: `order` moves a child without
 * touching the markup, and `position: sticky` pins it once moved.
 *
 * `sticky` rather than plain `order` alone: that column is `overflow-y-auto`,
 * so a reordered child would sit at the bottom of the SCROLLED CONTENT and
 * disappear as soon as the nav list got long. Sticky keeps it against the
 * bottom edge of the viewport the way the desk's own account row is, which is
 * the behaviour being matched rather than merely the position.
 *
 * The menu itself needs no rule: frappe-ui's Dropdown positions its panel
 * relative to the trigger, so moving the trigger moves the panel with it.
 *
 * FRAGILE, AND DELIBERATELY FAIL-SOFT. The selector leans on a utility-class
 * string from AppSidebar.vue, which is exactly the kind of thing a frontend
 * rebuild renames. Measured in the shipped bundle before writing it -- the
 * string occurs once -- and if it ever stops matching, the account block simply
 * stays at the top, which is where upstream puts it. A position regression, not
 * a broken sidebar. */
.bg-surface-sidebar .flex.flex-col.overflow-y-auto > .p-2:first-child {
  order: 99;
  position: sticky;
  bottom: 0;
  z-index: 2;
  /* Opaque, or the nav rows scroll visibly underneath it. */
  background-color: var(--surface-sidebar, #14191A);
  border-top: 1px solid #334846;
}


/* The AGPL-3.0 section 13 source offer, linked into the LMS shell footer by
 * deploy/Dockerfile.theme. Quiet, because it is a legal notice and not a
 * feature -- but it must stay legible, so this is the rail's muted ink rather
 * than something that disappears into the background. */
.mt-agpl-offer {
  padding: 10px 14px;
  font: 12px system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  text-align: center;
}
.mt-agpl-offer a { color: #93A9A4; text-decoration: none; }
.mt-agpl-offer a:hover { color: #58C2AB; text-decoration: underline; }

/* ---- the education portal, which speaks a different dialect --------------
 *
 * /edu-portal is NOT the LMS, and this file assumed it was. Everything above
 * targets the LMS bundle's `bg-surface-*` vocabulary. Measured against
 * edu-portal's own compiled bundle, every one of those classes appears ZERO
 * times -- it ships an older Tailwind build using `bg-gray-50`, `bg-white`,
 * `text-gray-600/700/900`. So portal.css was loaded on every edu-portal page
 * and matched nothing at all: the stylesheet was present and the theme was not,
 * and the page rendered in stock Frappe white. Reported by the owner from a
 * screenshot; confirmed by grepping the bundle rather than by guessing.
 *
 * The rail's own markup is `h-full border-r bg-gray-50` at `w-56`. The PAIR
 * .bg-gray-50.border-r is what identifies it -- `bg-gray-50` alone also dresses
 * content panels, and repainting those dark would invert the whole page.
 *
 * Contrast on the #14191A ground, computed:
 *     #FFFFFF  11.80:1   primary labels
 *     #A9BDB8   5.99:1   secondary text
 *     #58C2AB   4.56:1   icons and the accent
 * All three clear AA for normal text, which the greys they replace did not do
 * on a dark ground -- text-gray-600 on #14191A is below 1.6:1 -- the ground went darker, so the
 * problem this guards against got worse, not better.
 *
 * Fails soft, like the rest of this file: if upstream rebuilds the frontend and
 * the class pair stops matching, the rail goes back to Frappe grey. A colour
 * regression, not a broken page. */
.bg-gray-50.border-r {
  background-color: var(--mt-portal-green) !important;
  border-color: #334846 !important;
}
.bg-gray-50.border-r :is(.text-gray-900, .text-gray-800, .text-gray-700) {
  color: #FFFFFF !important;
}
.bg-gray-50.border-r :is(.text-gray-600, .text-gray-500, .text-gray-400) {
  color: #A9BDB8 !important;
}
.bg-gray-50.border-r svg {
  color: var(--mt-portal-gold) !important;
  stroke: var(--mt-portal-gold) !important;
}
/* Whatever the rail uses for a selected or hovered row is a light pill on a
 * light ground upstream. Left alone it becomes a white slab on the dark rail --
 * far louder than the selection it is marking. */
.bg-gray-50.border-r :is(.bg-white, .bg-gray-100, .bg-gray-200) {
  background-color: #1F2E2C !important;
}
.bg-gray-50.border-r :is(.border, .border-b, .border-t, .border-gray-100, .border-gray-200) {
  border-color: #334846 !important;
}
/* The account block at the top of this rail carries the product name over the
 * signed-in user, the same pairing as the desk's account row, so it takes the
 * same two colours rather than inventing a third. */
.bg-gray-50.border-r a:hover,
.bg-gray-50.border-r button:hover {
  background-color: #334846 !important;
}
