/* Same-document app on wide windows. Short/narrow phone windows keep their existing layout.
 *
 *   stage    >= 600 x 480   fluid app column, bottom dock (Tailwind st:)
 *   desktop  >= 960 x 560   the desktop shell on /game (components/desktop, styles/desktop-shell.css;
 *                           Tailwind dt:/dw:/dx: are keyed on html[data-mf-shell="desktop"])
 * App column width = window - 2 x gutter (gutter grows to 32px), at most 1680px; the desktop
 * shell is flush with the window instead. */
@media (min-width: 600px) and (min-height: 480px) {
  html[data-mf-stage] {
    display: flex;
    justify-content: center;
    min-height: 100%;
    --mf-stage-gutter: clamp(0px, 4vw - 24px, 32px);
    --mf-layout-width: min(calc(100vw - 2 * var(--mf-stage-gutter)), 1680px);
    --mf-stage-hud-height: 0px;
    --mf-stage-dock-height: 0px;
    --mf-stage-dim: transparent;
    /* Extend the actual HUD and dock chrome; content remains in one column. */
    background:
      linear-gradient(var(--mf-stage-dim), var(--mf-stage-dim)),
      linear-gradient(to top, var(--mf-hud-border) 0 1px, transparent 1px) top / 100% var(--mf-stage-hud-height) no-repeat,
      linear-gradient(var(--mf-hud-surface), var(--mf-hud-surface)) top / 100% var(--mf-stage-hud-height) no-repeat,
      linear-gradient(to bottom, var(--mf-dock-border) 0 1px, transparent 1px) bottom / 100% calc(var(--mf-stage-dock-height) + 1px) no-repeat,
      var(--mf-dock-gradient) bottom / 100% var(--mf-stage-dock-height) no-repeat,
      var(--mf-app-surface);
  }

  html[data-mf-stage]:has([data-tour-target="hud-bar"]) {
    --mf-stage-hud-height: var(--hud-height, 56px);
  }
  html[data-mf-stage]:has([data-game-navigation]) {
    --mf-stage-dock-height: var(--navigation-height, 71px);
  }
  /* Match game-modal's first backdrop and vaul's darker drawer backdrop. */
  html[data-mf-stage]:has(body[data-scroll-locked]):has([role="dialog"][data-state="open"]) {
    --mf-stage-dim: rgba(0, 0, 0, 0.3);
  }
  html[data-mf-stage]:has(body[data-scroll-locked]):has([data-vaul-overlay][data-state="open"]) {
    --mf-stage-dim: rgba(0, 0, 0, 0.8);
  }
  html[data-mf-stage]:has(body[data-scroll-locked]):has([data-vaul-overlay][data-state="open"].bg-black\/50) {
    --mf-stage-dim: rgba(0, 0, 0, 0.5);
  }

  /* The desktop shell is the whole window: no gutters, no width cap. A length, not a
   * percentage: components compute calc(var(--mf-layout-width) - 8px) and the like. */
  html[data-mf-stage][data-mf-shell="desktop"] {
    --mf-stage-gutter: 0px;
    --mf-layout-width: 100vw;
  }

  html[data-mf-stage] .mf-stage-poppers {
    position: fixed;
    inset: 0;
    contain: layout;
    pointer-events: none;
    z-index: 10050;
  }
  html[data-mf-stage] .mf-stage-poppers > * { pointer-events: auto; }

  /* Mobile toast placement must clear the HUD inside a narrow stage. */
  html[data-mf-stage] [data-mf-toast-viewport] { z-index: 9999; }
  @container mf-stage (max-width: 639px) {
    html[data-mf-stage] [data-mf-toast-viewport] {
      top: calc(var(--hud-height, 56px) + 8px);
    }
  }

  /* Legacy modal vw sizes refer to the app column, not the window. */
  html[data-mf-stage] [role="dialog"][class~="w-[80vw]"] { width: 80%; }
  html[data-mf-stage] [role="dialog"][class~="w-[90vw]"] { width: 90%; }
  html[data-mf-stage] [role="dialog"][class~="w-[95vw]"] { width: 95%; }
  html[data-mf-stage] [role="dialog"][class~="w-[98vw]"] { width: 98%; max-width: 98%; }

  html[data-mf-stage] body {
    /* No auto margins: react-remove-scroll-bar caches margins as padding. */
    width: var(--mf-layout-width);
    flex: 0 0 var(--mf-layout-width);
    margin: 0 !important;
    height: 100vh;
    position: relative;
    contain: layout paint;
    container-type: inline-size;
    container-name: mf-stage;
    /* Keep the column untransformed for Telegram Desktop HiDPI text. */
  }
}

