/* ============================================================================
   KobiiCraft Pass - minimalist dual-theme skin for the Vaultwarden web vault.

   Shipped INSIDE the fork (apps/web/src/css/kobiicraft-pass.css) and referenced
   from index.html via the webpackIgnore <link> upstream leaves dangling. That
   link is Vaultwarden's own operator hook, so this survives upstream merges:
   upstream has no file here to conflict with. The file is COPIED verbatim into
   the build (never compiled), so a CSS change needs no Angular rebuild.

   SCOPE: cosmetic only. Never vault data, crypto, session or auth flow.

   ---------------------------------------------------------------------------
   THEME MODEL - native, single source of truth
   ---------------------------------------------------------------------------
   Vaultwarden already owns a complete theme state machine. theme_head.js runs
   BEFORE paint and reads localStorage['global_theming_selection']
   ('"light"' | '"dark"' | '"system"'; unset or "system" -> prefers-color-scheme),
   then stamps <html class="theme_light"> or <html class="theme_dark">.

   This file therefore introduces NO key, NO data-attribute and NO persistence
   logic - it only supplies token sets keyed off those two native classes.
   js/kp-theme.js renders the sun/moon control and writes that SAME key, so the
   control and Settings -> Appearance -> Theme can never disagree.

   Earlier revisions FORCED a dark login shell over theme_light by painting
   containers transparent. That override is gone: re-pointing the background
   tokens themselves serves both themes and stops chasing every container
   upstream adds.

   ---------------------------------------------------------------------------
   SELECTOR POLICY
   ---------------------------------------------------------------------------
   Attribute / content hooks only, taken from the REAL rendered DOM (render
   service dump). Never hashed Tailwind classes, never guessed.

   ---------------------------------------------------------------------------
   CONTRAST LAW (learned the hard way, twice - do not re-earn)
   ---------------------------------------------------------------------------
   Never set a foreground colour without setting its background in the SAME rule.
     - a navy --color-primary-* override once made the primary CTA INVISIBLE;
     - a global `span { color: cream }` once made light secondary buttons
       UNREADABLE (cream on white).
   Monochrome raises the stakes: with brand hue removed there is no accidental
   colour left to rescue a broken pair. Every rule below declares fg AND bg.

   Token derivation + deviations:
   vault/docs/KOBIICRAFT_PASS_MINIMALIST_TOKEN_MAP_2026-07-26.md
   ============================================================================ */

/* ---- 1. KP token sets, keyed off the NATIVE theme class ---------------------
   Values read verbatim from the Owner's reference HTML (Downloads/
   kobicraft-dashboard.html and -dark.html), not paraphrased.
   Vaultwarden's own --color-* properties are space-separated RGB triplets
   (Tailwind convention), consumed as rgb(var(--color-x)) - so each override
   below is the triplet form of the hex in the comment. */

html.theme_dark {
  --kp-bg:         #0a0a0a;
  --kp-bg-side:    #0f0f0f;
  --kp-bg-hover:   #1a1a1a;
  --kp-border:     #232323;
  --kp-border-mid: #262626;
  --kp-t1:         #ededed;
  --kp-t2:         #a1a1a1;
  --kp-t3:         #444444;

  /* Primary CTA: reference --btn-from / --btn-bdr. */
  --kp-cta-bg:       #fafafa;
  --kp-cta-bdr:      #fafafa;
  --kp-cta-fg:       #0a0a0a;
  --kp-cta-bg-hover: #ffffff;

  /* Secondary: ghost on the ground it sits on. */
  --kp-alt-bg:       transparent;
  --kp-alt-bdr:      #232323;
  --kp-alt-fg:       #ededed;
  --kp-alt-bg-hover: #1a1a1a;

  --kp-field-bg:  #0a0a0a;
  --kp-field-bdr: #3A3A3A;   /* was #262626 - invisible on #101010 */
  --kp-field-focus: #8A8A8A;

  /* logo.svg is a light mark: correct as-is on a dark ground. */
  --kp-logo-filter: none;
}

html.theme_light {
  --kp-bg:         #FFFFFF;
  --kp-bg-side:    #F7F7F7;
  --kp-bg-hover:   #F0F0F0;
  --kp-border:     #E5E5E5;
  --kp-border-mid: #D4D4D4;
  --kp-t1:         #111111;
  --kp-t2:         #6B6B6B;
  --kp-t3:         #A0A0A0;

  /* Primary CTA: near-black on white. Border MATCHES the fill deliberately -
     the reference's #D4D4D4 belongs to its light pill (.btn), which maps to the
     SECONDARY button below; a near-black button ringed in light grey reads as a
     rendering artefact. See token-map section 4. */
  --kp-cta-bg:       #111111;
  --kp-cta-bdr:      #111111;
  --kp-cta-fg:       #FFFFFF;
  --kp-cta-bg-hover: #2A2A2A;

  /* Secondary: the reference's light pill. */
  --kp-alt-bg:       #FAFAFA;
  --kp-alt-bdr:      #D4D4D4;
  --kp-alt-fg:       #111111;
  --kp-alt-bg-hover: #EBEBEB;

  --kp-field-bg:  #FFFFFF;
  --kp-field-bdr: #C4C4C4;   /* was #D4D4D4 = 1.48:1 on white, under the floor */
  --kp-field-focus: #111111;

  /* logo.svg is a light mark: invert to read on a white ground. */
  --kp-logo-filter: invert(1);
}

/* ---- 2. Re-point Vaultwarden's own ramp to the monochrome scale -------------
   Set on <body> so every descendant reading rgb(var(--color-background)) picks
   it up by inheritance. This covers BOTH surfaces (login shell and the
   authenticated interior) in one place - the previous split existed only to
   support forcing dark over light, which no longer happens. */
html.theme_dark body {
  --color-background:      10 10 10;    /* #0a0a0a  dash --bg */
  --color-background-alt:  15 15 15;    /* #0f0f0f  dash --bg-elev */
  --color-background-alt2: 26 26 26;    /* #1a1a1a  dash --bg-hover */
  --color-background-alt3: 35 35 35;    /* #232323  dash --border */
  --color-background-alt4: 15 15 15;    /* #0f0f0f  dash --bg-elev */
  --color-text-main:      237 237 237;  /* #ededed  dash --text */
  --color-text-muted:     161 161 161;  /* #a1a1a1  dash --text-dim, was #8A8A8A
                                           = dash --text-mut; stepped UP one
                                           tier because dash itself does this
                                           wherever muted text sits on a lit
                                           surface (4.5:1 floor) */
}

html.theme_light body {
  --color-background:      255 255 255; /* #FFFFFF */
  --color-background-alt:  247 247 247; /* #F7F7F7 */
  --color-background-alt2: 240 240 240; /* #F0F0F0 */
  --color-background-alt3: 229 229 229; /* #E5E5E5 */
  --color-background-alt4: 247 247 247; /* #F7F7F7 */
  --color-text-main:        17 17 17;   /* #111111 */
  --color-text-muted:      107 107 107; /* #6B6B6B */
}

/* Accent ramp -> neutral grey ladder, both themes. Bitwarden derives EVERY
   accent from these two scales, so neutralising them here removes #175DDC at
   the root instead of chasing each component that happens to use it. */
html.theme_dark body,
html.theme_light body {
  --color-brand-050: 245 245 245;  --color-primary-050: 245 245 245;
  --color-brand-100: 235 235 235;  --color-primary-100: 235 235 235;
  --color-brand-200: 212 212 212;  --color-primary-200: 212 212 212;
  --color-brand-300: 160 160 160;  --color-primary-300: 160 160 160;
  --color-brand-400: 138 138 138;  --color-primary-400: 138 138 138;
  --color-brand-500: 107 107 107;  --color-primary-500: 107 107 107;
  --color-brand-600:  46  46  46;  --color-primary-600:  46  46  46;
  --color-brand-700:  35  35  35;  --color-primary-700:  35  35  35;
  --color-brand-800:  30  30  30;  --color-primary-800:  30  30  30;
  --color-brand-900:  22  22  22;  --color-primary-900:  22  22  22;
  --color-brand-950:  12  12  12;  --color-primary-950:  12  12  12;
}

/* ---- 3. Ground -------------------------------------------------------------
   Flat, no gradient: in the reference the space IS the design. */
