/* ============================================================================
   theme-lokentra.css — the Lokentra brand skin for the GRMS console.

   WHY THIS FILE EXISTS
   --------------------
   The console shell was ported from lokentra/BuzzRFP, which is themed for a
   US-government look: navy #205493, steel #3B82A0, gold #FBBF24. This is a
   Lokentra deployment, so the console should
   wear the product's own mark, not a federal-procurement palette.

   Every colour below is SAMPLED FROM THE BRAND MARK, decoded pixel-by-pixel
   rather than eyeballed. The wordmark is a blue gradient — deep navy #082038
   through steel #106088 — with no second hue in it, so the ramp is that
   gradient and the accent is chosen rather than sampled:

     steel  #106088   the wordmark's light end      (7.05% of all opaque
                                                     pixels — the single most
                                                     common inked colour)
     navy   #082840   the wordmark's dark end       (deepest blue in the art)
     clay   #ED9462   NOT sampled — chosen. The mark is monochrome, so the
                      accent had to come from somewhere; clay is the warm
                      complement of this blue and keeps the existing rule that
                      anything AI-generated is clay.
     ink    #1B2422   body text

   LOAD ORDER MATTERS. This sheet loads LAST in base.html, after
   tailwind.compiled.css. Tailwind v4 puts its utilities in `@layer utilities`,
   and in the CSS cascade an UNLAYERED rule beats a layered one regardless of
   specificity — so the rules here win over Tailwind utilities on the same
   element. That is deliberate and is what lets `.lokentra-input` replace a string
   of six utility classes. It is also a hazard: keep selectors scoped to
   `.lokentra-*` classes and to the shell's own ids. Do NOT add bare element
   selectors here (`h1 { ... }`, `a { ... }`) — they would silently outrank
   every Tailwind utility in the console. The one exception is the
   `:focus-visible` block, which is scoped to a state no utility sets.

   NO NESTED COMMENTS ANYWHERE IN THIS FILE. CSS comments do not nest; an inner
   close-marker ends the block early and the remaining prose leaks out as a
   broken selector. That has already silently swallowed a rule in this codebase.
   ============================================================================ */

/* ----------------------------------------------------------------------------
   1. Brand tokens
   ---------------------------------------------------------------------------
   Two ramps built around the two sampled hues, plus a warm-neutral ink scale
   (the seal's darks are slightly green-black, not blue-black, so the standard
   cool grays looked wrong beside it).

   The contrast figure on each interactive step is measured, not guessed —
   WCAG 2.1 relative luminance against the stated ground. Read these before
   picking a token for text: unlike the palette this replaced, every step from
   500 up clears AA on white — the sampled brand colour #106088 is itself
   6.88:1, so it carries body text as well as fills.
   ------------------------------------------------------------------------- */
:root {
    /* Teal — the night sky. 500 is the sampled brand value. */
    --lokentra-blue-50:  #F2F7FB;
    --lokentra-blue-100: #E1EDF6;
    --lokentra-blue-200: #C0D8EA;
    --lokentra-blue-300: #93BCD9;
    --lokentra-blue-400: #4E93BE;   /* 3.36:1 on white - large text / UI only */
    --lokentra-blue-500: #1E74A0;   /* 5.17:1 on white - AA text */
    --lokentra-blue-600: #106088;   /* SAMPLED FROM THE WORDMARK. 6.88:1 - AA text, fills */
    --lokentra-blue-700: #0C4C6C;   /* 9.27:1 on white */
    --lokentra-blue-800: #083850;   /* sampled. 12.41:1 on white */
    --lokentra-blue-900: #082840;   /* sampled. 15.13:1 on white - dark chrome */
    --lokentra-blue-950: #061B2C;   /* 17.49:1 on white - sidebar, footer */

    /* Clay — the cupped hand. Replaces the federal gold as the accent. */
    --lokentra-clay-50:  #FEF6F1;
    --lokentra-clay-100: #FDE9DC;
    --lokentra-clay-200: #FAD2B9;
    --lokentra-clay-300: #F6B78E;   /* 5.85:1 on teal-900 - accents on dark chrome */
    --lokentra-clay-400: #F09A63;   /* 6.63:1 on teal-950 */
    --lokentra-clay-500: #ED9462;   /* sampled from seal */
    --lokentra-clay-600: #DC7538;
    --lokentra-clay-700: #B85B29;   /* 4.60:1 on white - AA text */
    --lokentra-clay-800: #8F4823;
    --lokentra-clay-900: #733C20;

    /* Warm ink — the feather ring. Body text and headings. */
    --lokentra-ink:      #1B2422;
    --lokentra-ink-2:    #3A4644;   /* secondary text */
    --lokentra-ink-3:    #66716F;   /* muted text, 4.6:1 on white */
    --lokentra-ink-4:    #939C9A;   /* placeholder / disabled */

    /* Surfaces and hairlines, warmed a few degrees toward the seal's darks. */
    --lokentra-canvas:      #F6F8F7;
    --lokentra-surface:     #FFFFFF;
    --lokentra-surface-alt: #FAFBFA;
    --lokentra-line:        #E3E8E7;
    --lokentra-line-strong: #CBD3D1;

    /* Focus ring. Used by every interactive element in the console. */
    --lokentra-ring: rgba(19, 168, 158, 0.30);

    /* Elevation. Softer and warmer than Tailwind's neutral shadows, which look
       grey-blue against the teal chrome. */
    --lokentra-shadow-sm: 0 1px 2px rgba(6, 27, 44, 0.06),
                       0 1px 3px rgba(6, 27, 44, 0.05);
    --lokentra-shadow:    0 1px 2px rgba(6, 27, 44, 0.05),
                       0 4px 12px -2px rgba(6, 27, 44, 0.08);
    --lokentra-shadow-lg: 0 2px 4px rgba(6, 27, 44, 0.05),
                       0 12px 28px -6px rgba(6, 27, 44, 0.14);

    /* The night sky, reused by the sidebar, the footer and the login hero so
       all three read as one surface. */
    --lokentra-sky: linear-gradient(168deg,
                     var(--lokentra-blue-700) 0%,
                     var(--lokentra-blue-800) 46%,
                     var(--lokentra-blue-950) 100%);
}

/* ----------------------------------------------------------------------------
   2. Re-point the inherited token systems
   ---------------------------------------------------------------------------
   theme-buzzrfp.css consumes its own `--primary-*` / `--accent-*` / `--gov-*`
   ramps in 53 places, and grms-console-monorepo.css consumes `--brand-blue` in
   8 (buttons, file inputs, metric card accents, pagination, saved-search
   chips). Re-pointing the tokens rethemes all of that without touching either
   sheet.

   `--brand-blue` was never actually DEFINED in any sheet this build loads — it
   lives in design-tokens.css, which base.html no longer includes — so what
   rendered was the `var(--brand-blue, #205493)` fallback. Defining it here is
   what turns those components teal.

   The ramp is shifted one step darker at the interactive end on purpose:
   `--primary-600` is the step the theme uses for both fills and text, and
   teal-600 is only 4.22:1 on white (fails AA for body text). Mapping it to
   teal-700 (6.09:1) passes, while the true sampled brand teal stays visible at
   `--primary-500` for gradients and decoration.

   `--success-*`, `--warning-*`, `--danger-*` and `--info-*` are deliberately
   NOT touched. Those carry meaning — green means awarded, amber means a
   deadline is near, red means overdue. Recolouring them to brand would destroy
   information that the grants tables depend on.
   ------------------------------------------------------------------------- */