/* Mouse and keyboard on the stage: hover feedback on cards and rows, visible keyboard
 * focus and slim scrollbars (phones keep hidden scrollbars and touch feedback). */
@media (min-width: 600px) and (min-height: 480px) {
  html[data-mf-stage] body :is(button, a, [role="button"], [tabindex]:not([tabindex="-1"])):focus-visible {
    outline: 2px solid #c6ad70;
    outline-offset: 2px;
  }
  html[data-mf-stage] body ::-webkit-scrollbar { width: 10px; height: 10px; }
  html[data-mf-stage] body ::-webkit-scrollbar-track { background: transparent; }
  html[data-mf-stage] body ::-webkit-scrollbar-thumb {
    border: 3px solid transparent;
    border-radius: 999px;
    background: rgba(255, 255, 255, .14) padding-box;
  }
  html[data-mf-stage] body ::-webkit-scrollbar-thumb:hover { background-color: rgba(255, 255, 255, .26); }
}
@media (min-width: 600px) and (min-height: 480px) and (hover: hover) {
  html[data-mf-stage] [data-catalog-card],
  html[data-mf-stage] [data-forge-history-row],
  html[data-mf-stage] [data-partner-local-banner] {
    transition: box-shadow 150ms ease, border-color 150ms ease;
  }
  html[data-mf-stage] [data-catalog-card]:hover,
  html[data-mf-stage] [data-partner-local-banner]:hover {
    box-shadow: 0 0 0 1px rgba(212, 175, 55, .55), 0 8px 24px rgba(0, 0, 0, .45);
  }
  html[data-mf-stage] [data-forge-history-row]:hover {
    box-shadow: inset 0 0 0 1px rgba(212, 175, 55, .35);
  }
}

/* Desktop client frame: tokens and the shell-scoped placement of existing overlays.
 * Active on html[data-mf-shell="desktop"] only (game page, window >= 960 x 560; see
 * lib/layout/desktop-shell.ts). Phones and 600-959 px windows never match.
 * Component styles live next to the components (components/desktop/*.module.css);
 * the tokens are global so portals (menus, dialogs, tooltips) can use them too.
 * Values come from the approved concepts (mf-desktop-concepts-20261002, mf-v2.css). */

