/* ---------------------------------------------------------------------------
   Theme tokens.

   Colours are raw RGB channels so Tailwind's opacity modifiers keep working
   (bg-surface/90). The neutral "ink" scale is inverted in the light theme:
   components always ask for ink-900 for a heading and ink-100 for the page,
   and the theme decides whether that is dark-on-light or light-on-dark.

   Dark is the default; an explicit light choice sets data-theme="light".
--------------------------------------------------------------------------- */
:root {
    color-scheme: dark;

    --canvas:  11  17  22;
    --surface: 19  26  33;
    --raised:  27  36  45;

    --ink-50:  15  20  26;
    --ink-100: 19  26  33;
    --ink-200: 39  50  61;
    --ink-300: 61  75  89;
    --ink-400: 122 139 155;
    --ink-500: 148 163 179;
    --ink-600: 176 189 202;
    --ink-700: 203 213 224;
    --ink-800: 226 232 240;
    --ink-900: 241 245 249;
    --ink-950: 248 250 252;

    --brand-50:  16  46  44;
    --brand-100: 19  62  58;
    --brand-200: 21  84  78;
    --brand-300: 24 116 107;
    --brand-400: 45 168 154;
    --brand-500: 34 197 180;
    --brand-600: 45 212 191;
    --brand-700: 94 234 212;
    --brand-800: 153 246 228;
    --brand-900: 204 251 241;

    --up:   52 211 153;
    --down: 248 113 113;
    --warn: 251 191  36;
    --info:  96 165 250;

    --tint-up:    6  59  46;
    --tint-down: 69  10  10;
    --tint-warn: 69  26   3;
    --tint-info: 23  37  84;
    --tint-brand:16  46  44;

    /* The sidebar is chrome, not content: it stays dark in both themes and so
       needs its own scale rather than the inverted ink one. */
    --sidebar:        15  21  27;
    --sidebar-hover:  27  36  45;
    --sidebar-fg:    159 176 191;
    --sidebar-muted:  93 107 120;
    --sidebar-strong:241 245 249;

    --shadow-card: 0 1px 2px 0 rgb(0 0 0 / 0.35), 0 1px 3px 0 rgb(0 0 0 / 0.30);
    --shadow-pop:  0 10px 30px -12px rgb(0 0 0 / 0.70);
}

:root[data-theme="light"] {
    color-scheme: light;

    --canvas:  241 245 249;
    --surface: 255 255 255;
    --raised:  248 250 252;

    --ink-50:  248 250 252;
    --ink-100: 241 245 249;
    --ink-200: 226 232 240;
    --ink-300: 203 213 225;
    --ink-400: 148 163 184;
    --ink-500: 100 116 139;
    --ink-600:  71  85 105;
    --ink-700:  51  65  85;
    --ink-800:  30  41  59;
    --ink-900:  15  23  42;
    --ink-950:   2   6  23;

    --brand-50:  239 252 249;
    --brand-100: 211 245 238;
    --brand-200: 168 234 221;
    --brand-300: 111 217 200;
    --brand-400:  56 191 173;
    --brand-500:  23 163 148;
    --brand-600:  13 148 136;
    --brand-700:  15 118 110;
    --brand-800:  17  94  89;
    --brand-900:  19  78  74;

    --up:     5 150 105;
    --down: 220  38  38;
    --warn: 217 119   6;
    --info:  37  99 235;

    --tint-up:   236 253 245;
    --tint-down: 254 242 242;
    --tint-warn: 255 251 235;
    --tint-info: 239 246 255;
    --tint-brand:239 252 249;

    --sidebar:        15  23  42;
    --sidebar-hover:  30  41  59;
    --sidebar-fg:    203 213 225;
    --sidebar-muted: 100 116 139;
    --sidebar-strong:255 255 255;

    --shadow-card: 0 1px 2px 0 rgb(15 23 42 / 0.04), 0 1px 3px 0 rgb(15 23 42 / 0.06);
    --shadow-pop:  0 10px 30px -12px rgb(15 23 42 / 0.25);
}

/* Activity Panel — small stylesheet on top of the Tailwind CDN build. */

[x-cloak] { display: none !important; }

/* The bottom bar owns its geometry here rather than through utilities.
   `pb-[env(safe-area-inset-bottom)]` was an arbitrary Tailwind value whose
   generated selector has to escape parentheses and which carries no fallback,
   so a single mis-parse took the whole rule — and the bar's sizing — with it.
   Plain CSS also lets the five cells be explicitly minmax(0, 1fr), so a long
   label can never widen the bar past the screen. */
.bottom-nav {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 30;
    box-sizing: border-box;
    width: 100%;
    max-width: 100%;
    display: grid;
    /* The bar carries five or six items depending on whether the property runs
       a guest-call round; the count comes in as an inline custom property. */
    grid-template-columns: repeat(var(--bottom-nav-count, 5), minmax(0, 1fr));
    padding-bottom: env(safe-area-inset-bottom, 0px);
}