:root {
    --primary-50:  var(--lokentra-blue-50);
    --primary-100: var(--lokentra-blue-100);
    --primary-200: var(--lokentra-blue-200);
    --primary-300: var(--lokentra-blue-300);
    --primary-400: var(--lokentra-blue-400);
    --primary-500: var(--lokentra-blue-500);
    --primary-600: var(--lokentra-blue-700);
    --primary-700: var(--lokentra-blue-800);
    --primary-800: var(--lokentra-blue-900);
    --primary-900: var(--lokentra-blue-950);
    --primary-950: #041F1D;

    /* The gold highlight ramp becomes the clay ramp. `--accent-400` was the
       federal gold #FBBF24 and is the step the theme actually paints with. */
    --accent-50:  var(--lokentra-clay-50);
    --accent-100: var(--lokentra-clay-100);
    --accent-200: var(--lokentra-clay-200);
    --accent-300: var(--lokentra-clay-300);
    --accent-400: var(--lokentra-clay-500);
    --accent-500: var(--lokentra-clay-600);
    --accent-600: var(--lokentra-clay-700);
    --accent-700: var(--lokentra-clay-800);
    --accent-800: var(--lokentra-clay-900);
    --accent-900: #5C3019;

    /* The `--gov-*` set: a federal palette inherited from the ported shell. */
    --gov-navy:       var(--lokentra-blue-950);
    --gov-blue:       var(--lokentra-blue-700);
    --gov-blue-light: var(--lokentra-blue-500);
    --gov-gold:       var(--lokentra-clay-500);
    --gov-cream:      var(--lokentra-clay-50);

    /* design-tokens.css names, consumed by grms-console-monorepo.css. */
    --brand-navy: var(--lokentra-blue-950);
    --brand-blue: var(--lokentra-blue-700);
    --brand-teal: var(--lokentra-blue-500);
    --brand-gold: var(--lokentra-clay-500);

    /* Semantic aliases. */
    --color-accent:        var(--lokentra-blue-700);
    --color-accent-hover:  var(--lokentra-blue-800);
    --color-accent-subtle: var(--lokentra-blue-50);
    --color-border:        var(--lokentra-line);
    --color-border-strong: var(--lokentra-line-strong);
    --color-text:          var(--lokentra-ink);
    --color-text-secondary: var(--lokentra-ink-2);
    --color-text-muted:    var(--lokentra-ink-3);
    --color-surface-hover: var(--lokentra-surface-alt);
    --shadow-card:         var(--lokentra-shadow-sm);
    --shadow-card-hover:   var(--lokentra-shadow);

    /* The three base.html brand variables, which drive `.brand-primary`,
       `.bg-brand-primary`, `.bg-gradient-brand` and friends. base.html sets
       these from optional template context with lokentra defaults; when no
       tenant branding is supplied those defaults are what render, so they are
       re-pointed here rather than in the template. */
    /* blue-600 is the colour SAMPLED FROM THE WORDMARK (#106088, 6.88:1 on
       white). blue-700 would be a shade darker than the mark itself, which is
       what the console showed before it was tied back to the art. */
    --primary-color:   var(--lokentra-blue-600);
    --secondary-color: var(--lokentra-blue-900);
    --accent-color:    var(--lokentra-clay-500);
}

/* ----------------------------------------------------------------------------
   3. Brand utility classes
   ---------------------------------------------------------------------------
   These replace the arbitrary-value Tailwind classes the ported templates used
   for brand colour: `text-[#205493]`, `bg-[#205493]`, `border-[#205493]`,
   `bg-[#205493]/10`, `text-[#FBBF24]`, `bg-[#FBBF24]`, and the
   `from-[#205493] to-[#3B82A0]` gradient pair.

   Those classes could not simply be recoloured in place: an arbitrary-value
   class is a literal hex in its own name, so a rule making `.text-\[\#205493\]`
   render teal leaves a selector that lies about what it does. Worse, arbitrary
   values only exist in this build because the monorepo happened to use them —
   the compiled Tailwind bundle is purged, so a NEW arbitrary value such as
   `text-[#106088]` would produce no style at all. Hence named classes here.
   ------------------------------------------------------------------------- */
.lokentra-fg   { color: var(--lokentra-blue-700); }
.lokentra-bg   { background-color: var(--lokentra-blue-700); }
.lokentra-bd   { border-color: var(--lokentra-blue-600); }
.lokentra-tint { background-color: var(--lokentra-blue-50); }

/* Text link in brand teal. Underline appears on hover only, so dense tables
   stay quiet until pointed at. */
