/* ============================================================================================
   Sistema de Clientes — brand skin over Bootstrap 5.3.

   WHAT CHANGED, AND WHY (task 03d)

   Task 03b built every component by hand — buttons, inputs, tables, badges, alerts, pagination —
   on the reasoning that a framework is a dependency that rots. Half of that reasoning was right and
   half of it was wrong, and the screens are what settled it: hand-rolled controls at this budget
   look like an unstyled form with borders on it, and no amount of token discipline fixes the
   spacing, the focus states, the disabled states and the control sizing that a framework has
   already argued about for fifteen years.

   The durability argument survives intact, because it was never about frameworks — it was about
   BUILD STEPS and REMOTE ASSETS. wwwroot/lib/bootstrap/bootstrap.min.css is a committed file, like
   htmx. There is no npm, no bundler, no Sass, and no CDN reference. Bootstrap 5.3 was chosen over
   any earlier version for exactly one reason: every component reads its colours from local CSS
   custom properties, so this file re-themes it completely without recompiling anything.

   THE DIVISION OF LABOUR

     Bootstrap owns   buttons, forms, tables, badges, alerts, cards, pagination, dropdowns,
                      modals, the grid, spacing utilities, and the whole reset.
     This file owns   the brand palette, the application shell, the authentication screen,
                      the htmx affordances, the sort-header arrows and the print rules.

   If a component exists in Bootstrap, use Bootstrap's. Adding a hand-written .btn-something here
   is how a project ends up with two design systems and no design.

   RULES THAT SURVIVED FROM 03b

     - every colour, space and radius is a custom property declared in :root
     - no literal hex outside the token block
     - ⚠ white text on --brand-orange is 3.15:1 and FAILS WCAG AA. Anything orange carrying a
       label uses --brand-orange-ink (4.6:1). Do not "fix" a washed-out control by matching the
       logo exactly; that is the mistake this note exists to prevent
     - a new component goes in /Dev/Styles in the same commit, or it will be reinvented

   Sections:  TOKENS · BOOTSTRAP SKIN · BASE · SHELL · AUTH · TABLES · HTMX · EDIT ZONE ·
              AGENDA · THE MONTH GRID · UTILITIES · PRINT
   ============================================================================================ */


/* ---- TOKENS -------------------------------------------------------------------------------
   The palette is taken from the company logo: an orange TD over a transformer, a yellow bolt,
   navy lettering. Navy is the structure, orange the accent — never the reverse. People look at
   this application for eight hours a day.
   ------------------------------------------------------------------------------------------- */

:root {
    /* Brand — the company's guideline, docs/brand/brand-guidelines.md (task 13c/05). */
    --brand-navy: #1E2D3B;
    --brand-navy-dark: #1B2430;
    --brand-steel: #3D5975;           /* hover lift off navy, and secondary ink on white — 7.3:1 */
    --brand-orange: #FC5000;          /* surfaces that carry NO text */
    --brand-orange-ink: #c74e14;      /* anything with a label on it — 4.6:1 on white */
    --brand-orange-deep: #a33f0f;     /* hover/active for the above */
    --brand-yellow: #EDCD1F;          /* the active-section rule on navy — 8.9:1 */

    /* Triplets, for the translucent focus rings and shadows Bootstrap builds with rgba().
       Kept here so the "no hex outside :root" rule still holds one line below. */
    --brand-navy-rgb: 30, 45, 59;
    --brand-orange-rgb: 252, 80, 0;

    /* Text and rules that sit on a brand-coloured surface.

       --color-on-brand-muted was called --brand-steel until task 13c/05. It had to give the name
       up: the company's guideline spends "steel" on #3D5975, a DARK blue for light surfaces, and
       this is a LIGHT blue that only ever appears as text on the navy header. Two opposite meanings
       under one name is how a find-and-replace turns the main menu into 1.9:1. */
    --color-on-brand: #ffffff;
    --color-on-brand-muted: #b8c4d4;  /* secondary text on navy — 8.0:1 */

    /* Neutrals */
    --color-page: #f4f5f7;
    --color-surface: #ffffff;
    --color-surface-alt: #fafbfc;
    --color-surface-sunk: #eef0f3;
    --color-border: #D6DCE4;
    --color-border-strong: #b6bcc7;
    --color-text: #1b2430;
    --color-muted: #5c6673;

    /* Semantics */
    --color-danger: #b3261e;
    --color-danger-dark: #8f1e18;
    --color-danger-bg: #fdecea;
    --color-danger-border: #f0bdb8;
    --color-warn: #8a6100;
    --color-warn-bg: #fdf6e3;
    --color-warn-border: #e8d9a6;
    --color-ok: #1e6b3a;
    --color-ok-bg: #eaf6ee;
    --color-ok-border: #b3ddc2;
    --color-info: #1c5480;
    --color-info-bg: #eaf2f9;
    --color-info-border: #b6d2e8;

    /* Type — TWO STACKS, and the rule that used to be here has been revisited rather than broken.

       It read: "a webfont means a CDN link or a licensed binary, and neither earns its place".
       The objection was never to webfonts, it was to CDNs and to licences — and Poppins is neither.
       It is the company's own typeface, it ships under the SIL OFL, and it sits in
       wwwroot/lib/poppins/ as three committed woff2 files totalling 24 KB, exactly like Bootstrap
       Icons. Nothing is fetched at runtime and nothing is compiled. There are now two vendored
       fonts, not "one exception".

       The split is deliberate and it is not decoration. --font-display carries headings, the brand
       block and the navigation: the type a reader glances at. --font-body carries everything a
       reader STARES at — table data, form controls, labels, help text — and stays on the system
       stack, because this application is a table the customer reads for eight hours a day and
       Segoe UI is the face Windows tuned for exactly that. The company's guideline asks for the
       same division in the same words. */
    --font-display: "Poppins", "Segoe UI Variable Display", "Segoe UI", system-ui, sans-serif;
    --font-body: "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system,
                 "Noto Sans", "Helvetica Neue", Arial, sans-serif;
    --font-mono: "Cascadia Mono", Consolas, "DejaVu Sans Mono", "Courier New", monospace;

    /* Space — a 4px scale. Bootstrap's own spacing utilities (.p-3, .gap-2) are on a 4px scale
       too, so the two agree rather than compete. */
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.5rem;
    --space-6: 2rem;
    --space-7: 3rem;

    --radius-sm: 3px;
    --radius: 5px;
    --radius-lg: 8px;

    --shadow-sm: 0 1px 2px rgba(var(--brand-navy-rgb), 0.08);
    --shadow: 0 2px 8px rgba(var(--brand-navy-rgb), 0.10);
    --shadow-card: 0 10px 30px rgba(0, 0, 0, 0.25);

    /* A wash over the navy header, for hover. Translucent rather than a mixed colour so it keeps
       working if the header's background ever changes. */
    --overlay-on-brand: rgba(255, 255, 255, 0.06);

    /* The open editor's surface and edge. Navy, not orange — see the EDIT ZONE section for the
       whole argument. Translucent rather than a fixed grey so it sits correctly on the white card
       body and on the alt-surface of a card header alike. */
    --color-edit-bg: rgba(var(--brand-navy-rgb), 0.06);
    --color-edit-border: rgba(var(--brand-navy-rgb), 0.28);

    --header-height: 3.5rem;

    /* One week of the month grid. Every cell is exactly this tall so the weeks can be compared by
       weight, which means the number has to be arithmetic rather than taste: 0.5rem of padding,
       1.5rem for the day number, three chips at 1.1rem with 2px between them, and 0.9rem for
       "+N más" — the four gaps included, 6.95rem, rounded up. Change MaxChips and this changes. */
    --agenda-cal-alto: 7rem;

    /* A monogram on a coloured chip. The same white as the label it sits beside, held well back, so
       "who" is legible without competing with the client's name for the eye. */
    --overlay-on-brand-strong: rgba(255, 255, 255, 0.24);

    /* 100rem, not 90. The record screens are two columns of dense data read at 1920 all day; the
       ten rem this adds is the difference between the equipment table breathing and it scrolling
       sideways. Prose screens are the exception here, not the rule — nothing in this application
       is a paragraph long. */
    --content-width: 100rem;
}


/* ---- THE DISPLAY FACE ----------------------------------------------------------------------
   Poppins, vendored. Three weights and no more: 500 for the navigation, 600 for section titles,
   700 for page titles and the wordmark. Every weight added here is another 8 KB that every user
   downloads on their first visit and a fourth file to keep in step, and the guideline's hierarchy
   asks for exactly these three.

   ⚠ font-display: swap, NOT block. The fallback renders immediately and the page is readable the
   instant the HTML arrives; when Poppins lands, the headings re-draw. block would hold the titles
   invisible for up to three seconds on a cold cache to avoid that re-draw, which is trading the
   thing that matters for the thing that looks tidy. It is the same reasoning that keeps every
   control here working with JavaScript off, applied to a font: the page must survive the asset
   not arriving.

   ⚠ NO local() in the src list. A machine with some other Poppins installed — a designer's, most
   likely — would render the interface in it and nobody would be able to reproduce the result.
   The committed file is the only Poppins this application knows about.

   The paths are relative because this is a stylesheet and not a Razor view: ~/ means nothing to a
   browser. wwwroot/lib/poppins/ is a sibling of wwwroot/css/, so ../ climbs out of it — the same
   shape bootstrap-icons.min.css uses to find its own woff2.
   ------------------------------------------------------------------------------------------- */

@font-face {
    font-family: "Poppins";
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url("../lib/poppins/poppins-500.woff2") format("woff2");
}

@font-face {
    font-family: "Poppins";
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url("../lib/poppins/poppins-600.woff2") format("woff2");
}

@font-face {
    font-family: "Poppins";
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url("../lib/poppins/poppins-700.woff2") format("woff2");
}


/* ---- BOOTSTRAP SKIN -----------------------------------------------------------------------
   Everything below re-declares a variable Bootstrap already reads. Nothing here invents a
   component. When Bootstrap is upgraded, this is the file to re-check — and the only one.

   Bootstrap 5.3 keeps most component colours in local custom properties, but a handful of
   states are still written as literal declarations in its stylesheet: :focus rings on form
   controls, the checked state of a checkbox, and the link underline offset. Those need real
   rules rather than variables, and they are marked where they appear.
   ------------------------------------------------------------------------------------------- */