.bottom-nav > a {
    min-width: 0;
    overflow: hidden;
}

@media (min-width: 1024px) {
    .bottom-nav { display: none; }
}

/* The sidebar's link list scrolls under a 4rem-tall brand block. Expressed here
   for the same reason as the bottom bar: an arbitrary utility with parentheses
   is one escaping quirk away from dropping out entirely. */
.drawer-links {
    max-height: calc(100vh - 4rem);
    overflow-y: auto;
}

/* Off-canvas navigation. Declared here so the closed position applies on the
   first paint; Alpine only ever adds or removes .is-open. */
.drawer {
    transform: translateX(-100%);
    transition: transform 200ms ease;
}
.drawer.is-open { transform: translateX(0); }

@media (min-width: 1024px) {
    .drawer { transform: none; transition: none; }
}

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

/* The theme icon follows the attribute the boot script sets before first paint,
   so it never flashes the wrong glyph while Alpine is still loading. */
.theme-icon-light { display: none; }
:root[data-theme="light"] .theme-icon-dark { display: none; }
:root[data-theme="light"] .theme-icon-light { display: block; }

/* iOS inflates text in some blocks when the device rotates. */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

/* The page must never scroll sideways. Anything that needs horizontal room —
   a wide table, a swipe row — scrolls inside its own box; letting the document
   scroll instead drags the content out from under the fixed bottom bar, which
   is what made the bar look misaligned.
   `hidden` and not `clip`: only hidden propagates from the root to the viewport
   in Chrome, and because it propagates, the root itself still behaves as
   `visible`, so the sticky header keeps working. */
html,
body {
    overflow-x: hidden;
    max-width: 100%;
}

body { position: relative; }

/* Safari renders its own control chrome unless appearance is reset, which left
   the filter selects looking unstyled on iPhone and iPad. */
select, input, textarea, button { font: inherit; }
select {
    -webkit-appearance: none;
    appearance: none;
    background-image: none;
}

@media (max-width: 1023px) {
    /* Below 16px iOS zooms the whole page when a control takes focus, which is
       what made the layout look broken rather than any style being wrong. */
    select,
    textarea,
    input:not([type='checkbox']):not([type='radio']) {
        font-size: 16px;
    }

    /* Comfortable tap targets on touch screens. */
    a, button, label, select { -webkit-tap-highlight-color: rgb(var(--brand-500) / 0.15); }
    .table-scroll select,
    header select,
    header button,
    header a[href] { min-height: 40px; }
}

/* ---------------------------------------------------------------------------
   Compact tables.

   A four-column table (property, two periods, change) fits a phone comfortably,
   and reading nine properties as nine rows beats nine stacked cards — that is
   how the team reads the figures in the report this replaces. Only genuinely
   wide tables become cards; narrow ones just tighten up.
--------------------------------------------------------------------------- */
@media (max-width: 1023px) {
    table.compact-sm { min-width: 0 !important; width: 100%; font-size: 12px; }

    table.compact-sm th,
    table.compact-sm td { padding: 6px 5px !important; white-space: nowrap; }

    table.compact-sm th:first-child,
    table.compact-sm td:first-child { padding-left: 12px !important; }

    table.compact-sm th:last-child,
    table.compact-sm td:last-child { padding-right: 12px !important; }

    /* The name column may wrap; the figures must not. */
    table.compact-sm td:first-child { white-space: normal; }
    table.compact-sm th { font-size: 9px; letter-spacing: 0.02em; }
    table.compact-sm .col-sticky { position: static; }

    /* Same typography, but wide tables keep their min-width and scroll inside
       the card rather than being squeezed unreadably. */
    table.compact-scroll { font-size: 12px; }
    table.compact-scroll th,
    table.compact-scroll td { padding: 6px 8px !important; white-space: nowrap; }
    table.compact-scroll th { font-size: 9px; letter-spacing: 0.02em; }
    table.compact-scroll td:first-child { white-space: normal; }
}

/* The larger phones (16 Plus / Pro Max at 430-440pt) have room to spare, so
   spend it on legibility rather than leaving the figures needlessly small. */
@media (min-width: 414px) and (max-width: 1023px) {
    table.compact-sm,
    table.compact-scroll { font-size: 13px; }
    table.compact-sm th,
    table.compact-sm td,
    table.compact-scroll th,
    table.compact-scroll td { padding: 7px 8px !important; }
    table.compact-sm th,
    table.compact-scroll th { font-size: 10px; }
}