.lokentra-link {
    color: var(--lokentra-blue-700);
    text-decoration: none;
    transition: color 0.15s ease;
}
.lokentra-link:hover {
    color: var(--lokentra-blue-800);
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* Clay accent, for use on the dark chrome only — clay-500 is 2.33:1 on white
   and must never carry text there. `--accent-fg` is the lighter step because
   the grounds it sits on are teal-700 through teal-950. */
.lokentra-accent-fg { color: var(--lokentra-clay-300); }
.lokentra-accent-bg { background-color: var(--lokentra-clay-500); }
.lokentra-accent-link {
    color: rgba(255, 255, 255, 0.78);
    text-decoration: none;
    transition: color 0.15s ease;
}
.lokentra-accent-link:hover { color: var(--lokentra-clay-300); }

/* Clay text on a light ground needs the AA step instead. */
.lokentra-accent-strong { color: var(--lokentra-clay-700); }

/* ----------------------------------------------------------------------------
   4. Buttons
   ---------------------------------------------------------------------------
   `.grms-btn` in grms-console-monorepo.css already went teal via
   `--brand-blue`. `.lokentra-btn` is for the ported buttons that carried their
   own padding utilities plus a raw `bg-[#205493]`, so it sets colour and state
   only and leaves geometry to the utilities already on the element.
   ------------------------------------------------------------------------- */
.lokentra-btn {
    background-color: var(--lokentra-blue-700);
    color: #fff;
    border: 1px solid transparent;
    box-shadow: var(--lokentra-shadow-sm);
    cursor: pointer;
    transition: background-color 0.15s ease, box-shadow 0.15s ease,
                transform 0.12s ease;
}
.lokentra-btn:hover  { background-color: var(--lokentra-blue-800);
                    box-shadow: var(--lokentra-shadow); }
.lokentra-btn:active { background-color: var(--lokentra-blue-900);
                    transform: translateY(1px); box-shadow: none; }
.lokentra-btn:disabled, .lokentra-btn[disabled] {
    background-color: var(--lokentra-ink-4);
    box-shadow: none; cursor: not-allowed; transform: none;
}

/* Secondary: reads as pressable without competing with the primary action. */
.lokentra-btn-ghost {
    background-color: #fff;
    color: var(--lokentra-ink);
    border: 1px solid var(--lokentra-line-strong);
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease;
}
.lokentra-btn-ghost:hover {
    background-color: var(--lokentra-blue-50);
    border-color: var(--lokentra-blue-300);
    color: var(--lokentra-blue-800);
}

/* The primary CTA on the dashboard and the login form. The sampled steel
   #106088 is the gradient's light end — the one place the wordmark's own colour
   shows at size, which is what ties the console to the mark. */
.lokentra-btn-brand {
    background-image: linear-gradient(135deg,
                          var(--lokentra-blue-600) 0%,
                          var(--lokentra-blue-800) 100%);
    color: #fff;
    border: 1px solid transparent;
    box-shadow: var(--lokentra-shadow-sm);
    cursor: pointer;
    transition: box-shadow 0.18s ease, transform 0.12s ease, filter 0.15s ease;
}
.lokentra-btn-brand:hover  { filter: brightness(1.08);
                          box-shadow: var(--lokentra-shadow); }
.lokentra-btn-brand:active { transform: translateY(1px); box-shadow: none;
                          filter: brightness(0.96); }

/* ----------------------------------------------------------------------------
   5. Form controls
   ---------------------------------------------------------------------------
   Replaces the six-utility string the ported templates repeated on every field
   (`w-full px-3 py-2.5 border border-gray-300 rounded-lg text-sm
   focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-[#205493]`)
   — which also carried a duplicated `focus:ring-2` on the login page.

   One class, and the focus state becomes a teal ring instead of Tailwind's
   default blue.
   ------------------------------------------------------------------------- */
/* NOTE: deliberately sets no `display` and no `width`. This class owns a
   field's APPEARANCE; Tailwind's `w-full` / `flex-1` own its LAYOUT, the same
   way they do everywhere else in the console. An earlier version forced
   `display: block; width: 100%` here, and because this sheet is unlayered it
   beat the utilities — every compact inline field broke out of its row, and
   the grants filter bar became four stacked full-width controls. */
.lokentra-input {
    padding: 0.5625rem 0.75rem;
    font-size: 0.875rem;
    line-height: 1.4;
    color: var(--lokentra-ink);
    background-color: #fff;
    border: 1px solid var(--lokentra-line-strong);
    border-radius: 0.5rem;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.lokentra-input::placeholder { color: var(--lokentra-ink-4); }
.lokentra-input:hover        { border-color: var(--lokentra-ink-4); }
.lokentra-input:focus {
    outline: none;
    border-color: var(--lokentra-blue-500);
    box-shadow: 0 0 0 3px var(--lokentra-ring);
}
.lokentra-input:disabled {
    background-color: var(--lokentra-canvas);
    color: var(--lokentra-ink-3);
    cursor: not-allowed;
}
/* Native select draws its own chevron; reserve room so the label does not
   collide with it. Same fix as `.grms-page select`. */
select.lokentra-input { padding-right: 1.75rem; }
textarea.lokentra-input { min-height: 5rem; resize: vertical; }

/* Compact variant. The ported pages styled 74 fields inline with
   `px-2 py-1.5 border border-gray-300 rounded-md text-sm` — the same look
   retyped 74 times, which is why field height, radius and focus behaviour
   drifted between panels. This is that geometry, once. */
.lokentra-input--sm {
    padding: 0.375rem 0.5rem;
    font-size: 0.8125rem;
    border-radius: 0.375rem;
}
select.lokentra-input--sm { padding-right: 1.6rem; }

.lokentra-label {
    display: block;
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--lokentra-ink-2);
    margin-bottom: 0.375rem;
}

/* Native checkboxes, radios and range thumbs paint themselves with the
   browser's own accent — Chrome's is blue — so every role checkbox on the
   Users page and every filter box stayed blue no matter what the theme said.
   One property fixes all of them. Selected by attribute so it cannot affect
   anything else. */
input[type="checkbox"],
input[type="radio"],
input[type="range"],
progress {
    accent-color: var(--lokentra-blue-600);
}

/* ----------------------------------------------------------------------------
   5b. Informational surfaces
   ---------------------------------------------------------------------------
   The ported pages use Tailwind blue (`bg-blue-50 border-blue-200
   text-blue-800`) for explanatory notes, unread rows and "currently watching"
   toggles. Blue is a defensible choice for "information" in the abstract, but
   beside this palette's teal it reads as a second, competing brand colour.

   Teal takes over the informational role here. The genuinely semantic hues are
   untouched — green still means awarded, amber still means a deadline is near,
   red still means overdue — so the console keeps one accent plus a traffic
   light, rather than two accents plus a traffic light.
   ------------------------------------------------------------------------- */
.lokentra-note {
    background: var(--lokentra-blue-50);
    border: 1px solid var(--lokentra-blue-200);
    color: var(--lokentra-blue-900);
}
.lokentra-note-chip {
    background: var(--lokentra-blue-50);
    border: 1px solid var(--lokentra-blue-200);
    color: var(--lokentra-blue-800);
}
/* Unread notification row. Kept very light — it marks a row, it is not a
   status the eye should be dragged to. */
.lokentra-unread { background: rgba(19, 168, 158, 0.06); }

/* A toggle that is currently on (the grant "Watching" button). */
.lokentra-toggle-on {
    border-color: var(--lokentra-blue-500) !important;
    color: var(--lokentra-blue-800) !important;
    background: var(--lokentra-blue-50) !important;
}
/* Its sibling: the "Muted" state of the notification toggle beside it. Amber,
   because a silenced grant is a caution the owner should notice, not a second
   informational state - and the two buttons must not both read as teal or the
   pair stops distinguishing itself. The colour comes from --warning-*, so it
   stays in step with every other amber in the console.

   `.lokentra-toggle-on` uses !important because it has to beat the utility
   classes on the same element; this one carries no competing utilities but
   matches its sibling so the pair stays symmetrical to read and to edit.
   Text is --warning-800 on --warning-50: 6.84:1, AA at any size. The value it
   replaced (#D97706 text on Tailwind amber-50) measured 3.07:1 and failed. */
.lokentra-toggle-muted {
    border-color: var(--warning-500) !important;
    color: var(--warning-800) !important;
    background: var(--warning-50) !important;
}

/* Progress bars and the workflow stepper's current step. */
.lokentra-bar-track { background: var(--lokentra-blue-100); }
.lokentra-bar {
    background-image: linear-gradient(90deg,
                          var(--lokentra-blue-600),
                          var(--lokentra-blue-500));
}
.lokentra-step-active {
    background: var(--lokentra-blue-700) !important;
    color: #fff !important;
    box-shadow: 0 0 0 4px var(--lokentra-blue-100);
}

/* ----------------------------------------------------------------------------
   5c. AI actions — the clay family
   ---------------------------------------------------------------------------
   grms-console-monorepo.css gives AI actions a violet `.grms-btn--ai`, and
   grant_detail.html carries a matching set of `*-purple-*` utilities for the
   draft-generation progress bar and the section editors. Violet is the usual
   web convention for "this was written by a model", but it is a third hue
   against teal and clay and it looked like a bug.

   Clay takes that role instead. It is the seal's own second colour, so AI
   surfaces stay clearly distinct from ordinary teal controls without
   introducing anything off-palette: teal is what the system does, clay is what
   the model did. clay-700 is the step used for text and fills because it is
   the first one that reaches 4.6:1 on white.
   ------------------------------------------------------------------------- */
.grms-btn--ai { background: var(--lokentra-clay-700) !important; }
.grms-btn--ai:hover  { background: var(--lokentra-clay-800) !important; filter: none; }

/* ----------------------------------------------------------------------------
   5d. Button hierarchy
   ---------------------------------------------------------------------------
   `.grms-btn--info` (blue) and `.grms-btn--success` (green) were being used as
   "a second and third button colour", not as semantics: "Add to Repository",
   "Create", "Ingest", "Filter", "Validate Compliance" and "Assign" are all
   just the primary action of the panel they sit in. The result was that every
   action on a page was a saturated coloured button, so no action stood out —
   a page with six equally loud buttons has no primary action at all.

   Both fold into the brand teal, which restores an actual hierarchy: teal
   solid is the one main action, `--ghost` is secondary, `--ai` (clay) is a
   model action, and red stays destructive. Aliasing them here rather than
   rewriting 16 call sites keeps the diff honest — nothing about those buttons
   changed except that they stopped claiming a meaning they never had.
   ------------------------------------------------------------------------- */
.grms-btn--info,
.grms-btn--success { background: var(--lokentra-blue-700); }

/* Secondary actions sit beside a primary one and must not compete with it. */
.grms-btn--ghost {
    background: #fff;
    color: var(--lokentra-ink);
    border-color: var(--lokentra-line-strong);
}
.grms-btn--ghost:hover {
    background: var(--lokentra-blue-50);
    border-color: var(--lokentra-blue-300);
    color: var(--lokentra-blue-800);
    filter: none;
}
.grms-btn:focus-visible { box-shadow: 0 0 0 3px var(--lokentra-ring); }

.lokentra-ai-fg    { color: var(--lokentra-clay-700); }
.lokentra-ai-bd    { border-color: var(--lokentra-clay-200) !important; }
.lokentra-ai-track { background: var(--lokentra-clay-100); }
.lokentra-ai-bar   { background-image: linear-gradient(90deg,
                                        var(--lokentra-clay-600),
                                        var(--lokentra-clay-400)); }
/* Section editor fields, so a field being AI-adjacent is visible on focus. */
.lokentra-ai-input {
    border: 1px solid var(--lokentra-line-strong);
    border-radius: 0.375rem;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.lokentra-ai-input:focus {
    outline: none;
    border-color: var(--lokentra-clay-500);
    box-shadow: 0 0 0 3px rgba(237, 148, 98, 0.28);
}

/* ----------------------------------------------------------------------------
   6. Keyboard focus
   ---------------------------------------------------------------------------
   The ported shell relies on Tailwind's `focus:` utilities, which are applied
   per element and were missing from most of the console's links and buttons —
   so keyboard navigation had no visible position in many places. One
   `:focus-visible` rule covers everything. `:focus-visible` (not `:focus`)
   means mouse clicks do not draw a ring.

   Scoped to a state rather than to elements, so it does not outrank the
   Tailwind utilities on any element's resting appearance.
   ------------------------------------------------------------------------- */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible,
[role="button"]:focus-visible {
    outline: 2px solid var(--lokentra-blue-600);
    outline-offset: 2px;
}
/* On the dark chrome an ink-coloured ring would vanish. */
#sidebar a:focus-visible,
#sidebar button:focus-visible,
#main-footer a:focus-visible,
.lokentra-hero a:focus-visible {
    outline-color: var(--lokentra-clay-300);
}

/* ----------------------------------------------------------------------------
   7. Top bar
   ------------------------------------------------------------------------- */
#grms-topbar {
    background: #fff;
    border-bottom: 1px solid var(--lokentra-line);
    box-shadow: 0 1px 2px rgba(6, 27, 44, 0.04),
                0 8px 24px -12px rgba(6, 27, 44, 0.12);
}
/* A three-pixel brand rule across the top: the seal's two colours in the order
   they appear in it, teal disc then clay hand. This is the console's signature
   and the reason the header reads as Lokentra's at a glance. */
#grms-topbar::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 3px;
    background: linear-gradient(90deg,
                    var(--lokentra-blue-700) 0%,
                    var(--lokentra-blue-500) 42%,
                    var(--lokentra-clay-500) 100%);
}