:root,
[data-bs-theme="light"] {
    --bs-font-sans-serif: var(--font-body);
    --bs-font-monospace: var(--font-mono);

    --bs-body-bg: var(--color-page);
    --bs-body-color: var(--color-text);
    --bs-emphasis-color: var(--color-text);
    --bs-secondary-color: var(--color-muted);
    --bs-secondary-bg: var(--color-surface-sunk);
    --bs-tertiary-bg: var(--color-surface-alt);

    --bs-border-color: var(--color-border);
    --bs-border-radius: var(--radius);
    --bs-border-radius-sm: var(--radius-sm);
    --bs-border-radius-lg: var(--radius-lg);

    /* Navy is the primary. Not the orange: primary is what buttons, links and focus rings are
       painted with, and an interface whose every action is the accent colour has no accent. */
    --bs-primary: var(--brand-navy);
    --bs-primary-rgb: var(--brand-navy-rgb);
    --bs-secondary: var(--color-muted);
    --bs-danger: var(--color-danger);
    --bs-success: var(--color-ok);
    --bs-warning: var(--brand-yellow);
    --bs-info: var(--color-info);

    --bs-link-color: var(--brand-navy);
    --bs-link-hover-color: var(--brand-orange-ink);
    --bs-link-color-rgb: var(--brand-navy-rgb);

    --bs-focus-ring-color: rgba(var(--brand-orange-rgb), 0.35);
    --bs-focus-ring-width: 3px;

    --bs-box-shadow-sm: var(--shadow-sm);
    --bs-box-shadow: var(--shadow);
}

/* Buttons ------------------------------------------------------------------------------------ */

.btn {
    --bs-btn-font-weight: 600;
    --bs-btn-focus-box-shadow: 0 0 0 3px rgba(var(--brand-orange-rgb), 0.35);
}

.btn-primary {
    --bs-btn-bg: var(--brand-navy);
    --bs-btn-border-color: var(--brand-navy);
    --bs-btn-color: var(--color-on-brand);
    --bs-btn-hover-bg: var(--brand-steel);
    --bs-btn-hover-border-color: var(--brand-steel);
    --bs-btn-hover-color: var(--color-on-brand);
    --bs-btn-active-bg: var(--brand-navy-dark);
    --bs-btn-active-border-color: var(--brand-navy-dark);
    --bs-btn-disabled-bg: var(--brand-navy);
    --bs-btn-disabled-border-color: var(--brand-navy);
    --bs-btn-disabled-color: var(--color-on-brand);
}

/* The accent action — "Nuevo cliente" and nothing else on a screen. Deep orange rather than the
   logo orange precisely because it carries a label; see the warning at the top of this file. */
.btn-accent {
    --bs-btn-bg: var(--brand-orange-ink);
    --bs-btn-border-color: var(--brand-orange-ink);
    --bs-btn-color: var(--color-on-brand);
    --bs-btn-hover-bg: var(--brand-orange-deep);
    --bs-btn-hover-border-color: var(--brand-orange-deep);
    --bs-btn-hover-color: var(--color-on-brand);
    --bs-btn-active-bg: var(--brand-orange-deep);
    --bs-btn-active-border-color: var(--brand-orange-deep);
    --bs-btn-disabled-bg: var(--brand-orange-ink);
    --bs-btn-disabled-border-color: var(--brand-orange-ink);
    --bs-btn-disabled-color: var(--color-on-brand);
}

.btn-outline-secondary {
    --bs-btn-color: var(--color-text);
    --bs-btn-border-color: var(--color-border-strong);
    --bs-btn-hover-bg: var(--color-surface-sunk);
    --bs-btn-hover-border-color: var(--color-border-strong);
    --bs-btn-hover-color: var(--color-text);
    --bs-btn-active-bg: var(--color-surface-sunk);
    --bs-btn-active-border-color: var(--brand-navy);
    --bs-btn-active-color: var(--color-text);
    --bs-btn-disabled-color: var(--color-muted);
    --bs-btn-disabled-border-color: var(--color-border);
}

.btn-danger {
    --bs-btn-bg: var(--color-danger);
    --bs-btn-border-color: var(--color-danger);
    --bs-btn-hover-bg: var(--color-danger-dark);
    --bs-btn-hover-border-color: var(--color-danger-dark);
    --bs-btn-active-bg: var(--color-danger-dark);
    --bs-btn-active-border-color: var(--color-danger-dark);
    --bs-btn-disabled-bg: var(--color-danger);
    --bs-btn-disabled-border-color: var(--color-danger);
}

.btn-link {
    --bs-btn-color: var(--brand-navy);
    --bs-btn-hover-color: var(--brand-orange-ink);
    --bs-btn-font-weight: 600;
}

/* Forms -------------------------------------------------------------------------------------- */

.form-control,
.form-select {
    --bs-body-bg: var(--color-surface);
    border-color: var(--color-border-strong);
}

.form-control:hover:not(:disabled):not([readonly]),
.form-select:hover:not(:disabled) {
    border-color: var(--brand-steel);
}

/* Written out rather than set through a variable: Bootstrap hard-codes the focus border and
   shadow of a form control to its default blue, so there is no property to re-declare. */
.form-control:focus,
.form-select:focus,
.form-check-input:focus {
    border-color: var(--brand-orange);
    box-shadow: 0 0 0 3px rgba(var(--brand-orange-rgb), 0.30);
}

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

.form-control:disabled,
.form-select:disabled {
    background-color: var(--color-surface-sunk);
    color: var(--color-muted);
}

.form-label {
    font-weight: 600;
    font-size: 0.875rem;
    margin-bottom: var(--space-1);
}

/* ASP.NET Core's validation tag helpers write these class names, not Bootstrap's. Mapping them
   here is what makes server-side validation look like the rest of the form without every page
   remembering to add .is-invalid by hand. */
.input-validation-error {
    border-color: var(--color-danger);
}

.field-validation-error {
    display: block;
    margin-top: var(--space-1);
    font-size: 0.875rem;
    color: var(--color-danger);
}

.field-validation-valid:empty {
    display: none;
}

/* The mark on a required field's label. Colour is not the carrier — the glyph is — because the
   whole point is that it survives a monochrome print and a red-green eye. --color-danger only
   makes it findable at a glance among fourteen labels that do not have it. */
.campo-obligatorio {
    color: var(--color-danger);
    font-weight: 700;
}

.validation-summary-valid {
    display: none;
}

/* Tables ------------------------------------------------------------------------------------- */

.table {
    --bs-table-bg: var(--color-surface);
    --bs-table-color: var(--color-text);
    --bs-table-border-color: var(--color-border);
    --bs-table-striped-bg: var(--color-surface-alt);
    --bs-table-striped-color: var(--color-text);
    --bs-table-hover-bg: var(--color-surface-sunk);
    --bs-table-hover-color: var(--color-text);
}

.table > thead {
    background-color: var(--color-surface-sunk);
}

.table > thead th {
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-muted);
    white-space: nowrap;
    border-bottom-color: var(--color-border-strong);
}

/* Badges ------------------------------------------------------------------------------------- */

.badge {
    --bs-badge-font-weight: 600;
    letter-spacing: 0.02em;
}

/* Pagination --------------------------------------------------------------------------------- */

.page-link {
    --bs-pagination-color: var(--brand-navy);
    --bs-pagination-hover-color: var(--brand-orange-ink);
    --bs-pagination-hover-bg: var(--color-surface-sunk);
    --bs-pagination-focus-color: var(--brand-orange-ink);
    --bs-pagination-focus-bg: var(--color-surface-sunk);
    --bs-pagination-focus-box-shadow: 0 0 0 3px rgba(var(--brand-orange-rgb), 0.30);
    --bs-pagination-active-bg: var(--brand-navy);
    --bs-pagination-active-border-color: var(--brand-navy);
    --bs-pagination-border-color: var(--color-border);
    --bs-pagination-disabled-color: var(--color-muted);
    --bs-pagination-disabled-border-color: var(--color-border);
}

/* Cards and alerts --------------------------------------------------------------------------- */

.card {
    --bs-card-bg: var(--color-surface);
    --bs-card-border-color: var(--color-border);
    --bs-card-cap-bg: var(--color-surface-alt);
    --bs-card-box-shadow: var(--shadow-sm);
}

.alert {
    --bs-alert-border-color: transparent;
    border-left-width: 4px;
    border-left-style: solid;
}

.alert-info {
    --bs-alert-bg: var(--color-info-bg);
    --bs-alert-color: var(--color-info);
    border-left-color: var(--color-info-border);
}

.alert-warning {
    --bs-alert-bg: var(--color-warn-bg);
    --bs-alert-color: var(--color-warn);
    border-left-color: var(--color-warn-border);
}

.alert-success {
    --bs-alert-bg: var(--color-ok-bg);
    --bs-alert-color: var(--color-ok);
    border-left-color: var(--color-ok-border);
}

.alert-danger {
    --bs-alert-bg: var(--color-danger-bg);
    --bs-alert-color: var(--color-danger);
    border-left-color: var(--color-danger-border);
}


/* ---- BASE ---------------------------------------------------------------------------------- */

html {
    font-size: 100%;
    -webkit-text-size-adjust: 100%;
}

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

/* THE ONLY PLACE --font-display IS APPLIED BROADLY, and the list below is the whole of the rest:
   .app-brand, .app-nav a, .app-nav__menu > summary, the auth screen's brand block, and
   .report-head__empresa. Nothing else — .report-head__titulo is an <h1> and is already covered
   here. If a rule elsewhere in this file starts reaching for --font-display, the split described
   in the token block has stopped being true.

   Bootstrap's .h1–.h6 classes come along because the five <legend class="h6"> in the forms are
   section titles wearing a different tag; a legend in Segoe under an <h2> in Poppins reads as a
   bug rather than as a decision. */
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
    font-family: var(--font-display);
}

