/* ==========================================================================
   Construmarket Theme - corporate.css
   Global design system loaded on every GLPI page (add_css hook).
   Pure presentation: no selector here touches GLPI's markup structure,
   only visual properties (color, spacing, radius, shadow, motion).
   Depends on the CSS variables defined in corporate-vars.css.
   ========================================================================== */

/* -- Modern derived tokens, computed from the 6 base colors set in the
      config panel (color-mix() keeps everything "same family, richer
      shades" automatically, even if an admin changes the base palette). -- */
:root {
    /* "Azul activo / selección" and "Amarillo hover" from the 2026 palette
       spec are exact, deliberately-chosen hexes, not a value meant to be
       re-derived from --cm-primary/--cm-accent via color-mix() like the
       tokens below - hardcoded here instead of computed so they land
       exactly on-spec. Every other token in this block stays a live
       derivation on purpose (unchanged philosophy - see the file-level
       comment on this block): if an admin ever changes the base primary/
       accent color from the config panel, everything BELOW keeps
       repainting itself in that new color's family automatically; these
       two just won't happen to still be *this exact* hex once that
       happens, same as any other fixed brand value would. */
    --cm-primary-vivid: #3478D1;
    --cm-primary-soft: color-mix(in srgb, var(--cm-primary) 35%, white);
    --cm-gold-soft: color-mix(in srgb, var(--cm-accent) 78%, white);
    --cm-gold-deep: #E5AD00;
    --cm-ink: color-mix(in srgb, var(--cm-primary-dark) 88%, black);
    --cm-shadow-primary: color-mix(in srgb, var(--cm-primary-dark) 45%, transparent);
    /* Table header background - a step above the page's own --cm-gray-bg
       instead of matching it exactly, so header rows read as a distinct
       band rather than blending into the page (2026 palette spec: light
       #EEF4FB / dark #123A73, the latter set in dark-mode.css). */
    --cm-table-header-bg: #EEF4FB;
    --cm-radius-lg: 20px;
    --cm-radius-md: 14px;
    --cm-radius-sm: 10px;
    --cm-radius-xs: 8px;
    --cm-radius-pill: 999px;
    --cm-font-display: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

    /* -- Navy/blue/yellow tonal scale --
       The 2026 "ConstruMarket UI" design system (sidebar, KPI tiles,
       buttons, badges, calendar) is built on a full tonal scale, not just
       the 3 blues + 1 gold this plugin's config panel exposes. Every step
       here is *derived* from those same admin-configurable base colors via
       color-mix() - same principle as --cm-primary-vivid/--cm-ink above -
       so the richer look still repaints itself automatically if an admin
       ever changes the base palette, instead of being hard-coded hexes
       that would silently drift out of sync with the config panel. */
    --cm-navy-950: color-mix(in srgb, var(--cm-primary-dark) 88%, black);
    --cm-navy-900: color-mix(in srgb, var(--cm-primary-dark) 94%, black);
    --cm-navy-800: var(--cm-primary-dark);
    --cm-navy-700: color-mix(in srgb, var(--cm-primary-dark) 78%, var(--cm-primary));
    --cm-navy-600: color-mix(in srgb, var(--cm-primary-dark) 52%, var(--cm-primary));

    --cm-blue-700: color-mix(in srgb, var(--cm-primary) 88%, black);
    --cm-blue-600: var(--cm-primary);
    --cm-blue-500: color-mix(in srgb, var(--cm-primary) 76%, white);
    --cm-blue-400: color-mix(in srgb, var(--cm-primary) 60%, white);
    --cm-blue-300: color-mix(in srgb, var(--cm-primary) 42%, white);
    --cm-blue-200: color-mix(in srgb, var(--cm-primary) 26%, white);
    --cm-blue-100: var(--cm-primary-light);
    --cm-blue-50: color-mix(in srgb, var(--cm-primary-light) 55%, white);

    --cm-yellow-500: var(--cm-accent);
    --cm-yellow-400: color-mix(in srgb, var(--cm-accent) 86%, white);
    --cm-yellow-100: color-mix(in srgb, var(--cm-accent) 22%, white);

    --cm-purple-600: #7558C9;
    --cm-purple-100: #EEE8FF;
    --cm-orange-600: color-mix(in srgb, var(--cm-accent) 75%, black);
    --cm-orange-100: color-mix(in srgb, var(--cm-accent) 32%, white);

    --cm-shadow-xs: 0 2px 5px rgba(0, 31, 77, .06);
    --cm-shadow-sm: 0 4px 12px rgba(0, 31, 77, .08);
    --cm-shadow-md: 0 10px 28px rgba(0, 31, 77, .11);
    --cm-shadow-lg: 0 24px 70px rgba(0, 22, 56, .2);

    /* Piggy-back on GLPI's own Tabler CSS variables so every
       Bootstrap-based component (buttons, active nav items, focus rings...)
       re-colors app-wide without overriding each rule individually. */
    --tblr-primary: var(--cm-primary);
    --tblr-primary-rgb: 0, 87, 184;
    --tblr-link-color: var(--cm-primary);
    --tblr-link-color-rgb: 0, 87, 184;
}

body {
    color: var(--cm-text);
    -webkit-font-smoothing: antialiased;
}

/* Safety net against horizontal overflow on phones - reported live: a
   ghost strip of extra blank width appears to the right of the page
   (with its own horizontal scrollbar) specifically after focusing a form
   input on a real touchscreen device, which is the classic symptom of a
   mobile browser's on-screen keyboard triggering a viewport-resize
   recalculation that some element gets briefly wider than the actual
   visible screen for. This doesn't try to find and fix that one
   element/script (GLPI core renders this specific page, not this theme -
   nothing here controls the User edit form's own grid), it just clamps
   the whole page so nothing can ever push it wider than the real
   viewport, regardless of the cause. html (not just body) is included -
   the scrollbar reported live sits at the very edge of the whole page,
   consistent with <html> itself being the element that grew. Scoped to
   phone/tablet widths only, matching this theme's other mobile-only
   breakpoints - a wide desktop table that intentionally scrolls
   sideways inside its own .table-responsive wrapper is a different,
   already-contained kind of horizontal scroll and is left alone. */
@media (max-width: 1199.98px) {
    html,
    body {
        max-width: 100vw;
        overflow-x: hidden;
    }
}

a {
    color: var(--cm-primary);
}

::selection {
    background: var(--cm-accent);
    color: var(--cm-ink);
}

/* -- Primary CTA = the gold/yellow accent, not blue - the ConstruMarket
      UI design system treats yellow as the "do this" action color and
      navy/blue as structural (nav, headers, secondary actions). Dark
      navy text on the light gold gradient keeps contrast high (a white
      label on gold reads poorly), same pairing as the sidebar's brand
      mark badge below. -- */
.btn-primary {
    background: linear-gradient(180deg, var(--cm-yellow-400), var(--cm-yellow-500)) !important;
    border-color: color-mix(in srgb, var(--cm-yellow-500) 80%, black) !important;
    /* Fixed, not var(--cm-navy-950) - the palette spec calls for this
       exact text color on the gold CTA in BOTH light and dark mode (it's
       listed identically under each), independent of --cm-text (which
       does switch between modes) or of whatever --cm-primary-dark
       happens to be. */
    color: #172033 !important;
    border-radius: 10px;
    font-weight: 700;
    box-shadow: 0 6px 16px color-mix(in srgb, var(--cm-yellow-500) 35%, transparent);
    transition: transform .18s ease, box-shadow .18s ease, filter .18s ease;
}

.btn-primary:hover,
.btn-primary:focus {
    filter: brightness(1.04);
    transform: translateY(-1px);
    box-shadow: 0 10px 22px color-mix(in srgb, var(--cm-yellow-500) 38%, transparent);
    color: #172033 !important;
}

.btn-primary * {
    color: inherit !important;
}

/* Tabler's own "is submitting" state (.btn-loading, toggled by GLPI core
   JS - e.g. Forms/RendererController.js while it awaits the ticket-creation
   AJAX response) works by setting color:transparent on the button so the
   label disappears and only its ::after spinner shows. .btn-primary above
   forces a literal navy color with !important, and loads after Tabler's
   core CSS, so on a gold CTA that navy text silently wins the tie and stays
   readable - the spinner still spins behind it, but with the label still
   legible on top there is no visible "this is working" cue (reported live:
   clicking "Continuar"/"Enviar" on the Alta de Colaborador form gave no
   feedback that anything was happening). Two classes here (0,2,0) beats the
   single-class .btn-primary rule (0,1,0) even with matching !important, so
   this restores Tabler's transparent-label behavior without touching the
   gold background/border or any other non-loading state. */
.btn-primary.btn-loading,
.btn-primary.btn-loading * {
    color: transparent !important;
}

.btn-outline-primary {
    color: var(--cm-primary) !important;
    border-color: var(--cm-primary) !important;
    border-radius: 10px;
}

.btn-outline-primary:hover {
    background-color: var(--cm-primary) !important;
    color: #FFFFFF !important;
}

/* Any other component that inherited the swapped --tblr-primary
   background (badges, "bg-primary" utility, buttons that aren't
   .btn-primary) needs its text forced too: Tabler pairs each background
   with a precomputed text color that was tuned for GLPI's original blue,
   not for a custom override, so it can land dark-on-dark otherwise. */
.bg-primary,
.badge.bg-primary,
.text-bg-primary {
    color: #FFFFFF !important;
}

/* -- Tabs: same reasoning as buttons above - force explicit, readable
      text on the active/selected state instead of trusting Tabler's
      precomputed pairing. Covers both the modern Bootstrap tab markup
      and GLPI's legacy jQuery-UI-flavoured tab classes. -- */
.nav-tabs .nav-link.active,
.nav-tabs .nav-item.show .nav-link,
.nav-pills .nav-link.active,
.ui-tabs .ui-tabs-active > a,
.ui-state-active {
    background-color: var(--cm-primary) !important;
    border-color: var(--cm-primary) !important;
    color: #FFFFFF !important;
}

.nav-tabs .nav-link:not(.active),
.nav-pills .nav-link:not(.active) {
    color: var(--cm-text);
}