/* Identity chip beside the user's name. Replaces the arbitrary-value gradient
   `bg-gradient-to-br from-[#205493] to-[#3B82A0]`. */
.lokentra-avatar {
    background-image: linear-gradient(135deg,
                          var(--lokentra-blue-500) 0%,
                          var(--lokentra-blue-800) 100%);
    color: #fff;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.22);
}

#grms-topbar .hamburger-line { background-color: var(--lokentra-ink-2); }
#hamburger-btn:hover .hamburger-line { background-color: var(--lokentra-blue-600); }

/* ----------------------------------------------------------------------------
   8. Sidebar — the night sky
   ---------------------------------------------------------------------------
   The seal's centre is a teal disc holding stars and a crescent moon inside a
   ring of feathers. The sidebar is that disc: a deep teal gradient with a
   sparse star field scattered over it.

   The stars are eleven hand-placed radial-gradients rather than an image, so
   there is no extra request and nothing to keep in sync with the logo file.
   They are kept faint (0.30-0.55 alpha, 1-2px) — enough to notice, not enough
   to compete with the nav labels sitting on top of them.

   `background-attachment` is left at its default. For a scrollable element
   that anchors the background to the element's own box, so the sky stays put
   while a long nav list scrolls over it.
   ------------------------------------------------------------------------- */
#sidebar {
    background:
        radial-gradient(1.5px 1.5px at 18% 9%,  rgba(255,255,255,0.55), transparent 100%),
        radial-gradient(1px 1px     at 74% 6%,  rgba(255,255,255,0.45), transparent 100%),
        radial-gradient(1px 1px     at 41% 17%, rgba(255,255,255,0.35), transparent 100%),
        radial-gradient(1.5px 1.5px at 88% 22%, rgba(255,255,255,0.40), transparent 100%),
        radial-gradient(1px 1px     at 12% 31%, rgba(255,255,255,0.30), transparent 100%),
        radial-gradient(1px 1px     at 63% 38%, rgba(255,255,255,0.38), transparent 100%),
        radial-gradient(1.5px 1.5px at 29% 52%, rgba(255,255,255,0.34), transparent 100%),
        radial-gradient(1px 1px     at 82% 61%, rgba(255,255,255,0.30), transparent 100%),
        radial-gradient(1px 1px     at 47% 74%, rgba(255,255,255,0.32), transparent 100%),
        radial-gradient(1.5px 1.5px at 21% 86%, rgba(255,255,255,0.30), transparent 100%),
        radial-gradient(1px 1px     at 71% 93%, rgba(255,255,255,0.28), transparent 100%),
        var(--lokentra-sky);
    border-right: 1px solid rgba(255, 255, 255, 0.06);
    box-shadow: 2px 0 20px -8px rgba(6, 27, 44, 0.35);
}

/* The console card at the top of the rail. */
#sidebar .sidebar-header-inner {
    background: rgba(255, 255, 255, 0.09) !important;
    border: 1px solid rgba(255, 255, 255, 0.10);
}
#sidebar .sidebar-header-inner h2 { letter-spacing: 0.02em; }
/* The ported markup sets this subtitle to `text-white/40`, which is 2.4:1 on
   the sky and effectively illegible. */
#sidebar .sidebar-header-text p { color: rgba(255, 255, 255, 0.62) !important; }

/* Section label above the nav list. */
#sidebar .grms-nav-section { color: rgba(210, 244, 239, 0.55) !important; }

/* --- Nav items -----------------------------------------------------------
   The ported markup paints inactive items `text-white/70` (5.5:1 on the sky)
   and the active one `bg-white/15`, which is a weak "you are here" — on a
   glance the active row is barely distinguishable from a hover.

   Here the resting label is teal-100 at 8.7:1, and the active row gets a
   three-pixel clay rail on its leading edge. Clay is the seal's hand colour
   and appears nowhere else in the rail, so the current page is unmistakable.
   The rail is an inset box-shadow rather than a border so it does not shift
   the label by 3px when a row becomes active.
   --------------------------------------------------------------------- */