/* One focus ring everywhere, and it is orange because the interface is navy — a navy ring on a
   navy control is invisible, which is the failure mode that matters.

   ⚠ The guideline recommends a 2px yellow focus ring. DECLINED on light surfaces, and the number
   is the whole argument: #EDCD1F on white is 1.6:1, where WCAG 2.2 asks 3:1 of a non-text
   indicator. A focus ring nobody can see is worse than a plain one, because keyboard users are
   the only people it exists for. Orange on white is 3.3:1 and stays.

   The header is the exception, and it is where the guideline was right: on navy the orange ring
   is 4.2:1 but the yellow is 8.9:1, so focus inside the header draws in yellow — the same colour
   that now marks the current section, on the one surface where both are legible. */
:focus-visible {
    outline: 3px solid var(--brand-orange);
    outline-offset: 2px;
}

.app-header :focus-visible {
    outline-color: var(--brand-yellow);
}

/* Figures that are read down a column — folios, quantities, dates — must not jitter. */
.tabular,
.table td code,
.table td .num {
    font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* An icon inside a control is decorative; the control's text names it. An icon that is the whole
   control needs aria-label, which is a markup rule and not something CSS can enforce. */
.bi {
    vertical-align: -0.125em;
}


/* ---- SHELL --------------------------------------------------------------------------------
   Deliberately not Bootstrap's .navbar. The application header is a fixed-shape bar with a
   wordmark, four links and an identity block — it never collapses to a hamburger, because there
   is no phone layout — and expressing that through the navbar's collapse machinery would be more
   markup for less control. Everything INSIDE a page is Bootstrap's.

   The class names here are load-bearing beyond the styling: UiFoundationTests asserts that
   .app-header and .app-nav are absent from the sign-in page and present on /Error.
   ------------------------------------------------------------------------------------------- */

.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 100;
    padding: var(--space-2) var(--space-4);
    background: var(--color-surface);
    color: var(--color-text);
    font-weight: 600;
    border: 2px solid var(--brand-orange);
    border-radius: 0 0 var(--radius) 0;
}

.skip-link:focus {
    left: 0;
}

.app-header {
    display: flex;
    align-items: center;
    gap: var(--space-5);
    min-height: var(--header-height);
    padding: 0 var(--space-5);
    background: var(--brand-navy);
    color: var(--color-on-brand);
    border-bottom: 3px solid var(--brand-orange);
}

.app-brand {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: var(--space-2);
    line-height: 1.15;
    text-decoration: none;
    color: inherit;
    padding: var(--space-2) 0;
    font-family: var(--font-display);
}

.app-brand:hover {
    color: inherit;
}

/* ⚠ THE WHITE TILE IS THE POINT, not decoration. The artwork is a white transformer body drawn in
   black outline with an orange TD across it; dropped straight onto #1E2D3B the outlines disappear
   into the navy and the mark reads as an orange smear. The guideline's own instruction is a "solid
   backing surface", and this is it.

   ⚠ NO padding AND NO background here, and that is a fix rather than an omission. Both used to be
   in this rule while logo-tile-40.png was itself a 40px canvas holding 32px of art — so border-box
   scaled the whole 40px image into a 32px content box, landing the art at 25.6px, of which only
   64% is transformer. About 16px of actual mark. The tile's white and its 2px inset now live in
   the FILE, defined once in build-assets.ps1 where the result can be looked at. This rule supplies
   size and corner radius and must not supply anything else.

   40px inside a 3.5rem (56px) header leaves 8px above and below, so THE HEADER DOES NOT GROW —
   which is what the guideline asks for in as many words. flex:none so the tile never absorbs the
   slack the wordmark and the navigation are competing for. */
.app-brand__mark {
    flex: none;
    width: 40px;
    height: 40px;
    border-radius: var(--radius);
}

/* The company name IS the wordmark now — it inherits the treatment .app-brand__product had, and
   the old uppercase letter-spaced .app-brand__company rules are gone with the element they styled.

   1rem, not the 1.0625rem the product name used, and the difference is a measurement rather than
   taste: "Grupo Técnica Dieléctrica" is 25 characters where "Sistema de Clientes" was 19, and in
   Poppins 700 it runs 225px at 17px against the old wordmark's 155px. At 16px it is 212px, which
   is what the header's horizontal budget has room for at 1366px beside five nav entries and a
   user block. See DECISIONS.md for the arithmetic.

   ⚠ white-space: nowrap IS THE GUARD, and it is not cosmetic. .app-header is a flex row with no
   wrapping above 60rem; if the wordmark broke across two lines the header would grow taller, which
   is the one thing this sub-workspace is not allowed to do. Nowrap makes the failure mode
   horizontal and visible instead of vertical and silent. */
.app-brand__company {
    font-size: 1rem;
    font-weight: 700;
    white-space: nowrap;
}

.app-brand:hover .app-brand__company {
    color: var(--brand-yellow);
}

/* Below 78rem the wordmark stands down and the mark carries the brand alone. The company's name is
   still in the <title>, in the footer and — the moment anything is printed — on the sheet. This is
   the permitted answer to the header running out of room; a taller header and fewer navigation
   entries are not. It sits above the 60rem breakpoint below which .app-header wraps anyway. */
@media (max-width: 78rem) {
    .app-brand__company {
        display: none;
    }
}

.app-nav {
    display: flex;
    align-items: stretch;
    gap: var(--space-1);
    align-self: stretch;
}

.app-nav a {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 0 var(--space-3);
    color: var(--color-on-brand-muted);
    text-decoration: none;
    font-family: var(--font-display);
    font-weight: 600;
    white-space: nowrap;
    border-bottom: 3px solid transparent;
    margin-bottom: -3px;
}

.app-nav a:hover {
    color: var(--color-on-brand);
    background: var(--overlay-on-brand);
}

/* aria-current is the marker: it is what a screen reader announces AND what draws the underline.
   One attribute carrying both, rather than a decorative class that says nothing to anybody.

   YELLOW, not orange, as of task 13c/05. The guideline asks for "a thin yellow rule or active
   underline to identify the current section" and keeps orange for high-priority ACTIONS — which
   is a real distinction and not a preference: "you are here" and "press this" were competing for
   the same colour, and the one the eye is drawn to should be the button. On navy the yellow
   measures 8.9:1, against the orange's 4.2:1, so the indicator also got more visible by moving.

   ⚠ The header's own 3px bottom rule stays orange. That is structure — it separates the header
   from the page and it never changes — and structure is not state. */
.app-nav a[aria-current="page"] {
    color: var(--color-on-brand);
    border-bottom-color: var(--brand-yellow);
}

/* ---- THE ADMINISTRACIÓN MENU --------------------------------------------------------------
   A <details> disclosure and not Bootstrap's .dropdown, which needs bootstrap.bundle.js to open.
   Every screen in this application works with scripting disabled, and a menu is exactly the kind
   of control that stops existing when it does not — same reasoning as D30, D40 and D48.

   The summary carries aria-current="true" ("the current item in a set") when the user is inside
   the section, and only the exact page's entry inside the panel carries "page". Two values, two
   meanings, and the "exactly one current entry" count still sees exactly one.
   -------------------------------------------------------------------------------------------- */

.app-nav__menu {
    position: relative;
    display: flex;
    align-items: stretch;
}

.app-nav__menu > summary {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 0 var(--space-3);
    color: var(--color-on-brand-muted);
    font-family: var(--font-display);
    font-weight: 600;
    cursor: pointer;
    user-select: none;
    border-bottom: 3px solid transparent;
    margin-bottom: -3px;

    /* The disclosure triangle is replaced by the chevron below, which can sit after the label
       instead of before it. Both spellings are needed: the pseudo-element is WebKit's. */
    list-style: none;
}

.app-nav__menu > summary::-webkit-details-marker {
    display: none;
}

.app-nav__menu > summary:hover {
    color: var(--color-on-brand);
    background: var(--overlay-on-brand);
}

.app-nav__menu > summary[aria-current="true"] {
    color: var(--color-on-brand);
    border-bottom-color: var(--brand-yellow);
}

.app-nav__caret {
    font-size: 0.6875rem;
}

.app-nav__menu[open] > summary .app-nav__caret {
    transform: rotate(180deg);
}

.app-nav__panel {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 20;
    display: flex;
    flex-direction: column;
    min-width: 15rem;
    padding: var(--space-1) 0;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 0 0 var(--radius) var(--radius);
    box-shadow: var(--shadow);
}

/* .app-nav a is a descendant selector, so the panel's links arrive wearing the header's clothes:
   muted on navy, with an underline that belongs on a bar and not in a list. Undo it here. */
.app-nav__panel a {
    padding: var(--space-2) var(--space-3);
    color: var(--color-text);
    font-weight: 500;
    border-bottom: 0;
    border-left: 3px solid transparent;
    margin-bottom: 0;
    white-space: nowrap;
}

.app-nav__panel a:hover {
    color: var(--color-text);
    background: var(--color-surface-sunk);
}

/* ⚠ THE ONE ACTIVE INDICATOR THAT STAYS ORANGE, and it is not an oversight. The panel is a white
   surface; --brand-yellow on white is 1.6:1 and the marker would simply disappear. WCAG 2.2 wants
   3:1 from a non-text indicator, and the bar above gets 8.9:1 only because it sits on navy. The
   guideline's yellow rule is a rule about the navy header, whatever it says about "active states"
   in general — so the panel keeps orange, and the reader still has weight, colour and background
   telling them where they are. */
.app-nav__panel a[aria-current="page"] {
    color: var(--brand-navy);
    font-weight: 700;
    background: var(--color-surface-alt);
    border-bottom-color: transparent;
    border-left-color: var(--brand-orange);
}

.app-user {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-left: auto;
    font-size: 0.875rem;
}

.app-user__name {
    font-weight: 600;
}

.app-user__role {
    color: var(--color-on-brand-muted);
}

.app-user .btn-link {
    --bs-btn-color: var(--color-on-brand-muted);
    --bs-btn-hover-color: var(--brand-yellow);
    padding: 0;
}

.app-main {
    flex: 1;
    width: 100%;
    max-width: var(--content-width);
    margin: 0 auto;

    /* Tighter at the top than it was. The first thing on every screen is a breadcrumb, and two
       centimetres of nothing above it is a centimetre of the client's data pushed under the fold. */
    padding: var(--space-5) var(--space-5) var(--space-6);
}

