:root{
  --ink:#1B2430;
  --muted:#6B7684;
  --line:#E3E7EC;
  --bg:#F5F7FA;
  --panel:#FFFFFF;
  --accent:#2F5DE8;
  --accent-dark:#2447BD;
  --success:#1E8E5A;
  --success-bg:#E9F7EF;
  --danger:#C23A3A;
  --danger-bg:#FBEAEA;
  --warning:#9A6B00;
  --warning-bg:#FFF6E0;
  --sidebar:#151B26;
  --sidebar-muted:#8B95A6;
}
*{box-sizing:border-box; margin:0; padding:0;}
body{font-family:-apple-system,BlinkMacSystemFont,'Inter',Segoe UI,sans-serif; background:var(--bg); color:var(--ink); line-height:1.5; font-size:14px;}
a{color:var(--accent); text-decoration:none;}
a:hover{text-decoration:underline;}

/* AUTH PAGES */
.auth-wrap{min-height:100vh; display:flex; align-items:center; justify-content:center; background:var(--sidebar);}
.auth-card{background:var(--panel); border-radius:12px; padding:40px 36px; width:100%; max-width:380px; box-shadow:0 24px 60px rgba(0,0,0,0.35);}
.auth-card h1{font-size:20px; margin-bottom:6px;}
.auth-card p.sub{color:var(--muted); font-size:13px; margin-bottom:24px;}