body.layout_frontend,
body:not(.layout_frontend) {
  background-color: var(--kp-bg) !important;
  background-image: none !important;
  color: var(--kp-t1) !important;
  font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, sans-serif !important;
  font-size: 14px !important;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* ---- 4. Typography ---------------------------------------------------------
   Inter is ALREADY bundled (fonts/inter.*.woff2) and already the app font, and
   the live CSP is `font-src 'self'` - so the minimalist type treatment is the
   REMOVAL of the previous Georgia serif display override, not a font addition.
   Zero extra network requests, nothing for the CSP to block. */
h1, h2, h3 {
  font-family: inherit !important;
  font-weight: 600 !important;
  letter-spacing: -0.3px !important;
  color: var(--kp-t1) !important;
}

/* ---- 5. Brand marks --------------------------------------------------------
   The nav mark is an INLINE <svg viewBox="0 0 290 60"> inside
   <bit-svg aria-label="KobiiCraft Pass logo">, not the images/logo.svg file -
   which is why replacing that file alone changed nothing on screen. Hide the
   inline art and paint our asset on the wrapper instead. */
body.layout_frontend bit-svg[aria-label="KobiiCraft Pass logo"] > svg,
body.layout_frontend svg[viewBox="0 0 290 60"] {
  display: none !important;
}

body.layout_frontend bit-svg[aria-label="KobiiCraft Pass logo"] {
  display: inline-block !important;
  width: 200px !important;
  height: 42px !important;
  background-image: url("/images/logo.svg") !important;
  background-repeat: no-repeat !important;
  background-position: left center !important;
  background-size: contain !important;
  filter: var(--kp-logo-filter) !important;
}

/* Centre mark above the heading: keep the slot, swap the art. */
body.layout_frontend svg[viewBox="0 8.33 80 66.67"] > * {
  display: none !important;
}
body.layout_frontend svg[viewBox="0 8.33 80 66.67"] {
  background-image: url("/images/icon-white.svg") !important;
  background-repeat: no-repeat !important;
  background-position: center !important;
  background-size: contain !important;
  filter: var(--kp-logo-filter) !important;
  opacity: .92 !important;
}

/* Bitwarden corner illustrations - stable content-based viewBoxes. The
   reference design is empty space, so these go in both themes. */
body.layout_frontend svg[viewBox="0 0 501 226"],
body.layout_frontend svg[viewBox="0 0 506 297"] {
  display: none !important;
}

/* Interior sidebar wordmark: a DIFFERENT inline svg (viewBox 0 0 800 200)
   inside <bit-nav-logo>, and NOT logo-white.svg, which the bundle never
   references. Hide it, paint our asset. */
body:not(.layout_frontend) bit-nav-logo svg[viewBox="0 0 800 200"] {
  display: none !important;
}
body:not(.layout_frontend) bit-nav-logo a {
  display: block !important;
  height: 34px !important;
  margin: 2px 4px !important;
  background-image: url("/images/logo.svg") !important;
  background-repeat: no-repeat !important;
  background-position: left center !important;
  background-size: contain !important;
  filter: var(--kp-logo-filter) !important;
}

/* ---- 6. Login form surface -------------------------------------------------
   Applied to the form container ONLY. An earlier revision also hit
   .tw-bg-background and bit-card, which cascaded onto nested controls and
   produced an input/checkbox overlap artefact. Narrowed deliberately. */
body.layout_frontend form {
  background-color: var(--kp-bg-side) !important;
  color: var(--kp-t1) !important;
  border: 1px solid var(--kp-border) !important;
  border-radius: 10px !important;
  box-shadow: none !important;
}

body.layout_frontend form label,
body.layout_frontend bit-label,
body.layout_frontend bit-form-field label {
  background-color: transparent !important;
  color: var(--kp-t1) !important;
  font-weight: 500 !important;
}

/* ---- 7. Fields: explicit pair ----------------------------------------------
   Hint text shown inside an empty field is deliberately left alone: browsers
   derive its colour from the input's own `color`, which IS set per theme here,
   so it stays legible in both without an extra rule. */
body input:not([type="checkbox"]):not([type="radio"]),
body select,
body textarea {
  background-color: var(--kp-field-bg) !important;
  color: var(--kp-t1) !important;
  border: 1px solid var(--kp-field-bdr) !important;
  border-radius: 8px !important;
}

body input:not([type="checkbox"]):not([type="radio"]):focus,
body select:focus,
body textarea:focus {
  background-color: var(--kp-field-bg) !important;
  color: var(--kp-t1) !important;
}

/* INSIDE bit-form-field the control must stay borderless: upstream ships it
   with `tw-border-none` and draws the field as a THREE-SEGMENT notched outline
   (left cap / label notch with border-t-0 / right cap) in an absolutely
   positioned overlay. The rule above used to put a 1px border AND a same-colour
   1px focus outline on the input itself, which produced three visible artefacts
   at once, all measured 2026-08-19:
     - a doubled 2px edge (border + outline, identical colour, adjacent)
     - a second, UN-notched rectangle inside the real one
     - its top edge landed at y=349 inside the label band 335-356, so the
       border struck through "Email address (required)"
   Section 23 restores the real outline instead. */
body bit-form-field input:not([type="checkbox"]):not([type="radio"]),
body bit-form-field select,
body bit-form-field textarea {
  background-color: transparent !important;
  border: 0 !important;
  border-radius: 0 !important;
  outline: none !important;
}

/* Chromium paints autofilled inputs with its OWN opaque background and text
   colour, which beats a plain background-color declaration - that is why the
   email field once rendered light grey on a dark form. The inset box-shadow is
   the only reliable repaint, and -webkit-text-fill-color is what actually
   controls autofilled text. Both halves of the pair, again. Themed via the
   token so light mode does not inherit a dark fill. */
body input:-webkit-autofill,
body input:-webkit-autofill:hover,
body input:-webkit-autofill:focus,
body input:-internal-autofill-selected {
  -webkit-text-fill-color: var(--kp-t1) !important;
  -webkit-box-shadow: 0 0 0 1000px var(--kp-field-bg) inset !important;
  box-shadow: 0 0 0 1000px var(--kp-field-bg) inset !important;
  caret-color: var(--kp-t1) !important;
}

/* bit-form-field wraps the control; without this the wrapper keeps its theme
   fill and shows through around the input. */
body bit-form-field,
body bit-form-field > div {
  background-color: transparent !important;
}

body .tw-text-muted,
body .tw-text-secondary,
body bit-hint {
  background-color: transparent !important;
  color: var(--kp-t2) !important;
}

/* ---- 8. PRIMARY button: fg AND bg, plus hover and disabled -----------------
   Applies to BOTH surfaces (login CTA and the interior "+ New"). The interior
   button reads its fill from a utility class whose token scope the ramp
   override alone did not reach, so the explicit !important pair is what
   actually wins - retained from the navy build, re-pointed monochrome. */
body button[buttontype="primary"],
body a[buttontype="primary"] {
  background-color: var(--kp-cta-bg) !important;
  border: 1px solid var(--kp-cta-bdr) !important;
  color: var(--kp-cta-fg) !important;
  border-radius: 8px !important;
  font-weight: 500 !important;
  letter-spacing: -0.01em !important;
}
body button[buttontype="primary"] *,
body a[buttontype="primary"] * {
  color: var(--kp-cta-fg) !important;
}
body button[buttontype="primary"]:hover:not(:disabled),
body a[buttontype="primary"]:hover:not(:disabled) {
  background-color: var(--kp-cta-bg-hover) !important;
  border-color: var(--kp-cta-bg-hover) !important;
  color: var(--kp-cta-fg) !important;
}
body button[buttontype="primary"]:disabled {
  background-color: var(--kp-bg-hover) !important;
  border-color: var(--kp-border-mid) !important;
  color: var(--kp-t3) !important;
}

/* ---- 9. SECONDARY buttons: explicit pair ----------------------------------- */
body button[bitbutton]:not([buttontype="primary"]),
body a[bitbutton]:not([buttontype="primary"]) {
  background-color: var(--kp-alt-bg) !important;
  border: 1px solid var(--kp-alt-bdr) !important;
  color: var(--kp-alt-fg) !important;
  border-radius: 8px !important;
}
body button[bitbutton]:not([buttontype="primary"]) *,
body a[bitbutton]:not([buttontype="primary"]) * {
  color: var(--kp-alt-fg) !important;
}
body button[bitbutton]:not([buttontype="primary"]):hover:not(:disabled),
body a[bitbutton]:not([buttontype="primary"]):hover:not(:disabled) {
  background-color: var(--kp-alt-bg-hover) !important;
  border-color: var(--kp-t3) !important;
  color: var(--kp-alt-fg) !important;
}

/* ---- 10. Links: no blue, ever ---------------------------------------------- */
body a:not([bitbutton]):not([buttontype]) {
  background-color: transparent !important;
  color: var(--kp-t1) !important;
  text-underline-offset: 3px !important;
}
body a:not([bitbutton]):not([buttontype]):hover {
  background-color: transparent !important;
  color: var(--kp-t2) !important;
}

/* ---- 11. Hairline borders --------------------------------------------------
   1px, no decorative shadow. In the reference the border IS the separation.

   CAUTION: `.tw-border` is a BLANKET utility hook. Every one of bit-form-field's
   three outline segments carries it, so this rule also repaints the field's
   notched outline to --kp-border - #1E1E1E on a #101010 field, i.e. invisible.
   That invisibility is what made an earlier revision border the <input> instead.
   Section 23 re-asserts the field outline at higher specificity; do not "fix"
   that by weakening this rule, and do not add another blanket border rule. */
body bit-card,
body .tw-border,
body table,
body th,
body td {
  border-color: var(--kp-border) !important;
}

/* ---- 12. Legal footer ------------------------------------------------------
   The Vaultwarden / Bitwarden(R) trademark disclaimer is HIDDEN, by Owner
   ruling 2026-08-12. This section previously argued it must stay; that
   argument was half wrong and is recorded here rather than deleted.

   The AGPL half was wrong. AGPL section 13 obliges the OPERATOR to offer the
   corresponding source to users interacting over a network. That obligation
   is discharged by making the source available - it is not a rendering
   requirement, no stylesheet can satisfy or breach it, and hiding a footer
   line does not touch it. It remains live and is the Owner's to meet.

   The trademark half was a real consideration, not a rule: the notice
   disclaims association with Bitwarden Inc. Whether to display it is an
   operator judgement about their own exposure, and the operator made it.

   Hidden, not deleted: this layer is CSS over an application it does not
   build, so removal from the DOM is not available to it. display:none takes
   the node out of flow, which is what lets the footer collapse instead of
   leaving a void the height of the text that used to be there.

   `body footer` keeps its typography. The footer element still exists and
   still projects `environment-selector` - a functional control - so the rule
   that styles the container must not be collapsed into the rule that hides
   one of its children.
   ============================================================================ */
body #vw-disclaimer {
  display: none !important;
}

body footer {
  background-color: transparent !important;
  color: var(--kp-t3) !important;
  font-size: 12px !important;
  line-height: 1.5 !important;
}

/* ---- 13. Theme toggle ------------------------------------------------------
   Rendered by js/kp-theme.js. Fixed top-right so it is reachable on every
   screen, including the login shell where Vaultwarden offers no theme control
   at all. z-index sits above the app shell but below modal overlays. */
#kp-theme-toggle {
  position: fixed !important;
  top: 14px !important;
  right: 14px !important;
  z-index: 900 !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  width: 32px !important;
  height: 32px !important;
  padding: 0 !important;
  font-size: 14px !important;
  line-height: 1 !important;
  cursor: pointer !important;
  border-radius: 8px !important;
  background-color: var(--kp-bg-side) !important;
  border: 1px solid var(--kp-border) !important;
  color: var(--kp-t1) !important;
  transition: background-color .15s ease, border-color .15s ease !important;
}
#kp-theme-toggle:hover {
  background-color: var(--kp-bg-hover) !important;
  border-color: var(--kp-border-mid) !important;
  color: var(--kp-t1) !important;
}
#kp-theme-toggle:focus-visible {
  outline: 2px solid var(--kp-t2) !important;
  outline-offset: 2px !important;
}
/* ---- 14. Semantic bg/text family -------------------------------------------
   Discovered empirically AFTER the first gate run failed: the login ground is
   painted by Bitwarden's own

       body.layout_frontend { background-color: var(--color-bg-tertiary) !important }

   at specificity (0,1,1), which outranked a bare `body { ... !important }` at
   (0,0,1). The measured result was #020618 (dark) / #F2F6FA (light) - both blue.
   A declared rule is only a hypothesis until you check what WINS the cascade.

   Two corrections, belt and braces: the ground rule above now also matches at
   (0,1,1) and loads later, AND the semantic tokens themselves are re-pointed
   here so every OTHER component reading them turns monochrome too, instead of
   this being chased one selector at a time.

   NOTE the convention split, verified on the live render: this --color-bg-*
   family holds HEX, while --color-background* / --color-text-* hold
   space-separated RGB triplets. Writing a triplet here silently voids the
   declaration. Values below are hex on purpose.
   ============================================================================ */
html.theme_dark body {
  --color-bg-primary:     #0f0f0f;  /* dash --bg-elev, was #101010 */
  --color-bg-secondary:   #1a1a1a;  /* dash --bg-hover, was #161616 */
  --color-bg-tertiary:    #0a0a0a;  /* dash --bg. THE login ground - but see
                                       25.7: three shell elements SPEND this
                                       token, and they are what actually paint
                                       the ground. This value is now only the
                                       fallback for the ones left opaque. */
  --color-bg-quaternary:  #262626;  /* was #314158 */
  --color-bg-contrast:    #EBEBEB;  /* was #e6ecf2 */
  --color-bg-disabled:    #161616;  /* was #020618 */
  --color-bg-dark:        #161616;  /* was #1d293d */
  --color-bg-hover:       rgba(255, 255, 255, 0.05);
  --color-bg-overlay:     rgba(0, 0, 0, 0.85);
  --color-border-base:    #232323;  /* dash --border, was #1E1E1E */
}

