/* ==================================================================
   Wyndham — App shell and layout
   ------------------------------------------------------------------
   Desktop  fixed sidebar sitting directly on the canvas, with the
            current view inside a rounded rectangle to its right.
   Tablet   the same sidebar down to 768px.
   Mobile   content directly on the canvas, no header box, and a
            floating translucent navigation pill near the bottom.
   ================================================================== */

.app {
    display: flex;
    min-height: 100dvh;
    background: var(--bg);
}

/* ==================================================================
   Sidebar (>= 768px)
   ================================================================== */

.sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    z-index: 40;
    display: none;
    flex-direction: column;
    width: var(--sidebar-width);
    padding: var(--space-5) var(--space-3) var(--space-4);
    padding-top: calc(var(--space-5) + var(--safe-top));
    padding-left: calc(var(--space-3) + var(--safe-left));
    background: var(--bg);
}

.sidebar__brand {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3) var(--space-6);
}

.sidebar__mark {
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    flex: 0 0 30px;
    border-radius: var(--radius-sm);
    background: var(--accent);
    color: var(--on-accent);
}

.sidebar__mark svg { width: 18px; height: 18px; }

.sidebar__name {
    font-size: var(--size-md);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-tight);
}

.sidebar__scroll {
    flex: 1;
    overflow-y: auto;
    overscroll-behavior: contain;
}

.sidebar__group + .sidebar__group { margin-top: var(--space-6); }

.sidebar__label {
    padding: 0 var(--space-3) var(--space-2);
    font-size: var(--size-2xs);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
    color: var(--text-3);
}

.sidebar__foot {
    padding-top: var(--space-3);
    border-top: 1px solid var(--line);
    margin-top: var(--space-3);
}

/* --- Navigation items --------------------------------------------- */

.nav-item {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    color: var(--text-2);
    font-size: var(--size-base);
    font-weight: var(--weight-medium);
    letter-spacing: var(--tracking-snug);
    text-align: left;
    transition: background var(--duration-fast) var(--ease),
                color var(--duration-fast) var(--ease);
}

.nav-item + .nav-item { margin-top: 1px; }

.nav-item svg {
    width: 19px;
    height: 19px;
    flex: 0 0 19px;
    stroke-width: 1.75;
}

.nav-item__text {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.nav-item__count {
    font-size: var(--size-xs);
    font-variant-numeric: tabular-nums;
    color: var(--text-3);
}

@media (hover: hover) {
    .nav-item:hover { background: var(--surface-2); color: var(--text); }
}

.nav-item:active { background: var(--surface-3); }

.nav-item.is-active {
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: var(--weight-semibold);
}

.nav-item.is-active .nav-item__count { color: var(--accent); }

/* ==================================================================
   Main region
   ================================================================== */

.main {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 100dvh;
}

/* On mobile the view sits straight on the canvas: no card, no border. */
.view {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
    padding-left: max(var(--space-4), var(--safe-left));
    padding-right: max(var(--space-4), var(--safe-right));
    padding-top: var(--safe-top);
    /* Room for the floating nav pill plus the home indicator. */
    padding-bottom: calc(var(--tabbar-height) + var(--space-8) + var(--safe-bottom));
}

/* --- View header --------------------------------------------------
   Same colour as the canvas on mobile, with no rule beneath it.     */

.view__header {
    position: sticky;
    top: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: 52px;
    background: var(--bg);

    /* The header must own the status bar area, not just sit below it.
       viewport-fit=cover extends the layout viewport under the system
       clock and indicators, and `top: 0` pins this element to the true
       top of the screen — so once the page scrolls, the title would sit
       behind the status bar and become unreadable.

       The negative margin cancels the safe-area padding on .view and
       lets the header stretch up through that band; the matching top
       padding puts its content back where it was. The result is
       identical on load, and when pinned the header's own opaque
       background sits behind the status bar instead of page content. */
    margin-top: calc(-1 * var(--safe-top));
    padding: calc(var(--space-4) + var(--safe-top)) 0 var(--space-3);
}

.view__header::after {
    /* A soft fade rather than a hard rule, so content dissolves under
       the header as it scrolls. */
    content: "";
    position: absolute;
    inset: 100% 0 auto;
    height: 16px;
    background: linear-gradient(var(--bg), transparent);
    pointer-events: none;
}

.view__titles { flex: 1; min-width: 0; }

.view__title {
    font-size: var(--size-xl);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-tight);
    line-height: var(--leading-tight);
}

.view__subtitle {
    margin-top: 2px;
    font-size: var(--size-sm);
    color: var(--text-2);
}