/* -- Brand swap: whatever GLPI paints inside <a class="navbar-brand"> for
      its own logo (templates/layout/parts/page_header.html.twig) - be it a
      background-image, a CSS mask or literal text - is collapsed to
      nothing here (font-size:0 kills stray text, visibility:hidden kills
      the icon span), then "CMK" is painted fresh on top via an absolutely
      positioned pseudo-element. This is deliberately defensive: it does
      not assume *how* GLPI renders its own mark, only that it lives
      inside .navbar-brand. -- */
.navbar:has(.navbar-brand):not(.navbar-vertical),
header:has(.navbar-brand) {
    overflow: visible !important;
    /* Fluid, not a single fixed value - scales continuously between a
       phone-safe floor and the full desktop height instead of jumping
       between two hard-coded breakpoints (a fixed 88px was measured
       against a desktop-width header only; a phone/tablet header that
       ends up shorter than that clipped the logo icon against its own
       shrunk box). Floor raised 56->72px - reported live next to a
       reference screenshot as looking cramped/smaller than intended on a
       real phone, not just misaligned; 56px left too little room for a
       generously-sized icon and avatar to sit in comfortably. Floor
       raised again 72->86px alongside the icon's own height (54->66px)
       growing further, so a bigger icon still has real margin above/
       below it instead of barely fitting the row again. */
    min-height: clamp(86px, 12vw, 88px);
}

.navbar-brand {
    position: relative;
    display: inline-flex !important;
    align-items: center;
    overflow: visible !important;
    font-size: 0;
    color: transparent;
}

.navbar-brand .glpi-logo {
    visibility: hidden;
}

/* Belt-and-suspenders on top of the two rules above: reported live that
   the classic Central interface hides GLPI's own logo/wordmark cleanly
   (font-size:0 above collapses its text, .glpi-logo above hides its
   icon), but the self-service Helpdesk portal's own topbar keeps showing
   a real "Construmarket" wordmark at full size even on a narrow phone -
   this theme's own icon+text (painted via ::after below) still renders
   too, so the Helpdesk header ends up with BOTH a real logo/text AND
   this theme's own copy competing for the same space. Almost certainly
   the Helpdesk template's real logo/text sits in a child element with
   its own explicit font-size that overrides the inherited font-size:0
   above, or isn't classed .glpi-logo. Rather than guess the exact class,
   this hides ANY real element GLPI puts directly inside .navbar-brand,
   whatever it is - only this theme's own ::after pseudo-element (not a
   real child, unaffected by this) is left to render the brand mark,
   consistently on every interface. */
.navbar-brand > * {
    display: none !important;
}

/* Icon+wordmark size ("--cm-brand-icon") is fluid via clamp(), not a
   fixed 52px - a fixed size was tuned against a desktop-width header and
   clipped/overflowed on real phone widths, only ONLY in the horizontal
   self-service header (the classic admin interface reuses the same
   .navbar-brand markup inside a ~230px-wide VERTICAL sidebar,
   .navbar-vertical, which gets its own compact variant below). Every
   other value (font-size, padding-left, background-size) is derived from
   this one variable via calc()/var() so the icon, text and their gap
   always stay in the same proportion at any width instead of drifting
   out of sync. min-width uses min(...) so it can still shrink below
   300px on a phone narrower than that, instead of forcing overflow. */
.navbar:not(.navbar-vertical) .navbar-brand,
header:has(.navbar-brand) .navbar-brand {
    --cm-brand-icon: clamp(30px, 7vw, 52px);
    min-width: min(300px, 100%);
}

/* Icon badge, split into its own ::before instead of living inside the
   text ::after's background. Reported live as "aplastado" (squished) even
   after raising --cm-brand-icon (the height) alone: the OLD single ::after
   held the image inside a box that was --cm-brand-icon TALL but 210px
   WIDE (mobile) or auto-width-to-content (desktop) - background-size:
   contain math never actually stretched the image inside that box (it's
   mathematically impossible), but sizing a near-square logo by height
   alone inside a much wider box, left-aligned with no border-radius, reads
   as a flat rectangle bleeding into the header rather than a defined
   badge/icon - which is what "aplastado" most likely meant, not literal
   distortion. A dedicated, explicitly SQUARE box (width == height, both
   --cm-brand-icon) with its own border-radius fixes that regardless of
   the exact perceptual cause, and can never distort the image since
   width/height are locked equal. */
.navbar:not(.navbar-vertical) .navbar-brand::before,
header:has(.navbar-brand) .navbar-brand::before {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    width: var(--cm-brand-icon);
    height: var(--cm-brand-icon);
    border-radius: 20%;
    background: url("../pics/logo.png") center / contain no-repeat;
    box-shadow: 0 2px 6px rgba(0, 0, 0, .25);
}

.navbar:not(.navbar-vertical) .navbar-brand::after,
header:has(.navbar-brand) .navbar-brand::after {
    content: "Construmarket";
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    height: var(--cm-brand-icon);
    white-space: nowrap;
    font-size: clamp(1.05rem, 2.4vw, 1.7rem);
    font-weight: 800;
    letter-spacing: .3px;
    color: #FFFFFF;
    text-shadow: 0 1px 4px rgba(0, 0, 0, .35);
    padding-left: calc(var(--cm-brand-icon) + 16px);
}

/* -- Mobile header row: keep toggler + brand + user avatar on one line --
   Real markup (templates/layout/parts/page_header.html.twig, self-service
   "topbar" variant): <header class="navbar ... navbar-expand-xl topbar">
   > div.header-container.flex-xl-nowrap > [toggler, .navbar-brand,
   div.d-lg-none > user avatar, nav#navbar-menu (the collapsible link
   list, Bootstrap's own .collapse.navbar-collapse)] - all FOUR as direct
   siblings in the same flex row, not just the first three. That last one
   matters: it is empty/display:none until the toggler opens it, and once
   open it needs the FULL row width for its own links - Bootstrap's whole
   collapse mechanism depends on the row's default flex-wrap:wrap to drop
   it onto a fresh line when there's no room left, which is always, since
   it wants 100%. An earlier version of this fix forced nowrap on the
   whole row to solve the avatar-wrapping bug below, and broke exactly
   that: with nowrap forced, the opened menu had nowhere to wrap to and
   rendered squeezed into an ~0px sliver - reported live as "el menu de
   hamburguesa no aparece" (toggler responded, `.show` class did get
   added, but no menu items were visible).
   The actual, narrower fix: leave flex-wrap alone (still the default
   wrap, so navbar-menu keeps working exactly as GLPI intends), and
   address the ACTUAL cause of the avatar wrapping - this theme's own
   `.navbar-brand` above claims `min-width: min(300px, 100%)`, which on
   a real phone width is by itself already enough to leave no room for
   the trailing avatar on the first row, forcing IT to wrap before
   navbar-menu ever needs to. Letting the brand shrink past that 300px
   floor here (icon/text stay exactly as fluid-sized via clamp() above -
   only the box's minimum width changes) is enough on its own for
   toggler + brand + avatar to fit their one row normally; navbar-menu,
   wanting a full separate row regardless, keeps wrapping onto its own
   line exactly as before. margin-left:auto on the avatar wrapper is kept
   as a belt-and-suspenders right-alignment guarantee - harmless whether
   or not wrapping ends up happening. */
/* flex-shrink alone (tried first) turned out unreliable here: under the
   container's default flex-wrap:wrap (required so navbar-menu, a fourth
   sibling, can still drop to its own full-width line when opened - see
   the long note above), a browser's wrap algorithm gets to choose between
   shrinking an earlier flex item (the brand) or wrapping a later one (the
   avatar) to make a line fit, and reproduced live twice now, it kept
   choosing to wrap the avatar rather than shrink the brand, even with
   flex-shrink:1 min-width:0 set - so the fix isn't "let it shrink", it's
   "make its un-shrunk width small and fixed enough that shrinking is
   never needed in the first place". Hiding the "Construmarket" wordmark
   and keeping only the icon does that: the brand's natural width drops to
   a small, predictable ~40-70px (icon + fixed padding, no longer
   proportional to a translatable text string) that reliably fits
   alongside the toggler and avatar within a single row on any real phone
   width, without touching the container's wrap behavior at all - which
   is also what leaves navbar-menu's own line-break untouched and working. */