html.theme_light body {
  --color-bg-primary:     #FFFFFF;
  --color-bg-secondary:   #F7F7F7;  /* was #f2f6fa */
  --color-bg-tertiary:    #FFFFFF;  /* was #f2f6fa  THE login ground */
  --color-bg-quaternary:  #D4D4D4;  /* was #d5dde8 */
  --color-bg-contrast:    #111111;  /* was #1d293d */
  --color-bg-disabled:    #F0F0F0;  /* was #e6ecf2 */
  --color-bg-dark:        #111111;  /* was #1d293d */
  --color-bg-hover:       rgba(0, 0, 0, 0.04);
  --color-bg-overlay:     rgba(0, 0, 0, 0.30);
  --color-border-base:    #E5E5E5;  /* was #d5dde8 */
}
/* ---- 15. Theme toggle DOCKED in the vault header ---------------------------
   js/kp-theme.js sets [data-kp-docked] once it has inserted the control into
   the header flex row, immediately before the "+ New" button. The login shell
   has no header, so there the attribute is absent and section 13's fixed
   top-right placement still applies.

   Specificity is deliberate: #id[attr] is (1,1,0) and beats section 13's plain
   #id (1,0,0), so `position: static` reliably wins over `position: fixed`.
   Two !important declarations do NOT settle by source order when specificity
   differs - the lesson from Trap 314, applied on purpose this time.

   No margin is declared: the header row supplies its own gap, so borrowing it
   keeps the control spaced exactly like Vaultwarden's own buttons and cannot
   drift if that spacing is retuned upstream.

   Deliberately quiet - transparent ground, no border - so it reads as part of
   the header chrome rather than competing with the primary "+ New" CTA. The
   background is still declared explicitly rather than omitted: CONTRAST LAW
   wants both halves of the pair named, and `transparent` is a decision. */
#kp-theme-toggle[data-kp-docked] {
  position: static !important;
  top: auto !important;
  right: auto !important;
  z-index: auto !important;
  width: 40px !important;
  height: 40px !important;
  flex: 0 0 auto !important;
  font-size: 17px !important;
  background-color: transparent !important;
  border: 1px solid transparent !important;
  color: var(--kp-t1) !important;
}
#kp-theme-toggle[data-kp-docked]:hover {
  background-color: var(--kp-bg-hover) !important;
  border-color: var(--kp-border) !important;
  color: var(--kp-t1) !important;
}
#kp-theme-toggle[data-kp-docked]:focus-visible {
  outline: 2px solid var(--kp-t2) !important;
  outline-offset: 2px !important;
}

/* ---- 16. Semantic colour roles - DECLARED, DELIBERATELY UNCONSUMED -----------
   DS-02 II.3: the complete meaning of colour in KobiiCraft Pass. Four state
   roles plus an accent that is NOT a state.

   ---------------------------------------------------------------------------
   WHY NOTHING BELOW HAS A SELECTOR
   ---------------------------------------------------------------------------
   HR-83 and Contract clause C-26: an element may exist only if its capability
   exists. DS-02 II.3 rule 1: green asserts that the system MEASURED something
   and it passed.

   Vaultwarden's interior reports no health, expiry, rotation or coverage state.
   There is nothing measured here to be green about. Painting a vault row green
   because the row exists would assert a measurement that never happened - the
   compendium's designated worst-case failure, an optimistic green over an
   uninstrumented control. Grey, not green, is the colour of "we do not know".

   So this block defines a vocabulary and paints NOTHING. That is the whole and
   correct scope until a real state exists to bind. When one does, the consumer
   must also carry the II.3 redundancy spec - colour plus at least two of
   {text, shape, position} - because colour never carries a state alone (C-18).

   ---------------------------------------------------------------------------
   VALUE PROVENANCE - two different authority classes, do not conflate
   ---------------------------------------------------------------------------
   GREEN is REALIDAD VERIFICADA: read from the Owner's reference HTML via
   DS-02 II.3 (light #16A34A on #F0FDF4; dark brightens to #22C55E over a
   tinted ground, ringed #1a3d27).

   AMBER, RED, NEUTRAL and ACCENT are ARQUITECTURA PROPUESTA: the compendium
   specifies their MEANING but no values, because the reference contained only
   a green. They are derived here to sit in the same tonal family and every
   pair is COMPUTED against its own ground, never eyeballed - II.3 records that
   on a neutral ground a near-miss on contrast is invisible to review, since no
   adjacent hue exists to make the error obvious. Measured ratios are in the
   table at vault/pass/implementation/PASS_SPRINT0_CONTRAST_PROOF.md; all clear
   4.5:1.

   The ACCENT is drawn from the neutral ladder, never from the semantic hues
   (II.3 rule 3): selection and focus must stay readable on top of ANY state,
   and an accent that was also a state would put two meanings in one channel.
   It resolves to the value the primary CTA already uses, so "the accent" and
   "the primary action" cannot drift apart.

   Note the convention split from section 14: these are HEX, matching the
   --color-bg-* family. A space-separated triplet here would silently void the
   declaration. Contrast Law (C-28) applies to every future consumer: never a
   foreground without its background in the same rule - which is why each role
   ships fg, bg and border together rather than a bare hue.
   ============================================================================ */
/* WHY EACH ROLE SHIPS A -dot AND A -text, NOT ONE HUE
   II.3's redundancy spec renders a state as a dot PLUS a word. Those are two
   different accessibility objects with two different WCAG floors: a dot is a
   non-text graphical indicator (3:1), a word is text (4.5:1).

   Collapsing them into one value is not a rounding error. The first pass of
   this block did exactly that and the contrast gate failed the compendium's
   OWN reference green: #16A34A measures 3.30:1 on white - correct as a dot,
   below the floor as text. The reference was never wrong; it was being asked
   to do a job it was not specified for. Hence the split: the reference hue is
   preserved verbatim as the dot, and a darker sibling carries any coloured
   text. Every value below is the output of that search, not a choice.

   Proof: vault/pass/implementation/PASS_SPRINT0_CONTRAST_PROOF.md (28/28). */
html.theme_light body {
  /* PROTEGIDO - verified protected: measured, current, within policy. */
  --kp-state-ok-dot:      #16A34A;   /* 3.15:1 - reference value, preserved */
  --kp-state-ok-text:     #15803D;   /* 4.79:1 */
  --kp-state-ok-bg:       #F0FDF4;
  --kp-state-ok-bdr:      #4ADE80;   /* 1.74:1 edge */

  /* REVISION - approaching a threshold, or a policy question a human owes. */
  --kp-state-watch-dot:   #D97706;   /* 3.07:1 */
  --kp-state-watch-text:  #B45309;   /* 4.84:1 */
  --kp-state-watch-bg:    #FFFBEB;
  --kp-state-watch-bdr:   #FBBF24;   /* 1.67:1 edge */

  /* RIESGO - expired, exposed, failed, compromised. A STATE, never a control:
     destructive buttons are communicated by IV.3's confirmation ladder, not by
     fill. A red delete button beside a red expiry badge trains the operator to
     stop reading red. */
  --kp-state-risk-dot:    #DC2626;   /* 4.41:1 */
  --kp-state-risk-text:   #B91C1C;   /* 5.91:1 */
  --kp-state-risk-bg:     #FEF2F2;
  --kp-state-risk-bdr:    #F87171;   /* 2.77:1 edge */

  /* NEUTRAL - unknown, inactive, NOT MEASURED. Reuses the existing text ladder
     on purpose: "we do not know" is not a new colour, it is the absence of a
     claim. */
  --kp-state-neutral-dot:  #6B6B6B;  /* 4.97:1 */
  --kp-state-neutral-text: #6B6B6B;  /* 4.97:1 */
  --kp-state-neutral-bg:   #F7F7F7;
  --kp-state-neutral-bdr:  #D4D4D4;  /* 1.48:1 edge */

  /* Accent: selection, focus, primary action. Never a state. */
  --kp-accent:          #111111;     /* 18.88:1 */
  --kp-accent-fg:       #FFFFFF;
  --kp-accent-ring:     #6B6B6B;
}

html.theme_dark body {
  /* Same meanings, independently designed values - C-21: dark is not an
     inversion. Each hue is brightened against graphite and given its own
     tinted ground and edge, exactly as the reference does for green.

     Dot and text converge here, and that is a RESULT rather than a shortcut:
     on graphite every role already clears 4.5:1 at its dot value, so no darker
     sibling is needed. Light mode is the constrained theme, not dark - which
     is precisely why C-22 refuses to treat light as secondary. */
  --kp-state-ok-dot:      #22C55E;   /* 6.68:1 */
  --kp-state-ok-text:     #22C55E;   /* 6.68:1 */
  --kp-state-ok-bg:       #142A1E;
  --kp-state-ok-bdr:      #235233;   /* 2.17:1 edge */

  --kp-state-watch-dot:   #F5B23C;   /* 8.71:1 */
  --kp-state-watch-text:  #F5B23C;   /* 8.71:1 */
  --kp-state-watch-bg:    #2A1F0A;
  --kp-state-watch-bdr:   #4A3A1F;   /* 1.78:1 edge */

  --kp-state-risk-dot:    #F26D6D;   /* 5.94:1 */
  --kp-state-risk-text:   #F26D6D;   /* 5.94:1 */
  --kp-state-risk-bg:     #2A1414;
  --kp-state-risk-bdr:    #5A2424;   /* 1.60:1 edge */

  --kp-state-neutral-dot:  #8A8A8A;  /* 5.51:1 */
  --kp-state-neutral-text: #8A8A8A;  /* 5.51:1 */
  --kp-state-neutral-bg:   #101010;
  --kp-state-neutral-bdr:  #2E2E2E;  /* 1.44:1 edge */

  --kp-accent:          #EBEBEB;     /* 16.41:1 */
  --kp-accent-fg:       #0C0C0C;
  --kp-accent-ring:     #8A8A8A;
}

/* ---- 17. C-26: the product switcher --------------------------------------
   DS-01 I.5 ruled this RE-JUSTIFY: "meaningful only if Pass hosts multiple
   products. Today it does not." C-26 - an element may exist only if its
   capability exists - makes exists-but-empty a lie, and a nav affordance that
   promises products which do not exist erodes trust in every other one.

   Hidden with visibility rather than display:none ON PURPOSE. kp-theme.js
   docks the theme toggle against an anchor chain
   (vault-new-cipher-menu -> product-switcher -> app-account-menu) and tests
   each candidate with getBoundingClientRect(). display:none collapses the box
   to 0x0, which reads as off-screen and silently drops the middle link of that
   chain; visibility:hidden keeps the geometry, so the fallback still works on
   the Trash screen where "+ New" is absent. The element also stops taking
   pointer events, so nothing invisible remains clickable. */
