/* --- The desktop layout -----------------------------------------------------------------------
   Added 2026-08-20 (Documents/roadmap.md §2, "desktop has gotten essentially no deliberate design
   attention so far").

   WHAT THIS FILE IS. Everything below sits inside ONE media query, and that query is the whole
   argument of the feature: width AND shape AND input device, all three, or none of this applies.
   js/config.js's DESKTOP_LAYOUT_QUERY is the same string, character for character — read its
   comment for why each of the four clauses is there, and especially for why `pointer: fine` is
   what keeps every iPad on the mobile layout no matter how wide it is. The two copies are
   hand-synced; this is the load-bearing one, since the layout is pure CSS and needs no JS at all.

   WHAT IT DELIBERATELY IS NOT. It does not restyle any component's internals, define a colour, or
   touch anything below the breakpoint. It is a SHELL and AFFORDANCE layer over the mobile DOM: the
   phone-width column widens, the floating bottom capsule becomes a side rail, and the things a
   mouse and a keyboard need — hover feedback, focus rings, real cursors, a scrollbar that isn't
   shouting — get added where touch never needed them. Because it is one file and one media block,
   the whole desktop layout can be removed by deleting one <link>, which is the property that made
   it safe to ship into a codebase whose mobile behaviour has been tuned on real devices for
   months.

   It leans on two structural facts that were already true and cost nothing to reuse: #app is a
   centred column capped at --app-max-width, and every fixed-position element in the app
   (.week-header, .week-summary-bar, .toast-host, .ptr, the nav) is already centred against that
   same token. So widening ONE variable widens the app and re-centres all of its chrome for free,
   with no element-by-element positioning work and nothing left to drift out of sync.
   --------------------------------------------------------------------------------------------- */