html[data-mf-shell="desktop"] {
  /* Titles: Cinzel for Latin; Russian and Ukrainian titles in Cormorant SC (Cinzel has no
   * Cyrillic, the generic serif did not match). Its face covers Cyrillic only
   * (styles/self-hosted-fonts.css), so Latin titles are unchanged. */
  --font-cinzel: 'Cinzel', 'Cormorant SC', serif;
  /* Surfaces: one canvas shared by the sidebar, the top bar, the pages and the chat, with no
   * lines between them; panels are one shade lighter and separated by spacing (round 2). */
  --dt-bg: #232328;          /* the one canvas */
  --dt-l0: var(--dt-bg);     /* earlier name of the page canvas */
  --dt-l1: var(--dt-bg);     /* earlier name of the frame material (sidebar, top bar, chat) */
  --dt-l2: #2a2b31;          /* panel: one shade lighter */
  --dt-l2-raise: #31323a;    /* inputs, hovered rows inside a panel */
  --dt-l2-sunk: #222328;     /* wells inside a panel */
  --dt-l3: #37353d;          /* overlay: menus, popovers, dialogs */
  --dt-edge: transparent;    /* was the frame's gold hairline: no lines between frame and pages */
  --dt-line: rgba(255, 255, 255, .055);    /* rules inside panels and tables, used sparingly */
  --dt-line-2: rgba(255, 255, 255, .11);
  --dt-rule: rgba(255, 255, 255, .045);
  --dt-hair-gold: rgba(212, 175, 55, .30);
  --dt-gild: linear-gradient(90deg, rgba(212,175,55,0) 0%, rgba(212,175,55,.40) 20%, rgba(236,206,120,.58) 50%, rgba(212,175,55,.40) 80%, rgba(212,175,55,0) 100%);
  /* Text: every small-text colour is >= 4.5:1 on L0-L3. */
  --dt-ivory: #ECE4CF;
  --dt-text: #E8E6E0;
  --dt-text-2: #D4D1CB;
  --dt-muted: #B9B5BE;
  --dt-faint: #A5A1AB;
  /* Accents */
  --dt-gold: #D4AF37;
  --dt-gold-text: #DDBE62;
  --dt-gold-70: #A68B2F;
  --dt-purple: #A78BD6;
  --dt-green: #45C285;
  --dt-red: #E8837A;
  --dt-amber: #E5A85A;
  --dt-blue: #6FA8E6;
  /* Fills that carry text (counters, badges, numbered steps): deep enough for light text at
   * 4.5:1 or more. The game uses no black or near-black text anywhere (Alex, 2026-10-06), so a
   * coloured fill takes --dt-on-accent, never dark text on a bright accent. */
  --dt-on-accent: #FFF9EA;
  --dt-gold-fill: linear-gradient(180deg, #8C7428, #6F5A1C);
  --dt-gold-rim: rgba(240, 210, 120, .45);
  --dt-blue-fill: #2E6DAD;
  --dt-ember-fill: #B9501C;
  --dt-amber-fill: #8F5D1E;
  --dt-red-fill: #B5443B;
  /* Geometry */
  --dt-side: 72px;           /* collapsed sidebar, 960-1279 */
  --dt-top: 56px;
  --dt-pad: 24px;
  --dt-gap: 16px;
  --dt-radius: 10px;
  --dt-inspector: 380px;
  /* Chat panel width: the player drags the pinned chat's edge (--dt-chat-pref, saved per
   * device by chat-resize-handle.tsx). Keep a 600 px page wherever the window allows it;
   * at the 960 px desktop boundary a 320 px chat leaves a 568 px reflowing page. */
  --dt-chat-min: 320px;
  --dt-chat-default: 560px;
  --dt-chat-max: min(760px, calc(100vw - var(--dt-side) - 600px));
  --dt-chat-width: clamp(var(--dt-chat-min), var(--dt-chat-pref, var(--dt-chat-default)), var(--dt-chat-max));
  /* Telegram insets (round 3). Telegram Desktop fullscreen floats its menu and close buttons
   * (44 px, 8 px from the top and right edges, 8 px apart) over the top right of the window and
   * reports a content-safe-area top. The real client reported twice the buttons' band (112 px
   * at 150 %; Alex's review of 2026-10-03), which pushed the top bar down and left an empty
   * band above it. The frame therefore takes no top inset from Telegram (safe area or content
   * safe area): the top bar is always 56 px at the very top of the window, and only its right
   * end keeps the buttons' corner free (twice the reported band, capped at their footprint,
   * 8 + 44 + 8 + 44 + 8, plus an 8 px gap). Full-stage layers keep clear of Telegram's
   * controls through --dt-inset-*, the top one capped at the top bar's height. 0 elsewhere. */
  --dt-inset-top: min(calc(var(--tg-safe-area-inset-top, 0px) + var(--tg-content-safe-area-inset-top, 0px)), 56px);
  --dt-inset-right: calc(var(--tg-safe-area-inset-right, 0px) + var(--tg-content-safe-area-inset-right, 0px));
  --dt-inset-bottom: calc(var(--tg-safe-area-inset-bottom, 0px) + var(--tg-content-safe-area-inset-bottom, 0px));
  --dt-inset-left: calc(var(--tg-safe-area-inset-left, 0px) + var(--tg-content-safe-area-inset-left, 0px));
  --dt-safe-top: 0px;
  --dt-tg-controls: calc(var(--tg-safe-area-inset-top, 0px) + var(--tg-content-safe-area-inset-top, 0px));
  --dt-tg-corner: min(calc(var(--dt-tg-controls) * 2), 120px);
  --dt-top-total: var(--dt-top);
  /* Physical sides: the sidebar is on the left, or on the right in right-to-left languages
   * (html[dir="rtl"], below). Chat pins are physical too (left / right of the screen). */
  --dt-side-left: var(--dt-side);
  --dt-side-right: 0px;
  /* The page area's edges for page-level overlays (item views, dialogs, floating page buttons):
   * inside the sidebar and a chat pinned on either side. */
  --dt-page-left: calc(var(--dt-side-left) + var(--dt-dock-left, 0px));
  --dt-page-right: calc(var(--dt-inset-right) + var(--dt-side-right) + var(--dt-dock-right, 0px));
  /* Form fields: the surface's tone or one step lighter, never a darker well (Figma-like). */
  --dt-field: rgba(255, 255, 255, .05);
  --dt-field-hover: rgba(255, 255, 255, .075);
  --dt-field-line: rgba(255, 255, 255, .10);
  /* The chat panel's surfaces (messages, header and composer). No pin/unpin strip. */
  --dt-chat-surface: var(--dt-bg);
  --dt-chat-chrome: var(--dt-bg);
  --dt-chat-strip: 0px;
  /* Motion: colour, border, glow and opacity only. No scale, no translate, no lasting
   * transform or backdrop blur (Telegram Desktop rasterizes composited layers at 1x). */
  --dt-ease: cubic-bezier(.2, .7, .2, 1);
  --dt-dur-hover: 140ms;
  --dt-dur-press: 70ms;
  --dt-dur-tab: 200ms;
  --dt-dur-section: 150ms;
  --dt-dur-panel: 180ms;
  --dt-focus: 2px solid #c6ad70;

  /* Phone layout variables seen by existing components: no dock, the top bar is the HUD, no
   * floating chat button (the top bar holds the chat). */
  --hud-height: var(--dt-top-total);
  --navigation-height: 0px;
  --chat-launcher-clearance: 0px;
}
/* The expanded sidebar (from 1280 px, lib/layout/desktop-shell.ts): 240 px, room for the longest
 * localized sub-page labels with their counts on one line (round 4) and the stacked logo at 200 px. */
@media (min-width: 1280px) {
  html[data-mf-shell="desktop"] { --dt-side: 240px; }
}
html[data-mf-shell="desktop"][dir="rtl"] {
  --dt-side-left: 0px;
  --dt-side-right: var(--dt-side);
}
@media (min-width: 1760px) {
  html[data-mf-shell="desktop"] { --dt-inspector: 400px; }
}
html[data-mf-shell="desktop"][data-mf-chat-dock="left"] { --dt-dock-left: var(--dt-chat-width); }
html[data-mf-shell="desktop"][data-mf-chat-dock="right"] { --dt-dock-right: var(--dt-chat-width); --dt-chat-beside: var(--dt-chat-width); }

/* Scrollbars: thin and dark everywhere in the desktop client, portals included. The colour is
 * inherited, so scrollers that only set scrollbar-width (thin) are dark too; elements that hide
 * their scrollbar keep scrollbar-width: none, which wins over this zero-specificity default. */
html[data-mf-shell="desktop"] { scrollbar-color: rgba(255, 255, 255, .2) transparent; }
:where(html[data-mf-shell="desktop"]) * { scrollbar-width: thin; }
:where(html[data-mf-shell="desktop"]) ::-webkit-scrollbar { width: 8px; height: 8px; background: transparent; }
:where(html[data-mf-shell="desktop"]) ::-webkit-scrollbar-thumb { border: 2px solid transparent; border-radius: 8px; background: rgba(255, 255, 255, .2) padding-box; }
:where(html[data-mf-shell="desktop"]) ::-webkit-scrollbar-corner { background: transparent; }

/* Avatars are circles everywhere in the desktop client (round 4): packs, previews, the profile,
 * the chat, leaderboards and offers draw a player's picture through different components, so the
 * rule keys on the picture itself, every avatar image by its URL (direct or through the image
 * optimizer, the fallback avatar included). A frame drawn around an avatar (a selection border,
 * a hover glow) follows the circle in its own component. */
html[data-mf-shell="desktop"] img:is([src*="/images/avatars/"], [src*="%2Fimages%2Favatars%2F"], [src*="/images/fallback/avatar"], [src*="%2Fimages%2Ffallback%2Favatar"]) {
  border-radius: 50% !important;
  -o-object-fit: cover;
     object-fit: cover;
}

/* Native form controls in the theme: dark checkboxes, radios, selects (and their option lists),
 * ranges and pickers with the gold accent, wherever a page or dialog uses them. Zero
 * specificity: a control styled by its component keeps its own look. */
:where(html[data-mf-shell="desktop"]) :where(input[type="checkbox"], input[type="radio"], input[type="range"], input[type="number"], input[type="date"], input[type="time"], input[type="datetime-local"], select, progress, meter) {
  color-scheme: dark;
  accent-color: var(--dt-gold);
}
:where(html[data-mf-shell="desktop"]) :where(select) {
  background-color: var(--dt-field);
  color: var(--dt-text);
  border: 1px solid var(--dt-field-line);
  border-radius: 8px;
}
:where(html[data-mf-shell="desktop"]) :where(select option) {
  background-color: var(--dt-l3);
  color: var(--dt-text);
}

/* ---------------------------------------------------------------- chat panel
 * One chat panel (components/ui/chat-overlay.tsx, unchanged) at the window's inline end (the
 * right; the left in right-to-left languages, html[data-mf-chat-side]), under the top bar,
 * --dt-chat-width wide. It is always pinned in the desktop shell; the page reflows beside
 * its responsive column. Navigation and Escape never dismiss desktop chat;
 * only the window's own close button does. No player pin/unpin mode. */
html[data-mf-shell="desktop"] [data-chat-overlay] {
  top: var(--dt-top-total) !important;
  bottom: 0 !important;
  left: auto !important;
  right: calc(var(--dt-inset-right) + var(--dt-side-right)) !important;
  width: var(--dt-chat-width) !important;
  padding-top: var(--dt-chat-strip);
  box-sizing: border-box;
  background-color: var(--dt-chat-chrome);
  transform: none !important;
  container: desktopchat / inline-size;
}
html[data-mf-shell="desktop"][data-mf-chat-side="left"] [data-chat-overlay] {
  left: var(--dt-side-left) !important;
  right: auto !important;
}
html[data-mf-shell="desktop"][data-mf-chat-mode="panel"] [data-chat-overlay] {
  box-shadow: -18px 0 44px rgba(0, 0, 0, .42), -1px 0 0 rgba(255, 255, 255, .05);
}
html[data-mf-shell="desktop"][data-mf-chat-mode="panel"][data-mf-chat-side="left"] [data-chat-overlay] {
  box-shadow: 18px 0 44px rgba(0, 0, 0, .42), 1px 0 0 rgba(255, 255, 255, .05);
}
/* The smallest pinned columns give the switcher a full row, then the same three controls
 * their own row. ChatOverlay measures the header height for its pins and message clearance. */
@container desktopchat (max-width: 380px) {
  html[data-mf-shell="desktop"] [data-chat-header] { flex-wrap: wrap; height: auto !important; }
  html[data-mf-shell="desktop"] [data-chat-header] > :first-child { flex-basis: 100%; }
  html[data-mf-shell="desktop"] [data-chat-header-actions] { margin-inline-start: auto; }
}
/* While the pinned chat's edge is dragged, nothing else reacts to the pointer (the composer is
 * an iframe). */
html[data-mf-shell="desktop"][data-mf-chat-resizing] { cursor: col-resize; }
html[data-mf-shell="desktop"][data-mf-chat-resizing] body { -webkit-user-select: none; -moz-user-select: none; user-select: none; }
html[data-mf-shell="desktop"][data-mf-chat-resizing] iframe { pointer-events: none; }

/* The panel inside the window: a solid header (the window's controls, the conversation switcher
 * and the pinned message) and a solid composer footer, with the messages scrolling only between
 * them, for community and Ember alike. Phones keep the transparent header with floating
 * controls. The chat components are unchanged; this uses their hooks: the panel
 * [data-chat-conversation-panel] with --chat-header-clearance (the height under the controls
 * and the switcher), [data-chat-pinned-bar], [data-chat-search-panel], [data-chat-input], the
 * lists [data-ember-messages] and the community reader's scroller. Both lists measure the
 * composer and the pinned bar, so the footer and the gap under the pin keep messages clear. */
html[data-mf-shell="desktop"] [data-chat-overlay] > div {
  background-color: var(--dt-chat-surface) !important; /* the frame's material, as the sidebar and top bar */
  transition: background-color 220ms var(--dt-ease);
}
/* The header band takes the clicks between the controls, so no hidden message can be hit. */
html[data-mf-shell="desktop"] [data-chat-overlay] [data-chat-conversation-panel]::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: var(--chat-header-clearance, 64px);
  z-index: 10;
  background: var(--dt-chat-chrome);
  box-shadow: 0 1px 0 var(--dt-line);
  transition: background-color 220ms var(--dt-ease);
}
/* The pinned message (or the search panel) continues the header below the switcher. */
html[data-mf-shell="desktop"] [data-chat-overlay] [data-chat-conversation-panel]:has(:is([data-chat-pinned-bar], [data-chat-search-panel]))::before {
  box-shadow: none;
}
html[data-mf-shell="desktop"] [data-chat-overlay] :is([data-chat-pinned-bar], [data-chat-search-panel]) {
  background: var(--dt-chat-chrome);
  box-shadow: 0 1px 0 var(--dt-line);
}
html[data-mf-shell="desktop"] [data-chat-overlay] :is([data-chat-pinned-bar], [data-chat-search-panel]) > :last-child {
  margin-bottom: 10px;
}
/* Messages stay under the header and the footer whatever layers they gain (menus are portaled). */
html[data-mf-shell="desktop"] [data-chat-overlay] [data-chat-conversation-panel] :is([data-ember-messages], [class*="chat-reader-v2_scroller__"]) {
  isolation: isolate;
}
/* The composer: a full-width solid footer holding the input box (reply preview + input). */
html[data-mf-shell="desktop"] [data-chat-overlay] [data-chat-conversation-panel] [data-chat-input] {
  left: 0 !important;
  right: 0 !important;
  bottom: 0 !important;
  padding: 12px;
  border-radius: 0 !important;
  background: var(--dt-chat-chrome) !important;
  box-shadow: 0 -1px 0 var(--dt-line) !important;
  backdrop-filter: none !important;
  transition: background-color 220ms var(--dt-ease);
}
html[data-mf-shell="desktop"] [data-chat-overlay] [data-chat-conversation-panel] [data-chat-input] > * {
  background-image: linear-gradient(#2e2e35, #2e2e35);
  box-shadow: inset 1px 0 0 rgba(255, 255, 255, .08), inset -1px 0 0 rgba(255, 255, 255, .08);
}
html[data-mf-shell="desktop"] [data-chat-overlay] [data-chat-conversation-panel] [data-chat-input] > :first-child {
  border-top-left-radius: 16px;
  border-top-right-radius: 16px;
  box-shadow: inset 1px 0 0 rgba(255, 255, 255, .08), inset -1px 0 0 rgba(255, 255, 255, .08), inset 0 1px 0 rgba(255, 255, 255, .08);
}
html[data-mf-shell="desktop"] [data-chat-overlay] [data-chat-conversation-panel] [data-chat-input] > :last-child {
  border-bottom-left-radius: 16px;
  border-bottom-right-radius: 16px;
  box-shadow: inset 1px 0 0 rgba(255, 255, 255, .08), inset -1px 0 0 rgba(255, 255, 255, .08), inset 0 -1px 0 rgba(255, 255, 255, .08);
}
html[data-mf-shell="desktop"] [data-chat-overlay] [data-chat-conversation-panel] [data-chat-input] > :only-child {
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .08);
}
/* The reply preview keeps its darker tint over the input surface. */
html[data-mf-shell="desktop"] [data-chat-overlay] [data-chat-conversation-panel] [data-chat-input] > [data-chat-reply-preview] {
  background-image: linear-gradient(rgba(0, 0, 0, .2), rgba(0, 0, 0, .2)), linear-gradient(#2e2e35, #2e2e35);
}

/* Celebrations (craft reveal, discovery, pack opening, level up, achievement: [data-mf-moment],
 * z-index 350-401) are the top layer: while one is open, the notification stack (otherwise 9999,
 * above every page layer; Ember's new message is one of its cards) goes under it. */
html[data-mf-shell="desktop"]:has([data-mf-moment]) [data-mf-desktop-stack] {
  z-index: 340 !important;
}

/* Bottom action rows (Market basket): inside the page area, never under the sidebar or a
 * pinned chat. */
html[data-mf-shell="desktop"] [class*="game-floating-layout_actions__"] {
  left: calc(var(--dt-page-left) + var(--dt-pad)) !important;
  right: calc(var(--dt-page-right) + var(--dt-pad)) !important;
  bottom: 20px !important;
}

/* ---------------------------------------------------------------- interim page content
 * Pages not yet redesigned render today's content inside the frame. Their tiny fixed
 * phone labels are lifted one step for desktop viewing distance; explicit dt:/dw:/dx:
 * classes come later in the stylesheet and still win. Frame components never use these. */
html[data-mf-shell="desktop"] [data-mf-interim] [style*="font-size: 7px"],
html[data-mf-shell="desktop"] [data-mf-interim] [style*="font-size: 8px"] { font-size: 10px !important; }
html[data-mf-shell="desktop"] [data-mf-interim] [style*="font-size: 9px"] { font-size: 11px !important; }
html[data-mf-shell="desktop"] [data-mf-interim] [style*="font-size: 10px"] { font-size: 12px !important; }
html[data-mf-shell="desktop"] [data-mf-interim] [style*="font-size: 11px"] { font-size: 12.5px !important; }
html[data-mf-shell="desktop"] [data-mf-interim] .text-\[7px\],
html[data-mf-shell="desktop"] [data-mf-interim] .text-\[8px\] { font-size: 10px; }
html[data-mf-shell="desktop"] [data-mf-interim] .text-\[9px\] { font-size: 11px; }
html[data-mf-shell="desktop"] [data-mf-interim] .text-\[10px\] { font-size: 12px; }
html[data-mf-shell="desktop"] [data-mf-interim] .text-\[11px\] { font-size: 12.5px; }

/* Phone press/hover motion (scale-on-tap, lift-on-hover) becomes colour feedback. */
@media (hover: hover) {
  html[data-mf-shell="desktop"] [class*="hover:-translate-y"]:hover,
  html[data-mf-shell="desktop"] [class*="hover:translate-y"]:hover {
    --tw-translate-y: 0 !important;
  }
  html[data-mf-shell="desktop"] [class*="hover:scale"]:hover {
    --tw-scale-x: 1 !important;
    --tw-scale-y: 1 !important;
  }
}
html[data-mf-shell="desktop"] [class*="active:scale"]:active {
  --tw-scale-x: 1 !important;
  --tw-scale-y: 1 !important;
  filter: brightness(.9);
}
/* Every other control of a page answers the press the same way while it is held: a little
 * darker, nothing moves. Desktop and game buttons keep their own pressed look. */
html[data-mf-shell="desktop"] main :is(button, a[href], [role="button"], [role="tab"], [role="option"], [role="radio"], [role="menuitem"], [data-offer-id], tr[data-click]):not(:disabled):not([aria-disabled="true"]):not([data-variant]):not([data-game-button]):not([class*="active:scale"]):active {
  filter: brightness(.9);
}

/* The craft console opens as a centred panel, not a window-high bottom sheet. */
html[data-mf-shell="desktop"] [data-forge-new-sheet] {
  --mf-console-height: min(90vh, 960px);
  top: calc((100vh - var(--mf-console-height)) / 2);
  bottom: auto !important;
  height: var(--mf-console-height) !important;
  max-width: min(820px, calc(100% - 56px));
  border-radius: 16px;
  box-shadow: 0 24px 64px rgba(0, 0, 0, .55);
}
/* A centred panel has no drag handle (the close button stays). */
html[data-mf-shell="desktop"] [data-forge-new-sheet] [data-vaul-handle] { visibility: hidden; }

/* Celebrations stay full-stage moments centred in the window. The Fortune prize reveal is
 * a 640 px stage with a content-sized Claim instead of a window-wide phone button. */
html[data-mf-shell="desktop"] [class*="gacha-prize-overlay_stage__"] {
  max-width: 640px;
}
html[data-mf-shell="desktop"] [class*="gacha-prize-overlay_hint__"] {
  display: flex;
  justify-content: center;
}
html[data-mf-shell="desktop"] [class*="gacha-prize-overlay_hint__"] button {
  width: auto !important;
  min-width: 240px;
}

/* Dialogs keep dialog widths (440 / 560 / 720 / 880) instead of spanning the window. */
html[data-mf-shell="desktop"] [role="dialog"][class~="w-[98vw]"] { max-width: min(98%, 1200px); }
html[data-mf-shell="desktop"] [role="dialog"][class~="max-w-5xl"] { max-width: min(95%, 880px); }

/* Floating phone conveniences the frame replaces. */
html[data-mf-shell="desktop"] [data-mf-scroll-top] { display: none !important; }

@keyframes dtNoteIn { from { opacity: 0 } to { opacity: 1 } }
@keyframes dtNoteOut { from { opacity: 1 } to { opacity: 0 } }

@media (prefers-reduced-motion: reduce) {
  html[data-mf-shell="desktop"] {
    --dt-dur-hover: 0ms;
    --dt-dur-tab: 0ms;
    --dt-dur-section: 0ms;
    --dt-dur-panel: 0ms;
  }
}

/* Text fitted to one line (components/desktop/fit-lines.ts, [data-fit-line]): it stays on one line
 * and is set a little smaller when its box is too narrow; one that cannot fit even at its smallest
 * size wraps between its words ([data-fit-wrap]) instead of being cut: never inside a word and never
 * with a hyphen the browser inserts (desktop rules, Text). Only a single word wider than its box even
 * at the floor breaks where it must, still without a hyphen ([data-fit-wrap="break"]). */
html[data-mf-shell="desktop"] [data-fit-line] { white-space: nowrap; }
html[data-mf-shell="desktop"] [data-fit-line][data-fit-wrap] { white-space: normal; overflow-wrap: normal; hyphens: manual; text-wrap: balance; }
html[data-mf-shell="desktop"] [data-fit-line][data-fit-wrap="break"] { overflow-wrap: break-word; }