@media (max-width: 1199.98px) {
    /* Breathing room between toggler/brand/avatar - GLPI's own markup
       sets no gap between these three (reported live as the hamburger
       icon touching the logo before this was added). */
    header:has(.navbar-brand) .header-container {
        column-gap: .6rem;
    }

    /* Reported live next to a reference screenshot: the icon reads as
       noticeably smaller/"aplastado" than intended on a real phone, not
       just misaligned - the shared desktop/mobile floor (30px, from the
       clamp() a few rules up) is too conservative once the header itself
       is the taller 72px from the min-height bump above. Bumped for
       phones specifically, independent of the shared desktop value. */
    /* --cm-brand-icon only ever controls the icon's own square box
       (height here, and its width via background-size:contain scaling
       proportionally to match - logo.png itself is a near-square PNG,
       606x575, confirmed live, so contain can never stretch it off-
       ratio). The OUTER .navbar-brand box's own width (210px, a couple
       rules down) is a completely separate, fixed value that does NOT
       depend on this variable - raising this only makes the icon itself
       bigger/clearer within that same unchanged 210px row budget,
       exactly as requested ("hazlo más alto, deja el mismo ancho"). */
    header:has(.navbar-brand) .navbar-brand {
        --cm-brand-icon: 66px;
    }

    /* Icon-only mode (an earlier version of this block: fixed width equal
       to just the icon, content:"" hiding the wordmark) is gone - once
       toggler/brand/avatar were all correctly sized and centered (the
       fixes above and below), there turned out to be enough room to keep
       the "Construmarket" wordmark too, which is what was actually
       requested all along; icon-only was only ever a workaround for the
       avatar-wrapping bug, not the end goal. A FIXED width (not
       shrink-based flex sizing) is kept deliberately though - this file's
       own older notes documented flex-shrink alone as unreliable here
       (the browser kept choosing to wrap the avatar rather than shrink
       the brand) - so the safe pattern from that lesson is reused: give
       the box a small, fixed, predictable width that's known to leave
       enough room for the toggler and avatar, sized to comfortably fit
       icon+wordmark, with overflow/ellipsis as a safety net for any
       phone narrow enough that it still doesn't quite fit. */
    header:has(.navbar-brand) .navbar-brand {
        min-width: 0 !important;
        width: 210px !important;
        overflow: hidden !important;
    }

    header:has(.navbar-brand) .navbar-brand .glpi-logo {
        width: 0 !important;
        height: 0 !important;
    }

    /* content changed to the full "Grupo Construmarket" (was just
       "Construmarket") per request - a longer string needs both a wider
       box (180->210px above) and a smaller font-size than the desktop
       rule's own 1.05rem to have any real chance of fitting without
       relying on the ellipsis safety net for every single phone.
       !important kept throughout, defensively, per this block's own
       history of a GLPI/Tabler core rule on this exact selector winning
       ties on individual sub-properties. white-space/text-overflow are
       that safety net: on a phone too narrow even for this, it truncates
       with an ellipsis instead of overflowing past the box (which is
       what originally broke the avatar's row) or wrapping. */
    header:has(.navbar-brand) .navbar-brand::after {
        content: "Grupo Construmarket" !important;
        padding-left: calc(var(--cm-brand-icon) + 10px) !important;
        width: 210px !important;
        height: var(--cm-brand-icon) !important;
        /* Nudged down again (.82->.76rem) - the bigger icon's own
           padding-left now claims more of the fixed 210px box, leaving
           less room for text than before; ellipsis below still catches
           whatever doesn't fit on the narrowest real phones. */
        font-size: .76rem !important;
        white-space: nowrap !important;
        overflow: hidden !important;
        text-overflow: ellipsis !important;
    }

    /* Reported live on the self-service Helpdesk topbar: the JZ avatar
       chip sits visibly lower than the hamburger toggler and the logo,
       not sharing their vertical center - the three don't read as one
       aligned row. align-items:center on the row (forced explicitly,
       rather than trusting whatever GLPI/Bootstrap's own default for
       this container happens to be) plus align-self:center pinned
       directly on the avatar wrapper covers this regardless of which of
       the two was actually the mismatch. */
    header:has(.navbar-brand) .header-container {
        align-items: center !important;
    }

    header:has(.navbar-brand) .header-container > .d-lg-none {
        margin-left: auto;
        flex-shrink: 0;
        align-self: center !important;
        /* Reported live, still off after align-self alone: the mismatch
           survived the previous fix, meaning the actual misalignment is
           most likely one level deeper - GLPI's own avatar/dropdown-
           toggle markup inside this wrapper (unknown exact structure,
           same blind spot as the Helpdesk brand text elsewhere in this
           file) probably isn't itself a centered flex item by default.
           Turning THIS wrapper into its own flex+center context, and
           forcing every descendant to also center-align itself within
           whatever flex/box context it's actually in, covers that
           regardless of how many levels of nesting GLPI's real markup
           turns out to have - already-correct light-mode/desktop
           behavior is untouched since this whole block only ever applies
           below 1199.98px. */
        display: flex !important;
        align-items: center !important;
    }

    header:has(.navbar-brand) .header-container > .d-lg-none,
    header:has(.navbar-brand) .header-container > .d-lg-none * {
        align-self: center !important;
        margin-top: 0 !important;
        margin-bottom: 0 !important;
    }

    /* Same "reads smaller than the reference" note as the icon above -
       GLPI's own avatar chip size wasn't touched by this theme before,
       so it rendered at whatever compact default GLPI/Tabler ships. */
    header:has(.navbar-brand) .header-container > .d-lg-none .avatar,
    header:has(.navbar-brand) .header-container > .d-lg-none [class*="avatar"] {
        width: 42px !important;
        height: 42px !important;
        font-size: 1rem !important;
    }
}

/* Compact variant for the classic interface's vertical sidebar - same
   brand mark, sized to fit a ~230px-wide desktop column. Bootstrap
   collapses this same .navbar-vertical into a horizontal mobile topbar
   below "lg" (verified live), where its own real height varies with the
   device - fluid for the same reason as the horizontal header above. */
.navbar-vertical .navbar-brand {
    /* Floor bumped 26->30px: logo.png is the real Construmarket mark (3
       gradient swooshes + a gloss highlight), busier than the flat
       single-tone placeholder this was originally tuned against - it
       needs a few more px to stay legible at the small end. */
    --cm-brand-icon: clamp(30px, 8vw, 34px);
    min-width: 0;
}

/* width:100% only makes sense on the real desktop sidebar column, where
   the brand is the only thing in its row. Below "lg" (992px), Bootstrap
   turns this same .navbar-vertical into a horizontal mobile topbar where
   the brand shares its row with the user-avatar menu
   (.navbar-nav.order-md-last.user-menu, confirmed live) - forcing the
   brand to 100% width there left zero room for the avatar, pushing it
   onto its own second line and making the header awkwardly tall
   (reproduced live: a two-row mobile header instead of one compact bar).
   Auto width lets the brand size to its own content so the avatar can
   sit beside it on the same row, as GLPI's own markup already intends. */
@media (min-width: 992px) {
    .navbar-vertical .navbar-brand {
        width: 100%;
        /* Reported live: the logo sits flush against the very top edge of
           the sidebar, right under the 3px yellow accent line
           (.navbar-vertical::before), AND is clipped/not fully visible.
           First attempt used padding-top alone, which was wrong: this box
           has no real in-flow content (the icon is an absolutely-
           positioned ::after, the real .glpi-logo span is zeroed to 0x0
           above), so its own height is otherwise 0 - padding-top:1.25rem
           (20px) made the box only 20px tall, but the icon itself is
           30-34px tall (--cm-brand-icon). Centered via top:50% within a
           box SHORTER than itself, the icon's top edge sat above the
           box's own top edge (and above .navbar-vertical's padding-box) -
           still hugging the very top, with an ancestor's overflow
           clipping the part that stuck out above. min-height (not just
           padding) gives the box real vertical room - large enough that
           even the icon's tallest breakpoint (34px) centers with genuine
           margin above and below it, instead of again barely fitting. */
        min-height: 76px;
    }
}

/* left:0 (matching the horizontal header's own brand::after) left the
   icon+wordmark pinned to the left edge of this box, which is 100% of
   the ~230px sidebar column wide (see the width:100% rule above) -
   reported live as the logo sitting flush-left with a visibly empty gap
   to its right instead of centered in that column, unlike the horizontal
   header's brand (which sits flush-left against OTHER nav items sharing
   its row, where centering would look wrong - a real difference between
   the two layouts, not an inconsistency). left:50% + translate(-50%,-50%)
   centers the icon+text unit itself, not just the icon, so it stays
   centered whether the sidebar is expanded (icon+text) or collapsed to
   the icon-only rail below. */
.navbar-vertical .navbar-brand::after {
    content: "Construmarket";
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    display: inline-flex;
    align-items: center;
    height: var(--cm-brand-icon);
    white-space: nowrap;
    font-size: clamp(.95rem, 4vw, 1.05rem);
    font-weight: 800;
    letter-spacing: .2px;
    color: #FFFFFF;
    padding-left: calc(var(--cm-brand-icon) + 8px);
    background: url("../pics/logo.png") left center / contain no-repeat;
}

/* GLPI remembers "collapsed sidebar" (icon-only, ~70px rail) per user via
   a body.navbar-collapsed class - every other sidebar item already hides
   its label in that state and shows only its icon, but the wordmark text
   above kept rendering at full size and overflowed past the narrow rail
   (reproduced live: "Construmarket" spilling out over the page content).
   Same fix as everywhere else in the collapsed rail: text disappears,
   only the icon mark remains, sized to actually fit the rail.
   Wrapped in this min-width media query on purpose: navbar-collapsed is a
   stored per-user PREFERENCE, not a mobile state, and stays on the body
   even on a phone - where Bootstrap has already collapsed .navbar-vertical
   into the horizontal hamburger drawer instead of the desktop rail. Without
   this guard the rule fired there too and hid "Construmarket" on mobile
   (reproduced live), which is the exact opposite of what was asked. */
@media (min-width: 992px) {
    body.navbar-collapsed .navbar-vertical .navbar-brand::after {
        content: "";
        width: var(--cm-brand-icon);
        padding-left: 0;
    }
}

/* ==========================================================================
   Vertical sidebar (classic admin interface)
   Real markup verified live: <aside class="navbar navbar-vertical ...">
   > ul.navbar-nav > li.nav-item.dropdown > button.nav-link.dropdown-toggle
   (icon + span.menu-label) whose click opens a *flyout* .dropdown-menu
   mega-panel with columns of real links - GLPI's sidebar is a mega-menu,
   not a flat expand-in-place list. Only the dark navy gradient/texture,
   the top accent line and the top-level row chrome are restyled here;
   the flyout itself already inherits the global light .dropdown-menu
   styling below, and the open/close mechanism is entirely GLPI's own.
   ========================================================================== */
/* Reported live as reading like two flat, hard-stepped color blocks
   instead of one smooth gradient - the diagonal repeating-linear-gradient
   texture plus a late 58% stop made the navy-700->navy-900 transition
   look like a visible seam rather than a continuous blend. Swapped for
   the exact same gradient formula the login page's own hero panel uses
   (.cm-login-shell__brand in login.css) - two soft radial glows over one
   continuous diagonal linear-gradient - so the sidebar and the login
   screen are literally the same background treatment, per the request
   to make them consistent, not just a similarly-blue variant of it. */
.navbar-vertical {
    background:
        radial-gradient(circle at 85% 8%, color-mix(in srgb, var(--cm-accent) 14%, transparent) 0%, transparent 45%),
        radial-gradient(circle at 8% 92%, color-mix(in srgb, var(--cm-primary) 35%, transparent) 0%, transparent 45%),
        linear-gradient(165deg, var(--cm-ink) 0%, var(--cm-primary-dark) 55%, var(--cm-primary) 100%) !important;
    /* Flat edge (no shadow) requested to match a reference design. */
    box-shadow: none;
}

.navbar-vertical::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 3px;
    background: var(--cm-yellow-400);
    z-index: 1;
}

/* Same faint dot grid as the login hero panel's own ::before (login.css) -
   ::before is already the top accent line above, so this reuses ::after
   instead; same visual result. */
.navbar-vertical::after {
    content: "";
    position: absolute;
    inset: 0;
    background-image: radial-gradient(rgba(255, 255, 255, .18) 1px, transparent 1px);
    background-size: 16px 16px;
    opacity: .5;
    pointer-events: none;
}