#sidebar .menu-parent,
#sidebar .menu-item {
    color: var(--lokentra-blue-100) !important;
    border-radius: 0.5rem;
    transition: background-color 0.18s ease, color 0.18s ease,
                box-shadow 0.18s ease;
}
#sidebar .menu-parent > span > i,
#sidebar .menu-item > span > i {
    color: rgba(166, 233, 225, 0.85);
    transition: color 0.18s ease;
}
#sidebar .menu-parent:hover,
#sidebar .menu-item:hover {
    background-color: rgba(255, 255, 255, 0.10) !important;
    color: #fff !important;
}
#sidebar .menu-parent:hover > span > i,
#sidebar .menu-item:hover > span > i { color: #fff; }

/* Active row. `aria-current="page"` is the honest hook — the ported templates
   already set it alongside the `bg-white/15` utility, so styling the attribute
   keeps CSS and assistive technology telling the same story. `.active` is
   matched too because the sidebar script adds it at runtime. */
#sidebar [aria-current="page"].menu-parent,
#sidebar [aria-current="page"].menu-item,
#sidebar a.active,
#sidebar button.active,
#sidebar a.bg-primary-600,
#sidebar button.bg-primary-600 {
    background-color: rgba(255, 255, 255, 0.16) !important;
    color: #fff !important;
    font-weight: 600;
    box-shadow: inset 3px 0 0 0 var(--lokentra-clay-400);
}
#sidebar [aria-current="page"] > span > i,
#sidebar a.active i,
#sidebar button.active i { color: var(--lokentra-clay-300) !important; }

/* Tooltip shown when the rail is minimized. */
#sidebar.sidebar-minimized .menu-item:hover::after,
#sidebar.sidebar-minimized .menu-parent:hover::after {
    background-color: var(--lokentra-blue-950);
    box-shadow: 0 6px 18px -4px rgba(6, 27, 44, 0.5);
    border: 1px solid rgba(255, 255, 255, 0.12);
    font-weight: 500;
}

/* The rail's own scrollbar, so a long nav list does not put a light-grey
   Tailwind scrollbar over the night sky. */
#sidebar { scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.22) transparent; }
#sidebar::-webkit-scrollbar { width: 8px; }
#sidebar::-webkit-scrollbar-track { background: transparent; }
#sidebar::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.18);
    border-radius: 9999px;
    border: 2px solid transparent;
    background-clip: content-box;
}
#sidebar::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.32);
                                          background-clip: content-box; }

/* ----------------------------------------------------------------------------
   9. Footer
   ---------------------------------------------------------------------------
   Same sky as the sidebar, angled the other way so the two surfaces read as
   one material rather than two flat panels meeting at a corner.
   ------------------------------------------------------------------------- */
#main-footer {
    background: linear-gradient(105deg,
                    var(--lokentra-blue-950) 0%,
                    #10534E 55%,
                    var(--lokentra-blue-700) 100%);
    border-top: 3px solid transparent;
    border-image: linear-gradient(90deg,
                      var(--lokentra-clay-500) 0%,
                      var(--lokentra-blue-500) 58%,
                      var(--lokentra-blue-700) 100%) 1;
}
#main-footer .lokentra-rule {
    display: inline-block;
    width: 2rem;
    height: 2px;
    background: var(--lokentra-clay-400);
    margin-right: 0.5rem;
    border-radius: 2px;
}
/* The ported markup paints footer body copy `text-gray-300`, a cool grey that
   sits oddly on teal. */
#main-footer .text-gray-300 { color: rgba(226, 240, 238, 0.80) !important; }

/* ----------------------------------------------------------------------------
   10. Page canvas and content surfaces
   ------------------------------------------------------------------------- */
body { background-color: var(--lokentra-canvas); color: var(--lokentra-ink); }
/* Tailwind's `bg-gray-50` on <body> is a cool grey; warm it to match the seal.
   Scoped to body so page-level grey panels keep their own value. */
body.bg-gray-50, body.bg-gray-100 { background-color: var(--lokentra-canvas); }

/* Cards. The ported `.grms-card` already picks up the warmed border and shadow
   through the token remap above; this adds the hover lift for the ones that
   are links, and a tighter radius that matches the inputs and buttons. */
.grms-card, .lokentra-card {
    background: var(--lokentra-surface);
    border: 1px solid var(--lokentra-line);
    border-radius: 0.75rem;
    box-shadow: var(--lokentra-shadow-sm);
}

/* KPI tiles: the accent edge becomes a soft vertical gradient rather than a
   flat 4px bar, and the tile tints on hover. */
.grms-metric { border-radius: 0.75rem; }
.grms-metric--primary { border-left-color: var(--lokentra-blue-600); }
a.grms-metric:hover {
    background: var(--lokentra-blue-50);
    border-color: var(--lokentra-blue-200);
}
.grms-metric__value { color: var(--lokentra-ink); letter-spacing: -0.02em; }

/* Section headings. The ported pages use a bare `text-2xl font-bold`; tighten
   the tracking so the display size stops looking loose beside Inter's body
   text. Scoped to the page wrapper so the shell's own headings are untouched. */
.grms-page h1 { letter-spacing: -0.021em; color: var(--lokentra-ink); }
.grms-page h2, .grms-page h3 { letter-spacing: -0.011em; color: var(--lokentra-ink); }

/* --- Page header ---------------------------------------------------------
   Nine pages had four different header shapes between them: some put the
   "back" link above the title, some floated it top-right beside the actions,
   some had a description line and some did not, and the icon-to-title gap was
   set three different ways. None of that was a decision — it accumulated.

   One shape: breadcrumb, then title and actions on a shared baseline row,
   then an optional one-line description. The description is capped at 70ch
   because a full-bleed 90rem console stretches a sentence past the point where
   the eye tracks back to the next line reliably.
   --------------------------------------------------------------------- */
.lokentra-head { margin-bottom: 1.5rem; }
.lokentra-head__crumb {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font-size: 0.8125rem;
    color: var(--lokentra-ink-3);
    text-decoration: none;
    margin-bottom: 0.5rem;
    transition: color 0.15s ease;
}
.lokentra-head__crumb:hover { color: var(--lokentra-blue-700); }
.lokentra-head__bar {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1.5rem;
    flex-wrap: wrap;
}
.lokentra-head__title {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    font-size: 1.5rem;
    font-weight: 700;
    letter-spacing: -0.021em;
    color: var(--lokentra-ink);
    margin: 0;
    min-width: 0;
}
/* The title's leading icon. Sized down from the inherited inline `mr-2` glyph,
   which rendered at the full 1.5rem of the heading and competed with the words. */
.lokentra-head__title > i {
    font-size: 1.0625rem;
    color: var(--lokentra-blue-600);
    flex-shrink: 0;
}
/* The "— 843 total" style count that several pages append to the title. */
.lokentra-head__count {
    font-size: 0.875rem;
    font-weight: 400;
    color: var(--lokentra-ink-3);
    white-space: nowrap;
}
.lokentra-head__sub {
    font-size: 0.875rem;
    color: var(--lokentra-ink-3);
    margin-top: 0.375rem;
    max-width: 70ch;
    line-height: 1.5;
}
.lokentra-head__actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-shrink: 0;
}

/* --- Toolbar -------------------------------------------------------------
   The filter/search strip above a table. The ported pages wrapped these in a
   full `.grms-card`, so a row of controls carried the same white panel and
   shadow as a content section and read as a piece of content rather than
   chrome.
   --------------------------------------------------------------------- */
.lokentra-toolbar {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    padding: 0.625rem 0.75rem;
    background: var(--lokentra-surface-alt);
    border: 1px solid var(--lokentra-line);
    border-radius: 0.625rem;
}