.view__actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex: 0 0 auto;
}

.view__body {
    flex: 1;
    min-height: 0;
    padding-bottom: var(--space-6);
}

.view--narrow .view__body { max-width: 680px; }

.view--centred {
    align-items: center;
    justify-content: center;
    text-align: center;
}

/* ==================================================================
   Mobile navigation — a floating translucent pill
   ================================================================== */

.tabbar {
    position: fixed;
    z-index: 60;
    left: max(var(--space-4), var(--safe-left));
    right: max(var(--space-4), var(--safe-right));
    bottom: calc(var(--space-4) + var(--safe-bottom));
    display: flex;
    align-items: stretch;
    height: var(--tabbar-height);
    padding: var(--space-1);
    border: 1px solid var(--material-line);
    border-radius: var(--radius-xl);
    background: var(--material);
    box-shadow: var(--shadow-nav);
    -webkit-backdrop-filter: saturate(180%) blur(22px);
    backdrop-filter: saturate(180%) blur(22px);
    transition: transform var(--duration-slow) var(--ease),
                opacity var(--duration-base) var(--ease);
}

/* Slides away while reading, and whenever the keyboard is up. */
.tabbar.is-hidden {
    transform: translateY(calc(100% + var(--space-6)));
    opacity: 0;
    pointer-events: none;
}

.tabbar__item {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    border-radius: var(--radius-lg);
    color: var(--text-2);
    font-size: var(--size-2xs);
    font-weight: var(--weight-medium);
    letter-spacing: var(--tracking-normal);
    transition: color var(--duration-fast) var(--ease),
                background var(--duration-fast) var(--ease);
}

.tabbar__item svg {
    width: 21px;
    height: 21px;
    stroke-width: 1.75;
}

.tabbar__item.is-active {
    color: var(--accent);
}

.tabbar__item:active { background: var(--surface-2); }

/* ==================================================================
   Breakpoints
   ================================================================== */

/* --- Tablet and desktop: sidebar appears, content becomes a card --- */
@media (min-width: 768px) {
    .sidebar { display: flex; }
    .tabbar  { display: none; }

    .main {
        margin-left: var(--sidebar-width);
        padding: var(--space-4) var(--space-4) var(--space-4) 0;
        padding-right: max(var(--space-4), var(--safe-right));
        /* Clear the iPad status bar: the content card below resets its
           own top padding, so the inset has to be applied out here or
           the card's top edge tucks under the clock. */
        padding-top: calc(var(--space-4) + var(--safe-top));
        padding-bottom: calc(var(--space-4) + var(--safe-bottom));
    }

    /* The current view lives inside a rounded rectangle on the canvas. */
    .view {
        border: 1px solid var(--line);
        border-radius: var(--radius-xl);
        background: var(--surface);
        padding: 0 var(--space-7);
        padding-bottom: var(--space-6);
        overflow: hidden;
        box-shadow: var(--shadow-sm);
    }

    .view__header {
        background: var(--surface);
        /* The card already sits below the status bar, so the header
           reverts to ordinary flow inside it. */
        margin-top: 0;
        padding-top: var(--space-6);
        min-height: 64px;
    }

    .view__header::after { background: linear-gradient(var(--surface), transparent); }

    .view__body { max-width: var(--content-max); }
}

@media (min-width: 1100px) {
    .view { padding-left: var(--space-8); padding-right: var(--space-8); }
    .view__title { font-size: var(--size-2xl); }
}

/* --- Compact phones ------------------------------------------------ */
@media (max-width: 380px) {
    .view { padding-left: var(--space-3); padding-right: var(--space-3); }
    .view__title { font-size: var(--size-lg); }
    .tabbar__item span { font-size: 10px; }
}

/* ==================================================================
   iPadOS 26 windowed mode
   ------------------------------------------------------------------
   In windowed mode the system paints traffic-light controls over the
   top-left of the web content. There is no safe-area inset for them,
   so js/app.js detects the state and sets .ipad-windowed on <html>;
   these rules are gated on it and therefore never apply in full
   screen, where the controls do not exist and a gap would just look
   like dead space.

   Which element sits top-left changes at the 768px breakpoint, so the
   clearance is applied to the sidebar above it and to the view header
   below it.
   ================================================================== */

/* Wide layouts: the sidebar's brand row is the top-left element, so
   push it straight down past the control band. */