.app-footer {
    padding: var(--space-4) var(--space-5);
    background: var(--color-surface);
    border-top: 1px solid var(--color-border);
    color: var(--color-muted);
    font-size: 0.8125rem;
}

@media (max-width: 60rem) {
    .app-header {
        flex-wrap: wrap;
        gap: var(--space-3);
        padding: var(--space-2) var(--space-4);
    }

    .app-nav {
        order: 3;
        width: 100%;
    }

    .app-user {
        font-size: 0.8125rem;
    }

    .app-main {
        padding: var(--space-5) var(--space-4) var(--space-6);
    }
}


/* ---- AUTH ---------------------------------------------------------------------------------
   The pages reached before there is a session. A visitor who is not signed in must not be shown
   a navigation bar full of links they cannot follow, which is what _AuthLayout exists to prevent.
   ------------------------------------------------------------------------------------------- */

.auth {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-5);
    padding: var(--space-6) var(--space-4);
    background: linear-gradient(160deg, var(--brand-navy) 0%, var(--brand-navy-dark) 100%);
}

/* Inside the card as of 13c/06b, so it no longer paints itself onto the navy — it inherits the
   card's text colour and separates itself from the form with a rule rather than with distance. */
.auth__brand {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    text-align: center;
    font-family: var(--font-display);
    padding-bottom: var(--space-5);
    margin-bottom: var(--space-5);
    border-bottom: 1px solid var(--color-border);
}

/* 3rem to 6rem, and this is ONE OF THE THREE GEOMETRY CHANGES the sub-workspace permits — the
   "logo de inicio" the customer asked for by name.

   No tile and no padding: the block sits inside .auth__card now, so the card's white IS the
   backing surface. width is auto and only the height is fixed, because the artwork is 105x96 and
   not square — forcing a square would stretch it 9%, which the guideline forbids in as many
   words. */
.auth__mark {
    width: auto;
    height: 6rem;
}

/* Navy, because the surface under it is now white. 14.06:1 — which is exactly what white on the
   navy gradient measured, so this is not a legibility improvement and should not be recorded as
   one. It is a heading on a card now rather than a caption on a backdrop, and it reads like one. */
.auth__company {
    font-size: 1.375rem;
    font-weight: 700;
    color: var(--brand-navy);
}

.auth__card {
    width: 100%;
    max-width: 24rem;
    padding: var(--space-6);
    background: var(--color-surface);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
}

.auth__card h1 {
    font-size: 1.25rem;
    margin-bottom: var(--space-5);
}

.auth__footer {
    color: var(--color-on-brand-muted);
    font-size: 0.8125rem;
}


/* ---- TABLES -------------------------------------------------------------------------------
   Bootstrap has no sortable header, so the sort affordance is here. aria-sort is both the
   accessibility marker and the CSS hook, for the same reason aria-current is in the navigation.
   ------------------------------------------------------------------------------------------- */

.table > thead th > a {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    color: inherit;
    text-decoration: none;
}

.table > thead th > a:hover {
    color: var(--brand-orange-ink);
    text-decoration: underline;
}

.table > thead th[aria-sort] {
    color: var(--color-text);
}

.table > thead th[aria-sort="ascending"] > a::after {
    content: "▲";
    font-size: 0.625rem;
}

.table > thead th[aria-sort="descending"] > a::after {
    content: "▼";
    font-size: 0.625rem;
}

/* The column that holds row actions: as narrow as its buttons, never a share of the leftovers. */
.table .actions {
    width: 1%;
    white-space: nowrap;
    text-align: right;
}

.table .num {
    text-align: right;
}

/* The empty state sits below the table rather than inside it as a full-width cell, so the column
   headers stay readable and explain what would have been there. */
.table-empty {
    padding: var(--space-7) var(--space-4);
    text-align: center;
    color: var(--color-muted);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-top: 0;
    border-radius: 0 0 var(--radius) var(--radius);
}

.table-empty .bi {
    display: block;
    font-size: 1.75rem;
    margin-bottom: var(--space-2);
    color: var(--color-border-strong);
}


/* ---- HTMX ---------------------------------------------------------------------------------
   htmx toggles .htmx-request on the element named by hx-indicator for the life of the request.
   Nothing here is a JavaScript file: the busy state is an attribute htmx sets and CSS reacts to.
   ------------------------------------------------------------------------------------------- */

.htmx-indicator {
    opacity: 0;
    transition: opacity 120ms ease-in;
}

.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
    opacity: 1;
}

.spinner {
    display: inline-block;
    width: 1rem;
    height: 1rem;
    border: 2px solid var(--color-border-strong);
    border-top-color: var(--brand-orange);
    border-radius: 50%;
    animation: spin 700ms linear infinite;
}

@keyframes spin {
    to {
        transform: rotate(360deg);
    }
}

/* The results block dims while it is being replaced. Enough to say "this is stale", not enough
   to make the page flash on every keystroke of a search-as-you-type field. */
[aria-busy="true"] {
    opacity: 0.6;
    transition: opacity 120ms ease-in;
}


/* ---- EDIT ZONE ----------------------------------------------------------------------------
   The block that says "you are working HERE".

   Every editor in this application opens IN PLACE: a contact row becomes a form, the last-service
   date becomes a form, a new movement appears at the top of a list of ten. That is the right
   behaviour — it keeps the record you are editing beside the record it belongs to — but it has one
   failure mode, and the customer named it: the card silently changes shape and nothing on screen
   says which part of it is now yours. You have to read the whole thing to work out what happened.

   So an open editor is wrapped in one of these, and it carries three signals at once:

     the tinted surface     the area is different from everything around it
     the thick left edge    it starts HERE, which a background alone does not say
     the title              what you are doing, and to which record

   The title is the half that does the real work. "Editando movimiento · del 12/mar/2019, ANÁLISIS
   DE ACEITE" answers the question directly; a coloured box only says "something".

   THE DANGER VARIANT IS NOT DECORATION. A delete confirmation is also an editor that replaced a
   row, and it is the one where mistaking which row you are on is expensive. Same component, red.

   NAVY, NOT ORANGE, AND THIS WAS TRIED THE OTHER WAY FIRST. The first version washed the panel in
   the accent, on the reasoning that a grey one would read as "disabled". Three things were wrong
   with it on screen:

     - a translucent orange over white lands within a couple of steps of --color-danger-bg, so the
       editing state and the about-to-delete state were the same colour. The variant that has to be
       unmistakable was competing with the one that is routine. (The two values are in D41; they are
       not repeated here, because the check that keeps hex out of this file is a grep and does not
       read comments)
     - orange is this interface's alarm colour everywhere else — the overdue badge, the PCB
       warning, the focus ring. A whole panel in it reads as a problem, and opening a form is not
       one
     - the focus ring is orange, so the ring around the field being typed into vanished inside the
       panel. That is a functional loss, not a matter of taste

   Navy is the structure, and an open editor IS structure: a panel inset into a card. The wash lands
   near --color-page, so it reads as a recess rather than as a state, red goes back to meaning only
   one thing, and the orange focus ring is the brightest thing in the panel again — which is right,
   because it marks the field the cursor is in.
   ------------------------------------------------------------------------------------------- */

.edit-zone {
    padding: var(--space-3) var(--space-4);
    background: var(--color-edit-bg);
    border: 1px solid var(--color-edit-border);
    border-left: 4px solid var(--brand-navy);
    border-radius: var(--radius);
}

.edit-zone__title {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--space-1) var(--space-2);
    margin-bottom: var(--space-3);
    padding-bottom: var(--space-2);
    border-bottom: 1px solid var(--color-edit-border);
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--brand-navy);
}

/* Which record. Not uppercased with the label: it is a name or a date and a movement type, and
   twelve words in capitals is a line nobody reads. */
.edit-zone__subject {
    font-weight: 400;
    letter-spacing: normal;
    text-transform: none;
    color: var(--color-text);
}

.edit-zone--danger {
    background: var(--color-danger-bg);
    border-color: var(--color-danger-border);
    border-left-color: var(--color-danger);
}

.edit-zone--danger .edit-zone__title {
    border-bottom-color: var(--color-danger-border);
    color: var(--color-danger);
}

/* El renglón de una tabla que se abre en un formulario (task 13e/06). Lo usa la tarjeta de
   equipos: la unidad que se edita deja de dibujarse como renglón y su <tr> pasa a ser UNA sola
   celda a todo lo ancho con la .edit-zone adentro.

   ⚠ Un <form> no puede ser hijo de <tr> — el parser lo saca de la tabla y los campos no envían
   nada — pero <td> admite contenido de flujo, así que dentro de la celda es perfectamente legal.
   Esa distinción es la que permitió que los equipos volvieran a la ficha sin perder la tabla.

   La celda no pone relleno propio: el que se ve es el de .edit-zone, y sumarle el de la tabla
   dejaría el formulario despegado del borde izquierdo justo donde el ojo busca la línea de color
   que dice dónde empieza. */
.fila-editor > td {
    padding: var(--space-2) 0;
}


/* ---- ATAJOS DE FECHA ----------------------------------------------------------------------
   Un renglón de días comunes encima de una caja de fecha (task 13e/04). Tres pantallas lo usan:
   el recordatorio del expediente, el reagendado de la agenda y la barra de lote de Seguimiento.

   ⚠ HIDDEN UNTIL date-presets.js ADDS .gtd-js TO <html>, and that is the load-bearing part of
   this block rather than the styling. The buttons do nothing without the script, so with scripting
   off there must be no row at all — the same rule the notification controls follow (13d/18), and
   the arrangement D30, D40 and D48 leave room for. It is a class on the ROOT and not a hidden
   attribute the script clears, because two of the three rows arrive inside htmx swaps: a swapped-in
   copy is revealed by a rule that was already true, with nothing to initialise a second time.

   Small, quiet and outline: they sit above a form control and must not out-shout the control they
   fill, still less the form's own submit button. Bootstrap has no size below .btn-sm that reads as
   a chip, which is what the four rules below are for. */
.atajos-fecha {
    display: none;
    flex-wrap: wrap;
    gap: var(--space-1);
    margin-bottom: var(--space-1);
}

.gtd-js .atajos-fecha {
    display: flex;
}