body:not(.layout_frontend) product-switcher {
  visibility: hidden !important;
  pointer-events: none !important;
}

/* ---- 18. Navigation chrome: the --color-nav-* family ------------------------
   THE reason the sidebar stayed near-black while theme_light was active.

   Bitwarden paints the side nav from its own token family, consumed through
   three utilities in the shipped stylesheet:

       .tw-bg-bg-nav       { background-color: var(--color-nav-bg-primary); }
       .tw-text-fg-nav     { color:            var(--color-nav-fg-primary); }
       .tw-bg-bg-nav-hover { background-color: var(--color-nav-bg-primary-hover); }

   and <nav id="bit-side-nav" class="... tw-bg-bg-nav tw-text-fg-nav"> carries
   them. This sheet referenced --color-nav- ZERO times, so nothing here ever
   competed with it.

   It was never a cascade failure. Upstream ships a near-black sidebar in LIGHT
   mode deliberately: the base scope hardcodes

       --color-nav-bg-primary: #212529;

   and .theme_dark is the only block that overrides it. The stock .theme_light
   block is SIXTY-NINE characters long - light is not a theme in that
   stylesheet, it is the base scope. So a rule keyed on `html.theme_light body`
   had nothing to beat: the value it needed to displace lived unscoped.

   Consequence for maintenance: a rebase re-asserts #212529. Upstream is not
   drifting toward us and never will, because from its point of view nothing is
   broken. This block is a permanent override, not a temporary patch.

   NOTATION - a THIRD convention in the same stylesheet. Section 2's
   --color-background* family holds space-separated RGB triplets; section 14's
   --color-bg-* family holds HEX; this family holds RAW COLOUR (hex or rgba())
   consumed directly by var(). Writing a triplet here voids the declaration
   silently, exactly as it does in the other two.

   -secondary mirrors -primary throughout. side-nav.component.html swaps the
   primary tokens for the secondary ones via an inline [style] when
   variant() === 'secondary', so mirroring them makes the result independent of
   which variant a layout happens to choose - one less thing to re-verify after
   an upstream change.

   Ratios are computed against the ground each value actually sits on. Full
   derivation: vault/docs/KOBIICRAFT_PASS_LIGHT_THEME_NAV_AUDIT_2026-07-30.md
   ============================================================================ */
html.theme_light body {
  --color-nav-bg-primary:                #F7F7F7;
  --color-nav-bg-secondary:              #F7F7F7;
  --color-nav-bg-primary-strong:         #EBEBEB;   /* active item ground */
  --color-nav-bg-secondary-strong:       #EBEBEB;
  --color-nav-bg-primary-hover:          rgba(0, 0, 0, 0.04);
  --color-nav-bg-secondary-hover:        rgba(0, 0, 0, 0.04);
  --color-nav-bg-primary-hover-strong:   rgba(0, 0, 0, 0.08);
  --color-nav-bg-secondary-hover-strong: rgba(0, 0, 0, 0.08);
  --color-nav-fg-primary:                #6B6B6B;   /* 4.97:1 on #F7F7F7 */
  --color-nav-fg-secondary:              #6B6B6B;
  --color-nav-border-primary:            #E5E5E5;
  --color-nav-border-secondary:          #E5E5E5;
  --color-nav-border-primary-focus:      #111111;
  --color-nav-border-secondary-focus:    #111111;
}

html.theme_dark body {
  --color-nav-bg-primary:                #101010;
  --color-nav-bg-secondary:              #101010;
  --color-nav-bg-primary-strong:         #262626;
  --color-nav-bg-secondary-strong:       #262626;
  --color-nav-bg-primary-hover:          rgba(255, 255, 255, 0.05);
  --color-nav-bg-secondary-hover:        rgba(255, 255, 255, 0.05);
  --color-nav-bg-primary-hover-strong:   rgba(255, 255, 255, 0.10);
  --color-nav-bg-secondary-hover-strong: rgba(255, 255, 255, 0.10);
  --color-nav-fg-primary:                #8A8A8A;   /* 5.51:1 on #101010 */
  --color-nav-fg-secondary:              #8A8A8A;
  --color-nav-border-primary:            #1E1E1E;
  --color-nav-border-secondary:          #1E1E1E;
  --color-nav-border-primary-focus:      #EBEBEB;
  --color-nav-border-secondary-focus:    #EBEBEB;
}

/* ---- 19. Shell chrome: sidebar edge, header edge, active item, separator ----
   Section 18 re-points the tokens so every consumer follows. The two rules
   below are the belt to that braces: they name the elements directly, so the
   result does not depend on upstream keeping those utility classes on those
   elements. Same both-corrections pattern section 14 records.

   #bit-side-nav is an ID that upstream sets in the template - a stronger hook
   than any Tailwind class here, and the only ID on the element. */
body:not(.layout_frontend) #bit-side-nav {
  background-color: var(--kp-bg-side) !important;
  color: var(--kp-t2) !important;
  border-right: 1px solid var(--kp-border) !important;
}

/* INACTIVE nav links. This rule exists because of a rule EARLIER IN THIS FILE,
   not because of anything upstream does.

   Section 10 ("no blue, ever") is `body a:not([bitbutton]):not([buttontype])`
   at specificity (0,2,2) with !important. A sidebar link matches it - it is an
   <a> carrying neither attribute - and (0,2,2)!important outranks Tailwind's
   `.!tw-text-fg-nav` at (0,1,0)!important. So section 10 was painting every nav
   link with --kp-t1 and section 18's --color-nav-fg-primary never reached the
   text, only the ground.

   Re-pointing a token is not enough when a local rule already hardcodes the
   property that token feeds. Adding an ID to the selector takes this to
   (1,1,2), which beats (0,2,2) on the ID column alone.

   The consumed value is the nav TOKEN, not --kp-t2, so section 18 stays the
   single source for nav foreground even though the two currently coincide. */
body:not(.layout_frontend) #bit-side-nav a {
  background-color: transparent !important;
  color: var(--color-nav-fg-primary) !important;
}

/* Active nav item. The hook is an ATTRIBUTE, not a class: nav-item.component
   uses `routerLinkActive` bare (which adds no class) and binds
   ariaCurrentWhenActive, whose declared default is "page"
   (nav-item.component.ts:91) - so the active link renders aria-current="page".
   A `.active` rule would have been dead code.

   Weight carries the state alongside colour on purpose. Active-vs-inactive is
   a state, and a state is never allowed to ride on colour alone (C-18) - the
   same redundancy the semantic roles in section 16 are specified with.

   The declared background is `transparent` BY DECISION, not by omission: the
   active ground is painted on the container behind this link, from
   --color-nav-bg-primary-strong. That is the ground the 15.84:1 was measured
   against (#111111 on #EBEBEB), not the transparent link box. */
body:not(.layout_frontend) #bit-side-nav a[aria-current] {
  background-color: transparent !important;
  color: var(--kp-t1) !important;
  font-weight: 600 !important;
}

/* Header edge. bit-header is a component selector, so it renders as a literal
   tag wrapping the <header> element that carries the layout classes. */
body:not(.layout_frontend) bit-header > header {
  background-color: var(--kp-bg) !important;
  color: var(--kp-t1) !important;
  border-bottom: 1px solid var(--kp-border) !important;
}

/* Separator between the sun/moon control and the primary action.

   Placed on the control's NEXT SIBLING rather than on the control itself, for
   two independent reasons:

     1. The docked control's hover paints border-color on all four sides, so a
        border-based divider on the control would be swallowed by the hover
        ring - present at rest, gone exactly when the user is looking at it.
     2. `position: relative` + ::after was the other candidate and is refused:
        verify_dock_gate asserts position === "static" as the PROOF that the
        docking rule outranks the fixed login rule (Trap 314). A separator must
        not cost us that assertion.

   `+ *` rather than a named element: on the Trash screen "+ New" is absent and
   the control docks before <product-switcher> instead, so naming the wrapper
   would drop the separator on exactly one screen. With the row's own gap on
   the left and this padding on the right, the line lands in the middle of the
   gap instead of hugging either control. */
body:not(.layout_frontend) #kp-theme-toggle[data-kp-docked] + * {
  border-left: 1px solid var(--kp-border) !important;
  padding-left: 8px !important;
}

/* ---- 20. Login controls this server cannot honour --------------------------
   Both controls below were LIVE DEFECTS, measured on the production page:

     "Use single sign-on"  -> click does nothing; /identity/connect/oidc-signin
                              returns 404 and /api/config reports  "sso": ""
     "Log in with passkey" -> navigates, then fails with a 404 and the message
                              "Passkey authentication failed"

   The vw-* class names are Vaultwarden's own OPERATOR HOOKS: they appear in
   login.component.html and are styled NOWHERE in the shipped stylesheet or in
   index.html (verified: zero matches). They exist precisely so a deployment can
   decide which login paths it offers. Using them is the intended mechanism, not
   a workaround.

   THE TWO CASES ARE DIFFERENT AND ARE TREATED DIFFERENTLY.

   SSO is a CONFIGURATION gap, so it is hidden CONDITIONALLY. js/kp-theme.js
   reads the same public /api/config the app itself reads and stamps
   data-kp-sso="on" when the server reports a non-empty SSO url. The rule below
   only matches while that attribute is absent, so the day an OIDC authority is
   configured the button returns BY ITSELF - no code change, nothing to
   remember. Selecting on :not() rather than adding a reveal rule also means the
   button keeps its own native display value instead of us guessing it.

   Fail direction is deliberate: if the probe fails the attribute is never set
   and the button stays hidden. Never show a control we cannot prove works.

   Passkey is a CAPABILITY gap, so it is hidden UNCONDITIONALLY. Vaultwarden
   1.36.0 has no passwordless login at all: grep finds no `assertion-options`
   and no `passwordless` in the binary, while `two-factor/get-webauthn` IS
   present - WebAuthn ships as second factor only. No configuration can turn
   this on, so there is nothing to probe. Revisit on a Vaultwarden upgrade.

   The "or" divider goes with the SSO button. With both alternatives gone it
   would separate nothing, and a dangling "o" over a single button is a defect
   we would have introduced ourselves.
   ============================================================================ */
body.layout_frontend .vw-passkey-login {
  display: none !important;
}

html:not([data-kp-sso="on"]) body.layout_frontend .vw-sso-login,
html:not([data-kp-sso="on"]) body.layout_frontend .vw-or-text,
html:not([data-kp-sso="on"]) body.layout_frontend input.vw-email-sso {
  display: none !important;
}