.navbar-vertical .navbar-nav > .nav-item > .nav-link.dropdown-toggle {
    display: flex;
    align-items: center;
    gap: .65rem;
    margin: .15rem .6rem;
    padding: .6rem .7rem;
    border-radius: 10px;
    color: #c8d7ee !important;
    font-weight: 650;
    font-size: .84rem;
    transition: background-color .15s ease, color .15s ease;
}

.navbar-vertical .navbar-nav > .nav-item > .nav-link.dropdown-toggle:hover,
.navbar-vertical .navbar-nav > .nav-item > .nav-link.dropdown-toggle.show,
.navbar-vertical .navbar-nav > .nav-item.show > .nav-link.dropdown-toggle {
    background: rgba(255, 255, 255, .1);
    color: #FFFFFF !important;
}

.navbar-vertical .navbar-nav > .nav-item > .nav-link.dropdown-toggle > i:first-child {
    display: inline-flex;
    width: 28px;
    height: 28px;
    flex: 0 0 28px;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    color: #a9bfdf;
    background: rgba(255, 255, 255, .05);
    font-size: .95rem;
}

.navbar-vertical .navbar-nav > .nav-item.show > .nav-link.dropdown-toggle > i:first-child,
.navbar-vertical .navbar-nav > .nav-item > .nav-link.dropdown-toggle:hover > i:first-child {
    color: var(--cm-yellow-400);
    background: rgba(253, 187, 17, .12);
}

/* The flyout mega-panel's own heading ("Activos", "Asistencia"...) - same
   quiet uppercase-kicker treatment used for section titles elsewhere in
   this theme, instead of GLPI's plain bold default. */
.navbar-vertical .dropdown-menu .dropdown-header {
    color: color-mix(in srgb, var(--cm-ink) 60%, transparent);
    font-size: .68rem;
    font-weight: 800;
    letter-spacing: .1em;
    text-transform: uppercase;
}

/* -- Contrast fix: any muted/secondary text GLPI renders inside the dark
      navbar (e.g. the "Grupo Construmarket" line under the user's name,
      or the nav links themselves) defaults to a low-contrast gray that
      barely reads on a dark blue background. Force a light, near-white
      tone everywhere inside that bar. -- */
.navbar,
header:has(.navbar-brand) {
    --tblr-secondary-color: rgba(255, 255, 255, .85);
}

.navbar .text-secondary,
.navbar .text-muted,
header:has(.navbar-brand) .text-secondary,
header:has(.navbar-brand) .text-muted {
    color: rgba(255, 255, 255, .85) !important;
}

.navbar-nav .nav-link,
.navbar .nav-link {
    color: rgba(255, 255, 255, .92) !important;
}

.navbar-nav .nav-link:hover,
.navbar-nav .nav-link.active,
.navbar .nav-link:hover,
.navbar .nav-link.active {
    color: #FFFFFF !important;
}

/* -- Header polish: gradient instead of flat navy, a thin brand-gradient
      cap along the bottom edge, pill-shaped nav links with a gold
      underline on the active page, and a re-colored user avatar (GLPI
      assigns a semi-random background per user, which just as often
      clashes with the palette as it matches it - forcing the brand
      gradient keeps every user's avatar consistent and on-brand).
      Scoped to :not(.navbar-vertical): the classic admin interface reuses
      the same .navbar/.nav-link classes for its left SIDEBAR, where a
      horizontal gradient, a bottom accent line and forced pill padding
      make no sense and were breaking that layout. -- */
.navbar:has(.navbar-brand):not(.navbar-vertical),
header:has(.navbar-brand) {
    background: linear-gradient(100deg, var(--cm-ink) 0%, var(--cm-primary-dark) 55%, var(--cm-primary) 100%) !important;
    /* Flat edge (no shadow) requested to match a reference design. */
    box-shadow: none;
    position: relative;
}

.navbar:has(.navbar-brand):not(.navbar-vertical)::before,
header:has(.navbar-brand)::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 3px;
    background: linear-gradient(90deg, var(--cm-primary), var(--cm-accent));
}

.navbar:not(.navbar-vertical) .navbar-nav {
    gap: .2rem;
}

.navbar:not(.navbar-vertical) .nav-link,
header:has(.navbar-brand) .nav-link {
    position: relative;
    font-weight: 600;
    border-radius: 8px;
    padding: .55rem .9rem !important;
    transition: background-color .18s ease, color .18s ease;
}

.navbar:not(.navbar-vertical) .nav-link:hover,
header:has(.navbar-brand) .nav-link:hover {
    background-color: rgba(255, 255, 255, .1);
}

.navbar:not(.navbar-vertical) .nav-link.active::after,
header:has(.navbar-brand) .nav-link.active::after {
    content: "";
    position: absolute;
    left: .9rem;
    right: .9rem;
    bottom: .15rem;
    height: 2px;
    border-radius: 2px;
    background: var(--cm-accent);
}

.navbar .avatar,
.navbar [class*="avatar"] {
    background: linear-gradient(135deg, var(--cm-primary-vivid), var(--cm-primary)) !important;
    color: #FFFFFF !important;
    font-weight: 700;
    box-shadow: 0 0 0 2px rgba(255, 255, 255, .3);
}

/* Bootstrap modals (delete confirmations, item-creation popups, etc.) -
   dark navy header with a gold bottom border, matching the ConstruMarket
   UI modal spec. Only chrome; GLPI's own modal open/close/focus-trap
   logic is untouched. */
.modal-content {
    border: none;
    border-radius: var(--cm-radius-md);
    /* Same clip-vs-shadow conflict as .grid-stack-item-content below:
       overflow:hidden here (needed to round the header/body/footer to
       these corners) clips a same-element box-shadow to invisible too -
       filter:drop-shadow() is exempt from that clip. */
    filter: drop-shadow(var(--cm-shadow-lg));
    overflow: hidden;
}

.modal-header {
    border-bottom: 2px solid var(--cm-yellow-500);
    color: #FFFFFF;
    background: var(--cm-navy-900);
}

.modal-header .modal-title {
    color: #FFFFFF;
    font-weight: 800;
}

.modal-header .btn-close {
    filter: invert(1) grayscale(1) brightness(1.8);
}

.modal-footer {
    background: color-mix(in srgb, var(--cm-gray-bg) 60%, var(--cm-white));
}

/* Bootstrap dropdown menus (user menu, and everywhere else they appear) */
.dropdown-menu {
    border: none;
    border-radius: 12px;
    box-shadow: 0 16px 40px rgba(15, 34, 58, .18);
    padding: .5rem;
}

.dropdown-item {
    border-radius: 8px;
    padding: .55rem .8rem;
    font-weight: 500;
}

.dropdown-item:hover,
.dropdown-item:focus {
    background-color: var(--cm-primary-light);
    color: var(--cm-ink);
}

.dropdown-item.active,
.dropdown-item:active {
    background-color: var(--cm-primary) !important;
    color: #FFFFFF !important;
}

/* -- Generic reusable animation utilities, consumed by js/animations.js -- */
.cm-fade-in-up {
    opacity: 0;
    transform: translateY(16px);
    transition: opacity .5s ease, transform .5s ease;
}

.cm-fade-in-up.cm-visible {
    opacity: 1;
    transform: translateY(0);
}

.cm-fade-in {
    opacity: 0;
    transition: opacity .5s ease;
}

.cm-fade-in.cm-visible {
    opacity: 1;
}

/* ==========================================================================
   Corporate footer
   Injected at the end of <body> by js/corporate.js on every page rendered
   inside the self-service / public layout.
   ========================================================================== */
.cm-footer {
    background: linear-gradient(150deg, var(--cm-ink), var(--cm-primary-dark) 55%, var(--cm-primary));
    color: var(--cm-white);
    /* Reported live as feeling cramped/"pegado" on the Helpdesk portal -
       more room top and bottom, not just between the rows inside it. */
    padding: 3.25rem 1.5rem 2.25rem;
    margin-top: 3rem;
    font-size: .875rem;
}

.cm-footer__brand {
    display: flex;
    align-items: center;
    gap: .75rem;
    font-weight: 700;
    font-size: 1.1rem;
    margin-bottom: 1.1rem;
    color: var(--cm-white);
}

.cm-footer__brand img {
    height: 30px;
    width: auto;
}

.cm-footer__tagline {
    opacity: .9;
    margin-bottom: 1rem;
}

.cm-footer__countries {
    display: flex;
    flex-wrap: wrap;
    gap: .6rem 1.25rem;
    list-style: none;
    padding: 0;
    margin: 0 0 1.75rem;
    color: rgba(255, 255, 255, .92);
    font-weight: 500;
}

.cm-footer__countries li::before {
    content: "\2022";
    color: var(--cm-accent);
    margin-right: .4rem;
    font-weight: 700;
}

.cm-footer__meta {
    border-top: 1px solid rgba(255, 255, 255, .2);
    padding-top: 1.4rem;
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: .5rem;
    color: rgba(255, 255, 255, .78);
    font-size: .8rem;
}

@media (max-width: 576px) {
    .cm-footer__meta {
        flex-direction: column;
    }
}

/* ==========================================================================
   Tables & datatables
   Applies app-wide (search results, ticket lists, the "Casos en
   curso/resueltos" tables on the portal home, etc.) since add_css loads on
   every page. Only chrome (header, spacing, hover, pagination, radius) is
   touched - status dot/badge colors are left alone everywhere because
   they carry real meaning (ticket state) that must not be reinterpreted
   as a brand color.
   ========================================================================== */

/* No overflow:hidden here on purpose - see the long comment on .card::before
   below for why: clipping a table/its wrapper broke GLPI's own search
   results table (rows present, correctly styled, but clipped out of view
   whenever their async-loaded content settled after the container's
   height was first measured). Rounded corners on a live, dynamically
   sized table just aren't worth that risk. */
.table-responsive,
table.table,
.card-table {
    border-radius: var(--cm-radius-md);
}

/* border-radius above only rounds the corners of the table/wrapper's own
   box - a <table>'s border-radius is never inherited by each cell's own
   background paint, and without overflow:hidden (deliberately absent -
   see the comment above) the header row's background and the last body
   row's background stay perfectly square, poking past that rounded
   corner. The visible result is a corner that looks notched/cut instead
   of a clean curve - reported live comparing against a reference design.
   Rounding the actual corner-painting cells directly fixes this without
   ever touching overflow, so the async-content clipping bug documented
   above still can't happen. */
.table thead tr:first-child th:first-child,
table.table thead tr:first-child th:first-child {
    border-top-left-radius: var(--cm-radius-md);
}

