/* web_clientes portal -- "Catalogo" theme (concept_09).
   ------------------------------------------------------------------
   Mobile-first, read-only. Editorial ink surface (#0C0F0B) with brass
   (action + identity) accents, cream text, self-hosted Termina /
   Montserrat. Catalogo design language: white pill primary actions,
   glass ghost buttons, outlined display headlines, fogged hero photo.

   Glassmorphism / backdrop-blur is ALLOWED here where the Catalogo
   concept uses it (ghost .pbtn-style buttons) -- the old Sala Verde
   "no blur" rule does not apply to this theme.

   File roles (loaded in this order from base.html):
     portal.css        -- tokens + fonts + grain + topbar + auth +
                          dashboard + errors + alerts + lightbox
     portal-pages.css  -- breadcrumb + project head + KPI + REF
                          accordion + unit cards + timeline + photos
                          (consumes the tokens defined here; rewritten
                          in parallel against this exact token spec)

   Mobile-first structure: every unprefixed rule targets phones.
   Enhancements layer upward ONLY via min-width:576 / 768 / 992.
   The class/selector contract is preserved verbatim; only the VALUES
   of the design tokens change, plus a handful of NEW tokens are added
   (--ink/--green/--cream/--gray) -- no existing token is renamed or
   removed.

   Heavy photos (test_kitchen.png) are referenced ONLY inside
   min-width:768px media queries so phones never download them.
   ------------------------------------------------------------------ */

/* ============================================================ Fonts
   Self-hosted, paths relative to THIS css file. font-display:swap so
   text paints immediately in the fallback and re-flows when the face
   arrives. Display = Termina (700, uppercase headings + big numbers);
   body = Montserrat (200..700). */
@font-face{font-family:'Termina';src:url('../fonts/Termina-Bold.ttf') format('truetype');font-weight:700;font-style:normal;font-display:swap}
@font-face{font-family:'Montserrat';src:url('../fonts/Montserrat-ExtraLight.ttf') format('truetype');font-weight:200;font-style:normal;font-display:swap}
@font-face{font-family:'Montserrat';src:url('../fonts/Montserrat-Light.ttf') format('truetype');font-weight:300;font-style:normal;font-display:swap}
@font-face{font-family:'Montserrat';src:url('../fonts/Montserrat-Regular.ttf') format('truetype');font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:'Montserrat';src:url('../fonts/Montserrat-Medium.ttf') format('truetype');font-weight:500;font-style:normal;font-display:swap}
@font-face{font-family:'Montserrat';src:url('../fonts/Montserrat-SemiBold.ttf') format('truetype');font-weight:600;font-style:normal;font-display:swap}
@font-face{font-family:'Montserrat';src:url('../fonts/Montserrat-Bold.ttf') format('truetype');font-weight:700;font-style:normal;font-display:swap}

/* ============================================================ Tokens
   Every custom-property name the templates / other stylesheet consume
   is preserved; the values are remapped to the Catalogo palette. The
   shared token spec is authoritative -- portal-pages.css is rewritten
   against this same set, so values here must not drift from it. */