/* The SSO email FIELD was missed when the button and the divider were hidden,
   and a control hidden by halves is worse than either extreme. Measured: the
   login form renders `input.vw-email-continue` and `input.vw-email-sso` inside
   the SAME bit-form-field - an ancestor walk from the second finds no ancestor
   that excludes the first. Two 38px controls in a 44px min-height flex row
   means the second overflows its wrapper by 34px and lands on the
   Remember-email checkbox at y403 and its label at y407, covering the label
   completely. The label measured 142x21 and `visibility: visible` throughout:
   a box is not paint, and only elementFromPoint could see it.

   The rule targets the INPUT, never its wrapper: bit-form-field is shared with
   the primary email field, so hiding the wrapper would take the login field
   with it. `input.vw-email-sso` carries no label of its own - the only
   label[for] values in the form point at the continue field and the master
   password - so nothing is orphaned by hiding it.

   Anchor on the CLASS, never the id. `bit-input-N` is generated per render
   order, which is why an earlier session recorded this pair by id, watched the
   ids drift, and concluded the note was stale and the artefact upstream. The
   classes never moved. */

/* ---- 21. The API Keys folder ----------------------------------------------
   Vaultwarden has NO api-key item type and this fork cannot add one:
   CipherType is Object.freeze'd at Login/SecureNote/Card/Identity/SshKey,
   guarded by isCipherType(), and normalised through @bitwarden/sdk-internal -
   a Rust/WASM package. A sixth value needs the SDK and the Rust server.

   So the category is carried by a FOLDER, which is real vault data: encrypted,
   searchable, exportable, and something the vault genuinely filters by. The
   secret itself lives in a Hidden custom field, environment and owner in Text
   fields. Nothing here invents a taxonomy - it makes an existing one legible.

   WHY THE MATCH IS EXACT AND FOLDER-SCOPED, NOT A SUBSTRING
   js/kp-theme.js marks a row only when BOTH hold: the row carries an
   i.bwi-folder (so it is a FOLDER, never a collection or a type filter), and
   its label matches a convention name EXACTLY after trimming and NBSP
   normalisation. Substring matching on "api" would badge "therAPIst",
   "RapidAPI" and "Wasabi", in a vault that renders in two languages. A badge
   that asserts a category it cannot actually determine teaches the operator to
   stop trusting badges - worse than no badge at all.

   The mark is an ATTRIBUTE set by JS and consumed here. JS decides WHAT is an
   API-key folder; CSS decides how it LOOKS. Renaming the folder removes the
   attribute on the next observation, so the treatment cannot outlive its cause.

   Deliberately monochrome. Section 16 records that green asserts a MEASUREMENT
   that passed; this badge asserts a CONVENTION the operator adopted, which is
   not a measured state. Grey is the honest colour for that. The accent is the
   neutral one the primary action already uses, never a semantic hue.

   The badge ground is --kp-bg rather than --kp-bg-hover on purpose: the row
   itself is painted --kp-bg-hover, so a badge sharing that value would vanish
   into it. Contrast measured against the ground it actually sits on -
   #111111 on #FFFFFF = 18.88:1 light, #EBEBEB on #0C0C0C = 16.41:1 dark.

   "API" is used as the badge text because it is language-neutral - identical in
   ES and EN. Contract clause C-2 forbids mixing languages on one screen, and a
   translated badge would do exactly that on a screen the user set to the other
   language.
   ============================================================================ */
body:not(.layout_frontend) li.filter-option[data-kp-apikeys] {
  background-color: var(--kp-bg-hover) !important;
  color: var(--kp-t1) !important;
  border-left: 2px solid var(--kp-accent) !important;
  border-radius: 4px !important;
}

/* Swap the folder glyph for the key glyph. \f130 is bwi-key, read from the
   shipped icon font (.bwi-key:before{content:"\f130"}), not guessed - these are
   private-use codepoints with no meaning outside this font. */
body:not(.layout_frontend) li.filter-option[data-kp-apikeys] > .filter-buttons > .filter-button > i.bwi::before {
  content: "\f130" !important;
}

body:not(.layout_frontend) li.filter-option[data-kp-apikeys] > .filter-buttons > .filter-button::after {
  content: "API";
  margin-left: 6px;
  padding: 0 5px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.06em;
  line-height: 16px;
  border-radius: 3px;
  vertical-align: middle;
  background-color: var(--kp-bg);
  color: var(--kp-accent);
  border: 1px solid var(--kp-border);
}

/* ---- 22. The landing footer: product wordmark and version ------------------
   The last visible "Vaultwarden" on the login shell. This is a CARRY-OVER, not
   a regression: the v1 kp-brand.js hid these two lines, that script died in the
   2026-07-21 fork cutover, and the hide was never migrated into this sheet.

   bit-landing-footer > footer holds exactly five things, in order:
   environment-selector (renders empty here), "Vaultwarden Web", "2026.4.1", a
   separator <br>, and div#vw-disclaimer. Sections 1-21 never touched it.

   WHY THE RULE IS COMPONENT-SCOPED AND NOT GLOBAL
   A bare [bittypography="body2"] selector ALSO matches the "Recordar correo
   electrónico" label inside the login form - measured on the live DOM, not
   assumed. Hiding it would silently strip the label off a functional control,
   which is a worse defect than the wordmark we came to remove. The v1 script
   got away with a global match because it additionally counted elements; a
   stylesheet cannot count, so it must scope instead. bit-landing-footer is an
   Angular component selector and a real element in the DOM, and it contains no
   other body2 node - so the scope is both sufficient and exact.

   WHY "> footer > br" AND NOT "br"
   #vw-disclaimer carries its OWN <br> between its two sentences. A rule reading
   bit-landing-footer br would collapse the disclaimer onto a single line - a
   regression manufactured by the fix itself. The child combinator confines this
   to the footer's own separator, which would otherwise leave a blank line
   hanging where the version block used to be.

   WHY display:none AND NOT visibility:hidden
   The footer is a centred flow block. Reserving the boxes would leave a void of
   exactly the size of the thing we removed.

   WHAT SURVIVES HERE
   `environment-selector`, which this section must not touch: it is a functional
   control that renders empty on this deployment but is not guaranteed to stay
   empty. That is why the rule targets the two body2 children and the footer's
   own separator, never `bit-landing-footer` or `footer` wholesale.

   #vw-disclaimer is hidden by SECTION 12, which owns it - one element, one
   owning section. Do not add a second rule for it here; section 12 carries the
   reasoning and the Owner ruling behind that decision.
   ============================================================================ */
bit-landing-footer div[bittypography="body2"] {
  display: none !important;
}

bit-landing-footer > footer > br {
  display: none !important;
}

/* ============================================================================
   23. FIELD OUTLINE, CTA WEIGHT AND RHYTHM
   ----------------------------------------------------------------------------
   Owner brief 2026-08-19: "hay textos que no se leen bien ... los bordes de las
   textboxes son raros", and raise the login to the quality of
   neom.jacobolopezcortijo.com.

   Everything below is anchored to values MEASURED on the live render, never to
   declared ones. The reference was measured too (rendered pixels, per the visual
   hard rule): warm ground, ONE card with real elevation, ~48px controls, a
   confident filled primary, and a subtle - not heavy - field border.

   Deliberately NOT adopted: the reference's blue (#0C60AE). DS-02 governs this
   surface and it is monochrome; "as good as" is a quality bar, not a palette
   swap. Hierarchy here is carried by weight and fill instead of by hue.
   ============================================================================ */

/* 23.1 The field outline is the SEGMENT ROW, not the control.
   Specificity is the whole mechanism: (0,1,3) beats section 11's `body
   .tw-border` at (0,1,1). Both are !important, so specificity decides - two
   !important declarations do NOT settle by source order (Trap 314). */
body bit-form-field div[class*="tw-border"] {
  border-color: var(--kp-field-bdr) !important;
}

/* Upstream already widens these to 2px on focus-within; it only needed a colour
   that can be seen. One ring, drawn by the element designed to draw it. */
body bit-form-field:focus-within div[class*="tw-border"] {
  border-color: var(--kp-field-focus) !important;
}

/* 23.2 Legibility: the required marker shipped at 10px, small enough to read as
   noise next to a 14px label. Colour was never the fault - it carried the same
   token as the label - so this is a SIZE fix, which is why a contrast scan of
   this page returned zero findings while the defect was plainly visible. */
body bit-form-field label > span {
  font-size: 12px !important;
  color: var(--kp-t2) !important;
}

/* 23.3 Primary action. Dark shipped #232323 on a #101010 card - about 1.4:1
   against its own surface, so it did not read as a button at all, while light
   was already correct at #111111 on #FFFFFF. DS-02: dark is DESIGNED, not
   derived. Inverting it is the monochrome equivalent of the reference's filled
   CTA. Foreground and background are set in the SAME rule (Contrast Law). */
html.theme_dark body button[buttontype="primary"],
html.theme_dark body a[buttontype="primary"] {
  background-color: #fafafa !important;
  border-color: #fafafa !important;
  color: #0a0a0a !important;
}
html.theme_dark body button[buttontype="primary"] *,
html.theme_dark body a[buttontype="primary"] * {
  color: #0a0a0a !important;
}
html.theme_dark body button[buttontype="primary"]:hover:not(:disabled),
html.theme_dark body a[buttontype="primary"]:hover:not(:disabled) {
  background-color: #ffffff !important;
  border-color: #ffffff !important;
  color: #0a0a0a !important;
}

/* 23.4 Rhythm. Controls were 40px and the field 44px; the reference sits at 48.
   min-height, never height: the field grows when it carries an error line. */
body bit-form-field div[class*="tw-min-h-"] {
  min-height: 48px !important;
}
body button[bitbutton],
body a[bitbutton] {
  min-height: 44px !important;
}

/* 23.5 ONE card, not two.
   Measured on the live render: `bit-base-card` (448x339, radius 16) is
   Bitwarden's real card, and section 6 painted the <form> INSIDE it as a second
   surface (382x273, radius 14, its own border and fill). Two nested rounded
   rectangles, ~33px apart - the reference uses exactly one.

   The card is therefore bit-base-card, and the form is not a surface at all.
   Section 6 still owns the form's TEXT colour; this owns its surface. Same
   "one element, one owning section" split section 22 makes with section 12.

   Specificity: section 6 is `body.layout_frontend form` (0,1,2), so this leads
   with `html` to reach (0,1,3) and win. Both are !important - specificity is
   what settles it (Trap 314). */
html body.layout_frontend form {
  background-color: transparent !important;
  border: 0 !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  padding: 0 !important;
}

/* The one surface. A shadow is spent on LIGHT only: on a near-black ground it
   is invisible and the hairline border does the separating instead - the same
   asymmetry DS-02 means by "dark is designed, not derived". */
body.layout_frontend bit-base-card {
  border-radius: 14px !important;
  padding: 32px 28px !important;
}
html.theme_light body.layout_frontend bit-base-card {
  box-shadow: 0 1px 2px rgba(17, 17, 17, .04),
              0 10px 28px rgba(17, 17, 17, .08) !important;
}