.table thead tr:first-child th:last-child,
table.table thead tr:first-child th:last-child {
    border-top-right-radius: var(--cm-radius-md);
}

.table tbody tr:last-child td:first-child,
table.table tbody tr:last-child td:first-child {
    border-bottom-left-radius: var(--cm-radius-md);
}

.table tbody tr:last-child td:last-child,
table.table tbody tr:last-child td:last-child {
    border-bottom-right-radius: var(--cm-radius-md);
}

.table thead th,
table.table thead th {
    background: var(--cm-table-header-bg);
    color: color-mix(in srgb, var(--cm-text) 68%, transparent);
    text-transform: uppercase;
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .06em;
    border-bottom: 2px solid color-mix(in srgb, var(--cm-primary) 20%, transparent);
    padding-top: .9rem;
    padding-bottom: .9rem;
    white-space: nowrap;
}

.table tbody tr,
table.table tbody tr {
    transition: background-color .15s ease;
}

/* Zebra striping - a plain white table of many rows is harder to scan
   than one with a faint alternating tint, same convention as the
   ConstruMarket UI table spec. Kept very subtle (55% mix) so it never
   competes with the real status badge colors inside each row. */
.table tbody tr:nth-child(even) td,
table.table tbody tr:nth-child(even) td {
    background-color: color-mix(in srgb, var(--cm-gray-bg) 55%, var(--cm-white));
}

.table tbody tr:hover td,
table.table tbody tr:hover td {
    background-color: var(--cm-primary-light) !important;
}

.table tbody td,
table.table tbody td {
    border-bottom-color: rgba(15, 34, 58, .06);
    vertical-align: middle;
}

.table tbody td a,
table.table tbody td a {
    font-weight: 600;
}

/* Bootstrap/Tabler pagination, used by most GLPI list views */
.pagination .page-link {
    border: none;
    border-radius: 8px;
    margin: 0 2px;
    color: var(--cm-primary);
    font-weight: 600;
}

.pagination .page-link:hover {
    background-color: var(--cm-primary-light);
}

.pagination .page-item.active .page-link {
    background: var(--cm-primary);
    color: #FFFFFF !important;
}

.pagination .page-item.disabled .page-link {
    color: color-mix(in srgb, var(--cm-text) 45%, transparent);
}

/* Legacy jQuery DataTables markup, still used by some classic GLPI list
   screens outside the new self-service pages. */
.dataTables_wrapper .dataTables_paginate .paginate_button {
    border-radius: 8px !important;
    margin: 0 2px !important;
}

.dataTables_wrapper .dataTables_paginate .paginate_button.current {
    background: var(--cm-primary) !important;
    border-color: var(--cm-primary) !important;
    color: #FFFFFF !important;
}

.dataTables_wrapper .dataTables_length select,
.dataTables_wrapper .dataTables_filter input {
    border-radius: 8px;
}

/* ---- Tabs + table panel on the portal home ("Casos en curso / Casos
   resueltos"): give the whole block a single cohesive card instead of a
   flat tab strip sitting directly on the page background. ---- */
.tickets-banner .nav-tabs {
    border-bottom: none;
    gap: .4rem;
}

.tickets-banner .nav-tabs .nav-link {
    border-radius: 10px 10px 0 0;
    font-weight: 700;
    padding: .7rem 1.1rem;
}

.tickets-banner .nav-tabs .nav-link:not(.active) {
    background: var(--cm-white);
    color: var(--cm-text);
    border-color: transparent;
}

.tickets-banner .tab-content,
.tickets-banner .table-responsive {
    background: var(--cm-white);
    border-radius: 0 var(--cm-radius-md) var(--cm-radius-md) var(--cm-radius-md);
    border: 1px solid rgba(15, 34, 58, .1);
    border-top: none;
}

/* ==========================================================================
   Forms
   Every ticket/form/service-catalog screen in GLPI is built from the same
   handful of Bootstrap/Tabler primitives (.card, .form-control,
   .form-select, .form-label) plus TinyMCE for rich text. Styling those
   primitives once here covers "Request a service", ticket creation,
   the plugin's own config screen and anything else built the same way -
   nothing here is specific to one form.
   ========================================================================== */

:root {
    --cm-font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Inter", Roboto, "Helvetica Neue", Arial, sans-serif;
}

body,
.form-control,
.form-select,
.form-label,
.btn,
h1, h2, h3, h4, h5,
.card-title {
    font-family: var(--cm-font-sans);
}

/* Page titles ("Request a service", "Alta de Colaborador"...): bigger,
   heavier, brand-colored, with a short accent rule underneath instead of
   sitting flush against the form card like plain black text. */
h1 {
    font-weight: 800;
    font-size: 1.7rem;
    letter-spacing: -.01em;
    color: var(--cm-ink);
    position: relative;
    padding-bottom: .6rem;
    margin-bottom: 1.5rem !important;
}

h1::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: 0;
    width: 46px;
    height: 4px;
    border-radius: 2px;
    background: linear-gradient(90deg, var(--cm-primary), var(--cm-accent));
}

.card {
    /* Flat card look requested to match a reference design - a resting
       drop-shadow was reads as "dated skeuomorphic UI" next to that
       design's flat, border-only cards. Border strengthened slightly
       (.06 -> .1 alpha) to keep card edges legible without the shadow
       that used to carry that job. */
    border: 1px solid rgba(15, 34, 58, .1);
    border-radius: var(--cm-radius-lg);
    position: relative;
}

/* Subtle permanent brand stripe on every form/content card - a quiet,
   consistent signature rather than a one-off decoration.
   IMPORTANT: this used to rely on `overflow: hidden` on .card to clip the
   stripe's square corners to the card's rounded ones. That silently
   broke unrelated cards whose content is loaded/resized asynchronously
   (verified case: GLPI's ticket search-results table, wrapped in
   `.card.search-card` - its one matching row was present, correctly
   styled and had a real height, but was clipped clean out of view
   because the card's own height gets finalized before the async row
   content settles). Rounding just the stripe's own top corners gets the
   same visual without ever constraining the card's content box. */
.card::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 4px;
    border-radius: var(--cm-radius-lg) var(--cm-radius-lg) 0 0;
    background: linear-gradient(90deg, var(--cm-primary), var(--cm-primary-vivid) 55%, var(--cm-accent));
}

.card-body {
    padding: 1.75rem 2rem;
}

.card-header {
    border-bottom: 1px solid rgba(15, 34, 58, .06);
}

h2, h3, .card-title {
    color: var(--cm-ink);
    font-weight: 700;
}

/* Small-caps, muted labels above bolder field values reads as a modern
   SaaS form (Linear/Notion-style hierarchy) instead of every label
   competing for attention in the same weight and color as the input. */
.form-label {
    color: color-mix(in srgb, var(--cm-ink) 72%, transparent);
    font-weight: 700;
    font-size: .72rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    margin-bottom: .5rem;
}

.mb-3:has(.form-label),
.form-group {
    margin-bottom: 1.5rem !important;
}

/* Filled inputs instead of plain white-with-border boxes: flatter, calmer,
   and the focus state (white background + brand ring) becomes the clear
   signal instead of every idle field shouting with a heavy border. */
.form-control,
.form-select {
    background-color: var(--cm-gray-bg);
    border: 1.5px solid transparent;
    border-radius: 10px;
    padding: .65rem .9rem;
    font-size: .95rem;
    color: var(--cm-ink);
    transition: border-color .18s ease, box-shadow .18s ease, background-color .18s ease;
}

.form-control::placeholder {
    color: color-mix(in srgb, var(--cm-text) 55%, transparent);
}

.form-control:hover,
.form-select:hover {
    border-color: color-mix(in srgb, var(--cm-primary) 25%, transparent);
}

.form-control:focus,
.form-select:focus {
    background-color: var(--cm-white);
    border-color: var(--cm-primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--cm-primary) 18%, transparent);
}

.form-control:disabled,
.form-select:disabled {
    background-color: var(--cm-gray-bg);
    color: color-mix(in srgb, var(--cm-text) 55%, transparent);
}

/* Required marker: kept red - it's a universal, high-contrast convention
   and swapping it for the (light) gold accent would repeat the exact
   low-contrast mistake already fixed on the hero tagline. Left as the
   browser/Bootstrap default intentionally. */

.form-check-input:checked {
    background-color: var(--cm-primary);
    border-color: var(--cm-primary);
}

.form-check-input:focus {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--cm-primary) 18%, transparent);
}

.input-group-text {
    background: var(--cm-gray-bg);
    border-color: rgba(15, 34, 58, .15);
    color: var(--cm-text);
}

/* File attachments drop zone, when rendered as a plain bordered box */
.dropzone,
.filepond--panel-root {
    border: 2px dashed color-mix(in srgb, var(--cm-primary) 35%, transparent) !important;
    border-radius: var(--cm-radius-md) !important;
    background: var(--cm-primary-light) !important;
}

/* TinyMCE rich-text editor (ticket/form description fields) */
.tox.tox-tinymce {
    border: 1px solid rgba(15, 34, 58, .15) !important;
    border-radius: var(--cm-radius-md) !important;
    overflow: hidden;
}

.tox .tox-toolbar,
.tox .tox-toolbar__primary,
.tox .tox-toolbar-overlord {
    background: var(--cm-gray-bg) !important;
}

.tox .tox-tbtn--enabled,
.tox .tox-tbtn:hover {
    background: var(--cm-primary-light) !important;
}

.tox:focus-within {
    border-color: var(--cm-primary) !important;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--cm-primary) 18%, transparent);
}

/* ==========================================================================
   Enhanced dropdowns (Tom-Select / Select2 / Choices.js / native <select>)
   GLPI renders most dropdowns through a JS widget, not a plain native
   <select> popup, so its option list is regular styleable DOM. The exact
   library isn't asserted here - all three common ones plus the native
   fallback are covered defensively, each with the same three states so
   the currently-selected value is always readable:
     - idle option:      dark text on white
     - hovered/active:   white text on brand blue (clear affordance)
     - already selected: dark text on light-blue tint + bold (distinct
                          from both idle and hover, never blue-on-blue)
   ========================================================================== */

/* Tom-Select (GLPI's current default dropdown widget) */
.ts-wrapper .ts-control {
    background-color: var(--cm-gray-bg) !important;
    border: 1.5px solid transparent !important;
    border-radius: 10px !important;
    color: var(--cm-ink) !important;
    box-shadow: none !important;
}

