/* RUMBO LIBRE Manager — brand-consistent admin shell (reuses tokens.css). */
*{box-sizing:border-box;}
html,body{margin:0;padding:0;}
body{font-family:Manrope,system-ui,-apple-system,sans-serif;background:var(--sand,#F5EFE3);color:var(--ink,#0C3A5B);-webkit-font-smoothing:antialiased;}
a{color:inherit;text-decoration:none;}
h1,h2,h3{letter-spacing:-.01em;}

/* App shell: sidebar + main */
.mgr{display:grid;grid-template-columns:212px 1fr;min-height:100vh;}
/* Pinned to the viewport height so the brand header and the collapse toggle always stay put; .nav (below) is
   the only part that scrolls once the role-gated link list grows taller than the screen. */
/* No side padding (1:1 with the design canvas): the rows run edge to edge, so the active row's accent sits on the
   sidebar's own left edge and a label has the full 212px to itself — with 12px gutters «Cobertura de tarifas»,
   «Importar contenido» and four more labels broke onto two lines. */
.side{background:var(--ink-deep,#0C3A5B);color:#fff;display:flex;flex-direction:column;padding:16px 0 0;position:sticky;top:0;height:100vh;overflow:hidden;}
.side .brand{display:flex;align-items:center;gap:10px;font-weight:800;font-size:17px;padding:8px 20px 18px;flex:none;}
.side .brand img{width:30px;height:30px;border-radius:50%;}
.side .brand small{display:block;font-size:12px;font-weight:600;color:#9fc0c7;letter-spacing:.04em;text-transform:uppercase;}
/* flex:1 1 auto + min-height:0 is what lets a flex child scroll instead of forcing its flex-column parent
   (.side) to grow past the viewport — min-height:0 overrides flexbox's default min-height:auto sizing. */
.nav{display:flex;flex-direction:column;gap:3px;flex:1 1 auto;min-height:0;padding-bottom:16px;overflow-y:auto;overflow-x:hidden;scrollbar-width:none;-ms-overflow-style:none;}
.nav::-webkit-scrollbar{width:0;height:0;display:none;}
/* Redesign sidebar (1:1 with the design canvas): grouped, flat rows with a left accent on the active
   one — not rounded pills. Headings are a quiet divider; items sit at 13px (the secondary tier: at 14px «Reporte de
   campañas» and «Facturas de proveedor» broke onto two lines in the 212px rail) with a 3px transparent
   left border so the active border never shifts the text.
   Type floor (2026-10-17): the group heading was 10.5px in #5F7E8A — 2.73:1 on the navy, unreadable. It is now
   12px (the floor of anything read; the board audit measures the chrome at 12) in #8FABB5, 4.89:1 on --ink-deep:
   still quieter than the #AAB4C2 items (5.65:1), now legible. */
.nav .grp{font-size:12px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:#8FABB5;padding:14px 18px 5px;}
.nav a{display:flex;align-items:center;gap:11px;padding:9px 18px;border-left:3px solid transparent;color:#AAB4C2;font-size:13px;font-weight:600;transition:background .12s,color .12s;}
.nav a i{font-size:17px;width:18px;text-align:center;}
.nav a:hover{background:rgba(255,255,255,.06);color:#fff;}
/* Active item: white label (≈13:1 on --ink-deep — well past AA) on a subtle wash, with the ocean-500
   accent down the left edge. The accent is decorative; the white-on-dark label carries the contrast. */
.nav a.active{background:rgba(255,255,255,.08);border-left-color:var(--teal,#1577B5);color:#fff;}
.nav a.active i{color:#5EA9DA;}
.nav a.disabled{opacity:.5;cursor:default;}
.nav a .soon{margin-left:auto;font-size:12px;font-weight:700;background:rgba(255,255,255,.14);padding:2px 7px;border-radius:999px;}

/* Blazor Server's built-in circuit-error banner (MainLayout.razor). Hidden by default — the framework's boot
   script sets an inline display:block only when an unhandled circuit error actually occurs; without this rule
   the div has no default state and renders visible on every page load. */
/* Same look as an error toast (.toast.toast-error): a card at the bottom centre, not a red strip across the
   page. Blazor sets display:block when it shows it, so the flex layout lives on a :not([style*=none]) guard. */
#blazor-error-ui{display:none;position:fixed;left:50%;bottom:20px;transform:translateX(-50%);z-index:9999;width:min(520px,calc(100vw - 32px));
  box-sizing:border-box;padding:13px 15px;border-radius:12px;background:#FDECEC;border:1px solid #F3C0BD;color:#B42318;
  font-size:14px;font-weight:600;line-height:1.4;box-shadow:0 10px 30px rgba(8,20,24,.18);}
#blazor-error-ui[style*="block"]{display:flex !important;align-items:center;gap:11px;}
#blazor-error-ui > i{font-size:20px;flex:none;}
#blazor-error-ui .err-text{flex:1;min-width:0;}
#blazor-error-ui .reload{color:#B42318;text-decoration:underline;font-weight:700;white-space:nowrap;}
#blazor-error-ui .dismiss{margin-left:4px;}

.main{display:flex;flex-direction:column;min-width:0;}
/* Minimal top bar (redesign, 1:1 with the canvas): search left, notifications + user menu right.
   No page title — each page carries its own <h1>. */
.topbar{height:56px;background:#fff;border-bottom:1px solid var(--line-2);display:flex;align-items:center;justify-content:space-between;padding:0 24px;gap:16px;position:sticky;top:0;z-index:5;}
.topbar-left{display:flex;align-items:center;gap:12px;min-width:0;flex:1 1 auto;}
.topbar-right{display:flex;align-items:center;gap:12px;flex:none;}
/* The menu button (owner, 2026-10-10): on desktop it folds the sidebar to its icons and back; on mobile, where the sidebar is an
   off-canvas drawer, it opens it. One button, one icon per breakpoint. */
.navtoggle{display:inline-flex;flex:none;}
.navtoggle .nt-menu{display:none;}
/* FocusOnNavigate (Routes.razor, Selector="h1") moves focus to the page's <h1> after each SPA navigation for
   screen-reader users. A heading isn't an interactive control, so its visible focus ring reads as a stuck
   selection — suppress it (keyboard focus on real controls is unaffected). */
h1:focus{outline:none;}

/* User menu — a CSS-only <details> dropdown (SSR-safe, no circuit). The summary is the avatar + name chip. */
.usermenu{position:relative;}
.usermenu > summary{list-style:none;cursor:pointer;}
.usermenu > summary::-webkit-details-marker{display:none;}
.userchip{display:flex;align-items:center;gap:9px;background:#fff;border:1px solid var(--line-2);border-radius:999px;padding:4px 10px 4px 4px;transition:background .12s,border-color .12s;}
.userchip:hover{background:rgba(31,42,46,.04);}
.usermenu[open] > .userchip{border-color:var(--teal,#1577B5);}
.uav{width:30px;height:30px;border-radius:50%;background:var(--teal-d,#0E4C77);color:#fff;font-size:12px;font-weight:700;display:flex;align-items:center;justify-content:center;flex:none;}
.uwho{display:flex;flex-direction:column;line-height:1.15;text-align:left;min-width:0;}
.uwho b{font-size:13px;font-weight:700;color:var(--ink,#0C3A5B);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:150px;}
.uwho span{font-size:12px;color:var(--muted);}
.uchev{font-size:13px;color:var(--faint,#626F73);flex:none;}
.usermenu-pop{position:absolute;top:calc(100% + 8px);right:0;width:210px;padding:6px;display:flex;flex-direction:column;gap:2px;box-shadow:0 12px 32px rgba(21,51,61,.18);z-index:61;}
.usermenu-pop form{margin:0;}
.umi{display:flex;align-items:center;gap:10px;width:100%;text-align:left;font-size:14px;font-weight:600;color:var(--ink,#0C3A5B);background:none;border:0;border-radius:8px;padding:9px 10px;cursor:pointer;font-family:inherit;}
.umi i{font-size:17px;color:var(--muted);}
.umi:hover{background:var(--teal-50,#E2F0F8);color:var(--teal-d,#0E4C77);}
.umi:hover i{color:var(--teal-d,#0E4C77);}

.btn.sm{padding:6px 10px;font-size:12px;}

/* Notification bell (MainLayout topbar) */
.bellwrap{position:relative;}
.bellbtn{position:relative;background:transparent;border:1px solid var(--line-2);border-radius:8px;width:36px;height:36px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;font-size:18px;color:var(--ink,#0C3A5B);}
.bellbtn:hover{background:rgba(31,42,46,.06);}
/* The counter reads at the 12px floor; the disc grows with it (18px, round for one digit, a pill for «99+»). */
.bell-badge{position:absolute;top:-5px;right:-5px;background:var(--danger);color:#fff;font-size:12px;font-weight:700;line-height:1;padding:3px 5px;border-radius:999px;min-width:18px;text-align:center;}
.bell-scrim{position:fixed;inset:0;z-index:20;background:transparent;}
.bell-panel{position:absolute;top:46px;left:50%;transform:translateX(-60%);width:340px;max-height:420px;display:flex;flex-direction:column;z-index:21;box-shadow:0 12px 32px rgba(21,51,61,.18);}
.bell-head{display:flex;align-items:center;justify-content:space-between;padding:12px 14px;border-bottom:1px solid var(--line-2);}
.bell-empty{padding:20px 14px;text-align:center;}
.bell-list{overflow-y:auto;}
.bell-item{display:flex;flex-direction:column;align-items:flex-start;gap:2px;width:100%;text-align:left;padding:10px 14px;border:none;border-bottom:1px solid var(--line-2);background:#fff;cursor:pointer;font-family:inherit;}
.bell-item:hover{background:var(--row-hover);}
.bell-item:last-child{border-bottom:none;}
.bell-item-title{font-weight:700;font-size:13px;color:var(--ink,#0C3A5B);}
.bell-item-body{font-size:12px;color:var(--muted);}
.bell-item-time{font-size:12px;color:var(--muted);margin-top:2px;}
@media (max-width:900px){
  /* Reset the desktop centering transform (translateX(-60%), relative to left:50%) — otherwise it still
     applies on top of the fixed left/right positioning below and drags the panel off-screen to the left. */
  .bell-panel{position:fixed;top:62px;left:12px;right:12px;width:auto;transform:none;}
}
.content{padding:24px 28px;}
/* The File workspace (redesign) draws its own full-bleed header band + sand body, exactly like the
   design canvas, so it opts out of the 24px content gutter when its root (.fw) is the page. */
.content:has(> .fw){padding:0;}

/* Primary is the BRAND's own call to action: solid deep teal with white text — the same --teal-d the public
   site puts on every CTA, so the console and the site are visibly one product. It was a pale celeste tint for
   a while; the tint read as a disabled control and belonged to no palette in particular, and a pastel primary
   next to a white secondary gives the eye almost nothing to rank. Solid brand blue vs. paper is unmistakable
   at a glance, and #0E4C77 on white is 5.8:1 — past AA with room to spare. */
.btn{background:var(--teal-d,#0E4C77);color:#fff;border:1px solid var(--teal-d,#0E4C77);border-radius:8px;padding:9px 16px;min-height:44px;box-sizing:border-box;font-weight:700;cursor:pointer;font-family:inherit;font-size:13px;display:inline-flex;align-items:center;justify-content:center;gap:8px;}
/* Small pill buttons (bulk actions, select-all) keep their compact height. */
.btn.sm{min-height:0;}
.btn:hover{background:#0C3A5B;border-color:#0C3A5B;}
/* Nothing styled a disabled .btn, so every blocked action in the Manager — Send with a rate missing,
   Confirm sale on an already-confirmed file — looked exactly like a live button and simply did nothing
   when clicked. :disabled wins over :hover here because it is declared after it. */
.btn:disabled,.btn[disabled]{opacity:.45;cursor:default;box-shadow:none;}
.btn:disabled:hover,.btn[disabled]:hover{background:var(--teal-d,#0E4C77);border-color:var(--teal-d,#0E4C77);}
.btn.ghost:disabled:hover,.btn.ghost[disabled]:hover{background:#fff;}
/* Was `background:transparent` — on the cream (--sand) page background that left only a 1px gray border to
   read the whole button by, which all but disappeared (reported live: "Move"/"Reply (AI)" on a lead's header).
   White + a small shadow is the same "elevated surface on cream" treatment .panel/.lead/.bcard already use —
   consistent, and it actually separates the button from the page. */
.btn.ghost{background:#fff;color:var(--ink,#0C3A5B);border:1px solid var(--line-2);box-shadow:var(--shadow-pill,0 1px 4px rgba(0,0,0,.05));}
/* A translucent dark overlay (not an opaque sand fill) so the hover darkens whatever's actually behind
   it — white, sand, or a tinted card (e.g. the pink "overdue" reminder background) — instead of stamping
   an opaque sand rectangle that clashes against a differently-colored container. */
.btn.ghost:hover{background:rgba(31,42,46,.06);}

/* Dashboard cards */
.kpis{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:16px;}
/* Exactly four cards never leave an orphan on a second row: four across, or two by two. */
.kpis:has(> .kpi:nth-child(4):last-child){grid-template-columns:repeat(4,minmax(0,1fr));}
@media(max-width:1000px){.kpis:has(> .kpi:nth-child(4):last-child){grid-template-columns:repeat(2,minmax(0,1fr));}}
.kpi{background:#fff;border:1px solid var(--line-2);border-radius:12px;padding:16px 18px;}
/* A figure with a list behind it is a link to that list — same card, the whole of it clickable. */
a.kpi.kpi-link{display:block;color:inherit;text-decoration:none;}
a.kpi.kpi-link:hover{border-color:var(--teal,#1577B5);}
a.kpi.kpi-link:focus-visible{outline:2px solid var(--teal,#1577B5);outline-offset:2px;}
.kpi .k{font-size:12px;color:var(--muted);display:flex;align-items:center;gap:8px;}
.kpi .v{font-size:24px;font-weight:800;margin-top:6px;}
.kpi .soon{display:inline-block;margin-top:8px;font-size:12px;font-weight:700;color:var(--teal-d,#0E4C77);background:var(--teal-50,#E2F0F8);padding:3px 9px;border-radius:999px;}
.kpi.link{transition:border-color .12s,box-shadow .12s,transform .12s;}
.kpi.link:hover{border-color:var(--teal,#1577B5);box-shadow:0 6px 18px rgba(21,119,181,.10);transform:translateY(-1px);}

/* Dashboard section heading with a "view all" link on the right. */
.dsec{display:flex;align-items:baseline;justify-content:space-between;margin:28px 0 12px;}
.dsec h2{font-size:16px;margin:0;}
/* A section title with its counter: the pill sat glued to the last word («nuevas2»). */
.dsec h2.with-count{display:inline-flex;align-items:center;gap:8px;}
/* The counter is a circle for one digit (22×22) and grows sideways only when the number needs it — it measured 25×23, an oval. */
.dsec h2.with-count .pill{box-sizing:border-box;min-width:22px;height:22px;padding:0 6px;justify-content:center;line-height:1;}
/* The intake row's one action, as on the board (design/MainDashboard): a quiet outlined button that lines up with the
   panel's 16px inset — it was a solid primary 4px from the edge, the heaviest thing on the dashboard. */
.tbl td.intake-act{text-align:right;}
/* What a date in a list is the date OF, under it («12 oct» / «seña»): small, muted, on its own line, never wider than the cell. */
.tbl td .tbl-note{display:block;font-size:12px;font-weight:400;color:var(--muted);line-height:1.25;}
.tbl td.intake-act .btn.sm{min-height:30px;padding:5px 12px;font-size:12px;}
/* The "see all" link of a dashboard section is a standalone control, not a link inside a sentence, so it owes
   the same 24px as the rest — it measured 18. */
.dsec a{font-size:13px;font-weight:700;color:var(--teal-d,#0E4C77);display:inline-flex;align-items:center;min-height:24px;}

/* Quick-access tiles: compact icon + label shortcuts to the most-used areas. */
.quick{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;}
@media(max-width:1200px){.quick{grid-template-columns:repeat(3,1fr);}}
@media(max-width:820px){.quick{grid-template-columns:repeat(2,1fr);}}
@media(max-width:560px){.quick{grid-template-columns:1fr;}}
.qtile{display:flex;align-items:center;gap:11px;background:#fff;border:1px solid var(--line-2);border-radius:12px;padding:14px 15px;font-weight:700;color:var(--ink,#0C3A5B);transition:border-color .12s,box-shadow .12s,transform .12s;}
.qtile:hover{border-color:var(--teal,#1577B5);box-shadow:0 6px 18px rgba(21,119,181,.10);transform:translateY(-1px);}
.qtile i{font-size:22px;color:var(--teal-d,#0E4C77);flex:0 0 auto;}

/* Catalog & audience snapshot — clean white stat cards (no grey container/gaps). */
/* Same column ladder as .quick above it, deliberately: the two sections sit one under the other, so a width
   where one is three across and the other four reads as a mistake rather than as two grids. */
.snap{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;}
/* Five or six tiles (a role that sees catalogue, contacts, campaigns and leads) sit in ONE row on a wide screen instead of wrapping 4 + 2. */
@media(min-width:1201px){.snap:has(> .s:nth-child(5):last-child){grid-template-columns:repeat(5,1fr);}.snap:has(> .s:nth-child(6):last-child){grid-template-columns:repeat(6,1fr);}}
@media(max-width:1200px){.snap{grid-template-columns:repeat(3,1fr);}}
@media(max-width:820px){.snap{grid-template-columns:repeat(2,1fr);}}
.snap .s{background:#fff;border:1px solid var(--line-2);border-radius:12px;padding:15px 16px;}
.snap .s .sv{font-size:23px;font-weight:800;color:var(--ink,#0C3A5B);}
.snap .s .sk{font-size:13px;color:var(--muted);display:flex;align-items:center;gap:6px;margin-top:4px;}
.snap .s .sk i{color:var(--teal-d,#0E4C77);font-size:15px;}

/* Horizontal mini-bars (e.g. "what clients ask about" by source). */
.bars{display:flex;flex-direction:column;gap:9px;}
.bars .row{display:grid;grid-template-columns:150px 1fr 40px;align-items:center;gap:10px;font-size:13px;}
.bars .track{background:var(--teal-50,#E2F0F8);border-radius:999px;height:9px;overflow:hidden;}
.bars .fill{background:var(--teal-d,#0E4C77);height:100%;border-radius:999px;}
.bars .cnt{text-align:right;font-weight:700;color:var(--muted);}
@media (max-width:560px){ .bars .row{grid-template-columns:110px 1fr 34px;} }

/* Login — same Atlantic language as the shell: deep-azure field, elevated white card, wave-mark brand
   with the «Manager» kicker, a warm welcome. */
.auth{min-height:100vh;display:grid;place-items:center;background:radial-gradient(1200px 600px at 20% -10%,#15568A 0%,transparent 60%),linear-gradient(135deg,#0C3A5B,#0E4C77);padding:20px;}
/* width must fit inside .auth's own 20px×2 padding, or the card overflows its grid track — when a
   centered grid item can't fit, browsers fall back to "safe" start-alignment instead of true centering,
   which is what made this look shoved to the left rather than centered on narrow screens. */
.authcard{background:#fff;border-radius:18px;padding:36px 34px;width:min(392px,calc(100vw - 40px));box-shadow:0 24px 64px rgba(8,20,32,.38);}
.authcard .brand{display:flex;align-items:center;gap:11px;font-weight:800;font-size:19px;letter-spacing:.2px;color:var(--ink,#0C3A5B);}
.authcard .brand img{width:38px;height:38px;border-radius:50%;}
.authcard .brand small{display:block;font-size:11px;font-weight:600;color:var(--muted);letter-spacing:.09em;text-transform:uppercase;}
.authcard h1{font-size:22px;font-weight:750;letter-spacing:-.01em;margin:22px 0 4px;color:var(--ink,#0C3A5B);}
.authcard .sub{color:var(--muted);font-size:14px;margin-bottom:20px;}
.authcard .btn{width:100%;margin-top:4px;}
.field{margin-bottom:14px;}
.field label{display:block;font-size:13px;font-weight:600;margin-bottom:6px;}
/* One note size for every legend under a control. .fieldnote and .hint are the same note seen from two
   places (a field, a form block), and a note written with the generic .muted colour helper inherited body
   size — so three explanations on one form rendered at three different sizes. */
.fieldnote{display:block;font-size:12px;color:var(--muted);margin-top:5px;line-height:1.45;}
/* A titled part of a long dialog (flight, passenger, service): small caps with an icon over a hairline, so a form of ten fields reads as four short ones. */
.msec{display:flex;align-items:center;gap:8px;margin:20px 0 12px;padding-bottom:8px;border-bottom:1px solid var(--line-2);font-size:12px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--muted);}
.msec:first-child{margin-top:0;}
.msec > i{font-size:16px;color:var(--teal-d);}
/* A narrow field of a row (a code, a number) keeps its width; its neighbours take the rest. */
.frow > .field.w-sm{flex:0 0 150px;}
.pax-lead{margin-top:4px;}
.field input.up{text-transform:uppercase;}
.field input.up::placeholder{text-transform:none;}
/* What is wrong in one row of a dialog: said once under the row, over the full width, so no field grows and the boxes stay on one line. */
.rowmsgs{display:flex;flex-direction:column;gap:4px;margin:-6px 0 14px;}
.rowmsgs .field-msg{display:flex;gap:6px;align-items:flex-start;font-size:12px;color:#A03225;line-height:1.4;}
.rowmsgs .field-msg > i{flex:none;font-size:16px;}
.field .field-msg{display:flex;gap:6px;align-items:flex-start;}
.field .field-msg > i{flex:none;font-size:16px;margin-top:0;}
/* Origin → destination: the arrow sits on the inputs' line, between the two fields. */
.frow.route{align-items:flex-end;}
.frow.route > .route-arrow{flex:none;display:flex;align-items:center;justify-content:center;width:24px;height:44px;margin-bottom:14px;color:var(--muted);font-size:18px;}
.field > .muted,.field > p.muted{font-size:12px;line-height:1.45;}
.field.grid2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;}
@media(max-width:480px){.field.grid2{grid-template-columns:minmax(0,1fr);}}
/* Two fields side by side in a form or a dialog. The class existed only as «.field.grid2»: a plain <div class="grid2"> around two
   fields (the emergency-contact dialog) had no style at all, so its ten fields stacked into a 1.017px dialog. */
.grid2:not(.field){display:grid;grid-template-columns:repeat(2,minmax(0,1fr));column-gap:16px;align-items:start;}
@media(max-width:560px){.grid2:not(.field){grid-template-columns:minmax(0,1fr);}}
.chk{display:flex;align-items:flex-start;gap:8px;font-size:13px;line-height:20px;color:var(--ink,#0C3A5B);margin:9px 0;cursor:pointer;user-select:none;}
.chk input{width:18px;height:18px;margin:1px 0 0;accent-color:var(--teal-d,#0E4C77);cursor:pointer;flex:0 0 auto;}   /* 20px line, 18px box: level with the first line of a label that wraps */

/* Form controls — one shared, modern base for every text input/select/textarea across the app
   (login card, Leads/LeadDetail, Settings, Quote/Reservation/Invoice editors, table line-item
   cells, etc.). Consolidated here instead of duplicated per page so radius/border/focus stay
   consistent; `.cell` (compact table line-item inputs) shares the same treatment at a smaller size. */
.field input,.field textarea,.field select,.cell,.mkf-input{
  width:100%;font-family:inherit;font-size:14px;color:var(--ink,#0C3A5B);
  background:#fff;border:1px solid var(--line-2);border-radius:10px;
  padding:11px 13px;transition:border-color .12s ease,box-shadow .12s ease;
}
/* Pin form controls to the 44px control height so they line up with .btn / .ssel-control in any row.
   Compact table .cell stays short (it overrides padding below); wide search bars (.cell.wide) opt back in. */
.field input,.field select{min-height:44px;box-sizing:border-box;}
.field textarea{min-height:80px;box-sizing:border-box;}
.field textarea{resize:vertical;}
/* Every real checkbox looks the same everywhere: 16px, teal tick. (Switch inputs are visually hidden by the
   later, equally-specific `.switch input` rule, so this base never affects toggles.) */
/* 18px, not 16: the WCAG 2.2 target minimum is 24px, and a checkbox in a picking list (rate requests, bulk
   selects) is a target an operator hits repeatedly, not decoration. With the padding on the cells around it
   this clears the standard's spacing exception. */
/* Radios get the same treatment as checkboxes. They were never styled at all — Selection policy is the only
   screen in the console that uses one, so its three options sat at the browser's 13×13 default, in the
   browser's blue, beside a console that is 18px and brand-teal everywhere else. */
input[type=checkbox],input[type=radio]{width:18px;height:18px;flex:0 0 auto;accent-color:var(--teal-d,#0E4C77);cursor:pointer;}
.field input[type=checkbox]{width:18px;height:18px;flex:0 0 auto;padding:0;accent-color:var(--teal-d,#0E4C77);cursor:pointer;}

/* Modern async file/image picker — a dashed drop-zone that's click-to-browse AND drag-and-drop (the Blazor
   InputFile is stretched invisibly over the whole box). Consistent across image + document uploads. */
.dropzone{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;
  text-align:center;border:2px dashed var(--line-2);border-radius:12px;padding:24px 20px;background:#fdfefe;
  cursor:pointer;transition:border-color .12s ease,background .12s ease;}
.dropzone:hover{border-color:var(--teal,#1577B5);background:var(--teal-50,#E2F0F8);}
.dropzone i{font-size:30px;color:var(--teal-d,#0E4C77);}
.dropzone .dz-title{font-weight:700;font-size:14px;color:var(--ink,#0C3A5B);}
.dropzone .dz-sub{font-size:12px;color:var(--muted);max-width:520px;}
.dropzone input[type=file]{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer;}
.dropzone.busy{opacity:.55;pointer-events:none;}

/* Toggle switch — replaces action checkboxes in lists/forms for a cleaner, more professional look. Wrap a
   real <input type=checkbox> so Blazor @bind keeps working: <label class="switch"><input .../><span class="slider"></span></label> */
.switch{position:relative;display:inline-flex;align-items:center;cursor:pointer;vertical-align:middle;}
.switch input{position:absolute;opacity:0;width:0;height:0;margin:0;}
.switch .slider{width:38px;height:22px;background:#c9d4d2;border-radius:999px;position:relative;transition:background .15s ease;flex:0 0 auto;}
.switch .slider::before{content:"";position:absolute;width:16px;height:16px;background:#fff;border-radius:50%;top:3px;left:3px;transition:transform .15s ease;box-shadow:0 1px 2px rgba(0,0,0,.28);}
.switch input:checked + .slider{background:var(--teal-d,#0E4C77);}
.switch input:checked + .slider::before{transform:translateX(16px);}
.switch input:focus-visible + .slider{outline:2px solid var(--teal,#1577B5);outline-offset:2px;}
.switch input:disabled + .slider{opacity:.5;cursor:default;}
/* A switch followed by its label on one row (used in edit modals). */
.switchrow{display:inline-flex;align-items:flex-start;gap:10px;cursor:pointer;font-size:14px;line-height:22px;}   /* the switch (22px) is level with the label's FIRST line, so a label that wraps never leaves it floating in the middle of the block */
/* When a switch row IS a field's control (sibling of the field label, e.g. "Primary"), keep its own inline-flex
   centring instead of inheriting the block `.field label` style — and give it the same height as an input so it
   lines up with a neighbouring input/select in a two-column .frow. */
.field label.switchrow{display:inline-flex;align-items:flex-start;gap:10px;font-size:14px;line-height:22px;font-weight:400;margin-bottom:0;min-height:42px;padding-top:10px;box-sizing:border-box;}
/* Stacked group of switch rows (e.g. Suggested / Featured / Allow online payment). */
.switchset{display:flex;flex-direction:column;gap:11px;margin:8px 0 4px;}
/* Reusable filter bar (search + dropdowns) above a data table. Wraps on narrow screens. The search input is
   forced to full input height so it lines up with the dropdown controls next to it. */
/* Canonical filter bar: one layout for every list's search + filters row (wrap it in .panel.pad).
   .filters is kept as an alias so existing pages stay consistent without a rename. */
/* The bar OWNS its layout — padding, gap, wrapping, the space below it and the margin a labelled field
   would otherwise add. Every list used to repeat the same declarations in a style= attribute, each copy
   drifting a little (center vs flex-end, with and without the bottom margin), so no two bars in the Manager
   lined up and the mobile rule below needed !important to beat the inline. One rule, every list. */
.filterbar,.filters{display:flex;gap:12px;flex-wrap:wrap;align-items:flex-end;padding:12px 16px;margin-bottom:14px;}
.filterbar > .field,.filters > .field{margin-bottom:0;}
/* Six filters on one row squeezed each to ~160px; as two rows of three every one reads its full value. */
.filterbar.grid3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px 16px;}
.filterbar.grid3 > .field{min-width:0;max-width:none;}
/* A lone switch in the grid sits level with the fields' controls (44 px), at the bottom of its cell. */
.filterbar.grid3 > .switchrow{align-self:end;align-items:center;min-height:44px;}
@media(max-width:820px){.filterbar.grid3{grid-template-columns:1fr;}}
/* Three roles, so no page needs a style= to lay out a bar: the search box takes the biggest share of the
   row, a filter grows into whatever is left over, and anything else keeps its own size.
   The row SHARES its width rather than letting one control take it all. A search on `flex:1 1 220px` with
   no ceiling swallowed every spare pixel on a wide screen while the dropdown beside it stayed pinned at
   175px and truncated its own longest option — "Todas las antigüedades" rendered as "Todas las antigü…"
   next to a 1000px-wide search box. The search now stops at a readable width and the pickers expand. */
/* The row is filled EDGE TO EDGE (owner 2026-10-30: the dropdowns should fill the whole width, there are too many blank spaces): the search takes the biggest share, every filter
   takes an equal share of the rest, and no control stops at a fixed width — a 340/720 px cap left a blank strip at the right of every bar on a wide screen. A long option label
   is still protected by the min-width and by the flex-basis. */
.filterbar .grow,.filters .grow{flex:2 1 260px;min-width:200px;max-width:none;}
.filterbar .pick,.filters .pick{flex:1 1 190px;min-width:170px;max-width:none;}
.filterbar .fixed,.filters .fixed{flex:0 0 auto;}
/* Opt-in: park a trailing action (Export) against the right edge instead of crowding the last filter. */
.filterbar .end,.filters .end{margin-left:auto;}
.filters .cell{padding:11px 13px;font-size:14px;}
/* A segmented control used AS a filter lines up with the inputs beside it — same 44px control height, and
   none of the standalone strip's bottom margin (which pushed it off the row's baseline). */
.filterbar .seg,.filters .seg{margin-bottom:0;min-height:44px;box-sizing:border-box;align-items:stretch;}
/* The same strip as a page-head action (Files ↔ Board): 44 px like the button beside it, one centre line, no global bottom margin. A link is a segment too. */
.pa-actions .seg{margin:0;min-height:44px;box-sizing:border-box;align-items:stretch;flex-wrap:nowrap;}
.pa-actions .seg-btn,.filterbar .seg a.seg-btn{padding-top:0;padding-bottom:0;text-decoration:none;}
.seg a.seg-btn{white-space:nowrap;}
.filterbar .seg.sm .seg-btn,.filters .seg.sm .seg-btn,
.filterbar .seg .seg-btn,.filters .seg .seg-btn{padding-top:0;padding-bottom:0;}
/* A checkbox filter is centred on the control height too, instead of sitting on the row's bottom edge. */
.filterbar > .chk,.filters > .chk{margin:0;min-height:44px;align-items:center;}
/* On mobile AND tablet every control stacks to full width so a select never sits half-width or wraps ragged
   (search full, selects capped at their flex-basis). 820 aligns with .filterbar.grid3's own stack point and the
   Manager's main breakpoint, so the awkward 641–820 flex-wrap band is gone. */
@media(max-width:820px){
  .filterbar,.filters{flex-direction:column;align-items:stretch;}
  .filterbar > *,.filters > *{width:100%;min-width:0;max-width:none;}
  /* flex:0 0 auto is not cosmetic: stacked, the container's main axis is VERTICAL, so the row's
     `flex:3 1 260px` reads 260px as a HEIGHT and renders the search box as a 260px-tall block. Spelled
     out per role because the roles' own rules outrank a `.filterbar > *` reset. */
  /* width:100% + max-width:none at THIS specificity (2 classes) is required: the desktop `.filterbar .pick`
     caps max-width at 340px and out-specifies the `.filterbar > *{max-width:none}` reset above (1 class), so a
     stacked pick stayed 340px (≈83% of a ~410px bar — the "half-width select" symptom). */
  .filterbar .grow,.filterbar .pick,.filterbar .fixed,
  .filters .grow,.filters .pick,.filters .fixed{flex:0 0 auto;width:100%;max-width:none;}
  .filterbar .cell,.filterbar .field,.filters .cell,.filters .field{width:100%;box-sizing:border-box;}
  .filterbar .field .cell{width:100%;max-width:none;}
  .filterbar .end,.filters .end{margin-left:0;}
}

/* The result count is a note ABOUT the list, not a control that filters it. Inside the bar it sat as a
   stray "0 results" floating between the inputs, aligned to nothing; here it reads as a caption directly
   under the bar, small and muted, right where the eye lands before the table. */
.filter-note{display:flex;align-items:center;gap:6px 14px;flex-wrap:wrap;margin:-8px 0 12px;padding:0 2px;
  font-size:13px;color:var(--muted);}
/* The same caption under an ACTION inside a form/modal (why «Cotizar» is off, a basis warning). .filter-note's
   -8px top margin is for sitting tight under a filter bar; under a button it slid up into the button. */
.action-note{display:flex;align-items:flex-start;gap:6px;margin:8px 0 0;font-size:13px;line-height:1.4;color:var(--muted);}
.action-note i{font-size:14px;margin-top:1px;flex:none;}
.action-note.warn{color:#9A5B00;margin:0 0 12px;}
/* Room-mix rows: the table scrolls inside its own box instead of pushing past the modal's edge. */
.mix-rows{overflow-x:auto;border:1px solid var(--line-2);border-radius:12px;}
.mix-tbl thead th,.mix-tbl tbody td{padding:6px 10px;}
/* Markup · «Get rates» on one row, the button on the input's baseline; nothing stretches. */
.frow.mix-foot{margin-top:10px;}
.frow.mix-foot > .field{flex:0 0 auto;}
.frow.mix-foot > .field:first-child:not(.btn-field){max-width:260px;}
.frow.mix-foot{align-items:flex-start;}
.frow.mix-foot > .field.btn-field{justify-content:flex-start;padding-top:24px;}   /* 24 px = the label above the markup input: the button sits on the input's line, not under its hint */
.mix-tbl tbody td{vertical-align:middle;}
.mix-tbl td .ssel{min-width:150px;}
.mix-tbl input.cell.sm{width:76px;text-align:right;}
.mix-tbl tbody tr:hover{background:transparent;}
/* Was --line-2 (a border colour) at 1.08:1 — the separator simply was not there. */
.filter-note .sep{color:var(--faint,#626F73);}
/* The count prints only once the list has loaded (never «0 results» beside the spinner); the empty note keeps its line so nothing jumps when it fills. */
.filter-note:empty{min-height:20px;}
/* Belt-and-suspenders: any panel that currently has an open dropdown lets it overflow (covers non-padded panels). */
.panel:has(.ssel.is-open){overflow:visible;}

/* Campaign editor: compose column + sticky live preview. Collapses to one column on narrow screens. */
.camp-cols{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:18px;align-items:start;}
.camp-form{display:flex;flex-direction:column;gap:16px;min-width:0;}
.camp-preview{position:sticky;top:16px;min-width:0;}
.camp-preview-tools{display:flex;align-items:center;gap:8px;}
/* The email is a fixed 600px design; the iframe is scaled down by campPreview (App.razor) to fit the panel with
   NO horizontal scroll. The wrap clips the scaled frame and its height is set to match. */
.camp-frame-wrap{width:100%;overflow:hidden;border:1px solid var(--line-2);border-radius:10px;background:#fff;}
.camp-frame{border:0;display:block;background:#fff;}
@media(max-width:1100px){.camp-cols{grid-template-columns:1fr;}.camp-preview{position:static;}}

/* Single-image slot (ImageSlot.razor) — same upload pipeline as the product galleries, one image. */
.imgslot{width:100%;}
.imgslot-shell{display:flex;gap:12px;align-items:center;border:1px solid var(--line-2);border-radius:12px;padding:10px;background:#fff;}
.imgslot-thumb{width:104px;height:74px;object-fit:cover;border-radius:8px;border:1px solid var(--line-2);flex:0 0 auto;background:var(--sand,#F5EFE3);}
.imgslot-meta{display:flex;flex-direction:column;gap:8px;min-width:0;}
.imgslot-name{font-size:13px;font-weight:600;color:var(--ink,#0C3A5B);}
.imgslot-actions{display:flex;gap:8px;align-items:center;}
/* One centred loading state everywhere (Loader.razor): brand spinner + caption. */
.loader{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:13px;padding:56px 16px;min-height:180px;}
.loader-spin{width:34px;height:34px;border:3px solid var(--line-2);border-top-color:var(--teal,#1577B5);border-radius:50%;animation:rl-spin .7s linear infinite;}
.loader-text{color:var(--muted);font-size:14px;font-weight:600;}
/* Skeleton loaders (Loader Kind="list|editor|board"): a modern shimmer placeholder that holds the page's shape so
   the content does not jump in when the data arrives (owner, 2026-09-25). */
.skel{padding:2px 0;}
.skel-block{display:block;background:linear-gradient(90deg,var(--line-2) 25%,rgba(255,255,255,.6) 37%,var(--line-2) 63%);background-size:400% 100%;border-radius:8px;height:16px;animation:skel-shimmer 1.4s ease infinite;}
@keyframes skel-shimmer{0%{background-position:100% 0;}100%{background-position:0 0;}}
@media (prefers-reduced-motion: reduce){.skel-block{animation:none;}}
.skel-head{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-bottom:18px;}
.skel-title{height:28px;width:220px;max-width:60%;}
.skel-btn{height:34px;width:130px;border-radius:10px;flex:none;}
.skel-filters{display:flex;gap:12px;margin-bottom:16px;flex-wrap:wrap;}
.skel-filters .skel-block{height:44px;flex:1;min-width:160px;border-radius:10px;}
.skel-rows{display:flex;flex-direction:column;gap:10px;}
.skel-row{display:grid;grid-template-columns:2fr 1fr 1fr;gap:12px;}
.skel-row .skel-block{height:20px;}
/* Body-only skeletons (Loader Kind="table" | "form"): the head and the tabs above are real, so this is drawn as the panel that is coming —
   same surface, same inset — and tall enough that the page does not collapse and refill (the spinner it replaces was 180px). */
.skel.skel-form{display:block;background:#fff;border:1px solid var(--line-2);border-radius:var(--r-card);padding:18px;min-height:300px;box-sizing:border-box;}
.skel.skel-form > .skel-form{margin:0;}
.skel-form{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;}
.skel-field{display:flex;flex-direction:column;gap:8px;}
.skel-label{height:12px;width:40%;}
.skel-input{height:40px;border-radius:10px;}
.skel-cols{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;}
.skel-col{display:flex;flex-direction:column;gap:10px;}
.skel-colhead{height:18px;width:70%;}
.skel-card{height:64px;border-radius:12px;}
@media (max-width:900px){.skel-form{grid-template-columns:repeat(2,1fr);}.skel-cols{grid-template-columns:repeat(2,1fr);}}
@media (max-width:600px){.skel-form{grid-template-columns:1fr;}.skel-row{grid-template-columns:1fr;}}
/* Static preloader (App.razor): covers the blank moment between the HTML and the first draw of the circuit; gone once the app rendered a shell,
   a sign-in card, or the framework's own error surface. */
.app-preload{position:fixed;inset:0;z-index:2;display:grid;place-items:center;background:var(--sand,#F5EFE3);animation:app-preload-out 0s linear 8s forwards;}
/* Safety net: a browser without :has(), or a page that never draws, still gets its screen back after 8 s instead of a permanent cover. */
@keyframes app-preload-out{to{visibility:hidden;pointer-events:none;}}
body:has(.mgr, .auth, #blazor-error-ui) .app-preload{display:none;}
@keyframes rl-spin{to{transform:rotate(360deg);}}
/* Inline spinner for a button mid-action (Saving…/Sending…) — pair with a disabled button to block double-clicks. */
.spin{display:inline-block;animation:rl-spin .7s linear infinite;}
/* Any button whose click is still being handled (window.rlBusy, BusyAwareComponent): its icon becomes a spinner
   (a spinner is added in front when it has no icon), the text stays, and it takes no further clicks. */
.btn.is-busy,.iconbtn.is-busy{pointer-events:none;cursor:progress;}
/* Filters, pager arrows, select options and the bell have no spinner slot: dim them, so a stale table never looks idle (UI-25). */
.seg-btn.is-busy,.mv.is-busy,.bellbtn.is-busy,.pager button.is-busy{pointer-events:none;cursor:progress;opacity:.6;}
.btn.is-busy > i.ph:first-child,.iconbtn.is-busy > i.ph{display:none;}
.btn.is-busy::before,.iconbtn.is-busy::before{content:"";display:inline-block;flex:none;width:15px;height:15px;box-sizing:border-box;
  border-radius:50%;border:2px solid currentColor;border-right-color:transparent;animation:rl-spin .7s linear infinite;}
.iconbtn.is-busy::before{width:18px;height:18px;vertical-align:middle;}
.btn.icon.is-busy::before{width:18px;height:18px;}

/* Bottom toast notifications — the ONLY place confirmations/errors appear (never inline banners, which can be
   scrolled out of view). Centered stack, auto-dismiss, manual close; full-width on mobile. */
.toast-stack{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);display:flex;flex-direction:column;gap:10px;z-index:3000;width:min(460px,calc(100vw - 32px));pointer-events:none;}
.toast{display:flex;align-items:center;gap:11px;padding:13px 15px;border-radius:12px;font-size:14px;font-weight:600;line-height:1.4;
  background:#fff;border:1px solid var(--line-2);box-shadow:0 10px 34px rgba(14,42,51,.20);pointer-events:auto;animation:toastin .2s ease;}
.toast i{font-size:20px;flex:0 0 auto;}
.toast span{flex:1;min-width:0;}
.toast-x{background:none;border:none;font-size:20px;line-height:1;cursor:pointer;color:inherit;opacity:.55;flex:0 0 auto;padding:0 2px;}
.toast-x:hover{opacity:1;}
.toast-success{background:#E5F3E7;border-color:#BFE3C8;color:#2A7440;}
.toast-error{background:#FDECEC;border-color:#F3C0BD;color:#B42318;}
.toast-info{background:#E2F0F8;border-color:#BEE0E8;color:#0E4C77;}
@keyframes toastin{from{opacity:0;transform:translateY(14px);}to{opacity:1;transform:none;}}
@media(max-width:560px){.toast-stack{bottom:12px;width:calc(100vw - 24px);}}

/* Searchable dropdown (SearchSelect.razor). Matches .field select chrome (border, radius, teal focus) so it
   drops in anywhere a select was used, but adds a type-to-filter popup. `.sm` is the compact table-cell size. */
.ssel{position:relative;width:100%;}
/* Matches .field input height exactly (min-height pins it to the same 44px so a dropdown and a text input line
   up perfectly next to each other). */
.ssel-control{width:100%;min-height:44px;box-sizing:border-box;display:flex;align-items:center;justify-content:space-between;gap:8px;background:#fff;
  border:1px solid var(--line-2);border-radius:10px;padding:10px 13px;font-family:inherit;font-size:14px;
  color:var(--ink,#0C3A5B);cursor:pointer;text-align:left;line-height:1.2;}
.ssel-control:hover{border-color:#C7D2D1;}
.ssel.is-open .ssel-control{border-color:var(--teal,#1577B5);box-shadow:0 0 0 3px var(--teal-50,#E2F0F8);}
.ssel-control i.ph-caret-down{font-size:15px;color:var(--muted);transition:transform .15s ease;flex:0 0 auto;}
.ssel.is-open .ssel-control i.ph-caret-down{transform:rotate(180deg);}
/* line-height 1.45 + 1px of air: the ellipsis needs overflow:hidden, and at the inherited 1.2 the box was shorter than the font's descent — the tails of g, p, y and the parentheses were cut. */
.ssel-value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.45;padding:1px 0;}
.ssel-value.placeholder{color:var(--faint,#8A969A);}
.ssel.is-disabled{opacity:.6;}
.ssel.is-disabled .ssel-control{cursor:default;background:var(--sand,#F5EFE3);}
/* position:fixed — ssel.open (App.razor) sets left/top/width so the popup anchors to its control, flips up when
   there's no room below, right-aligns near the edge, and is never clipped by a panel/modal. opacity starts at 0
   to avoid a top-left flash before JS positions it. */
/* A column, so ssel.open can cap the whole popup to the room it actually has on screen and let the LIST
   take the slack: a country list opened from the middle of a tall modal used to run off the bottom of the
   window with its last options unreachable — scrolling to them closed it. */
.ssel-pop{position:fixed;z-index:2600;min-width:200px;background:#fff;opacity:0;display:flex;flex-direction:column;
  border:1px solid var(--line-2);border-radius:12px;box-shadow:0 14px 36px rgba(14,42,51,.18);overflow:hidden;}
.ssel-search{flex:0 0 auto;}
.ssel-search{display:flex;align-items:center;gap:8px;padding:9px 11px;border-bottom:1px solid var(--line-2);}
.ssel-search i{font-size:15px;color:var(--muted);flex:0 0 auto;}
.ssel-input{border:none;outline:none;width:100%;font-family:inherit;font-size:14px;background:transparent;color:var(--ink,#0C3A5B);}
.ssel-list{max-height:244px;min-height:0;flex:1 1 auto;overflow:auto;overscroll-behavior:contain;padding:5px;}
.ssel-opt{width:100%;display:flex;align-items:center;justify-content:space-between;gap:8px;background:transparent;border:none;
  border-radius:8px;padding:9px 10px;font-family:inherit;font-size:14px;color:var(--ink,#0C3A5B);cursor:pointer;text-align:left;}
.ssel-opt:hover{background:var(--teal-50,#E2F0F8);}
.ssel-opt.sel{color:var(--teal-d,#0E4C77);font-weight:600;}
.ssel-opt.sel i{color:var(--teal-d,#0E4C77);font-size:15px;flex:0 0 auto;}
.ssel-empty{padding:14px 12px;color:var(--muted);font-size:13px;text-align:center;}
.ssel.sm .ssel-control{padding:5px 10px;font-size:13px;border-radius:8px;min-height:34px;}
/* «bare»: on a board card the control is text with a caret until the card is hovered or the list is open. */
.ssel.bare{width:auto;min-width:0;max-width:100%;flex:1;}
.ssel.bare .ssel-control{border-color:transparent;background:transparent;padding-left:4px;}
.lead:hover .ssel.bare .ssel-control,.ssel.bare.is-open .ssel-control{border-color:var(--line-2);background:#fff;}
/* The %/USD switch beside an adjustment's value: as wide as its two short options need. */
.ssel.adj-mode{width:auto;min-width:78px;flex:0 0 auto;}

/* Restricted HTML editor (HtmlLite.razor): a toolbar over a contenteditable area. Same border/focus chrome as
   the other inputs so it reads as one field. */
.htmled{border:1px solid var(--line-2);border-radius:10px;overflow:hidden;background:#fff;}
.htmled:focus-within{border-color:var(--teal,#1577B5);box-shadow:0 0 0 3px var(--teal-50,#E2F0F8);}
.htmled-tools{display:flex;align-items:center;gap:4px;padding:6px 8px;border-bottom:1px solid var(--line-2);background:var(--sand,#F5EFE3);}
.htmled-b{width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;border:1px solid transparent;
  border-radius:7px;background:transparent;color:var(--ink,#0C3A5B);cursor:pointer;font-size:16px;}
.htmled-b:hover{background:#fff;border-color:var(--line-2);}
.htmled-hint{margin-left:auto;font-size:12px;color:var(--muted);}
.htmled-area{min-height:120px;max-height:360px;overflow:auto;padding:12px 14px;font-size:14px;line-height:1.55;color:var(--ink,#0C3A5B);outline:none;}
.htmled-area p{margin:0 0 10px;}
.htmled-area p:last-child{margin-bottom:0;}
@media(max-width:560px){.htmled-hint{display:none;}}

/* Image picker spans the full content width and its dropzone fills the panel. */
.img-gallery{width:100%;}
.img-gallery .dropzone{width:100%;box-sizing:border-box;}
.cell{font-size:13px;padding:7px 9px;border-radius:8px;}
.field input::placeholder,.field textarea::placeholder,.cell::placeholder,.mkf-input::placeholder{color:var(--faint,#8A969A);opacity:1;}
.field input:hover,.field textarea:hover,.field select:hover,.cell:hover,.mkf-input:hover{border-color:#C7D2D1;}
.field input:focus,.field textarea:focus,.field select:focus,.cell:focus,.mkf-input:focus{
  outline:none;border-color:var(--teal,#1577B5);box-shadow:0 0 0 3px var(--teal-50,#E2F0F8);
}
.field input:disabled,.field textarea:disabled,.field select:disabled,.cell:disabled{
  background:var(--sand,#F5EFE3);color:var(--muted);border-color:var(--line-2);
  opacity:1;cursor:not-allowed;
}
/* Custom <select> chevron — removes the native OS arrow and draws a Phosphor-style caret-down via
   inline SVG so it renders consistently across browsers without depending on the icon font/CDN.
   Applies both to full-size .field selects and to the compact .cell selects used inside table
   line-item rows (Products/CircuitDetail/HotelDetail/QuoteEditor/etc.). */
.field select,select.cell{
  appearance:none;-webkit-appearance:none;-moz-appearance:none;cursor:pointer;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath fill='%235b6b6e' d='M213.66 101.66l-80 80a8 8 0 01-11.32 0l-80-80A8 8 0 0153.66 90.34L128 164.69l74.34-74.35a8 8 0 0111.32 11.32z'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
}
.field select{padding-right:38px;background-position:right 12px center;background-size:16px;}
select.cell{padding-right:26px;background-position:right 8px center;background-size:13px;}
.field select:disabled,select.cell:disabled{
  cursor:not-allowed;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath fill='%238A969A' d='M213.66 101.66l-80 80a8 8 0 01-11.32 0l-80-80A8 8 0 0153.66 90.34L128 164.69l74.34-74.35a8 8 0 0111.32 11.32z'/%3E%3C/svg%3E");
}
.authcard .btn{width:100%;justify-content:center;margin-top:6px;}
.autherr{background:#FDECEC;color:#B42318;border:1px solid #F3C0BD;border-radius:10px;padding:10px 12px;font-size:13px;margin-bottom:16px;display:flex;align-items:center;gap:8px;}

/* Page header + data panels/tables.
   The header holds a title/subtitle block on the left and an actions cluster on the right. It wraps: when the
   two don't fit on one line the actions drop to their own line, still right-aligned, so nothing ever collides
   or sticks to the edge. Wrap the action buttons in `.pa-actions` (or add `.stacked` to always break them onto
   a new right-aligned line). `.titleline` keeps an h1 + trailing pills/stars vertically centred. */
.page-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px 16px;margin-bottom:20px;flex-wrap:wrap;}
/* A header action button is ALWAYS pushed to the right — including when the header wraps onto a second line on a
   narrow screen, where `justify-content:space-between` would otherwise leave a lone button at the left edge. */
.page-head > .btn,.page-head > a.btn,.page-head > button.btn{margin-left:auto;}
/* Stage tab strip for list screens (Files, Leads, Quotes…) — 1:1 with the design canvas: flat text tabs
   with a quiet count, an ocean-800 underline on the active one, and an optional cross-link («Enquiries →»).
   Shared so every list that filters by stage reads identically. */
.listtabs{display:flex;gap:0;margin:-6px 0 18px;border-bottom:1px solid var(--line-2);flex-wrap:wrap;}
.listtabs > *{font-size:14px;color:var(--muted);padding:11px 0;margin-right:22px;background:none;border:0;border-bottom:2px solid transparent;cursor:pointer;font-family:inherit;font-weight:400;white-space:nowrap;}
.listtabs > *:hover{color:var(--ink,#0C3A5B);}
.listtabs .active{font-weight:700;color:var(--ink,#0C3A5B);border-bottom-color:var(--teal-d,#0E4C77);}
.listtabs .cnt{color:var(--muted);font-weight:500;margin-left:4px;font-variant-numeric:tabular-nums;}
@media(max-width:640px){
  /* On a phone the tab strip scrolls sideways, but WITHOUT a visible scrollbar (owner, 2026-10-06) — same
     treatment as .seg: swipe to reach the rest, no grey bar under the tabs. */
  .listtabs{flex-wrap:nowrap;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none;}
  .listtabs::-webkit-scrollbar{display:none;}
  .listtabs > *{margin-right:18px;white-space:nowrap;}
}

/* ── Full-bleed header band (redesign, 1:1 with the design canvas) ───────────────────────────
   The title row and its SectionTabs strip read as ONE white band spanning the content width with a
   bottom rule, exactly like every wireframe — while pages keep their existing `.page-head` +
   `<SectionTabs>` markup (no per-page change). Negative gutters cancel the .content padding
   (24px vertical / 28px horizontal) so the band reaches the top and both edges. Scoped to a DIRECT
   child of .content, so a `.page-head`/`.listtabs` used inside a panel or modal is untouched, and
   FileWorkspace/.inbox (which set .content padding:0 and draw their own band) are unaffected. */
.content > .page-head{background:#fff;margin:-24px -28px 24px;padding:18px 28px;border-bottom:1px solid var(--line-2);}
/* Followed by a tab strip → the band continues into the tabs: this row drops its own rule and bottom
   gap so the two are one surface, and the tabs below carry the band's border. */
.content > .page-head:has(+ .listtabs){border-bottom:none;margin-bottom:0;padding-bottom:10px;}
.content > .listtabs{background:#fff;margin:0 -28px 24px;padding:0 28px;border-bottom:1px solid var(--line-2);}
.page-head h1{margin:0 0 4px;font-size:22px;font-weight:750;line-height:1.15;}
.page-head .sub{margin:0;color:var(--muted);font-size:14px;}
/* The head says what the page is in ONE line; the rule that needs a paragraph goes under the tabs, where its length moves nothing. */
.page-intro{margin:-8px 0 16px;color:var(--muted);font-size:13px;line-height:1.5;max-width:980px;}
/* A code (a reference, a product code, a permission key) is written in the console's own typeface, in bold: never the browser's monospace. ONE rule for every <code>. */
code,kbd,samp{font:inherit;font-weight:700;font-variant-numeric:tabular-nums;letter-spacing:.2px;}
.page-head .sub .refcode{color:var(--ink,#0C3A5B);font-variant-numeric:tabular-nums;letter-spacing:.3px;}
.page-head>.head-main{min-width:min(100%,320px);flex:1 1 auto;}
.page-head .pa-actions{display:flex;gap:10px;flex-wrap:wrap;align-items:center;justify-content:flex-end;margin-left:auto;}
.page-head.stacked .pa-actions{flex-basis:100%;}
/* Header rule: the action cluster (44px buttons) is CENTRED on the title line (27.6px), on every page. The cluster is top-aligned to the header
   block, so the title gets an 8px top margin = (44 − 27.6) / 2, which puts its centre level with the buttons' centre. A detail page starts with a
   «← Back» link above the title (24px + 6px), so its cluster is pushed down by that row as well: 8 + 30 − 8 = 30px... measured, not derived:
   the cluster's top must land at the title's top margin edge, i.e. 30px below the block start. If the cluster does not fit beside the title and
   wraps under it, that offset is its gap to the text (the row gap is dropped so the two do not add up). Below 641px the cluster is full-width
   under the title and needs no offset. A media query, not a container query: container-type would make the header the containing block of a
   dropdown's position:fixed popup (see the bulk bar note further down). */
@media(min-width:641px){
  .page-head:has(> .head-actions, > .pa-actions) h1{margin-top:8px;}
  /* …and the title sits at that same height on a page WITHOUT actions: sibling pages of one section (Cuentas corrientes · Saldos · Pagos)
     differed by those 8px, so the title and the tab strip under it stepped up and down on every tab (measured 132 ↔ 140px, 2026-10-10). */
  .content > .page-head h1{margin-top:8px;}
  .page-head:has(> :first-child > .backlink){row-gap:0;}
  .page-head:has(> :first-child > .backlink) > .head-actions,
  .page-head:has(> :first-child > .backlink) > .pa-actions{margin-top:30px;}
}

/* Also used by every detail heading that is a NUMBER + a badge + a note: Razor swallows the whitespace
   around @{…} and @if blocks, so relying on a literal space rendered "QUO-2607-00008Cotação5 sem tarifa" —
   the same defect that once made two badges read "B2CQuoted". The gap is the layout's job, not the markup's. */
.titleline{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}
.titleline h1{margin:0;}

/* Form footer: the action row that closes a form. Save is always last and the whole cluster is right-aligned,
   the same on every page and inside modals, so users always find Save in the same place (bottom-right). Use
   `.spacer` on a leading element to push secondary/destructive actions to the left. `.tight` drops the divider
   for compact panels/modals. */
.formfoot{display:flex;align-items:center;justify-content:flex-end;gap:10px;flex-wrap:wrap;margin-top:18px;padding-top:16px;border-top:1px solid var(--line-2);}
.formfoot.tight{margin-top:14px;padding-top:0;border-top:none;}

.muted{color:var(--muted);}
.panel{background:#fff;border:1px solid var(--line-2);border-radius:12px;
  /* overflow-x:auto (not hidden) at EVERY width. A .panel wrapping a wide .tbl used to clip on any viewport
     above the 820px breakpoint: at 900px the Files table hid 279px — «Saldo pendiente» and the whole actions
     column — with no scrollbar and no way to reach them. Any laptop or tablet window narrower than the table
     silently lost its right-hand columns. overflow-y stays hidden so the rounded corners still clip the
     table's first/last rows, which is what the original overflow:hidden was for. */
  overflow-x:auto;overflow-y:hidden;}

/* A panel that scrolls sideways but shows no sign of it is the same defect the Settings tab strip had, one
   scale up: macOS hides its scrollbar until you already know to scroll, so a table wider than the window
   still looks like the whole table. Same answer as .seg — say there is more that way — but in pure CSS:
   `local` gradients travel with the content and `scroll` ones stay pinned, so the shadow appears on
   whichever side has something hidden and disappears by itself at each end. No class to maintain, no
   observer, and nothing at all on a table that fits or on a .panel wrapping a form.
   :has() scopes it to table panels, whose rows are transparent so the shadow reads through them. */
.panel:has(> .tbl){
  background:
    linear-gradient(to right,#fff 30%,rgba(255,255,255,0)) left center/34px 100% no-repeat local,
    linear-gradient(to left,#fff 30%,rgba(255,255,255,0)) right center/34px 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%,rgba(21,51,61,.16),rgba(21,51,61,0)) left center/13px 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%,rgba(21,51,61,.16),rgba(21,51,61,0)) right center/13px 100% no-repeat scroll,
    #fff;
}
.tbl{width:100%;border-collapse:collapse;font-size:14px;}
.tbl thead th{text-align:left;font-size:12px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--muted);padding:11px 16px;border-bottom:1px solid var(--line-2);background:var(--sand,#F5EFE3);}
.tbl tbody td{padding:14px 16px;border-bottom:1px solid var(--line-2);}
/* Data cells stay on one line so reference codes/numbers never wrap into a broken stack (e.g. QUO-2607-00001
   splitting over 3 lines) and columns don't compress on narrow screens — the .panel scrolls horizontally
   instead (see the max-width:820px rule). Cells holding long free text opt back into wrapping with .wrap. */
.tbl tbody td,.tbl thead th{white-space:nowrap;}
.tbl td.wrap,.tbl th.wrap{white-space:normal;min-width:180px;}
/* A wrapping cell whose value has no spaces to break at (an e-mail, a request path, an entity id): let it break anywhere
   instead of holding the column 300px wide — the audit log ran 160px past its panel at 1280px. */
.tbl td.brk,.tbl .tbl-sub.brk,.tbl .tbl-sub-xl.brk{overflow-wrap:anywhere;}
/* On a phone the name column of a folded list (catalogue, supplier invoices) may shrink further: at 375px a 180px name + status + two icon buttons is 21px wider than the panel. */
@media(max-width:640px){.tbl td.wrap-sm,.tbl th.wrap-sm{min-width:110px;}}
/* A wrapping cell whose value is a name or a code (supplier, their invoice number): 120px is enough, and 180px pushed a ten-column list 19px past its panel at 1440. */
.tbl td.wrap-md,.tbl th.wrap-md{min-width:120px;}
/* The quote/invoice line editors are full-width forms, not scrolling lists — let their cells wrap normally. */
.tbl.lines tbody td,.tbl.lines thead th{white-space:normal;}
/* …but a row's icon actions stay on ONE line: in a line table the cell wrapped and stacked its three icons. */
.tbl.lines tbody td.act{white-space:nowrap;}
.tbl tbody tr:last-child td{border-bottom:none;}
/* ONE hover for every list (owner, 2026-10-12). It was the page's own sand: a hovered row looked like a hole cut through the
   panel to the background. Now a cool tint that exists nowhere else on the screen. A row that opens something says so with its
   pointer and its own caret or link — the 3px accent on its leading edge read as a stray blue bar beside the caret (owner,
   2026-10-14) and is gone. */
:root{--row-hover:#EAF3F9;}
.tbl tbody tr:hover{background:var(--row-hover);}
/* A voided row (payment, line, …) stays in the table — struck through and muted reads as "happened, but no
   longer counts" without needing a separate explanatory column. */
.tbl tbody tr.is-void td{color:var(--muted);text-decoration:line-through;text-decoration-color:#B4BFC0;}
.tbl tbody tr.is-void td.act{text-decoration:none;}
/* The «Total» line that closes a table of periods (Monthly summary): the months above add up to it. */
.tbl tbody tr.total-row td{background:#FAF7F0;font-weight:700;border-top:1px solid var(--line-2);}
/* A clickable row that can be focused has to SHOW that it is focused, or keyboard users tab through the list
   blind. Inset, because an outline drawn outside a table row is clipped by the panel's own scroller. */
.tbl tbody tr.rowlink:focus-visible{outline:2px solid var(--teal,#1577B5);outline-offset:-2px;background:var(--teal-50,#E2F0F8);}
/* Long text (a long owner name, an unmapped Source string, etc.) truncates with an ellipsis instead of
   wrapping or stretching the pill — a badge/pill's rounded shape only reads correctly on a single line. */
.badge{display:inline-block;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle;font-size:12px;font-weight:700;padding:3px 9px;border-radius:999px;background:var(--line-2);color:var(--ink,#0C3A5B);}
/* Badges that sit side by side in a cell: a row with a gap, never touching. */
.badge-row{display:inline-flex;flex-wrap:wrap;align-items:center;gap:6px;}
.badge.warn{background:#FBE7C6;color:#8A5A09;margin-left:8px;font-size:12px;}
/* The stage word and the detail it hides are one status, so they are drawn as one shape: a filled pill for
   the stage, a hollow one for the detail. Loose 11.5px grey text hanging under a pill read as a stray
   fragment of the row rather than as part of the badge above it. */
.stagecell{display:inline-flex;flex-direction:column;align-items:flex-start;gap:4px;}
.stage-note{display:inline-block;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  vertical-align:middle;font-size:12px;font-weight:600;line-height:18px;padding:1px 9px;border-radius:999px;
  border:1px solid var(--line-2);background:#fff;color:var(--muted);}
.badge.ok{background:#E5F3E7;color:#2A7440;}
/* Badge text does NOT use --muted: that grey is tuned for secondary text on white, and on a tinted chip it
   measured 3.8–4.4:1 — under the 4.5:1 a 12px label needs. These three were the failures. */
.badge.off{background:var(--line-2);color:#556467;}
/* Danger chip — same red family as .toast-error / .autherr / .btn.danger. 6.9:1 on its tint, clears AA. */
.badge.err{background:#FDECEC;color:#B42318;}
.lk{color:var(--teal-d,#0E4C77);font-weight:700;text-decoration:none;}
.lk:hover{text-decoration:underline;}
.badge-staff{background:#0C3A5B;color:#fff;}
.badge-agency{background:var(--teal-50,#E2F0F8);color:var(--teal-d,#0E4C77);}
.badge-finalclient{background:#FBF1DD;color:#855B12;}
.empty{padding:22px 16px;color:var(--muted);font-size:14px;}
.sec-title{font-size:18px;margin:28px 0 12px;letter-spacing:-.01em;}
/* Simple horizontal bars (search analytics) */
.barrow{display:flex;align-items:center;gap:12px;padding:5px 0;}
.barlbl{flex:0 0 90px;font-size:13px;color:var(--muted);}
.bartrack{flex:1;height:14px;background:var(--sand,#F5EFE3);border-radius:999px;overflow:hidden;}
.barfill{height:100%;background:var(--teal,#1577B5);border-radius:999px;min-width:2px;transition:width .3s ease;}
.barval{flex:0 0 56px;text-align:right;font-size:13px;font-weight:700;color:var(--ink,#0C3A5B);}

/* Lead card (lives on the shared .tablero kit below). */
/* White, not the page's sand tone — a tinted card washed out the badges/gray meta text sitting on it
   (low contrast). White + a subtle lift reads as a distinct card against the equally-white column. */
.lead{background:#fff;border:1px solid var(--line-2);border-radius:10px;padding:10px;margin-bottom:8px;box-shadow:var(--shadow-pill,0 1px 4px rgba(0,0,0,.05));}
.lead-top{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-bottom:8px;}
/* Every tag on a card row is the same pill. .badge-type ships a step smaller than .badge, so a card
   carrying channel + type + priority put its three tags at three different heights and the row read as
   ragged rather than as one set. Inside a card they share a height and a type size. */
.lead-top .badge{display:inline-flex;align-items:center;min-height:22px;font-size:12px;line-height:1;}
.lead-top a.badge{min-height:24px;}  /* the one that is a link (to the file it became) is a tap target */
.lead-date{font-size:12px;color:#000;flex-basis:100%;margin-left:0;display:inline-flex;align-items:center;gap:4px;white-space:nowrap;background:none;border:none;border-radius:0;padding:0;}
.lead-date i{font-size:12px;opacity:.8;}
.lead-name{font-weight:700;font-size:14px;color:var(--ink,#0C3A5B);}
.lead-sub{font-size:12px;color:var(--muted);display:flex;align-items:center;gap:5px;margin-top:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.lead-sub i{font-size:13px;flex:0 0 auto;}
/* Compact owner-assign select on the lead card (not inside a .field wrapper) — same modern chevron
   and teal focus treatment as .field select, sized down to fit the card row. */
/* The excerpt is clamped, so it needs to LOOK clamped: a hard cut mid-sentence reads as a rendering
   fault. A long pasted URL also has to break instead of forcing the card wide. */
/* Five lines, then «…». A gradient fade was doing this job, and a faded half-line reads as a rendering
   fault — it looked like the box was clipped rather than like the text goes on. line-clamp cuts on a whole
   line and prints the ellipsis itself.
   The clamp lives on an INNER span (.lead-msg-t), not on this box: line-clamp measures against the
   content box, and this box's OWN padding-bottom sat below that measurement — so the hidden 6th line's
   top sliver showed through the padding instead of being cut. Padding/background/border stay here;
   font/line-height/clamp move to the child, which has none of its own padding to peek through. */
.lead-msg{margin-top:7px;background:var(--sand,#F5EFE3);border:1px solid var(--line-2);border-radius:8px;padding:7px 9px;}
.lead-msg-t{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:5;line-clamp:5;overflow:hidden;
  font-size:13px;color:var(--ink,#0C3A5B);line-height:1.45;overflow-wrap:anywhere;}
/* Where the whole comment is printed (the lead and customer screens) there is nothing to clamp — these
   three set the text directly inside .lead-msg (no .lead-msg-t child), so the clamp rule never applies. */
.lead-msg.full{display:block;font-size:13px;color:var(--ink,#0C3A5B);line-height:1.45;overflow-wrap:anywhere;}
.lead-actions{display:flex;justify-content:flex-end;gap:6px;margin-top:8px;}
.mv{width:28px;height:28px;border-radius:8px;border:1px solid var(--line-2);background:#fff;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;color:var(--teal-d,#0E4C77);}
.mv:hover:not(:disabled){background:var(--teal-50,#E2F0F8);}
.mv:disabled{opacity:.35;cursor:default;}
.mv.danger{color:#B42318;}
.mv.danger:hover:not(:disabled){background:#FDECEC;border-color:#F3C0BD;}

/* Paged list footer: total/range on the left, Prev/Next + "Page X of Y" on the right (reuses .mv buttons).
   ONE canonical .pager (there was a second base rule under the product list that clashed with this one).
   Horizontal padding matches the table cell's 16px so the range legend lines up under the first column and
   the nav buttons under the last; align-items:center keeps the 13px legend centred against the taller buttons. */
.pager{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:12px;padding:8px 16px;font-size:13px;}
.pager-nav{display:flex;align-items:center;gap:10px;}
.badge-b2c{background:#FBF1DD;color:#855B12;}
.badge-b2b{background:var(--teal-50,#E2F0F8);color:var(--teal-d,#0E4C77);}
.badge-type{background:#EEF1F0;color:#556467;font-size:12px;}

/* ─── Shared kanban kit (.tablero) ──────────────────────────────────────────
   One column system for BOTH boards — the commercial pipeline (/board) and the enquiries board
   (/leads) — so the two never drift apart. Columns are soft grey troughs headed by a colour dot;
   each board drops its OWN card inside (the commercial board a read-only link, the leads board the
   interactive triage card with move/assign). Grey trough + dot is the one look; the card is the
   board's business. */
/* Flex, not a fixed-track grid: with a few stages the columns GROW to share the full width (a 4-stage
   board must not leave a quarter of the page empty), and with many they hold a floor and the board
   scrolls sideways inside itself — the page never does. Equal height (stretch) reads as one board. */
.tablero{display:flex;gap:14px;align-items:stretch;overflow-x:auto;padding-bottom:8px;scrollbar-width:thin;}
/* The commercial board has FIVE stages and they are the point of the screen: at 1440px (1172px of content) a
   264px floor cut «Operación» off behind a sideways scroll. .fit5 lowers the floor so all five show from 1440px
   up; narrower windows still scroll inside the board. The leads board has five states too and takes the same floor: its triage card was measured
   at 223px with nothing cut (2026-10-24), where the 264px floor hid «Descartado» at 1440. */
.tablero.fit5 > .tcol{min-width:212px;}
.tcol{flex:1 1 280px;min-width:264px;background:#EFF1F4;border-radius:var(--r-card,12px);padding:12px 10px;display:flex;flex-direction:column;min-height:160px;}
.tcol-h{display:flex;align-items:center;gap:8px;padding:2px 4px 10px;}
.tcol-dot{width:9px;height:9px;border-radius:50%;flex:0 0 auto;}
.tcol-title{font-size:13px;font-weight:700;letter-spacing:.01em;color:var(--ink,#20302F);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.tcol-cnt{margin-left:auto;flex:0 0 auto;font-size:12px;font-weight:700;color:var(--muted);
  background:#fff;border-radius:999px;padding:1px 8px;min-width:22px;text-align:center;}
/* The leads board explains each stage under its header; the explanations wrap to two lines, so the
   height is reserved everywhere and every column starts its cards at the same line. */
.tcol-note{font-size:12px;color:var(--faint,#8A969A);padding:0 4px 10px;line-height:1.4;min-height:40px;}
.tcol-cards{display:flex;flex-direction:column;gap:8px;}
.tcol-empty{display:flex;flex-direction:column;align-items:center;gap:4px;padding:18px 8px;
  color:var(--muted);font-size:12px;text-align:center;}
.tcol-empty i{font-size:20px;opacity:.6;}
@media (max-width:720px){.tablero > .tcol{flex:0 0 82vw;}}

/* Sales: quotes list + editor */
.tbl th.num,.tbl td.num{text-align:right;}
.tbl th.sortable{cursor:pointer;user-select:none;}
.tbl th.sortable:hover{color:var(--ink,#0C3A5B);}
/* The sort arrow (SortTh): attached to the text, without moving the column when it appears, and with a visible keyboard focus. */
.tbl th.sortable .sort-ico{font-size:14px;margin-left:5px;vertical-align:-2px;color:var(--teal-d,#0E4C77);}
.tbl th.sortable:focus-visible{outline:2px solid var(--teal,#1577B5);outline-offset:-2px;}
.rowlink{cursor:pointer;}
.rlink{color:var(--teal-d,#0E4C77);text-decoration:none;}
.rlink:hover{text-decoration:underline;}
.rowlink:hover{background:var(--row-hover);}
.vtag{display:inline-block;font-size:12px;font-weight:700;color:var(--teal-d,#0E4C77);background:var(--teal-50,#E2F0F8);padding:1px 7px;border-radius:999px;margin-left:6px;vertical-align:middle;}
/* An agency request nobody on staff has opened yet (TravelFile.StaffUnseenSinceUtc): the gold accent, 5.9:1 on its fill. */
.badge.unseen-badge{background:#FBF1DD;color:#6E4A06;margin-left:6px;}
/* In the list's number cell it sits under the number: beside it the column grew 95 px and the table scrolled at 1440 (2026-10-24). */
.rowwide > .badge.unseen-badge{display:table;margin:4px 0 0;}
.backlink{display:inline-flex;align-items:center;gap:6px;font-size:13px;font-weight:600;color:var(--teal-d,#0E4C77);margin-bottom:6px;min-height:24px;}

/* WCAG 2.2 2.5.8 asks 24×24 of any control that is not a link sitting inside a sentence. These four are
   standalone controls that happened to be styled as bare text, so they inherited the line-height of their
   font and nothing else: measured 18–19px tall (the "← Files" backlink, a lead card's name, the two record
   links in an editor header) and 23px for a reference badge that is really a link to another record. They
   are given the height here rather than padding in four markup files, because the only thing wrong with
   them was the height. */
.qmeta-links a,a.lead-name{min-height:24px;}
.qmeta-links a{display:inline-flex;align-items:center;}
/* `gap` because inline-flex drops the whitespace between an icon and its label — a plain .badge is
   inline-block, where that space renders, so a badge that became a link lost it. */
a.badge{display:inline-flex;align-items:center;gap:5px;min-height:24px;box-sizing:border-box;}
/* Header action cluster (editor pages). Same behaviour as .pa-actions: right-aligned, wraps to its own line
   below the title when they don't fit, never cramped against the title. */
.head-actions{display:flex;gap:10px;flex-wrap:wrap;align-items:center;justify-content:flex-end;margin-left:auto;}
/* Puts real distance between the primary action and a destructive one at the end of the same bar. Collapses
   to nothing when the bar wraps onto its own line on a phone, where the buttons are already stacked. */
.head-actions .act-split{width:1px;align-self:stretch;min-height:28px;background:var(--line-2);margin:0 4px;}
@media(max-width:820px){.head-actions .act-split{display:none;}}
/* A long action bar in GROUPS (.head-actions.grouped > .act-group): each group stays whole and wraps as one piece, so a row never
   ends in the middle of «documents» or splits an input from its button. On a phone the groups stack, each one a full row. */
.head-actions.grouped{gap:10px 16px;}
.act-group{display:flex;align-items:center;gap:8px;flex-wrap:nowrap;}
/* A control in an action bar is a full-size control: the same 44px as the buttons beside it (the in-table .cell is 34). */
.act-group.act-mail input.cell{width:220px;min-width:0;min-height:44px;height:44px;font-size:14px;}
@media(max-width:820px){
  .head-actions.grouped{flex-direction:column;align-items:stretch;width:100%;margin-left:0;}
  .act-group{justify-content:flex-start;flex-wrap:wrap;}
  .act-group.act-mail input.cell{flex:1 1 160px;width:auto;}
}
/* On a phone the header actions no longer hang off the right edge. Right-aligned + wrapping put the LAST
   button — always the primary one — alone on its own row. Owner directive (2026-10-06): the «Nuevo» / primary
   action stays pinned RIGHT at every width, mobile included — so the group keeps its own row but right-aligns,
   rather than reading from the left. */
@media(max-width:640px){
  .head-actions,.page-head .pa-actions{width:100%;margin-left:0;justify-content:flex-end;}
}
/* Destructive: solid red when it IS the primary action of a dialog, outlined red when it sits beside one
   (the .ghost variant). Without the explicit fill, .danger only recoloured the text and left the brand-blue
   background underneath — red letters on a blue button. */
.btn.danger{background:#B42318;color:#fff;border-color:#B42318;}
.btn.danger:hover{background:#8F1A12;border-color:#8F1A12;}
.btn.ghost.danger{background:transparent;color:#B42318;border-color:#F3C0BD;}
.btn.ghost.danger:hover{background:#FDECEC;}
/* Solid destructive button for confirm-delete modals. */
/* The one button that stays solid. Everything else went pastel to read lighter; a confirm-delete is the
   place where a soft, low-contrast control would understate what is about to happen. */
.btn.solid-danger{background:#B42318;color:#fff;border:none;}
.btn.solid-danger:hover{background:#8f1a12;}

/* Icon-only action buttons (page headers): square, no label, distinct accent colours + native
   title tooltips. Base is .btn.ghost (white + neutral border); accent modifiers recolour the
   icon/border and tint the hover. Disabled-until-saved links get .is-disabled. */
.btn.icon{width:42px;height:42px;min-height:0;padding:0;justify-content:center;gap:0;}
.btn.icon i{font-size:19px;}
.btn.icon.is-disabled{opacity:.42;pointer-events:none;}
.btn.acc-pdf{color:#B42318;border-color:#F3C0BD;}
.btn.acc-pdf:hover{background:#FDECEC;}
.btn.acc-issue{color:#12689D;border-color:#9CCAE8;}
.btn.acc-issue:hover{background:#EEEBFB;}
.btn.acc-warn{color:#8A6100;border-color:#EBD9A6;}
.btn.acc-warn:hover{background:#FBF3DE;}

/* Bare icon action buttons (row actions: edit / delete / etc.) — NO border, just the icon, with a soft
   rounded hover so tables read clean. Use for icon-only actions inside tables/cards; keep .btn for labelled
   buttons. .danger tints red; disabled dims + blocks pointer. Sized comfortably for touch on mobile. */
.iconbtn{background:transparent;border:none;color:var(--muted);cursor:pointer;padding:7px;border-radius:9px;
  display:inline-flex;align-items:center;justify-content:center;font-family:inherit;line-height:0;transition:background .12s,color .12s;}
.iconbtn:hover{background:rgba(31,42,46,.08);color:var(--ink,#0C3A5B);}
.iconbtn:focus-visible{outline:2px solid var(--teal,#1577B5);outline-offset:1px;}
.iconbtn i{font-size:18px;}
.iconbtn.danger{color:#B42318;}
.iconbtn.danger:hover{background:#FDECEC;}
.iconbtn.ok{color:#2A7440;}
.iconbtn.ok:hover{background:#E5F3E7;}
.iconbtn:disabled{opacity:.4;cursor:default;background:transparent;}
/* Row action cell: keep the icons together on one line, delete first. */
/* Row actions, one per column: delete and edit each get their OWN narrow, centred column — never stacked. */
.tbl th.act,.tbl td.act{width:1%;white-space:nowrap;text-align:center;padding-left:3px;padding-right:3px;}
.tbl td.act .iconbtn{padding:6px;}
/* An action cell that holds a wider text button (e.g. "Review") rather than centered icon buttons: keep it off the
   panel edge with the same right gutter as the other cells. */
.tbl th.act.act-wide,.tbl td.act.act-wide{padding-right:16px;}
/* The LAST cell of every row keeps the gutter the first one has on the left (16px). An icon or a chevron in a narrow .act cell
   (3px) stood on the panel's border, and so did a text button («Cargar tarifa»). Phones fold a row into a card (below), so only
   from 641px. */
@media(min-width:641px){.tbl tr > th:last-child,.tbl tr > td:last-child{padding-right:16px;}}
/* Tariff explorer: list on top, the selected product's tariffs/cost below — full width, even space between. */
.qstack{display:flex;flex-direction:column;gap:16px;}
/* The detail pane's first element sits flush with the stack gap (no extra top margin → symmetric spacing). */
.qstack > div > :first-child{margin-top:0;}
/* A soft notice box (e.g. "not available for these dates") — reads as a state, not weak muted text. */
.notice{display:flex;align-items:center;gap:8px;margin-top:14px;padding:12px 14px;border-radius:12px;background:var(--sand,#F5EFE3);border:1px solid var(--line-2);color:var(--ink,#0C3A5B);font-size:13px;}
.notice i{font-size:16px;flex:none;color:var(--muted);}
/* Shared load-error band (LoadError.razor): amber «needs attention», with the retry pushed to the right. */
.notice.loaderr{background:var(--amber-50,#FBF1DD);border-color:var(--amber,#E8BF7D);color:var(--amber-d,#7A5A12);}
.notice.loaderr i{color:var(--amber-d,#7A5A12);}
/* The same amber state inside a dialog or a form (owner 2026-10-29: more orderly and compact): one tight line of icon + bold lead + text, not a padded card with a pill. */
.notice.warn{margin:10px 0 8px;padding:8px 12px;align-items:flex-start;line-height:1.4;background:var(--amber-50,#FBF1DD);border-color:var(--amber,#E8BF7D);color:var(--amber-d,#7A5A12);}
.notice.warn i{margin-top:1px;color:var(--amber-d,#7A5A12);}
.loaderr-retry{margin-left:auto;flex:none;}
/* Aligned inline loader (spinner vertically centred with its text). */
.loadrow{display:flex;align-items:center;gap:8px;padding:14px 2px;color:var(--muted);font-size:14px;}
.loadrow i{font-size:16px;}
/* Cost result: a compact figure grid (Costo / Venta / per-night) instead of a long breakdown table. */
.costgrid{display:flex;flex-wrap:wrap;gap:12px;margin-top:14px;}
.costcard{flex:1 1 160px;min-width:150px;border:1px solid var(--line-2);border-radius:12px;padding:12px 14px;background:#fff;}
.costcard .k{font-size:12px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--muted);}
.costcard .v{font-size:20px;font-weight:700;color:var(--ink,#0C3A5B);margin-top:4px;}
.costcard .v small{font-size:12px;font-weight:600;color:var(--muted);margin-left:4px;}
.costcard.sale{background:var(--teal-50,#E2F0F8);border-color:var(--teal-100,#C7E2F2);}
/* Empty detail pane (nothing selected yet). */
.empty-pane{text-align:center;padding:44px 24px;}
.empty-pane i{font-size:32px;color:var(--muted);}
.empty-pane p{margin-top:10px;}
/* A secondary section heading within the detail pane. */
.sec-title.sub-sec{font-size:15px;margin-top:18px;}
/* A .field in a .frow that carries only a button (no label) — keep the button on the inputs' baseline. */
.frow > .field.btn-field{justify-content:flex-end;}
/* Availability summary bar. */
.availbar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:12px;}
/* A filter bar whose only control is the search box: let it fill the full width (no 720px cap, no dead space). */
.filterbar.search-only{padding:0;}
.filterbar.search-only .grow{max-width:none;flex:1 1 100%;}
/* A KPI tile whose value is a status word/phrase, not a big number: small, wrapping, never the 26px number size. */
.kpi .v.v-status{font-size:14px;font-weight:700;line-height:1.3;}
/* An integration page: values that come from the supplier's live API. A fixed-size placeholder holds the place until they arrive (nothing moves), and a long line stays on ONE row (the full text is its tooltip). */
.kpi .v{min-height:30px;}
.kpi .v .live-skel{display:inline-block;vertical-align:middle;width:64px;height:20px;}
.conn-tbl td{height:44px;}
.conn-tbl td .live-skel{display:inline-block;vertical-align:middle;width:140px;height:16px;}
.conn-tbl td.one-line{max-width:0;width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.tbl td.num .live-skel{display:inline-block;vertical-align:middle;width:44px;height:16px;}
/* Header action badges (e.g. the coverage "Synced" pill) stay pill-sized like every other badge. */
.page-head .pa-actions .badge{display:inline-flex;align-items:center;gap:5px;font-size:12px;min-height:24px;}
.page-head .pa-actions .badge i{font-size:13px;}
/* Section header: a section title with an action (e.g. New) at the SAME height, action right-aligned — so the
   New button sits by the title, never at the bottom of the table/list. Wraps on narrow screens. */
.sec-head{display:flex;align-items:center;justify-content:space-between;gap:10px 16px;flex-wrap:wrap;margin:28px 0 12px;}
/* With a title and ONE action, space-between reads fine. With two ("Del catálogo" + "Manual", or "Agregar
   servicios" + "Factura de proveedor") it split the row three ways and left the first button stranded in the
   middle, far from its sibling. The title claims the slack instead, so every action stays grouped at the
   right whatever their number — identical to space-between when there is only one.
   It has to live ON this rule: a separate `.sec-head > :first-child` selector is less specific than
   `.sec-head .sec-title` and its margin:0 simply won. */
.sec-head .sec-title{margin:0;margin-right:auto;}
.sec-head.flush{margin-top:0;}
@media (max-width:560px){ .iconbtn{padding:9px;} .iconbtn i{font-size:19px;} }

/* Status badges (quotes/bookings) */
/* Stage chips (SolicitudDetalle canvas): the whole path visible at once, the current one filled. A chip is
   a real button — disabled for a role without leads.edit, and aria-pressed carries the state to a reader. */
.stagechips{display:flex;flex-wrap:wrap;gap:6px;}
.stagechip{font-family:inherit;font-size:13px;font-weight:600;color:var(--muted);background:#fff;
  border:1px solid var(--line-2);border-radius:999px;padding:6px 12px;cursor:pointer;transition:background .12s,border-color .12s,color .12s;}
.stagechip:hover:not(:disabled){border-color:var(--teal,#1577B5);color:var(--teal-d,#0E4C77);}
.stagechip.on{background:var(--teal-d,#0E4C77);border-color:var(--teal-d,#0E4C77);color:#fff;}
.stagechip:disabled{opacity:.55;cursor:default;}
.st-draft{background:#EEF2F1;color:#5b6b6e;}
/* The pre-qualified lead stages (Contacted / In conversation): warm amber — being worked, but not yet
   judged worth quoting. --amber-d on --amber-50 is 7.1:1, past AA. */
.st-progress{background:var(--amber-50,#FBF1DD);color:var(--amber-d,#7A5A12);}
.st-sent{background:#E2F0F8;color:#0E4C77;}
.st-confirmed{background:#E5F3E7;color:#2A7440;}
.st-rejected{background:#FDECEC;color:#B42318;}
.st-expired{background:#FBF1DD;color:#7A5A12;}
.st-pending{background:#FBF1DD;color:#7A5A12;}
/* Invoice states. st-issued and st-paid had no rule at all, so an issued invoice and a paid one wore the
   same grey default badge — on the finance screens, of all places. Void reads as struck-through-neutral,
   never as an error: voiding is a correct, deliberate act. */
.st-issued{background:#E2F0F8;color:#0E4C77;}
.st-paid{background:#E5F3E7;color:#2A7440;}
.st-void{background:#EEF2F1;color:#8A969A;text-decoration:line-through;}

/* Quote editor layout */
/* Roomy two-column editor grid — the first (meta) column grows instead of a cramped fixed 320px sidebar. */
.qgrid{display:grid;grid-template-columns:minmax(320px,1fr) 1.7fr;gap:18px;align-items:start;}
/* Grid items default to min-width:auto, so a wide inner table (the quote/invoice lines) would force the
   column — and the whole page — wider than the viewport instead of letting the panel's overflow-x:auto
   scroll it. min-width:0 lets the column shrink so the inner scroll takes over. */
.qgrid>*{min-width:0;}
/* Two panels of the same kind of content (a form of fields beside a reconciliation summary of the same
   shape) read as one lopsided page under the 1:1.7 split above, which exists for a narrow form beside a
   WIDE table/email body. .even makes both panels the same width — SupplierInvoiceEditor's Document +
   Reconciliation pair. */
.qgrid.even{grid-template-columns:1fr 1fr;}
/* Quote details panel (QuoteEditor): the quote's own fields above the full-width services table, in rows that breathe — 24 px between
   rows and columns, labels above, hints below, every control 44 px. Six tracks: a field takes two (three to a row), .half takes three
   (Name and Email share a row), .wide takes the whole row (Agency, Show price per service, Important notes). Two to a row on a tablet,
   one on a phone. Dedicated to this panel: the shared .formgrid is not touched. */
.qmeta-head{display:flex;justify-content:space-between;align-items:center;gap:14px;flex-wrap:wrap;margin-bottom:20px;}
.qmeta-links{display:flex;gap:14px;flex-wrap:wrap;font-size:13px;}
.qdetails{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:24px;}
.qdetails > .field{grid-column:span 2;margin-bottom:0;min-width:0;}
.qdetails > .field.half{grid-column:span 3;}
.qdetails > .field.wide{grid-column:1 / -1;}
/* A label never breaks mid-word (body's word-break would), and a switch sits at the same 44 px as an input. */
.qdetails .field label{overflow-wrap:normal;word-break:normal;hyphens:none;}
.qdetails .field label.switchrow{min-height:44px;padding-top:11px;}
/* The markup control reserves the height of its hint AND of the suggestion line under the input (44 + 6 + 18 + 6 + 30), so the row below it
   never jumps when either appears. */
.qdetails .mkf{min-height:104px;}
.qdetails .mkf-row{min-height:44px;}
/* Important notes: two rows (66 px with its padding) to start with, growing with the text where the browser supports it (field-sizing), resizable everywhere. */
.qdetails .field textarea{min-height:66px;resize:vertical;field-sizing:content;}
/* A read-only free-text note (FileEditor's Important notes): one full row, line breaks kept, a long token wraps instead of overflowing. */
.note-text{white-space:pre-wrap;overflow-wrap:anywhere;font-size:14px;line-height:1.55;color:var(--ink,#0C3A5B);}
@media(max-width:1100px){
  .qdetails{grid-template-columns:repeat(4,minmax(0,1fr));}
  .qdetails > .field.half{grid-column:span 2;}
}
@media(max-width:600px){
  .qdetails{grid-template-columns:minmax(0,1fr);gap:20px;}
  .qdetails > .field,.qdetails > .field.half{grid-column:auto;}
  .qdetails > .field.wide{grid-column:1 / -1;}
}
/* overflow:visible so a searchable dropdown's popup isn't clipped by the panel (padded panels are forms/filter
   bars, never edge-to-edge tables, so they don't need the table-corner clipping that plain .panel has). */
.panel.pad{padding:18px;overflow:visible;}
/* Fields on one row bottom-align their CONTROLS, whatever their labels do. A label that wraps to two lines
   — «% de markup por defecto (aplica a servicios)» — used to push its own input a line lower than the three
   beside it, so a row of four boxes sat on two different baselines. Making each field a column that packs to
   the end means the label grows upward and the inputs stay on one line. */
.frow{display:flex;gap:12px;align-items:stretch;}
.frow > .field{display:flex;flex-direction:column;justify-content:flex-end;}
/* The label still hugs its own control; only the slack above it moves. */
.frow > .field > label{margin-top:auto;}
/* A legend under a control (.fieldnote) must NOT push its input up out of alignment with a note-less
   neighbour in the same .frow: the fields are bottom-packed, so a note in the flow lifts the input by the
   note's height. Take the note out of flow and hang it just below the input, and reserve room under the
   row so it never collides with the next one. */
.frow > .field{position:relative;}
.frow > .field > .fieldnote{position:absolute;top:100%;left:0;right:0;margin-top:4px;}
.frow:has(> .field > .fieldnote){margin-bottom:26px;}
/* A row with ONE field has nobody to line up with: its note stays in the flow (a two-line note hung out of the flow printed over the next field's label —
   «Se cuenta hacia atrás desde el primer servicio…» over «Avisar a la agencia»), and the row keeps no extra reserve. */
.frow > .field:only-child > .fieldnote{position:static;margin-top:5px;}
.frow:has(> .field:only-child > .fieldnote){margin-bottom:0;}
/* min-width:0 overrides flexbox's default min-width:auto — without it a child with wide intrinsic
   content (a wide control can't shrink below its content width)
   refuses to shrink to fit the flex row and overflows the panel instead of wrapping/clipping inside it. */
.frow .field{flex:1;min-width:0;}
/* A field with a message UNDER its input that stays in the flow (.field-msg: a hint that explains the number) would, with the bottom-packing above, leave its neighbour's input hanging at the
   bottom of the taller row — «Neto por unidad» at the top, «Markup» 100 px lower (owner 2026-11-03: inputs aligned at the TOP, always). Such a row aligns its fields at the top instead: labels on one line,
   controls on one line, the message below its own control. */
.frow:has(> .field > .field-msg){align-items:flex-start;}
.frow:has(> .field > .field-msg) > .field{justify-content:flex-start;}
.frow:has(> .field > .field-msg) > .field > label{margin-top:0;}
/* The right-hand half of a two-decision row: its control sits on the row's right edge instead of leaving a
   gap between the two. The linked-customer banner is the exception — it is a box, so it keeps the full half
   and only its button moves. */
.frow > .field.field-end{align-items:flex-end;}
/* …and the pair has to read as ONE row: labels on one line, controls on the next. The segmented control
   carries its own 16px bottom margin and stands two pixels taller than a button, so bottom-aligning the two
   dropped the right-hand label half a line below the left one. Same height, no stray margin, one row. */
/* A row whose fields carry a legend (.hint) under the control: with the fields bottom-packed the taller one pushed its
   neighbour's label down, so «Código» and «Orden de aparición» sat on two different lines. `.top` anchors the labels
   at the top of the row instead, so the labels share one line and the controls the next; the legend hangs below. Opt-in. */
.frow.top{align-items:flex-start;}
.frow.top > .field{justify-content:flex-start;}
.frow.top > .field > label{margin-top:0;}
.frow > .field > .seg{margin-bottom:0;min-height:46px;}
.frow > .field.field-end > .btn{min-height:46px;}
.frow > .field.field-end > label{align-self:stretch;text-align:right;}
.frow > .field.field-end > .cust-linked{align-self:stretch;}
/* Sidebar meta rows (quote editor): the 320px column can't hold three native date/number inputs
   side by side — each needs ~130px for the dd/mm/yyyy text plus the calendar icon, so at flex:1
   they crush together and clip. .wrap lets fields drop to the next line and gives each a real
   min-width, so Travel from / Travel to sit together and Passengers wraps below when space is tight. */
.frow.wrap{flex-wrap:wrap;}
.frow.wrap .field{flex:1 1 130px;min-width:130px;}
/* Passengers is a short count, not a date — keep it from stretching to a full-width row on its own line. */
.frow.wrap .field.pax{flex:0 1 96px;min-width:80px;}
.tbl.lines td,.tbl.lines th{padding:9px 12px;}
.cell.sm{width:84px;text-align:right;}
/* Selects never need the .sm right-alignment (that's for numbers/dates), and the custom chevron
   needs a little more breathing room than the old native arrow, so give them a touch more width —
   130px clears the longest real option text (IngestionStatus.PendingReview) plus the chevron. */
select.cell.sm{width:130px;text-align:left;}
/* Full-width search/edit inputs share the standard field-input height (11px/14.5px) — a compact table .cell is
   short on purpose, but a search bar or a wide inline input should line up with every other input in the app. */
.cell.wide{min-width:160px;padding:11px 13px;font-size:14px;border-radius:10px;min-height:44px;box-sizing:border-box;}
/* Compact inline date input for stacked ranges (Products «Publicado desde/hasta» in one column). */

/* Products catalog — READ-ONLY list (beglobber-core model): the row shows status and opens the detail page to
   edit. No inline controls, so the columns are plain and the table fits its panel at every width. */
/* The product's name is never cut: it wraps inside its cell. Capped at 215px it read «…Volcán Villar…» in a cell of 306px. */
.tbl.products td:first-child{white-space:normal;min-width:200px;}
.tbl.products td:first-child .rlink{display:inline-block;max-width:100%;white-space:normal;overflow-wrap:anywhere;vertical-align:bottom;}
/* A name that is the row's link is a tap target: 24px tall at least (it measured 19 on a phone). */
.tbl.products td:first-child .rlink{min-height:24px;line-height:24px;}
/* The supplier wraps too («<code> · Integra…» was cut at 140px). */
.tbl.products td.trunc{max-width:190px;white-space:normal;overflow-wrap:anywhere;}
.tbl.products th:nth-child(3),.tbl.products td:nth-child(3){width:92px;}
/* The whole row opens the product; the name link and caret both go there too. */
.tbl.products tr.rowlink{cursor:pointer;}
.tbl.products tr.rowlink:hover > td{background:var(--row-hover);}
.tbl.products .prov-cell.broken{color:#B42318;display:flex;align-items:center;gap:5px;}
.tbl.products .pub-cell{white-space:nowrap;font-variant-numeric:tabular-nums;}
.tbl.products .rowchev{color:var(--faint,#9AA7A6);font-size:16px;}
/* Read-only visibility flags: a flag that is ON shows in its colour; OFF is a faint outline icon. */
.vis-flags{display:inline-flex;gap:9px;align-items:center;justify-content:center;font-size:16px;color:#CDD5D4;}
.vis-flags i.on{color:var(--ocean-800,#0E4C77);}      /* Suggested */
.vis-flags i.on.star{color:#B8860B;}                  /* Featured  */
.vis-flags i.on.pay{color:#2A7440;}                   /* Pay online*/
/* Circuit net-rates «Detalle» cell: an optional free-text (often a long hotel route) truncated to one line so
   it never blows the row height; the full value is on hover (title). */
.tbl.lines td.cat-detail{max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.tbl.lines td.num .cell{display:inline-block;}
/* Safety net: an input/select dropped straight into a table cell with no class rendered as a raw browser box
   (black 1px border) — found in three modals on one day. Same look as .cell; markup should still say .cell. */
.tbl td > input:not([class]):not([type=checkbox]):not([type=radio]):not([type=hidden]):not([type=file]),
.tbl td > select:not([class]){font-family:inherit;font-size:13px;color:var(--ink,#0C3A5B);background:#fff;
  border:1px solid var(--line-2);border-radius:8px;padding:7px 9px;box-sizing:border-box;max-width:100%;}
.tbl td > input:not([class]):focus,.tbl td > select:not([class]):focus{outline:none;border-color:var(--teal,#1577B5);box-shadow:0 0 0 3px var(--teal-50,#E2F0F8);}
.totals{margin-top:14px;background:#fff;border:1px solid var(--line-2);border-radius:14px;padding:8px 18px;}
.trow{display:flex;justify-content:space-between;align-items:center;padding:9px 0;border-bottom:1px solid var(--line-2);font-size:14px;}
.trow:last-child{border-bottom:none;}
.trow span{color:var(--muted);}
.trow.grand{font-size:17px;}
.trow.grand b{color:var(--teal-d,#0E4C77);}
/* Quote/file adjustments (QuoteAdjustmentsPanel): one card like the services table, its add row inside it. */
.adj-card{overflow-x:auto;}
.adj-tbl td{vertical-align:middle;}
.adj-name{display:flex;align-items:center;gap:8px;min-width:0;}
.adj-hid{flex:none;display:inline-flex;align-items:center;gap:4px;font-size:12px;font-weight:600;color:var(--muted);padding:2px 8px;border-radius:999px;background:var(--sand,#F5EFE3);}
.adj-tbl td.wrap{min-width:220px;width:100%;}
.adj-tbl td .cell{min-height:40px;}
.adj-tbl select.cell{min-width:140px;}
.adj-tbl input.cell.sm{width:90px;text-align:right;}
.adj-tbl .adj-amt{font-weight:600;}
.adj-tbl .adj-amt.is-discount{color:#2A7440;}
.adj-add{padding:14px 16px;}
.adj-tbl + .adj-add{border-top:1px solid var(--line-2);}
.adj-add .adj-kind{flex:0 0 160px;}
.adj-add .adj-pct{flex:0 0 110px;}
/* The new adjustment's row: its description keeps a readable width (it was squeezed to 35px — «p. ej. 1…» — between four fixed
   fields), the %/USD switch is as wide as its two short options, and the row wraps before anything is crushed. */
.frow.adj-add{flex-wrap:wrap;align-items:center;}
.adj-add .field.grow{flex:1 1 220px;min-width:200px;}
.adj-add .adj-mode-f{flex:0 0 96px;}
.adjustments > .totals{margin-top:12px;}
/* Line editor: which kind of service this is — quoted with the tariffs (re-quotes itself) or manual (net typed). */
.line-kind{display:flex;align-items:center;gap:12px;padding:11px 14px;border-radius:12px;margin:0 0 16px;font-size:13px;}
.line-kind > i{font-size:20px;flex:none;}
.line-kind .grow{flex:1;min-width:0;}
.line-kind .muted{font-size:12px;margin-top:2px;}
.line-kind.is-rate{background:var(--teal-50,#E2F0F8);border:1px solid #C5E3EA;}
.line-kind.is-rate > i{color:var(--teal-d,#0E4C77);}
.line-kind.is-manual{background:var(--sand,#F5EFE3);border:1px solid var(--line-2);}
.line-kind.is-manual > i{color:var(--muted);}
.badge.line-manual{margin-left:6px;font-size:12px;}
/* A destination (leg) header inside a services table — quote editor and file components. */
/* A destination's header in a line table: a section title across the whole table — the city in bold, its dates and service count
   beside it, a rule above and below (owner 2026-10-14: adjust the design of the headers — on a phone the bar sat inset in a card). */
.tbl tr.leg-row td{background:var(--sand,#F5EFE3);padding:10px 16px;font-size:13px;color:var(--ink,#0C3A5B);border-top:1px solid var(--line-2);border-bottom:1px solid var(--line-2);}
.tbl tr.leg-row td i{color:var(--teal-d,#0E4C77);font-size:15px;}
.tbl tr.leg-row .leg-name{display:inline-flex;align-items:center;gap:6px;font-weight:700;font-size:14px;margin-right:10px;}
.tbl tr.leg-row .leg-meta{display:inline-flex;flex-wrap:wrap;gap:0 10px;color:var(--muted);font-size:12px;}
.tbl tr.leg-row .leg-meta > span + span::before{content:"·";margin-right:10px;color:#C9D2D1;}
.tbl tbody tr.leg-row:hover{background:transparent;}
/* An expanded detail row under a clickable row (IntegrationDetail's import runs: errors and notes). Same sand band
   as .leg-row so it reads as belonging to the row above; wraps, because its content is sentences, not cells. */
.tbl tr.subrow td{background:var(--sand,#F5EFE3);padding:8px 16px 10px 36px;font-size:13px;color:var(--ink,#0C3A5B);white-space:normal;border-bottom:1px solid var(--line-2);}
.tbl tr.subrow td ul{margin:4px 0 8px;padding-left:18px;}
.tbl tr.subrow td ul:last-child{margin-bottom:0;}
.tbl tr.subrow td li{margin:2px 0;overflow-wrap:anywhere;}
.tbl tbody tr.subrow:hover{background:transparent;}
/* An import run's detail (Integración › Importaciones): each block its own padded card — the notes ran flush against the row. */
/* One frame, not two: the run's row keeps the list's own white (no sand subrow behind the card — owner, 2026-10-14). */
.tbl tr.subrow.run-sub td{background:transparent;padding:12px 16px 16px;}   /* the card spans the row's own content edges */
/* Rows stack as cards below 900px and the row already pads its sides (12px 14px): the card takes none of its own, so it lines
   up with the row's text instead of sitting inset twice. */
@media (max-width:900px){.tbl tr.subrow.run-sub td{padding:10px 0 8px;}}
.run-detail{display:grid;gap:10px;padding:0;}
.run-box{background:#fff;border:1px solid var(--line-2);border-radius:10px;padding:14px 18px;}
.run-box h4{margin:0 0 8px;display:flex;align-items:center;gap:6px;font-size:12px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--muted);}
.run-box h4 .n{font-weight:600;letter-spacing:0;text-transform:none;}
.tbl tr.subrow td .run-box ul{margin:0;padding-left:18px;}
.tbl tr.subrow td .run-box li{margin:6px 0;line-height:1.5;font-size:13px;color:var(--ink,#0C3A5B);}
.run-box.is-err{border-color:#F0C9C4;background:#FDF6F5;}
.run-box.is-err h4{color:var(--danger,#B42318);}
/* Import content: one width for the form, the error and the review. */
.import-card{max-width:960px;}
.import-files{display:flex;flex-direction:column;margin-top:10px;border:1px solid var(--line-2);border-radius:10px;background:#fff;}
.import-file{display:flex;align-items:center;gap:10px;padding:6px 8px 6px 12px;border-bottom:1px solid var(--line-2);font-size:13px;}
.import-file:last-child{border-bottom:none;}
.import-file > i{font-size:18px;color:var(--teal-d,#0E4C77);}
.import-file-n{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.field textarea.code-area{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;line-height:1.5;min-height:320px;}
/* «Sobre este cliente»: three facts top-aligned, values as removable chips. */
.chip{display:inline-flex;align-items:center;gap:4px;background:var(--sand,#F5EFE3);border:1px solid var(--line-2);border-radius:999px;padding:3px 4px 3px 10px;font-size:13px;line-height:1.3;}
/* Compact activity log: one line per entry — what, who, when. */
/* The assistant's notes (AiTextFormat): paragraphs, nested bullets, bold labels — readable, not one wall of text. */
.ai-note{background:var(--sand,#F5EFE3);border:1px solid var(--line-2);border-radius:10px;padding:12px 16px;font-size:14px;line-height:1.55;color:var(--ink,#0C3A5B);}
.ai-note p{margin:0 0 6px;}
.ai-note p:last-child{margin-bottom:0;}
.ai-note ul{margin:2px 0 8px;padding-left:20px;}
.ai-note ul ul{margin:2px 0 4px;}
.ai-note li{margin:2px 0;}
.ai-note b{font-weight:700;}
.act-list{display:flex;flex-direction:column;}
.act-row{display:flex;align-items:baseline;gap:14px;padding:9px 2px;border-bottom:1px solid var(--line-2);font-size:13px;}
.act-row:last-child{border-bottom:none;}
.act-what{flex:1 1 auto;min-width:0;font-weight:600;color:var(--ink,#0C3A5B);}
.act-who{flex:0 0 auto;font-size:13px;}
.act-when{flex:0 0 auto;font-size:12px;white-space:nowrap;}
@media(max-width:640px){.act-row{flex-wrap:wrap;gap:4px 12px;}.act-what{flex-basis:100%;}}
@media(max-width:720px){.adj-add{flex-wrap:wrap;}.adj-add .field.grow{flex:1 1 100%;}}


/* Supplier-text quote builder: one card per proposed line, laid out with room to breathe. */
/* Header above the AI proposed lines: title/help on the left, selection tools on the right. */
.sqhead{display:flex;justify-content:space-between;align-items:flex-end;gap:14px;flex-wrap:wrap;margin:18px 0 12px;}
.sqhead-tools{display:flex;align-items:center;gap:10px;white-space:nowrap;}
/* The step heads of the AI quote wizard and the supplier-reply modal: a small title and its one-line explanation
   (they carried the same inline font-size/margin five times). */
.sqhead .sec-title{font-size:14px;margin:0;}
.sqhead .sec-title + .muted{font-size:12px;}
.sqhead-tools .lnk:disabled{opacity:.4;cursor:default;text-decoration:none;}

/* A labelled row whose button must sit on the input's baseline: the fields give up their bottom margin, so
   a button beside them is not left 14px lower than the box it belongs to. */
.frow.end{align-items:flex-end;}
.frow.end > .field{margin-bottom:0;}

/* Linked-client banner on the supplier-text quote customer card. */
.cust-linked{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;background:#E5F3E7;border:1px solid #BFE3C8;color:#2A7440;border-radius:10px;padding:10px 14px;font-size:13px;margin:12px 0 0;}
.cust-linked .lnk{background:none;border:none;color:var(--teal-d,#0E4C77);text-decoration:underline;cursor:pointer;font:inherit;padding:0;}

/* Travel file workspace */
.kpi .sub-amt{font-size:12px;font-weight:700;margin-top:6px;}
.kpi .sub-amt.due{color:#B42318;}
.kpi .sub-amt.ok{color:#2A7440;}
/* Neither owed nor settled: more money is recorded than the file sells for. Amber, because it is something
   to look at rather than something wrong with the customer. --amber-d is the AA-safe amber (7.0:1 on white). */
.kpi .sub-amt.warn{color:var(--amber-d,#7A5A12);}
.cf-pending{background:#EEF2F1;color:#5b6b6e;}
.cf-requested{background:#FBF1DD;color:#7A5A12;}
.cf-confirmed{background:#E5F3E7;color:#2A7440;}
.cf-declined{background:#FDECEC;color:#B42318;}
.cf-cancelled{background:#ECECEC;color:#6b6b6b;}
.payadd{display:flex;gap:8px;flex-wrap:wrap;align-items:center;padding:12px 16px;border-top:1px solid var(--line-2);}
/* width:auto overrides the base .cell rule's width:100% — without it, flex-basis:auto still resolves to
   that 100%, so a plain .cell/.cell.wide inside .payadd stretches to fill the row and stacks instead of
   sitting compact and inline (the bug behind Contacts.razor's "Add group" bar rendering as full-width
   stacked inputs instead of Markets.razor's compact single-row layout). */
.payadd .cell{flex:0 0 auto;width:auto;}
.payadd .cell.sm{width:120px;}
.payadd .cell.wide{width:260px;}

/* Standalone action bar: for a Save/Add/etc. button (or a row of only buttons) that sits alone at the
   bottom of a section, with no input fields sharing its line — e.g. "Add rate"/"Add line" table footers,
   a form's trailing Save button, or a Cancel+Confirm pair. Not for .payadd (button + inline inputs), which
   keeps its natural left-to-right reading order. */
.actionbar{display:flex;justify-content:flex-end;gap:10px;flex-wrap:wrap;}
.amt-due{color:#B42318;}
.amt-ok{color:#2A7440;}

/* Segmented switcher (products) + centered cells */
.seg{display:inline-flex;flex-wrap:wrap;gap:4px;background:#fff;border:1px solid var(--line-2);border-radius:12px;padding:4px;margin-bottom:16px;max-width:100%;}
/* Two segmented controls in a row (Leads: scope + view) are two controls, not one: they never touch. */
.seg + .seg{margin-left:12px;}
/* In a dialog a segmented choice (Hotel · Servicio · Circuito, the refund options…) takes the WHOLE width, its buttons equal (owner 2026-10-29: they should take the whole width). */
.modal .seg{display:flex;width:100%;box-sizing:border-box;}
.modal .seg > .seg-btn{flex:1 1 0;min-width:0;justify-content:center;text-align:center;}
/* The markup field's % / ÷ switch keeps its own size inside a dialog: stretched to the whole width it wrapped under the value and made this field taller than its neighbour (Net). */
.modal .mkf-mode{display:inline-flex;width:auto;}
.modal .mkf-mode > .seg-btn{flex:0 0 auto;}
.mkf-row{flex-wrap:nowrap;}
/* On small screens the tab strip scrolls horizontally instead of wrapping onto a second (misaligned) line. */
@media(max-width:640px){
  /* !important overrides the inline flex-wrap:wrap some pages set for desktop, so on mobile the strip scrolls. */
  .seg{display:flex !important;flex-wrap:nowrap !important;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none;}
  .seg::-webkit-scrollbar{display:none;}
  .seg-btn{flex:0 0 auto;white-space:nowrap;}
  /* The strip scrolled, but silently: Settings hid 292px of tabs (Payments, Integrations) behind an edge that
     looked like the end of the row, so on a phone those tabs simply did not exist. A mask fades the last few
     pixels of overflow, which reads as "there is more this way" — and it fades out once you reach the end,
     so a strip that fits shows no false hint. */
  .seg.is-scrollable{-webkit-mask-image:linear-gradient(to right,#000 calc(100% - 26px),transparent 100%);
                     mask-image:linear-gradient(to right,#000 calc(100% - 26px),transparent 100%);}
  /* .seg-fill: a two/three-way toggle (e.g. Receivable/Payable), not a strip of many tabs — on a phone it should
     span the full width with each option sharing it evenly, not scroll. Opts out of the tab-strip rules above. */
  .seg-fill{width:100%;}
}
/* A segmented FILTER (SegFilter): fills its column, one 44 px row, every option the same share, never wrapping — the few short options of a bar (channel, priority, period). */
.seg.seg-filter{display:flex;width:100%;margin:0;flex-wrap:nowrap;box-sizing:border-box;min-height:44px;height:44px;align-items:stretch;}
.seg-filter .seg-btn{flex:1 1 0;min-width:0;justify-content:center;padding:0 8px;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
@media(max-width:640px){
  .seg-fill .seg-btn{flex:1 1 0;white-space:normal;text-align:center;justify-content:center;padding-left:8px;padding-right:8px;}
  .seg.is-scrolled-end{-webkit-mask-image:none;mask-image:none;}
}
.seg-btn{border:none;background:transparent;color:var(--ink,#0C3A5B);font-family:inherit;font-size:14px;font-weight:700;padding:8px 16px;border-radius:9px;cursor:pointer;display:inline-flex;align-items:center;gap:8px;}
.seg-btn:hover{background:rgba(31,42,46,.06);}
/* White on --teal measures 3.87:1 — under AA for a 14px label. --teal-d is 5.8:1 and is the same blue the
   primary button uses, so "selected" and "primary" read as one idea. */
.seg-btn.on{background:var(--teal-d,#0E4C77);color:#fff;}
.seg-btn .cnt{font-size:12px;font-weight:700;background:rgba(0,0,0,.08);padding:1px 8px;border-radius:999px;}
.seg-btn.on .cnt{background:rgba(0,0,0,.2);}
/* Compact variant: sits inline next to a .cell search input (e.g. Customers' filter bar) — the default .seg
   is sized/margined for a standalone filter row and reads too tall/offset next to a compact .cell input. */
.seg.sm{padding:3px;margin-bottom:0;}
.seg.sm .seg-btn{padding:6px 14px;font-size:13px;}
.tbl th.ctr,.tbl td.ctr{text-align:center;}
.tbl td.ctr input[type=checkbox]{width:18px;height:18px;accent-color:var(--teal-d,#0E4C77);cursor:pointer;}

/* Customer picker */

/* A list of search RESULTS is not a list of cards. Rounded, tinted rows stacked inside an already-bordered
   panel gave each result its own box inside a box, and the two customer pickers had drifted apart anyway —
   one used .pickrow, the other a block of inline styles. Flat rows separated by a hairline, in one place. */
.pick-result{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;text-align:left;
  background:none;border:none;border-bottom:1px solid var(--line-2);padding:11px 14px;
  font-family:inherit;font-size:14px;color:var(--ink,#0C3A5B);cursor:pointer;}
.pick-result:last-child{border-bottom:none;}
.pick-result:hover{background:var(--teal-50,#E2F0F8);}
a.lead-name{display:block;cursor:pointer;}
a.lead-name:hover{color:var(--teal-d,#0E4C77);text-decoration:underline;}

/* Communication thread */
.thread{display:flex;flex-direction:column;}
.tmsg{padding:14px 16px;border-bottom:1px solid var(--line-2);}
.tmsg:last-child{border-bottom:none;}
.tmeta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:12px;margin-bottom:6px;}
.tsub{font-weight:700;font-size:14px;margin-bottom:3px;}
.tbody{font-size:14px;color:var(--ink,#0C3A5B);white-space:pre-wrap;}
.terr{margin-top:6px;font-size:13px;color:#B42318;background:#FDECEC;border-radius:8px;padding:6px 9px;}

/* The menu button's appearance — the same square as the bell beside the user menu, so the top bar has one kind of icon button.
   Base display is set at .navtoggle above. */
.navtoggle{align-items:center;justify-content:center;width:36px;height:36px;background:transparent;border:1px solid var(--line-2);cursor:pointer;color:var(--ink,#0C3A5B);font-size:18px;line-height:1;padding:0;border-radius:8px;}
.navtoggle:hover{background:rgba(31,42,46,.06);}
html.nav-collapsed .navtoggle{color:var(--teal-d,#0E4C77);}
.navscrim{display:none;}

/* Product image gallery (HotelDetail/ServiceDetail/CircuitDetail). */
.img-gallery{margin:0 0 8px;}
/* flex-wrap so the label and tools stack cleanly (tools right below the label, not flung to the far
   edge with a dead gap) on narrower panels instead of the tools clipping/crowding the label. */
.img-gallery-head{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px 20px;margin-bottom:8px;}
.img-gallery-head h3{margin:0;font-size:15px;font-weight:600;color:var(--ink,#0C3A5B);}
/* The Type picker + Upload button read as one coherent toolbar (inline, vertically centered) instead of
   two stacked block-level <label>s pinned to the corner — was the "stuck to one edge" look. */
.img-gallery-tools{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}
.img-type-pick{display:flex;align-items:center;gap:8px;font-size:13px;font-weight:600;white-space:nowrap;}
.img-type-pick .cell{width:auto;min-width:120px;}
/* The type picker is a SearchSelect (.ssel); give it a real width so it doesn't collapse to just the value. */
.img-type-pick .ssel{width:auto;min-width:170px;flex:0 0 auto;}
.img-strip{display:flex;flex-wrap:wrap;gap:12px;}
/* Each room's gallery gets its own bordered card with breathing room, instead of running straight into
   the next room separated only by a gap — reads as a set of distinct panels, not one crowded block. */
.img-card{position:relative;width:170px;margin:0;border:1px solid var(--line-2);border-radius:12px;overflow:hidden;background:#fff;}
.img-card.is-primary{border-color:var(--teal-d,#0E4C77);box-shadow:0 0 0 1px var(--teal-d,#0E4C77);}
.img-card img{display:block;width:170px;height:120px;object-fit:cover;background:var(--sand,#F5EFE3);}
.img-badge{position:absolute;top:6px;left:6px;display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:999px;background:var(--teal-d,#0E4C77);color:#fff;font-size:12px;font-weight:600;}
.img-badge i{font-size:12px;}
.img-actions{display:flex;gap:6px;padding:8px;justify-content:center;border-top:1px solid var(--line-2);}

@media(min-width:821px){
  .mgr{transition:grid-template-columns .18s ease;}
  html.nav-collapsed .mgr{grid-template-columns:68px 1fr;}
  html.nav-collapsed .side{padding:16px 8px;}
  html.nav-collapsed .side .brand{justify-content:center;padding:8px 0 18px;}
  html.nav-collapsed .side .brand span{display:none;}
  html.nav-collapsed .nav a{justify-content:center;padding:10px 0;}
  html.nav-collapsed .nav a .nl{display:none;}
  /* Collapsed rail: group titles become a thin divider line instead of text. */
  html.nav-collapsed .nav .grp{font-size:0;padding:0;height:1px;margin:9px 10px;background:rgba(255,255,255,.12);overflow:hidden;}
}

/* .qgrid.even repeated here on purpose: two classes outweigh the bare .qgrid above by specificity, so without
   this the SupplierInvoiceEditor two-panel layout (the only .qgrid.even in the codebase) never collapsed on
   tablet/phone and stayed squeezed into two columns (bug found 2026-09-17). */
@media(max-width:980px){.qgrid,.qgrid.even{grid-template-columns:1fr;}}
/* Client detail: two independently-packing columns (wide left = Overview + Timeline, narrow right = form + Activity). */
/* Single full-width column — no cramped sidebar; every panel gets the full content width. */
.cust-cols{display:flex;flex-direction:column;gap:18px;}
.cust-main{min-width:0;display:flex;flex-direction:column;gap:18px;}
.cust-side{width:100%;display:flex;flex-direction:column;gap:18px;}
/* Long request bodies in the timeline clamp to 3 lines until "Show more" is clicked. */
/* The clamp is vertical on purpose — «Ver más» opens the rest. It also has to break a long unbroken string,
   though: a pasted URL in a timeline entry was cut off SIDEWAYS on a phone, where no control reveals it. */
.tbody.clamp3{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere;}
/* «Ver más» is the only way to read a clamped timeline entry in full, and it measured 45×17 — under the
   WCAG 2.2 floor for a control that is not a link inside a sentence. */
.tl-more{margin-top:6px;display:inline-flex;align-items:center;gap:4px;background:none;border:none;padding:0;min-height:24px;color:var(--teal-d,#0E4C77);font-family:inherit;font-size:12px;font-weight:600;cursor:pointer;}
.tl-more:hover{text-decoration:underline;}
/* Settings two-column tabs: on desktop a balanced 40% / 60% split (a roomier left column). fr units (not %)
   so the columns share the space left AFTER the gap — with 40%/60% the gap overflowed the row and pushed the
   right panel against the viewport edge. Stacks on mobile via the .qgrid rule above. Single-panel tabs use
   .settings-single so a lone card doesn't stretch full width. */
@media(min-width:981px){.settings-grid{grid-template-columns:2fr 3fr;}}
.settings-single{max-width:640px;}

@media(max-width:820px){
  .mgr{grid-template-columns:1fr;}
  .topbar{padding:0 14px;}
  .navtoggle .nt-rail{display:none;}
  .navtoggle .nt-menu{display:inline;}
  .content{padding:16px;}
  /* The full-bleed header bands bleed to the .content edge with negative gutters sized for the desktop
     28px padding; at the 16px mobile padding those −28px gutters over-pull 12px past the viewport, so every
     page scrolled sideways 12px. Re-match the band gutters (and the page-head's top pull) to 16px. */
  .content > .page-head{margin:-16px -16px 20px;padding:16px;}
  .content > .listtabs{margin:0 -16px 20px;padding:0 16px;}
  /* Sidebar becomes an off-canvas drawer */
  .side{position:fixed;top:0;left:0;bottom:0;width:248px;max-width:84vw;z-index:60;transform:translateX(-100%);transition:transform .22s ease,box-shadow .22s ease,visibility 0s linear .22s;visibility:hidden;}
  /* Open: visible at once, with its shadow. Closed: hidden after the slide-out, so its links also leave the Tab order (UI-03/UI-04). */
  body.nav-open .side{transform:translateX(0);visibility:visible;transition:transform .22s ease,box-shadow .22s ease,visibility 0s;box-shadow:0 12px 40px rgba(0,0,0,.32);}
  .navscrim{display:block;position:fixed;inset:0;background:rgba(8,20,24,.5);opacity:0;visibility:hidden;transition:opacity .2s ease;z-index:55;}
  body.nav-open .navscrim{opacity:1;visibility:visible;}
  body.nav-open{overflow:hidden;}
  /* On mobile the top bar keeps just the hamburger + search icon + bell + avatar; the name/role text and
     the chevron collapse so the user menu is a tidy avatar button. */
  .usermenu .uwho,.usermenu .uchev{display:none;}
  .userchip{padding:4px;}
  /* Two tiles a row on a phone: one a row made four money tiles a screen and a half of scrolling before the first service. */
  .kpis{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;}
  .kpis > .kpi{padding:12px 14px;min-width:0;}
  .kpis > .kpi .v{font-size:18px;overflow-wrap:anywhere;}
  /* .frow defaults to flex-wrap:nowrap (its non-modal usages rely on an inline flex-wrap:wrap or explicit
     .field min-widths to stay usable at narrow widths). The bulk-action bar's .frow (Task B9) has neither —
     once its confirm state adds a warning badge + Cancel/Confirm buttons, the row overflows horizontally
     and the Confirm button becomes unreachable instead of wrapping onto its own line. */
  .bulkbar .frow{flex-wrap:wrap;}
  /* Every modal form (owner, 2026-09-16: one input per row on mobile): a paired .frow — From/To booking,
     Voucher number/date, Issue/Due date, and so on — is two inputs each a bit over 100px wide on a phone. One
     per row, full width, reads like the rest of the form instead of squinting at half a date picker. */
  /* Stacked, the row adds NO gap of its own: each field already carries its 14px, and the two together made the space after a
     field inside a row (28px) twice the space between rows (14px) — an uneven column of controls. */
  .modal-b .frow{flex-direction:column;gap:0;}
  .modal-b .frow > .field{width:100%;}
}
/* A dropdown + its action button, kept together as one unit in the bulk-action bar. */

/* Modal: brand-consistent overlay + centered card (reuses .field/.frow/.btn for the body). */
.modal-scrim{position:fixed;inset:0;background:rgba(8,20,24,.5);display:flex;align-items:flex-start;justify-content:center;padding:24px 16px;z-index:80;overflow-y:auto;}
.modal{background:#fff;border:1px solid var(--line-2);border-radius:16px;width:100%;max-width:520px;box-shadow:0 18px 50px rgba(8,20,24,.28);}
.modal-h{display:flex;align-items:center;justify-content:space-between;padding:12px 18px;border-bottom:1px solid var(--line-2);}
.modal-h h2{margin:0;font-size:16px;font-weight:700;}
/* THE close button of every dialog and drawer (73 of them): one exact 40 px square, the cross drawn at 26 px in a light weight, a soft rounded hover — it used to be a bare 22 px glyph whose hit area followed the glyph. */
.modal-h .x,.sdr-h .x,.dpk-h .x{flex:none;box-sizing:border-box;width:40px;height:40px;min-width:40px;padding:0 0 3px;margin:-4px -6px -4px 8px;display:inline-flex;align-items:center;justify-content:center;
  background:transparent;border:0;border-radius:12px;cursor:pointer;font-family:inherit;font-size:28px;font-weight:300;line-height:1;color:var(--muted,#5C6B70);transition:background .12s,color .12s;}
.modal-h .x:hover,.sdr-h .x:hover,.dpk-h .x:hover{background:rgba(31,42,46,.08);color:var(--ink,#0C3A5B);}
.modal-h .x:active,.sdr-h .x:active,.dpk-h .x:active{background:rgba(31,42,46,.14);}
.modal-h .x:focus-visible,.sdr-h .x:focus-visible,.dpk-h .x:focus-visible{outline:2px solid var(--teal,#1577B5);outline-offset:1px;}
.modal-b{padding:14px 18px;}
.modal-f{display:flex;justify-content:flex-end;gap:10px;padding:12px 18px;border-top:1px solid var(--line-2);}
/* A dialog is never taller than the window: its heading and its actions stay in view and the BODY scrolls. Before, a long
   form (a circuit rate is 976px) pushed «Guardar» below the fold of a laptop and the whole overlay scrolled instead. Only
   for the head/body/foot shape, and not on a window too short to leave the body any room. */
@media(min-height:520px){
  .modal:has(> .modal-b){display:flex;flex-direction:column;max-height:calc(100dvh - 48px);}
  .modal:has(> .modal-b) > .modal-h,.modal:has(> .modal-b) > .modal-f{flex:0 0 auto;}
  .modal:has(> .modal-b) > .modal-b{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;}
}
/* Mobile: let the modal sit near the top and use tighter padding so it feels native on a phone. */
@media(max-width:560px){
  .modal-scrim{padding:16px 10px;align-items:flex-start;}
  .modal{border-radius:14px;}
  .modal:has(> .modal-b){max-height:calc(100dvh - 32px);}
  .modal-h,.modal-b,.modal-f{padding-left:16px;padding-right:16px;}
  .modal-h h2{font-size:16px;}
}

/* Supplier-invoice reconciliation: the one number that means "look at this" — a non-zero difference, an
   unmatched line, an overdue date. Everything else on the row is neutral. */
.diff-flag{color:#B42318;font-weight:700;}

/* Supplier lifecycle actions on a file component: small inline buttons, and the penalty flag. */
.cf-actions{display:flex;gap:4px;flex-wrap:wrap;margin-top:5px;}
/* The confirmation cell is a PICKER, so a component row stays one line tall. Styled as the badge it replaces
   — same pill shape and state colour — with a caret so it reads as something you can change rather than as a
   label that happens to be clickable. Native <select> on purpose: its list escapes the table's own horizontal
   scroller, which would clip any popover drawn inside the cell. */
.cf-pick{appearance:none;-webkit-appearance:none;font-family:inherit;font-size:12px;font-weight:700;
  padding:4px 24px 4px 10px;border-radius:999px;border:1px solid transparent;cursor:pointer;max-width:100%;
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5 6 8l3.5-3.5' fill='none' stroke='%23445' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 7px center;background-size:11px;}
/* A compact control inside a table row is 34px, like .cell beside it — it measured 32. */
.cf-pick{min-height:34px;box-sizing:border-box;}
.cf-pick:hover{filter:brightness(.97);}
.cf-pick.cf-pending{background-color:#EDF0EF;color:#41545A;}
.cf-pick.cf-requested{background-color:var(--amber-50,#FBF1DD);color:var(--amber-d,#7A5A12);}
.cf-pick.cf-confirmed{background-color:var(--green-50,#E5F3E7);color:#1E6B33;}
.cf-pick.cf-declined,.cf-pick.cf-cancelled{background-color:#FDECEC;color:#9A1F14;}
.cf-penalty{font-size:12px;font-weight:700;color:#B42318;margin-top:3px;display:flex;align-items:center;gap:3px;}
/* How long a supplier has been sitting on a rate request. Quiet while it is normal, and only turns amber
   once it crosses the same threshold the chase job uses — a row that shouts on day one teaches people to
   stop reading it. */
.cf-waiting{font-size:12px;font-weight:600;color:var(--muted);margin-top:3px;display:flex;align-items:center;gap:3px;}

/* The full text of a logged message, folded away. The subject is what an operator scans; the body is what
   they need only when they are reconstructing what was actually said. */

/* Why an action is unavailable, written next to the disabled button instead of hidden in its title —
   a tooltip on a disabled button is unreachable by keyboard and silent to a screen reader.
   It rides on its own line UNDER the action row (.head-note): a sentence in a row of buttons was reading as
   a fifth button, and it stretched the row until the real ones went out of line. */
.blocked-why{display:inline-flex;align-items:center;gap:6px;font-size:13px;line-height:1.3;color:#9A5B00;
  background:#FFF7E6;border:1px solid #F5D9A3;border-radius:8px;padding:6px 10px;max-width:340px;}
.blocked-why i{font-size:14px;flex:none;}
.head-actions .head-note,.pa-actions .head-note{flex:1 0 100%;display:flex;justify-content:flex-end;}
@media(max-width:820px){.head-actions .head-note,.pa-actions .head-note{justify-content:flex-start;}}
/* The same note under the page's subtitle, when a page has a single blocked action and the reason is really
   about the record (the quote editor: «Esta cotización todavía no tiene servicios»). */
.page-head .head-why{margin-top:10px;}
.page-head .head-why .blocked-why{max-width:none;}

/* Lead detail: a named main column + rail, instead of letting seven panels of very different heights
   flow into a 2-track grid in document order (which put the short "Details" card beside a panel three
   times its height and left a hole under it). */
/* The rail GROWS with the screen (32%, floor 380px): at a fixed 340px its paired fields (Nombre | Empresa)
   and the mailing-group picker + button were squeezed while the main column had room to spare. */
.leadgrid{display:grid;grid-template-columns:minmax(0,1fr) minmax(380px,32%);gap:18px;align-items:start;}
.leadgrid .lead-main,.leadgrid .lead-rail{display:flex;flex-direction:column;gap:18px;min-width:0;}
/* Read view: the SAME two columns as the File workspace (design/Lead.dc.html and File.dc.html share them) — a
   340px rail, 16px between the columns and between the main cards, 12px between the rail cards. The edit view
   keeps the wider rail its forms need. */
.leadgrid.read{grid-template-columns:minmax(0,1fr) 340px;gap:16px;}
.leadgrid.read .lead-main{gap:16px;}
.leadgrid.read .lead-rail{gap:12px;}
@media(max-width:1100px){.leadgrid,.leadgrid.read{grid-template-columns:1fr;}}

/* Board card: a converted lead is a different KIND of card — it already produced a file — so it reads as
   one at a glance instead of only through its badge. */
.lead.is-converted{border-left:3px solid var(--green,#2A7440);}

/* The owner control sat on every card as a full-width dropdown, which made "Unassigned" the loudest thing
   on the board. It recedes to a chip until pointed at or focused; keyboard focus still shows it fully. */

/* One line under a column head saying what the stage MEANS. "Descartado" reads as destructive until you
   know nothing is deleted. */

/* Lead timeline: audit entries and communications in one list. Replaces .thread/.tmsg, which described
   only half of it. */
.tl{display:flex;flex-direction:column;gap:8px;}
.tl-row{border:1px solid var(--line,#ECEFEE);border-radius:10px;padding:9px 11px;background:#fff;}
.tl-meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:13px;}
.tl-meta{margin-bottom:2px;}
/* A timeline badge names what happened; cut at the default 200px it read «Datos de la solicitud actualiza…». */
.tl-meta .badge{max-width:none;white-space:normal;}
.tl-title{font-weight:700;font-size:14px;margin-top:8px;}
.tl-body{font-size:13px;color:var(--muted);margin-top:4px;white-space:pre-wrap;overflow-wrap:anywhere;}

/* The two stages the palette was missing. «Reserva» is sold but not yet held — amber, because it still needs
   somebody; «Cerrado» is finished and filed — deep and quiet, so it stops competing for attention. */
.st-booked{background:#FBF1DD;color:#92600a;}
.st-closed{background:#DDE6E4;color:#0C3A5B;}

/* --- My profile (/profile) -------------------------------------------------
   Two-column form on a desktop, one on a phone; the save button spans both so it never sits beside a field
   looking like it belongs to it. */
.formgrid{display:grid;grid-template-columns:1fr 1fr;gap:0 18px;}
.formgrid .field.wide{grid-column:1 / -1;}
/* Starts a fresh row in column one (see Profile.razor's password block). A no-op once the grid collapses to
   a single column. */
.formgrid .field.newrow{grid-column:1;}
/* The comment above promises the save button spans both columns, but nothing enforced it: .formfoot is a
   plain grid item, so it auto-flowed into whatever cell came next in the field count's parity (beside the
   last field on an odd count, e.g. Profile.razor's Role) instead of sitting on its own full-width row. */
.formgrid .formfoot{grid-column:1 / -1;}
@media(max-width:700px){.formgrid{grid-template-columns:1fr;}}
/* Read-only value shown where a field would be (email, role): same height and rhythm as an input so the form
   does not go ragged, but visibly not editable. */
.ro{min-height:44px;display:flex;align-items:center;padding:0 12px;border-radius:10px;background:var(--sand,#F5EFE3);color:var(--ink,#0C3A5B);font-size:14px;}
/* One line of guidance under a field. Used by the profile, the circuit rate importer and the content import —
   it was written into the markup on those pages but never actually styled. */
.nowrap{white-space:nowrap;}
/* A panel that replaces a <Loader/> keeps the loader's 180px, so the page below does not jump when a short table arrives (UI-27). */
.panel.reserve{min-height:180px;}
.not-found-title{font-size:18px;font-weight:700;margin:0 0 10px;color:var(--ink,#0C3A5B);}
.hint{font-size:12px;color:var(--muted);margin:6px 0 0;line-height:1.45;}
/* A hint that explains the row ABOVE it belongs to that row. Sitting after the row's own 14px field gap put
   «Ingrese un nombre o un email» closer to the Teléfono label below than to the Nombre box it is about; the
   row gives up its bottom margin and the hint carries the gap instead. */
.frow:has(+ .hint) > .field{margin-bottom:0;}
.frow + .hint{margin:6px 0 14px;}
/* A row of unlabeled inputs with buttons beside them (e.g. «Agregar ajuste»). .frow stretches its items, so the
   buttons grew over the fields' 14px bottom margin and stood 58px tall next to 44px inputs; with the margin gone
   every control on the row is the same 44px. */
.frow.bare > .field{margin-bottom:0;}
.ok-note{display:flex;align-items:center;gap:8px;font-size:13px;font-weight:600;color:#2A7440;border-color:#CDE6D3;background:#F3FAF5;}
.ok-note i{font-size:17px;}
/* Its counterpart: why a password change was refused. Red, because unlike the save banner this one means
   nothing happened. */
.err-note{display:flex;align-items:center;gap:8px;font-size:13px;font-weight:600;color:#A03225;border-color:#EFCFCA;background:#FDF4F3;}
.err-note i{font-size:17px;}
/* A required field left empty (or malformed) after a refused save: red box + one line under it saying what is
   wrong. The asterisk marks the field as required before anyone tries. */
.field label .req{color:#B42318;font-weight:700;}
.field.bad input,.field.bad select,.field.bad textarea{border-color:#D14343;}
.field.bad input:focus,.field.bad select:focus,.field.bad textarea:focus{border-color:#B42318;box-shadow:0 0 0 3px rgba(209,67,67,.15);}
.field .field-msg{font-size:12px;color:#A03225;margin-top:5px;line-height:1.35;}
/* The service editor's «price of ONE unit» note sits under BOTH price inputs, across the whole row (it is not inside a .field): same look as a field's note. */
.price-hint{font-size:12px;color:#A03225;margin:-4px 0 8px;line-height:1.35;}

/* --- Catalog product picker (quote builder) ------------------------------------------------------------
   A shortlist of real products the operator picks by NAME. Rows are buttons, not table cells: the whole row
   is the target, and it has to read as clickable without a "select" affordance next to it. */
.pick-results{max-height:264px;overflow-y:auto;padding:4px;margin-bottom:12px;}
.pick-row{display:flex;flex-direction:column;gap:2px;width:100%;text-align:left;background:none;border:0;
  padding:9px 11px;border-radius:9px;cursor:pointer;font:inherit;color:inherit;}
.pick-row:hover,.pick-row:focus-visible{background:var(--teal-50,#E2F0F8);outline:none;}
.pick-main{display:flex;align-items:center;gap:8px;}
.pick-sub{font-size:12px;color:var(--muted);}
/* The confirmed pick. Same two lines as a result row so the choice looks like the thing that was chosen,
   with the escape hatch parked at the end of the row rather than under it. */
.picked{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:2px 12px;padding:10px 12px;
  background:var(--sand,#F5EFE3);border:1px solid var(--line-2);border-radius:10px;}
/* Two lines at the left, «Cambiar» centred against both at the right. As a wrapping flex row the full-width second
   line pushed the button to a THIRD line of its own. */
.picked .pick-main,.picked .pick-sub{grid-column:1;min-width:0;}
.picked .btn{grid-column:2;grid-row:1 / span 2;}

/* A bar with SIX filters (Leads) could not fit them at a laptop width: five landed on the row and the sixth
   wrapped alone, then grew to the 340px cap while its five peers sat at 196px — so the second line read as a
   different control rather than the same one continued.
   `dense` trims the cap AND stops the pickers growing. Left on flex-grow:1 they expanded to fill the row and
   pushed the sixth off it regardless of their basis: the first attempt fit at 1131px of bar and wrapped again
   at 1116. Fixed at their basis, five pickers plus their gaps leave the slack to the search box and all six
   fit with room to spare. Opt-in, so the 340px cap that keeps long option labels from truncating stays the
   default everywhere else. */
/* Wider viewports ONLY. `dense` is about fitting six controls on one row, and stacked there is no row to fit.
   Unscoped it also applied on a phone, where the column layout reads a flex-basis as a HEIGHT — the very
   trap the max-width:640px block above is written to disarm. It won there not by being more specific in a way
   that mattered but by being declared later, so /leads drew its six filters as 165–200px-tall blocks with
   ~600px of empty panel between them and pushed the lead cards off the screen entirely. */
@media(min-width:641px){
  /* `dense` only trims the BASIS so six controls fit on one row; it no longer caps their width — the bar still fills edge to edge (2026-10-30). In a .grid3 bar the grid cell decides. */
  .filterbar.dense .pick{flex:1 1 165px;min-width:160px;max-width:none;}
  .filterbar.dense .grow{flex:2 1 200px;min-width:190px;}
  .filterbar.grid3.dense .pick,.filterbar.grid3.dense .grow{flex:none;max-width:none;}
}
/* A badge that IS a control (a one-click filter), not just a label. Keeps the badge's look while dropping the
   button chrome, so it does not need an inline style= in the bar. */
/* A badge that is also a BUTTON needs two things a plain badge does not: a hit target (21px tall was under
   the WCAG 24px floor) and something that says it can be clicked. The active/inactive toggles on Markups and
   Exchange rates were a badge wrapped in a bare .iconbtn — visually identical to the read-only badges in the
   next column, so nothing told a new operator that this one changes the record. */
.badge-btn{border:1px solid transparent;cursor:pointer;font:inherit;min-height:26px;transition:border-color .12s,filter .12s;}
.badge-btn:hover{border-color:currentColor;filter:brightness(.97);}
/* A badge that filters a list (the composition counters of the Circuits list): the chosen one carries a ring, and a long label is never cut. */
.badge-btn.on{border-color:currentColor;box-shadow:0 0 0 2px #fff,0 0 0 3px currentColor;}
/* The composition counters sit in their own row (no negative margin: a second line of chips never slides under the bar above). Label first, the count in a small disc at the end;
   a gap with none is quiet (grey, tick) so the ones to look at are the amber ones. */
.gapbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:4px 0 14px;}
.gapbar > b{margin-right:4px;}
.gapbar .badge{display:inline-flex;align-items:center;gap:6px;max-width:none;overflow:visible;margin-left:0;padding:3px 6px 3px 10px;}
.gapbar .badge .n{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;border-radius:999px;background:#fff;color:inherit;font-size:12px;font-variant-numeric:tabular-nums;}
.gapbar .badge.zero{background:var(--line-2);color:var(--muted);font-weight:600;}
.gapbar .badge.zero .n{background:transparent;}
.gapbar .badge.on.zero{color:var(--ink,#0C3A5B);}
/* On a phone the counters are two to a row, the label wraps inside its chip and the count stays at the right edge. */
@media (max-width:640px){.gapbar{display:grid;grid-template-columns:1fr 1fr;gap:6px;}.gapbar > b,.gapbar > .btn{grid-column:1 / -1;}.gapbar .badge{white-space:normal;justify-content:space-between;text-align:left;border-radius:12px;padding:5px 6px 5px 10px;}.gapbar .badge .lbl{flex:1;min-width:0;line-height:1.25;}.gapbar .badge .n{flex:none;}}
/* An unsaved mark on a tab: a small amber dot after its name. */
.tab-dot{display:inline-block;width:8px;height:8px;border-radius:50%;background:#e0b000;margin-left:7px;vertical-align:middle;}
/* A totals row that is a warning rather than a number — used when a quote would go out at cost. */
.totals .warn-row{color:var(--rose,#A72F5C);}
.totals .warn-row b{font-weight:600;font-size:13px;text-align:right;}

/* --- Supplier rate request (RFQ) modal ------------------------------------------------------------------
   Grouped by supplier because that is the unit that gets sent: one card = one email. A group that cannot be
   emailed is shown, not hidden — greyed and struck through with its reason — so "these three services were
   not asked about" is visible at the moment of sending rather than discovered days later. */
.rfq-group{border:1px solid var(--line-2);border-radius:12px;padding:10px 12px;margin-bottom:10px;background:#fff;}
.rfq-group.is-blocked{background:#FAF9F6;border-style:dashed;}
.rfq-group.is-blocked .rfq-head b{color:var(--muted);}
.rfq-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:4px;}
.rfq-copy{margin-left:auto;}
.rfq-head .badge.warn{margin-left:auto;}
.rfq-group .tbl.compact td{padding:5px 8px;font-size:13px;}
.rfq-group .tbl.compact tr:last-child td{border-bottom:none;}
/* Long service names wrap inside their card instead of running past the modal. */
.rfq-group{overflow-x:auto;}
.rfq-group .tbl.compact td.wrap{white-space:normal;overflow-wrap:anywhere;min-width:0;}
.rfq-assign{display:flex;align-items:center;gap:8px;margin-left:auto;flex-wrap:wrap;}
.rfq-assign-pick{min-width:240px;}
.rfq-preview{margin-top:14px;border:1px solid var(--line-2);border-radius:12px;background:#FBFAF7;}
.rfq-preview>summary{cursor:pointer;padding:11px 14px;font-weight:700;font-size:13px;color:var(--teal-d,#0E4C77);}
.rfq-preview>summary::marker{color:var(--muted);}
.rfq-preview-b{padding:0 14px 14px;}
.rfq-preview-b pre{white-space:pre-wrap;font-family:inherit;font-size:13px;line-height:1.55;color:var(--ink,#1F2A2E);
  background:#fff;border:1px solid var(--line,#ECEFEE);border-radius:9px;padding:11px 13px;margin:0;}
/* The request as plain text (mail to suppliers off): the whole text visible, scrolling inside its own box, selectable as one block. */
.rfq-preview-b pre.rfq-text{max-height:min(420px,50dvh);overflow:auto;user-select:text;}
.rfq-frame{display:block;width:100%;height:min(520px,60dvh);border:1px solid var(--line-2);border-radius:10px;background:#fff;}
/* A modal that lists things needs more width than the 460px form modals. */
.modal.wide{max-width:960px;}
/* The modal width scale (owner, 2026-10-12: they should fit well and not be too narrow) — FIVE steps, chosen by what the
   dialog holds, never an inline max-width and never narrower than 480:
     w480  a confirmation, or ONE field (a reason, a password)
     w560  a short form: up to 4 fields, at most two side by side; a search-and-pick list
     w640  a form of 5–9 fields or three side by side; anything with a table or a charge allocator
     w760  10 fields or more, four side by side, the rate and service editors
     wide  (960) dialogs that are a working table: room mix, supplier reply, rate request, day by day
   A guard fails on any other width class or a bare .modal. */
.modal.w480{max-width:480px;}
.modal.w560{max-width:560px;}
.modal.w640{max-width:640px;}
.modal.w760{max-width:760px;}
/* Supplier reply: the rows scroll inside their card instead of pushing the table's header past the modal. */
.reply-rows{overflow-x:auto;}
.reply-tbl tbody td{vertical-align:middle;}
.reply-tbl td.wrap:first-child{min-width:220px;}
.reply-tbl td.reply-pick{min-width:260px;}
.reply-tbl input.cell.sm{width:100px;}
.reply-tbl tbody tr:hover{background:transparent;}

/* Why a form is read-only, said where the read-only fields are. A disabled form with no sentence beside it
   is indistinguishable from a broken screen — which is exactly how a first-time operator reads it. */
.lockbar{display:flex;align-items:flex-start;gap:9px;margin:0 0 14px;padding:10px 13px;border-radius:10px;
  background:var(--amber-50,#FBF1DD);color:var(--amber-d,#7A5A12);font-size:13px;line-height:1.5;}
.lockbar i{font-size:17px;flex:none;margin-top:1px;}


/* A notification that navigates says so; one that only clears itself does not pretend to. */
/* width:100% because .bell-item is a column with align-items:flex-start — without it the title shrinks to
   its text and the arrow's margin-left:auto has nothing to push against. */
.bell-item .bell-item-title{display:flex;align-items:center;gap:6px;width:100%;}
.bell-item-go{margin-left:auto;font-size:13px;color:var(--muted);flex:0 0 auto;}
.bell-item.is-link:hover .bell-item-go{color:var(--teal-d);}

/* Skip link (WCAG 2.4.1): the first Tab stop on every page, visible only while focused. */
.skip-link{position:absolute;left:12px;top:-64px;z-index:200;padding:10px 16px;border-radius:8px;background:var(--teal);color:#fff;font-weight:700;text-decoration:none;transition:top .12s ease;}
.skip-link:focus{top:12px;outline:2px solid #fff;outline-offset:2px;box-shadow:0 0 0 4px var(--teal-50);}
.content:focus{outline:none;}

/* Global search. Sits in the top bar next to the bell: the two things you reach for when you arrive with a
   reference in your hand or a question about what is late. */
.gsearch{position:relative;display:flex;align-items:center;min-width:0;}
.gsearch-i{position:absolute;left:10px;font-size:15px;color:var(--faint);pointer-events:none;}
.gsearch-in{
  width:clamp(240px,26vw,340px);max-width:38vw;height:44px;padding:0 12px 0 32px;
  border:1px solid var(--line-2);border-radius:8px;background:#fff;color:var(--ink);font-size:13px;
  /* Every other field inherits the UI font; without this the search box (and its placeholder) fell back to the browser default. */
  font-family:inherit;
}
.gsearch-in:focus{outline:none;border-color:var(--teal);box-shadow:0 0 0 3px var(--teal-50);}
.gsearch-in::placeholder{color:var(--faint);}
/* The clear (×) of every search field: the browser's own is a bare bold cross hugging the border. Here a soft round chip with a thin cross, a
   24 px target with room to the right, darker on hover, the same ring as the fields on keyboard focus (owner 2026-11-03: «la x para limpiar … un estilo más moderno»).
   The chip and the cross are one SVG (a data URI), so it needs no icon font and the pseudo-element can carry it. */
input[type=search]::-webkit-search-cancel-button{
  -webkit-appearance:none;appearance:none;box-sizing:border-box;width:24px;height:24px;margin:0 2px 0 8px;border-radius:50%;cursor:pointer;
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='12' fill='%23E6EAF0'/%3E%3Cpath d='M8.5 8.5l7 7M15.5 8.5l-7 7' stroke='%235B6675' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E") center/100% no-repeat;
  transition:filter .12s ease,transform .12s ease;
}
input[type=search]::-webkit-search-cancel-button:hover{filter:brightness(.92) contrast(1.15);}
input[type=search]::-webkit-search-cancel-button:active{transform:scale(.92);}
/* Above the panel so a click outside closes it, below the panel's own content. */
.gsearch-scrim{position:fixed;inset:0;z-index:60;}
.gsearch-panel{
  position:absolute;top:44px;right:0;z-index:61;width:340px;max-width:88vw;
  max-height:420px;overflow:auto;padding:6px;box-shadow:0 6px 18px rgba(21,119,181,.10);
}
.gsearch-empty{padding:16px 12px;font-size:13px;}
.gsearch-group + .gsearch-group{margin-top:4px;border-top:1px solid var(--line);padding-top:4px;}
.gsearch-label{
  font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--faint);padding:7px 10px 3px;
}
.gsearch-item{
  display:flex;flex-direction:column;gap:1px;width:100%;text-align:left;
  padding:8px 10px;border:0;background:none;border-radius:6px;cursor:pointer;
}
.gsearch-item:hover{background:var(--teal-50);}
/* Keyboard focus needs a real indicator, not just the hover tint: teal-50 against white is about 1.1:1,
   which is a hint at best. Same outline the rest of the console uses. */
.gsearch-item:focus-visible{background:var(--teal-50);outline:2px solid var(--teal);outline-offset:-2px;}
.gsearch-t{font-size:14px;font-weight:600;color:var(--ink);}
.gsearch-s{font-size:12px;color:var(--muted);}
/* The field and its close button; the icon button and the close button only exist on a phone (below). */
.gsearch-field{position:relative;display:flex;align-items:center;min-width:0;}
.gsearch-btn,.gsearch-close{display:none;}
/* On a phone (2026-09-14, owner: «el buscador en mobile sea un icono y se abra un contenedor, bien armado y prolijo»).
   It used to be a 44px input with an off-centre icon, and its 340px results panel — anchored to that tiny box — opened
   past the left edge of the screen. Now: a real 44×44 icon button like the other top-bar actions; tapping it opens the
   search as its own container over the top bar (field full width, close, results below, dimmed page). */
@media (max-width:860px){
  .gsearch-btn{display:grid;place-items:center;width:44px;height:44px;padding:0;border:1px solid var(--line-2);
    border-radius:10px;background:#fff;color:var(--ink);font-size:19px;cursor:pointer;}
  .gsearch-btn:focus-visible{outline:2px solid var(--teal);outline-offset:2px;}
  .gsearch-field{display:none;}
  .gsearch.is-sheet .gsearch-field{display:flex;position:fixed;top:0;left:0;right:0;height:62px;gap:8px;padding:9px 12px;
    background:#fff;border-bottom:1px solid var(--line-2);z-index:72;}
  .gsearch.is-sheet .gsearch-i{left:25px;font-size:17px;}
  .gsearch.is-sheet .gsearch-in{flex:1;width:auto;max-width:none;height:44px;padding-left:38px;font-size:16px;border-radius:10px;}
  .gsearch.is-sheet .gsearch-close{display:grid;place-items:center;flex:none;width:44px;height:44px;padding:0;border:0;
    border-radius:10px;background:var(--sand,#F4EFE6);color:var(--ink);font-size:19px;cursor:pointer;}
  .gsearch.is-sheet .gsearch-scrim{background:rgba(8,20,24,.45);z-index:70;}
  .gsearch.is-sheet .gsearch-panel{position:fixed;top:62px;left:0;right:0;width:auto;max-width:none;
    max-height:calc(100dvh - 62px);border-radius:0 0 14px 14px;z-index:71;padding:6px 8px 10px;}
  .gsearch.is-sheet .gsearch-item{padding:11px 10px;min-height:44px;}
  /* The three top-bar actions read as one set: same 44×44 touch target (the bell was 38, sign-out 48×44). */
  .bellbtn{width:44px;height:44px;}
  /* The label goes at the SAME width the button is squeezed to 44px: hidden only at ≤640 it spilled 3px out of
     the 44px box between 641 and 860, and every page scrolled sideways at 768. The button keeps its name in
     title/aria-label. */
}

/* ==== UI consistency sweep (2026-09-10) ==== */
/* Nine screens write their caution banners as .formerr (scan cut off, email not configured, unmatched invoice
   lines, a charge billed twice…) and the Manager never styled it: the icon and the sentence rendered as plain
   body text, indistinguishable from the paragraph beside them. Amber like .lockbar for a caution; a real
   conflict (role=alert) goes red. */
.formerr{display:flex;align-items:flex-start;gap:9px;padding:10px 13px;border-radius:10px;border:1px solid #F5D9A3;
  background:#FFF7E6;color:#8A5A00;font-size:13px;line-height:1.5;}
.formerr i{font-size:17px;flex:none;margin-top:1px;}
.formerr[role=alert]{border-color:#F3C0BD;background:#FDECEC;color:#B42318;}
/* An empty or not-found state at page level is the same white card an empty table sits in — it was loose grey
   text on the sand background on 20 screens, next to lists whose empty state is a row inside a white panel. */
.content > .empty{background:#fff;border:1px solid var(--line-2);border-radius:14px;}
/* «Back to files» inside a not-found state inherited body colour (a{color:inherit}) — a link nobody could see. */
.empty a{color:var(--teal-d,#0E4C77);font-weight:700;}
.empty a:hover{text-decoration:underline;}
/* .tbl sets nowrap on every cell, so a long empty-state sentence stretched the table sideways instead of
   wrapping inside it. */
.tbl tbody td.empty{white-space:normal;}
/* A KPI strip followed straight by a filter bar or a panel sat flush against it (AI usage, Import monitor);
   where a page already spaces it inline the two margins collapse to the larger one. */
.kpis + .panel,.kpis + .filterbar{margin-top:16px;}
/* A segmented control used as a section's action carried its standalone 16px bottom margin into .sec-head and
   sat 8px above the title's centre line (campaign «Per-recipient results»). */
.sec-head .seg{margin-bottom:0;}
/* Key/value list (rate import detail). <dl class="kv"> had no rule: each value sat under its label with the
   browser's 40px indent. */
.kv{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:9px 22px;margin:0;font-size:14px;}
.kv dt{color:var(--muted);font-weight:600;}
.kv dd{margin:0;min-width:0;overflow-wrap:anywhere;}
@media(max-width:560px){.kv{grid-template-columns:1fr;gap:2px;}.kv dd{margin-bottom:8px;}}
/* The quote editor's header links (customer record, originating lead) inherited body colour and read as text. */
.qmeta-links a{color:var(--teal-d,#0E4C77);font-weight:700;gap:5px;}
.qmeta-links a:hover{text-decoration:underline;}
/* Selection policy: the chosen strategy card said so only through its radio. */
.pol-card{display:flex;gap:12px;align-items:flex-start;padding:12px 14px;border:1px solid var(--line,#e6e9eb);border-radius:8px;cursor:pointer;}
.pol-card.sel{background:var(--teal-50,#E2F0F8);box-shadow:inset 0 0 0 1px var(--teal-d,#0E4C77);}
/* Supplier-invoice lines: an amount needs more than .cell.sm's 84px (five-figure BRL plus the spinner). */
.si-lines input.cell.sm{width:110px;}
/* A form footer whose destructive actions sit on the left, apart from Save and the primary action. */
.formfoot .foot-left{display:flex;gap:10px;flex-wrap:wrap;margin-right:auto;}
/* An upload control inside a .field (the campaign image slots, the Settings logo) is itself a <label>, so the
   generic `.field label` caption rule — block, 13px, 600, 6px margin — beat .dropzone's flex column and
   .btn's inline-flex: the drop-zone's icon and text ran together on one line and «Replace» shrank to caption
   size. Give both their own layout back. */
.field label.dropzone{display:flex;margin-bottom:0;font-size:inherit;font-weight:inherit;}
.field label.btn{display:inline-flex;margin-bottom:0;font-size:14px;font-weight:700;}
.field label.btn.sm{font-size:12px;}
/* `.field label.switchrow` pins a lone switch to input height (42px) so it lines up with a neighbouring input;
   a LIST of switches inside a field (a service's guide languages, its operating days) inherited it too and each
   row stood 42px tall plus the set's gap — eleven rows became a 580px column. */
.field .switchset label.switchrow{min-height:0;padding-top:0;}
/* ==== end UI consistency sweep ==== */

/* ==== Controls contract (2026-09-10) ==== */
/* THE CONTROLS CONTRACT — one way to draw each control, on every screen. Read this before adding markup.
   Dropdown : <SearchSelect> (.ssel, 44px like an input) — ALWAYS (owner, 2026-10-12: «todos los dropdowns del mismo estilo»).
              In a table row or a card: Class="sm" (32px); on a board card: Class="sm bare". A plain <form method=post> posts
              its value through a hidden input beside it (Profile). NO native <select> anywhere: it opens the operating
              system's list — another look, another spacing. A guard fails if one comes back.
   Checkbox : choice / filter  = <label class="chk"><input type=checkbox> Text</label>
              on/off setting   = <label class="switchrow"><span class="switch"><input type=checkbox><span class="slider"></span></span> Text</label>
                                 (several stacked inside .switchset)
              in a table cell  = td.ctr > label.switch[title]
              row selection = bare input in th.sel / td.sel.  18px, teal, everywhere.
   Buttons  : .btn primary · .btn.ghost secondary · .btn.sm ONLY inside table rows, cards and popovers · .btn.icon
              square header icon · .iconbtn bare row icon · .danger (row delete = .iconbtn.danger + ph-trash).
              A label never wraps. .sec-head actions are regular size and sit right; page actions in .head-actions;
              .modal-f = Cancel (ghost) first, then the primary; .formfoot right-aligned, Save last.
   Forms    : .field > label + control; .frow = a row of fields; .frow.bare = buttons beside UNLABELLED inputs;
              .frow.end = LABELLED fields beside a button (it lines up with the input); read-only value .ro;
              guidance .hint / .fieldnote; error .field-msg; note under a button .action-note.
   Tables   : .panel > .tbl; th/td.num for numbers; td.act for row actions; the empty state is a row inside tbody.
   Badges   : .badge + a state class; the text comes from a label helper (FileStatusView, LeadStatusView, EnumText
              .Label) — never a raw enum name. */

/* A button label is one line. «Asignar / responsable» folding onto two lines was the Leads bulk bar's defect,
   and any .btn squeezed by its row did the same: the button grew taller than the inputs beside it. A long label
   now claims its width; the rows that hold buttons wrap instead (.head-actions, .sec-head, .formfoot, .actionbar
   already do; the modal footer is added here so a phone-width dialog wraps rather than overflowing). */
.btn{white-space:nowrap;}
.modal-f{flex-wrap:wrap;}

/* Leads bulk bar: a header row (count + «Clear selection») over the actions as equal groups. Each group is a
   .field — its action named above the dropdown — holding a .frow.bare of the dropdown and a one-line «Apply» at
   the same 44px height. 4 columns where the four fit at ~300px each (so «Grupo de mailing…» is not cut), 2 below
   that, 1 on a phone. Media queries,
   not container queries: container-type would make the bar a containing block candidate for the dropdown's
   position:fixed popup in some engines, and the popup is positioned against the viewport. */
.bulkbar{background:#FAFCFB;margin-bottom:12px;}
.bulkbar-head{display:flex;align-items:center;justify-content:space-between;gap:10px 16px;flex-wrap:wrap;
  padding-bottom:12px;margin-bottom:14px;border-bottom:1px solid var(--line-2);}
.bulkbar-tools{display:flex;flex-wrap:wrap;gap:10px;align-items:center;}
.bulkbar-head strong{display:inline-flex;align-items:center;gap:8px;font-size:15px;}
.bulkbar-head strong i{font-size:18px;color:var(--teal-d,#0E4C77);}
.bulkbar-grid{display:grid;grid-template-columns:minmax(0,1fr);gap:14px 16px;}
@media(min-width:700px){.bulkbar-grid{grid-template-columns:repeat(2,minmax(0,1fr));}}
@media(min-width:1600px){.bulkbar-grid{grid-template-columns:repeat(4,minmax(0,1fr));}}
@media(min-width:1420px){html.nav-collapsed .bulkbar-grid{grid-template-columns:repeat(4,minmax(0,1fr));}}
.bulkbar-grid > .bulk-act{margin-bottom:0;min-width:0;}
/* The ≤820px `.bulkbar .frow{flex-wrap:wrap}` rule was written for the old one-row bar; here the pair must stay
   side by side — the dropdown shrinks, the button keeps its one line. */
.bulkbar-grid .frow{flex-wrap:nowrap;}
.bulkbar-grid .frow > .btn{flex:0 0 auto;}

/* Row-selection column: as narrow as its checkbox, the box centred on the row like every other cell. */
.tbl th.sel,.tbl td.sel{width:1%;padding-right:4px;text-align:center;vertical-align:middle;}
.tbl th.sel input,.tbl td.sel input{display:block;margin:0 auto;}

/* Pick-several sets (a service's guide languages and operating days, a supplier's product types) are ticks in
   one wrapping row. They were columns of on/off switches — a switch says "setting", and eleven stacked rows made
   one field 580px tall. A .chk inside a .field keeps its own row layout: the generic `.field label` caption rule
   (block, 600 weight) otherwise wins on specificity and drops the text under the box. */
.chkset{display:flex;flex-wrap:wrap;gap:0 22px;}
/* `.field input{min-height:44px}` reached checkboxes and radios too: the box stayed 18px but claimed a 44px line,
   so a tick inside a .field stood in a row twice the height of the same tick anywhere else. */
.field input[type=checkbox],.field input[type=radio]{min-height:0;}
.field label.chk{display:flex;font-size:13px;font-weight:400;margin:8px 0;}
.chkset > .chk{margin:6px 0;}
/* The same sets as switches (owner 2026-10-29: «siempre switches»): a wrapping row, the switch level with its label, no extra field padding. */
.chkset > .switchrow,.field .chkset > label.switchrow{margin:5px 0;min-height:0;padding-top:0;}
.filterbar > .switchrow,.filters > .switchrow{min-height:44px;align-items:center;}
/* The same ticks in a grid: every option gets one equal cell, so a long label («Portugués») no longer shifts the ones after it
   and the second line starts under the first. Opt-in (.chkset.cols). */
.chkset.cols{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:0 16px;}

/* «Filtered by supplier» on the catalog lists: one class instead of the same inline flex copied three times. */

/* The parenthetical note inside a label or a section title is lighter than the label itself. 53 labels and a
   dozen titles wrote this as style="font-weight:400" (and font-size:13px); the class now carries it. */
.field label .muted{font-weight:400;}
.sec-title .muted{font-size:13px;font-weight:400;}
/* A section title that opens a padded panel sits on the panel's padding, not 28px below it. */
.panel.pad > .sec-title:first-child{margin-top:0;}

/* Every empty row in a table is the same height. The rows carried their own style="padding:18px|16px|14px"
   (59 of them); the markup no longer does, and this is the one value. */
.tbl tbody td.empty{padding:18px 16px;}

/* .frow.end with the button pushed to the right edge when the field before it is optional (the provider
   picker on the client-reply screen): with a single provider the button is alone and still sits right. */
.frow.end.right > .btn:last-child{margin-left:auto;}
/* Campaign «Send» and «Schedule» rows: the address / date keeps a readable width, the buttons stay beside it. */
.frow.camp-send{flex-wrap:wrap;}
.frow.camp-send > .field{flex:0 1 300px;min-width:200px;}

/* A status sentence shown as a read-only value (Settings: SMTP, Stripe key) — full width, icon tinted by state.
   In a .badge (max 200px) the long ones were cut mid-word. */
.ro{gap:8px;}
.ro > i{font-size:17px;flex:none;}
.ro.line-chips{flex-wrap:wrap;gap:6px;padding:6px 12px;}
.ro.is-ok > i{color:#2A7440;}
.ro.is-off > i{color:#B42318;}
/* Payment refund (inside the payment dialog): the amount keeps an amount's width beside its button. */
/* ==== end controls contract ==== */

/* ==== Long text, shown short (2026-09-10) ==== */
/* ClampText: N lines, then «…» and «Ver más». line-clamp on the inner box cuts on a whole line (a fade reads as a
   rendering fault — see .lead-msg-t); it also clamps across the <p>/<li> of an AI brief. */
.clamp-t{overflow-wrap:anywhere;}
.clamp:not(.open) .clamp-t{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:var(--clamp,6);line-clamp:var(--clamp,6);overflow:hidden;}
.clamp-more{display:inline-flex;align-items:center;gap:4px;margin-top:6px;padding:0;border:0;background:none;
  font:inherit;font-size:13px;font-weight:600;color:var(--teal-d,#0E4C77);cursor:pointer;}
.clamp-more:hover{text-decoration:underline;}
.clamp-more i{font-size:12px;}
/* «Qué se pidió» on the lead: one service per row — name, then where/when — each cut to one line while closed. */
.req-tbl{table-layout:fixed;width:100%;margin-bottom:0;}
.req-tbl th.num,.req-tbl td.num{width:110px;}
.req-tbl td{vertical-align:top;padding-top:9px;padding-bottom:9px;}
.req-t{font-size:14px;font-weight:600;color:var(--ink,#0C3A5B);line-height:1.4;}
.req-m{font-size:12px;color:var(--muted);line-height:1.4;margin-top:1px;}
.req-t.clip,.req-m.clip{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.req-label{display:block;font-size:11px;letter-spacing:1px;text-transform:uppercase;color:var(--muted);margin:16px 0 4px;}
/* ==== end long text ==== */

/* The file's payment plan (deposit / balance dates) above the payments table, and its status chips. */

/* ==== Typography + pagers (2026-09-10) ==== */
/* THE TYPE SCALE — every size in the console is one of these. A new rule picks a tier by ROLE, never a value.
   24  page title (.page-head h1)
   18  section and dialog title (.sec-title, .modal-h h2, topbar title)
   16  sub-section title (.dsec h2, a gallery head)
   14  BODY: table cells, reading text, toasts, and every full-size CONTROL — text input, <select>, textarea,
       SearchSelect (.ssel-control + its search box and options), .btn, .seg-btn, switch/checkbox labels.
       Controls share ONE height too: 44px (.btn, .field input/select, .cell.wide, .ssel-control).
   13  SECONDARY: field labels (600), captions under a list (.filter-note — the result count) or an action
       (.action-note, .blocked-why), banners (.formerr, .lockbar, .ok-note, .err-note), meta rows, chips, the
       pager, and the COMPACT controls inside a table row (.cell, select.cell, .btn.sm, .seg.sm) at 34px.
       Utility: .note (13px) — e.g. <div class="muted note">.
   12  SMALL: hints (.hint, .fieldnote, .field-msg), table headers (700, uppercase), badges/pills (700), the
       second line under a cell's main value, timestamps, card meta. Utility: .small (12px) —
       <div class="muted small">@code</div>. 12px is the floor for anything that has to be READ.
   11  EYEBROW only: uppercase + tracked labels in the page body (.band-tag, .gsearch-label, .req-label). Never
       lowercase running text, never the chrome: the sidebar group label, «Manager» under the logo, the bell
       counter and every avatar's initials read at 12 (2026-10-17, measured by the board render audit).
   Half-pixel sizes (11.5 / 12.5 / 13.5 / 14.5) had crept in for the same roles as their neighbours — a hint at
   12 beside a caption at 12.5 beside a banner at 13.5. They were first snapped here by overriding them later in
   the file, which left the originals in place and missed the rules this block did not list (.pill, the sidebar,
   the table headers, the folded sub-lines…). Since 2026-10-17 every rule DECLARES its tier where it is written;
   the lists below stay as the role map. ShellGuards (type floor) fails on a half pixel, on anything under 12px
   that is not an uppercase label, and on pill/badge/sidebar text under 4.5:1.
   Weights: 400 body · 600 labels/meta emphasis · 700 buttons, headers, badges, titles · 800 KPI figures.
   Colour: text is --ink, secondary --muted (≥5.2:1 on white AND sand), never lighter; tinted chips use their
   own dark hue (≥4.5:1 on their tint, measured). */

/* 14 — every full-size control, one size (inputs were 14.5 against 14 on the buttons beside them). */
.field input,.field textarea,.field select,.cell.wide,.filters .cell,.ssel-control{font-size:14px;}
/* 13 — compact controls in a table row: one size and one 34px height. select.cell stood 32px beside a 34px
   input.cell, and .btn.sm 31px at 12px. */
.cell, .seg.sm .seg-btn, .gsearch-in, .btn.sm, .field label.btn.sm{font-size:13px;}
.tbl td > input:not([class]):not([type=checkbox]):not([type=radio]):not([type=hidden]):not([type=file]),
.tbl td > select:not([class]){font-size:13px;min-height:34px;}
.cell{min-height:34px;box-sizing:border-box;}
.btn.sm{min-height:34px;}
/* On a phone a small button is still a finger's target: 40px (it measured 34 on «Abrir», «Este mes», «Agregar nota»). */
@media(max-width:640px){.btn.sm{min-height:40px;}.iconbtn{min-width:40px;min-height:40px;}}
/* The square header icon button was 42px in a row of 44px buttons (.head-actions centres them, so it sat 1px
   short at both edges). */
.btn.icon{width:44px;height:44px;}
/* A label beside a tick or a switch is the control's value, so it reads at control size — .chk was 13 and
   .switchrow 14 for the same role. */
.chk,.field label.chk{font-size:14px;}

/* 14 — reading text that had drifted to 13.5. */
.ai-note,.tbody,.tl-title,.req-t,.gsearch-t{font-size:14px;}
/* 13 — notes, captions, banners, meta rows (were 12.5 or 13.5 for the same roles). */
.filter-note, .action-note, .blocked-why, .lead-msg-t, .lead-msg.full, .terr, .clamp-more, .tl-more, .snap .s .sk, .formerr, .lockbar, .autherr, .ok-note, .err-note, .cust-linked, .line-kind, .import-file, .act-row, .rfq-group .tbl.compact td, .rfq-preview>summary, .field textarea.code-area{font-size:13px;}
.bulkbar-head strong{font-size:14px;}
.img-gallery-head h3{font-size:16px;}
/* 12 — small print that must still be read: was 10–11.5px in nineteen places. */
.req-m,.line-kind .muted,.bell-item-time,.badge.warn,.stage-note,.lead-top .badge,.lead-date,
.badge-type,.vtag,.badge.line-manual,.img-badge,
.cf-penalty,.cf-waiting,.htmled-hint,.kpi .soon{font-size:12px;}
/* 12 — the two numeric/decorative counters that were 10px (11px until 2026-10-17). */
.bell-badge,.nav a .soon{font-size:12px;}
/* The lead card's date was pure #000 — the only black text in a console drawn in --ink. */
.lead-date{color:var(--ink,#1F2A2E);}

/* Utilities for the two secondary tiers, so a page never writes style="font-size:…" again. */
.small{font-size:12px;line-height:1.45;}
.note{font-size:13px;line-height:1.5;}
/* A parenthetical inside ANY label is lighter than the label (the .field-scoped rule missed labels outside a
   .field — dialog pickers, Settings). */
label .muted{font-weight:400;}

/* Contrast (WCAG 2.2: 4.5:1 text, 3:1 control boundaries), measured on the actual pairs:
   - amber status text #9A6A16 on its #FBF1DD tint: 4.15:1 → --amber-d 5.59:1 (Pending / Expired / Requested);
   - a voided invoice #8A969A on #EEF2F1: 2.69:1 → --faint 4.60:1 (still reads as the quiet one);
   - an OFF switch was a #C9D4D2 track with a white knob: 1.52:1 against white, the control all but vanished. Off
     now draws the track's OUTLINE and the knob in #7D8C8A (3.51:1 on white, 3.06:1 on sand) on a pale fill — the
     familiar outlined-off / filled-on pair — so "off" is visible and still plainly not "on";
   (an excluded AI line card, since removed, used to fade its grey text to 3.62:1: excluded text keeps full strength.) */
.st-expired,.st-pending,.cf-requested{color:var(--amber-d,#7A5A12);}
.st-void{color:var(--faint,#626F73);}
.switch .slider{background:#F4F6F5;box-shadow:inset 0 0 0 1.5px #7D8C8A;}
.switch .slider::before{background:#7D8C8A;box-shadow:none;}
.switch input:checked + .slider{background:var(--teal,#1577B5);box-shadow:none;}
.switch input:checked + .slider::before{background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.28);}

/* Pagers: ONE footer for every list that can outgrow a screen — the shared <Pager> directly under the table's
   .panel, range on the left, ‹ Page X of Y › on the right (Contacts, Campaigns). In-memory lists page through
   the same component with Services/ListPaging. */
.panel + .pager{padding-top:12px;}
/* ==== end typography + pagers ==== */

/* A titled block INSIDE a panel (the lead's «Asignar proveedor», its assistant box, the wizard's trip summary):
   the 16px sub-section size of the type scale, instead of three inline sizes (13/14px). */
.sec-title.minor{font-size:16px;margin:16px 0 8px;display:flex;align-items:center;gap:6px;}
.sec-title.minor.flush{margin-top:0;}

/* Narrow tables fold a secondary column under the main one instead of scrolling sideways (Usuários, 2026-09-14): mark the secondary
   columns .col-wide and repeat their value as .tbl-sub inside the cell that keeps it. */
.tbl .tbl-sub{display:none;}
@media(max-width:1180px){
  .tbl th.col-wide,.tbl td.col-wide{display:none;}
  /* .tbl tbody td is nowrap by default (line ~508) — fine for the single Number/Name value beside it, but a
     folded value with several parts (Quotes.razor: margin + valid-until + updated) needs to wrap onto its own
     lines instead of forcing the whole row wider (found live at 375px, 2026-09-17). */
  .tbl .tbl-sub{display:block;margin-top:3px;font-size:12px;font-weight:400;color:var(--muted);white-space:normal;}
}

/* The traffic sheet (/traffic, 2026-10-10): movements grouped under a day row. The movement cell carries its icon beside the text, and the
   file and supplier cells wrap (a customer or supplier name must not hold the table wider than its panel). */
.tbl tr.daysep td{padding:9px 16px;background:var(--surf-2);font-size:13px;}
.tbl tr.daysep td > *{margin-right:8px;}
.tbl tr.daysep .muted{font-weight:400;}
.tbl.traffic td.wrap-md{white-space:normal;}
.tbl.traffic td.tnum{font-variant-numeric:tabular-nums;}
.tbl.traffic .trf-mv{display:flex;gap:10px;align-items:flex-start;}
/* What a passenger needs or the booking asked for: read before the pick-up, so it is not grey like the other sub-lines. */
.trf-care{color:var(--amber-d,#7A5A12);}
/* A sub-line with its own icon (pick-up note, children, needs): the icon in its column, the text wrapping beside it — not under it. */
.tbl.traffic .trf-line{display:flex;gap:6px;align-items:flex-start;margin-top:2px;}
.tbl.traffic .trf-line > i{font-size:14px;flex:none;margin-top:2px;}
.tbl.traffic .tbl-sub{margin-top:4px;}
.tbl.traffic .trf-mv > i{font-size:18px;color:var(--teal-d,#0E4C77);margin-top:1px;flex:0 0 auto;}
/* The arrival checklist (Traffic › Arrivals and departures): one chip per point — in order, to look at, missing. A chip carries data
   (a flight, a hotel, an amount), so unlike a pill it may wrap inside a narrow cell rather than push the table sideways. */
.tbl.traffic .ctl-list{display:flex;flex-wrap:wrap;gap:6px;}
.tbl.traffic .ctl-pt{display:inline-flex;align-items:center;gap:5px;min-height:28px;padding:3px 10px;border-radius:14px;font-size:12px;line-height:1.3;white-space:nowrap;max-width:100%;box-sizing:border-box;}
.tbl.traffic .ctl-pt .ctl-txt{flex:0 1 auto;min-width:0;max-width:280px;}   /* the text is cut with «…» at 280 px (200 on a phone); the card on hover reads it whole */
@media(max-width:640px){.tbl.traffic .ctl-pt .ctl-txt{max-width:200px;}}
.tbl.traffic .ctl-pt i{font-size:14px;flex:none;}
.tbl.traffic .ctl-pt b{font-weight:700;}
.tbl.traffic .ctl-pt.ok{background:#E5F3E7;color:#2A7440;}
.tbl.traffic .ctl-pt.warn{background:#FBF1DD;color:#7A5A12;}
.tbl.traffic .ctl-pt.err{background:#FDECEC;color:#B42318;}
.tbl.traffic a.reflink{font-weight:700;color:var(--teal-d,#0E4C77);font-variant-numeric:tabular-nums;white-space:nowrap;}
/* The file's own day-by-day (/files/{id}/schedule): time · icon · service · supplier. Below 720px the supplier drops under the service. */
.sched-day{margin-bottom:10px;padding:10px 16px 4px;}
.sched-day.today{border:2px solid var(--teal-d,#0E4C77);}
.sched-dh{display:flex;align-items:center;flex-wrap:wrap;gap:4px 10px;padding-bottom:8px;border-bottom:1px solid var(--line-2);}
.sched-dn{font-size:12px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--teal-d,#0E4C77);}
.sched-dd{margin:0;font-size:15px;font-weight:700;}
.sched-dp{display:inline-flex;align-items:center;gap:4px;font-size:13px;color:var(--ink,#0C3A5B);}
.sched-dc{margin-left:auto;font-size:12px;color:var(--muted);}
.sched-row{display:grid;grid-template-columns:48px 112px minmax(0,1fr) auto;align-items:start;gap:4px 12px;padding:8px 0;border-bottom:1px solid var(--line-2);}
.sched-row:last-child{border-bottom:0;}
.sched-time{font-weight:700;font-variant-numeric:tabular-nums;font-size:13px;padding-top:1px;}
.sched-kind{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;color:var(--muted);padding-top:2px;white-space:nowrap;}
.sched-kind > i{font-size:16px;color:var(--teal-d,#0E4C77);}
.sched-kind > i.muted{color:var(--muted);}
.sched-main{min-width:0;}
.sched-main > b{font-size:14px;}
.sched-sub{display:flex;flex-wrap:wrap;align-items:center;gap:2px 10px;margin-top:2px;font-size:12px;color:var(--muted);}
.sched-tag{display:inline-block;padding:1px 8px;border-radius:999px;background:var(--line,#EEF1F1);color:var(--ink,#0C3A5B);font-weight:600;}
.sched-tag.priv{background:#E3EEF7;color:var(--teal-d,#0E4C77);}
.sched-tag.room{background:#EAF3EA;color:#2E6B3A;}
.sched-sup{display:flex;align-items:center;gap:8px;font-size:13px;text-align:right;}
@media(max-width:720px){.sched-row{grid-template-columns:44px minmax(0,1fr);}.sched-kind{grid-column:2;}.sched-main{grid-column:1 / -1;}.sched-sup{grid-column:1 / -1;text-align:left;}}

/* A wider tier of the same fold (Products, 2026-10-09): .col-xl columns hide up to 1360px and ride under the name as .tbl-sub-xl, so a
   table whose seven columns need ~1050px still fits its panel on a laptop. Below 1180px .col-wide folds as well, and both sub-lines show. */
.tbl .tbl-sub-xl{display:none;}
@media(max-width:1360px){
  .tbl th.col-xl,.tbl td.col-xl{display:none;}
  .tbl .tbl-sub-xl{display:block;margin-top:3px;font-size:12px;font-weight:400;color:var(--muted);white-space:normal;}
}

/* ==== Responsive + class hygiene pass (2026-09-15) ==== */
/* EnrichProduct: text and «Enrich with AI» wrap onto two lines on a phone. A fixed 220px minimum beside the
   button pushed the button 42px past the screen on Hotel/Service/Circuit detail at 375. */
.frow.enrich-row{align-items:center;gap:14px;flex-wrap:wrap;}
.enrich-text{flex:1 1 220px;min-width:0;}
/* Stacked (a phone), the 220px basis is a HEIGHT: the text sat in a 220px box with a blank band before its button. */
@media(max-width:640px){.enrich-text{flex:0 0 auto;}}
/* Hotel stars beside the title (was an inline colour). */
.phstars{color:var(--amber-d,#7A5A12);}
/* Lead reply composer's assistant box (was an inline style). */
.ai-bar{border:1px solid var(--line-2);border-radius:8px;padding:10px;margin-bottom:10px;background:#FAFCFB;}
/* Rate coverage count pill (was a page <style> redefining .pill.ok/.warn for the whole document). */
.rate-count{font-size:12px;font-weight:700;padding:2px 9px;border-radius:999px;}
.rate-count.ok{background:var(--green-50,#E5F3E7);color:#2A7440;}
.rate-count.warn{background:var(--rose-50,#F7E7EE);color:var(--rose,#A72F5C);}
.rate-count.live{background:var(--teal-50,#E2F0F8);color:var(--teal-d,#0E4C77);}
/* Phone tables fold one step further than tablet: .col-mid columns hide under 640px and their value rides
   under the main cell as .tbl-sub-sm (Usuários scrolled 130px inside its panel at 375). */
.tbl .tbl-sub-sm{display:none;}
.tbl .tbl-sub-sm.diff-flag{color:#B42318;font-weight:700;}
@media(max-width:640px){
  .tbl th.col-mid,.tbl td.col-mid{display:none;}
  .tbl .tbl-sub-sm{display:block;margin-top:3px;font-size:12px;font-weight:400;color:var(--muted);white-space:normal;}
}
/* iOS Safari zooms the page into any focused text control under 16px, and every input, select and textarea
   in the console was 13–14px. !important because table-cell inputs carry a (0,6,3) selector.
   Only on a TOUCH device (pointer:coarse): the rule exists for the phone's keyboard, and applied by width alone it also blew
   every control up to 16px in a narrow desktop window, beside 13px labels (2026-10-10). */
@media(max-width:640px) and (pointer:coarse){
  input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file]):not([type=color]):not([type=hidden]), select, textarea, .field input, .field select, .field textarea, .cell, .ssel-control, .ssel-input, .gsearch-in{font-size:16px !important;}
  /* .ssel-opt (the option rows in the OPEN dropdown) was left out above — closed .ssel-control read at 16px,
     opening it dropped every option back to 14px, a size jump in the same control (2026-09-17 audit). */
  .ssel-opt{font-size:16px !important;}
}
/* Supplier-integration actions under a file line's confirmation (FileEditor): book / sync / release + the Reference sent. */

/* ── Every form row adapts to the device (owner, 2026-09-16: it should always adapt to mobile and tablets … all
   screens). .frow was a nowrap flex row, so on a phone three fields shared 300px — «Quote with AI» step 1 showed
   Name/Email/Phone as three 100px boxes and a date picker cut in half. One rule for every screen instead of one fix
   per page: on a tablet a row wraps once its fields would drop below ~200px; on a phone it is one field per row. ── */
@media(max-width:1024px){
  .frow{flex-wrap:wrap;}
  .frow > .field{flex:1 1 200px;}
  .frow.wrap .field{flex:1 1 160px;}
  /* A row that can wrap cannot hang its notes out of the flow: the note of the field above was printed over the label of the one that wrapped under it
     («Se cuenta hacia atrás…» over «Avisar a la agencia», Ajustes at 1024). From here a note is part of its field, and the fields pack from the top. */
  .frow > .field{justify-content:flex-start;}
  .frow > .field > label{margin-top:0;}
  .frow > .field > .fieldnote{position:static;margin-top:5px;}
  .frow:has(> .field > .fieldnote){margin-bottom:0;}
}
@media(max-width:640px){
  .frow{flex-direction:column;align-items:stretch;gap:0;}
  .frow > .field,.frow.wrap .field,.frow.wrap .field.pax{flex:1 1 auto;width:100%;min-width:0;}
  .frow > .field > label{margin-top:0;}
  /* Stacked, a field's note is part of the field: hung below it (so that a row's controls line up on a laptop) it was printed over the
     next field's label — «Por ahora sólo USD» under «Canal preferido». */
  .frow > .field > .fieldnote{position:static;margin-top:4px;}
  /* A heading with a note beside it («Trip summary (optional — …)») stacks instead of splitting into two columns. */
  .sec-title.minor{flex-wrap:wrap;}
  .sec-title.minor > .muted{flex-basis:100%;font-size:13px;}
}

/* The agency's own comment, shown read-only in QuoteEditor (SpecialRequests, Phase 4C, 2026-09-16) — same
   .lockbar shape, informative teal instead of the warning amber: nothing here is asking staff to fix anything. */
.lockbar.agency-note{background:var(--teal-50,#E2F0F8);color:var(--teal-d,#0E4C77);}

/* MarkupField — the one markup control (Components/Shared/MarkupField.razor): the value, a % / ÷ switch and, under
   it, the same markup written both ways with a worked example. The switch reuses .seg so it looks like every
   other segmented control in the console; it stretches to the input's height so the row reads as one control. */
/* Wraps instead of squeezing: below ~190px (a narrow grid cell) the switch drops under the value rather than
   leaving the value ~50px wide. */
.mkf-row{display:flex;flex-wrap:wrap;gap:8px;align-items:stretch;}
.mkf-input{flex:1 1 96px;min-width:0;min-height:44px;box-sizing:border-box;}
/* min-height: when the field wraps under a narrow card the toggle no longer has the 44px input beside it to stretch to, and its
   two buttons shrank to 18px. */
.mkf-mode{flex:0 0 auto;margin-bottom:0;align-items:stretch;min-height:44px;box-sizing:border-box;}
/* The ÷ glyph is small at the console's 13px button size and reads as a + — one step up, centred. */
/* Two icons of one weight and size, so the pair reads as a switch and neither glyph looks smaller than the other. */
.mkf-mode .seg-btn{min-width:42px;justify-content:center;padding:0 10px;}
.mkf-mode .seg-btn i{font-size:18px;line-height:1;}
.mkf-hint{margin-top:6px;font-size:12px;line-height:1.4;color:var(--muted);}
.mkf-hint.is-error{color:#A03225;font-weight:600;}
.mkf-input[aria-invalid="true"]{border-color:#D9A29B;}
.mkf-suggest{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:6px;font-size:13px;line-height:1.4;color:var(--ink,#0C3A5B);}

/* Roles — the permission matrix (Components/Pages/Roles.razor): one panel per area, one row per permission, a sticky save bar. */
.perm-head,.perm-group{margin-bottom:14px;}
/* The header: name + description, then the search and its switch on ONE row, both flush with the fields above (the shared .filterbar's own padding is dropped here). */
.perm-head .frow{margin-bottom:12px;}
.perm-head .hint{margin:10px 0 0;font-size:13px;line-height:1.4;}
/* One panel per area: a slim header (title, count, two small buttons), then rows of three columns — tick · what it allows · its key and kind. */
.perm-group{padding:6px 18px 8px;}
.perm-group .sec-head{margin:0;padding:10px 0 8px;border-bottom:1px solid var(--line-2);}
.perm-group .sec-title{font-size:15px;line-height:1.3;}
.perm-count{font-size:13px;font-weight:600;margin-left:6px;font-variant-numeric:tabular-nums;}
.perm-row{display:grid;grid-template-columns:18px minmax(0,1fr) minmax(190px,max-content);column-gap:14px;align-items:start;padding:8px 6px;border-top:1px solid var(--line-2);cursor:pointer;}
.perm-group .sec-head + .perm-row{border-top:0;}
.perm-row:hover{background:var(--row-hover);}
.perm-row input[type=checkbox]{width:16px;height:16px;margin:2px 0 0;accent-color:var(--teal-d,#0E4C77);cursor:pointer;}
.perm-text{display:contents;}
.perm-name{grid-column:2;font-size:14px;line-height:1.4;color:var(--ink,#0C3A5B);}
.perm-row.high .perm-name{font-weight:600;}
.perm-meta{grid-column:3;display:flex;flex-wrap:wrap;gap:6px;align-items:center;justify-content:flex-end;font-size:12px;color:var(--muted);}
.perm-meta code{font-size:12px;color:var(--ink,#0C3A5B);}
.perm-meta .badge{font-size:12px;padding:2px 8px;margin-left:0;}
@media (max-width:760px){
  .perm-row{grid-template-columns:18px minmax(0,1fr);row-gap:4px;}
  .perm-meta{grid-column:2;justify-content:flex-start;}
}
/* The save bar: one sticky footer for every long editor, so Save and Discard never scroll out of reach. `.perm-savebar` is the
   role editor's (summary on the left, actions on the right); `.formfoot.sticky` gives the same bar to a section's own footer. */
/* bottom:12px (not 0) leaves a small gap below the sticky bar so it floats just above the screen edge rather than
   sitting flush against it (owner, 2026-09-25). margin-bottom matches it so the last content clears the bar. */
.perm-savebar,.formfoot.sticky{position:sticky;bottom:12px;margin-bottom:12px;z-index:5;display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;padding:12px 16px;background:#fff;border:1px solid var(--line-2);border-radius:12px;box-shadow:0 -6px 20px rgba(20,40,45,.06);}
.formfoot.sticky{justify-content:flex-end;margin-top:18px;}
.formfoot.sticky .foot-left{margin-right:auto;}
.formfoot.sticky .foot-status{margin-right:auto;}
.formfoot.sticky .foot-actions{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-end;}

/* Supplier routing coverage (Components/Pages/Coverage.razor): destinations grouped by country (collapsed to a
   summary row, expandable to their cities) × Hotel/Service/Circuit columns. Each cell is a button that cycles
   default -> Include -> Exclude -> default; the icon+colour pair carries the four states so the grid reads at a
   glance without opening anything. */
.cov-tbl th.ctr,.cov-tbl td.ctr{text-align:center;width:92px;}
.cov-country td:first-child{cursor:pointer;}
.cov-country:hover{background:var(--row-hover);}
.cov-city td.indent{padding-left:34px;color:var(--muted);}
.cov-cell{background:transparent;border:none;cursor:pointer;padding:6px;border-radius:8px;font-size:18px;line-height:1;display:inline-flex;}
.cov-cell:hover{background:rgba(31,42,46,.08);}
.cov-cell:focus-visible{outline:2px solid var(--teal,#1577B5);outline-offset:1px;}
.cov-cell.on i{color:#2A7440;}
.cov-cell.inherit i{color:#8FCBA4;}
.cov-cell.excluded i{color:#B42318;}
.cov-cell.off i{color:#9AA6A9;}

/* ══════════════════════════════════════════════════════════════════════════════════════════════
   SHARED REDESIGN PRIMITIVES (1:1 with the design canvas). Additive — pages opt in by class name.
   ══════════════════════════════════════════════════════════════════════════════════════════════ */

/* Status / category PILL — one rounded label, six semantic tones. The canvas uses these for channels,
   account types, stages and states. Single line, truncates; a neutral grey is the default. Prefer these
   over ad-hoc inline backgrounds so a tone is "the" green, not "a" green. */
.pill{display:inline-flex;align-items:center;gap:5px;max-width:220px;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;vertical-align:middle;font-size:12px;font-weight:600;line-height:1.5;padding:3px 9px;
  border-radius:var(--r-pill,999px);background:#EEF0F2;color:var(--muted);}
.pill.ocean{background:var(--ocean-50,#E2F0F8);color:var(--ocean-800,#0E4C77);}
.pill.green{background:var(--green-50,#E5F3E7);color:var(--green,#2A7440);}
.pill.amber{background:var(--amber-50,#FBF1DD);color:var(--amber-d,#7A5A12);}
.pill.red{background:#FBE9E7;color:var(--danger);}
.pill.purple{background:var(--purple-50,#F3E7F3);color:var(--purple,#86306F);}
.pill.grey{background:#EEF0F2;color:var(--muted);}
.pill.solid{background:var(--danger);color:#fff;}            /* count badge on a tab/heading */
.pill i{font-size:13px;}
/* A pill or badge never touches the text or the control beside it (owner 2026-10-29: pills should always keep a little margin at the sides): 2 px each side
   always, and 6 px when it follows bold text or a link directly (a name and its «Con fallas» printed edge to edge). A rule with more weight (.badge.warn, .unseen-badge…) keeps its own. */
.pill,.badge{margin-inline:2px;}
:is(strong,b,a,em) + :is(.pill,.badge){margin-left:6px;}

/* Coloured status DOT — the 8–9px disc the Tablero columns and the follow-up list use before a label. */

/* Initials AVATAR — the owner/person disc. Default navy; a per-person hue is set inline via --av
   (e.g. style="--av:#6B4FA1"). Sizes: default 30px, .sm 24px, .lg 36px — initials never under the 12px floor
   (they were 11px and 9.5px on .sm), so the discs grew by 2px to hold them. */
.avatar{width:30px;height:30px;border-radius:50%;flex:0 0 auto;display:inline-flex;align-items:center;
  justify-content:center;font-size:12px;font-weight:700;color:#fff;background:var(--av,var(--ocean-800,#0E4C77));
  text-transform:uppercase;letter-spacing:.02em;}
.avatar.sm{width:24px;height:24px;font-size:12px;}
.avatar.ghost{background:#fff;color:var(--muted);border:1px dashed var(--line-2);}  /* "asignar" */

/* EMPTY / ERROR state — a centred guide that fills a panel body when there is nothing to show, instead of a
   blank box. The canvas rule: an empty state names the next step; an error state offers Reintentar. */

/* Amber inline banner for a page-level informational/maintenance notice (e.g. payment gateway off). Full-width
   card above the list, reusing the same amber tone as the stage/aviso pills. */

/* Note/callout: a .panel with a coloured left rule and a coloured heading, for a page-level warning or
   confirmation that carries a short list or sentence beneath it — more than a single line.
   .warn (amber) and .ok (green) keep the tones the hand-rolled callouts used; the heading uses .notebox-h,
   and a warning icon inside a .warn box inherits the amber so it needs no inline colour. */
.notebox-h{font-weight:600;font-size:14px;display:flex;align-items:center;gap:8px;flex-wrap:wrap;}
.notebox-h > i{font-size:18px;flex:none;}
/* The points of a notice: plain lines under the head, aligned with its text (past the icon), no browser bullets. */
.notebox-list{list-style:none;margin:8px 0 0;padding:0 0 0 26px;color:var(--muted);font-size:13px;line-height:1.5;}
.notebox-list li + li{margin-top:4px;}
.notebox.warn{border-left:3px solid #e0b000;}
.notebox.warn .notebox-h,.notebox.warn .ph-warning-circle{color:#9a6a16;}
.notebox.ok{border-left:3px solid #2c8a5a;}
.notebox.ok .notebox-h{color:#256c47;}

/* Linear TOP loader — a slim indeterminate bar for a refresh / filter change that must NOT blank the list
   (the canvas's non-destructive refresh rule). Place at the top of the panel/table being refreshed. */
.linebar{position:relative;height:2px;width:100%;overflow:hidden;background:var(--ocean-50,#E2F0F8);border-radius:999px;}
.linebar::before{content:"";position:absolute;inset:0;width:40%;background:var(--ocean-500,#1577B5);border-radius:999px;
  animation:rl-linebar 1.1s ease-in-out infinite;}
@keyframes rl-linebar{0%{left:-40%;}100%{left:100%;}}
@media (prefers-reduced-motion:reduce){.linebar::before{animation:none;left:0;width:100%;opacity:.5;}}

/* Section-title spacing modifiers — last in the file so they win over the container rules (.sec-head / .sqhead
   .sec-title, .sec-title.sub-sec) exactly as the inline style="margin…" they replace used to. */
.sec-title.flush{margin-top:0;}
.sec-title.bare{margin:0;}

/* ==== ROWS AS CARDS (2026-10-10) — a table on a phone ====
   Below 641px a table does not scroll sideways: its header row is taken out of view and each row becomes a card — the first
   cell is the card's title on its own line, every other cell a «label: value» pair that wraps, the row's actions at the end.
   The label is the column's own header (data-th, written by the script in App.razor). 33 screens scrolled sideways at 482px;
   this is one rule for all of them. A table that must stay a grid on a phone opts out with .tbl.keep-grid. */
@media(max-width:640px){
  .tbl:not(.keep-grid),.tbl:not(.keep-grid) > tbody{display:block;width:100%;}
  .tbl:not(.keep-grid) > thead{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);}
  .tbl:not(.keep-grid) > tbody > tr{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 16px;padding:12px 14px;border-bottom:1px solid var(--line-2);}
  .tbl:not(.keep-grid) > tbody > tr:last-child{border-bottom:0;}
  /* A destination header is the section's title, not a card: no card padding, its bar across the full width. */
  .tbl:not(.keep-grid) > tbody > tr.leg-row{padding:0;border-bottom:0;}
  .tbl:not(.keep-grid) > tbody > tr.leg-row > td{padding:10px 14px;}
  .tbl:not(.keep-grid) > tbody > tr.leg-row .leg-name{display:flex;margin:0 0 2px;}
  .tbl:not(.keep-grid) > tbody > tr > td{padding:0;border:0;width:auto;min-width:0;max-width:100%;text-align:left;white-space:normal;}
  /* A column the table already folds under its first cell (.col-wide / .col-xl / .col-mid → .tbl-sub) stays folded: shown again here it
     would say everything twice. */
  .tbl:not(.keep-grid) > tbody > tr > td:not(.col-wide):not(.col-xl):not(.col-mid){display:block;}
  .tbl:not(.keep-grid) > tbody > tr > td[data-th]:first-child{flex:1 0 100%;font-weight:600;}
  .tbl:not(.keep-grid) > tbody > tr > td[colspan]{flex:1 0 100%;}
  .tbl:not(.keep-grid) > tbody > tr > td[data-th]:not(:first-child)::before{content:attr(data-th) ": ";color:var(--muted);font-size:12px;font-weight:400;}
  /* The card's title is the first cell that SHOWS: where the first column is folded away (Customers: «Code»), the cell after it is the
     title — it read «Nombre: Familia Pérez», a label on the one line that needs none. */
  .tbl:not(.keep-grid) > tbody > tr > td:first-child:is(.col-wide,.col-xl,.col-mid) + td[data-th]{flex:1 0 100%;font-weight:600;}
  .tbl:not(.keep-grid) > tbody > tr > td:first-child:is(.col-wide,.col-xl,.col-mid) + td[data-th]::before{content:none;}
  /* The row's actions sit together at the end: the FIRST action cell takes the free space, the ones after it follow it. */
  .tbl:not(.keep-grid) > tbody > tr > td.act{margin-left:0;display:flex;align-items:center;gap:4px;align-self:center;}
  .tbl:not(.keep-grid) > tbody > tr > td:not(.act) + td.act,.tbl:not(.keep-grid) > tbody > tr > td.act:first-child{margin-left:auto;}
  .tbl:not(.keep-grid) > tbody > tr > td.act::before{content:none;}
  /* The actions are the card's LAST LINE, together at the right. Sharing the last line of values, they sat wherever the text ended:
     one icon at the right and the next wrapped to the left of a new line (Destinos, Agencias). */
  .tbl:not(.keep-grid) > tbody > tr:has(> td.act)::after{content:"";flex:1 0 100%;height:0;order:1;margin-top:-4px;}
  .tbl:not(.keep-grid) > tbody > tr > td.act{order:2;}
  .tbl:not(.keep-grid) > tbody > tr > td:empty{display:none;}
  /* A value that is itself a control (a select, an input in a line editor) takes the row, its label above it. */
  .tbl:not(.keep-grid) > tbody > tr > td:has(> input:not([type=checkbox]),> select,> .ssel,> .mkf){flex:1 0 100%;}
  /* …and that label is a label: on its own line over the control, never «Tipo:» hanging beside a 44px box. The control fills its cell. */
  .tbl:not(.keep-grid) > tbody > tr > td[data-th]:not(:first-child):has(> input:not([type=checkbox]),> select,> .ssel,> .mkf)::before{content:attr(data-th);display:block;margin-bottom:4px;}
  .tbl:not(.keep-grid) > tbody > tr > td:has(> input:not([type=checkbox]),> select) > input:only-of-type:not([type=checkbox]),
  .tbl:not(.keep-grid) > tbody > tr > td:has(> input:not([type=checkbox]),> select) > select:only-of-type{width:100%;}
  .tbl:not(.keep-grid) > tbody > tr:has(> td > input:not([type=checkbox]),> td > select,> td > .ssel){gap:10px 12px;align-items:flex-end;}
  /* A card has the whole line: a long name ends in «…» at the card's edge, not at the table's 200px cap. */
  .tbl:not(.keep-grid) > tbody > tr .clip,.tbl:not(.keep-grid) > tbody > tr .clamp-2{max-width:100%;}
}

/* The adjustments of a quote or a booking on a phone (.adj-tbl): a two-column grid per row — the description across, «Tipo» and
   «Valor» side by side with their labels above, then the amount it comes to with the remove button on its line (owner, 2026-10-12). */
@media(max-width:640px){
  .adj-card{overflow-x:visible;}
  .tbl.adj-tbl:not(.keep-grid) > tbody > tr{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:10px 12px;align-items:end;}
  .tbl.adj-tbl:not(.keep-grid) > tbody > tr > td.wrap{grid-column:1 / -1;min-width:0;width:auto;}
  .tbl.adj-tbl:not(.keep-grid) > tbody > tr > td.adj-kind-c > .ssel{min-width:0;width:100%;}
  .tbl.adj-tbl:not(.keep-grid) > tbody > tr > td.adj-val:has(> input){display:grid !important;grid-template-columns:minmax(0,1fr) 76px;gap:0 8px;}
  .tbl.adj-tbl:not(.keep-grid) > tbody > tr > td.adj-val:has(> input)::before{grid-column:1 / -1;}
  .tbl.adj-tbl:not(.keep-grid) > tbody > tr > td.adj-val > input.cell.sm,.tbl.adj-tbl:not(.keep-grid) > tbody > tr > td.adj-val > .ssel{width:100%;min-width:0;}
  .tbl.adj-tbl:not(.keep-grid) > tbody > tr > td.adj-amt{align-self:center;}
  .tbl.adj-tbl:not(.keep-grid) > tbody > tr > td.act{align-self:center;justify-self:end;margin-left:0;}
}

/* A numbering column («#») is not a card's title: on a phone it is left out and the next cell — the name — is the title. */
@media(max-width:640px){
  .tbl:not(.keep-grid) > tbody > tr > td[data-th="#"]:first-child{display:none !important;}
  .tbl:not(.keep-grid) > tbody > tr > td[data-th="#"]:first-child + td{flex:1 0 100%;font-weight:600;}
  .tbl:not(.keep-grid) > tbody > tr > td[data-th="#"]:first-child + td::before{content:none;}
}

/* A form row at the top of a panel that holds a table (the panel has no padding of its own): it gets the table's side margins. */
.panel > .frow.panel-row{padding:14px 16px;border-bottom:1px solid var(--line-2);}

/* The arrivals desk on a phone (.tbl.traffic.ctl): a card per party — the time as its first line, the party below it with no
   «Pasajeros:» label (the card says it), the checklist under an eyebrow, each on its own full row. The day header is one band edge to edge. */
@media(max-width:640px){
  .tbl.traffic > tbody > tr.daysep{padding:0;background:var(--surf-2);}
  .tbl.traffic > tbody > tr.daysep > td{padding:10px 14px;background:none;}
  .tbl.traffic > tbody > tr > td.wrap{flex:1 0 100%;}
  .tbl.traffic.ctl > tbody > tr{gap:6px 16px;}
  .tbl.traffic.ctl > tbody > tr > td.tnum{font-size:16px;line-height:1.2;}
  .tbl.traffic.ctl > tbody > tr > td[data-th]:not(:first-child)::before{content:none;}
  .tbl.traffic.ctl > tbody > tr > td:last-child[data-th]::before{content:attr(data-th);display:block;margin:6px 0;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);}
}

/* The file's services on a phone (.tbl.svc-lines): a card that reads like the service — its type as an eyebrow, the description as
   the title, the supplier under it, then the supplier state beside its reference, the three amounts in one even row, and the
   actions together at the end. The generic card put «Servicio» as the title and a 16px status select in the middle (owner, 2026-10-10). */
@media(max-width:640px){
  .tbl.svc-lines > tbody > tr{gap:6px 14px;align-items:center;}
  /* The card shows the type as its own eyebrow cell, so the laptop's folded copy inside the description stays out. */
  .tbl.svc-lines > tbody > tr > td.c-type.col-xl{display:block;}
  .tbl.svc-lines .tbl-sub-xl.svc-type{display:none;}
  .tbl.svc-lines > tbody > tr > td.c-type{flex:1 0 100%;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);}
  .tbl.svc-lines > tbody > tr > td.c-desc{flex:1 0 100%;font-size:14px;font-weight:600;}
  .tbl.svc-lines > tbody > tr > td.c-sup{flex:1 0 100%;font-size:13px;}
  /* The type, the title and the state pill say what they are: no «label:» in front of them. */
  .tbl.svc-lines > tbody > tr > td.c-type[data-th]::before,.tbl.svc-lines > tbody > tr > td.c-desc[data-th]:not(:first-child)::before,
  .tbl.svc-lines > tbody > tr > td.c-conf[data-th]:not(:first-child)::before{content:none;}
  .tbl.svc-lines > tbody > tr > td.c-conf{flex:1 0 100%;}
  /* The reference has its own line: sharing one with the amounts it pushed the third amount onto a row of its own. */
  .tbl.svc-lines > tbody > tr > td.c-money{flex:1 1 28%;font-weight:600;font-variant-numeric:tabular-nums;}
  .tbl.svc-lines > tbody > tr > td.c-money[data-th]:not(:first-child)::before{content:attr(data-th);display:block;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);}
  .tbl.svc-lines .cf-pick{font-size:13px !important;font-weight:700;min-height:30px;padding:3px 24px 3px 10px;}
}

/* The file's services on a laptop: the type rides above the description as a small eyebrow, and the two text columns give back 30px each. */
@media(max-width:1360px){
  .tbl.svc-lines .tbl-sub-xl.svc-type{margin:0 0 2px;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;}
  .tbl.svc-lines td.wrap{min-width:150px;}
}

/* ── PRINT (2026-10-10) ──────────────────────────────────────────────────────────────────────────────────────────────────
   A list on paper: the traffic sheet is what is taken to the airport. Nothing printed before — the page came out with the
   sidebar, the filters and half the columns folded away. On paper: no menu, no top bar, no filters, tabs or buttons; every
   column of a table shown as a column (the folded sub-lines hide instead); a row never split across pages; chips keep their
   colour. `.print-only` is for a line that says what the sheet is, since the filters that said it are gone. */
.print-only{display:none;}
@page{size:A4 landscape;margin:12mm;}
@media print{
  html,body{background:#fff !important;}
  .side,.topbar,.navscrim,.skip-link,.filterbar,.filter-note,.listtabs,.head-actions,.pa-actions,.linebar,.pager,.toast-stack,#blazor-error-ui,#components-reconnect-modal{display:none !important;}
  .mgr{display:block !important;min-height:0;}
  .main{display:block;}
  .content{padding:0 !important;background:#fff !important;}
  .content > .page-head{margin:0 0 8px !important;padding:0 !important;border:none !important;background:#fff !important;min-height:0 !important;}
  .page-head .sub{display:none;}
  .print-only{display:block !important;}
  /* What is the screen's and not the paper's: anything marked so, a row's action buttons, and a voided movement (a statement sent to a
     client lists what stands). */
  .no-print,.tbl th.act,.tbl td.act,.tbl tr.is-void{display:none !important;}
  .kpis{display:grid !important;grid-template-columns:repeat(3,minmax(0,1fr)) !important;gap:8px !important;}
  .kpi{border:1px solid #ccc !important;padding:8px 10px !important;}
  .kpi .v{font-size:16px !important;}
  .sec-title{margin:14px 0 6px !important;font-size:14px !important;}
  .print-head{margin:0 0 12px;font-size:13px;font-weight:700;}
  .panel{border:none !important;box-shadow:none !important;border-radius:0 !important;}
  .tbl{font-size:12px;}
  .tbl thead{display:table-header-group;}
  .tbl tr{break-inside:avoid;page-break-inside:avoid;}
  .tbl th.col-wide,.tbl td.col-wide,.tbl th.col-xl,.tbl td.col-xl{display:table-cell !important;}
  .tbl .tbl-sub,.tbl .tbl-sub-xl{display:none !important;}
  .tbl td,.tbl th{padding:6px 8px !important;}
  a{color:inherit !important;text-decoration:none !important;}
  *{-webkit-print-color-adjust:exact;print-color-adjust:exact;}
}

/* «Supplier's answer» (SupplierAnswerModal): one block per service — its name, the three-way choice, the reference when confirmed. */

/* A list row on a phone (≤640px) is a CARD with a fixed order — who and stage · reference · what it is · money · dates
   (Quotes.razor, Files.razor). The laptop's folded lines (.rowwide) stay for the widths that use them. */
.rowcard{display:none;}
@media(max-width:640px){
  .rowcard{display:flex;flex-direction:column;gap:5px;font-weight:400;}
  .rowcard + .rowwide{display:none;}
  .rowcard .rc-top{display:flex;align-items:center;justify-content:space-between;gap:10px;}
  .rowcard .rc-who{font-size:15px;font-weight:700;min-width:0;overflow-wrap:anywhere;}
  .rowcard .rc-top .badge{flex:none;}
  .rowcard .rc-ref{font-size:13px;color:var(--muted);display:flex;flex-wrap:wrap;gap:4px 6px;align-items:center;}
  .rowcard .rc-what{font-size:14px;}
  .rowcard .rc-money{display:flex;flex-wrap:wrap;gap:2px 18px;font-size:14px;}
  .rowcard .rc-k,.rowcard .rc-sub{font-size:12px;color:var(--muted);}
  .rowcard .rc-sub{display:flex;flex-wrap:wrap;gap:2px 14px;}
}

/* Saved texts: the preview column shares the table's free width on a laptop (max-width:0 lets it shrink); in a phone card that
   same rule left it one word wide — there it takes the card's line. */
.tbl td.st-prev{max-width:0;}
@media(max-width:640px){.tbl:not(.keep-grid) > tbody > tr > td.st-prev{max-width:100%;flex:1 0 100%;}}

/* An adjustment's row on a laptop: the value and its %/USD switch share the cell on one line; the kind has room for «Descuento». */
.tbl.adj-tbl td.adj-val > .ssel.adj-mode{display:inline-block;vertical-align:middle;margin-left:6px;}
.tbl.adj-tbl td.adj-val > input.cell.sm{vertical-align:middle;}
.tbl.adj-tbl td.adj-kind-c > .ssel{min-width:132px;}

/* Nothing is squeezed to make room (owner, 2026-10-12: the content should not be cramped, everything clearly visible): a state pill or a
   badge keeps its whole word in a flex row — the text beside it wraps instead («a pagar 300» and «Oculto» were cut). */
.badge,.pill{flex-shrink:0;}
/* A FILTER's options on a phone wrap onto a second line, all in sight; the sideways strip hid «En disputa» and «Pagada» off the
   panel's edge. (Tab-like strips keep their scroll.) */
@media(max-width:640px){
  .seg.sm{flex-wrap:wrap !important;overflow-x:visible;}
}

/* A dropdown in a TABLE ROW is the row's size — 34px, like the .cell inputs beside it — on a laptop, whether or not the page asked for
   «sm»: the supplier invoice's «cargo coincidente» stood 44px tall between two 34px fields. (On a phone the row is a card and every
   control takes the touch height.) */
@media(min-width:641px){
  .tbl td > .ssel .ssel-control{padding:5px 10px;font-size:13px;border-radius:8px;min-height:34px;}
}

/* The charges a payment settles, as cards on a narrow dialog (owner, 2026-10-12: align the inputs well … the whole
   content should be balanced): the charge's name on its own line, then ONE line — what is owed at the left, «Aplicar» and its box at
   the right edge. The generic card put the label above a 120px box stranded at the left, with half the card empty. */
@media(max-width:640px){
  .tbl.alloc-tbl:not(.keep-grid) > tbody > tr{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:8px 12px;}
  .tbl.alloc-tbl:not(.keep-grid) > tbody > tr > td:first-child{grid-column:1 / -1;}
  .tbl.alloc-tbl:not(.keep-grid) > tbody > tr > td:nth-child(3){display:flex !important;align-items:center;justify-content:flex-end;gap:8px;flex:none;}
  .tbl.alloc-tbl:not(.keep-grid) > tbody > tr > td[data-th]:nth-child(3):has(> input)::before{content:attr(data-th);display:inline;margin:0;}
  .tbl.alloc-tbl:not(.keep-grid) > tbody > tr > td:nth-child(3) > input.cell{width:120px !important;flex:none;}
}
/* In a dialog between a phone and a tablet (481–640px) two short fields of a row still fit side by side: stacked one per line they
   made a long, left-heavy column of full-width boxes holding «320» and a date. Below 481px they stack. */
@media(min-width:481px) and (max-width:640px){
  .modal-b .frow{flex-direction:row;flex-wrap:wrap;gap:0 16px;align-items:flex-start;}
  .modal-b .frow > .field{flex:1 1 calc(50% - 8px);width:auto;min-width:0;}
  .modal-b .frow > .field:has(> textarea){flex-basis:100%;}
}

/* A row's selection checkbox on a card (owner, 2026-10-12: arrange and align the checkboxes properly). The row is a wrapping flex
   line aligned on the text BASELINE, and a checkbox's baseline is its bottom edge: it rode above the reference beside it. The
   box is centred on the first line, and the cell after it is the card's title — bold, no «Referencia:» label, the rest of the line. */
@media(max-width:640px){
  .tbl:not(.keep-grid) > tbody > tr > td:first-child:has(> input[type=checkbox]:only-child){flex:none;align-self:center;display:flex !important;align-items:center;font-weight:400;}
  .tbl:not(.keep-grid) > tbody > tr > td:first-child:has(> input[type=checkbox]:only-child) > input{margin:0;}
  /* The title takes the rest of the first line (so a reference is never broken in two) and the values start on the next. */
  .tbl:not(.keep-grid) > tbody > tr > td:first-child:has(> input[type=checkbox]:only-child) + td[data-th]{flex:1 0 calc(100% - 44px);min-width:0;font-weight:600;}
  .tbl:not(.keep-grid) > tbody > tr > td:first-child:has(> input[type=checkbox]:only-child) + td[data-th]::before{content:none;}
  /* A stage with its note (the quote's number) is one line on a card: label, pill, note — the note hung alone under the pill. */
  .tbl:not(.keep-grid) > tbody > tr > td:has(> .stagecell){display:flex !important;align-items:center;gap:6px;flex-wrap:wrap;}
  .tbl:not(.keep-grid) > tbody > tr > td > .stagecell{display:inline-flex;flex-direction:row;align-items:center;gap:6px;flex-wrap:wrap;}
  .tbl:not(.keep-grid) > tbody > tr > td > .stagecell > .stage-note{margin:0;}
}

/* One control height per device (owner, 2026-10-12: check that everything is properly aligned). On a phone the 16px text makes a text
   box 46px tall while a date box stayed pinned at the laptop's 44px and a dropdown at its own height: two fields side by side did
   not share a top and a bottom. Every field control is 46px there. */
@media(max-width:640px){
  .field input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file]),.field select,.ssel-control,.cell.wide{min-height:46px;box-sizing:border-box;}
  .tbl.alloc-tbl:not(.keep-grid) > tbody > tr > td:nth-child(2){align-self:center;}
}

/* A dialog that lists and then adds (an agency's team): the «add» part opens with its own small heading and a
   one-paragraph explanation, then ordinary labelled fields — never bare inputs. */

/* A control never falls back to the system typeface: a button or input whose own rule forgot `font-family` drew its
   text in Arial/Helvetica beside the app's — seen on the file's «+ Servicio» chips (2026-10-12). */
button,input,select,textarea{font-family:inherit;}

/* Laptop density (2026-10-12): between a phone card and a wide desk the columns keep their text and give up side padding
   (16 → 10px between columns; the card's two edges keep 16). Six-to-nine-column tables — a file's movements, ageing —
   were 5–60px wider than their card at 1280 and scrolled sideways inside it. */
@media(min-width:641px) and (max-width:1366px){
  .tbl thead th,.tbl tbody td{padding-left:10px;padding-right:10px;}
  .tbl thead th:first-child,.tbl tbody td:first-child{padding-left:16px;}
  .tbl thead th:last-child,.tbl tbody td:last-child{padding-right:16px;}
  /* A long heading takes two lines before it pushes the table out of its card («PROVEEDORES CONSULTADOS», «TODAVÍA NO VENCE»). */
  .tbl thead th{white-space:normal;line-height:1.25;vertical-align:bottom;}
}

/* Bulk AI enrichment («Enrich with AI these N»): the run's progress on the product list and in its report. A bar, the
   counts on one wrapping line, and the run's own actions on the right. */
.enrich-meter{height:8px;border-radius:999px;background:var(--line-2);overflow:hidden;margin:12px 0 10px;}
.enrich-meter > span{display:block;height:100%;background:var(--teal);transition:width .4s ease;}
.enrich-facts{display:flex;flex-wrap:wrap;gap:4px 16px;color:var(--muted);font-size:13px;}
.enrich-reason{margin:8px 0 0;color:var(--muted);font-size:13px;line-height:1.5;}
.enrich-actions{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end;margin-top:12px;}
.enrich-warn{color:#9a6a16;line-height:1.45;}
.enrich-warn + .muted,.enrich-warn + div{margin-top:4px;}

/* The icon tooltip (App.razor script): the control's own `title`, drawn at once beside it. 12 px is the floor of what is read; it never takes a click. */
.rl-tip{position:fixed;z-index:10000;max-width:260px;padding:6px 10px;border-radius:8px;background:var(--ink,#0C3A5B);color:#fff;font-size:12px;font-weight:600;line-height:1.35;pointer-events:none;box-shadow:0 6px 18px rgba(12,58,91,.28);}
.rl-tip.long{max-width:360px;font-weight:500;line-height:1.45;}   /* a sentence of explanation (data-tip), not a name */

/* LONG TEXT IN A LIST (owner 2026-10-30: titles and names end in «…» and, on hover, show the FULL text — a custom card, never the browser's `title`).
   .clip     one line, then «…» (a name, a title, a subject, an e-mail). In a table cell it caps at --clip-w (default 320px; 200px on a phone), so one 220-character
             name no longer stretches its column and the row stays one line high; in a flex row or any block it follows its box (max-width:100%, min-width:0).
   .clamp-2  two lines, then «…» (a description, a note). Wraps inside a table cell that is otherwise nowrap.
   The card itself is drawn by the script in App.razor, only when the text really is cut (scrollWidth/scrollHeight), and reads the element's own text (or data-full). */
:root{--clip-w:320px;--clip-w-sm:200px;--clamp-w:360px;--clamp-w-sm:240px;}
.clip{display:block;max-width:100%;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.req-tbl .clip{max-width:100%;}   /* a fixed-layout table: the column already caps the text */
.clip.inl{display:inline-block;vertical-align:bottom;}   /* inline: a badge or a pill may follow the name on the same line */
.tbl .clip{max-width:var(--clip-w,320px);}
/* Tighter caps for a table whose long names must still fit a 1280 px laptop without a sideways scroll (Products): the name 260, the destination 200. */
.tbl .clip.clip-md{--clip-w:260px;}
.tbl .clip.clip-sm{--clip-w:200px;}
.clamp-2{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;overflow:hidden;overflow-wrap:anywhere;white-space:normal;min-width:0;max-width:100%;}
.tbl .clamp-2{min-width:160px;max-width:var(--clamp-w,360px);}
@media(max-width:640px){.tbl .clip{max-width:var(--clip-w-sm,200px);}.tbl .clamp-2{min-width:140px;max-width:var(--clamp-w-sm,240px);}}
.rl-clip-tip{position:fixed;z-index:10000;max-width:420px;max-height:50vh;overflow:auto;padding:10px 13px;background:#fff;color:var(--ink,#0C3A5B);border:1px solid var(--line-2);border-radius:var(--r-card,12px);box-shadow:0 12px 32px rgba(21,51,61,.18);font-size:13px;font-weight:400;line-height:1.45;white-space:normal;overflow-wrap:anywhere;user-select:text;-webkit-user-select:text;cursor:text;}

/* Hotel «Details» tab (HotelDetail): the amenities as a tidy list (one column on a phone, up to four on a laptop) and the places near the hotel as one line each. */
.amen-list{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:8px 20px;font-size:14px;}
.amen-list li{display:flex;align-items:flex-start;gap:8px;min-width:0;line-height:1.4;}
.amen-list li > i{flex:none;color:var(--green,#2A7440);font-size:16px;margin-top:2px;}
.amen-list li > span{min-width:0;overflow-wrap:anywhere;}
.amen-more{margin-top:14px;}
.place-list{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:4px 24px;font-size:14px;}
.place-list li{display:flex;align-items:center;gap:10px;min-width:0;padding:7px 0;border-bottom:1px solid var(--line-2);}
.place-list li > i{flex:none;color:var(--teal-d,#0E4C77);font-size:18px;width:22px;text-align:center;}
.place-list .pl-n{flex:1;min-width:0;overflow-wrap:anywhere;}
.place-list .pill{flex:none;}
.kv dd .iconbtn{vertical-align:middle;margin-left:6px;}
.amen-group{font-size:13px;font-weight:700;color:var(--muted);text-transform:none;margin:16px 0 8px;}
.amen-group:first-child{margin-top:0;}
/* Adjustments dialog: the value and its unit (% / currency) side by side, at the height of the field next to them. */
.adj-valrow{display:flex;gap:8px;align-items:stretch;}
.adj-valrow > input{flex:1 1 0;min-width:0;}
.adj-valrow > *:last-child{flex:0 0 96px;}
/* Dialog text stays at the console's body size: labels 13 px, explanations and notes 13 px, nothing bigger than the 14 px of the controls. */
.modal-b .field > label,.modal-b .field > .lbl{font-size:13px;margin-bottom:4px;}
.modal-b .muted,.modal-b .hint,.modal-b .fieldnote,.modal-b p{font-size:13px;line-height:1.45;}
.modal-b .field{margin-bottom:10px;}
/* A note under a field inside a dialog: one small muted block with its icon on the first line, close under the control. */
.modal-b .field > .fieldnote{display:flex;align-items:flex-start;gap:6px;margin-top:6px;font-size:12px;line-height:1.4;color:var(--muted);}
.modal-b .field > .fieldnote > i{flex:none;font-size:14px;line-height:1.4;}
/* The quote's acts in the file screen's rail (QuoteEditor in Bare mode): one tidy card — the worded act on its own full-width row, the lifecycle icons under it, then the quote's tools and its settings. */
.rail-quote{display:flex;flex-direction:column;gap:8px;margin-bottom:8px;}
.rail-quote .page-head.qe-embed-head{margin:0;justify-content:flex-start;}
.rail-quote .head-actions{display:flex;flex-wrap:wrap;align-items:center;gap:6px;width:100%;margin:0;}
.rail-quote .head-actions > .btn:not(.icon){order:-1;flex:1 0 100%;}
.rail-quote .head-actions > .btn.icon{width:40px;height:40px;min-height:0;}
.rail-quote .qtoolbar{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin:0;padding-top:8px;border-top:1px solid var(--line-2);}
.rail-quote .qtoolbar > .btn.icon{width:40px;height:40px;min-height:0;}
.rail-quote .qtoolbar > .tb-sp{display:none;}
.rail-quote .qtoolbar > .btn:not(.icon){flex:1 0 100%;min-height:40px;}
/* «Pedir tarifas»: one slim note, one row for the language and the AI pass, tight supplier cards, a 3-line message. */
.rfq-note{display:flex;align-items:flex-start;gap:8px;margin:0 0 10px;font-size:12px;line-height:1.45;color:var(--muted);}
.rfq-note > i{flex:none;font-size:15px;color:var(--teal-d,#0E4C77);}
.rfq-bar{display:flex;align-items:flex-end;justify-content:space-between;gap:10px;margin-bottom:10px;}
.rfq-bar > .field{flex:0 1 280px;margin:0;}
.rfq-warn{margin:0 0 10px;padding:8px 12px;font-size:13px;}
.rfq-msg{margin-top:12px;}
.rfq-preview{margin-top:10px;}
/* «Ajustes del File» dialog: tight grid (10 / 14 px), labels at 13 px, no second heading under the dialog's own, links on one right-aligned line, the markup block under a hairline. */
.modal .qmeta-head{margin-bottom:6px;justify-content:flex-end;}
.modal .qdetails{gap:10px 14px;}
.modal .qdetails .field label{margin-bottom:4px;font-size:13px;}
.modal .qdetails .field textarea{min-height:56px;}
.modal .qdetails .field label.switchrow{min-height:44px;padding-top:11px;margin-bottom:0;}
.modal .lockbar{margin-bottom:10px;}
.modal .qset-markup{margin-top:12px;padding-top:12px;border-top:1px solid var(--line-2);}
.modal .qset-markup .qcard-t{display:block;margin-bottom:4px;font-size:13px;}
/* The rail's quote bar, tidied (owner 2026-10-29: the send-quote button came out wrong): the worded act keeps a READABLE face when it is blocked (the dimmed navy with navy text vanished), and both icon rows are one even grid, left to right. */
.rail-quote .head-actions,.rail-quote .qtoolbar{display:grid;grid-template-columns:repeat(auto-fill,minmax(44px,1fr));gap:6px;width:100%;margin-left:0;justify-content:stretch;}
.rail-quote .head-actions > .btn,.rail-quote .qtoolbar > .btn{margin:0;}
.rail-quote .head-actions > .btn.icon,.rail-quote .qtoolbar > .btn.icon{width:100%;height:44px;min-height:0;padding:0;}
.rail-quote .head-actions > .btn:not(.icon),.rail-quote .qtoolbar > .btn:not(.icon){grid-column:1 / -1;order:0;min-height:44px;}
.rail-quote .head-actions > .btn:not(.icon){order:-1;}
.rail-quote .qtoolbar{padding-top:8px;}
/* The rail's quote bar in two titled groups — «Cliente» and «Acciones» (owner 2026-10-29): a small caption, the worded act(s) on full-width rows, the 44 px icons centred in one row. */
.rail-quote{display:flex;flex-direction:column;gap:14px;background:#fff;border:1px solid var(--line-2);border-radius:12px;padding:14px;}   /* a card like the other rail containers (owner 2026-10-29: loose on the sand it got lost) */
.rail-quote:empty{display:none;}
.rq-grp{display:flex;flex-direction:column;gap:8px;}
.rq-t{font-size:12px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--muted);}
.rq-row{display:flex;flex-wrap:wrap;justify-content:flex-start;align-items:center;gap:6px;}   /* icons start at the left edge, under their title (owner 2026-10-29) */
.rq-row .btn.icon,.rq-row a.btn.icon{flex:0 0 44px;width:44px;height:44px;min-height:0;padding:0;margin:0;}
.rq-grp .rq-main{width:100%;min-height:44px;margin:0;}
.rq-pair{display:grid;grid-template-columns:1fr 1fr;gap:6px;}
.rq-pair .btn{min-height:44px;margin:0;}
.rq-grp .acc-issue.rq-main:disabled,.rq-grp .acc-issue.rq-main[disabled]{background:var(--sand,#F5EFE3);color:var(--muted);border-color:var(--line-2);opacity:1;cursor:not-allowed;box-shadow:none;}
/* The worded «Send the quote» is a SOLID button: white text on the navy (the .acc-issue blue was made for the light icon and vanished on it). */
.btn.acc-issue.rq-main:not(:disabled){background:var(--teal-d,#0E4C77);color:#fff;border-color:var(--teal-d,#0E4C77);}
.btn.acc-issue.rq-main:not(:disabled):hover{background:#0C3A5B;border-color:#0C3A5B;}
/* Side pickers (destinations, customers): one drawer look — scrim, panel, header, search bar, body, rows, footer. */
.dpk-scrim { position:fixed; inset:0; background:rgba(12,40,60,.35); z-index:60; }
.dpk { position:fixed; top:0; right:0; bottom:0; width:min(420px,100vw); background:#fff; z-index:61; display:flex; flex-direction:column; box-shadow:-12px 0 32px rgba(12,40,60,.18); }
.dpk-h { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:14px 16px 10px; }
.dpk-h h2 { margin:0; font-size:16px; }
.dpk-bar { padding:0 16px 10px; display:flex; flex-direction:column; gap:8px; border-bottom:1px solid var(--line-2); }
.dpk-search { position:relative; }
.dpk-search > i { position:absolute; left:12px; top:50%; transform:translateY(-50%); color:var(--muted); font-size:18px; pointer-events:none; }
.dpk-search .dpk-q { width:100%; box-sizing:border-box; padding-left:38px; }
.dpk-nights { display:flex; align-items:center; gap:6px; font-size:13px; color:var(--muted); }
.dpk-nights b { min-width:2ch; text-align:center; color:var(--ink); font-size:15px; font-variant-numeric:tabular-nums; }
.dpk-nights small { margin-left:auto; font-size:12px; }
.dpk-body { flex:1 1 auto; overflow-y:auto; padding:4px 10px 12px; }
.dpk-empty { padding:16px 6px; font-size:13px; color:var(--muted); }
.dpk-gh { padding:12px 8px 4px; font-size:12px; font-weight:700; letter-spacing:.05em; text-transform:uppercase; color:var(--muted); }
.dpk-rw { display:flex; align-items:center; gap:4px; }
.dpk-row { flex:1 1 auto; min-width:0; display:flex; align-items:center; gap:10px; padding:6px 8px; border:0; border-radius:10px; background:none; font:inherit; text-align:left; cursor:pointer; }
.dpk-row:hover:not(:disabled) { background:var(--teal-50,#E8F3FA); }
.dpk-tile { flex:none; width:32px; height:32px; border-radius:9px; display:flex; align-items:center; justify-content:center; background:var(--sand,#F5EFE3); color:var(--muted); font-size:17px; }
.dpk-row.is-on .dpk-tile { background:var(--teal-50,#E8F3FA); color:var(--teal-d,#0E4C77); }
.dpk-name { flex:1 1 auto; min-width:0; font-size:14px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.dpk-cnt { flex:none; font-size:12px; font-weight:700; color:var(--teal-d,#0E4C77); background:var(--teal-50,#E8F3FA); border-radius:999px; padding:1px 8px; }
.dpk-check { flex:none; color:var(--teal-d,#0E4C77); font-size:16px; }
.dpk-minus { flex:none; width:32px; height:32px; border:1px solid var(--line-2); border-radius:9px; background:#fff; color:var(--muted); cursor:pointer; display:flex; align-items:center; justify-content:center; }
.dpk-minus:hover:not(:disabled) { color:#B42318; border-color:#F3C0BD; background:#FDECEC; }
.dpk-f { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:12px 16px; border-top:1px solid var(--line-2); }
.dpk-count { font-size:13px; color:var(--muted); }
.cpk-av{border-radius:50%;background:var(--teal-d,#0E4C77);color:#fff;font-size:12px;font-weight:700;}
.cpk-txt{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:1px;}
.cpk-txt .dpk-name{flex:none;line-height:1.4;}
.cpk-sub{font-size:12px;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.4;}
.cpk-kind{flex:none;font-size:12px;font-weight:600;color:var(--muted);background:var(--sand,#F5EFE3);border-radius:999px;padding:2px 8px;}
.dpk .seg{display:flex;width:100%;margin:0;}
.dpk .seg > .seg-btn{flex:1 1 0;min-width:0;justify-content:center;padding:6px 4px;font-size:13px;white-space:nowrap;}
.rq-grp a.btn.rq-main{display:flex;justify-content:center;align-items:center;width:100%;min-height:44px;text-decoration:none;}
.rq-grp .rq-off{display:flex;justify-content:center;align-items:center;min-height:44px;background:var(--sand,#F5EFE3);color:var(--muted);border:1px solid var(--line-2);cursor:not-allowed;}
/* Uploaders are always this dropzone (owner 2026-10-29): a dashed card with an icon, a title and a hint — never the browser's «Choose File / No file chosen». Compact = one row, for a logo or a single file. */
.dropzone:focus-within{border-color:var(--teal,#1577B5);box-shadow:0 0 0 3px rgba(21,119,181,.15);}
.dropzone.dz-compact{flex-direction:row;justify-content:flex-start;gap:12px;padding:12px 16px;width:100%;box-sizing:border-box;text-align:left;}
.dropzone.dz-compact i{font-size:24px;flex:none;}
.dropzone.dz-compact .dz-title{font-size:13px;}
/* Notifications panel: compact like the dialogs (owner 2026-10-29: the texts were large) — 14px title, 13px body, 12px action. */
.bell-panel{width:320px;}
.bell-head{padding:10px 14px;}
.bell-head b{font-size:14px;font-weight:700;}
.bell-head .btn.sm{font-size:12px;padding:4px 10px;min-height:0;height:28px;}
.bell-empty{padding:16px 14px;font-size:13px;}
.bell-item{padding:9px 14px;}
/* Password fields: a show/hide eye inside the input (owner 2026-10-29). */
.pw{position:relative;}
.pw input{width:100%;box-sizing:border-box;padding-right:44px;}
.pw .pw-eye{position:absolute;right:4px;top:50%;transform:translateY(-50%);width:36px;height:36px;display:inline-flex;align-items:center;justify-content:center;border:0;background:transparent;border-radius:8px;color:var(--muted);font-size:18px;cursor:pointer;}
.pw .pw-eye:hover{background:rgba(31,42,46,.06);color:var(--ink,#0C3A5B);}
/* The balance IS the way into the account (owner 2026-10-29: a lone «Cuenta» button at the bottom of the dialog got lost). */
.ro.ro-link{display:flex;align-items:center;justify-content:space-between;gap:10px;text-decoration:none;color:var(--ink,#0C3A5B);cursor:pointer;}
.ro.ro-link span{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;color:var(--teal-d,#0E5E8F);}
.ro.ro-link:hover{background:#EBE3D2;}
.team-bar{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:0 0 10px;font-size:13px;}
/* A destination's groups, in the B2B builder's order: the stays, then the services day by day. */
.dest-grp{display:flex;align-items:center;gap:6px;margin:10px 0 4px;font-size:12px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--muted);}
.dest-grp i{font-size:14px;}
.dest > .dest-grp:first-of-type{margin-top:6px;}
/* The file data form (owner 2026-11-03), inside «File data»: compact, grouped under small headings, never more than TWO fields to a row. */
.fsettings .qdetails{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px 14px;}
.fsettings .qdetails > .field,.fsettings .qdetails > .field.half{grid-column:span 1;}
.fsettings .qdetails > .field.wide,.fsettings .qdetails > .qsec{grid-column:1 / -1;}
.fsettings .qsec{margin:6px 0 0;padding-top:8px;border-top:1px solid var(--line-2);font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);}
.fsettings .qdetails > .qsec:first-child{margin-top:0;padding-top:0;border-top:0;}
.fsettings .qset-markup{display:grid;grid-template-columns:1fr 1fr;gap:0 14px;align-items:start;margin-top:8px;padding-top:0;border-top:0;}
.fsettings .qset-markup > .qsec{grid-column:1 / -1;margin:6px 0 8px;}
.fsettings .qset-markup > .qcard-t,.fsettings .qset-markup > .mkf,.fsettings .qset-markup > .qhint{grid-column:1;}
/* The switch sits on the markup field's own line (the 44 px control), not centred on the whole block with its note under it. */
.fsettings .qset-markup > .switchrow{grid-column:2;grid-row:3;margin-top:0 !important;align-self:start;display:flex;align-items:center;gap:10px;min-height:44px;}
.fsettings .qset-markup .mkf{min-height:0;}
.fsettings .qmeta-head{margin-bottom:2px;}
.fsettings-inline{margin-top:18px;padding-top:6px;border-top:1px solid var(--line-2);}
.fsettings .fs-bar{display:flex;justify-content:flex-end;gap:10px;margin-top:14px;padding-top:12px;border-top:1px solid var(--line-2);}
@media(max-width:560px){
  .fsettings .qdetails,.fsettings .qset-markup{grid-template-columns:minmax(0,1fr);}
  .fsettings .qdetails > .field,.fsettings .qdetails > .field.half{grid-column:1 / -1;}
  .fsettings .qset-markup > .switchrow{grid-column:1;grid-row:auto;margin-top:10px !important;}
}
/* Dialog rows on a phone: one column (two narrow fields side by side clipped their labels and buttons), and a button under a field wraps instead of spilling out of it. */
.modal .frow > .field > .btn{margin-top:8px;white-space:normal;text-align:left;height:auto;min-height:40px;justify-content:flex-start;}
@media(max-width:560px){
  .modal .frow{flex-direction:column;gap:16px;}
  .modal .frow > .field{flex:none;width:100%;}
  .modal .frow > .field > label{margin-top:0;margin-bottom:6px;}
  .modal .frow + .frow,.modal .frow + .field,.modal .field + .frow,.modal .hint + .frow{margin-top:16px;}
  .modal .frow:has(> .field > .fieldnote){margin-bottom:0;}
}
/* The rail's last act, under «Ajustes del File», and a disabled button is plainly grey (owner 2026-10-29: the sand-coloured one read as a styled button, not as unavailable). */
.rq-cta{display:flex;flex-direction:column;gap:8px;}
.rq-cta:empty{display:none;}
.rq-grp .btn:disabled,.rq-grp .btn[disabled],.rq-grp .btn.is-disabled,.rq-cta .btn:disabled,.rq-cta .btn[disabled],.rq-grp .acc-issue.rq-main:disabled,.rq-cta .acc-issue.rq-main:disabled{background:#ECEEEF;border-color:#D9DEDF;color:#98A3A6;opacity:1;cursor:not-allowed;box-shadow:none;}
.rq-cta .rq-main{width:100%;min-height:44px;margin:0;}
.rq-cta .rq-pair{display:grid;grid-template-columns:1fr 1fr;gap:6px;}
.rq-cta .rq-pair .btn{min-height:44px;margin:0;}
.rq-grp .rq-off{background:#ECEEEF;border-color:#D9DEDF;color:#98A3A6;}
/* The file's rail is sticky, so it is its own stacking layer: a dialog opened from inside it (the client's request, the file's settings) was drawn UNDER the top bar and lost its heading. While one is open the rail rises above the bar. */
.fw-rail:has(.modal-scrim){z-index:90;}
/* A button in a filter bar is as tall as the inputs and dropdowns beside it (44 px) and sits on their line (owner 2026-10-29: «Hoy y mañana» was shorter). */
.filterbar > .btn,.filters > .btn{height:44px;min-height:44px;margin-top:0;margin-bottom:0;padding:0 16px;font-size:14px;box-sizing:border-box;align-self:flex-end;}
/* «Exportar» always sits at the right edge of its bar (owner 2026-10-29). */
.filterbar > .btn:has(> .ph-download-simple),.filters > .btn:has(> .ph-download-simple){margin-left:auto;}
.filterbar > .btn:has(> .ph-download-simple) ~ .btn:has(> .ph-download-simple),.filters > .btn:has(> .ph-download-simple) ~ .btn:has(> .ph-download-simple){margin-left:0;}   /* two downloads (CSV, Excel) stay together at the right */
/* Traffic lists, tighter (owner 2026-10-29: they look fine, just a little more compact): shorter rows, one-line sub-texts. */
@media(min-width:901px){
  .tbl.traffic > tbody > tr:not(.daysep) > td{padding-top:7px;padding-bottom:7px;}
  .tbl.traffic > tbody > tr.daysep > td{padding-top:7px;padding-bottom:7px;}
  .tbl.traffic .tbl-sub{margin-top:1px;line-height:1.35;}
  .tbl.traffic td > .tbl-sub,.tbl.traffic td .trf-mv .tbl-sub{display:block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .tbl.traffic td{line-height:1.35;}
}

/* TRAFFIC › MOVEMENTS never scrolls sideways (owner 2026-11-02: no horizontal scroll; analyse it properly). What widened the table was never the columns: the movement cell repeated the
   route (.tbl-sub) as ONE unbreakable line (white-space:nowrap + «…» above), and a cell's minimum width is its longest unbreakable line whatever its overflow says — «Arrivals hall,
   after baggage reclaim, with a name sign → Grand Carima Resort & Convention Center, Foz do Iguaçu» held the whole table ~800 px wide, and it showed at EVERY width above 900 px,
   next to the column that already carried it. Now every long text of the table wraps (up to three lines, then «…» and the whole text in the card of App.razor on hover/focus), the
   secondary columns fold under the movement exactly where they did (.col-xl ≤1360, .col-wide ≤1180) and show there ONLY there, and the movement keeps a floor of 120 px (the other columns need 473 px at 641 px, the narrowest table; the card is 607 px there). */
.clamp-2.lines-3{-webkit-line-clamp:3;line-clamp:3;}
.tbl.traffic.mvt .clamp-2{min-width:0;max-width:100%;}
.tbl.traffic.mvt td.trf-main{min-width:120px;width:38%;}
.tbl.traffic.mvt td.col-wide{width:22%;}
.tbl.traffic.mvt .trf-body{flex:1 1 auto;min-width:0;}
.tbl.traffic.mvt .trf-title{display:-webkit-box;font-size:14px;line-height:1.35;}
.tbl.traffic.mvt .trf-meta{font-size:13px;line-height:1.35;}
.tbl.traffic.mvt .trf-line > span{min-width:0;font-size:13px;line-height:1.35;}
.tbl.traffic.mvt .trf-mv .tbl-sub,.tbl.traffic.mvt .trf-mv .tbl-sub-xl{display:none;margin-top:2px;font-size:12px;line-height:1.35;white-space:normal;text-overflow:clip;}
@media(max-width:1180px){.tbl.traffic.mvt .trf-mv .tbl-sub{display:-webkit-box;}}
@media(max-width:1360px){.tbl.traffic.mvt .trf-mv .tbl-sub-xl{display:-webkit-box;}}
.tbl.traffic.mvt td.act{vertical-align:middle;}
/* Between 821 and 900 px the menu is still open (212 px) and the card is only ~550–630 px: the columns that do not fold (time, pax, file number, state, the 44 px pencil) need 473 px, so
   they give up side padding and the file number a point of size — the movement keeps at least 100 px (120 elsewhere). Measured 821–1920 and 360–820 with the real stylesheet: no width scrolls. */
@media(min-width:641px) and (max-width:900px){
  .tbl.traffic.mvt thead th,.tbl.traffic.mvt tbody td{padding-left:7px;padding-right:7px;}
  .tbl.traffic.mvt thead th:first-child,.tbl.traffic.mvt tbody td:first-child{padding-left:14px;}
  .tbl.traffic.mvt thead th:last-child,.tbl.traffic.mvt tbody td:last-child{padding-right:8px;}
  .tbl.traffic.mvt a.reflink{font-size:13px;}
  .tbl.traffic.mvt td.trf-main{min-width:100px;}
}
@media print{.tbl.traffic.mvt .clamp-2{display:block;-webkit-line-clamp:unset;line-clamp:unset;overflow:visible;}}
/* Every list, tighter on a desktop (owner 2026-10-29: check all the lists and make them more compact): rows 14 → 9 px above and below, headings 11 → 9. A phone keeps its card rows. */
@media(min-width:901px){
  .tbl tbody td{padding-top:9px;padding-bottom:9px;}
  .tbl thead th{padding-top:9px;padding-bottom:9px;}
  .tbl tbody tr.daysep > td{padding-top:7px;padding-bottom:7px;}
}
.filter-note-fixed{min-height:22px;}
/* Lists, one notch tighter again (owner 2026-10-29): 8 px above and below, row icons 26 px, badges on a short line. */
@media(min-width:901px){
  .tbl tbody td{padding-top:8px;padding-bottom:8px;}
  .tbl td.act .iconbtn{padding:4px;}
  .tbl .badge{line-height:1.25;padding-top:2px;padding-bottom:2px;}
}
/* The adjustments card (the file screen's third card): its heading is the same size and spacing as «Itinerario» and «Vuelos». */
.fw-card3.adj .sec-head{margin:0 0 10px;}
.fw-card3.adj .sec-title{font-size:15px;font-weight:700;margin:0;}
.fw-card3.adj .adj-card{margin-top:0;}

/* ==== Pickers (owner 2026-10-30: our own date picker … and time picker … across the whole Manager … no native controls) ====
   DatePicker, MonthPicker, TimePicker and DateTimePicker (Components/Shared). One 44 px text field (46 on a phone, like every field) that also takes typing, a button inside it that opens a
   popover; the popover is position:fixed (placed by rlpk in App.razor) so a dialog or a table never clips it, and on a phone (≤ 640 px) it is a bottom sheet with 44 px targets. The look follows
   the Portal's date picker (datepicker.css): azure selected day, soft hover, rounded 14 px card. A state is never colour alone: the selected day is filled AND bold, today is ringed, a day
   outside Min/Max is struck through, an error has an icon and a sentence. Sizes are the type scale: 14 body, 12 the floor. */
.pk{position:relative;display:block;width:100%;min-width:0;}
/* A date or a time is never squeezed below what its text needs (the value, the icon button and the padding): a row of four fields wraps or overflows before the digits are cut. */
.pk-date{min-width:138px;}
.pk-timef{min-width:104px;}
.pk .pk-input{width:100%;height:44px;min-height:44px;box-sizing:border-box;margin:0;padding:0 44px 0 13px;font-family:inherit;font-size:14px;line-height:1.2;color:var(--ink,#0C3A5B);background:#fff;
  border:1px solid var(--line-2);border-radius:10px;font-variant-numeric:tabular-nums;transition:border-color .12s ease,box-shadow .12s ease;}
.pk .pk-input::placeholder{color:var(--faint);opacity:1;}
.pk .pk-input:hover:not(:disabled){border-color:#C3CECD;}
.pk .pk-input:focus{outline:none;border-color:var(--teal,#1577B5);box-shadow:0 0 0 3px var(--teal-50,#E2F0F8);}
.pk .pk-input:disabled{background:var(--surf-2,#FAFBFB);color:var(--muted);cursor:not-allowed;}
.pk.is-invalid .pk-input,.pk.is-invalid .pk-input:hover{border-color:var(--danger,#C0392B);box-shadow:inset 0 0 0 1px var(--danger,#C0392B);}
.pk-btn{position:absolute;top:4px;right:4px;width:36px;height:36px;padding:0;border:0;border-radius:8px;background:transparent;color:var(--teal-d,#0E4C77);font-size:20px;line-height:1;
  display:inline-flex;align-items:center;justify-content:center;cursor:pointer;}
.pk-btn:hover:not(:disabled),.pk.is-open .pk-btn{background:var(--teal-50,#E2F0F8);}
.pk-btn:disabled{color:var(--faint);opacity:.55;cursor:not-allowed;}
/* The message sits in the flow under its field (the row grows, the controls above stay level because rows are top-aligned). In a bar, whose controls are bottom-aligned, it hangs
   below instead so the field does not jump up. */
.pk-err{display:flex;align-items:flex-start;gap:4px;margin-top:4px;font-size:12px;font-weight:600;line-height:1.35;color:var(--danger,#C0392B);}
.filterbar .pk-err,.filters .pk-err,.cal-nav .pk-err{position:absolute;left:0;top:calc(100% + 3px);z-index:4;width:max-content;max-width:min(100%,300px);padding:1px 6px 1px 3px;border-radius:6px;background:#fff;margin-top:0;}
.pk-err i{flex:none;margin-top:1px;font-size:14px;}
.pk.sm .pk-input{height:34px;min-height:34px;padding:0 34px 0 10px;font-size:13px;border-radius:8px;}
.pk.sm .pk-btn{top:2px;right:2px;width:30px;height:30px;font-size:17px;}
/* The files calendar's jump-to-date field: one 44 px control in the nav row, level with the buttons beside it. */
.cal-nav .pk{flex:none;width:176px;}
/* A date and a time of day side by side. */
.pk-dt{display:grid;grid-template-columns:minmax(138px,3fr) minmax(104px,2fr);gap:8px;align-items:start;min-width:250px;}

.pk-pop{position:fixed;z-index:2700;opacity:0;box-sizing:border-box;padding:12px;background:#fff;color:var(--ink,#0C3A5B);font-size:14px;border:1px solid var(--line-2);border-radius:14px;
  box-shadow:0 14px 36px rgba(14,42,51,.2);max-width:calc(100vw - 16px);overflow:auto;overscroll-behavior:contain;}
.pk-cal{width:316px;}
.pk-time{width:236px;}
.pk-scrim,.pk-sheet-h{display:none;}
.pk-head{display:flex;align-items:center;justify-content:space-between;gap:4px;margin-bottom:6px;}
.pk-nav{flex:none;width:36px;height:36px;padding:0;border:0;border-radius:8px;background:transparent;color:var(--ink,#0C3A5B);font-size:18px;line-height:1;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;}
.pk-nav:hover:not(:disabled){background:var(--teal-50,#E2F0F8);color:var(--teal-d,#0E4C77);}
.pk-nav:disabled{opacity:.35;cursor:not-allowed;}
.pk-title{flex:1;min-width:0;height:36px;padding:0 8px;border:0;border-radius:8px;background:transparent;font-family:inherit;font-size:14px;font-weight:700;color:var(--ink,#0C3A5B);
  display:inline-flex;align-items:center;justify-content:center;gap:6px;cursor:pointer;white-space:nowrap;}
.pk-title i{font-size:14px;color:var(--muted);}
.pk-title:hover{background:var(--teal-50,#E2F0F8);color:var(--teal-d,#0E4C77);}
.pk-body{min-height:268px;}
.pk-row{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:2px;margin-bottom:2px;}
.pk-dow span{height:26px;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;color:var(--muted);}
.pk-day{position:relative;height:38px;padding:0;border:0;border-radius:8px;background:transparent;font-family:inherit;font-size:14px;color:var(--ink,#0C3A5B);font-variant-numeric:tabular-nums;cursor:pointer;}
.pk-day.we{background:#F5F8F8;}
.pk-day.out{color:#7F8D8C;font-weight:400;}
.pk-day:hover:not(.off):not(.sel){background:var(--teal-50,#E2F0F8);color:var(--teal-d,#0E4C77);}
.pk-day.today,.pk-cell.today{font-weight:800;box-shadow:inset 0 0 0 2px var(--teal,#1577B5);}
.pk-day.sel,.pk-cell.sel,.pk-t.sel{background:var(--teal-d,#0E4C77);color:#fff;font-weight:800;}
.pk-day.sel.today,.pk-cell.sel.today{box-shadow:inset 0 0 0 2px #fff;}
.pk-day.off,.pk-cell.off{color:#A5B0AF;text-decoration:line-through;background:transparent;cursor:not-allowed;}
.pk-day:focus-visible,.pk-cell:focus-visible,.pk-t:focus-visible,.pk-act:focus-visible,.pk-nav:focus-visible,.pk-title:focus-visible,.pk-btn:focus-visible{outline:2px solid var(--teal,#1577B5);outline-offset:1px;}
.pk-mgrid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));grid-auto-rows:60px;gap:6px;align-content:start;}
.pk-cell{padding:0;border:0;border-radius:10px;background:#F5F8F8;font-family:inherit;font-size:14px;color:var(--ink,#0C3A5B);cursor:pointer;font-variant-numeric:tabular-nums;}
.pk-cell:hover:not(.off):not(.sel){background:var(--teal-50,#E2F0F8);color:var(--teal-d,#0E4C77);}
.pk-foot{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:8px;padding-top:8px;border-top:1px solid var(--line-2);}
.pk-act{min-height:34px;padding:0 12px;border:0;border-radius:8px;background:transparent;font-family:inherit;font-size:13px;font-weight:700;color:var(--teal-d,#0E4C77);cursor:pointer;}
.pk-act:hover:not(:disabled){background:var(--teal-50,#E2F0F8);}
.pk-act:disabled{color:var(--faint);opacity:.6;cursor:not-allowed;}
.pk-foot .pk-act:last-child:not(:first-child){color:var(--muted);}
/* The time popover: an hour column and a minute column, each scrolls on its own. */
.pk-tcols{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:8px;}
.pk-tcol{position:relative;height:240px;overflow-y:auto;padding:0 4px 4px;border:1px solid var(--line-2);border-radius:10px;overscroll-behavior:contain;scrollbar-width:thin;}
.pk-tcap{position:sticky;top:0;z-index:1;padding:7px 0 5px;background:#fff;text-align:center;font-size:12px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--muted);}
.pk-t{display:block;width:100%;height:34px;padding:0;border:0;border-radius:8px;background:transparent;font-family:inherit;font-size:14px;text-align:center;color:var(--ink,#0C3A5B);font-variant-numeric:tabular-nums;cursor:pointer;}
.pk-t:hover:not(.sel){background:var(--teal-50,#E2F0F8);color:var(--teal-d,#0E4C77);}

@media(max-width:640px){
  .pk .pk-input{height:46px;min-height:46px;font-size:16px;}
  .pk-btn{top:5px;right:4px;width:40px;height:36px;}
  .pk.sm .pk-input{height:40px;min-height:40px;font-size:14px;}
  .pk.sm .pk-btn{top:3px;width:34px;height:34px;}
  .pk-dt{grid-template-columns:minmax(0,1fr);min-width:0;}
  .pk-date,.pk-timef{min-width:0;}
  .cal-nav .pk .pk-input{height:44px;min-height:44px;}   /* the calendar's bar keeps its 44 px on a phone, level with its buttons */
  .cal-nav .pk .pk-btn{top:4px;}
  .pk-scrim{display:block;position:fixed;inset:0;z-index:2690;background:rgba(8,20,24,.45);}
  .pk-pop{left:0!important;right:0!important;top:auto!important;bottom:0!important;width:auto!important;max-width:none;max-height:92dvh!important;opacity:1;
    padding:6px 12px calc(12px + env(safe-area-inset-bottom));border-radius:16px 16px 0 0;box-shadow:0 -10px 32px rgba(12,40,60,.22);}
  .pk-sheet-h{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:2px 0 4px 4px;font-size:16px;font-weight:700;}
  .pk-nav{width:44px;height:44px;}
  .pk-title{height:44px;font-size:16px;}
  .pk-body{min-height:310px;}
  .pk-dow span{height:30px;}
  .pk-day{height:44px;font-size:16px;}
  .pk-mgrid{grid-auto-rows:64px;}
  .pk-cell{font-size:16px;}
  .pk-act{min-height:44px;font-size:14px;}
  .pk-tcol{height:280px;}
  .pk-t{height:44px;font-size:16px;}
}
/* ==== end pickers ==== */

/* A password field with its «Generate» button on one line, both 44 px (the approval dialog). */
.pwd-row{display:flex;gap:8px;align-items:center;}
.pwd-row input{flex:1 1 auto;min-width:0;}
.pwd-row .btn{flex:none;}

/* The change log in a file's «Actividad»: a row per save that opens to the field-level before → after (every size declared). */
.act-diff{font-size:14px;}
.act-diff > summary{cursor:pointer;list-style:none;font-size:14px;}
.act-diff > summary::-webkit-details-marker{display:none;}
.act-diff > summary::before{content:"▸";display:inline-block;width:14px;color:var(--muted);}
.act-diff[open] > summary::before{content:"▾";}
.act-diff ul{margin:6px 0 2px 14px;padding:0;list-style:none;font-size:13px;color:var(--muted);}
.act-diff li{margin:2px 0;overflow-wrap:anywhere;}
.act-diff li s{color:var(--faint);}
/* «Register the booking» on a quote with alternative hotels: one radio card per option, its total on the right. */
.pol-opts{display:grid;gap:8px;margin-top:14px;}
.pol-opts-h{margin:0 0 2px;font-size:14px;font-weight:600;}
.pol-opts .pol-card{align-items:center;min-height:44px;}
.pol-opts-name{flex:1 1 auto;min-width:0;font-size:14px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.pol-opts-total{flex:0 0 auto;font-size:14px;}

/* ---- «Registrar cobro» (MoneyActionModal) ----------------------------------------------------------------------------------------------------------------------
   The amount is the big field; below it, when no booking is picked, the open parts a receipt pays — a headed card: title + two actions in its header, then one row per part
   (what · what it owes · what this receipt pays). Every control on a row is 44 px and shares the row's centre line (owner 2026-11-02: «este modal quedó mal»). */
.money-modal .money-amount{font-size:20px;font-weight:700;min-height:48px;padding:10px 14px;text-align:left;font-variant-numeric:tabular-nums;}
.money-modal .split{margin:12px 0 16px;border:1px solid var(--line-2);border-radius:12px;background:#fff;overflow:hidden;}
.money-modal .split-h{display:flex;align-items:center;flex-wrap:wrap;gap:8px;padding:10px 12px;background:var(--ocean-50);border-bottom:1px solid var(--ocean-100);}
.money-modal .split-t{flex:1 1 220px;min-width:0;display:flex;align-items:center;gap:8px;font-size:13px;font-weight:700;color:var(--ocean-900);}
.money-modal .split-t > i{font-size:18px;color:var(--ocean-700);flex:none;}
.money-modal .split-h .btn{min-height:36px;padding:5px 12px;font-size:12px;}
.money-modal .split-row{display:grid;grid-template-columns:minmax(0,1fr) auto 128px;align-items:center;gap:12px;min-height:56px;padding:8px 12px;border-top:1px solid var(--line);}
.money-modal .split-row:first-of-type{border-top:0;}
.money-modal .split-what{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;color:var(--ink);}
.money-modal .split-owes{font-size:13px;font-weight:600;color:var(--muted);white-space:nowrap;font-variant-numeric:tabular-nums;}
.money-modal .split-row .dec{width:128px;min-height:44px;text-align:right;font-variant-numeric:tabular-nums;}
.money-modal .split-empty{display:flex;align-items:center;gap:8px;padding:12px;font-size:13px;color:var(--muted);}
.money-modal .split-empty > i{font-size:18px;flex:none;}
@media (max-width:700px){
  .money-modal .split-row{grid-template-columns:minmax(0,1fr) 128px;}
  .money-modal .split-owes{grid-column:1;grid-row:2;}
  .money-modal .split-row .dec{grid-column:2;grid-row:1 / span 2;}
}

/* ---- «Record receipt» from Receipts and payments (QuickReceiptModal): who paid, in one list ------------------------------------------------------------------- */
.quick-receipt .qr-head{display:flex;align-items:center;justify-content:space-between;margin:14px 0 6px;font-size:12px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--muted);}
.quick-receipt .qr-list{list-style:none;margin:0;padding:0;max-height:min(46vh,420px);overflow:auto;border:1px solid var(--line-2);border-radius:12px;background:#fff;}
.quick-receipt .qr-list li + li{border-top:1px solid var(--line);}
.quick-receipt .qr-row{display:grid;grid-template-columns:36px minmax(0,1fr) auto 18px;align-items:center;gap:12px;width:100%;min-height:60px;padding:8px 14px;border:0;background:#fff;text-align:left;font:inherit;color:var(--ink);cursor:pointer;}
.quick-receipt .qr-row:hover,.quick-receipt .qr-row:focus-visible{background:var(--ocean-50);outline:none;}
.quick-receipt .qr-row.first{box-shadow:inset 3px 0 0 var(--ocean-700);}   /* Enter takes the first row: said by a stripe, not by colour alone */
.quick-receipt .qr-row:disabled{opacity:.5;cursor:default;}
.quick-receipt .qr-ic{display:grid;place-items:center;width:36px;height:36px;border-radius:10px;background:var(--ocean-50);color:var(--ocean-800);font-size:18px;}
.quick-receipt .qr-main{min-width:0;display:flex;flex-direction:column;gap:2px;}
.quick-receipt .qr-name{font-size:14px;font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.quick-receipt .qr-main small{font-size:12px;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.quick-receipt .qr-amt{text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums;}
.quick-receipt .qr-amt b{font-size:15px;color:var(--ocean-900);}
.quick-receipt .qr-go{color:var(--faint);font-size:16px;}
.quick-receipt .qr-empty{display:flex;align-items:center;gap:10px;padding:18px 14px;border:1px dashed var(--line-2);border-radius:12px;color:var(--muted);font-size:13px;}
.quick-receipt .qr-empty > i{font-size:20px;flex:none;}

/* The role editor's permission search: its own strip, fixed under the top bar while the long list scrolls (owner 2026-11-02). */
.perm-searchbar{position:sticky;top:56px;z-index:4;display:flex;flex-wrap:wrap;align-items:flex-end;gap:12px 16px;margin:0 0 14px;padding:12px 16px;background:#fff;border:1px solid var(--line-2);border-radius:var(--r-card,12px);box-shadow:0 6px 16px rgba(12,58,91,.08);}
.perm-searchbar .grow{flex:1 1 320px;max-width:none;margin:0;}
.perm-searchbar .fixed{min-height:44px;display:flex;align-items:center;margin:0;}
.perm-searchbox{position:relative;display:flex;align-items:center;}
.perm-searchbox > i.ph-magnifying-glass{position:absolute;left:13px;font-size:18px;color:var(--muted);pointer-events:none;}
.perm-searchbox input{width:100%;padding-left:40px;padding-right:44px;}
.perm-searchbox .iconbtn{position:absolute;right:4px;}
.perm-found{min-height:44px;display:flex;align-items:center;font-size:13px;font-weight:600;color:var(--muted);white-space:nowrap;}
.perm-none{display:flex;align-items:center;gap:10px;color:var(--muted);}
.perm-none > i{font-size:20px;flex:none;}
.perm-row mark{background:#FFE9A8;color:inherit;border-radius:3px;padding:0 1px;}

/* «Respuesta del proveedor» (owner 2026-11-03: more compact, better built). One line per service — its name on the left, its answer on the right; the reference opens under the name only for a confirmed
   one. The states carry colour AND an icon AND a filled background (never colour alone): confirmed green + check, declined red + cross, no answer neutral. */
/* The services a confirmation covers (the confirm dialog): one soft row each — name first, the facts under it, the supplier's charge on the right. */
.cf-list{display:grid;gap:8px;margin:12px 0 16px;max-height:40vh;overflow:auto;}
.cf-item{display:flex;align-items:flex-start;gap:12px;padding:12px 14px;border:1px solid var(--line-2);border-radius:12px;background:var(--surf-2);}
.cf-ic{flex:none;font-size:20px;line-height:22px;color:var(--green);}
.cf-main{flex:1 1 auto;min-width:0;}
.cf-name{font-size:14px;font-weight:700;line-height:20px;color:var(--ink);overflow-wrap:anywhere;}
.cf-facts{margin-top:2px;font-size:13px;line-height:18px;color:var(--muted);}
.cf-cost{flex:none;font-size:14px;font-weight:700;line-height:20px;color:var(--ink);white-space:nowrap;}