/* ============================================================================
   24. SELECTED TEXT, AND TEXT DE-EMPHASISED BY OPACITY
   ----------------------------------------------------------------------------
   Owner report 2026-08-21: selected text is unreadable in dark, and the
   completed items in the vault's getting-started panel cannot be read either.

   TWO different causes. The first hypothesis here was wrong and the PIXELS
   corrected it, which is the whole reason this project measures renders rather
   than stylesheets:

     a) SELECTION is the BROWSER DEFAULT, not Bitwarden's. Upstream does ship
          [&>*::selection]:tw-bg-primary-700 + [&>*::selection]:tw-text-contrast
        and it was tempting to blame section 14 for greying primary-700. But
        that utility is SCOPED to elements carrying the class, and it never
        reaches ordinary prose. Selecting a heading and sampling the pixels gave
        (2, 54, 160) on (12, 12, 12) = 1.90:1 in dark - Chromium's own highlight
        blue on a near-black ground. Light measured 5.25:1 and was never broken.
        A CSSOM reading would have "explained" this defect with the wrong
        mechanism and produced a fix that changed nothing.

     b) THE COMPLETED-TASK STRIKETHROUGH is ours. Upstream marks a finished task
          tw-line-through tw-decoration-primary-700 tw-opacity-50
        (read out of the compiled bundle, not guessed), and section 14 did
        re-point --color-primary-700 to 35 35 35, so the line that MEANS "done"
        turned near-black on a near-black ground while tw-opacity-50 halved an
        already-muted foreground.

   Both are fixed with explicit pairs rather than by re-pointing primary-700:
   that token has other consumers this layer has not audited, and swapping a
   shared token to fix a symptom is how (b) was created in the first place.
   ============================================================================ */

/* 24.1 Selection. Inverted on purpose: monochrome has no accent hue to spend,
   so the unambiguous signal is a full tone flip. Both halves in the same rule.
   Specificity (0,1,2) clears upstream's (0,1,0) utility, and the bare ::selection
   pair covers every node the utility never reached - which is most of the app. */
html.theme_dark body ::selection,
html.theme_dark body *::selection {
  background-color: #fafafa !important;
  color: #0a0a0a !important;             /* 17.4:1 */
}
html.theme_light body ::selection,
html.theme_light body *::selection {
  background-color: #111111 !important;
  color: #FFFFFF !important;             /* 18.88:1 */
}

/* 24.2 De-emphasis must not cost legibility.
   `tw-opacity-50` over a muted foreground lands near 2.6:1 on this ground. The
   fix does not merely raise the opacity: it makes the result DETERMINISTIC by
   declaring the colour outright, because the inherited base differs per
   component and half of an unknown is still unknown. De-emphasis is then
   carried by the strikethrough and the muted tone - both still legible - which
   is what "completed" is supposed to communicate.

   Anchored on the line-through utility, taken from the compiled bundle: it is
   what actually marks a finished task, so the rule cannot drift onto ordinary
   muted text that merely happens to be dim. */
body [class*="tw-line-through"] {
  opacity: 1 !important;
  color: var(--kp-t2) !important;                  /* 5.67:1 dark, 5.33:1 light */
  text-decoration-color: var(--kp-t2) !important;  /* the line that means "done" */
}


/* ============================================================================
   25. DASH PARITY - adoption of the dash.kobicraft.net design system
   ----------------------------------------------------------------------------
   Owner brief 2026-08-26: Pass must carry the same design as the Puesto de
   Mando (dash.kobicraft.net). Sections 1, 2, 23.3, 24.1 and the control radii
   were re-pointed IN PLACE to dash's exact values; this section adds the two
   things dash has that this file did not: an ambient login shell and a glass
   login card.

   Values were read from dash's SERVED bundle
   (/_next/static/css/726954f14827239d.css, 52,446 B, fetched 2026-08-26),
   never from its repo source - the visual hard rule makes rendered bytes the
   authority. Full extraction and the measured delta:
   vault/pass/DASH_DESIGN_SYSTEM.md in KobiiCraft Core Files.

   ---------------------------------------------------------------------------
   THREE DELIBERATE DIVERGENCES - all recorded, none accidental
   ---------------------------------------------------------------------------
   1. ORNAMENTAL HUE IS SCOPED TO THE LOGIN SHELL (Owner-ratified 2026-08-26).
      dash spends blue #60a5fa on brand: radial washes, --kc-tint, focus ring.
      DS-02 makes this file's zero-hue base a SEMANTIC mechanism - hue is what
      marks a credential compromised, weak or reused, and "ornamental hue ->
      signal dilution" is a named failure mode there. Blue is therefore adopted
      ONLY on body.layout_frontend, the unauthenticated shell, which has no
      credential state to signal. Inside the vault this file stays achromatic
      and the greyscale test still holds.

      This NARROWS section 23's "deliberately NOT adopted: the reference's
      blue"; it does not repeal it. The rule is now: no ornamental hue where
      meaning lives.

   2. NO GOOGLE FONTS - not a preference, a CSP fact. dash loads Inter and
      JetBrains Mono from fonts.googleapis.com. The live policy here is
      `default-src 'none'; font-src 'self'`, so that request is BLOCKED. Inter
      is already bundled (fonts/inter.*.woff2) and already the app font, so
      dash's primary face is exact at zero cost and zero new requests.
      JetBrains Mono is NOT bundled: --kp-mono below declares dash's fallback
      chain minus that face. KNOWN GAP, recorded rather than hidden - monospace
      runs render in the system mono. Closing it means shipping a woff2 into
      this web-vault, which is a binary-asset decision, not a stylesheet one.

   3. LIGHT IS DERIVED, NOT COPIED. dash is dark-only; it has no light palette
      to be identical TO. Dark is authored to match it exactly and is now what
      a fresh visitor sees (js/kp-theme-boot.js). Light survives as an explicit
      user choice per Visual Contract C-4, keeping its own contrast-verified
      neutral ramp - re-deriving values that already pass would risk a verified
      surface for no visual gain. Where light needs a value dash cannot supply
      (the focus ring) it is derived to the 3:1 non-text floor, not guessed.
   ============================================================================ */

/* 25.1 Monospace. See divergence 2 - dash's chain minus JetBrains Mono. */
html.theme_dark,
html.theme_light {
  --kp-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* 25.2 Body rhythm. dash's baseline is 14px/1.5; section 3 already sets the
   family and size, so this only adds the line-height it did not declare. */
body.layout_frontend,
body:not(.layout_frontend) {
  line-height: 1.5 !important;
}

/* 25.3 The login shell ambient. dash's .login-wrap, verbatim: three radials,
   two blue and one white lift from the top edge.

   Section 3 sets `background-image: none` at (0,1,1); leading with html.theme_*
   reaches (0,2,2) and wins. background-attachment is fixed so the wash stays
   put behind a card that grows when a field carries an error line - a scrolling
   gradient would slide out from under the very element it is lighting.

   The blue lives here and nowhere else: this is the shell you see before you
   authenticate, so there is no credential state on screen for it to compete
   with (divergence 1). */
html.theme_dark body.layout_frontend {
  --kp-focus-ring: #60a5fa;
  background-color: var(--kp-bg) !important;
  background-image:
    radial-gradient(1200px 820px at 30% 24%, rgba(96, 165, 250, .14), transparent 62%),
    radial-gradient(1000px 700px at 74% 80%, rgba(96, 165, 250, .08), transparent 58%),
    radial-gradient(120% 80% at 50% 0, hsla(0, 0%, 100%, .04), transparent 55%) !important;
  background-attachment: fixed !important;
}

/* Light counterpart. Same geometry, lower alpha, and the white lift dropped -
   a white-on-white radial is invisible work. dash supplies no light values
   here; these are derived (divergence 3). */
html.theme_light body.layout_frontend {
  --kp-focus-ring: #2563eb;
  background-color: var(--kp-bg) !important;
  background-image:
    radial-gradient(1200px 820px at 30% 24%, rgba(96, 165, 250, .10), transparent 62%),
    radial-gradient(1000px 700px at 74% 80%, rgba(96, 165, 250, .06), transparent 58%) !important;
  background-attachment: fixed !important;
}

/* 25.4 The login card as glass - dash's .login-card.

   Two tiers, exactly as dash ships them. The FIRST is an opaque-enough fill
   that stands on its own; the second upgrades it where backdrop-filter is
   supported. Shipping only the blurred tier would leave a near-transparent
   card on any engine without support - the card would simply vanish.

   The card is bit-base-card, not <form>: section 23.5 established that after
   measuring two nested rounded rectangles on the live render. Specificity here
   is (0,2,3) against 23.5's (0,1,2), so this wins on specificity - two
   !important declarations do not settle by source order (Trap 314). */
html.theme_dark body.layout_frontend bit-base-card {
  background-color: rgba(24, 24, 27, .86) !important;
  border: 1px solid hsla(0, 0%, 100%, .09) !important;
  box-shadow: 0 24px 60px -20px rgba(0, 0, 0, .7),
              inset 0 1px 0 hsla(0, 0%, 100%, .14) !important;
}

@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  html.theme_dark body.layout_frontend bit-base-card {
    background-color: rgba(24, 24, 27, .6) !important;
    -webkit-backdrop-filter: blur(30px) saturate(180%) !important;
    backdrop-filter: blur(30px) saturate(180%) !important;
  }
}

/* Light keeps the shadow 23.5 gave it and gains dash's hairline. No blur: over
   a near-white ground there is nothing luminous to refract, which is the same
   reason dash pairs its glass with an ambient layer rather than flat ground. */
html.theme_light body.layout_frontend bit-base-card {
  border: 1px solid rgba(17, 17, 17, .10) !important;
}

/* 25.5 Focus ring on the login shell only.

   A focus indicator is an affordance, not ornament, so it is the one place the
   shell's blue does real work. Inside the vault the ring stays achromatic -
   section 23.1 keeps drawing it from --kp-field-focus.

   Light uses #2563eb, not dash's #60a5fa: that blue measures about 2.2:1 on
   white, under the 3:1 floor a non-text indicator has to clear. Derived to the
   floor rather than copied into illegibility.

   Specificity (0,4,4) clears section 23.1's (0,2,3). */
html.theme_dark body.layout_frontend bit-form-field:focus-within div[class*="tw-border"],
html.theme_light body.layout_frontend bit-form-field:focus-within div[class*="tw-border"] {
  border-color: var(--kp-focus-ring) !important;
}

body.layout_frontend :focus-visible {
  outline: 2px solid var(--kp-focus-ring) !important;
  outline-offset: 2px !important;
}

/* 25.6 Reduced motion. dash ships a full block; this file animates almost
   nothing, but the login shell now carries large gradients and the toggle
   swaps a glyph, so the courtesy is cheap and belongs with them. */