.ts-wrapper.focus .ts-control {
    background-color: var(--cm-white) !important;
    border-color: var(--cm-primary) !important;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--cm-primary) 18%, transparent) !important;
}

.ts-dropdown {
    border: 1px solid rgba(15, 34, 58, .12) !important;
    border-radius: 10px !important;
    /* Same clip-vs-shadow conflict as .grid-stack-item-content/.modal-content
       above: overflow:hidden here clips a same-element box-shadow along
       with it - filter:drop-shadow() is exempt. */
    filter: drop-shadow(0 14px 32px rgba(15, 34, 58, .16));
    overflow: hidden;
}

.ts-dropdown .ts-dropdown-content .option,
.ts-dropdown [data-selectable] {
    background: var(--cm-white) !important;
    color: var(--cm-ink) !important;
}

.ts-dropdown .option.active,
.ts-dropdown [data-selectable].active {
    background: var(--cm-primary) !important;
    color: #FFFFFF !important;
}

.ts-dropdown .option.selected,
.ts-dropdown [data-selectable].selected {
    background: var(--cm-primary-light) !important;
    color: var(--cm-ink) !important;
    font-weight: 700;
}

/* Select2 (legacy screens still using it) */
.select2-container .select2-selection {
    background-color: var(--cm-gray-bg) !important;
    border: 1.5px solid transparent !important;
    border-radius: 10px !important;
}

.select2-container--focus .select2-selection,
.select2-container--open .select2-selection {
    background-color: var(--cm-white) !important;
    border-color: var(--cm-primary) !important;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--cm-primary) 18%, transparent) !important;
}

.select2-results__option {
    background: var(--cm-white) !important;
    color: var(--cm-ink) !important;
}

.select2-results__option--highlighted {
    background: var(--cm-primary) !important;
    color: #FFFFFF !important;
}

.select2-results__option[aria-selected="true"] {
    background: var(--cm-primary-light) !important;
    color: var(--cm-ink) !important;
    font-weight: 700;
}

/* Choices.js */
.choices__list--dropdown .choices__item,
.choices__list[aria-expanded] .choices__item {
    background: var(--cm-white) !important;
    color: var(--cm-ink) !important;
}

.choices__list--dropdown .choices__item--selectable.is-highlighted {
    background: var(--cm-primary) !important;
    color: #FFFFFF !important;
}

.choices__list--dropdown .choices__item.is-selected {
    background: var(--cm-primary-light) !important;
    color: var(--cm-ink) !important;
    font-weight: 700;
}

/* Native <select><option> fallback (limited browser support, harmless) */
.form-select option {
    background: var(--cm-white);
    color: var(--cm-ink);
}

.form-select option:checked {
    background: var(--cm-primary-light);
    color: var(--cm-ink);
    font-weight: 700;
}

/* ==========================================================================
   Calendar (Reservations, planning views - built on FullCalendar)
   FullCalendar publishes its own set of CSS custom properties specifically
   so themes don't have to fight its internal specificity - using --fc-*
   here is the officially supported way to restyle it, same spirit as the
   --tblr-* variable bridge at the top of this file.
   ========================================================================== */

.fc {
    --fc-border-color: rgba(15, 34, 58, .08);
    --fc-today-bg-color: var(--cm-primary-light);
    --fc-neutral-bg-color: var(--cm-gray-bg);
    --fc-button-bg-color: var(--cm-white);
    --fc-button-border-color: rgba(15, 34, 58, .15);
    --fc-button-text-color: var(--cm-ink);
    --fc-button-hover-bg-color: var(--cm-primary-light);
    --fc-button-hover-border-color: var(--cm-primary);
    --fc-button-active-bg-color: var(--cm-primary);
    --fc-button-active-border-color: var(--cm-primary);
    --fc-event-bg-color: var(--cm-primary);
    --fc-event-border-color: var(--cm-primary-dark);
    --fc-page-bg-color: var(--cm-white);
    font-family: var(--cm-font-sans);
}

.fc .fc-toolbar-title {
    color: var(--cm-ink);
    font-weight: 800;
    font-size: 1.35rem;
    letter-spacing: -.01em;
}

.fc .fc-toolbar {
    padding-bottom: .75rem;
    margin-bottom: .5rem !important;
    border-bottom: 1px solid rgba(15, 34, 58, .06);
}

/* Segmented-control look for grouped buttons (the Mes/Semana/Día/Agenda
   toggle, and the prev/next pair): one soft pill container instead of
   several separately-outlined buttons, with the active view as a solid
   brand-gradient segment - the same pattern used for .nav-pills earlier. */
.fc .fc-button-group {
    background: var(--cm-gray-bg);
    border-radius: 10px;
    padding: 3px;
    gap: 2px;
    box-shadow: inset 0 0 0 1px rgba(15, 34, 58, .06);
}

.fc .fc-button {
    background: transparent !important;
    border: none !important;
    color: var(--cm-text) !important;
    border-radius: 8px !important;
    font-weight: 600;
    text-transform: capitalize;
    box-shadow: none !important;
    padding: .45rem .9rem !important;
    transition: background-color .15s ease, color .15s ease;
}

.fc .fc-button-group .fc-button:hover:not(.fc-button-active) {
    background: rgba(255, 255, 255, .75) !important;
    color: var(--cm-ink) !important;
}

.fc .fc-button-primary:not(:disabled).fc-button-active,
.fc .fc-button-primary:not(:disabled):active {
    background: linear-gradient(135deg, var(--cm-primary-vivid), var(--cm-primary)) !important;
    color: #FFFFFF !important;
    box-shadow: 0 4px 12px var(--cm-shadow-primary) !important;
}

.fc .fc-button:focus {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--cm-primary) 22%, transparent) !important;
}

/* Prev/next re-styled as circular icon buttons (Google/Apple Calendar
   convention) - more specific than .fc-button-group's flat reset above,
   so this wins for these two regardless of which group wraps them. */
.fc .fc-prev-button,
.fc .fc-next-button {
    width: 32px;
    height: 32px;
    padding: 0 !important;
    border-radius: 50% !important;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--cm-white) !important;
    box-shadow: 0 1px 4px rgba(15, 34, 58, .12) !important;
}

.fc .fc-prev-button:hover,
.fc .fc-next-button:hover {
    background: var(--cm-primary) !important;
    color: #FFFFFF !important;
}

.fc .fc-today-button {
    background: var(--cm-white) !important;
    border: 1px solid rgba(15, 34, 58, .15) !important;
    color: var(--cm-primary) !important;
    border-radius: 999px !important;
    font-weight: 700 !important;
    padding: .4rem 1.15rem !important;
}

.fc .fc-today-button:hover:not(:disabled) {
    background: var(--cm-primary-light) !important;
    border-color: var(--cm-primary) !important;
}

.fc .fc-today-button:disabled {
    opacity: .5;
}

.fc .fc-col-header-cell {
    background: var(--cm-gray-bg);
}

.fc .fc-col-header-cell-cushion {
    color: color-mix(in srgb, var(--cm-text) 70%, transparent);
    text-transform: uppercase;
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .05em;
    padding: .6rem 0;
}

/* Subtle weekend wash - a common calendar convention that visually
   separates the work week without touching GLPI's own semantic weekend
   date-number color (left untouched on purpose, see notes elsewhere). */
.fc .fc-day-sat:not(.fc-day-today) .fc-daygrid-day-frame,
.fc .fc-day-sun:not(.fc-day-today) .fc-daygrid-day-frame {
    background-color: color-mix(in srgb, var(--cm-gray-bg) 55%, transparent);
}

.fc .fc-daygrid-day-frame {
    transition: background-color .15s ease;
}

.fc .fc-daygrid-day:not(.fc-day-today):hover .fc-daygrid-day-frame {
    background-color: var(--cm-primary-light);
    cursor: pointer;
}

.fc .fc-daygrid-day-number {
    color: var(--cm-text);
    font-weight: 600;
    padding: .4rem;
}

/* Today: a filled brand-blue circle behind the date number (instant
   recognition, same convention as Google/Apple/Outlook calendars) plus a
   soft tinted cell background instead of the library's default flat gray. */
.fc .fc-day-today {
    background-color: color-mix(in srgb, var(--cm-primary-light) 65%, transparent) !important;
}

.fc .fc-day-today .fc-daygrid-day-number {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    margin: .25rem;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--cm-primary-vivid), var(--cm-primary));
    color: #FFFFFF !important;
    font-weight: 800;
    box-shadow: 0 3px 8px var(--cm-shadow-primary);
}

.fc-event {
    background: linear-gradient(135deg, var(--cm-primary-vivid), var(--cm-primary)) !important;
    border: none !important;
    border-radius: 6px !important;
    color: #FFFFFF !important;
    font-weight: 600;
    padding: 1px 5px;
    box-shadow: 0 2px 5px rgba(0, 20, 45, .18);
    transition: transform .12s ease, box-shadow .12s ease;
}

.fc-event:hover {
    filter: brightness(1.08);
    transform: translateY(-1px);
    box-shadow: 0 5px 12px rgba(0, 20, 45, .25);
}

.fc-daygrid-more-link {
    color: var(--cm-primary) !important;
    font-weight: 700;
}

/* ==========================================================================
   Dashboard (Gridstack-based central home / asset / ticket dashboards)
   Only the outer widget "card" chrome is touched (radius, shadow, hover
   lift, font). The data itself - the green/red/teal per-asset-type tiles,
   the ticket-status colors in the stacked bar, the donut segments - is
   deliberately left alone: those colors are meaningful (they encode asset
   type / ticket status consistently with the rest of GLPI) and overriding
   them would make the dashboard harder to read, not more elegant.
   Gridstack is the actual widget-grid engine GLPI uses here, and
   .grid-stack-item-content is its own stable wrapper around every widget.
   No margin/gutter between tiles on purpose: gridstack positions this
   element with exact inline top/left/width/height computed by its own
   JS, and adding a margin shrank the effective box those pixel
   coordinates assumed, clipping widget labels (verified: "Casos
   entrantes" rendered as "Casos ntrantes"). Rounded corners still read
   as separate cards even edge-to-edge.
   ========================================================================== */