@media (min-width: 1024px) and (min-aspect-ratio: 5/4) and (hover: hover) and (pointer: fine) {

  :root {
    /* 480px -> 760px. Chosen against real content rather than by feel: wide enough that a matchup
       card's two team buttons stop being cramped and that .binder-grid's auto-fill picks up a
       third column (that rule's own comment anticipates exactly this — "so a genuinely wider
       container, the desktop layout on the roadmap, picks up a third column on its own"), and
       narrow enough that a Podium row and a line of body text are still one comfortable reading
       measure. Past roughly 800px a single-column list stops reading as a list and starts reading
       as a stretched one; the answer to more screen than this is the rail beside it and the space
       around it, not a wider column. */
    --app-max-width: 760px;

    /* The side rail that replaces the floating bottom capsule. --rail-width is set so the widest
       tab label ("Previous") sits on one line under its icon at .nav-btn's own font-size. */
    --rail-width: 96px;
    --rail-gap: 22px;
  }

  /* --- The page around the app ---------------------------------------------------------------
     body's flat #050505 letterbox is right on a tablet, where it is a thin strip either side. On a
     1440px window it is two thirds of the screen, and a dead flat field that size reads as "the
     page failed to load" rather than as a frame. A very low-contrast radial lift behind the column
     makes it read as a lit stage instead — deliberately subtle enough that it can never compete
     with the app's own --bg (#1f1f1f), and fixed so it does not travel on scroll. */
  body {
    background:
      radial-gradient(120% 80% at 50% 0%, #131313 0%, #050505 62%) fixed,
      #050505;
  }

  /* Hairline edges plus a deeper shadow, so the column reads as a surface sitting ON the page
     rather than a rectangle painted onto it. The existing 40px shadow was tuned for a tablet's
     narrow margins and effectively disappears at this scale. */
  #app {
    border-left: 1px solid rgba(255, 255, 255, 0.05);
    border-right: 1px solid rgba(255, 255, 255, 0.05);
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5), 0 24px 90px rgba(0, 0, 0, 0.75);
  }

  /* The single biggest space win on this screen, and it is a deletion rather than an addition:
     base.css reserves --nav-height (72) + --nav-float-gap (14) + 8 = 94px at the bottom of every
     screen so scrolled content never hides under the floating bar. There is no floating bar down
     there any more — it is the rail — so all 94px goes back to content. */
  #screen-root {
    padding-bottom: 40px;
  }

  /* --- The bottom nav becomes a side rail ----------------------------------------------------
     PURELY A RESHAPE OF THE EXISTING ELEMENT — no JS, no second nav, no DOM change. That is what
     makes it safe: js/components/bottomNav.js is mounted once for the app's whole lifetime and its
     setActive/setNavVisible/setCollapsed only ever toggle classes and attributes, every one of
     which still means exactly what it meant before. Its decorative .bottom-nav__edge sibling is
     dropped rather than reshaped — see that rule below for the bug it was already causing here.

     Anchored to the COLUMN, not to the viewport edge: `left` is "just outside #app", with a 16px
     floor so it can never be pushed off-screen at the narrow end of this media query. Pinning it
     to the window edge instead would strand it half a screen away from the content it navigates on
     a wide monitor. Measured: at 1024px the computed value is 14px and the floor takes over at 16;
     at 1440px it lands at 222px, at 2560px at 782px. */
  .bottom-nav {
    left: max(16px, calc(50% - var(--app-max-width) / 2 - var(--rail-gap) - var(--rail-width)));
    top: 50%;
    bottom: auto;
    transform: translateY(-50%);
    width: var(--rail-width);
    max-width: none;
    height: auto;
    flex-direction: column;
    gap: 4px;
    padding: 10px 8px;
    border-radius: 28px;
    /* The collapse animation's clip-path is a horizontal wipe tuned to a wide pill, and scroll
       collapse is disabled on desktop anyway (bottomNav.js gates it on isDesktopLayout()). Reset
       to a plain rounded rect so a stale class left over from a resize across the breakpoint
       cannot leave the rail clipped in half. */
    clip-path: inset(0 0 0 0 round 28px);
    /* A short, wide window is reachable inside this media query — the aspect-ratio clause only sets
       a FLOOR on how landscape the window is, so 1024x320 qualifies comfortably. The rail's height
       is its four buttons (308px measured), so without a ceiling it would centre itself off the top
       and bottom of a window shorter than that. Scrolling a four-item nav is not a good outcome,
       but it is a recoverable one, and it is the only one here that keeps every tab reachable. */
    max-height: calc(100vh - 32px);
    overflow-y: auto;
    /* The capsule is 42% opaque because on a phone it floats over content that is actively
       scrolling underneath it, and .bottom-nav__edge adds more frosting at its rounded ends.
       The rail floats over the page background instead, where a value tuned for that reads thin
       rather than as glass — so it takes on the whole material itself, which is also what lets the
       edge layer be dropped entirely below. */
    background: rgba(31, 31, 31, 0.72);
    backdrop-filter: blur(18px) saturate(130%);
    -webkit-backdrop-filter: blur(18px) saturate(130%);
  }

  /* THE LIQUID-GLASS EDGE LAYER IS DROPPED, NOT RESHAPED, and it was a real bug before it was a
     decision. That element is an empty <div> that tracks the bar's geometry by DUPLICATING its
     values rather than measuring it (see components.css), which works only because the bar's height
     is the fixed --nav-height. A rail's height is its four buttons, so there is no value to
     duplicate: `height: auto` on an empty div is zero, and the layer silently vanished while still
     being painted at 19 z-index — caught by asserting the two rects match rather than by looking at
     it, since a 0px-tall invisible element looks exactly like a correct one in a screenshot.
     Pinning the rail to a hard pixel height to give this something to copy would trade a live bug
     for a latent one: the number would be silently wrong the first time a fifth tab is added.
     Removing it costs nothing here. Its whole job is to concentrate a warped-blur "glass" edge at
     the ends of a translucent pill floating over moving content — the rail floats over a static
     page background and is 72% opaque here, so the effect had almost nothing left to refract. */
  .bottom-nav__edge { display: none; }

  /* `flex: 1 1 0` is right in a fixed-height row and catastrophic in an auto-height column — a
     flex-basis of 0 on the main axis with no container height to grow into collapses every button
     to nothing at all. Explicit auto sizing instead. */
  .nav-btn {
    flex: 0 0 auto;
    padding: 12px 4px;
    gap: 6px;
    border-radius: 18px;
    font-size: 0.72rem;
    transition: opacity var(--speed-slide) ease, background var(--speed-fast),
      transform var(--speed-fast);
  }
  .nav-btn:hover {
    opacity: 0.85;
    background: rgba(255, 255, 255, 0.06);
  }
  /* The active tab's -2px lift is a row gesture — it rises out of the bar. In a column it just
     reads as misalignment. A filled pill is the vertical-rail equivalent, and the icon glow that
     already marks the active tab is kept untouched. */
  .nav-btn[aria-current='true'] {
    transform: none;
    background: rgba(0, 191, 255, 0.14);
    box-shadow: inset 0 0 0 1px rgba(0, 191, 255, 0.22);
  }
  .nav-btn[aria-current='true']:hover {
    background: rgba(0, 191, 255, 0.2);
  }
  .nav-btn__icon { width: 24px; height: 24px; }

  /* The collapsed-state circle has no job here: scroll collapse is disabled on desktop, so this can
     only ever appear as a stray artifact of a resize that crossed the breakpoint while the page
     happened to be scrolled down. */
  .nav-bubble { display: none; }

  /* --- Chrome that was sized for a phone column ----------------------------------------------
     Each of these follows --app-max-width automatically and is therefore now WIDER than it should
     be, which is the one predictable cost of moving that token. Capped individually rather than by
     holding the whole column narrow, because in every case the right width is a property of the
     thing itself and not of the screen. */

  /* A 720px-wide toast reading "Couldn't save your pick" is a banner, not a toast. */
  .toast { max-width: 420px; }

  /* Twelve emoji across a 760px sheet gives 100px+ targets you have to travel between. Keep the
     sheet near its phone size and let it centre — components.css already stops it hugging the
     bottom above 700px for the same reason. */
  .reaction-picker__sheet { max-width: 460px; }

  /* A settings list is the one screen where more width is actively worse: every control here is a
     label on the left and a value on the right, and stretching that to 760px puts a control half a
     screen from the thing it controls. Centred at a form measure instead, which also leaves the
     page reading as deliberate rather than as a list that ran out of content. */
  .profile-screen {
    max-width: 560px;
    margin-left: auto;
    margin-right: auto;
  }

  /* THE SIGN-IN GATE HAS TO GO FULL-BLEED, AND THIS IS A REAL BUG THE RAIL INTRODUCED rather than
     a stylistic choice. The gate is not a route — it is an overlay mounted on #app that covers the
     shell while the nav stays mounted underneath it (see app.js's mountAuthGate), and the only
     thing hiding the nav from a signed-out visitor is that the overlay paints over it: z-index 25
     against the nav's 20. That works on a phone because `position: absolute; inset: 0` inside #app
     covers the full width of the screen. It stops working the moment the nav moves OUTSIDE the
     column: the rail sits in the letterbox, the overlay never reaches it, and a signed-out visitor
     is shown four working tabs that navigate the app underneath the gate.
     `fixed` restores the property the mobile layout has by accident — the overlay covers
     everything, including anything positioned outside the column — and it is the same correction
     .status-overlay already carries in components.css, for the same reason. Scoped to desktop
     because below this breakpoint the absolute version is genuinely correct and works today.

     The one deliberate consequence: at 760px the two sign-in buttons sit in the middle of a large
     empty field either way, and going full-bleed drops the column framing on this one screen. That
     reads better than a framed column with an orphaned rail beside it, and sign-in is the one view
     with no content of its own to frame. The buttons need no width rule — .oauth-buttons,
     .auth-actions and .auth-divider are all already capped at 300px in screens.css. Only the logo,
     kept small so it reads as a header above a form, is worth a nudge on a screen this empty. */
  .auth-screen { position: fixed; }
  .auth-screen__logo { width: 68px; }

  /* --- Content that genuinely wants the extra width ------------------------------------------ */

  /* THE ONE PLACE WHERE MORE WIDTH IS NOT AUTOMATICALLY MORE, and the trade is worth stating.
     .fancy-btn is `aspect-ratio: 1/1; width: 100%` inside a .matchup-team that is `width: 38%` of
     the row — so widening the column widens the logo tiles and, because they are square, makes
     every game card TALLER by the same amount. At 760px they render ~280px on a side and a
     1440x900 window fits barely two games. The Pick screen's job is scanning a ~25-game slate, so
     losing rows to bigger logos is a straight downgrade dressed up as a bigger design.
     Capping the tile near its phone size spends the extra width as air inside the card instead:
     the card grows, the row count is preserved, and the logos stay the size they were drawn for. */
  .matchup-team { max-width: 230px; }
  /* Where that reclaimed width actually goes. Capping the tiles alone just pushes the two teams
     together and leaves a wide empty margin either side of the card; spending it on the gap
     instead separates the two sides — which is what the card is about — and the "at"/worth column
     between them stops reading as cramped. */
  .matchup-row { gap: 40px; }

  /* The top-3 medals are the Podium's headline and the only thing on that screen with a fixed
     ceiling (`max-width: 120px`), so at 760px they stayed phone-sized in the middle of a wide
     column while every row beneath them grew. */
  .podium-medal { max-width: 160px; }
  .podium-top3 { gap: 16px; }


  /* At 760px the binder's `minmax(150px, 1fr)` fits four columns of ~160px — narrower cards than a
     phone gets at two columns, which is the wrong direction for a bigger screen. Raising the floor
     to 190px asks for three, at ~228px each: the widest these mini cards have ever been rendered,
     and the size they were designed at before the 480px cap forced two. */
  .binder-grid { grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); }

  /* NO RULE FOR .standings-chart__svg, deliberately, and it is worth writing down why rather than
     leaving the absence to look like an oversight. It is the one element here that most obviously
     wants more pixels, so a `min-height` was the reflex — but it is `width: 100%; height: auto`
     over a 340x190 viewBox, so it already scales to ~424px tall inside a 760px column and any
     floor below that is dead code. Above it, it would be worse than dead: the SVG would letterbox
     rather than grow, and standingsChart.js's crosshair maps pointer x through
     `(x / VIEW_W) * svgRect.width`, which assumes the rendered box and the viewBox stay in step.
     The graph gets its improvement from the wider column alone. */

  /* --- Mouse and keyboard affordances --------------------------------------------------------
     THIS IS THE HALF OF "DESKTOP SUPPORT" THAT IS NOT ABOUT LAYOUT AT ALL, and it is the half a
     touch-first app is guaranteed to be missing: every interactive surface in here was built with
     :active feedback (the finger is already on the thing, so there is nothing left to preview) and
     none of them with :hover (the pointer arrives before the click, so there is). Scoped inside
     this media query rather than added globally on purpose — a hover state on a touch device is a
     STUCK state, because a tap leaves the element "hovered" until something else is touched. */

  .final-row,
  .upcoming-row,
  .podium-row-card,
  .podium-medal,
  .previous-row,
  .binder-mini-card {
    transition: background var(--speed-fast), border-color var(--speed-fast);
  }
  .final-row:hover,
  .upcoming-row:hover,
  .podium-row-card:hover,
  .podium-medal:hover,
  .previous-row:hover,
  .binder-mini-card:hover {
    border-color: rgba(0, 191, 255, 0.35);
  }

  /* THE LIFT IS DELIBERATELY ONLY ON THE BINDER CARD, and NOT on .podium-medal or
     .podium-row-card, which is the more obvious place to want it. Both of those are FLIP-animated
     on every Podium re-render: screens/podium.js captures each card's getBoundingClientRect()
     before the render and compares it after, and getBoundingClientRect INCLUDES transforms. A 2px
     hover lift present at capture and gone at measure (or the reverse) is a 2px delta — and
     FLIP_MIN_PX is exactly 2, so `Math.abs(dy) < FLIP_MIN_PX` is false at exactly that value and
     the card would animate. That floor exists specifically to swallow sub-pixel reflow noise and
     stop the shimmer fixed in 0.11.28; a hover state that sits precisely on it would hand the bug
     back, and only to whichever card the mouse happened to be resting on. Those two get a border
     tint instead, which changes no geometry at all. .binder-mini-card is not FLIP-animated, so it
     can have the real thing. */
  .binder-mini-card {
    transition: background var(--speed-fast), border-color var(--speed-fast),
      transform var(--speed-fast);
  }
  .binder-mini-card:hover { transform: translateY(-2px); }

  .toggle-group button:hover,
  .section-header-action:hover,
  .final-collapse-btn:hover,
  .chip-help-btn:hover {
    color: var(--accent);
    opacity: 1;
  }

  /* The collapsing drawer's header (js/components/collapsible.js). Brightens its title and
     chevron rather than lifting or tinting a border — it is a text row, so it joins the group
     above, and NO TRANSFORM on purpose: this primitive is meant to be reused, and a 2px hover
     lift on anything FLIP-animated hands back the 0.11.28 shimmer for exactly the reasons the
     .binder-mini-card comment sets out. Its own children carry the opacity, so the parent's
     `opacity: 1` would do nothing and the two are targeted directly. */
  .collapsible__header:hover .collapsible__title,
  .collapsible__header:hover .collapsible__chevron {
    color: var(--accent);
    opacity: 1;
  }

  .week-header select:hover {
    border-color: var(--accent-dim);
  }

  /* Everything that responds to a click should say so. A touch UI never needed this, because there
     is no cursor to change — so it was never set, including on the many interactive elements here
     that are plain <div>s or <button>s with no rule of their own. */
  .nav-btn,
  .toggle-group button,
  .podium-row-card,
  .podium-medal,
  .previous-row,
  .binder-mini-card,
  .final-row,
  .upcoming-row,
  .week-summary-bar__stat--tappable,
  .section-header-action,
  .final-collapse-btn,
  .chip-help-btn {
    cursor: pointer;
  }

  /* KEYBOARD FOCUS IS A REAL GAP, not a nicety: this app has never been tab-navigable in any
     visible sense, because a phone has no tab key and the browser's default ring is nearly
     invisible against --bg. :focus-visible rather than :focus, so a mouse click never paints a
     ring — that distinction is what lets this be applied this broadly without every click leaving
     a box behind. */
  a:focus-visible,
  button:focus-visible,
  select:focus-visible,
  input:focus-visible,
  textarea:focus-visible,
  [tabindex]:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 8px;
  }

  /* The rail's own focus ring follows each button's pill rather than sitting in a square around it,
     since these are the first four stops in tab order and so the most likely to ever be seen. */
  .nav-btn:focus-visible {
    outline-offset: -2px;
    border-radius: 18px;
  }

  /* A default light scrollbar down the right-hand edge of a near-black page is the loudest thing on
     the screen. WebKit/Blink only; Firefox falls back to its own already much quieter styling,
     which is why this isn't worth a scrollbar-color duplicate. */
  ::-webkit-scrollbar { width: 12px; height: 12px; }
  ::-webkit-scrollbar-track { background: transparent; }
  ::-webkit-scrollbar-thumb {
    background: #3a3a3a;
    border: 3px solid #050505;
    border-radius: 999px;
  }
  ::-webkit-scrollbar-thumb:hover { background: #4d4d4d; }
}