/* ---------------------------------------------------------------------------
   Swipe rows.

   On a phone the filter bar and the metric pills wrapped onto three and four
   lines, pushing the actual report below the fold. Below the sidebar
   breakpoint they become a single row that swipes sideways instead, bleeding
   to the screen edges so it is obvious there is more to the right.

   A swipe row scrolls, so it clips: any pop-out anchored inside one has to be
   `position: fixed` *and* free of an ancestor that turns into its containing
   block (a transform, a filter, or `backdrop-filter`). See the header in
   `layouts/app.blade.php`.
--------------------------------------------------------------------------- */
@media (max-width: 1023px) {
    .swipe-row {
        flex-wrap: nowrap !important;
        overflow-x: auto;
        overflow-y: hidden;
        overscroll-behavior-x: contain;
        scroll-snap-type: x proximity;
        scrollbar-width: none;
        padding-right: 1.5rem;   /* a hint that the row continues */
    }

    .swipe-row::-webkit-scrollbar { display: none; }

    /* Nothing inside a swipe row may shrink, or it squeezes instead of scrolling. */
    .swipe-row > * {
        flex: 0 0 auto;
        scroll-snap-align: start;
    }

    /* What makes a swipe row read as one (app.js wraps rows marked
       data-swipe): round ‹ › buttons at the ends while there is more that
       way, and a thin track underneath showing which part is in view. Solid
       colours — a fade at the edge would be a gradient. */
    .swipe-shell.is-overflowing > .swipe-arrow:not([hidden]) { display: flex; }
    .swipe-arrow {
        position: absolute;
        top: calc(50% - 4.5px);
        margin-top: -14px;
        z-index: 5;
        width: 28px;
        height: 28px;
        align-items: center;
        justify-content: center;
        border-radius: 9999px;
        border: 1px solid rgb(var(--ink-200));
        background: rgb(var(--raised));
        color: rgb(var(--ink-800));
        box-shadow: 0 1px 4px rgb(0 0 0 / 0.35);
    }
    .swipe-arrow svg { width: 16px; height: 16px; }
    .swipe-arrow.is-prev { left: 2px; }
    .swipe-arrow.is-next { right: 2px; }

    .swipe-shell.is-overflowing > .swipe-track { display: block; }
    .swipe-track {
        position: relative;
        height: 3px;
        margin-top: 6px;
        border-radius: 2px;
        background: rgb(var(--ink-200));
        overflow: hidden;
    }
    .swipe-thumb {
        position: absolute;
        top: 0;
        bottom: 0;
        border-radius: 2px;
        background: rgb(var(--brand-500));
    }
}

.swipe-shell { position: relative; min-width: 0; }
.swipe-arrow, .swipe-track { display: none; }

/* ---------------------------------------------------------------------------
   Anchor targets.

   The top bar is sticky and, with the filter row, about 120px tall. A plain
   #hash jump scrolls the target to y=0, i.e. underneath it — so the click that
   was meant to reveal something appears to do nothing. This margin lands the
   target just below the bar instead.
--------------------------------------------------------------------------- */
.anchor-target { scroll-margin-top: 10rem; }

/* Momentum scrolling should not trap the page inside a table (Safari 16+;
   older versions simply keep the default behaviour). */
.table-scroll { overscroll-behavior-x: contain; }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgb(var(--ink-300)); border-radius: 999px; border: 3px solid transparent; background-clip: content-box; }
::-webkit-scrollbar-thumb:hover { background: rgb(var(--ink-400)); background-clip: content-box; }


/* A grid or flex track sizes to its content unless told otherwise, so a card
   holding a wide table grew to the table's min-width and pushed itself off the
   right edge of the screen — taking its charts with it and leaving the table
   with nothing to scroll. Letting these boxes shrink is what keeps the scroll
   inside the card where it belongs. */
main section,
main .grid > *,
main .flex > * { min-width: 0; }

.table-scroll { min-width: 0; }

/* Horizontal scrollers keep their header row pinned while the body scrolls. */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.table-scroll table { border-collapse: separate; border-spacing: 0; }

@media (min-width: 1024px) {
    .table-scroll thead th { position: sticky; top: 0; z-index: 2; background: rgb(var(--ink-50)); }
    .table-scroll .col-sticky { position: sticky; left: 0; z-index: 3; background: rgb(var(--surface)); }
    .table-scroll thead .col-sticky { z-index: 4; background: rgb(var(--ink-50)); }
    .table-scroll tbody tr:hover .col-sticky { background: rgb(var(--ink-100)); }
}


/* Properties held out of the group total sit below a rule, the way the
   reference report prints them. */
tr.is-separate > td { border-top: 2px solid rgb(var(--ink-300)) !important; background: rgb(var(--ink-50)); }
tr.is-separate + tr.is-separate > td { border-top-width: 1px !important; border-top-color: rgb(var(--ink-200)) !important; }
tr.is-separate .col-sticky { background: rgb(var(--ink-50)); }