:root{
    /* Catalogo NEW base hues. --ink is the page surface; --green is the
       primary-button hover accent; --cream feeds --text below; --gray is
       the outlined-headline stroke neutral. */
    --ink:#0C0F0B;
    --green:#1A3326;
    --cream:#F4F1EA;
    --gray:#D8D8D8;

    /* Legacy token names from the old theme, kept as aliases so any stale
       consumer still resolves to brass. Only --primary-dark is read
       directly today (brass progress-bar gradient end-stop). */
    --primary:var(--brass); --primary-dark:#9A774A; --primary-soft:var(--brass-lt);
    --accent:var(--brass-lt);

    /* Status semantics: success/instalado, warning/materia-prima,
       info/producción (brass), danger/blocked. No blue, no neon.
       Unchanged from the previous theme -- these hues already read AA
       on the (now darker) Catalogo surfaces. */
    --success:#8FBF9B; --warning:#D9B380; --info:#C9A874; --danger:#C26D5A;

    /* Text on the dark panels. */
    /* --text-muted at 0.60 (not lower): on the lightest muted-text
       surface (--card-hi #1E2417) this clears 4.5:1 (measured 5.95:1);
       the Catalogo surfaces are DARKER than Sala Verde's, so the ratio
       only improves vs the 4.78:1 previously measured on the lighter
       Sala Verde card-2. */
    --text:var(--cream); --text-soft:rgba(244,241,234,0.72); --text-muted:rgba(244,241,234,0.60);

    /* Surfaces. Catalogo ink stack: near-black page, dark editorial
       cards, brass hairline borders. */
    --bg:#0C0F0B; --bg-deep:#080A08; --bg-edge:#050706;
    --card:#12160E; --card-2:#181D12; --card-hi:#1E2417; --card-lo:#0E120B;
    /* Catalogo signature card surface (the concept's .cotiza-card 165deg
       recipe), tokenized so every card consumes ONE gradient and a hue
       retune happens in exactly one place. */
    --gradient-card:linear-gradient(165deg, var(--card-2) 0%, var(--card) 55%, var(--card-lo) 100%);
    --border:rgba(176,141,87,0.24);
    --border-soft:rgba(176,141,87,0.12);
    /* Brass chip-pill fill (badge/counter backgrounds) -- the 0.18-alpha
       sibling of --border / --border-soft. */
    --brass-fill:rgba(176,141,87,0.18);
    --ring:rgba(244,241,234,0.60);

    /* Brass detail shades. */
    --brass:#B08D57; --brass-lt:#C9A874; --brass-hi:#E8D5AE;
    --night:#060B07;

    /* Typography stacks. */
    --font-body:'Montserrat',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;
    --font-display:'Termina','Montserrat',system-ui,-apple-system,sans-serif;

    /* WCAG AA badge text tokens, kept at the AA-tuned values. Each is a
       light tint of its status hue so on-pill text clears 4.5:1 against
       the dark-tinted pill background; on the darker Catalogo surfaces
       the contrast only widens. */
    --badge-materia-prima-text:#E0BD8F;
    --badge-completion-text:#E3C893;
    --badge-installation-text:#A8CFB2;
    --badge-blocked-text:#EBA897;
    /* Alert-success text: brighter than --badge-installation-text on
       purpose -- flash alerts sit on a deeper tinted panel than badges. */
    --alert-success-text:#BFE0C7;

    /* Surface / overlay / thumb / lightbox tokens (used by
       portal-pages.css and the lightbox). Retuned to the ink surfaces. */
    --surface-muted:#0A0D09;
    --overlay-dark:rgba(4,6,4,0.80);
    --lightbox-bg:#060806;
    --lightbox-nav-bg:rgba(8,10,8,0.62);
    /* Hover fill bright enough that the --night glyph clears 4.5:1 when
       composited over --lightbox-bg. The previous theme needed 0.92
       brass to clear AA over a deeper background; on this near-black
       lightbox 0.92 keeps the same margin, so it is kept. */
    --lightbox-nav-hover:rgba(201,168,116,0.92);
    --lightbox-focus-ring:#E8D5AE;
    --lightbox-border:rgba(176,141,87,0.20);

    /* Catalogo radii: softer, larger. */
    --radius-sm:8px;
    --radius-md:14px;
    --radius-lg:18px;

    /* Catalogo plush shadow stack -- wide soft outer drop + inner top
       highlight, matching the concept's .cotiza-card depth. */
    --shadow-card:0 20px 55px rgba(2,5,3,0.55), 0 5px 16px rgba(2,5,3,0.40), inset 0 1px 0 rgba(255,255,255,0.06);
    --shadow-card-hover:0 38px 95px rgba(2,5,3,0.68), 0 12px 32px rgba(2,5,3,0.50), inset 0 1px 0 rgba(255,255,255,0.08);

    /* Bootstrap 5.3 theming -- set the framework's own variables so the
       dark theme is adopted at the source instead of fought with
       specificity. Values reference the Catalogo tokens above so a hue
       retune happens in exactly one place. (Accordion chevron data-URIs
       live in portal-pages.css with the accordion rules.) */
    --bs-body-bg:var(--bg);
    --bs-body-color:var(--text);
    --bs-border-color:var(--border);
    --bs-secondary-color:var(--text-muted);
    --bs-secondary-bg:var(--card);
    --bs-tertiary-bg:var(--card-2);
    --bs-emphasis-color:var(--text);
    --bs-heading-color:var(--text);
    --bs-link-color:var(--brass-lt);
    --bs-link-hover-color:var(--brass-hi);
}