.atajos-fecha__boton {
    padding: 0.05rem var(--space-2);
    border: 1px solid var(--color-border);
    border-radius: 999px;
    background: var(--color-surface);
    font-size: 0.75rem;
    line-height: 1.5;
    color: var(--brand-steel);
    cursor: pointer;
}

.atajos-fecha__boton:hover {
    border-color: var(--brand-navy);
    color: var(--brand-navy);
}


/* ---- MOVIMIENTO BUSCADO -------------------------------------------------------------------
   El renglón del expediente al que llegó un enlace (task 13e/05). "Ver movimiento" en la agenda
   y en la tarjeta de último servicio traen ?movimiento=N; el servidor abre la página que lo
   contiene y le pone esta marca.

   ⚠ LA DIBUJA EL SERVIDOR, no un script y no :target. Un :target se pierde en cuanto htmx
   reemplaza la región — y esta región se reemplaza al abrir un editor, al pasar de página y
   cuando otra tarjeta la manda de pasajera. Una clase que viene en el HTML sobrevive a todo eso
   porque no hay nada que volver a ejecutar.

   La marca SE QUEDA mientras ?movimiento= esté en la dirección, que es hasta que el lector
   presiona cualquier otra cosa dentro de la tarjeta. Un destello de tres segundos se pierde si
   alguien miró a otro lado, y esto se lee después de haber hecho clic desde otra pantalla.

   El borde a la izquierda es el mismo recurso que .edit-zone: dice DÓNDE EMPIEZA la cosa
   señalada, que es lo que un fondo solo no dice cuando el renglón mide cuatro líneas. */
.movimiento-buscado {
    border-left: 3px solid var(--brand-orange);
    border-radius: var(--radius-sm);
    background: var(--color-surface-sunk);
    padding: var(--space-2) var(--space-3);
    margin-left: calc(-1 * var(--space-3));
}


/* ---- AGENDA -------------------------------------------------------------------------------
   The work area's own three pieces. Everything else on that screen is Bootstrap's card, list and
   badge, which is why this section is short and not forty rules long.
   ------------------------------------------------------------------------------------------- */

/* N vencidas · N hoy · N próximas — the answer to "how am I doing" before the eye reaches the
   first row. Pendientes only, always: see AgendaWorkload. */
.agenda-summary {
    color: var(--color-muted);
    font-size: 0.875rem;
}

.agenda-summary__n {
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--color-text);
}

.agenda-summary__n--vencidas {
    color: var(--color-danger);
}

/* ---- THE THREE CARDS THAT OPEN --------------------------------------------------------------
   A <summary> only draws a disclosure triangle while it is display: list-item, and .card-header
   carries d-flex. So the one card that was a <details> looked exactly like the two solid cards
   above it — a header with a number in the corner and no body — and read as a section that had
   failed to render. The September 2026 audit filed it as empty.

   The chevron is therefore not decoration: it is the only thing on screen that says a card has
   contents and a state. list-style: none plus the WebKit pseudo-element is the same two-spelling
   removal the Administración menu needs, kept here so the two never disagree about which marker
   is showing.

   ⚠ SINCE 13e/02 ALL THREE BLOCKS CARRY THIS, which is why the class is named for what it does
   rather than for Próximos. Vencidas and Hoy became collapsible at the customer's request, and
   the finding above is three times as easy to reproduce as it was: a card of this shape that ships
   closed reads as a card that rendered nothing. All three ship open — see _Agenda.cshtml.
   -------------------------------------------------------------------------------------------- */

/* The three blocks of the work area, told apart by colour as well as by heading (task 08).

   ⚠ THE PAIRS ARE THE ONES .alert ALREADY SHIPS, and that is the whole reason they are these
   pairs: --color-danger on --color-danger-bg is what .alert-danger renders, so the contrast is
   one somebody already checked rather than one invented here. Picking fresh tints for three cards
   would have been three new contrast decisions in a file whose rule is that no colour exists
   outside the token block.

   The left rule is .alert's idiom too — 4px, semantic colour — so the screen gains an accent it
   already speaks somewhere else instead of a second visual language.

   The ramp is deliberate and reads without the labels: red is late, amber is now, blue is soon.
   ⚠ It is NOT red/amber/green: green is --color-ok, "this went well", and a follow-up you have
   not made yet has not gone well — it has not happened. */
.agenda-bloque {
    border-left: 4px solid var(--color-border-strong);
}

.agenda-bloque > .card-header h2,
.agenda-bloque > summary h2 {
    color: inherit;
}

.agenda-bloque--vencidas {
    --bs-card-cap-bg: var(--color-danger-bg);
    border-left-color: var(--color-danger);
    color: var(--color-danger);
}

.agenda-bloque--hoy {
    --bs-card-cap-bg: var(--color-warn-bg);
    border-left-color: var(--color-warn);
    color: var(--color-warn);
}

.agenda-bloque--proximas {
    --bs-card-cap-bg: var(--color-info-bg);
    border-left-color: var(--color-info);
    color: var(--color-info);
}

/* ⚠ The tint belongs to the header, not to the rows. The body goes back to ordinary text: the
   items inside carry their own meaning — a struck-through closed row, a client's name — and
   tinting three hundred pixels of list would drown all of it to say something the header has
   already said. */
.agenda-bloque > .card-body {
    color: var(--color-text);
}

.agenda-plegable > summary {
    cursor: pointer;
    user-select: none;
    list-style: none;
}

.agenda-plegable > summary::-webkit-details-marker {
    display: none;
}

/* --color-surface-sunk and not --color-surface-alt: Bootstrap already paints every .card-header
   with the latter (--bs-card-cap-bg), so hovering to it would change nothing at all. */
.agenda-plegable > summary:hover {
    background: var(--color-surface-sunk);
}

/* --color-text and not --color-muted: this is the control, not an annotation beside one. In
   muted grey it was legible and still did not read as something to press. */
.agenda-plegable__caret {
    font-size: 0.8125rem;
    color: var(--color-text);
    transition: transform 120ms ease;
}

.agenda-plegable[open] > summary .agenda-plegable__caret {
    transform: rotate(180deg);
}

/* Reduced motion turns the rotation into a jump, not into nothing: the direction the chevron
   points is the state, and it has to keep pointing. */
@media (prefers-reduced-motion: reduce) {
    .agenda-plegable__caret {
        transition: none;
    }
}

/* ---- THE DATE RANGES ON THE CLIENT LIST (13e/08) ---------------------------------------------
   Two ranges, four boxes, and a filter bar that already carried six controls. The customer chose
   the disclosure over a permanent third row, with one condition: CLOSED, IT MUST NOT COST A ROW.

   So the <details> is an ordinary column of the second row - it sits beside "Seguimiento" like one
   more box - and everything below is the two rules that let it grow: open, it takes the full width,
   and Bootstrap's .row (display: flex; flex-wrap: wrap) puts a 100 % item on a line of its own.
   Nothing is positioned, nothing overlays the table, and the results move down by exactly the
   height of what appeared.

   A <details> and not a Bootstrap collapse, as everywhere else here: it opens with no script.
   ------------------------------------------------------------------------------------------- */
.filtro-fechas > summary {
    cursor: pointer;
    user-select: none;
    list-style: none;

    /* Sized to look like the controls it stands among rather than like text: the same small
       height, the same border, the same radius. It is not a .form-select - that would draw a
       select's own caret behind this one - and it is not a .btn, because a filter that is
       currently OFF should not look like the row's action. */
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-height: calc(1.5em + 0.5rem + 2px);
    padding: 0.25rem 0.5rem;
    font-size: 0.875rem;
    color: var(--color-text);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
}

.filtro-fechas > summary::-webkit-details-marker {
    display: none;
}

.filtro-fechas > summary:hover {
    background: var(--color-surface-sunk);
}

/* The chevron is the state, so it turns. Muted, unlike the agenda's: that one is the only control
   on a card header, this one sits in a row of five other boxes and does not need to compete. */
.filtro-fechas__caret {
    margin-left: auto;
    font-size: 0.75rem;
    color: var(--color-muted);
    transition: transform 120ms ease;
}

.filtro-fechas[open] > summary .filtro-fechas__caret {
    transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
    .filtro-fechas__caret {
        transition: none;
    }
}

/* THE ONE RULE THE WHOLE SHAPE HANGS ON. flex-basis 100 % on a wrapping flex row moves this item
   to its own line and leaves the five controls above exactly where they were. */
.filtro-fechas[open] {
    flex: 0 0 100%;
    max-width: 100%;
}

/* Open, the summary stops being a box among boxes: it is now the heading of the panel underneath,
   and a full-width bordered control there reads as an input somebody forgot to fill in. */
.filtro-fechas[open] > summary {
    width: fit-content;
    border-color: transparent;
    background: transparent;
    font-weight: 600;
}

.filtro-fechas__cuerpo {
    margin-top: var(--space-1);
}

.filtro-fechas__cuerpo legend {
    /* A <legend> is block-level and full-width by default, which would push the two date boxes
       under it down by a line each on a narrow screen. */
    float: none;
    width: auto;
    font-size: 0.875rem;
    font-weight: 600;
}

/* ---- THE CONTACT ON AN AGENDA ROW (13e/03) --------------------------------------------------
   Who to call, one click down, on both agenda screens. The customer's sentence offered three
   shapes — a popup, an inline block, a card on hover — and this is a <details>: rendered with the
   page and hidden by the browser, so there is no second request; keyboard-reachable for free; and
   native, so it survives the htmx swap of #agenda that a Bootstrap popover would not.

   ⚠ NO d-flex ON THIS SUMMARY, and that is the whole reason it needs almost no rules. An element
   draws a disclosure marker only while it is display: list-item, and d-flex takes that away — the
   exact mechanism behind the two audit findings on the three blocks above. Those cards carry
   .card-header, so they must suppress the native marker and draw a chevron; this one is ordinary
   text and keeps the browser's own triangle.
   -------------------------------------------------------------------------------------------- */

/* Muted and small, because it sits in the run of muted small lines that already say the owner and
   «ver movimiento». It is the row's least urgent line: the date, the client and the título are what
   somebody reads first, and this is what they open once they have decided to act on it. */
.agenda-contacto > summary {
    cursor: pointer;
    color: var(--color-muted);
    font-size: 0.875rem;
    width: fit-content;
}