.grid-stack-item-content {
    position: relative;
    border-radius: 16px !important;
    /* Flat card look requested to match a reference design - was a
       layered "floating executive card" shadow, now a plain border like
       every other card in this file (see .card's own note above). */
    box-shadow: none !important;
    border: 1px solid rgba(15, 34, 58, .1);
    overflow: hidden !important;
    transition: transform .22s cubic-bezier(.2, .8, .2, 1), box-shadow .22s ease;
    /* Each tile must react to its OWN box size, not the viewport's -
       gridstack packs tiles of very different widths into the same row,
       and the same tile also gets narrower simply because the browser
       window/zoom changed, independent of any breakpoint. Container
       queries below key off this, not a media query. */
    container-type: inline-size;
    container-name: cm-widget;
}

/* A light hover lift is still normal, modern interaction feedback (not
   the "dated" resting shadow that was removed above) - kept, just much
   more subtle than before now that there's no shadow to begin with. */
.grid-stack-item-content:hover {
    transform: translateY(-4px);
    /* box-shadow would be the natural choice here, but this element sets
       its own overflow:hidden above (needed to clip the diagonal sheen
       ::after and any chart canvas overflow to the rounded corner) - and
       overflow:hidden clips a same-element box-shadow to nothing right
       along with it, reported live as the card's hover border/shadow
       looking cut off. filter:drop-shadow() renders after that clip is
       applied, so it survives untouched. */
    filter: drop-shadow(0 6px 16px rgba(15, 34, 58, .18));
    box-shadow: none !important;
}

/* A quiet diagonal "glass" sheen over every widget, colored tiles and
   white chart cards alike - it sits in its own layer above whatever
   background the widget itself paints, so it never needs to know that
   color and can't clash with it. This single touch is what pushes flat
   solid tiles from "consumer widget" to "executive dashboard tile"
   without changing a single data color. */
.grid-stack-item-content::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 2;
    pointer-events: none;
    background: linear-gradient(135deg, rgba(255, 255, 255, .22) 0%, rgba(255, 255, 255, 0) 42%, rgba(0, 0, 0, .05) 100%);
}

.grid-stack-item-content svg text,
.grid-stack-item-content text,
.grid-stack-item-content .legend {
    font-family: var(--cm-font-sans) !important;
}

/* Crisp, evenly-spaced digits for the big KPI numbers - a small detail
   that reads as "financial/BI-grade" rather than default browser text. */
.grid-stack-item-content {
    font-variant-numeric: tabular-nums;
}

/* Widget captions ("Computadoras por Fabricantes", etc.) - GLPI renders
   these as small plain text in the corner of each widget; give them the
   same quiet, uppercase treatment as table headers/form labels so the
   whole dashboard reads as one consistent system. */
.grid-stack-item-content .dashboard-item-title,
.grid-stack-item-content > .card-title {
    color: color-mix(in srgb, var(--cm-ink) 75%, transparent);
    font-weight: 700;
    font-size: .78rem;
    text-transform: uppercase;
    letter-spacing: .04em;
}

/* -- KPI tiles ("big-number" widgets: Casos abiertos, Casos cerrados...) --
   GLPI ships these as a flat, saturated-color tile (the whole card painted
   in one hue, a big flat icon bleeding off the right edge) - real markup
   verified live: <a id="bn_<random>" class="card big-number"> plus a
   sibling <style>#bn_<random>{background-color:X; color:Y}</style> that
   GLPI writes per widget, one X/Y pair per asset type or ticket metric.
   That per-widget hue is meaningful (it is how GLPI keeps "computers" vs
   "printers" vs "open tickets" visually distinct across the whole app) so
   it is kept, not replaced - js/corporate.js reads the real Y value out of
   that sibling <style> tag and exposes it as --cm-bn-accent on the card,
   which is all the rules below actually consume. The executive-dashboard
   look (white card, small circular icon badge, colored number only) comes
   purely from CSS; only the accent hue itself is GLPI's own data.
   !important is required here because GLPI's inline <style>#bn_X{...}
   block is an ID selector - normal specificity can never out-rank an ID
   selector, only the !important tier can, regardless of source order. */
/* The generic .card top accent stripe (fixed blue-to-gold gradient,
   "Forms" section above) would repeat identically on every KPI/chart tile
   regardless of that widget's own accent hue - same "competing signal"
   problem already solved for the ticket timeline, same fix. */
a.card.big-number::before,
.card.g-chart::before {
    content: none !important;
}

a.card.big-number {
    display: flex !important;
    flex-direction: column;
    justify-content: center;
    gap: .15rem;
    background: var(--cm-white) !important;
    color: var(--cm-ink) !important;
    padding: 1.1rem 3.1rem 1.1rem 1.2rem !important;
    text-decoration: none;
}

a.card.big-number:hover {
    background: var(--cm-white) !important;
    color: var(--cm-ink) !important;
}

a.card.big-number .content {
    order: 2;
    display: block !important;
}

a.card.big-number .formatted-number,
a.card.big-number .number,
a.card.big-number .suffix {
    text-align: left !important;
    width: auto !important;
}

a.card.big-number .number {
    color: var(--cm-bn-accent, var(--cm-primary)) !important;
    font-weight: 800 !important;
    font-size: 1.85rem !important;
    font-variant-numeric: tabular-nums;
}

/* Single line, truncated with an ellipsis rather than left to wrap - the
   label sits at the very top of the card, in the same row as the
   absolutely-positioned icon badge; a wrapped second line has nowhere
   safe to go and collides with the icon and/or the number below it
   (reproduced live: resizing an already-loaded dashboard down from
   1920px to 1366px wrapped "Dispositivos de red" onto a second line that
   visually overlapped the icon badge). GLPI's own inline width/font-size
   (its auto-fit-text pass) is intentionally overridden here for the same
   reason - it was measuring against the old flat-tile layout, not this
   one, and kept shrinking the number toward unreadable sizes on resize. */
a.card.big-number .label {
    order: 1;
    position: static !important;
    color: color-mix(in srgb, var(--cm-ink) 62%, transparent) !important;
    font-size: .78rem !important;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .03em;
    white-space: nowrap !important;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100% !important;
    /* GLPI's own auto-fit-text pass can collapse this element's box to a
       few px tall in some layouts (observed live: a 4px-tall box silently
       clipping the label to invisible on a real phone, even though the
       text node and font-size were both fine) - line-height/min-height
       are forced here as a hard floor so the label can never render
       invisible regardless of what GLPI's script computes for it. */
    line-height: 1.3 !important;
    min-height: 1.3em;
    flex-shrink: 0;
}

/* The oversized watermark icon becomes a small circular badge pinned to
   the top-right corner instead of a full-height glyph bleeding off the
   card's right edge. */
a.card.big-number .main-icon {
    order: 0;
    position: absolute !important;
    top: 14px !important;
    right: 14px !important;
    left: auto !important;
    bottom: auto !important;
    width: 34px !important;
    height: 34px !important;
    display: flex !important;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: color-mix(in srgb, var(--cm-bn-accent, var(--cm-primary)) 16%, white) !important;
    color: var(--cm-bn-accent, var(--cm-primary)) !important;
    font-size: 1rem !important;
    opacity: 1 !important;
}

/* Decorative "Ver detalles" caption injected by js/corporate.js - the
   whole tile is already the real GLPI link (clicking anywhere on the
   card navigates, exactly as before), this just makes that affordance
   visible the way the target design shows it. */
.cm-bn-cta {
    order: 3;
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    margin-top: .3rem;
    font-size: .74rem;
    font-weight: 600;
    color: color-mix(in srgb, var(--cm-ink) 50%, transparent);
}

a.card.big-number:hover .cm-bn-cta {
    color: var(--cm-bn-accent, var(--cm-primary));
}

/* Below ~190px of actual tile width, the padding/icon/font sizes tuned
   for a normal KPI tile stop fitting - shrink everything together, then
   drop the purely decorative CTA first (the whole tile still works as a
   link without it) before anything gets cramped enough to overlap. Keyed
   off the tile's own box (container query), not the viewport, because
   gridstack packs tiles of very different widths into one row and a
   given tile can also shrink just because the window/zoom changed. */
@container cm-widget (max-width: 190px) {
    a.card.big-number {
        padding: .85rem 2.5rem .85rem 1rem !important;
    }

    a.card.big-number .number {
        font-size: 1.4rem !important;
    }

    a.card.big-number .label {
        font-size: .68rem !important;
    }

    a.card.big-number .main-icon {
        width: 28px !important;
        height: 28px !important;
        top: 10px !important;
        right: 10px !important;
        font-size: .85rem !important;
    }

    .cm-bn-cta {
        display: none;
    }
}

@container cm-widget (max-width: 130px) {
    a.card.big-number {
        padding: .7rem 2.2rem .7rem .85rem !important;
    }

    a.card.big-number .number {
        font-size: 1.15rem !important;
    }

    a.card.big-number .label {
        font-size: .6rem !important;
    }
}

/* -- Chart widgets (donut/pie, bar, line) --
   These render to a <canvas> via ECharts (window.echarts), not SVG/CSS -
   confirmed live: `.chart.ct-chart` carries `_echarts_instance_` and GLPI
   itself calls `echarts.init(...).setOption(...)` in an inline <script
   type="module">. No CSS selector can reach inside a canvas, so recoloring
   the actual chart drawing (segments, bars, line) to the brand palette is
   done in js/corporate.js via `echarts.getInstanceByDom()` + `setOption()`
   - this block only themes the surrounding card chrome and caption. */
.grid-stack-item-content .card.g-chart {
    background: var(--cm-white) !important;
}

.grid-stack-item-content .card.g-chart .main-label {
    color: color-mix(in srgb, var(--cm-ink) 75%, transparent);
    font-weight: 700;
    font-size: .78rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    display: block;
    max-width: calc(100% - 1.5rem);
}

@container cm-widget (max-width: 220px) {
    .grid-stack-item-content .card.g-chart .main-label {
        font-size: .68rem;
    }

    .grid-stack-item-content .card.g-chart .main-icon {
        font-size: .85rem;
    }
}

.grid-stack-item-content .card.g-chart .main-icon {
    color: var(--cm-primary);
    opacity: .85;
}