/* --- Empty state --------------------------------------------------------- */
.lokentra-empty {
    text-align: center;
    padding: 2.5rem 1.5rem;
    color: var(--lokentra-ink-3);
}
.lokentra-empty__icon {
    font-size: 1.75rem;
    color: var(--lokentra-blue-200);
    margin-bottom: 0.75rem;
}
.lokentra-empty__title {
    font-size: 0.9375rem;
    font-weight: 600;
    color: var(--lokentra-ink);
    margin-bottom: 0.375rem;
}
.lokentra-empty__body { font-size: 0.8125rem; max-width: 46ch; margin: 0 auto; line-height: 1.55; }

/* --- Tables ------------------------------------------------------------
   The console's eight tables had four different header treatments between them
   (`bg-gray-800 text-white`, `bg-gray-50 text-gray-500 uppercase`, `bg-gray-50
   border-b`, and one bare `<thead>`). `.lokentra-table` is one treatment applied
   to all of them: a tinted brand header in micro-caps, hairline row rules, and
   a teal row hover so the eye can track across a wide row.
   ------------------------------------------------------------------- */
.lokentra-table { width: 100%; border-collapse: separate; border-spacing: 0; }
.lokentra-table thead th {
    background: var(--lokentra-blue-50);
    color: var(--lokentra-blue-800);
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    text-align: left;
    padding: 0.625rem 0.75rem;
    border-bottom: 1px solid var(--lokentra-blue-200);
    white-space: nowrap;
    /* NOT sticky. A sticky header looks like the obvious win on an 843-row
       grants table, and it is wrong here: `main` carries `overflow-x-hidden`,
       which computes overflow-y to `auto` and makes main itself the nearest
       scrolling ancestor. `top` then offsets from main's scrollport rather than
       the viewport, so the header rendered 4.5rem down the table — parked on
       top of the first data row while the page sat unscrolled. Verified in the
       browser. Leave this alone unless main stops being a scroll container. */
}
.lokentra-table thead th a { color: inherit; text-decoration: none; }
.lokentra-table thead th:first-child { border-top-left-radius: 0.6rem; }
.lokentra-table thead th:last-child  { border-top-right-radius: 0.6rem; }
.lokentra-table tbody td {
    padding: 0.625rem 0.75rem;
    border-bottom: 1px solid var(--lokentra-line);
    color: var(--lokentra-ink-2);
    vertical-align: top;
}
.lokentra-table tbody tr:last-child td { border-bottom: 0; }
.lokentra-table tbody tr { transition: background-color 0.12s ease; }
.lokentra-table tbody tr:hover { background: var(--lokentra-blue-50); }
/* Numeric columns line up when they share a tabular figure width. */
.lokentra-table .num { font-variant-numeric: tabular-nums; text-align: right; }

/* --- Column hierarchy ---------------------------------------------------
   Every cell in the ported tables was the same weight and colour, so a row
   had no anchor and the eye had nothing to track along. In practice one column
   is the row's identity (the grant or award title) and the rest are attributes
   of it.

   `--id` is that anchor: ink, medium weight, and allowed to wrap because a
   grant title is the one thing worth two lines. `--attr` is everything else:
   muted, and NOT allowed to wrap.

   The no-wrap is the density fix, and it is worth more than it sounds. Real
   Lokentra data has one funder ("Texas Education Agency") repeated down the whole
   page; at 25 rows it wrapped to two lines in every single one, doubling the
   height of the entire table for a string that never changes. Long values
   ellipsize with the full text still available on hover.
   ------------------------------------------------------------------- */