.agenda-contacto > summary:hover {
    color: var(--color-text);
}

/* Indented under the summary, so a row's card reads as belonging to that row and not as one more
   line of the row's own text. The border is the same 2px idiom the expediente's nested blocks use. */
.agenda-contacto__cuerpo {
    margin: var(--space-1) 0 0 var(--space-4);
    padding-left: var(--space-2);
    border-left: 2px solid var(--color-border);
    color: var(--color-muted);
    font-size: 0.875rem;
}

/* ⚠ The telephone in this card belongs to the CLIENT, not to the contact above it, and the label
   says so rather than letting somebody read a general switchboard as one person's mobile. It is
   here because it is the only telephone the imported book has: of SUR's 773 principal contacts,
   none carries a celular, while 769 of its 788 clients carry clients.telefonos. */
.agenda-contacto__fuente {
    color: var(--color-muted);
    font-size: 0.75rem;
    white-space: nowrap;
}


/* A LINK drawn as a checkbox, because a real <input type="checkbox"> cannot submit itself without
   script, and no control in this application depends on script (D30, D40, D48). The icon carries
   the state; the words carry the action.

   Named .toggle-link and not .agenda-toggle since task 08: it is "mostrar cerradas" on the agenda
   and "marcar los de esta página" on the follow-up screens, and a component two features share
   should not be named after the first of them. */
.toggle-link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-sm);
    color: var(--color-muted);
    text-decoration: none;
    font-size: 0.875rem;
}

.toggle-link:hover {
    color: var(--color-text);
    background: var(--color-surface-sunk);
}

/* A closed row, mixed in among the open ones. Struck through and quiet, but never invisible and
   never illegible: it keeps its estado badge, because meaning carried by styling alone is meaning
   a screen reader never announces — and its "Reabrir" button keeps full contrast, since undoing a
   mis-click is the entire reason the row is on this screen. */
.agenda-row--cerrada {
    color: var(--color-muted);
}

.agenda-row--cerrada .agenda-row__titulo {
    text-decoration: line-through;
}


/* ---- THE MONTH GRID (07c) ------------------------------------------------------------------
   The customer's reference is a Blazor desktop screen at a fixed window width, and what carries
   across is its density rather than its code: seven equal columns, no gutters, three items per cell
   and then a count, adjacent-month days drawn greyed rather than left blank.

   WHAT DOES NOT CARRY ACROSS is the popup. There, a cell opens a floating panel and "+N more" opens
   a modal; here selecting a day is a GET and the items render below the grid, so there is one code
   path instead of three and the address can be sent to somebody. That also decides the markup: the
   whole cell is one <a> and the chips are <span>, because an anchor inside an anchor is invalid and
   the parser unnests it without saying so.
   ------------------------------------------------------------------------------------------- */

.agenda-cal__bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-3) var(--space-4);
    margin-bottom: var(--space-3);
}

/* Fixed width, or the arrows shuffle sideways between "mayo de 2026" and "septiembre de 2026" and
   the button somebody is aiming at moves while they read the month. */
.agenda-cal__mes {
    min-width: 11rem;
    text-align: center;
    text-transform: none;
}

.agenda-cal__leyenda {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-4);
    margin-left: auto;
    font-size: 0.8125rem;
    color: var(--color-muted);
}

.agenda-cal__leyenda li {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}

/* A rounded square rather than a dot: it reads as a miniature of the chip it stands for. */
.agenda-cal__punto {
    width: 0.75rem;
    height: 0.75rem;
    border-radius: var(--radius-sm);
    background: var(--color-muted);
}

.agenda-cal__punto--vencida { background: var(--color-danger); }
.agenda-cal__punto--hoy { background: var(--color-warn); }
.agenda-cal__punto--proxima { background: var(--color-info); }
.agenda-cal__punto--lejana { background: var(--color-muted); }

.agenda-cal__punto--cerrada {
    background: var(--color-surface);
    border: 1px solid var(--color-border-strong);
}

/* NO GAP. Cells draw only their right and bottom edge and the first column re-adds its left, which
   collapses the borders into single rules — the difference between a paper calendar and a board of
   floating tiles. overflow:hidden is what lets the container's radius clip the corner cells.

   EVERY CELL IS THE SAME HEIGHT, and that is grid-auto-rows rather than a min-height on the cell.
   With a minimum, a week holding a three-chip day is taller than a week holding none, so the rows
   step up and down the month and the eye cannot compare two weeks by their weight any more — which
   is the one thing a month grid is read for. `grid-template-rows: auto` pins only the weekday
   header; every week after it takes the fixed height, whatever number of weeks the grid has. */
.agenda-cal__grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    grid-template-rows: auto;
    grid-auto-rows: var(--agenda-cal-alto);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    overflow: hidden;
    background: var(--color-surface);
}

.agenda-cal__dow {
    background: var(--brand-navy);
    color: var(--color-on-brand);
    padding: var(--space-2);
    text-align: center;
    font-size: 0.8125rem;
    font-weight: 600;
    letter-spacing: 0.04em;
}

/* overflow:hidden, and it is doing real work: the cell is a fixed height now, so a day that somehow
   holds more than it was measured for is clipped instead of stretching its whole week. */
.agenda-cal__dia {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    overflow: hidden;
    padding: var(--space-1) var(--space-2);
    border-top: 1px solid var(--color-border);
    border-right: 1px solid var(--color-border);
    color: var(--color-text);
    text-decoration: none;
}

.agenda-cal__grid > .agenda-cal__dia:nth-child(7n) {
    border-right: none;
}

a.agenda-cal__dia:hover {
    background: var(--color-surface-sunk);
}

/* Greyed and in place, never blank. A grid whose first row starts halfway across reads as a
   rendering failure, and the rows in those cells are real — they are the last days of August. */
.agenda-cal__dia--fuera {
    background: var(--color-surface-alt);
}

.agenda-cal__dia--fuera .agenda-cal__n,
.agenda-cal__dia--fuera .agenda-cal__n-largo {
    color: var(--color-muted);
}

/* Three cues at once, because "today" must not depend on colour: an inset ring, a wash, and the day
   number in a filled circle. An inset box-shadow rather than a border, so the cell does not grow by
   two pixels and shove its neighbours out of alignment. */
.agenda-cal__dia--hoy {
    background: rgba(var(--brand-navy-rgb), 0.05);
    box-shadow: inset 0 0 0 2px var(--brand-navy);
}