/* The property whose day-by-day view is on screen. */
/* Filter-bar date fields (x-date-input). iOS Safari gives an empty date input
   no intrinsic width, so without a minimum the field vanished and could not
   be tapped; it also centres the value, which reads oddly in a narrow bar. */
.date-input { min-width: 8.5rem; min-height: 1.9rem; }
.date-input::-webkit-date-and-time-value { text-align: left; }
.date-input.is-empty::-webkit-datetime-edit { color: transparent; }

/* Tables whose every value and header must stay on one line at every width.
   The phone layout already did this through .compact-scroll; on an iPad or a
   Mac the same table let "79 €" split into "79 / €", headers such as
   "НОМЕРО-НОЧИ" broke at the hyphen and the rows came out uneven. The table
   scrolls sideways inside its card instead. */
.nowrap-cells th,
.nowrap-cells td { white-space: nowrap !important; }

/* The by-property table on the overview, drawn after the Power BI report it
   replaced: sentence-case headers in the brand colour that may wrap onto a
   second line (as "Гостей на бронь" does there), hairline rules, no fills and
   no bars — the numbers are the whole point. */
.pbi-table thead th {
    color: rgb(var(--brand-700));
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0;
    text-transform: none;
    white-space: normal;
    line-height: 1.2;
    vertical-align: bottom;
}
.pbi-table tbody td { white-space: nowrap; }
.pbi-table tr.is-total > td { border-top: 2px solid rgb(var(--ink-300)); }

@media (max-width: 1023px) {
    table.compact-scroll.pbi-table { font-size: 13px; }
    table.compact-scroll.pbi-table th { font-size: 11px; letter-spacing: 0; white-space: normal; }
    table.compact-scroll.pbi-table td:first-child { white-space: nowrap; }

    /* Seven columns do not fit a phone, so the table scrolls sideways the way
       the Power BI one did — but the hotel name stays pinned, otherwise two
       swipes in nobody can tell whose ADR they are reading. */
    .pbi-table .col-sticky {
        position: sticky; left: 0; z-index: 1;
        background: rgb(var(--surface));
        box-shadow: 1px 0 0 rgb(var(--ink-200));
    }
    .pbi-table tr.is-focused .col-sticky { background: rgb(var(--tint-brand)); }
}

tr.is-focused > td { background: rgb(var(--tint-brand)); box-shadow: inset 3px 0 0 rgb(var(--brand-600)); }
tr.is-focused .col-sticky { background: rgb(var(--tint-brand)); }

/* Numeric columns line up when digits share a width. */
.tnum { font-variant-numeric: tabular-nums; }


@media print {
    aside, header .lg\:hidden, nav.fixed { display: none !important; }
    .lg\:pl-64 { padding-left: 0 !important; }
}

/* Revenue celebrations on the comparison report. Motion only ever comes from
   window.celebrate(), which checks prefers-reduced-motion first; the note is
   the still version for everyone who asked their device to reduce motion. */
.cheer-layer { position: fixed; inset: 0; z-index: 60; pointer-events: none; }
.cheer-bit { position: fixed; width: 7px; height: 11px; border-radius: 2px; }
.cheer-row > td { background: rgb(var(--tint-up)) !important; transition: background-color 300ms ease; }
.cheer-overlay {
    position: fixed; inset: 0; z-index: 70; display: grid; place-items: center;
    padding: 16px; background: rgb(var(--canvas) / 0.78); cursor: pointer;
}
.cheer-overlay canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.cheer-overlay.is-leaving { opacity: 0; transition: opacity 250ms ease; }
/* The repeat of the day: nothing to dismiss, nothing blocked underneath. */
.cheer-overlay.is-light { background: transparent; pointer-events: none; cursor: default; }
.cheer-card {
    position: relative; z-index: 1; display: flex; flex-direction: column; gap: 6px;
    max-width: 100%; padding: 20px 28px; border-radius: 16px; text-align: center;
    background: rgb(var(--surface)); border: 1px solid rgb(var(--brand-500)); box-shadow: var(--shadow-pop);
}
.cheer-card strong { font-size: 28px; font-weight: 800; color: rgb(var(--brand-600)); }
.cheer-card span { font-size: 15px; font-weight: 600; color: rgb(var(--ink-900)); }
.cheer-card small { font-size: 11px; color: rgb(var(--ink-500)); }
.cheer-note {
    position: fixed; left: 16px; right: 16px; bottom: calc(88px + env(safe-area-inset-bottom, 0px)); z-index: 70;
    max-width: 480px; margin: 0 auto; padding: 12px 16px; border-radius: 12px;
    background: rgb(var(--surface)); border: 1px solid rgb(var(--brand-500)); box-shadow: var(--shadow-pop);
    color: rgb(var(--ink-900)); font-size: 13px; font-weight: 600;
}