/* SHELL */
.shell{display:grid; grid-template-columns:230px 1fr; min-height:100vh;}
.sidebar{background:var(--sidebar); color:#fff; padding:22px 16px;}
.sidebar .brand{font-weight:700; font-size:15px; letter-spacing:.3px; margin-bottom:26px; padding:0 8px;}
.sidebar .brand span{display:block; font-size:11px; color:var(--sidebar-muted); font-weight:500; margin-top:2px;}
.sidebar .brand .chain-badge{display:inline-block; margin-top:8px; font-size:10.5px; font-weight:600; text-transform:none; letter-spacing:0; color:#fff; background:rgba(255,255,255,0.12); padding:3px 9px; border-radius:20px;}
.sidebar nav a{display:block; padding:9px 10px; border-radius:6px; color:var(--sidebar-muted); font-size:13.5px; font-weight:500; margin-bottom:2px;}
.sidebar nav a:hover{background:rgba(255,255,255,0.06); color:#fff; text-decoration:none;}
.sidebar nav a.active{background:var(--accent); color:#fff;}
/* Task #702: contextual sidebar (Bookings' reservation detail, Settings)
   -- swaps in for the standard nav list above when a page sets
   $pf_contextNav. .pf-nav-back is the "Main menu" link back to the
   standard 10-item nav; .pf-nav-divider is a hairline separating it from
   the section's own items; .pf-nav-flygroup is an item whose further
   sub-pages appear as a floating panel on hover instead of permanently
   nested rows, so a section with many destinations (Bookings' reservation
   detail has ten) doesn't push the sidebar's own height past the 10-item
   default it's swapping in for. */
.sidebar nav a.pf-nav-back{color:var(--sidebar-muted); font-size:12.5px; margin-bottom:2px;}
.pf-nav-divider{height:1px; background:rgba(255,255,255,0.1); margin:6px 10px 8px;}
.pf-nav-flygroup{position:relative;}
.pf-nav-flygroup > a{display:flex; align-items:center; justify-content:space-between; gap:6px;}
.pf-nav-caret{font-size:13px; color:rgba(255,255,255,0.4);}
.pf-nav-flyout{display:none; position:absolute; left:100%; top:-6px; min-width:200px; background:var(--panel); border:1px solid rgba(0,0,0,0.08); border-radius:10px; box-shadow:0 8px 24px rgba(0,0,0,0.14), 0 2px 6px rgba(0,0,0,0.08); padding:6px; z-index:30;}
/* Task #706: raw :hover alone drops the instant the cursor leaves the
   trigger link's own small box while crossing diagonally toward an item
   further down the flyout -- "the link vanishes when you hover over it".
   pf-nav-flyout.js adds a short close-delay via a JS-toggled
   .pf-flyout-open class on the flygroup; :hover stays as a no-JS
   fallback so this still works (without the grace window) if the script
   fails to load. */
.pf-nav-flygroup:hover .pf-nav-flyout, .pf-nav-flygroup.pf-flyout-open .pf-nav-flyout{display:block;}
/* Task #707: `.sidebar nav a` / `.sidebar nav a:hover` (line 35/36) are
   written for the dark navy sidebar -- muted light-gray text at rest,
   pure white on hover -- and were silently winning over the plain
   `.pf-nav-flyout a` rules below on specificity (two elements, `nav a`,
   beats one class + one element), even though the flyout itself sits on
   a white panel. Result: flyout links rendered in near-invisible
   light-gray at rest and literally white-on-white on hover -- "the
   links merge with the background and disappear". Prefixing every
   flyout link rule with `.sidebar ` adds a class to the selector,
   pushing it ahead of `.sidebar nav a`'s on specificity, and the hover
   rule now sets its own color explicitly rather than relying on
   inheriting the (correct, but no longer ambiguous) resting-state
   color. */
.sidebar .pf-nav-flyout a{display:block; padding:8px 10px; border-radius:6px; color:#1F2937; font-size:13px; font-weight:500; margin-bottom:0;}
.sidebar .pf-nav-flyout a:hover{background:#F3F4F6; color:#1F2937; text-decoration:none;}
.sidebar .pf-nav-flyout a.active{background:var(--accent); color:#fff;}
/* Sidebar nav group — a <details>/<summary> disclosure so a set of related
   sub-links (e.g. Property Profile's tabs) can collapse instead of
   always taking up vertical space in the sidebar. No JS: same <details>
   pattern used by the Super Admin sidebar's "Reports" group. */
.sidebar nav details.nav-group{margin-bottom:2px;}
.sidebar nav details.nav-group summary{display:flex; align-items:center; justify-content:space-between; gap:8px; padding:9px 10px; border-radius:6px; color:var(--sidebar-muted); font-size:13.5px; font-weight:500; cursor:pointer; list-style:none;}
.sidebar nav details.nav-group summary::-webkit-details-marker{display:none;}
.sidebar nav details.nav-group summary:hover{background:rgba(255,255,255,0.06); color:#fff;}
.sidebar nav details.nav-group summary::after{content:"\25B8"; font-size:11px; color:rgba(255,255,255,0.4); transition:transform .15s ease;}
.sidebar nav details.nav-group[open] summary::after{transform:rotate(90deg);}
.sidebar nav details.nav-group[open] summary{color:#fff;}
.sidebar nav a.nav-sub{padding-left:20px; font-size:12.5px;}
/* Bumped from a dim 65%-opacity 10.5px label to a fuller-contrast,
   heavier, hairline-divided one -- the old version read as barely-there
   against the nav links themselves, undercutting its own job as a
   section header. See README changelog for the approved mockup this
   matches. */
.nav-group-label{font-size:11px; font-weight:800; text-transform:uppercase; letter-spacing:.8px; color:#fff; opacity:.9; padding:14px 10px 7px; border-bottom:1px solid rgba(255,255,255,0.12); margin-bottom:4px;}
.nav-group-label:first-of-type{padding-top:6px;}
.sidebar .foot{margin-top:24px; padding-top:16px; border-top:1px solid rgba(255,255,255,0.1);}
.sidebar .foot a{display:block; color:var(--sidebar-muted); font-size:12.5px;}
.sidebar .foot a + a{margin-top:6px;}

/* min-width:0 overrides the browser default of min-width:auto on this
   grid item (.shell{grid-template-columns:230px 1fr;}). Without it, a
   CSS Grid item refuses to shrink below the intrinsic (max-content)
   width of whatever it contains, so ANY unwrapped wide element anywhere
   inside .main -- a table, a long unbreakable flex row, whatever --
   quietly stretches this whole column past its 1fr share, widening the
   entire .shell (sidebar included) past the viewport. This bit us three
   times on rate-grid.php specifically (see the "Fix: Rate Grid page
   rendering wider..." README entry and its two follow-ups): each
   individual overflow-x:auto wrapper fixed the specific table it was
   added to, but never addressed why .main was growable in the first
   place, so the next unwrapped wide element just reproduced a smaller
   version of the same bug. This is the actual root-cause fix -- .main
   can no longer be forced wider by its content, full stop, regardless
   of what any current or future page puts inside it. The overflow-x:auto
   wrappers already in place stay (they're still the correct way for a
   specific wide table to scroll internally instead of just clipping);
   this just removes the mechanism that let their absence leak out into
   the page-wide layout. */
.main{padding:28px 36px; min-width:0;}
.topbar{display:flex; justify-content:space-between; align-items:center; margin-bottom:24px;}

/* BOOKING-ENGINE STATUS BAR -- topmost element on every Client Admin page
   (client/includes/layout_header.php), reflects the currently active
   property's Hotels.Is_Live_On_Booking_Engine, independent of Is_Visible. */
.status-row{display:flex; align-items:center; justify-content:space-between; gap:12px; padding:12px 0; border-bottom:1px solid #EEF0F3;}
.status-row:last-child{border-bottom:none;}
.status-row-label{display:flex; align-items:center; gap:8px;}
.topbar h1{font-size:20px;}
.topbar .who{color:var(--muted); font-size:13px;}
/* Task #702: clickable path shown above the page title on any page that
   sets $pf_breadcrumb (currently Bookings' reservation detail and
   Settings -- the two pages with real sub-navigation). Stacks above h1
   inside a new .topbar-titles wrapper rather than sitting inline with it,
   so .topbar's own flex row still only ever sees two top-level items
   (this wrapper, .topbar-right), matching the same reasoning documented
   just above for .topbar-right. */
.topbar-titles{display:flex; flex-direction:column; gap:2px;}
.pf-breadcrumb{font-size:12px; color:var(--muted); display:flex; align-items:center; gap:5px;}
.pf-breadcrumb a{color:var(--muted);}
.pf-breadcrumb a:hover{color:var(--accent);}
.pf-breadcrumb-sep{color:var(--muted); opacity:.55;}
/* Wraps the hotel-name/property-switcher + "Signed in as" cluster (client
   app) or just "Signed in as" alone (admin app) in a pale tint of the
   sidebar's own navy (var(--sidebar), rgba at low opacity rather than a
   hand-picked gray, so it stays mathematically tied to that color if it
   ever changes) -- ties this identity/context info back to the nav
   chrome it describes, without the full-strength fill competing for
   attention against the page title next to it. */
.topbar-context{display:flex; align-items:center; gap:16px; background:rgba(21,27,38,0.07); border-radius:8px; padding:8px 16px;}
.topbar-context .who{color:#4A5568;}
/* Wraps topbar-context + the enabled-modules badge row below it into one
   right-aligned column, so .topbar's own flex row still only sees two
   items (h1, this wrapper) -- keeps .topbar's align-items:center working
   without the badge row becoming a third row-level flex item competing
   for that vertical centering. */
.topbar-right{display:flex; flex-direction:column; align-items:flex-end; gap:4px;}
/* Enabled-module badges, client-wide (see pf_client_enabled_modules() in
   admin/includes/shared_functions.php) -- deliberately smaller than the
   .req-adjacent pill badges used elsewhere (admin/clients.php's client-
   name row) since this sits on every single page, in the topbar, and
   needs to read as ambient context rather than competing for attention
   with the page title next to it. */
.topbar-modules{display:flex; gap:4px; flex-wrap:wrap; justify-content:flex-end;}
.topbar-module-badge{background:#fff; color:var(--success); font-size:9px; font-weight:600; padding:2px 7px; border-radius:20px; border:1px solid #cfe9da; text-transform:uppercase; letter-spacing:.2px;}

/* CARDS / TABLES */
.card{background:var(--panel); border:1px solid var(--line); border-radius:10px; padding:22px;}
.card + .card{margin-top:18px;}
.stat-grid{display:grid; grid-template-columns:repeat(4,1fr); gap:16px; margin-bottom:22px;}
.stat-card{background:var(--panel); border:1px solid var(--line); border-radius:12px; padding:20px; display:flex; align-items:center; gap:16px; transition:box-shadow .12s, border-color .12s;}
.stat-card:hover{box-shadow:0 6px 18px rgba(20,30,50,0.08); border-color:#D7DCE3;}
.stat-card .num{font-size:28px; font-weight:800; line-height:1.1;}
.stat-card .label{color:var(--muted); font-size:12.5px; margin-top:3px;}
.stat-card-icon{flex:0 0 auto; width:46px; height:46px; border-radius:50%; display:flex; align-items:center; justify-content:center;}
.stat-card-icon svg{width:22px; height:22px;}
.stat-icon-blue{background:#EAF0FF; color:var(--accent-dark);}
.stat-icon-gold{background:#FCF1DD; color:#8A6417;}
.stat-icon-rose{background:#FDEAF2; color:#B3115B;}
.stat-icon-teal{background:#E1F5F2; color:#0F766E;}

/* SETUP CHECKLIST -- Dashboard's "Setup" card: the pending tasks standing
   between a property and being genuinely ready for guests. See
   pf_client_setup_checklist()/pf_client_setup_progress() in
   client/includes/functions.php for what feeds this. */
.setup-progress-track{width:100%; height:8px; background:#EEF0F3; border-radius:20px; overflow:hidden; margin-bottom:16px;}
.setup-progress-fill{height:100%; background:var(--accent); border-radius:20px;}
.setup-hotel-picker{width:auto; min-width:240px; padding:8px 11px; border:1px solid var(--line); border-radius:7px; font-size:13px; font-family:inherit; color:var(--ink); background:#fff; margin-bottom:16px;}
.setup-item{display:flex; align-items:flex-start; gap:12px; padding:12px 0; border-bottom:1px solid #EEF0F3;}
.setup-item:last-child{border-bottom:none;}
.setup-item-icon{width:22px; height:22px; border-radius:50%; flex:0 0 auto; display:flex; align-items:center; justify-content:center; font-size:12px; margin-top:1px; font-weight:700;}
.setup-item-icon.done{background:var(--success-bg); color:var(--success);}
.setup-item-icon.pending{background:var(--danger-bg); color:var(--danger);}
.setup-item-icon.optional{background:#EEF0F3; color:var(--muted);}
.setup-item-title{font-weight:600; font-size:13.5px;}
.setup-item-desc{font-size:12px; color:var(--muted); margin-top:2px;}
.setup-item-action{margin-left:auto; flex:0 0 auto; font-size:12.5px; font-weight:600; color:var(--accent); white-space:nowrap; padding-top:2px;}
.setup-group-label{font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:0.4px; color:var(--muted); margin:16px 0 4px; padding-top:8px; border-top:1px solid #EEF0F3;}
.setup-group-label:first-of-type{margin-top:4px; padding-top:0; border-top:none;}
.chip-row{display:flex; flex-wrap:wrap; gap:6px;}
.chip{display:inline-flex; align-items:center; gap:4px; font-size:12px; font-weight:500; padding:4px 10px; border-radius:20px; text-decoration:none; border:none; cursor:default; line-height:1.3;}
.chip.done{background:var(--success-bg); color:var(--success);}
.chip.pending{background:var(--danger-bg); color:var(--danger); cursor:pointer;}
.chip.pending:hover{background:#F5D6D6;}
.chip.optional{background:#EEF0F3; color:var(--muted); cursor:pointer;}
.chip.optional:hover{background:#E4E7EC;}
.setup-note{margin-top:16px; padding:12px 14px; border-radius:8px; font-size:12.5px; font-weight:600; background:var(--success-bg); color:var(--success);}
.setup-note.blocked{background:var(--danger-bg); color:var(--danger);}

/* QUICK LINKS — Dashboard's link grid. Each destination is a clickable
   tile (icon + title + one-line description + trailing chevron) instead
   of a plain two-column link table, so the four most common next actions
   read as equally-weighted shortcuts rather than rows in a list. */
.quick-link-grid{display:grid; grid-template-columns:1fr 1fr; gap:10px;}
.quick-link-tile{display:flex; align-items:center; gap:12px; padding:14px; border:1px solid var(--line); border-radius:10px; color:var(--ink); transition:border-color .12s, background .12s;}
.quick-link-tile:hover{border-color:var(--accent); background:#FAFBFF; text-decoration:none;}
.quick-link-tile-icon{flex:0 0 auto; width:36px; height:36px; border-radius:9px; background:#EEF0F3; color:var(--muted); display:flex; align-items:center; justify-content:center;}
.quick-link-tile-icon svg{width:18px; height:18px;}
.quick-link-tile-title{font-size:13.5px; font-weight:600;}
.quick-link-tile-desc{font-size:11.5px; color:var(--muted); margin-top:2px; line-height:1.35;}
.quick-link-tile-chevron{margin-left:auto; color:var(--muted); font-size:18px; flex:0 0 auto;}

/* RECENT ACTIVITY — Dashboard's activity feed, restyled from a plain
   table into a vertical list: a small colored dot marks each entry, the
   action reads as the primary line, entity type + timestamp as a
   secondary line beneath it. */
.activity-list{display:flex; flex-direction:column;}
.activity-item{display:flex; align-items:flex-start; gap:12px; padding:11px 0; border-bottom:1px solid var(--line);}
.activity-item:last-child{border-bottom:none; padding-bottom:0;}
.activity-item:first-child{padding-top:0;}
.activity-action{font-size:13.5px; font-weight:600;}
.activity-meta{font-size:11.5px; color:var(--muted); margin-top:2px;}
.activity-meta .badge{margin-right:6px; vertical-align:1px;}

table{width:100%; border-collapse:collapse;}
th, td{text-align:left; padding:10px 12px; font-size:13.5px; border-bottom:1px solid var(--line);}
.pf-compact-table th, .pf-compact-table td{padding:6px 10px;}
th{color:var(--muted); font-weight:600; font-size:11.5px; text-transform:uppercase; letter-spacing:.4px;}
tr:last-child td{border-bottom:none;}

.badge{display:inline-flex; align-items:center; gap:5px; font-size:11px; font-weight:600; padding:3px 9px; border-radius:20px; text-transform:uppercase; letter-spacing:.3px;}
.badge.on{background:var(--success-bg); color:var(--success);}
.badge.off{background:#EEF0F3; color:var(--muted);}
.badge.warn{background:var(--warning-bg); color:var(--warning);}
.badge.danger{background:var(--danger-bg); color:var(--danger);}
.badge.mode-single{background:#EAF0FF; color:var(--accent-dark);}
.badge.mode-multi{background:#FFF3E2; color:#B4650C;}

/* ROUTE URL — the real guest-facing routed address (/X00001[/property/
   slug]) shown under a property's name on Client Admin's Properties page
   and Property status card, and Super Admin's client detail screen. A
   plain readonly input (click-to-select, same convention
   booking-branding.php's own embed-snippet fields already use) rather
   than a copy-to-clipboard button + JS -- one less thing to wire up, and
   consistent with the one other place in the app that shows a URL like
   this. */
.route-url-box{margin-top:6px; max-width:280px;}
.route-url-input{width:100%; font-size:11px; padding:4px 7px; border:1px solid var(--line); border-radius:6px; background:var(--bg); color:var(--muted); font-family:ui-monospace, SFMono-Regular, Menlo, monospace;}
.route-url-warning{margin-top:6px; padding:7px 9px; border-radius:7px; font-size:11px; font-weight:600; line-height:1.4; max-width:280px;}
.route-url-warning.blocked{background:var(--danger-bg); color:var(--danger);}
.route-url-warning.pending{background:var(--warning-bg); color:var(--warning);}
.route-live-row{display:flex; gap:6px; margin-bottom:4px; flex-wrap:wrap;}

/* FORMS */
.field{margin-bottom:16px;}
.field label{display:block; font-size:12.5px; font-weight:600; margin-bottom:6px; color:var(--ink);}
.req{color:var(--danger); margin-left:3px; font-weight:600;}
/* Anti-spam honeypot -- see admin/includes/shared_functions.php's
   pf_antispam_fields()/pf_is_spam_submission() docblock. Off-screen
   rather than display:none, since some bots skip display:none fields
   but still auto-fill everything else. */
.pf-hp{position:absolute; left:-9999px; top:-9999px; width:1px; height:1px; overflow:hidden;}
.required-hint{font-size:11.5px; color:var(--muted); margin-bottom:12px;}
.required-hint .req{margin-left:0;}
.hint{font-size:11.5px; color:var(--muted); margin-top:5px;}
.empty-state{display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center; padding:2rem 1rem; color:var(--muted);}
.empty-state svg{margin-bottom:8px; opacity:0.6;}
.empty-state p{margin:0; font-size:13.5px; color:var(--ink);}
.empty-state p.hint{margin-top:4px;}
input[type=text], input[type=password], input[type=email], input[type=number], select, textarea{
  width:100%; padding:9px 11px; border:1px solid var(--line); border-radius:7px; font-size:13.5px; font-family:inherit; color:var(--ink); background:#fff;
}
/* Narrower than the shared width:100% rule on purpose -- a date value is
   a fixed, short shape ("07/23/2026"), so letting it stretch to fill
   whatever wide container it's in (a grid-2 half, a table cell) just
   leaves empty box around a small piece of text. 160px comfortably fits
   the browser's native mm/dd/yyyy rendering plus room for the icon
   below, and matches the width client/rate-grid.php's own date pickers
   already used before this rule existed.

   The calendar icon itself is a plain inline SVG painted as a CSS
   background image, not the browser's own native indicator -- some
   browsers (Safari in particular) don't render one at all, so relying on
   native chrome meant the icon simply didn't exist for anyone on those
   browsers, not a styling bug. Painting it ourselves guarantees it's
   there in every browser, engine-independent. Chrome/Edge/Safari's own
   ::-webkit-calendar-picker-indicator still exists underneath (see the
   rule just below) -- made invisible but left clickable, sized to cover
   the same icon area, so clicking it still opens that browser's native
   picker. Firefox has no equivalent pseudo-element to hide, so it shows
   its own icon alongside ours there; a minor cosmetic overlap, not a
   missing-icon regression, in the one engine this technique can't fully
   reach. */
input[type=date]{
  width:160px; padding:9px 34px 9px 11px; border:1px solid var(--line); border-radius:7px; font-size:13.5px; font-family:inherit; color:var(--ink);
  background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236B7684' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E") no-repeat right 10px center; background-size:16px 16px;
  position:relative;
}
input[type=date]::-webkit-calendar-picker-indicator{
  position:absolute; right:0; top:0; width:34px; height:100%; margin:0; padding:0; opacity:0; cursor:pointer;
}
input:focus, select:focus, textarea:focus{outline:2px solid var(--accent); outline-offset:1px;}

/* FLOATING-LABEL FIELD -- replaces the label-above-the-box pattern for
   text/email/tel/number/textarea/select inputs on request (same
   convention rolled out to the guest site's public/assets/css/style.css
   first -- see that file's own long comment for the full reasoning,
   reproduced briefly here since this is a separate stylesheet/app on
   its own subdomain). Idle and empty, the field name (plus " *" for
   required fields) sits inside the box as placeholder text; on focus,
   or once a value is present, it lifts into a small caption cut into
   the top border via a background-color patch behind the label text --
   .ffield's own background must match whatever panel/card it sits on
   (this app is almost entirely white panels, var(--panel), unlike the
   guest site's mixed white/cream sections, so one background value
   covers it here). Label must be the input's next sibling in markup
   for the :focus/:not(:placeholder-shown) + label selectors below to
   work. Pairs with pf-floating-fields.js in includes/footer.php, which
   blanks the placeholder on focus so it doesn't double up with the
   floated label, and restores it on blur if still empty. Left on the
   old .field styling, not converted: date fields (own established
   calendar-icon convention above), checkboxes, and any custom widget. */
.ffield{position:relative; margin-bottom:16px;}
.ffield input[type=text], .ffield input[type=email], .ffield input[type=tel], .ffield input[type=number], .ffield input[type=password], .ffield select, .ffield textarea{
  width:100%; padding:14px 11px 8px; border:1.5px solid var(--line); border-radius:7px; font-size:13.5px; font-family:inherit; color:var(--ink); background:#fff; box-sizing:border-box; transition:border-color .12s ease;
}
.ffield textarea{resize:vertical; min-height:80px; padding-top:16px;}
.ffield input::placeholder, .ffield textarea::placeholder{color:var(--muted);}
.ffield label{position:absolute; top:12px; left:10px; font-size:13.5px; color:var(--muted); pointer-events:none; transition:all .12s ease; background:transparent; padding:0 3px; max-width:calc(100% - 20px); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; opacity:0;}
.ffield input:focus, .ffield select:focus, .ffield textarea:focus{border-color:var(--accent); outline:none;}
.ffield input:focus + label,
.ffield input:not(:placeholder-shown) + label,
.ffield select:focus + label,
.ffield select + label,
.ffield textarea:focus + label,
.ffield textarea:not(:placeholder-shown) + label{
  top:-8px; left:8px; font-size:10.5px; background:var(--panel); padding:0 4px; opacity:1;
}
.ffield input:focus + label, .ffield select:focus + label, .ffield textarea:focus + label{color:var(--accent-dark);}
.ffield .req{margin-left:2px;}
.ffield.ff-error input, .ffield.ff-error select, .ffield.ff-error textarea{border-color:var(--danger);}
.ffield.ff-error label{color:var(--danger); background:var(--panel);}
.ffield .ff-err-msg{display:none; font-size:11.5px; color:var(--danger); margin-top:5px;}
.ffield.ff-error .ff-err-msg{display:block;}
.ffield .hint{font-size:11.5px; color:var(--muted); margin-top:5px;}

/* Currency-symbol-prefixed price field -- one flat span+input row, used
   anywhere a rate/surcharge is entered (Rate Grid, Rooms Rates tab).
   white-space:nowrap + flex-shrink:0 on the symbol keeps it from ever
   wrapping onto its own line regardless of container width. */
.price-input{display:inline-flex; align-items:center; gap:5px; white-space:nowrap;}
.price-input .cur{font-size:12.5px; color:var(--muted); flex-shrink:0;}
.price-input input[type=number]{width:100px; padding:7px 9px; font-size:13.5px;}

.btn{display:inline-flex; align-items:center; gap:6px; font-weight:600; font-size:13px; padding:9px 16px; border-radius:7px; border:1px solid transparent; cursor:pointer;}
.btn-primary{background:var(--accent); color:#fff;}
.btn-primary:hover{background:var(--accent-dark); text-decoration:none;}
.btn-outline{background:#fff; border-color:var(--line); color:var(--ink);}
.btn-outline:hover{border-color:var(--accent); text-decoration:none;}
.btn-danger{background:var(--danger-bg); color:var(--danger); border-color:#F1C6C4;}
.btn-sm{font-size:12px; padding:6px 12px;}

.toggle{position:relative; display:inline-block; width:38px; height:22px;}
.toggle input{opacity:0; width:0; height:0;}
.toggle .track{position:absolute; inset:0; background:#D3D8DF; border-radius:22px; transition:.15s;}
.toggle .track:before{content:""; position:absolute; height:16px; width:16px; left:3px; top:3px; background:#fff; border-radius:50%; transition:.15s;}
.toggle input:checked + .track{background:var(--success);}
.toggle input:checked + .track:before{transform:translateX(16px);}
.toggle input:disabled + .track{opacity:.5; cursor:not-allowed;}

.flash{border-radius:8px; padding:12px 16px; font-size:13.5px; margin-bottom:20px;}
.flash.success{background:var(--success-bg); color:var(--success); border:1px solid #C8E6C9;}
.flash.error{background:var(--danger-bg); color:var(--danger); border:1px solid #F1C6C4;}
.flash.warning{background:var(--warning-bg); color:var(--warning); border:1px solid #F0DDA6;}

/* CRUD confirmation toast -- fixed top-right, auto-dismissed by
   assets/js/toast.js (see data-toast). Reuses .flash's success/error/
   warning color roles so a toast and an inline .flash still read as
   the same "kind" of message everywhere else they're used. */
.toast-wrap{position:fixed; top:20px; right:20px; z-index:9999; display:flex; flex-direction:column; gap:10px; max-width:340px;}
.toast{display:flex; align-items:center; justify-content:space-between; gap:12px; border-radius:8px; padding:12px 14px; font-size:13.5px; box-shadow:0 4px 16px rgba(0,0,0,0.14); animation:pfToastIn .18s ease-out;}
.toast.success{background:var(--success-bg); color:var(--success); border:1px solid #C8E6C9;}
.toast.error{background:var(--danger-bg); color:var(--danger); border:1px solid #F1C6C4;}
.toast.warning{background:var(--warning-bg); color:var(--warning); border:1px solid #F0DDA6;}
.toast.hide{animation:pfToastOut .18s ease-in forwards;}
.toast-close{background:none; border:none; cursor:pointer; color:inherit; font-size:16px; line-height:1; padding:0; opacity:.6; flex-shrink:0;}
.toast-close:hover{opacity:1;}
@keyframes pfToastIn{from{opacity:0; transform:translateX(14px);} to{opacity:1; transform:translateX(0);}}
@keyframes pfToastOut{from{opacity:1; transform:translateX(0);} to{opacity:0; transform:translateX(14px);}}
@media(max-width:600px){.toast-wrap{left:16px; right:16px; max-width:none;}}

/* Every card/section header across the app is a light-green full-width
   bar (same color as .form-subsection below) -- one consistent color used
   everywhere a page introduces a section, rather than plain bold text,
   per standing convention. */
.section-title{font-size:15px; font-weight:700; color:var(--success); background:var(--success-bg); padding:10px 14px; border-radius:6px; margin-bottom:14px;}
.muted{color:var(--muted);}
.mt-24{margin-top:24px;}
.grid-2{display:grid; grid-template-columns:1fr 1fr; gap:16px;}
/* Rate Grid's Meal plan pricing / Extra bed row (client/rate-grid.php)
   -- overrides .grid-2's even split with a fixed-width second column,
   since Extra bed only ever holds one price field and an even split
   either stranded it in empty space or squeezed Meal plan pricing's
   wider table into an unnecessary internal scrollbar. 300px is enough
   for the price input + "per night" label + button without wrapping.
   min-width:0 on the first column keeps it (not this row) as the one
   that's allowed to shrink, so Extra bed's 300px stays fixed instead of
   also getting squeezed if the row is ever rendered somewhere narrower. */
.pf-rate-summary-row{grid-template-columns:1fr 300px;}
.pf-rate-summary-row > .card:first-child{min-width:0;}
.grid-3{display:grid; grid-template-columns:1fr 1fr 1fr; gap:16px;}
.room-specs-row{display:grid; grid-template-columns:1fr 1fr 1fr 1fr; gap:10px;}
.room-specs-row .field{margin-bottom:16px;}
.room-occupancy-row{display:grid; grid-template-columns:1fr 1fr 1fr; gap:10px;}
.room-occupancy-row .field{margin-bottom:16px;}
.pf-tabs{display:flex; gap:4px; border-bottom:1px solid var(--line); margin-bottom:18px;}
.pf-tab{padding:9px 16px; font-size:13.5px; font-weight:600; color:var(--muted); border:none; border-bottom:2px solid transparent; margin-bottom:-1px; display:inline-block; background:none; cursor:pointer; font-family:inherit;}
.pf-tab:hover{color:var(--ink); text-decoration:none;}
.pf-tab.active{color:var(--accent); border-bottom-color:var(--accent);}

/* Dark left-rail layout for Reservation Detail (task #696, mockup
   panels 5-8/10-11) -- reuses .pf-tab's existing markup/IDs/JS
   untouched (see reservation-detail.php's inline comment); this block
   only restyles .pf-tab when it's inside .pf-res-rail, from a
   horizontal underline-tab look to a vertical dark rail item, matching
   the same --sidebar/--sidebar-muted tokens the main nav sidebar
   already uses so the two dark columns read as one consistent system.
   Task #697 reuses this same system for the Invoice Engine's document-
   type rail (.pf-doc-*) on client/invoices.php -- see that file's
   comment for why it's a second class pair instead of retrofitting
   .pf-res-* directly (invoices.php isn't a "reservation" page and
   .pf-res-content's child selector below is shaped around
   reservation-detail.php's specific per-tab-panel div nesting, which
   invoices.php doesn't have). */
.pf-res-layout, .pf-doc-layout{display:flex; gap:0; align-items:flex-start; background:var(--panel); border-radius:10px; overflow:hidden; box-shadow:0 1px 2px rgba(0,0,0,.04);}
.pf-res-rail, .pf-doc-rail{background:var(--sidebar); flex:0 0 200px; padding:14px 10px; display:flex; flex-direction:column; gap:2px; align-self:stretch;}
.pf-res-rail .pf-tab, .pf-doc-rail .pf-tab{color:var(--sidebar-muted); border-bottom:none; margin-bottom:0; text-align:left; border-radius:6px; padding:10px 12px; white-space:nowrap;}
.pf-res-rail .pf-tab:hover, .pf-doc-rail .pf-tab:hover{color:#fff; background:rgba(255,255,255,.06);}
.pf-res-rail .pf-tab.active, .pf-doc-rail .pf-tab.active{color:#fff; background:var(--accent); border-bottom:none;}
.pf-res-content, .pf-doc-content{flex:1; min-width:0; padding:20px;}
.pf-res-content > div > .card{box-shadow:none; padding:0; margin-bottom:0;}
.pf-doc-content > .card{box-shadow:none; padding:0; margin-bottom:0;}
@media (max-width:900px){
  .pf-res-layout, .pf-doc-layout{flex-direction:column;}
  .pf-res-rail, .pf-doc-rail{flex-direction:row; flex-wrap:wrap; flex-basis:auto; width:100%;}
}
.feature-check-grid{display:grid; grid-template-columns:repeat(auto-fill, minmax(160px, 1fr)); gap:2px 14px; margin-bottom:6px;}
.feature-check-grid label{display:flex; align-items:center; gap:6px; font-size:13px; font-weight:400; padding:5px 0;}
.feature-check-grid input[type=checkbox]{width:auto;}
/* See admin/assets/css/admin.css for why this override is needed — same
   sibling-combinator misalignment bug, same fix, copied file. */
.grid-2 > .card + .card{margin-top:0;}

/* Rate Grid: rate-type picker (segmented control) and small utility
   classes rate-grid.php's new hierarchy UI needs that nothing else on
   the client side had a use for yet -- badge status colors for the
   Active/Expired column on the all-rates view, and a plain-link-styled
   button for inline Delete actions inside a table cell. */
.segmented-control{display:flex; gap:10px; margin-bottom:18px; flex-wrap:wrap;}
.seg-btn{display:inline-block; width:auto; text-align:left; padding:11px 15px; border:1px solid var(--line); border-radius:10px; background:#fff; cursor:pointer;}
.seg-btn:hover{border-color:#C7D2E0;}
.seg-btn .seg-priority{display:block; font-size:11px; font-weight:600; color:var(--muted); margin-bottom:2px;}
.seg-btn .seg-label{display:block; font-size:13px; font-weight:700; color:var(--ink); white-space:nowrap;}
.seg-btn.active{background:#EAF0FF; border-color:var(--accent); border-width:2px; padding:10px 14px;}
.seg-btn.active .seg-priority, .seg-btn.active .seg-label{color:var(--accent-dark);}
.btn-link{background:none; border:none; padding:0; font-size:13px; font-weight:600; cursor:pointer; text-decoration:underline;}


/* TEMPLATE PICKER — property.php's Template & Theme card. Each card is a
   <label> wrapping a hidden radio + a visual body; selection state is pure
   CSS (the radio is a preceding sibling of .template-card-body), no JS
   needed. */
.template-grid{display:grid; grid-template-columns:repeat(auto-fill, minmax(180px, 1fr)); gap:14px; margin-top:6px;}
.template-card{cursor:pointer; position:relative;}
.template-card-radio{position:absolute; opacity:0; width:0; height:0;}
/* height:100% + flex column makes this stretch to the grid row's height
   (CSS Grid's default align-items:stretch already gives label.template-card
   -- and so this span -- a definite row height to resolve against) instead
   of only being as tall as its own content. Without this, cards whose
   Description text wraps to a different number of lines than their
   neighbors end up with mismatched border-box heights, so the row's bottom
   edges zig-zag instead of lining up -- confirmed against a live screenshot
   (the thumbnail boxes above them were already a fixed, uniform height, see
   .template-card-thumb, so this was the one remaining source of visible
   misalignment in the card grid). Shorter cards simply pick up blank space
   below their description instead of a shorter box -- approved via mockup. */
.template-card-body{display:flex; flex-direction:column; height:100%; border:2px solid var(--line); border-radius:10px; overflow:hidden; background:#fff; transition:border-color .12s, box-shadow .12s; position:relative;}
.template-card-radio:checked + .template-card-body{border-color:var(--accent); box-shadow:0 0 0 3px rgba(47,93,232,0.15);}
/* Explicit text badge on top of the border+glow highlight above -- that
   alone turned out too subtle to notice at a glance, especially between
   cards with similar thumbnail colors. Rendered server-side only on the
   card matching the DB-saved Template_ID (client/theme.php), NOT bound to
   :checked -- clicking a different radio changes :checked immediately
   (native browser behavior, needed for the form to submit the right
   value), but this badge is meant to answer "what's actually live", which
   only changes after a real Save. No display:none/:checked toggle needed
   here since PHP only prints the element for the one matching card. */
.template-card-selected{position:absolute; top:8px; right:8px; z-index:2; background:var(--accent); color:#fff; font-size:10.5px; font-weight:700; text-transform:uppercase; letter-spacing:.3px; padding:3px 9px; border-radius:20px;}
/* .template-card-thumb is a fixed-size span wrapper (not the <img> itself --
   see theme.php) so real-photo thumbnails and the gradient-generated
   fallback both get a guaranteed-uniform 90px box regardless of the
   source image's own dimensions; object-fit:cover then lives on the
   <img> child, cropping into that fixed box instead of stretching it. */
.template-card-thumb{display:flex; align-items:center; justify-content:center; width:100%; height:90px; overflow:hidden; color:#fff; font-weight:700; font-size:15px; text-align:center; padding:8px; box-sizing:border-box;}
.template-card-thumb img{display:block; width:100%; height:100%; object-fit:cover;}
.template-card-name{display:block; font-size:13px; font-weight:600; padding:8px 10px 2px;}
.template-card-desc{display:block; font-size:11px; color:var(--muted); padding:0 10px 10px;}

/* COLOR PALETTE — same card's color-override pickers. Swatches are radio
   inputs styled as circles; the checkmark is a sibling span shown only
   when its radio is :checked, so this also needs no JS. */
.color-palette{display:flex; flex-wrap:wrap; gap:8px; margin-top:4px; align-items:center;}
.color-swatch{position:relative; display:inline-flex; align-items:center; justify-content:center; width:30px; height:30px; border-radius:50%; cursor:pointer; border:2px solid var(--line);}
.color-swatch input{position:absolute; inset:0; opacity:0; margin:0; cursor:pointer;}
.color-swatch-check{opacity:0; color:#fff; font-size:14px; text-shadow:0 0 2px rgba(0,0,0,.7); pointer-events:none;}
.color-swatch input:checked ~ .color-swatch-check{opacity:1;}
.color-swatch-none{width:auto; height:30px; padding:0 10px; border-radius:16px; background:#fff; font-size:11.5px; color:var(--muted); gap:0;}
.color-swatch-none input:checked ~ span:last-child{color:var(--ink); font-weight:600;}

/* MODULE ACCESS CHECKLIST — client/users.php and admin/platform-admins.php's
   flat per-account module grants (migration 027 replaced the old named-role
   /per-action-tier system with this: an account either has full access via
   Owner/Is_Primary, or a direct set of modules is checked here, each
   granting full access to that module). Plain checkboxes in a wrapping
   flex row -- no custom radio-as-styled-control CSS needed the tier
   segment used to require, since each module is now an independent
   on/off grant rather than one-of-four-tiers. */
.module-checklist{display:flex; flex-wrap:wrap; gap:6px 16px; padding:8px 0;}
.module-checkbox{display:inline-flex; align-items:center; gap:6px; font-size:13px; color:var(--ink); cursor:pointer; white-space:nowrap;}
.module-checkbox input{margin:0;}
.module-checkbox.muted{color:var(--muted);}

/* FORM SUBSECTIONS — property.php's Property details form groups related
   fields (Basic info / Location and contact / Currency / Category and
   policy / Logo) under a small heading + divider, replacing one long
   undifferentiated field list. */
/* Each subsection on property.php's Property details form is a full-width
   solid-color bar instead of a plain grey divider line -- the whole row is
   colored, not just a marker next to the text, so a section boundary is
   unmistakable while scanning/scrolling the form. Every section uses the
   same light green (this app's existing --success/--success-bg pair) so
   the bars read as one consistent visual rhythm down the form, not a
   different color per section. */
.form-subsection{display:block; width:100%; font-size:11.5px; font-weight:700; text-transform:uppercase; letter-spacing:.4px; color:var(--success); background:var(--success-bg); margin:22px 0 14px; padding:9px 14px; border-radius:6px;}
.form-subsection:first-of-type{margin-top:0;}

/* CHECK-IN/CHECK-OUT TIME BOX — property.php's Policies tab. Same green
   --success/--success-bg pair as .form-subsection/.flash.success/
   .toast.success above, plus the same #C8E6C9 border those last two
   already use, so this reads as "the same green" everywhere else in the
   app uses it rather than a one-off color. Margin-bottom (not just the
   hint paragraph's own spacing) is what puts real air between this box
   and the Cancellation policy field below it -- relying on the hint
   paragraph's default <p> margin alone left them visually touching. */
.checkin-checkout-box{background:var(--success-bg); border:1px solid #C8E6C9; border-radius:8px; padding:14px 16px; margin-bottom:24px; max-width:760px;}
.checkin-checkout-box .checkin-checkout-label{display:flex; align-items:center; gap:6px; font-size:13px; font-weight:700; color:var(--success); margin-bottom:12px;}
/* Date modification folded in as a 3rd column (Option A reflow) --
   max-width bumped 480px -> 760px above so all three columns (two
   time-dropdown trios + one select) fit without cramping. The divider
   class below draws a thin rule ahead of the 3rd column only, so it
   reads as "check-in/out, then a separate-but-related setting" rather
   than three interchangeable fields -- same #C8E6C9 border color as
   the box itself so the divider recedes instead of competing with it. */
.checkin-checkout-grid > .checkin-checkout-divider{border-left:1px solid #C8E6C9; padding-left:16px;}
.cancellation-tiers-box{background:var(--success-bg); border:1px solid #C8E6C9; border-radius:8px; padding:14px 16px; margin-bottom:24px;}
.cancellation-tiers-box .checkin-checkout-label{margin-bottom:4px;}
.cancellation-tiers-box > .hint{margin-bottom:14px;}
.hint-bullet-list{font-size:11.5px; color:var(--muted); margin:4px 0 6px; padding-left:16px;}
.hint-bullet-list li{margin-bottom:2px;}

/* ACCEPTED-CURRENCY CHIPS — property.php's Currencies card. A rotating set
   of background/text color pairs assigned by position (see
   $pf_currencyChipColors), purely so several accepted currencies are easy
   to tell apart at a glance -- not tied to any particular currency. */
.currency-chip{display:inline-flex; align-items:center; gap:6px; font-size:12px; font-weight:600; padding:5px 10px; border-radius:20px;}
.currency-chip-remove{border:none; background:none; cursor:pointer; padding:0; font-weight:700; line-height:1; color:inherit; opacity:.6;}
.currency-chip-remove:hover{opacity:1;}
.chip-blue{background:#EAF0FF; color:var(--accent-dark);}
.chip-teal{background:#E1F5F2; color:#0F766E;}
.chip-gold{background:#FCF1DD; color:#8A6417;}
.chip-violet{background:#F1EBFD; color:#6D3FC2;}
.chip-rose{background:#FDEAF2; color:#B3115B;}
.chip-green{background:var(--success-bg); color:var(--success);}

/* CLICKABLE PROPERTY ROW — properties.php's list. The whole name cell is a
   submit button (posts to switch-property.php) styled to look like a plain
   link with a trailing chevron, so clicking a property switches to it and
   opens its Property Profile — kept as a real <button> (not a JS-driven
   row click) so it works with this app's no-JS convention and remains a
   normal, auditable POST. */
.property-row-link{display:inline-block;}
.property-row-link-btn{display:inline-flex; align-items:center; gap:2px; border:none; background:none; cursor:pointer; padding:0; font:inherit; font-weight:600; color:var(--ink);}
.property-row-link-btn:hover{color:var(--accent);}
.property-row-link-chevron{font-size:16px; color:var(--muted); line-height:1;}
.property-row-link-btn:hover .property-row-link-chevron{color:var(--accent);}

/* CHILD AGE POLICY — property.php's own card (not nested inside the main
   Property details form, since its add/edit/remove each need their own
   POST action -- HTML doesn't allow a <form> nested inside another
   <form>). Accent-bordered so it reads as a distinct, newer feature
   alongside the plain-bordered cards around it, matching the callout
   treatment already used for an in-progress inline edit elsewhere in this
   file (.child-policy-edit). */
.child-policy-box{border:1.5px solid var(--accent); border-radius:10px; padding:18px; background:#FAFBFF;}
.child-policy-title{font-size:12.5px; font-weight:700;}
.child-policy-row{border:1px solid var(--line); border-radius:8px; padding:12px 14px; background:#fff; display:flex; justify-content:space-between; align-items:flex-start; gap:14px;}
.child-policy-row-label{font-size:13px; font-weight:700; margin-bottom:4px;}
.child-policy-row-text{font-size:12.5px; line-height:1.5;}
.child-policy-row-actions{display:flex; gap:12px; flex:0 0 auto; padding-top:2px;}
.child-policy-row-actions a{font-size:12px; font-weight:600;}
.child-policy-row-actions button{border:none; background:none; color:var(--muted); font-size:12px; cursor:pointer; padding:0;}
/* "Edit" is a <button> now (JS show/hide, not a navigation -- see property.php), styled to match how the old <a> looked rather than the plain "Remove" button beside it. */
.child-policy-row-actions button.btn-link{color:var(--accent); text-decoration:none; font-weight:600;}
.child-policy-row-actions button.btn-link:hover{text-decoration:underline;}
.pf-cap-error{background:var(--danger-bg); color:var(--danger); border:1px solid #F1C6C4; border-radius:6px; padding:8px 10px; font-size:12.5px; margin-bottom:10px;}
.child-policy-edit{border:1.5px solid var(--accent); border-radius:8px; padding:14px; background:#fff;}
.child-policy-edit + .child-policy-edit, .child-policy-edit + .child-policy-row, .child-policy-row + .child-policy-edit{margin-top:0;}
.child-policy-edit-label{font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:.4px; color:var(--muted); margin-bottom:8px;}
.child-policy-age-row{display:flex; align-items:center; gap:8px;}
.child-policy-age-row input[type=number]{width:80px; text-align:center;}
.child-policy-checkbox-row{display:flex; flex-wrap:wrap; gap:8px 24px;}
/* Add-panel bulk rows (property.php, pfAddChildPolicyBulkRow()) --
   deliberately compact (single line per bracket, no per-row labels or
   hint text -- one shared hint sits above the whole #pf_cap_bulk_rows
   box instead) since several of these can be stacked at once; the
   original full-labels-and-hints .child-policy-edit card style used
   here at first took up far too much vertical space once more than one
   or two brackets were being added in the same sitting. */
#pf_cap_bulk_rows{border:1px solid var(--line); border-radius:8px; padding:0 10px; background:#fff;}
.pf-cap-bulk-row{padding:8px 0; border-bottom:1px solid var(--line);}
.pf-cap-bulk-row:last-child{border-bottom:none;}
/* .pf-cap-bulk-row-fields is the compact single-line field layout shared
   by both the Add panel's stacked rows (each wrapped in .pf-cap-bulk-row
   above) and the inline Edit panel for an existing bracket (which has no
   .pf-cap-bulk-row wrapper of its own -- see property.php,
   pf_cap_edit_<id> / pfChildPolicyEditInnerHtml()). Scoped to
   .pf-cap-bulk-row-fields itself rather than to .pf-cap-bulk-row so both
   places get the same narrow inputs regardless of that wrapper. */
.pf-cap-bulk-row-fields{display:flex; align-items:flex-start; gap:10px;}
.pf-cap-bulk-row-fields .child-policy-age-row{flex:0 0 auto;}
.pf-cap-bulk-row-fields .child-policy-age-row input[type=number]{width:52px;}
.pf-cap-bulk-policy{flex:1; min-width:0;}
.pf-cap-bulk-flags{display:flex; gap:10px; flex:0 0 auto; padding-top:8px; white-space:nowrap; font-size:12px;}
.pf-cap-bulk-flags label{display:flex; align-items:center; gap:4px; font-weight:400;}
.pf-cap-bulk-flags input[type=checkbox]{width:auto;}
.pf-cap-bulk-remove{flex:0 0 auto; width:22px; height:22px; padding:0; margin-top:2px; border:none; background:none; color:var(--muted); font-size:15px; line-height:1; cursor:pointer;}
.pf-cap-bulk-remove:hover{color:var(--danger);}
.pf-cap-bulk-row .pf-cap-error{margin:8px 0 0;}
.pf-cap-edit > .pf-cap-error{margin-bottom:10px;}

/* CMS basic rich-text editor (client/cms.php) -- a contenteditable div
   dressed up to look like the rest of this form's inputs, with a small
   toolbar above it. Buttons reuse .btn/.btn-outline/.btn-sm as-is, this
   just styles the toolbar strip and the editable surface itself. */
.rte-toolbar{display:flex; flex-wrap:wrap; gap:6px; margin-bottom:8px;}
.rte-toolbar button{min-width:34px; padding:6px 10px;}
.rte-editable{
  width:100%; min-height:160px; padding:11px 12px; border:1px solid var(--line); border-radius:7px;
  font-size:13.5px; font-family:inherit; color:var(--ink); background:#fff; line-height:1.6; overflow-y:auto;
}
.rte-editable:focus{outline:none; border-color:var(--accent);}
.rte-editable h2{font-size:17px; margin:0 0 8px;}
.rte-editable p{margin:0 0 10px;}
.rte-editable ul, .rte-editable ol{margin:0 0 10px; padding-left:20px;}
.rte-editable img{max-width:100%; border-radius:6px;}
.rte-editable a{color:var(--accent);}

/* Pricing Rules list-detail layout (client/rate-grid.php, migration
   068) -- a left-hand list of every named pricing rule plus the
   permanent "Default" entry, and a right-hand detail pane for whichever
   one is selected. Room rates/meal plan pricing/extra bed all live
   inside the same .card in the detail pane now (rather than three
   separate cards) since they're always for the same date range -- see
   the page's own docblock for the full reasoning. */
.pf-pricing-layout{display:flex; gap:20px; align-items:flex-start;}
.pf-pricing-list{flex:0 0 270px; display:flex; flex-direction:column; gap:10px; position:sticky; top:20px;}
.pf-pricing-detail{flex:1 1 auto; min-width:0;}
.pf-pricing-list-card{
  display:block; background:var(--panel); border:1px solid var(--line); border-radius:10px; padding:12px 14px;
  color:inherit; text-decoration:none; transition:border-color .12s, background .12s;
}
.pf-pricing-list-card:hover{border-color:#C7D2E0; text-decoration:none;}
.pf-pricing-list-card.active{border-color:var(--accent); border-width:2px; padding:11px 13px; background:#FAFBFF;}
.pf-pricing-list-card .name{font-size:14px; font-weight:700; color:var(--ink);}
.pf-pricing-list-card .meta{font-size:12px; color:var(--muted); margin-top:2px;}
.pf-pricing-list-card .counts{display:flex; align-items:center; gap:8px; margin-top:8px; font-size:11.5px; color:var(--muted); flex-wrap:wrap;}

/* Selected rule's header row -- Name field (inline-editable, styled to
   look like plain text until focused) plus Priority/Active badges on
   the left, Delete rule on the right. */
.pf-rule-header{display:flex; align-items:flex-start; justify-content:space-between; gap:16px; margin-bottom:10px;}

/* Meal plan pricing / Extra bed accordion rows within a selected rule's
   card -- native <details>/<summary>, same zero-JS pattern already used
   by client/rooms.php's "add a custom item" disclosure and the sidebar
   nav groups, just styled to read as a bordered, tappable section
   header rather than plain text. */
.pf-accordion{border:1px solid var(--line); border-radius:10px; padding:14px 16px; margin-top:14px;}
.pf-accordion summary{cursor:pointer; font-weight:700; font-size:13.5px; color:var(--ink); list-style:none;}
.pf-accordion summary::-webkit-details-marker{display:none;}
.pf-accordion summary::before{content:"\25B8"; display:inline-block; margin-right:8px; color:var(--muted); transition:transform .12s;}
.pf-accordion[open] summary::before{transform:rotate(90deg);}

/* Pricing Rules lifecycle (migration 069) -- the banner explaining a
   selected rule's current editability (Draft/Active-unlocked/Locked/
   Inactive), plus the badge colors pf_pricing_rule_status_badge_class()
   maps each display_status onto. Reuses .badge's existing on/off/warn/
   mode-single/mode-multi variants (client/assets/css/admin.css, defined
   above) rather than inventing new ones -- warn (amber) for Draft,
   mode-single (blue) for Scheduled, on (green) for Active, off (grey)
   for both Expired and Inactive, since visually a hotelier doesn't need
   to tell those two apart at a glance the way they need Draft/Active
   told apart. */
.pf-lock-banner{
  border-radius:8px; padding:11px 14px; margin-bottom:16px; font-size:13px; line-height:1.5;
  background:var(--bg); border:1px solid var(--line); color:var(--ink);
}
.pf-lock-banner-draft{background:#FFF8E8; border-color:#F0D9A0; color:#8A6D1D;}
.pf-lock-banner-active{background:var(--success-bg); border-color:#BFE3CC; color:var(--success);}
.pf-lock-banner-locked{background:var(--danger-bg); border-color:#F0C4C4; color:var(--danger);}

@media(max-width:900px){
  .shell{grid-template-columns:1fr;}
  .sidebar{display:none;}
  .stat-grid, .grid-2{grid-template-columns:1fr;}
  .room-specs-row{grid-template-columns:1fr 1fr;}
  .room-occupancy-row{grid-template-columns:1fr 1fr;}
  .checkin-checkout-grid{grid-template-columns:1fr;}
  .checkin-checkout-grid > .checkin-checkout-divider{border-left:none; padding-left:0; border-top:1px solid #C8E6C9; padding-top:14px; margin-top:2px;}
  .pf-pricing-layout{flex-direction:column;}
  .pf-pricing-list{flex:1 1 auto; position:static; flex-direction:row; flex-wrap:wrap;}
  .pf-pricing-list-card{flex:1 1 220px;}
}

/* Shared custom calendar for every admin-section date field
   (client/assets/js/pf-calendar.js -- identical file also lives at
   admin/assets/js/pf-calendar.js, see that file's own docblock for why).
   Originally a Rate Grid-only build, generalized to every date field in
   both apps -- solves two problems the native <input type=date> popup
   can't: it can render partly below the visible viewport with no way
   for us to reposition or resize it (a hard platform limitation, not a
   CSS bug -- see the guest site's own .bbf-cal-* comment in
   public/assets/css/style.css for the same root cause there), and its
   own closed-state text is rendered by the browser/OS in whatever
   locale format it prefers, which can never consistently show
   "01 Jan 2026" the way every other date display in this app does.
   Each date field is now `.pf-date-field` wrapping a real hidden input
   (the actual submitted value, under the field's original name/id) and
   a real `.pf-date-display` text input showing the formatted date --
   see pf-calendar.js's docblock for the full markup contract and why
   the hidden input deliberately carries no `required` attribute. One
   shared popup panel is reused for every date field on the page rather
   than one per field. */
.pf-date-field{position:relative; display:inline-block;}
.pf-date-display{
  width:160px; padding:9px 34px 9px 11px; border:1px solid var(--line); border-radius:7px; font-size:13.5px; font-family:inherit; color:var(--ink); cursor:pointer;
  background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236B7684' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E") no-repeat right 10px center; background-size:16px 16px;
}
.pf-date-display:disabled{cursor:not-allowed; color:var(--muted); background-color:var(--bg);}
.pf-cal-panel{display:none; position:fixed; width:260px; background:#fff; border-radius:8px; border:1px solid var(--line); box-shadow:0 16px 40px rgba(27,36,48,0.18); padding:14px; z-index:300;}
.pf-cal-panel.is-open{display:block;}
.pf-cal-head{display:flex; align-items:center; justify-content:space-between; margin-bottom:10px;}
.pf-cal-month-label{font-size:13px; font-weight:600; color:var(--ink);}
.pf-cal-nav{width:24px; height:24px; border-radius:50%; border:1px solid var(--line); background:#fff; cursor:pointer; font-size:14px; line-height:1; color:var(--muted); padding:0;}
.pf-cal-dow{display:grid; grid-template-columns:repeat(7,1fr); gap:2px; margin-bottom:2px;}
.pf-cal-dow span{text-align:center; font-size:10px; color:var(--muted); font-weight:600;}
.pf-cal-grid{display:grid; grid-template-columns:repeat(7,1fr); gap:2px;}
.pf-cal-day{text-align:center; padding:6px 0; font-size:12.5px; color:var(--ink); cursor:pointer; border-radius:14px;}
.pf-cal-day:hover{background:var(--bg);}
.pf-cal-day.is-muted{color:var(--line); cursor:not-allowed;}
.pf-cal-day.is-muted:hover{background:none;}
.pf-cal-day.is-selected{background:var(--accent); color:#fff;}
.pf-cal-day.is-selected:hover{background:var(--accent);}

/* Inventory Engine calendar grid (client/inventory.php) -- a real 14-
   night calendar, rooms down the side (sticky first column so a wide
   date range doesn't scroll the room name off screen), one column per
   night. Reuses the page's existing overflow-x:auto wrapper convention
   (see rate-grid.php) rather than any bespoke scroll handling. Distinct
   pf-invgrid- prefix, not pf-cal- -- that prefix already belongs to the
   unrelated pf-calendar.js date-picker popup component. */
.pf-invgrid{width:auto; min-width:100%;}
.pf-invgrid th, .pf-invgrid td{text-align:center; white-space:nowrap; padding:8px 10px; font-size:12.5px;}
.pf-invgrid th:first-child, .pf-invgrid td:first-child{text-align:left;}
.pf-invgrid-today{background:var(--bg); border-radius:4px 4px 0 0;}
.pf-invgrid-cell{font-weight:600;}
.pf-invgrid-blocked{background:var(--danger-bg); color:var(--danger); font-weight:600; font-size:11px; text-transform:uppercase; letter-spacing:.3px;}
.pf-invgrid-sold{background:var(--warning-bg); color:var(--warning);}
/* Room-name divider row above each room type's Total/Available/Blocked/
   Sold sub-rows (task #695, mockup panel 1 parity) -- a shaded, bolded
   spanning row rather than a rowspan on the label column, since rowspan
   interacts awkwardly with this table's sticky first-column CSS above. */
.pf-invgrid-group td{background:var(--bg); border-top:1px solid var(--border);}

/* Engine shell -- the reference mockup's dark left-rail-within-a-panel
   pattern, distinct from the main app .sidebar (that's the whole app's
   navigation; this is a secondary rail scoped to one screen's own
   sub-views -- User & Access's Users/Roles/Permissions/Access Logs,
   Reservation Detail's Overview/Guest Details/etc., Invoice Engine's
   Invoice/Credit Notes/Receipts, Settings' own section list). Same dark
   navy + accent-blue-active language as .sidebar so it reads as part of
   the same visual system, just one level down. Two-column flex layout;
   .engine-rail is a fixed width, .engine-main takes the rest. */
.engine-shell{display:flex; gap:0; background:var(--panel); border-radius:12px; overflow:hidden; box-shadow:0 1px 2px rgba(16,24,40,.04); border:1px solid var(--line);}
.engine-rail{flex:0 0 220px; background:var(--sidebar); padding:14px 10px; display:flex; flex-direction:column; gap:2px;}
.engine-rail-link{display:flex; align-items:center; gap:10px; padding:10px 12px; border-radius:8px; color:var(--sidebar-muted); font-size:13.5px; font-weight:500;}
.engine-rail-link svg{width:17px; height:17px; flex:0 0 auto;}
.engine-rail-link:hover{color:#fff; text-decoration:none;}
.engine-rail-link.active{background:var(--accent); color:#fff;}
.engine-main{flex:1; min-width:0; padding:22px; background:var(--bg);}
.engine-main .card:first-child{margin-top:0;}
@media (max-width:860px){
  .engine-shell{flex-direction:column;}
  .engine-rail{flex-direction:row; flex-wrap:wrap; width:100%;}
}