/* ==========================================================================
   Ticket timeline (templates/components/itilobject/timeline/timeline.html.twig)
   Real GLPI markup has no per-entry status "chip" or type icon - only the
   plain classes below (verified against GLPI 11.0.8 source): ITILFollowup,
   TicketTask, ITILSolution, ITILValidation on .timeline-item/.timeline-content,
   plus state classes info/todo/done and solution_class waiting/accepted/
   refused. The colored icon dot and label chip are built by
   js/corporate.js from those real classes - nothing here is fabricated
   data, just a themed reading of what GLPI already encodes.
   ========================================================================== */

:root {
    --cm-success: #2F9E5B;
    --cm-success-soft: color-mix(in srgb, var(--cm-success) 16%, white);
    --cm-warning: var(--cm-gold-deep);
    --cm-warning-soft: var(--cm-primary-light);
    --cm-danger: #C0392B;
    --cm-danger-soft: color-mix(in srgb, var(--cm-danger) 14%, white);
}

.itil-timeline {
    position: relative;
    padding-left: 2.75rem !important;
}

.itil-timeline::before {
    content: "";
    position: absolute;
    left: 20px;
    top: 20px;
    bottom: 20px;
    width: 2px;
    background: rgba(15, 34, 58, .12);
}

.timeline-item {
    position: relative;
}

.timeline-item .timeline-content.card {
    border-radius: var(--cm-radius-md);
    border: 1px solid rgba(15, 34, 58, .08);
    border-left: 3px solid var(--cm-primary);
    box-shadow: 0 3px 14px rgba(15, 34, 58, .06);
}

/* The generic .card top accent stripe (corporate.css, "Forms" section)
   reads as noise repeated across a long list of timeline entries - each
   entry already gets its own left-border + icon color, so it's dropped
   here only. */
.timeline-item .timeline-content.card::before {
    content: none !important;
}

.timeline-item.cm-tl-success .timeline-content.card { border-left-color: var(--cm-success); }
.timeline-item.cm-tl-warning .timeline-content.card { border-left-color: var(--cm-warning); }
.timeline-item.cm-tl-danger .timeline-content.card { border-left-color: var(--cm-danger); }

.timeline-header .creator .badge {
    background: var(--cm-gray-bg);
    color: color-mix(in srgb, var(--cm-text) 80%, transparent);
    border-radius: 999px;
    font-weight: 500;
}

/* Status/type chip, built by js/corporate.js from the real GLPI classes
   listed above - same visual language as the KPI/table/dropdown chips
   used everywhere else in this theme. */
.cm-tl-badge {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .2rem .65rem;
    border-radius: 999px;
    font-size: .68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    margin: 0 .5rem .35rem 0;
}

.cm-tl-badge-primary { background: var(--cm-primary-light); color: var(--cm-primary-dark); }
.cm-tl-badge-success { background: var(--cm-success-soft); color: var(--cm-success); }
.cm-tl-badge-warning { background: var(--cm-warning-soft); color: color-mix(in srgb, var(--cm-warning) 75%, black); }
.cm-tl-badge-danger { background: var(--cm-danger-soft); color: var(--cm-danger); }

/* Icon dot anchored on the vertical line - js/corporate.js sets its
   `top` inline (measured against .itil-timeline's real layout), CSS only
   owns shape/color so it degrades to "no dot" instead of a broken layout
   if the timeline markup ever changes shape. */
.cm-tl-dot {
    position: absolute;
    left: 8px;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: #FFFFFF;
    font-size: .78rem;
    /* The ring is a "cutout" against the timeline line behind it, not a
       decorative shadow (see dark-mode.css's own note on this same rule) -
       kept. Its accompanying drop-shadow was removed to match the flat
       look requested elsewhere in this pass. */
    box-shadow: 0 0 0 3px var(--cm-white);
    z-index: 3;
}

.cm-tl-dot.cm-tl-primary { background: linear-gradient(135deg, var(--cm-primary-vivid), var(--cm-primary)); }
.cm-tl-dot.cm-tl-success { background: var(--cm-success); }
.cm-tl-dot.cm-tl-warning { background: var(--cm-warning); }
.cm-tl-dot.cm-tl-danger { background: var(--cm-danger); }

/* ---- Translated requirement detail (js/corporate.js::enhanceTranslatedRequirementDetail) ----
   Renders the label/value pairs public/js/corporate.js fetches from
   front/ajax/translated_requirement.php as an actual "form" look (label
   above its value, grouped by section) instead of the dense HTML table
   GLPI originally baked into the ticket - matches the request that this
   read as a form, not a table. */
.cm-tform-section {
    margin-bottom: 1.25rem;
}

.cm-tform-section:last-child {
    margin-bottom: 0;
}

.cm-tform-section__title {
    font-size: .95rem;
    font-weight: 700;
    color: var(--cm-primary);
    margin: 0 0 .6rem;
    padding-bottom: .35rem;
    border-bottom: 2px solid var(--cm-primary-light);
}

.cm-tform-row {
    display: grid;
    grid-template-columns: minmax(160px, 240px) 1fr;
    gap: .25rem 1rem;
    padding: .45rem 0;
    border-bottom: 1px solid rgba(15, 34, 58, .06);
}

.cm-tform-row:last-child {
    border-bottom: none;
}

.cm-tform-label {
    font-weight: 600;
    color: var(--cm-text);
}

.cm-tform-value {
    color: var(--cm-ink);
    word-break: break-word;
}

@media (max-width: 576px) {
    .cm-tform-row {
        grid-template-columns: 1fr;
        gap: .1rem;
    }
}

/* ==========================================================================
   Density & polish refinements
   Reported live: at 100% browser zoom, table rows and the KPI/big-number
   tiles at the top of a Search results page (Central "Casos" list, etc.)
   read as cramped - GLPI's own Tabler defaults use a tighter padding
   scale than this theme's own cards/forms (already generous - see
   .card-body above), and every rule this theme added on top only ever
   ADDED padding/gap that wasn't there before, on selectors already used
   and verified elsewhere in this file - nothing here removes or
   restructures anything, so light-mode layouts already working keep
   working exactly the same, just with more breathing room. Purely
   presentational, same as the rest of this file - no branding/color
   change, no markup dependency beyond what's already relied on above.
   ========================================================================== */

body {
    line-height: 1.55;
}

/* GLPI/Tabler's own default table body cell padding is noticeably
   tighter than the header row this theme already pads above (.table
   thead th, ~.9rem) - every list view app-wide (Search results, the
   Central "Casos" list, the portal's own "Casos en curso/resueltos"
   tables) inherits that same tight default with no override until now. */
.table tbody td,
table.table tbody td {
    padding: .8rem 1rem;
}

.table thead th,
table.table thead th {
    padding-top: 1rem;
    padding-bottom: 1rem;
}

/* KPI/"big-number" tiles (Casos abiertos, Casos pendientes, Casos
   cerrados...) - a touch more internal padding and spacing between the
   label/number/CTA stack than the original compact-tile spec, without
   changing the tile's own fixed layout, icon position or breakpoints
   above (@container rules already handle the truly narrow cases). */
a.card.big-number {
    padding: 1.35rem 3.1rem 1.35rem 1.4rem !important;
    gap: .25rem;
}

/* Vertical breathing room between wrapped rows of cards (KPI tile rows,
   card decks) - row-gap only ever adds space where a row already wraps,
   it cannot affect single-row layouts or the existing horizontal gutter
   Bootstrap's grid already manages via its own column padding. */
.row-cards,
.row-deck {
    row-gap: 1.25rem;
}

/* ==========================================================================
   Sidebar "Modo oscuro" switch (js/corporate.js::initDarkModeToggle)
   A pill-style on/off switch, sidebar-appropriate (light text/icon on the
   dark navy sidebar - correct regardless of the light/dark toggle state
   itself, same reasoning as the rest of .navbar-vertical's own always-
   dark chrome documented earlier in this file).
   ========================================================================== */

.cm-dark-toggle {
    display: flex;
    align-items: center;
    gap: .65rem;
    width: calc(100% - 1.2rem);
    margin: .15rem .6rem .5rem;
    padding: .6rem .7rem;
    background: transparent;
    border: none;
    border-radius: 10px;
    color: #c8d7ee;
    font-weight: 650;
    font-size: .84rem;
    cursor: pointer;
    transition: background-color .15s ease, color .15s ease;
}

.cm-dark-toggle:hover {
    background: rgba(255, 255, 255, .1);
    color: #FFFFFF;
}

.cm-dark-toggle i {
    display: inline-flex;
    width: 28px;
    height: 28px;
    flex: 0 0 28px;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    color: #a9bfdf;
    background: rgba(255, 255, 255, .05);
    font-size: .95rem;
}

.cm-dark-toggle--on i {
    color: var(--cm-yellow-400);
    background: rgba(253, 187, 17, .12);
}

.cm-dark-toggle__label {
    flex: 1 1 auto;
    text-align: left;
}

.cm-dark-toggle__switch {
    flex: 0 0 auto;
    width: 34px;
    height: 19px;
    border-radius: 999px;
    background: rgba(255, 255, 255, .18);
    position: relative;
    transition: background-color .18s ease;
}

.cm-dark-toggle--on .cm-dark-toggle__switch {
    background: var(--cm-yellow-500);
}

.cm-dark-toggle__thumb {
    position: absolute;
    top: 2px;
    left: 2px;
    width: 15px;
    height: 15px;
    border-radius: 50%;
    background: #FFFFFF;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
    transition: transform .18s ease;
}

.cm-dark-toggle--on .cm-dark-toggle__thumb {
    transform: translateX(15px);
}

/* Collapsed sidebar (body.navbar-collapsed): only the icon stays, same
   treatment as every other item in this menu - the switch pill itself
   has nowhere to fit at the collapsed 70px width. */
body.navbar-collapsed .cm-dark-toggle__switch {
    display: none;
}

/* ==========================================================================
   KPI tile icon - bolder, filled treatment
   Reported live as looking too subtle/flat compared to a reference design
   using solid, vividly-colored icon badges - the icon badge already
   existed (see "KPI tiles" section above), only its fill/size changes
   here: a full-strength accent circle with a white icon instead of a
   16%-tint circle with a colored icon. Purely a color/size refinement of
   the SAME element, no layout/markup change, so every documented
   container-query breakpoint above still applies unchanged.
   ========================================================================== */

a.card.big-number .main-icon {
    width: 42px !important;
    height: 42px !important;
    background: var(--cm-bn-accent, var(--cm-primary)) !important;
    color: #FFFFFF !important;
    font-size: 1.15rem !important;
}