@media (prefers-reduced-motion: reduce) {
  body.layout_frontend *,
  body.layout_frontend *::before,
  body.layout_frontend *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* 25.7 Hand the login ground back to the body.

   Upstream paints the shell with its own chrome, not with the body: three
   elements in the anon layout carry `tw-bg-background-alt`, which resolves to
   --color-bg-tertiary. The middle one is `tw-flex-1`, so it grows to fill the
   column and covers the entire centre of the viewport.

   That is why 25.3's wash was measured only in the bottom strip and behind the
   card: it WAS painting, and this was painting over it. A point-sample reads
   that as "the gradient is missing" and sends you to re-argue specificity; a
   coverage map shows the wash present exactly where nothing covers it, which is
   the signature of an occluder rather than a lost cascade. Section 14 named the
   token years before this; it did not name the elements that spend it.

   Making these transparent is what makes Pass's shell behave like dash's
   .login-wrap - one lit ground, with the card floating on it. It costs nothing
   in light, where --color-bg-tertiary and --kp-bg are the same white.

   Scoped to body.layout_frontend so the authenticated vault, which legitimately
   uses these surfaces to separate regions, is untouched. Matched on the utility
   NAME, which is stable API in the compiled bundle - never on a hashed class,
   per this file's selector policy.

   Specificity (0,2,3) clears upstream's .tw-bg-background-alt at (0,1,0) and
   its !important twin. */
html body.layout_frontend header[class*="tw-bg-background-alt"],
html body.layout_frontend div[class*="tw-bg-background-alt"],
html body.layout_frontend footer[class*="tw-bg-background-alt"] {
  background-color: transparent !important;
}


/* ============================================================================
   26. VAULT INTERIOR - dash's elevation, and the hue that only meaning may own
   ----------------------------------------------------------------------------
   Sections 1-25 skinned the ground and the login shell. This one carries the
   SYSTEM into the authenticated vault, where 75 non-neutral declarations across
   27 distinct colours were measured on a probe container serving this exact
   web-vault over its own empty database.

   ---------------------------------------------------------------------------
   26.0 THE FORMAT TRAP, AND WHY LINKS WERE STILL BLUE
   ---------------------------------------------------------------------------
   Section 14 recorded that `--color-bg-*` holds HEX while `--color-background*`
   holds space-separated triplets, and that "writing a triplet here silently
   voids the declaration". That was true of more families than it audited.
   Counted over the compiled bundle:

       brand      consumed BARE var() x31    declared as HEX x11
       primary    consumed rgb(var())  x134  declared as triplet x8
       secondary  consumed rgb(var())  x90   declared as triplet x10
       success / info / danger / warning     declared as triplet

   Section 2 neutralises `--color-brand-*` with TRIPLETS. All 31 consumers read
   it bare, so `color: var(--color-brand-400)` resolved to the literal text
   "138 138 138", which is not a colour, so the property fell back to upstream's
   #6BAEFA. Valid CSS, no console error, no visual change - and section 10's
   promise of "Links: no blue, ever" quietly untrue for as long as it has
   existed. THE FORMAT IS PART OF THE DECLARATION.

   The triplet forms are left in place for any consumer that wants them; the hex
   forms below are added for the family that is read bare. Both can be true.

   ---------------------------------------------------------------------------
   26.0.1 ORNAMENT VERSUS MEANING
   ---------------------------------------------------------------------------
   DS-02 gives the test as "enumerate non-neutral pixels; each maps to a
   meaning". Run against the vault it produced two piles.

   ORNAMENT (goes neutral): #175DDC across eight illustration paths - the exact
   family C-1 requires to be ZERO on primary surfaces - plus #FFBF00, the
   #8200DB avatar, #6BAEFA links, and Bitwarden's slate greys (#303946 on the
   FILTERS border, #525B6A on the table head).

   MEANING (stays, re-pointed to dash's own values): success, info, danger and
   warning. Hue here is the whole point - it is what marks a credential
   compromised, weak or reused - so it is not removed, it is made to agree with
   dash instead of with Bitwarden.

   The login shell keeps its blue (25.5); the vault does not. That is the
   Owner-ratified line: no ornamental hue where meaning lives.
   ============================================================================ */

/* 26.1 The brand ramp, in the format its consumers actually read.
   Links land on --color-brand-400, so the dark value clears 4.5:1 on the vault
   ground and the light value clears it on white - a single mid-grey cannot do
   both, which is why this is per-theme rather than one shared ramp. */
html.theme_dark body {
  --color-brand-050: #f5f5f5;  --color-brand-100: #ededed;
  --color-brand-200: #d4d4d4;  --color-brand-300: #bcbcbc;
  --color-brand-400: #a1a1a1;  /* links: 6.9:1 on #0a0a0a */
  --color-brand-500: #8a8a8a;  --color-brand-600: #6b6b6b;
  --color-brand-700: #3a3a3a;  --color-brand-800: #2e2e2e;
  --color-brand-900: #232323;  --color-brand-950: #1a1a1a;
}
html.theme_light body {
  --color-brand-050: #fafafa;  --color-brand-100: #f0f0f0;
  --color-brand-200: #d4d4d4;  --color-brand-300: #a0a0a0;
  --color-brand-400: #4a4a4a;  /* links: 8.6:1 on #FFFFFF */
  --color-brand-500: #3a3a3a;  --color-brand-600: #2e2e2e;
  --color-brand-700: #232323;  --color-brand-800: #1a1a1a;
  --color-brand-900: #111111;  --color-brand-950: #0a0a0a;
}

/* 26.2 The secondary ramp - triplets, because it is read through rgb(var()).
   Never declared by this file before, which is why #303946 drew the FILTERS
   border and #525B6A the table head: two blue-greys surviving inside a system
   that claims zero hue. */
html.theme_dark body {
  --color-secondary-100:  35  35  35;   /* #232323  dash --border */
  --color-secondary-300:  58  58  58;
  --color-secondary-500: 138 138 138;
  --color-secondary-600: 161 161 161;   /* dash --text-dim */
  --color-secondary-700: 190 190 190;
}
html.theme_light body {
  --color-secondary-100: 229 229 229;
  --color-secondary-300: 196 196 196;
  --color-secondary-500: 138 138 138;
  --color-secondary-600: 107 107 107;
  --color-secondary-700:  74  74  74;
}

/* 26.3 Illustrations. Bitwarden ships its brand INSIDE the artwork, so the
   empty-state key and card render #175DDC and #FFBF00 on a surface DS-02 says
   must carry zero hue. Greyed rather than hidden: the drawing still explains
   the empty state, it just stops advertising. */
html.theme_dark body {
  --color-illustration-bg-primary:    35  35  35;
  --color-illustration-bg-secondary:  46  46  46;
  --color-illustration-bg-tertiary:   26  26  26;
  --color-illustration-logo:         161 161 161;
  --color-illustration-outline:      138 138 138;
  --color-illustration-tertiary:     161 161 161;   /* was the yellow key */
  --color-bw-blue:                   138 138 138;
  --color-text-contrast:              10  10  10;   /* text on a light chip */
}
html.theme_light body {
  --color-illustration-bg-primary:   229 229 229;
  --color-illustration-bg-secondary: 212 212 212;
  --color-illustration-bg-tertiary:  247 247 247;
  --color-illustration-logo:         107 107 107;
  --color-illustration-outline:      138 138 138;
  --color-illustration-tertiary:     107 107 107;
  --color-bw-blue:                   107 107 107;
  --color-text-contrast:             255 255 255;
}

/* 26.4 Semantic colour KEPT, re-pointed to dash's own values.
   This is the half of the enumeration that maps to a meaning, so removing it
   would be the actual defect - a password manager that cannot say "compromised"
   in colour has lost a channel, not gained purity. Dark takes dash's tokens
   verbatim; light needs darker variants to clear 4.5:1 on white, which dash
   never had to solve because dash has no light theme. */
html.theme_dark body {
  --color-success-600:  74 222 128;   /* #4ade80  dash --green */
  --color-success-700:  74 222 128;
  --color-info-600:     96 165 250;   /* #60a5fa  dash --blue */
  --color-info-700:     96 165 250;
  --color-danger-600:  248 113 113;   /* #f87171  dash --red */
  --color-danger-700:  248 113 113;
  --color-warning-600: 251 191 36;    /* #fbbf24  dash --amber */
  --color-warning-700: 251 191 36;
}
html.theme_light body {
  --color-success-600:  22 163  74;
  --color-success-700:  22 163  74;
  --color-info-600:     37  99 235;
  --color-info-700:     37  99 235;
  --color-danger-600:  220  38  38;
  --color-danger-700:  220  38  38;
  --color-warning-600: 180  83   9;
  --color-warning-700: 180  83   9;
}

/* 26.5 The avatar. Bitwarden derives its fill from a hash of the user id and
   writes it as an INLINE style, so no token re-point can reach it - the one
   leak in the enumeration that CSS variables cannot close. Overridden on the
   element, which is why this rule looks blunter than the rest of the file. */
html body:not(.layout_frontend) app-account-menu svg,
html body:not(.layout_frontend) bit-avatar svg,
html body:not(.layout_frontend) app-avatar svg {
  background-color: var(--kp-bg-hover) !important;
}
html.theme_dark body:not(.layout_frontend) bit-avatar svg text,
html.theme_dark body:not(.layout_frontend) app-avatar svg text {
  fill: var(--kp-t1) !important;
}

/* 26.6 The skip-to-content link. Visually hidden until focused, then it paints
   a #BEDBFF pill - a hue that appears only for keyboard users, which is how it
   survived every visual review this file has had. */
html body:not(.layout_frontend) nav a[href*="#"]:focus,
html body:not(.layout_frontend) a[class*="skip"]:focus {
  background-color: var(--kp-bg-hover) !important;
  color: var(--kp-t1) !important;
}

/* ---------------------------------------------------------------------------
   26.7 ELEVATION - the half of dash that is structure, not colour
   ---------------------------------------------------------------------------
   dash's doctrine, applied rather than copied: glass on ELEVATED surfaces only;
   sidebar, header and tables stay flat and are correct as they are; and glass
   needs an ambient ground because backdrop-filter over a flat fill has nothing
   luminous to refract.

   The vault's measured ground: nav 288x900 at #0f0f0f, header at #0a0a0a, main
   at #0f0f0f, table borders at #232323. Those are already dash's values and are
   deliberately NOT touched here - flat IS the design for them.

   The ambient is NEUTRAL white-alpha, not dash's blue radials. Inside the vault
   hue belongs to meaning (26.0.1), so the lift that makes glass legible has to
   arrive without hue. `background-attachment: local` ties it to the scrolled
   content instead of the viewport, so it does not slide out from under the
   panels it is lighting when a long vault scrolls. */
html.theme_dark body:not(.layout_frontend) main {
  background-image:
    radial-gradient(760px 380px at 18% 0, hsla(0, 0%, 100%, .040), transparent 60%),
    radial-gradient(620px 340px at 95% 8%, hsla(0, 0%, 100%, .022), transparent 55%) !important;
  background-repeat: no-repeat !important;
  background-attachment: local !important;
}

/* Overlay panels are the vault's elevated surfaces: the New menu, the account
   menu, the product switcher, and every dialog. Measured: .bit-menu-panel wraps
   an inner div that carries the fill (#0f0f0f), the border (#232323) and the
   8px radius - the pane itself is transparent, so styling the pane would paint
   nothing.

   Two tiers, as dash ships them: an opaque-enough fill first, upgraded to the
   material where backdrop-filter exists. A menu that vanishes on an engine
   without support is worse than a menu that never blurred. */
html body:not(.layout_frontend) .bit-menu-panel > div,
html body:not(.layout_frontend) .cdk-overlay-pane > div[class*="tw-bg-background"],
html body:not(.layout_frontend) .cdk-overlay-pane bit-dialog > div {
  border: 1px solid hsla(0, 0%, 100%, .09) !important;
  border-radius: var(--kc-radius, 14px) !important;
  box-shadow: 0 24px 60px -20px rgba(0, 0, 0, .7),
              inset 0 1px 0 hsla(0, 0%, 100%, .14) !important;
}
html.theme_dark body:not(.layout_frontend) .bit-menu-panel > div,
html.theme_dark body:not(.layout_frontend) .cdk-overlay-pane > div[class*="tw-bg-background"],
html.theme_dark body:not(.layout_frontend) .cdk-overlay-pane bit-dialog > div {
  background-color: rgba(24, 24, 27, .92) !important;
}

@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  html.theme_dark body:not(.layout_frontend) .bit-menu-panel > div,
  html.theme_dark body:not(.layout_frontend) .cdk-overlay-pane > div[class*="tw-bg-background"],
  html.theme_dark body:not(.layout_frontend) .cdk-overlay-pane bit-dialog > div {
    background-color: rgba(24, 24, 27, .72) !important;
    -webkit-backdrop-filter: blur(22px) saturate(175%) !important;
    backdrop-filter: blur(22px) saturate(175%) !important;
  }
}