.agenda-cal__dia--hoy .agenda-cal__n {
    background: var(--brand-navy);
    color: var(--color-on-brand);
    border-radius: 50%;
    width: 1.5rem;
    height: 1.5rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* The selected day. The reference has no such state — it opened a popup instead — so this is new,
   and it is orange because it is the one thing on the grid the reader chose.

   ⚠ AUDITED AGAINST THE BRAND GUIDELINE IN TASK 13c/05 AND DELIBERATELY LEFT ALONE. The guideline
   puts yellow on "today" and "selected" and keeps orange for overdue and high priority, so on
   paper this cell and the one above it both disagree with it. The measurement is why they stand:
   these cells are white and near-white, and #EDCD1F on white is 1.6:1 — a ring nobody can see.
   The guideline's yellow is written for the navy header, where it reaches 8.9:1 and where the
   navigation now uses it. Orange-ink here is 4.6:1 and navy is 14:1.

   Neither marker is colour alone in any case — today has an inset ring, a wash AND a filled day
   number; selected has a ring of a different colour on top of them — so the guideline's real rule,
   "never rely on color alone", is the one being kept. Do not re-open this without a contrast
   checker open beside the grid. */
.agenda-cal__dia--sel {
    box-shadow: inset 0 0 0 2px var(--brand-orange-ink);
}

.agenda-cal__dia--hoy.agenda-cal__dia--sel {
    box-shadow: inset 0 0 0 2px var(--brand-orange-ink),
                inset 0 0 0 4px var(--brand-navy);
}

/* A fixed 1.5rem square whether or not it is today, because today's filled circle is 1.5rem and a
   number that grew only on one cell would make that one cell's chips start a quarter of a rem lower
   than every other cell's. flex:none so it never absorbs the slack the chips need. */
.agenda-cal__n {
    flex: none;
    align-self: flex-start;
    height: 1.5rem;
    line-height: 1.5rem;
    font-size: 0.875rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* "lun 14 sep" — only below lg, where the grid is one column and a bare number says nothing. */
.agenda-cal__n-largo {
    display: none;
    font-size: 0.875rem;
    font-weight: 600;
}

.agenda-cal__chips {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    min-height: 0;
    overflow: hidden;
}

/* Sibling of the chip stack rather than its last child, so when a cell does run out of room the
   chips are what gets clipped and the count survives — it is the only thing on the cell that admits
   the cell is incomplete. flex:none for the same reason. */
.agenda-cal__mas {
    flex: none;
    font-size: 0.75rem;
    line-height: 1.2;
    font-weight: 600;
    color: var(--brand-orange-ink);
}

/* One item on one day. Colour says how urgent, the icon says the same thing without colour, and the
   title attribute carries the text the truncation ate. */
.agenda-chip {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    min-width: 0;
    padding: 1px var(--space-1);
    border-radius: var(--radius-sm);
    font-size: 0.75rem;
    line-height: 1.3;
    color: var(--color-on-brand);
    background: var(--color-muted);
}

/* min-width:0 on the flex child is what actually lets the ellipsis happen; without it the text
   refuses to shrink below its content and pushes the cell wider than its column. */
.agenda-chip__texto {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* WHOSE IT IS, on the division's month only — see CalendarView.MostrarDuenio. Pushed to the trailing
   edge with margin-left:auto and never allowed to shrink: the left of a chip is always urgency, the
   right is always the person, and a reader scanning a week for their own two letters is scanning one
   column rather than reading every chip. The client's name gives up the width, because it has the
   ellipsis and the monogram does not — two letters cut in half are not two letters. */
.agenda-chip__quien {
    flex: none;
    margin-left: auto;
    padding: 0 3px;
    border-radius: var(--radius-sm);
    background: var(--overlay-on-brand-strong);
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.02em;
}

/* A closed chip has a white background, where a translucent white monogram is invisible. */
.agenda-chip--cerrada .agenda-chip__quien,
.agenda-cal__leyenda .agenda-chip__quien {
    background: var(--color-surface-sunk);
    color: var(--color-muted);
}

.agenda-chip--vencida { background: var(--color-danger); }
.agenda-chip--hoy { background: var(--color-warn); }
.agenda-chip--proxima { background: var(--color-info); }
.agenda-chip--lejana { background: var(--color-muted); }

/* A closed item loses its urgency colour ENTIRELY — it is no longer urgent, and leaving it red
   would be the calendar shouting about work somebody already did. Struck through and outlined, the
   same idiom .agenda-row--cerrada uses in the list. */
.agenda-chip--cerrada {
    background: var(--color-surface);
    border: 1px solid var(--color-border-strong);
    color: var(--color-muted);
    padding: 0 calc(var(--space-1) - 1px);
}

.agenda-chip--cerrada .agenda-chip__texto {
    text-decoration: line-through;
}

/* Below lg the seven columns become one. A calendar that scrolls sideways is a calendar nobody
   reads, and rendering the work list as well just so CSS could throw one of them away would mean
   two queries on every load to show one of them. So the month stays the month: the days that have
   work, in order, with the weekday spelled out because the column that used to say it is gone. */
@media (max-width: 991.98px) {
    .agenda-cal__grid {
        grid-template-columns: 1fr;
        grid-auto-rows: auto;
    }

    .agenda-cal__dow,
    .agenda-cal__dia--fuera,
    .agenda-cal__dia--vacio {
        display: none;
    }

    /* The one column is a list of the days that have work, so here a cell SHOULD be as tall as what
       is in it — equal heights buy a comparison between weeks, and there are no weeks left. */
    .agenda-cal__dia {
        overflow: visible;
        border-right: none;
    }

    .agenda-cal__n {
        display: none;
    }

    .agenda-cal__n-largo {
        display: block;
    }

    .agenda-cal__leyenda {
        margin-left: 0;
    }
}


/* ---- EL RECORDATORIO EN LA BITÁCORA --------------------------------------------------------
   A movement that created a follow-up says so on its metadata line, under the author. Everything
   else on that line is muted grey 0.875rem and should stay that way — it is provenance. The
   reminder is not: it is the one item there that is still owed to somebody, and it is what the
   reader of a client's file is scanning for.

   ⚠ THE COLOURS ARE THE MONTH GRID'S, WHICH IS WHY THIS SECTION SITS DIRECTLY UNDER IT. The four
   buckets are AgendaWorkPolicy's, and .agenda-chip / .agenda-cal__punto twelve lines above already
   spend --color-danger, --color-warn, --color-info and --color-muted on exactly these four
   meanings. The tinted -bg variants are used here rather than the solid fills because this chip
   sits inside running text at 0.875rem, where a saturated block would outweigh the client's own
   name two lines above it — the brief was "resáltalo un poco".

   NO CLOSED STATE. ExpedienteRepository's subquery reads MIN(fecha_programada) WHERE estado =
   'pendiente', so an attended or cancelled reminder simply stops being rendered. There is nothing
   here for .agenda-chip--cerrada to correspond to.
   ------------------------------------------------------------------------------------------- */

.recordatorio {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: 0 var(--space-1);
    border-radius: var(--radius-sm);
    font-weight: 600;
}

.recordatorio--vencida {
    background: var(--color-danger-bg);
    color: var(--color-danger);
}

.recordatorio--hoy {
    background: var(--color-warn-bg);
    color: var(--color-warn);
}

.recordatorio--proxima {
    background: var(--color-info-bg);
    color: var(--color-info);
}

/* Beyond the fourteen-day window, and written out rather than left to the base rule: a bucket with
   no declaration of its own reads as one somebody forgot. It is the quietest of the four because a
   reminder three months out is not news. */
.recordatorio--lejana {
    background: var(--color-surface-sunk);
    color: var(--color-muted);
}


/* ---- UTILITIES ----------------------------------------------------------------------------
   Only what Bootstrap does not already provide. Bootstrap has .visually-hidden, .text-end,
   .text-nowrap, .d-none and the whole spacing scale — reach for those first.
   ------------------------------------------------------------------------------------------- */

/* The page heading and its count. A <h1> with a quiet number beside it, on every list screen. */
.page-title {
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
    flex-wrap: wrap;
    margin-bottom: var(--space-5);
}

.page-title h1 {
    font-size: 1.5rem;
    font-weight: 700;
    margin: 0;
}

.page-title__meta {
    color: var(--color-muted);
    font-size: 0.875rem;
}

/* Switches that choose WHICH VERSION of this screen is being read — "Mi agenda | La división", and
   from 07c "Lista | Calendario". They belong to the page rather than to the swappable region
   inside it: their state cannot change as a result of a swap, and a control that re-renders for no
   reason is a control that eventually re-renders wrong. Pushed to the right so the heading and the
   count keep reading as one phrase. */
.page-title__switches {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-left: auto;
}

/* The header of a RECORD screen — a client, later a movement — as opposed to .page-title, which
   heads the list screens.

   One thin bar carries the path, the identity and every page-level action; the name goes under it.
   The breadcrumb used to own a whole row and spend it on the word "Clientes" and a number, with the
   entire width to its right empty. Now that row also answers "which client am I looking at" for
   somebody who has scrolled back up, and holds the buttons.

   THE RULE THE ACTIONS FOLLOW, because it is the thing that keeps getting asked: an action that
   applies to the WHOLE RECORD goes in .page-head__actions and nowhere else. An action that applies
   to ONE BLOCK — change this date, manage these contacts — goes in that block's own card header,
   beside its heading. A button floating next to the <h1> belongs to neither and is what this
   component replaced. */
.page-head {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin-bottom: var(--space-4);
}

.page-head__bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-3);
    min-height: 2rem;
}

/* The breadcrumb is a block element with its own bottom margin, and it is in a flex row now. */
.page-head__bar .breadcrumb {
    --bs-breadcrumb-margin-bottom: 0;
}

.page-head__meta {
    color: var(--color-muted);
    font-size: 0.875rem;
}

.page-head__actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-left: auto;
}

.page-head__title {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--space-2);
}

/* The badges beside a record's name, grouped so htmx can swap them as one out-of-band region
   (task 13). They were direct children of .page-head__title and its gap spaced them; wrapped, they
   became one child. Same token, so nothing moved on screen. */
.page-head__badges {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.page-head__title h1 {
    font-size: 1.375rem;
    font-weight: 700;
    margin: 0;
}

/* A record's stored fields, read rather than edited.

   The <dl class="row"> this replaces spent a third of the card on a label gutter and a whole row on
   each field, so six fields filled a card and the other ten had nowhere to go. Labels sit above
   their values in as many columns as the card can fit, which puts the same six in two rows and
   leaves room for every field the record actually has. */
.data-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
    gap: var(--space-3) var(--space-4);
    margin: 0;
}

/* Each field is one <div> wrapping its <dt> and <dd>, so the pair travels together as a grid item.
   min-width: 0 lets a long value wrap instead of stretching its column. */
.data-grid > div {
    min-width: 0;
}

.data-grid dt {
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-muted);
}

.data-grid dd {
    margin: 0;
    overflow-wrap: anywhere;
}

/* For values that are wider than a column — an address, three telephone numbers in one field.
   Guarded by the breakpoint: on a narrow card the grid is one column wide, and spanning two would
   conjure an implicit second column and push half the card off the side. */
@media (min-width: 48rem) {
    .data-grid__wide {
        grid-column: span 2;
    }
}

/* THE LETTER, EXACTLY AS IT WILL BE SENT.

   Its whole job is `white-space: pre-wrap`, and that is not a detail: the body of a form letter is
   blank lines between paragraphs, and every other block in this application collapses them. Shown
   without it, a preview reads as one wall of text and the operator has no way to tell whether the
   letter is laid out or not until it is in the customer's inbox.

   Body type and not monospace — a customer reads this in a mail client, in a proportional face, and
   a preview in Consolas would misrepresent both the line breaks and the length. The sunk surface is
   what says "this is quoted content and not the page talking". */
.carta-preview {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    font-family: var(--font-body);
    font-size: 0.9375rem;
    line-height: 1.55;
    margin: 0;
    padding: var(--space-4);
    background: var(--color-surface-sunk);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
}

/* THE SAME LETTER WHEN IT IS HTML (task 13f/02).

   Deliberately the same frame as .carta-preview above — same border, same radius, same sunk
   surface — so the two read as one idea shown two ways rather than as two components.

   ⚠ THE HEIGHT IS FIXED BECAUSE AN IFRAME CANNOT SIZE ITSELF TO ITS CONTENT WITHOUT SCRIPT, and
   the frame this rule dresses is sandboxed precisely so that nothing in it may run. That is not a
   limitation to work around later: it is the security decision, and `overflow: auto` is the price.
   The letters this shows are one screen of A4, so scrolling is rare.

   White and not the sunk surface behind the frame: the document in there brings its own background
   and is what a customer sees in their mail client. Tinting behind it would misrepresent it — which
   is why this is --color-surface and .carta-preview above is --color-surface-sunk. */
.carta-preview-html {
    display: block;
    width: 100%;
    height: 30rem;
    overflow: auto;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
}

.muted {
    color: var(--color-muted);
}

.print-only {
    display: none;
}


/* ---- SEGUIMIENTO --------------------------------------------------------------------------
   The four follow-up screens (task 08). Almost everything here is Bootstrap as shipped — the
   table, the card, the pagination, .edit-zone for the bulk bar and .toggle-link for select-all.
   What is left is three marks a row can carry, and each one is a fact somebody has to see before
   they tick the checkbox rather than after they press the button.
   ------------------------------------------------------------------------------------------- */

/* The checkbox column. Sized to the control so a razón social gets the width instead, and centred
   so forty of them read as a column rather than as a ragged edge. */
.inbox__pick {
    width: 2.5rem;
    text-align: center;
}