*{box-sizing:border-box}
html{ -webkit-text-size-adjust:100%; text-size-adjust:100%; }
body{
    font-family:var(--font-body);
    font-weight:300;
    color:var(--text);
    margin:0;
    line-height:1.6;
    /* Page surface: Catalogo editorial atmosphere -- a high-right warm
       glow over near-black ink, deepened toward the edges. The pinned
       (background-attachment:fixed) variant lives in the >=992px tier
       below: iOS Safari ignores fixed-attachment on body and full-
       viewport fixed gradients cause scroll repaint jank on phones. */
    background-color:var(--bg);
    background-image:
        radial-gradient(85% 95% at 72% 12%, #121710 0%, rgba(18,23,16,0) 62%),
        radial-gradient(120% 90% at 0% 0%, rgba(8,10,8,0) 0%, var(--bg-edge) 92%),
        radial-gradient(120% 90% at 100% 100%, rgba(8,10,8,0) 0%, var(--bg-edge) 94%),
        radial-gradient(100% 80% at 50% 50%, var(--bg) 0%, var(--bg-deep) 70%, var(--bg-edge) 100%);
    -webkit-font-smoothing:antialiased;
    -moz-osx-font-smoothing:grayscale;
    text-rendering:optimizeLegibility;
    min-height:100vh;
}
@media (min-width:992px){
    body{ background-attachment:fixed; }
}
h1, h2, h3, h4, .display-font{
    font-family:var(--font-display);
    font-weight:700;
    line-height:0.96;
    letter-spacing:-0.01em;
    text-transform:uppercase;
    color:var(--text);
}
a{ color:var(--brass-lt); }
a:hover{ color:var(--brass-hi); }
/* Catalogo focus ring: crisp white outline, 3px offset. */
:focus-visible{ outline:2px solid #fff; outline-offset:3px; border-radius:4px; }
.visually-hidden{
    position:absolute !important; width:1px !important; height:1px !important;
    padding:0 !important; margin:-1px !important; overflow:hidden !important;
    clip:rect(0,0,0,0) !important; white-space:nowrap !important; border:0 !important;
}

/* ============================================================ Film grain
   Fixed full-bleed fractalNoise texture, Catalogo recipe (opacity .055,
   mix-blend-mode:overlay, 180x180 turbulence tile). z-index:90 -- ABOVE
   page chrome but BELOW Bootstrap's backdrop (1050) and modal (1055) so
   the lightbox stays crisp. pointer-events:none so it never eats clicks. */
.grain{
    position:fixed; inset:0; z-index:90; pointer-events:none; opacity:0.055;
    mix-blend-mode:overlay;
    background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180' viewBox='0 0 180 180'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/></filter><rect width='180' height='180' filter='url(%23g)' opacity='0.6'/></svg>");
}

/* ============================================================ Topbar
   Condensed on phones: logo image + Salir. Email hidden below 576px.
   Catalogo floating-nav feel (gradient fade) layered OVER a near-solid
   ink backing + brass-ink hairline, so a fixed/sticky app header stays
   legible over scrolled content (the marketing page used a pure
   transparent fade; an app header must keep a solid backing). */
.topbar{
    background:
        linear-gradient(to bottom, rgba(12,15,11,0.62), rgba(12,15,11,0)),
        linear-gradient(180deg, rgba(8,10,8,0.97), rgba(8,10,8,0.90));
    border-bottom:1px solid #1e231b;
    box-shadow:0 18px 40px -24px rgba(0,0,0,0.85), inset 0 -1px 0 rgba(176,141,87,0.07);
    padding:14px 0; position:sticky; top:0; z-index:100;
}
.topbar .container{
    display:flex; align-items:center; justify-content:space-between; gap:14px;
}
.brand{
    display:flex; align-items:center; gap:12px;
    text-decoration:none; color:var(--text); min-width:0;
}
/* Brass identity tile -- kept (harmless) for any template still emitting
   .brand-mark; the logo image is now the primary brand presence. */
.brand-mark{
    width:34px; height:34px; border-radius:9px;
    background:rgba(8,10,8,0.6); border:1px solid var(--border);
    color:var(--brass-lt); display:flex; align-items:center; justify-content:center;
    font-size:17px; flex:none;
}
/* New: logo <img> in topbar + auth brand rows. */
.brand-logo{ height:24px; width:auto; display:block; }
@media(min-width:768px){ .brand-logo{ height:28px; } }
/* New: small uppercase tracked label next to the logo. Catalogo .tag
   treatment: 9px, very wide tracking, near-white. */
.brand-sub{
    font-size:9px; letter-spacing:0.32em; text-transform:uppercase;
    color:rgba(255,255,255,0.7); font-weight:400; line-height:1.3;
}
.user-box{ display:flex; align-items:center; gap:14px; min-width:0; }
.user-email{
    font-size:12.5px; color:var(--text-soft); display:none;
    letter-spacing:0.02em; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
@media(min-width:576px){ .user-email{ display:inline; } }
/* Salir = Catalogo ghost pill (the concept's .pbtn glassmorphism):
   translucent white glass, blurred backdrop, white hairline, white
   uppercase tracked text; on hover it flips to a solid white pill with
   ink text. >=44px touch target. */
.btn-logout{
    background:rgba(255,255,255,0.08);
    -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px);
    border:1px solid rgba(255,255,255,0.4);
    color:#fff; padding:11px 18px; font-size:11px; font-weight:600;
    letter-spacing:0.14em; text-transform:uppercase;
    border-radius:30px; display:inline-flex; align-items:center; gap:8px;
    text-decoration:none; min-height:44px; cursor:pointer;
    transition:background .2s ease, color .2s ease, border-color .2s ease, transform .12s ease;
}
.btn-logout:hover{ background:#fff; color:var(--ink); border-color:#fff; }
.btn-logout:active{ transform:translateY(1px); }

/* ============================================================ Auth (login/verify)
   Mobile-first: form card only, stacked, full-viewport ink. The hero
   panel + its full-bleed kitchen photo appear ONLY from 768px (the
   image is declared exclusively inside that media query so phones never
   download it). */
.auth-shell{
    /* Escape <main class="container"> max-width so the dark surface
       covers the full viewport. */
    margin-left:calc(50% - 50vw);
    margin-right:calc(50% - 50vw);
    width:100vw;
    min-height:100vh;
    display:flex; align-items:center; justify-content:center;
    padding:32px 16px 72px;
    background-color:var(--bg);
    background-image:
        radial-gradient(85% 95% at 72% 12%, #121710 0%, rgba(18,23,16,0) 60%),
        radial-gradient(120% 100% at 50% 50%, var(--bg) 0%, var(--bg-deep) 68%, var(--bg-edge) 100%);
    position:relative;
    overflow:hidden;
}
.auth-shell::before{
    /* Subtle watermark ornament -- icon_white at very low opacity, fixed
       to the shell. pointer-events:none, decorative only. (icon_white is
       a tiny mark, fine to ship to phones -- the heavy photos are not.) */
    content:'';
    position:absolute; inset:0;
    background-image:url('../img/icon_white.png');
    background-repeat:no-repeat;
    background-position:right -70px bottom -70px;
    background-size:300px auto;
    opacity:0.05;
    pointer-events:none;
    z-index:0;
}
.auth-shell > *{ position:relative; z-index:1; }

/* Two-column layout: hero panel + form card. Mobile stacked (1 col),
   side-by-side from 992px. */
.auth-grid{
    width:100%;
    max-width:1120px;
    display:grid;
    grid-template-columns:1fr;
    gap:24px;
    align-items:center;
}
@media(min-width:992px){
    .auth-grid{
        grid-template-columns:1.1fr 0.9fr;
        gap:56px;
        /* stretch (not center): hero and card share the row height, so
           the form card always matches the hero panel. */
        align-items:stretch;
    }
    /* The stretched card is taller than its content -- lay it out as a
       flex column and center the form vertically inside. */
    .auth-card{
        display:flex; flex-direction:column; justify-content:center;
    }
}

/* ---- Hero panel: hidden on phones (form first), visible from 768px ---- */
.auth-hero{
    color:var(--text);
    padding:8px 4px 0;
    display:none;
}
@media(min-width:768px){
    /* Catalogo hero treatment: a full-bleed test_kitchen.png cover photo
       behind layered fog so the text side stays AA-readable. The ink
       fallback color guarantees contrast even before the image loads.
       This block is the ONLY place that references test_kitchen.png, so
       phones never request it. */
    .auth-hero{
        display:block;
        position:relative;
        padding:48px 40px;
        border-radius:var(--radius-lg);
        border:1px solid var(--border);
        overflow:hidden;
        background-color:var(--night);
        background-image:
            linear-gradient(to right, rgba(6,11,7,0.78), rgba(6,11,7,0.28) 30%, rgba(6,11,7,0) 56%),
            linear-gradient(to top, rgba(6,11,7,0.72), rgba(6,11,7,0) 36%),
            radial-gradient(125% 95% at 76% 28%, rgba(6,11,7,0) 42%, rgba(6,11,7,0.5) 100%),
            /* uniform 50% night veil: composites the photo at half opacity
               over the --night base so hero text stays readable. */
            linear-gradient(rgba(6,11,7,0.5), rgba(6,11,7,0.5)),
            url('../img/test_kitchen.png');
        background-size:cover, cover, cover, cover, cover;
        background-position:center, center, center, center, center;
        box-shadow:var(--shadow-card), inset 0 0 0 1px var(--brass-fill);
    }
    /* Catalogo brass edge-light: a soft radial brass glow on the left of
       the hero (the concept's .hero-img::before accent), reading as
       light grazing the photographed cabinetry. Decorative only. */
    .auth-hero::after{
        content:'';
        position:absolute; left:0; top:6%; bottom:6%; width:200px;
        background:radial-gradient(70% 50% at 0% 50%, rgba(176,141,87,0.26), rgba(176,141,87,0) 72%);
        pointer-events:none;
        z-index:0;
    }
    /* Keep the hero's own content above the brass glow layer. */
    .auth-hero > *{ position:relative; z-index:1; }
}
.auth-hero .hero-brand{
    display:flex; align-items:center; gap:12px;
    margin-bottom:28px;
}
.auth-hero .hero-brand .brand-mark{
    width:44px; height:44px; font-size:20px;
}
.auth-hero .hero-brand .brand-logo{ height:32px; }
.auth-hero .hero-brand .brand-sub{ color:rgba(255,255,255,0.7); font-size:9px; }

/* Catalogo pill: white hairline, large radius, translucent ink fill,
   small uppercase tracked label. */
.hero-pill{
    display:inline-flex; align-items:center; gap:8px;
    background:rgba(12,15,11,0.28); border:1px solid rgba(255,255,255,0.4);
    color:#fff; font-size:11px; font-weight:500;
    letter-spacing:0.1em; text-transform:uppercase;
    padding:8px 15px; border-radius:24px;
    margin-bottom:22px;
}
.hero-pill i{ font-size:13px; color:var(--brass-lt); }

.hero-h1{
    font-family:var(--font-display);
    font-size:clamp(28px, 4.4vw, 44px);
    font-weight:700;
    line-height:0.98;
    letter-spacing:-0.01em;
    text-transform:uppercase;
    color:var(--text);
    margin:0 0 20px;
}
/* Catalogo outlined-text accent: solid brass-hi fallback for engines
   without -webkit-text-stroke; the @supports block swaps to a
   transparent fill with a --gray stroke (the concept's .out treatment). */
.hero-h1 .accent{ color:var(--brass-hi); }
@supports ((-webkit-text-stroke:1px #fff)){
    .hero-h1 .accent{
        color:transparent;
        -webkit-text-stroke:1.4px var(--gray);
    }
}
.hero-lead{
    font-size:15px;
    line-height:1.65;
    color:var(--text-soft);
    margin:0 0 30px;
    max-width:520px;
    font-weight:300;
}
.hero-features{
    list-style:none; padding:0; margin:0;
    display:flex; flex-direction:column; gap:16px;
}
.hero-features li{
    display:flex; align-items:flex-start; gap:14px;
    color:var(--text-soft);
    font-size:14px;
    line-height:1.5;
}
.hero-features li i{
    color:var(--brass-lt);
    font-size:17px;
    flex-shrink:0;
    background:rgba(8,10,8,0.55);
    border:1px solid var(--border);
    width:36px; height:36px;
    display:inline-flex; align-items:center; justify-content:center;
    border-radius:10px;
}
.hero-features li strong{ color:var(--text); font-weight:600; display:block; margin-bottom:2px; letter-spacing:0.01em; }
.hero-features li span{ color:var(--text-muted); font-size:13px; }

/* ---- Form card: solid editorial panel (Catalogo .cotiza-card gradient) ---- */
.auth-card{
    width:100%; max-width:440px; margin:0 auto;
    background:var(--gradient-card);
    border:1px solid var(--border);
    border-radius:var(--radius-lg); padding:32px 24px;
    box-shadow:var(--shadow-card);
}
@media(min-width:576px){ .auth-card{ padding:40px 32px; } }
.brand-row{ display:flex; align-items:center; gap:12px; margin-bottom:28px; }
.brand-row .brand-mark{ width:44px; height:44px; font-size:20px; }
.brand-row .brand-logo{ height:30px; }
.brand-row .brand-sub{ font-size:9px; color:rgba(255,255,255,0.7); }
/* The card's brand row exists for PHONES, where the hero (and its brand
   block) is display:none. From 768px the hero is visible, so showing the
   logo + "Portal de clientes" in the card too duplicates the branding --
   hide the card copy whenever the hero carries it. */
@media(min-width:768px){
    .auth-card .brand-row{ display:none; }
}
.auth-card h1{
    font-family:var(--font-display);
    font-size:clamp(20px, 4.6vw, 24px); font-weight:700;
    margin:0 0 8px; letter-spacing:-0.01em; text-transform:uppercase; color:var(--text);
}
.lead-text{ color:var(--text-soft); font-size:14px; margin-bottom:30px; font-weight:300; }
.form-label{
    font-size:11px; font-weight:500; letter-spacing:0.18em; text-transform:uppercase;
    color:var(--brass-lt); margin-bottom:9px; display:block;
}
/* Dark ink well, cream text, brass focus border + soft ring. */
.form-control{
    padding:14px 16px; font-size:15px; border-radius:var(--radius-md);
    background:var(--surface-muted); color:var(--text);
    border:1px solid var(--border-soft); width:100%;
    font-family:var(--font-body); font-weight:300;
    box-shadow:inset 0 2px 6px rgba(0,0,0,0.45);
}
.form-control::placeholder{ color:var(--text-muted); }
.form-control:focus{
    border-color:var(--brass-lt);
    box-shadow:inset 0 2px 6px rgba(0,0,0,0.45), 0 0 0 3px var(--ring);
    outline:none; color:var(--text); background:#0B0E0A;
}
/* Primary action = Catalogo white pill: white fill, ink text, large pill
   radius, small uppercase tracked label; hover flips to green fill with
   white text and lifts 2px. The --bs-btn-* variables are set (not just
   the painted properties) so Bootstrap's own :hover/:active/:disabled
   machinery -- which reads these variables -- can never resurface the
   framework's blue defaults. */
.btn-primary{
    --bs-btn-bg:#fff; --bs-btn-border-color:#fff; --bs-btn-color:var(--ink);
    --bs-btn-hover-bg:var(--green); --bs-btn-hover-border-color:var(--green); --bs-btn-hover-color:#fff;
    --bs-btn-active-bg:var(--green); --bs-btn-active-border-color:var(--green); --bs-btn-active-color:#fff;
    --bs-btn-disabled-bg:#fff; --bs-btn-disabled-border-color:#fff; --bs-btn-disabled-color:var(--ink);
    background:#fff; border:1px solid #fff;
    color:var(--ink);
    padding:16px 30px; font-size:12px; font-weight:600;
    letter-spacing:0.1em; text-transform:uppercase;
    border-radius:30px; min-height:44px;
    transition:background .2s ease, color .2s ease, border-color .2s ease, transform .15s ease;
}
.btn-primary:hover, .btn-primary:focus-visible{
    background:var(--green); border-color:var(--green); color:#fff;
    transform:translateY(-2px);
}
.btn-primary:active{ transform:translateY(0); }
.helper{ font-size:12px; color:var(--text-soft); margin-top:18px; text-align:center; line-height:1.5; }
.helper i{ color:var(--success); margin-right:6px; }
.footer-note{
    position:absolute; left:0; right:0; bottom:16px;
    text-align:center; color:var(--text-muted);
    font-size:11px; letter-spacing:0.18em; text-transform:uppercase;
    font-weight:300;
    z-index:1;
}
/* OTP code field: wide-tracked cream digits on a dark ink well.
   Montserrat (not the Termina display face) on purpose: tabular-numeral
   digit legibility at 28px wins over display styling here. */
.code-input{
    text-align:center; font-family:var(--font-body); font-size:28px; font-weight:600;
    letter-spacing:12px; color:var(--text);
    font-feature-settings:"tnum"; font-variant-numeric:tabular-nums;
}
.resend{
    display:flex; align-items:center; justify-content:center; gap:8px;
    margin-top:20px; font-size:13px; color:var(--text-soft);
}
/* Brass text button -- >=44px touch target. */
.link-btn{
    background:transparent; border:none; color:var(--brass-lt); font-weight:600;
    font-size:13px; display:inline-flex; align-items:center; gap:6px; cursor:pointer;
    padding:10px 14px; min-height:44px; border-radius:8px;
    letter-spacing:0.04em;
}
.link-btn:disabled{ color:var(--text-muted); cursor:not-allowed; opacity:0.7; }
.link-btn:hover:not(:disabled){ background:rgba(201,168,116,0.12); color:var(--brass-hi); }
.cooldown-pill{
    font-feature-settings:"tnum"; font-variant-numeric:tabular-nums;
    background:var(--surface-muted); color:var(--brass-lt);
    border:1px solid var(--border-soft);
    padding:5px 11px; border-radius:999px; font-weight:600;
    font-size:12px;
}
.back-link{ margin-top:30px; text-align:center; font-size:13px; }
.back-link a{ color:var(--brass-lt); text-decoration:none; }
.back-link a:hover{ color:var(--brass-hi); text-decoration:underline; }

/* ============================================================ Dashboard */
.page-head{ padding:36px 0 18px; }
.page-head h1{
    font-family:var(--font-display);
    font-size:clamp(26px, 4.4vw, 34px);
    font-weight:700; margin:0 0 8px; letter-spacing:-0.01em; text-transform:uppercase;
    color:var(--text);
}
.page-head p{ color:var(--text-soft); font-size:14.5px; margin:0; max-width:60ch; font-weight:300; }

/* Dashboard tabs (Bootstrap nav-tabs). Inactive = cream-muted, active =
   brass text + brass underline; transparent backgrounds. Driven via
   Bootstrap's own nav vars to win without specificity fights. */
.nav-tabs{
    --bs-nav-tabs-border-color:var(--border-soft);
    --bs-nav-tabs-link-active-color:var(--brass-lt);
    --bs-nav-tabs-link-active-bg:transparent;
    --bs-nav-tabs-link-active-border-color:transparent;
    --bs-nav-tabs-link-hover-border-color:transparent;
    --bs-nav-link-color:var(--text-muted);
    --bs-nav-link-hover-color:var(--text);
    border-bottom:1px solid var(--border-soft); margin-bottom:24px; gap:4px; flex-wrap:wrap;
}
.nav-tabs .nav-link{
    color:var(--text-muted); font-weight:500; border:none;
    padding:12px 4px; margin-right:24px; font-size:14.5px;
    border-bottom:2px solid transparent; background:transparent;
    min-height:44px;
    transition:color .15s ease, border-color .15s ease;
}
.nav-tabs .nav-link:hover{ color:var(--text); }
.nav-tabs .nav-link.active{
    color:var(--brass-lt); border-bottom-color:var(--brass); background:transparent;
    font-weight:600;
}
/* Mini-project pills: active = brass fill, night-green text. */
.nav-pills{
    --bs-nav-pills-link-active-color:var(--night);
    --bs-nav-pills-link-active-bg:var(--brass);
    --bs-nav-pills-border-radius:8px;
    --bs-nav-link-color:var(--text-soft);
    --bs-nav-link-hover-color:var(--text);
    gap:6px;
}
.nav-pills .nav-link{
    color:var(--text-soft); font-weight:500; min-height:44px; border-radius:8px;
    border:1px solid transparent;
}
.nav-pills .nav-link:hover{ color:var(--text); border-color:var(--border-soft); }
.nav-pills .nav-link.active{ color:var(--night); background:var(--brass); font-weight:600; }

.project-card{
    background:var(--gradient-card);
    border:1px solid var(--border); border-radius:var(--radius-lg);
    padding:24px; height:100%; min-height:44px;
    transition:transform .18s cubic-bezier(.2,.6,.3,1), box-shadow .2s ease, border-color .15s ease;
    display:flex; flex-direction:column; text-decoration:none; color:var(--text);
    position:relative; overflow:hidden;
    box-shadow:var(--shadow-card);
}
.project-card::before{
    /* Brass hairline along the top edge -- a thread of light, always on. */
    content:''; position:absolute; inset:0 18px auto 18px; height:1px;
    background:linear-gradient(90deg, transparent, rgba(232,213,174,0.5), transparent);
}
.project-card:hover{
    transform:translateY(-4px); border-color:rgba(201,168,116,0.4);
    box-shadow:var(--shadow-card-hover);
}
.project-card:focus-visible{
    outline:none; border-color:var(--brass-lt);
    box-shadow:0 0 0 4px var(--ring), var(--shadow-card-hover);
}
.project-card h2{
    font-family:var(--font-display);
    font-size:18px; font-weight:700; margin:0 0 4px; letter-spacing:-0.01em;
    text-transform:uppercase; color:var(--text); line-height:1.0;
}
.cliente{
    font-size:11px; color:var(--text-muted); margin-bottom:20px;
    text-transform:uppercase; letter-spacing:0.12em; font-weight:500;
}
/* Big avance number -- Termina, cream. */
.avance-big{
    font-family:var(--font-display);
    font-size:clamp(40px, 7.5vw, 52px); font-weight:700;
    letter-spacing:0.01em;
    color:var(--text); line-height:1;
    font-feature-settings:"tnum"; font-variant-numeric:tabular-nums;
}
.avance-label{
    font-size:11px; color:var(--brass-lt); margin-top:6px;
    text-transform:uppercase; letter-spacing:0.2em; font-weight:500;
}
/* Progress bar: brass fill on a deep ink inset track, slightly thicker. */
.progress-bar-thin{
    height:9px; background:rgba(4,6,4,0.6); border-radius:999px;
    overflow:hidden; margin-top:16px;
    border:1px solid rgba(0,0,0,0.45);
    box-shadow:inset 0 2px 5px rgba(0,0,0,0.6);
}
.progress-bar-thin > span{
    display:block; height:100%;
    width: var(--bar-width, 0%);
    background:linear-gradient(180deg, var(--brass-hi), var(--brass) 55%, var(--primary-dark));
    border-radius:999px;
    box-shadow:0 0 12px rgba(201,168,116,0.4), inset 0 1px 0 rgba(255,255,255,0.35);
    transition:width .8s cubic-bezier(.2,.6,.3,1);
    position:relative;
}
.chips{ display:flex; gap:8px; margin-top:18px; flex-wrap:wrap; }
.chip{
    font-size:11px; padding:6px 11px; border-radius:999px;
    background:rgba(8,10,8,0.55); color:var(--text-soft); font-weight:500;
    border:1px solid var(--border-soft);
    display:inline-flex; align-items:center; gap:6px;
    font-variant-numeric:tabular-nums; letter-spacing:0.02em;
}
.chip i{ font-size:11px; }
/* Status-keyed chip tints (AA on dark): green=instalado, amber=materia
   prima, blue=producción (re-keyed to brass -- no blue in this theme). */
.chip.green{ background:color-mix(in srgb, var(--success) 14%, transparent); color:var(--success); border-color:color-mix(in srgb, var(--success) 35%, transparent); }
.chip.amber{ background:color-mix(in srgb, var(--warning) 14%, transparent); color:var(--warning); border-color:color-mix(in srgb, var(--warning) 35%, transparent); }
.chip.blue{ background:color-mix(in srgb, var(--brass-lt) 14%, transparent); color:var(--brass-lt); border-color:color-mix(in srgb, var(--brass-lt) 35%, transparent); }
.chip.green i{ color:var(--success); }
.chip.amber i{ color:var(--warning); }
.chip.blue i{ color:var(--brass-lt); }
.card-footer-link{
    margin-top:auto; padding-top:18px;
    border-top:1px solid var(--border-soft);
    display:flex; justify-content:space-between; align-items:center;
    font-size:12px; color:var(--brass-lt); font-weight:600; text-decoration:none;
    letter-spacing:0.14em; text-transform:uppercase;
}
.card-footer-link i{ transition:transform .2s ease; }
.project-card:hover .card-footer-link i{ transform:translateX(3px); }

/* Page-load reveal -- staggered fade-in for project cards. Both the
   animation block and the nth-child delay block target the same
   selector root so a future .row.g-2 etc. can't get delays without the
   animation (or vice-versa). */
@keyframes portal-rise {
    from { opacity:0; transform:translateY(8px); }
    to   { opacity:1; transform:translateY(0); }
}
.row > [class*='col-'] > .project-card{
    animation:portal-rise .45s cubic-bezier(.2,.6,.3,1) both;
}
.row > [class*='col-']:nth-child(1) > .project-card { animation-delay:.02s; }
.row > [class*='col-']:nth-child(2) > .project-card { animation-delay:.06s; }
.row > [class*='col-']:nth-child(3) > .project-card { animation-delay:.10s; }
.row > [class*='col-']:nth-child(4) > .project-card { animation-delay:.14s; }
.row > [class*='col-']:nth-child(5) > .project-card { animation-delay:.18s; }
.row > [class*='col-']:nth-child(6) > .project-card { animation-delay:.22s; }

@media (prefers-reduced-motion: reduce){
    .project-card,
    .project-card .card-footer-link i,
    .progress-bar-thin > span,
    .btn-logout,
    .btn-primary{
        animation:none !important; transition:none !important;
    }
    .project-card:hover{ transform:none !important; }
    .project-card:hover .card-footer-link i{ transform:none !important; }
    .btn-logout:active, .btn-primary:active, .btn-primary:hover{ transform:none !important; }
}

/* ============================================================ Errors / flash alerts */
.error-page{ padding:60px 24px; text-align:center; }
.error-page h1{
    font-family:var(--font-display);
    font-size:clamp(24px, 6vw, 30px); margin-bottom:12px; color:var(--text);
    text-transform:uppercase; letter-spacing:-0.01em;
}
.error-page p{ color:var(--text-soft); }
/* Error-page CTA = same Catalogo white pill as .btn-primary, so a plain
   <a class="error-cta"> or button on an error page matches the primary
   action treatment without depending on Bootstrap's .btn machinery. */
.error-page .error-cta,
.error-page .btn{
    display:inline-flex; align-items:center; gap:8px;
    background:#fff; border:1px solid #fff; color:var(--ink);
    padding:16px 30px; font-size:12px; font-weight:600;
    letter-spacing:0.1em; text-transform:uppercase;
    border-radius:30px; min-height:44px; text-decoration:none; cursor:pointer;
    transition:background .2s ease, color .2s ease, border-color .2s ease, transform .15s ease;
}
.error-page .error-cta:hover,
.error-page .btn:hover{ background:var(--green); border-color:var(--green); color:#fff; transform:translateY(-2px); }

/* Flash alerts: dark ink panel tinted with the status color at low
   alpha, tinted brass hairline border, AA-tuned light tinted text. */
.alert{
    padding:14px 16px; border-radius:var(--radius-md);
    border:1px solid var(--border-soft);
    background:var(--card); color:var(--text);
}
.alert-info{ background:color-mix(in srgb, var(--info) 12%, var(--card)); color:var(--brass-hi); border-color:color-mix(in srgb, var(--info) 40%, transparent); }
.alert-success{ background:color-mix(in srgb, var(--success) 12%, var(--card)); color:var(--alert-success-text); border-color:color-mix(in srgb, var(--success) 40%, transparent); }
.alert-danger{ background:color-mix(in srgb, var(--danger) 14%, var(--card)); color:var(--badge-blocked-text); border-color:color-mix(in srgb, var(--danger) 45%, transparent); }
.alert-warning{ background:color-mix(in srgb, var(--warning) 12%, var(--card)); color:var(--badge-materia-prima-text); border-color:color-mix(in srgb, var(--warning) 40%, transparent); }
/* Bootstrap's btn-close is a dark glyph -- invert it so it shows on dark. */
.alert .btn-close{ filter:invert(1) grayscale(1) brightness(1.8); opacity:0.8; }
.alert .btn-close:hover{ opacity:1; }
.alert .btn-close:focus-visible{ outline:2px solid #fff; outline-offset:2px; opacity:1; }

/* ============================================================ Photo lightbox (unit detail) */
.photo-lightbox .modal-dialog{
    margin: 1rem auto;
    max-width: min(1100px, 95vw);
}
.photo-lightbox .modal-content{
    background:var(--lightbox-bg);
    color:var(--text);
    border:1px solid var(--lightbox-border);
    border-radius:var(--radius-md);
    overflow:hidden;
    box-shadow:0 40px 90px -30px rgba(0,0,0,0.88);
}
.photo-lightbox .modal-header{
    border-bottom:1px solid var(--lightbox-border);
    padding:14px 18px;
}
.photo-lightbox .modal-title{
    font-family:var(--font-display);
    font-size:14px;
    font-weight:700; letter-spacing:0.06em; text-transform:uppercase;
    display:flex;
    align-items:center;
    gap:14px;
    color:var(--text);
    margin:0;
}
.photo-lightbox .lb-phase{
    color:var(--text);
}
.photo-lightbox .lb-counter{
    font-family:var(--font-body);
    font-size:13px;
    color:var(--text-muted);
    font-weight:500; letter-spacing:0.04em; text-transform:none;
    font-feature-settings:"tnum"; font-variant-numeric:tabular-nums;
}
.photo-lightbox .btn-close{
    filter:invert(1) grayscale(1) brightness(1.8);
    opacity:.85;
}
.photo-lightbox .btn-close:hover{ opacity:1; }
.photo-lightbox .btn-close:focus-visible{
    outline:2px solid var(--lightbox-focus-ring);
    outline-offset:2px;
    opacity:1;
}
.photo-lightbox .modal-body{
    position:relative;
    padding:0;
    background:#000;
    display:flex;
    align-items:center;
    justify-content:center;
    min-height:240px;
}
.photo-lightbox .lb-image{
    display:block;
    max-width:100%;
    max-height:85vh;
    width:auto;
    height:auto;
    object-fit:contain;
    margin:0 auto;
}
/* Nav buttons: dark ink disc, brass on hover. 44px touch target.
   The --lightbox-nav-hover fill (0.92 brass) keeps the --night glyph
   above 4.5:1 once composited over the near-black --lightbox-bg. */
.photo-lightbox .lb-nav{
    position:absolute;
    top:50%;
    transform:translateY(-50%);
    width:44px;
    height:44px;
    border-radius:999px;
    border:1px solid var(--lightbox-border);
    background:var(--lightbox-nav-bg);
    color:var(--text);
    font-size:22px;
    display:flex;
    align-items:center;
    justify-content:center;
    cursor:pointer;
    transition:background .15s ease, transform .15s ease, color .15s ease;
    z-index:5;
}
.photo-lightbox .lb-nav:hover{
    background:var(--lightbox-nav-hover);
    color:var(--night);
    transform:translateY(-50%) scale(1.06);
}
.photo-lightbox .lb-nav:focus-visible{
    outline:2px solid var(--lightbox-focus-ring);
    outline-offset:2px;
}
.photo-lightbox .lb-prev{ left:14px; }
.photo-lightbox .lb-next{ right:14px; }
.photo-lightbox .modal-footer{
    border-top:1px solid var(--lightbox-border);
    padding:10px 18px;
    justify-content:flex-start;
    color:var(--text-muted);
    font-size:12px;
    font-feature-settings:"tnum"; font-variant-numeric:tabular-nums;
    min-height:42px;
}
.photo-lightbox .lb-uploaded{ color:var(--text-muted); }
.photo-lightbox .lb-image.lb-loading{
    opacity:0.35;
    transition:opacity .15s ease;
}
@media(min-width:576px){
    .photo-lightbox .lb-nav{ width:48px; height:48px; }
}

/* Reduced motion: static lightbox for vestibular accessibility. */
@media (prefers-reduced-motion: reduce){
    .photo-lightbox .lb-nav{
        transition:none !important;
    }
    .photo-lightbox .lb-nav:hover{
        transform:translateY(-50%) !important;
    }
    .photo-lightbox .lb-image.lb-loading{
        transition:none !important;
    }
}

/* Belt-and-suspenders: clamp ALL animation/transition durations under
   reduced motion so anything added later is covered by default. The
   targeted blocks above remain for the transform/hover neutralizations
   a duration clamp alone cannot express. */
@media (prefers-reduced-motion: reduce){
    *, *::before, *::after{
        animation-duration:0.001ms !important;
        animation-iteration-count:1 !important;
        transition-duration:0.001ms !important;
    }
}

/* [hidden] must ALWAYS hide. Bootstrap declares its display utilities
   (.d-inline etc., display:...!important) AFTER reboot's
   [hidden]{display:none!important}, so on an equal-specificity tie the
   utility wins and a hidden element can still render (this is how the
   OTP resend form once showed two "Reenviar codigo" buttons). This
   sheet loads after Bootstrap, so re-declaring [hidden] last makes the
   attribute win every such tie, app-wide. */
[hidden]{ display:none !important; }