/* A menu radius of 14px on a 105px-wide panel reads as a lozenge, so the
   overlay keeps dash's CONTROL radius rather than its CARD radius. dash makes
   the same distinction: --radius 10px for panels, --kc-radius 14px for cards. */
html body:not(.layout_frontend) .bit-menu-panel > div {
  border-radius: 10px !important;
}


/* ============================================================================
   27. THE PRIMITIVE PALETTES, AT THE LEVEL WHERE ROLES DERIVE
   ----------------------------------------------------------------------------
   Section 26 removed the illustration blues, the avatar and the slate borders
   and took the vault from 75 non-neutral declarations to 50. What survived was
   blue links (#6BAEFA) and a blue-grey secondary button (#45556C) -- and the
   reason was structural, not a matter of specificity.

   THREE TOKEN LAYERS, NOT TWO
   ---------------------------
     primitives  --color-gray-*, --color-brand-*         on :root
     roles       --color-fg-*, --color-bg-*, ... (106)   on :root, re-declared
                                                          per theme on .theme_dark
     legacy      --color-background*, --color-bg-*       consumed directly

   Sections 2, 14 and 26 declare on `html.theme_dark body`, deliberately, so
   descendants reading rgb(var(--color-background)) inherit them. That is why
   the GROUND is correct and has been for months.

   But a role is DERIVED: `--color-fg-brand: var(--color-brand-400)`. The
   substitution resolves on the element where the ROLE is declared -- <html> --
   and the finished value is what inherits. Re-pointing --color-brand-400 on
   <body> arrives one level too late: <html> already resolved --color-fg-brand
   to upstream's #6BAEFA and handed that colour down.

   The probe showed both halves on the same element:

       --color-brand-400 = #a1a1a1    the override, inherited from body
       --color-fg-brand  = #6baefa    derived at html, before it existed

   A DERIVED TOKEN FREEZES AT THE SCOPE WHERE IT IS DERIVED. Overriding its
   source lower in the tree cannot reach it. No amount of !important or added
   specificity fixes that, because it is the wrong ELEMENT, not a lost cascade.

   Hence `html.theme_dark` / `html.theme_light`: specificity (0,1,1), the same
   element as :root (0,1,0) and .theme_dark (0,1,0), and later in the cascade.
   One ramp corrects all 106 roles at once.

   AND THE GREY RAMP IS NOT GREY. Ten of upstream's eleven --color-gray-* entries
   carry a measurable tint (#45556C, #314158, #1D293D, #0F172B, #020618 ...): it
   is a slate ramp wearing the name "gray", which is how a monochrome skin can
   re-point everything it can see and still render blue-grey. Naming is not
   neutrality.
   ============================================================================ */

/* 27.1 The neutral ramp. One set for both themes, mirroring upstream's own
   structure: the ramp is shared and the per-theme ROLES pick different shades
   from it (dark takes gray-200 for body text, light takes gray-600), so a
   single achromatic ladder serves both without a second declaration. Endpoints
   are dash's: #0a0a0a ground, #ededed text. */
html.theme_dark,
html.theme_light {
  --color-gray-050: #fafafa;
  --color-gray-100: #ededed;   /* dash --text */
  --color-gray-200: #d4d4d4;
  --color-gray-300: #bcbcbc;
  --color-gray-400: #a1a1a1;   /* dash --text-dim */
  --color-gray-500: #8a8a8a;   /* dash --text-mut */
  --color-gray-600: #6b6b6b;
  --color-gray-700: #3a3a3a;
  --color-gray-800: #232323;   /* dash --border */
  --color-gray-900: #161616;
  --color-gray-950: #0a0a0a;   /* dash --bg */

  /* The rgba() roles (--color-bg-hover, --color-bg-overlay) read a companion
     triplet, not the hex. Missing it would leave those two roles alone with
     upstream's slate while everything around them turned neutral. */
  --color-gray-950-rgb: 10, 10, 10;
  --color-white-rgb: 255, 255, 255;

  /* 27.2 Brand. Links resolve through --color-fg-brand -> --color-brand-400 in
     dark and -> --color-brand-700 in light, so BOTH shades have to clear 4.5:1
     against their own ground: #a1a1a1 is 6.9:1 on #0a0a0a, #3a3a3a is 10.9:1
     on #FFFFFF. In a monochrome system a link is distinguished by weight and
     underline, which upstream already applies -- the hue was never doing the
     work it appeared to do. */
  --color-brand-050: #fafafa;
  --color-brand-100: #ededed;
  --color-brand-200: #d4d4d4;
  --color-brand-300: #bcbcbc;
  --color-brand-400: #a1a1a1;   /* dark links */
  --color-brand-500: #8a8a8a;
  --color-brand-600: #6b6b6b;
  --color-brand-700: #3a3a3a;   /* light links */
  --color-brand-800: #232323;
  --color-brand-900: #161616;
  --color-brand-950: #0a0a0a;
}

/* 27.3 Semantic primitives KEPT and re-pointed to dash's values.
   The role layer derives success/danger/warning from green/red/orange
   primitives, so this is where dash's semantic palette has to land for the
   roles to carry it. Meaning survives; it just speaks dash's dialect.

   The accent families (teal / coral / purple / pink) are ORNAMENT here: they
   exist upstream for avatars and marketing accents, and a password manager has
   no meaning to attach to them, so they collapse onto the neutral ramp. That is
   also what removes the #8200DB avatar at its source rather than by overriding
   the element, which is what 26.5 had to do while this layer was unknown. */
html.theme_dark {
  --color-green-200: #86efac;  --color-green-400: #4ade80;  /* dash --green */
  --color-green-700: #4ade80;  --color-green-800: #16a34a;
  --color-green-050: #052e16;  --color-green-100: #14532d;  --color-green-900: #14532d;
  --color-green-950: #052e16;

  --color-red-200: #fecaca;    --color-red-400: #f87171;    /* dash --red */
  --color-red-700: #f87171;    --color-red-800: #dc2626;
  --color-red-050: #450a0a;    --color-red-100: #7f1d1d;    --color-red-900: #7f1d1d;
  --color-red-950: #450a0a;

  --color-orange-200: #fde68a; --color-orange-400: #fbbf24; /* dash --amber */
  --color-orange-600: #fbbf24; --color-orange-700: #d97706;
  --color-orange-050: #451a03; --color-orange-100: #78350f; --color-orange-900: #78350f;
  --color-orange-950: #451a03;
}
html.theme_light {
  --color-green-200: #bbf7d0;  --color-green-400: #16a34a;
  --color-green-700: #15803d;  --color-green-800: #166534;
  --color-green-050: #f0fdf4;  --color-green-100: #dcfce7;  --color-green-900: #14532d;
  --color-green-950: #052e16;

  --color-red-200: #fecaca;    --color-red-400: #dc2626;
  --color-red-700: #b91c1c;    --color-red-800: #991b1b;
  --color-red-050: #fef2f2;    --color-red-100: #fee2e2;    --color-red-900: #7f1d1d;
  --color-red-950: #450a0a;

  --color-orange-200: #fde68a; --color-orange-400: #b45309;
  --color-orange-600: #b45309; --color-orange-700: #92400e;
  --color-orange-050: #fffbeb; --color-orange-100: #fef3c7; --color-orange-900: #78350f;
  --color-orange-950: #451a03;
}

/* Ornament palettes -> the neutral ramp, both themes. */
html.theme_dark,
html.theme_light {
  --color-teal-050:   #fafafa;  --color-teal-100:   #ededed;
  --color-teal-200:   #d4d4d4;  --color-teal-300:   #bcbcbc;
  --color-teal-400:   #a1a1a1;  --color-teal-500:   #8a8a8a;
  --color-teal-600:   #6b6b6b;  --color-teal-700:   #3a3a3a;
  --color-teal-800:   #232323;  --color-teal-900:   #161616;
  --color-teal-950:   #0a0a0a;

  --color-coral-050:  #fafafa;  --color-coral-100:  #ededed;
  --color-coral-200:  #d4d4d4;  --color-coral-300:  #bcbcbc;
  --color-coral-400:  #a1a1a1;  --color-coral-500:  #8a8a8a;
  --color-coral-600:  #6b6b6b;  --color-coral-700:  #3a3a3a;
  --color-coral-800:  #232323;  --color-coral-900:  #161616;
  --color-coral-950:  #0a0a0a;

  --color-purple-050: #fafafa;  --color-purple-100: #ededed;
  --color-purple-200: #d4d4d4;  --color-purple-300: #bcbcbc;
  --color-purple-400: #a1a1a1;  --color-purple-500: #8a8a8a;
  --color-purple-600: #6b6b6b;  --color-purple-700: #3a3a3a;
  --color-purple-800: #232323;  --color-purple-900: #161616;
  --color-purple-950: #0a0a0a;

  --color-pink-300:   #bcbcbc;  --color-pink-600:   #6b6b6b;
}

/* 27.4 The focus ring inside the vault.
   --color-border-focus derives from --color-black / --color-white, which are
   already neutral, so nothing to re-point -- recorded so a future reader does
   not go looking for a rule that deliberately does not exist. The login shell's
   blue ring (25.5) is scoped to body.layout_frontend and is unaffected by any
   of this. */