/* The razón social, capped so that one long name cannot rearrange the columns beside it.

   The imported data averages 29 characters and runs to 96, and a table laid out automatically gives
   the widest cell whatever it asks for: a single 96-character name takes the width away from the
   two date columns until «hace 14 meses» breaks over three lines. The customer reported that as the
   format being lost on the follow-up screens.

   Flex, and that is the working part rather than a tidiness: .text-truncate sets overflow:hidden,
   which is precisely what lets a flex item shrink below its own content width, so the name yields
   and the Prospecto badge and the no-e-mail mark beside it never do. All three stay on one line.

   ⚠ The print partial does not use this class and must not: paper has no hover, so an ellipsis
   there loses the name for good. On screen the anchor carries the whole name in its title. */
.inbox__nombre {
    display: flex;
    align-items: center;
    gap: var(--space-1);

    /* 28rem ≈ 60 characters, and the value was chosen by looking rather than by arithmetic. At
       22rem the fix worked and cut too eagerly: on a 1600 px screen the column had room to spare
       and «TERMINAL PORTUARIA MALECÓN S. DE R.L. DE C.V.» was still losing its tail. What the cap
       has to stop is the 96-character outlier dictating the column, not the 45-character ordinary
       one — the imported book averages 29. */
    max-width: 28rem;
}

/* A client with no e-mail address anywhere in its contacts.

   Quiet on purpose: it is not an error, and it is not this screen's business — assigning a phone
   call to somebody works perfectly well without one. It is here because task 09 sends templates
   over this same selection, and a bulk send that silently drops a fifth of its targets is the
   failure this row is warning about. Colour is not the only carrier: the icon has a title and a
   visually-hidden label beside it. */
.inbox__sincorreo {
    color: var(--color-warn);
}

/* Somebody already owes this client a call. The row is not selectable and this says why — a
   checkbox missing with no explanation reads as a broken screen, and "por qué no puedo marcar
   éste" is the first question anybody asks. */
.inbox__asignado {
    display: inline-flex;
    align-items: baseline;
    gap: var(--space-1);
    color: var(--color-ok);
}


/* ---- REPORTS ------------------------------------------------------------------------------
   Task 10. A report is not a page of its own: it is a screen asked for with ?vista=impresion, so
   what is styled here is the head every printed sheet carries and the density the rows drop to.
   ------------------------------------------------------------------------------------------- */

/* Two columns as of task 13c/06 — the mark, then the block of three paragraphs that was the whole
   of this before. The border-bottom and the padding-bottom are UNCHANGED; the only thing that moved
   is that the text is now a flex item instead of the head's direct children.

   align-items: flex-start so the mark sits level with the company line rather than floating in the
   vertical middle of a head whose height changes with the filter text. */
.report-head {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    border-bottom: 2px solid var(--brand-navy);
    padding-bottom: var(--space-3);
    margin-bottom: var(--space-5);
}

/* Height only, so the natural 181x192 proportion is exact. flex:none so a long report title cannot
   squeeze the mark, and min-width:0 on the text column so a ninety-character razón social wraps
   instead of pushing the mark off the sheet. */
.report-head__marca {
    flex: none;
    width: auto;
    height: 2.5rem;
}

.report-head__texto {
    min-width: 0;
    flex: 1;
}

.report-head__empresa {
    margin: 0;
    font-family: var(--font-display);
    font-size: 0.8125rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--brand-navy);
}

.report-head__titulo {
    margin: var(--space-1) 0 0;
    font-size: 1.375rem;
}

.report-head__meta {
    margin: var(--space-1) 0 0;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    font-size: 0.875rem;
    color: var(--color-muted);
}

/* The sheet is short of rows and says so. On paper as well as on screen — that is the whole
   point of it, and it is why this is not an .alert, which the print rules would have to keep
   a coloured background for. */
.report-head__falta {
    margin: var(--space-3) 0 0;
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--color-danger-border);
    border-left: 4px solid var(--color-danger);
    background: var(--color-danger-bg);
    color: var(--color-danger-dark);
    font-size: 0.875rem;
}

/* A card handed to a customer: label above value, and never a two-column grid so narrow that a
   razón social of ninety characters has to wrap four times. */
.report-card__seccion {
    margin-top: var(--space-5);
}

.report-card__seccion > h2 {
    font-size: 1rem;
    border-bottom: 1px solid var(--color-border-strong);
    padding-bottom: var(--space-1);
    margin-bottom: var(--space-3);
}

/* One movement of the printed expediente. The rule is what makes a page of them readable; the
   break-inside below is what keeps one from being cut in half by the page edge. */
.report-mov {
    border-bottom: 1px solid var(--color-border);
    padding: var(--space-2) 0;
}

.report-mov__cab {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    font-size: 0.875rem;
    color: var(--color-muted);
}

.report-mov__texto {
    margin: var(--space-1) 0 0;
    white-space: pre-wrap;
}


/* ---- PRINT --------------------------------------------------------------------------------
   Task 03b laid this ground so task 10 would build reports rather than re-litigate margins.
   Task 10 added the page size, the widow rules and the report head. Bootstrap ships no print
   rules worth relying on.
   ------------------------------------------------------------------------------------------- */

/* THE TWO PRINT DISCLOSURES (task 13b/04).

   Printing is one click now: the button opens a window that prints itself and closes, so the
   .no-print bar that used to carry the "desmarcar Encabezados y pies de página" advice is never
   seen. The advice has to be on the screen the reader is already on — and a paragraph of prose
   beside a button somebody presses every week is clutter for ever, for a lesson that is learnt once
   and then remembered by the browser. Both of these are <details>: they open with no script at all,
   which is the same rule every other control here follows. */

/* The "?" beside a print button. Its summary is one icon; the panel drops out of the flow so a
   toolbar row does not change height when it opens. */
.print-advice {
    position: relative;
}

.print-advice > summary {
    list-style: none;
    cursor: help;
    color: var(--color-muted);
    line-height: 1;
}

.print-advice > summary::-webkit-details-marker {
    display: none;
}

.print-advice > summary:hover,
.print-advice[open] > summary {
    color: var(--bs-primary);
}

.print-advice > p {
    position: absolute;
    z-index: 5;
    top: calc(100% + var(--space-2));
    left: 0;
    width: max-content;
    max-width: min(28rem, 80vw);
    padding: var(--space-3);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

/* The control card's block chooser, opened from the page header. Same shape, wider, and anchored to
   the right because it hangs off the last button in .page-head__actions. */
.print-options {
    position: relative;
}

.print-options > summary {
    list-style: none;
}

.print-options > summary::-webkit-details-marker {
    display: none;
}

.print-options > form {
    position: absolute;
    z-index: 5;
    top: calc(100% + var(--space-2));
    right: 0;
    width: max-content;
    max-width: min(44rem, 90vw);
    box-shadow: var(--shadow);
}

@media print {
    @page {
        /* Carta is the paper this office buys. A hint, not a guarantee — the print dialog wins,
           and it should. */
        size: letter;
        margin: 1.5cm;
    }

    body {
        background: white;
        color: black;
        font-size: 11pt;
    }

    .app-header,
    .app-footer,
    .skip-link,
    .pagination,
    .btn,
    .no-print {
        display: none !important;
    }

    .app-main {
        max-width: none;
        padding: 0;
    }

    .print-only {
        display: block;
    }

    .card,
    .table {
        box-shadow: none;
    }

    /* A table that breaks across pages must repeat its header, or page two is a grid of
       unlabelled numbers. */
    thead {
        display: table-header-group;
    }

    tr,
    img,
    .agenda-cal__dia,
    .report-head,
    .report-mov {
        page-break-inside: avoid;
    }

    /* A section title alone at the foot of a page, with its table starting overleaf, reads as an
       empty section. Task 10: the control card has four of them. */
    h1,
    h2,
    h3,
    .report-head__titulo {
        page-break-after: avoid;
    }

    /* The head of a printed sheet keeps its rule — it is the line that separates "which report,
       which division, which filters" from the data, and without it the two run together. */
    .report-head {
        border-bottom: 2px solid black;
    }

    /* The one block whose colour has to survive the printer. It says the sheet is incomplete, and
       a grey line saying so is a line nobody reads. */
    .report-head__falta {
        border: 1px solid black;
        border-left: 4px solid black;
        background: white;
        color: black;
        font-weight: 600;
    }

    /* The grid keeps its rules on paper. Everything else in this file drops its borders when
       printed, but a calendar without them is forty-two numbers in a field. */
    .agenda-cal__dow {
        background: white;
        color: black;
        border-bottom: 1px solid black;
    }

    /* The URL behind a link is worthless on paper — every one of them is an intranet address
       nobody can type — so links print as plain text. */
    a {
        color: black;
        text-decoration: none;
    }
}

@media print {
    /* Both are .no-print, but they are absolutely positioned and one left open would land on top of
       the sheet rather than simply above it. */
    .print-advice,
    .print-options {
        display: none;
    }
}
/* ============================================================================================
   CTRL+P DE QUIEN NO PUEDE IMPRIMIR (13d/01)
   ============================================================================================ */

@media print {
    /* ⚠ A DETERRENT, NOT THE CONTROL, and the difference is worth being blunt about. The control is
       the server refusing ?vista=impresion — that is what a hand-typed URL, a bookmark and a
       forwarded link run into. This rule only governs what comes out of Ctrl+P on an ordinary
       screen, and nothing in CSS can stop a screenshot, a phone camera, or a browser built next
       year. What it removes is the FORMATTED SHEET: the whole control card, the whole directory,
       the whole overdue list. That is the difference between photographing twenty rows and walking
       out with 787 records.

       data-impresion is stamped on <body> by _Layout.cshtml from PrintPermission. "libre" for an
       administrator and for an anonymous request; "reservada" for everybody else. */
    body[data-impresion="reservada"] > * {
        display: none !important;
    }

    /* NOT A BLANK PAGE. Somebody who presses Ctrl+P and gets an empty sheet concludes the
       application is broken and asks somebody to fix it; somebody who gets one line learns the rule
       once. This project writes error messages that explain, and a blank sheet is the opposite of
       that. It is ::after on <body> rather than an element in the markup so that it exists nowhere
       on screen and cannot be styled, hidden or read by anything else. */
    body[data-impresion="reservada"]::after {
        content: "La impresión está reservada al administrador del sistema. Si necesita esta información en papel, pídasela a él.";
        display: block;
        font-size: 12pt;
        line-height: 1.5;
    }
}