@media (min-width: 768px) {
    html.ipad-windowed .sidebar {
        padding-top: calc(var(--ipad-controls-height) + var(--space-4) + var(--safe-top));
    }

    /* The content card starts beside the controls, not under them, so
       it only needs the small amount that keeps its corner clear. */
    html.ipad-windowed .main {
        padding-top: calc(var(--space-4) + var(--space-2));
    }
}

/* Narrow layouts: no sidebar, so the view header is top-left. Slide it
   down rather than across — the title would otherwise be tucked under
   the controls, where a resting palm can long-press "through" them. */
@media (max-width: 767px) {
    html.ipad-windowed .view__header {
        padding-top: calc(var(--ipad-controls-height) + var(--space-3) + var(--safe-top));
    }

    /* The reader draws its own chrome and needs the same clearance. */
    html.ipad-windowed .reader__bar--top {
        padding-top: calc(var(--ipad-controls-height) + var(--space-2) + var(--safe-top));
    }
}

/* The reader's top bar is full width at every size, so its controls
   move right to sit beside the traffic lights instead of beneath
   them — that preserves vertical space, which matters most when
   reading. */
@media (min-width: 768px) {
    html.ipad-windowed .reader__bar--top {
        padding-left: calc(var(--ipad-controls-width) + var(--space-2));
    }
}

/* ==================================================================
   Chromeless — signed out, or reading
   ------------------------------------------------------------------
   Set by js/app.js. The sign-in and registration screens have nothing
   to navigate to, so the sidebar and the navigation pill are taken
   away entirely and the content pane reclaims the full width.
   ================================================================== */

body.is-chromeless .tabbar { display: none; }

/* Without a tab bar there is nothing to clear at the bottom. */
body.is-chromeless .view {
    padding-bottom: max(var(--space-6), var(--safe-bottom));
}

@media (min-width: 768px) {
    body.is-chromeless .sidebar { display: none; }
    body.is-chromeless .main { margin-left: 0; }
}

/* ==================================================================
   Loading screen
   Shown until the shell has data, so the interface appears complete
   rather than assembling itself in front of the reader.
   ================================================================== */

.splash {
    position: fixed;
    inset: 0;
    z-index: 500;
    display: grid;
    place-items: center;
    background: var(--bg);
    transition: opacity var(--duration-slow) var(--ease),
                visibility var(--duration-slow) var(--ease);
}

.splash.is-done {
    opacity: 0;
    visibility: hidden;
}

.splash__inner {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-4);
}

.splash__mark {
    display: grid;
    place-items: center;
    width: 52px;
    height: 52px;
    border-radius: var(--radius-md);
    background: var(--accent);
    color: var(--on-accent);
    animation: splash-breathe 1.9s var(--ease-in-out) infinite;
}

.splash__mark svg { width: 28px; height: 28px; }

.splash__text {
    font-size: var(--size-sm);
    color: var(--text-3);
    letter-spacing: var(--tracking-snug);
}

@keyframes splash-breathe {
    0%, 100% { transform: scale(1);    opacity: 1; }
    50%      { transform: scale(0.94); opacity: 0.72; }
}

/* ==================================================================
   Offline banner
   ================================================================== */

.offline-bar {
    position: fixed;
    z-index: 70;
    left: 50%;
    bottom: calc(var(--tabbar-height) + var(--space-7) + var(--safe-bottom));

    /* Hidden until the connection actually drops.
       A percentage transform cannot do the hiding here: it shifts the bar
       by its own height, but the bar is anchored well above the bottom
       edge, so it would still land on top of the navigation pill.
       Opacity and visibility hide it; the small translate is only there
       to give it something to slide in from. */
    transform: translate(-50%, 10px);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;

    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-4);
    border: 1px solid var(--material-line);
    border-radius: var(--radius-full);
    background: var(--material);
    -webkit-backdrop-filter: blur(20px);
    backdrop-filter: blur(20px);
    box-shadow: var(--shadow-md);
    max-width: calc(100vw - var(--space-8));
    font-size: var(--size-sm);
    font-weight: var(--weight-medium);
    color: var(--text-2);
    transition: transform var(--duration-slow) var(--ease),
                opacity var(--duration-base) var(--ease),
                visibility var(--duration-base) var(--ease);
}

.offline-bar.is-visible {
    transform: translate(-50%, 0);
    opacity: 1;
    visibility: visible;
}

/* One line, so the pill keeps its shape on a narrow phone. */
.offline-bar span {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Without an explicit basis the flex row shrinks the icon to a sliver. */
.offline-bar svg { width: 15px; height: 15px; flex: 0 0 15px; }

@media (min-width: 768px) {
    .offline-bar { bottom: calc(var(--space-6) + var(--safe-bottom)); }
}