.lokentra-table td.lokentra-cell--id {
    color: var(--lokentra-ink);
    font-weight: 500;
}
.lokentra-table td.lokentra-cell--attr {
    color: var(--lokentra-ink-3);
    white-space: nowrap;
    max-width: 14rem;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* A date or a figure must not wrap: the grants table was breaking every
   deadline across two lines ("2014-12-" / "16"), which doubled the row height
   of all 25 rows and made the column unscannable. `.tabular-nums` is already on
   exactly the cells that hold figures, so it is the honest hook for this. */
.lokentra-table td.tabular-nums { white-space: nowrap; }

/* --- Tabs ---------------------------------------------------------------
   The notifications page toggled two arbitrary-hex classes plus two greys on
   every tab from JavaScript (four classList calls per tab per switch). One
   `.lokentra-tab-on` class does the same job, so the stylesheet and the script
   agree on a single name for "this tab is selected".
   ------------------------------------------------------------------- */
.lokentra-tab {
    border-bottom-color: transparent;
    color: var(--lokentra-ink-3);
    background: none;
    cursor: pointer;
    transition: color 0.15s ease, border-color 0.15s ease;
}
.lokentra-tab:hover { color: var(--lokentra-ink); }
.lokentra-tab-on {
    border-bottom-color: var(--lokentra-blue-600);
    color: var(--lokentra-blue-800);
    font-weight: 600;
}

/* --- Badges, chips and KPI accents --------------------------------------
   Success, warning and danger keep their hues — those three carry real meaning
   in a grants console and must stay a traffic light. The `info` and `neutral`
   tones are the ones that were merely "a different colour", and `info`'s blue
   is what made every workflow-stage pill (grms_tags.py resolves a stage to
   `grms-badge--info`) read as a second brand.

   Both fold into the teal family, which leaves the console with one accent in
   two weights plus a traffic light.
   ------------------------------------------------------------------- */
.grms-badge--info,
.grms-chip--info {
    background: var(--lokentra-blue-50);
    color: var(--lokentra-blue-800);
}
.grms-badge--neutral,
.grms-chip--neutral {
    background: var(--lokentra-surface-alt);
    color: var(--lokentra-ink-2);
}

/* --- Traffic-light status surfaces --------------------------------------
   WHY THESE EXIST. `grant_detail.html` and `notifications.html` coloured
   their pre-award, escalation and readiness surfaces with arbitrary Tailwind
   values - `bg-[#E3F3EC] text-[#1C7A54]` for a PURSUE pill, `bg-[#FBE6E4]
   text-[#A8352B]` for a DECLINE one, and so on. The compiled bundle in
   `static/css/tailwind.compiled.css` is PURGED to the classes the lokentra
   monorepo used, so none of those rules were ever generated: fifteen distinct
   class forms produced no style at all, and an "approved" pill, a "declined"
   pill and a "deferred" pill rendered as the same unstyled grey text. See
   `app/tests/test_no_dead_arbitrary_classes.py`, which fails the build if a
   template ever reaches for an arbitrary value the bundle does not contain.

   The replacements deliberately keep the hues. Green/amber/red are load-bearing
   here - green is awarded or approved, amber is a deadline drawing near or a
   soft warning, red is overdue or blocking - so this block re-expresses the
   dead values through `--success-*`, `--warning-*` and `--danger-*`, the same
   tokens `.grms-badge--success` and friends already use. No new hue is
   introduced and the teal/clay brand pair is untouched.

   Every pairing below is measured, not eyeballed, WCAG 2.1 against its own
   ground. Three of the values being replaced did not reach AA - #B46B12 on
   #FDF0DC was 3.70:1, and a white count on #D97706 was 3.19:1 - so the solid
   amber steps down to --warning-700 rather than keeping --warning-600.
   ------------------------------------------------------------------- */

/* Solid count badges, for the notification tab counters. The soft
   `.grms-badge--*` pills above are for a status word; these are for a number
   that has to be seen from across the tab strip. */
.grms-badge--danger-solid {
    background: var(--danger-600);    /* white on #DC2626 - 4.83:1 */
    color: #fff;
}
.grms-badge--warning-solid {
    background: var(--warning-700);   /* white on #B45309 - 5.02:1.
                                         --warning-600 would be 3.19:1 */
    color: #fff;
}

/* Bordered inline notes - the pre-award blocker and warning callouts. Used as
   `class="lokentra-note lokentra-note--danger"`: the base supplies the 1px border and
   the geometry, the modifier repoints all three colours. */
.lokentra-note--warning {
    background: var(--warning-50);
    border-color: var(--warning-200);
    color: var(--warning-800);        /* #92400E on #FFFBEB - 6.84:1 */
}
.lokentra-note--danger {
    background: var(--danger-50);
    border-color: var(--danger-200);
    color: var(--danger-800);         /* #991B1B on #FEF2F2 - 7.60:1 */
}

/* Row tint for a blocking risk in the pre-award table. The hover rule is not
   optional: `.lokentra-table tbody tr:hover` is unlayered and would otherwise
   repaint a blocking row teal the moment the pointer crossed it, which is the
   one row whose colour is carrying information. */
.lokentra-row--danger { background: var(--danger-50); }
.lokentra-table tbody tr.lokentra-row--danger:hover { background: var(--danger-100); }

/* Traffic-light text on a light ground, the counterpart to `.lokentra-fg`. The
   800 steps are used rather than the 600s because these carry real words at
   11-12px: --danger-800 is 8.31:1 on white and --warning-800 is 7.09:1, where
   the #D97706 and #DC2626 they replace are 3.19:1 and 4.83:1.

   The amber one matters most. Unlike the fifteen dead classes above, the raw
   --warning-600 hex WAS alive in the compiled bundle - it rendered exactly as
   written, and at 3.19:1 it failed AA in a way nothing flagged. Its three word
   sites in `notifications.html` (the day-count under each date in the Upcoming
   and Needs-attention tables, and the "nobody" holder) moved here on 2026-08-25;
   `app/tests/test_notification_contrast.py` fails the build if they slide back.
   Icon-only #D97706 on that page is deliberately untouched - AA governs text.

   These must clear AA on BOTH grounds a table row takes: white, and the
   --lokentra-blue-50 that `.lokentra-table tbody tr:hover` paints underneath. */
.lokentra-fg--danger  { color: var(--danger-800); }
.lokentra-fg--warning { color: var(--warning-800); }
/* A quiet icon button that turns danger on hover (the "Delete group" trash).
   --danger-600 is 4.83:1 on white and is the hue the destructive verbs use. */
.lokentra-fg--danger-hover:hover { color: var(--danger-600); }

/* KPI tile accent edges. `--info` becomes the bright sampled brand teal, so an
   "info" tile still reads as distinct from a deep-teal "primary" tile without
   reaching for a different hue. `--accent` is new: the seal's clay, available
   for a tile that needs to stand apart from both. */
.grms-metric--info   { border-left-color: var(--lokentra-blue-500); }
.grms-metric--accent { border-left-color: var(--lokentra-clay-500); }

/* Pagination active page. */
.grms-page-active {
    background: var(--lokentra-blue-700);
    border-color: var(--lokentra-blue-700);
}

/* ----------------------------------------------------------------------------
   11. Login page
   ---------------------------------------------------------------------------
   The sign-in screen is the only page most staff see before they are inside
   the console, so it carries the brand hardest: a two-panel card with the
   night sky and the wordmark on the left and the form on the right. Below 56rem
   the panels stack and the sky becomes a short banner.

   This is a whole PAGE, not a card on nothing. `.lokentra-login` is the column
   (background, card, footer), `.lokentra-login__shell` is the width both the
   card and the footer share, `.lokentra-hero` is the sky panel and
   `.lokentra-login__form` the sign-in panel. All of it is used by exactly one
   template, app/login.html.
   ------------------------------------------------------------------------- */
/* THE PAGE, not just the card. The card used to be centred in an otherwise
   empty viewport, which read as an unfinished page rather than a calm one.
   Three things fix that and none of them invents a claim about the product:
   a page that is a column (card + a quiet footer line) so the card is anchored
   to something; a background with actual texture instead of two washes so
   faint they were invisible; and a left panel given enough content to justify
   its own width. */
.lokentra-login {
    min-height: 100vh;
    /* Mobile browsers shrink the visual viewport as their toolbar retracts;
       100vh keeps measuring the tall value and leaves a scroll stub. Ignored
       where dvh is unknown, so the line above stays the floor. */
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 2.5rem 1rem;
    /* A dot grid at 5.5% of the darkest blue -- roughly #EFF1F1 against the
       canvas, so it is texture rather than pattern -- under two washes that
       place the brand blue behind the sky panel and the clay accent opposite
       it. The dots are the layer that stops the empty margins reading as a
       rendering failure. */
    background:
        radial-gradient(circle at 1px 1px,
                        rgba(8, 40, 64, 0.055) 1px, transparent 1.6px)
            0 0 / 24px 24px,
        radial-gradient(58rem 40rem at 14% 4%, var(--lokentra-blue-100), transparent 68%),
        radial-gradient(46rem 32rem at 90% 98%, var(--lokentra-clay-50), transparent 70%),
        var(--lokentra-canvas);
}

/* The column the card and the footer share, so both are the same width and
   the footer reads as part of the same object rather than loose page text. */
.lokentra-login__shell {
    width: 100%;
    max-width: 64rem;
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    /* `margin: auto`, NOT `justify-content: center` on the parent. Centring a
       flex column that overflows pushes its first child above the top of the
       scrollport, where nothing can scroll to it -- which is exactly what a
       short viewport plus an error message produces here. Auto margins collapse
       to zero instead of going negative. */
    margin: auto;
}

.lokentra-login__card {
    width: 100%;
    display: grid;
    grid-template-columns: 1fr;
    background: var(--lokentra-surface);
    border: 1px solid var(--lokentra-line);
    border-radius: 1rem;
    box-shadow: var(--lokentra-shadow-lg);
    overflow: hidden;
}
@media (min-width: 56rem) {
    .lokentra-login__card {
        grid-template-columns: 1.02fr 1fr;
        /* Floor rather than a fixed height: the card must still grow when the
           messages block appears above the form. Tuned against the hero's own
           content -- taller and the space-between hero opens a visible hole
           between the wordmark and the lede. */
        min-height: 29rem;
    }
}

/* The sky panel. Same star field as the sidebar, scaled up. */
.lokentra-hero {
    position: relative;
    padding: 2.75rem 2.25rem;
    color: #fff;
    background:
        radial-gradient(2px 2px at 22% 14%, rgba(255,255,255,0.65), transparent 100%),
        radial-gradient(1.5px 1.5px at 68% 9%, rgba(255,255,255,0.5), transparent 100%),
        radial-gradient(1.5px 1.5px at 86% 28%, rgba(255,255,255,0.45), transparent 100%),
        radial-gradient(2px 2px at 14% 44%, rgba(255,255,255,0.4), transparent 100%),
        radial-gradient(1.5px 1.5px at 54% 33%, rgba(255,255,255,0.38), transparent 100%),
        radial-gradient(1.5px 1.5px at 78% 58%, rgba(255,255,255,0.34), transparent 100%),
        radial-gradient(2px 2px at 32% 72%, rgba(255,255,255,0.36), transparent 100%),
        radial-gradient(1.5px 1.5px at 66% 86%, rgba(255,255,255,0.3), transparent 100%),
        var(--lokentra-sky);
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: 2rem;
}
/* A clay rule along the panel's inner edge, echoing the topbar's brand rule. */
.lokentra-hero::after {
    content: "";
    position: absolute;
    inset: auto 0 0 0;
    height: 3px;
    background: linear-gradient(90deg, var(--lokentra-clay-500), var(--lokentra-blue-500));
}
@media (min-width: 56rem) {
    .lokentra-hero::after { inset: 0 0 0 auto; width: 3px; height: auto;
                         background: linear-gradient(180deg,
                                         var(--lokentra-clay-500),
                                         var(--lokentra-blue-500)); }
}
.lokentra-hero__mark {
    /* The wordmark is drawn for dark grounds, so it sits directly on the sky --
       no white disc. A disc was right for a round seal and wrong for a
       horizontal wordmark, which it cropped to a cramped circle. */
    width: 13rem; height: auto; max-width: 78%;
    object-fit: contain;
}

.lokentra-hero__seal {
    width: 5.5rem; height: 5.5rem;
    object-fit: contain;
    /* Retained for any seal-shaped mark drawn for a light ground: a white disc
       behind it keeps the artwork as drawn rather than recolouring it. The
       login hero uses .lokentra-hero__mark instead. */
    background: #fff;
    border-radius: 9999px;
    padding: 0.5rem;
    box-shadow: 0 8px 24px -6px rgba(6, 27, 44, 0.55),
                0 0 0 1px rgba(255, 255, 255, 0.25);
}
.lokentra-hero__title {
    font-size: 1.5rem;
    font-weight: 700;
    line-height: 1.2;
    letter-spacing: -0.02em;
    color: #fff;
}
.lokentra-hero__sub  { color: var(--lokentra-blue-100); font-size: 0.9375rem;
                    line-height: 1.55; margin-top: 0.5rem; }
.lokentra-hero__ojibwe {
    color: var(--lokentra-clay-300);
    font-size: 0.75rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    line-height: 1.5;
}

/* CONTRAST. Every colour below is measured against #0C4C6C, which is teal-700
   and the LIGHTEST stop of --lokentra-sky, so these are worst-case figures for
   the panel; further down the gradient each one improves. */
.lokentra-hero__lede {
    color: var(--lokentra-blue-100);    /* 7.79:1 on teal-700 */
    font-size: 0.9375rem;
    line-height: 1.6;
    max-width: 28rem;
    margin: 0;
}
/* A plain inventory of what is behind the sign-in, drawn from the console's own
   navigation. It is a list of sections that exist, deliberately not a list of
   benefits -- this is a staff tool and the person reading it already has an
   account. */
.lokentra-hero__list {
    list-style: none;
    margin: 1.375rem 0 0;
    padding: 0;
    display: grid;
    gap: 0.6875rem;
}
.lokentra-hero__list li {
    display: flex;
    align-items: flex-start;
    gap: 0.6875rem;
    color: var(--lokentra-blue-200);    /* 6.29:1 on teal-700 */
    font-size: 0.875rem;
    line-height: 1.45;
}
.lokentra-hero__list i {
    flex: none;
    width: 1.0625rem;
    margin-top: 0.1875rem;
    font-size: 0.8125rem;
    text-align: center;
    color: var(--lokentra-clay-300);    /* 5.33:1 on teal-700 */
}

/* ---- The form panel ---- */
.lokentra-login__form {
    padding: 2.75rem 2.25rem;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

/* Password field plus its reveal button. The padding-right lives HERE and not
   on .lokentra-input, which owns appearance only -- see the note on that class.
   Scoping it to this wrapper keeps every other field in the console untouched. */
.lokentra-login__field { position: relative; display: block; }
.lokentra-login__field .lokentra-input { padding-right: 2.75rem; }
.lokentra-login__reveal {
    position: absolute;
    top: 0; bottom: 0; right: 0;
    width: 2.75rem;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
    border-radius: 0 0.5rem 0.5rem 0;
    color: var(--lokentra-ink-3);       /* 5.05:1 on white */
    transition: color 0.15s ease;
}
.lokentra-login__reveal:hover { color: var(--lokentra-blue-700); }

/* A fixed slot so the layout does not jump when the Caps Lock warning appears,
   and a live region that is always in the DOM so the warning is announced. */
.lokentra-login__hint { min-height: 1.125rem; margin-top: 0.375rem; }
.lokentra-login__caps {
    display: none;
    align-items: center;
    gap: 0.4375rem;
    margin: 0;
    font-size: 0.8125rem;
    line-height: 1.25rem;
    color: var(--warning-800);          /* #92400E on white - 7.09:1 */
}
.lokentra-login__caps.is-on { display: flex; }

.lokentra-login__help {
    margin: 1.75rem 0 0;
    font-size: 0.8125rem;
    line-height: 1.55;
    color: var(--lokentra-ink-3);       /* 5.05:1 on white. This line used to be
                                           ink-4, which is 2.81:1 and failed AA. */
}

/* The footer that anchors the card. Ink-2 rather than the quieter ink-3
   because it sits on the washed background, not on white: ink-3 is only
   4.25:1 over the blue-100 wash, while ink-2 holds 8.3:1 there and 9.2:1 on
   the bare canvas. */
.lokentra-login__foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 0.375rem 1.125rem;
    margin: 0;
    font-size: 0.8125rem;
    line-height: 1.5;
    text-align: center;
    color: var(--lokentra-ink-2);
}
.lokentra-login__foot span { display: inline-flex; align-items: center; gap: 0.4375rem; }
.lokentra-login__foot i { color: var(--lokentra-blue-600); font-size: 0.75rem; }
.lokentra-login__foot a { color: var(--lokentra-blue-700); text-decoration: none; }
.lokentra-login__foot a:hover { text-decoration: underline; text-underline-offset: 2px; }

/* ---- Narrow screens ----------------------------------------------------
   Below 56rem the panels stack and the sky becomes a banner. The section list
   is the one thing dropped: on a 390px screen it pushes the password field off
   the first screenful, and its job -- filling a column that is only wide on a
   desktop -- does not exist here. The lede stays, because "what am I signing
   in to" is worth four lines anywhere. */
@media (max-width: 55.999rem) {
    .lokentra-login       { padding: 1.25rem 0.875rem; gap: 1rem; }
    .lokentra-hero        { padding: 1.75rem 1.5rem; gap: 1.125rem; }
    .lokentra-hero__mark  { width: 11rem; }
    .lokentra-hero__title { font-size: 1.375rem; }
    .lokentra-hero__list  { display: none; }
    .lokentra-login__form { padding: 1.75rem 1.5rem; }
}

/* ----------------------------------------------------------------------------
   12. Loader
   ------------------------------------------------------------------------- */
#globalLoader .fa-circle-notch { color: var(--lokentra-blue-600); }
#globalLoader > div {
    border-radius: 0.875rem;
    box-shadow: var(--lokentra-shadow-lg);
    border: 1px solid var(--lokentra-line);
}

/* ----------------------------------------------------------------------------
   13. Reduced motion
   ---------------------------------------------------------------------------
   grms-console-monorepo.css already disables its entrance animations here.
   These are the transforms this sheet adds.
   ------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    .lokentra-btn, .lokentra-btn-brand, .lokentra-btn-ghost,
    #sidebar .menu-item, #sidebar .menu-parent,
    .lokentra-table tbody tr, .lokentra-link {
        transition: none;
    }
    .lokentra-btn:active, .lokentra-btn-brand:active { transform: none; }
}
