/* 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: about a quarter of the window by default (Alex, 2026-10-15: «по умолчанию
   * чат был справа, примерно на 25% ширины»; 427 px at his 1707 px window), never narrower than
   * 320 px. The player drags the pinned chat's edge (--dt-chat-pref, saved per device by
   * chat-resize-handle.tsx); double-click returns to the quarter. A pinned chat always leaves the
   * page 800 px; where even 320 px would not, the chat is a 320 px pull-out over the page instead
   * (html[data-mf-chat-mode="panel"]; lib/layout/chat-geometry.ts, decision 2026-10-15-tablets-
   * two-layouts-touch). Pages, dialogs, item views and the notification stack read the result
   * through --dt-chat-width / --dt-page-left / --dt-page-right / --dt-chat-overlay, never a copied
   * number. */
  --dt-chat-min: 320px;
  --dt-chat-default: 25vw;
  --dt-chat-max: min(760px, calc(100vw - var(--dt-side) - 800px));
  --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.
 * The shell keeps the 72 px rail instead (html[data-mf-sidebar="rail"]) while a pinned chat would
 * leave the page less than 800 px beside the menu (1280-1386 px windows; chat-geometry.ts). */
@media (min-width: 1280px) {
  html[data-mf-shell="desktop"]:not([data-mf-sidebar="rail"]) { --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. Pinned beside the page (data-mf-chat-mode="pinned") while the page keeps
 * 800 px: the page reflows beside its column, and navigation and Escape never dismiss it; only its
 * own close button and the top bar's chat button do. On narrower windows (data-mf-chat-mode=
 * "panel") it is a pull-out over the page above a light backdrop (chat-panel.tsx): a press on the
 * backdrop, Escape and a link to another page close it. 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 pull-out's backdrop (chat-panel.tsx): a light dim over the frame under the panel (z 150); a
 * press on it closes the pull-out. Opacity only. */
[data-mf-chat-backdrop] {
  position: fixed;
  inset: 0;
  z-index: 149;
  background: rgba(10, 10, 13, .32);
  animation: mfChatBackdropIn var(--dt-dur-panel, 180ms) var(--dt-ease, ease-out);
}
@keyframes mfChatBackdropIn {
  from { opacity: 0; }
  to { opacity: 1; }
}
/* 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; }

/*
 * Touch density (r11 item N; decisions/2026-10-15-tablets-two-layouts-touch.md in the control repo).
 *
 * An input layer, not a third layout: nothing here changes either composition (the phone
 * composition below 960 x 560, the desktop client above; lib/layout/desktop-shell.ts) or anything
 * a fine pointer sees. Every sizing rule sits inside `@media (pointer: coarse)`, the browser's own
 * measurement of the primary pointer: a finger gets targets of 44 CSS px or more in both
 * compositions (Alex, 2026-10-15: «на сенсорных экранах кнопки крупнее»), while a mouse, a trackpad
 * or a browser that reports no pointer at all keeps the authored sizes.
 *
 * Contract (owners may rely on it; tokens exist only on coarse pointers):
 *  --mf-touch-target  44px, the smallest side of a touch target.
 *  --mf-touch-gap     8px, room between neighbouring targets for owners laying out touch rows.
 *  ordinary controls  buttons, links (a[href], [role=link]), [role=button|tab|menuitem*|option|radio],
 *                     summary, select, a label around a native checkbox or radio (the label is its
 *                     tap target) and one-line text fields are at least --mf-touch-target tall and
 *                     wide. The floor wins over compact mouse sizes (Tailwind min-h-8 or min-w-0,
 *                     `all: unset` links); a row of controls that no longer fits must wrap or
 *                     re-flow in its owner's coarse rule, never shrink a target (e.g. Swap's chart
 *                     ranges).
 *  explanation marks  [data-info-tip] "i" marks are targets in the flow, like buttons: the drawn
 *                     mark keeps its size and colours, and a transparent ring around it makes its
 *                     box at least --mf-touch-target, so no neighbour lies under it or paints over
 *                     it (Swap's Limit beside the order-type "i"; a table's sticky head under a
 *                     panel title's "i").
 *  marks              switches, checkboxes and slider thumbs keep their drawn size and get an
 *                     invisible centred hit area of at least --mf-touch-target.
 *  fields             a label that draws a text field (a search box: icon and input) grows with
 *                     its input instead of letting the input overflow into the next row.
 *  rows               a [role=listitem] or [role=row] that holds a control is at least one target
 *                     tall, so a grown control never reaches into the next row (a 28 px order-book
 *                     row would otherwise put half of one player's button under the next one's).
 *  disclosures        [data-tap-disclosure] (a country flag on its own) is an ordinary target: a
 *                     44 px box in the flow with its drawn mark centred, and the desktop focus ring
 *                     on every pointer (the one rule outside the coarse layer).
 *  links              a link outside running text is a control, whatever its width or how its
 *                     owner laid it out: one with no display of its own (an <a> or a
 *                     <span role=link>, inline by default) becomes an inline box, label centred,
 *                     so the floor reaches it as it reaches a button (CSS sizes do not apply to an
 *                     inline box). Zero specificity: a display its owner set (inline-flex, block,
 *                     flex, grid, hidden) is kept, and a flex or grid item is a box already.
 *  not touched        two named cases, each for a reason: running text, which is p and
 *                     [data-prose] and nothing else (a sentence anywhere else is marked by its
 *                     owner: wrapped in p or given data-prose; its links and chips keep the line),
 *                     except a card inside that text, [data-chat-card], whose controls are
 *                     targets under p and [data-prose] alike; and the chat's drag handle
 *                     ([data-mf-chat-resize], a drag edge, not a tap target). Chat message
 *                     controls (avatar, name, quote, reactions, menu) are ordinary targets.
 *                     There is no general opt-out attribute.
 */

@media (pointer: coarse) {
  :root {
    --mf-touch-target: 44px;
    --mf-touch-gap: 8px;
  }

  /* Ordinary controls: at least one target tall and wide, whatever their layout (a growing flex item
   * included: a row whose labels do not fit at 44 px re-flows in its owner's coarse rule, e.g. a
   * recipe card's actions stack, never by shrinking or exempting a target). */
  :where(
    button:not([role="switch"], [role="checkbox"], [role="slider"]),
    [role="button"],
    [role="tab"],
    [role="menuitem"],
    [role="menuitemradio"],
    [role="menuitemcheckbox"],
    [role="option"],
    [role="radio"]:not(input),
    a[href],
    [role="link"],
    summary,
    select,
    label:has(> input:is([type="checkbox"], [type="radio"])),
    input:is(:not([type]), [type="text"], [type="search"], [type="number"], [type="email"], [type="tel"], [type="url"], [type="password"])
  ):where(:not(
    [data-info-tip],
    [data-mf-chat-resize],
    [hidden],
    .sr-only,
    p *:not([data-chat-card] *),
    [data-prose] *:not([data-chat-card] *)
  )) {
    min-height: var(--mf-touch-target) !important;
    min-width: var(--mf-touch-target) !important;
  }

  /* A link outside running text is laid out as a box so the floor above reaches it. Only the
   * elements that are inline by default (any other element with the link role is a box already),
   * with the same exclusions; inline-block keeps the label's own inline layout and wrapping, and
   * align-content centres it in the 44 px box. */
  :where(a[href], :is(a, span)[role="link"]):where(:not(
    [data-info-tip],
    [data-mf-chat-resize],
    [hidden],
    .sr-only,
    p *:not([data-chat-card] *),
    [data-prose] *:not([data-chat-card] *)
  )) {
    display: inline-block;
    align-content: center;
  }

  /* A disclosure's drawn mark (a 20 px flag) sits in the middle of its 44 px box. */
  [data-tap-disclosure] {
    justify-content: center;
  }

  /* A tab list's sliding indicator is decoration drawn over the tabs' lower edge: it never takes
   * the tap meant for the tab under it. */
  [role="tablist"] > [aria-hidden="true"] {
    pointer-events: none;
  }

  /* Rows that hold a control grow with it instead of letting it overlap the next row. */
  :where([role="listitem"], [role="row"]):has(:where(button, [role="button"], a[href], [role="link"])) {
    min-height: var(--mf-touch-target) !important;
  }

  /* A field drawn by its label (ToolbarSearch's 34 px box with an icon and the input) takes the
   * input's height; a fixed height would leave the grown input hanging out of it, under the next
   * heading. */
  :where(label):has(> input:is(:not([type]), [type="text"], [type="search"], [type="number"], [type="email"], [type="tel"], [type="url"], [type="password"])):where(:not(p *:not([data-chat-card] *), [data-prose] *:not([data-chat-card] *))) {
    height: auto !important;
  }

  /* An explanation mark is a target in the flow: a transparent ring around the drawn mark (18 px, the
   * smallest drawn mark, plus two rings of 13 px) makes its box at least one target. Its circle and
   * colours stay as drawn (the background stops at the ring), its focus ring stays close to it, and
   * its box takes its place in the row, so a neighbour can neither lie under it nor paint over it. */
  :where([data-info-tip]):where(:not(p *:not([data-chat-card] *), [data-prose] *:not([data-chat-card] *))) {
    box-sizing: content-box !important;
    border: calc((var(--mf-touch-target) - 18px) / 2) solid transparent !important;
    background-clip: padding-box !important;
    margin-block: 0 !important;
  }
  :where([data-info-tip]):where(:not(p *:not([data-chat-card] *), [data-prose] *:not([data-chat-card] *))):focus-visible {
    outline-offset: calc(2px - (var(--mf-touch-target) - 18px) / 2) !important;
  }

  /* Marks that keep their drawn size: an invisible centred hit area. */
  :where(
    button[role="switch"],
    button[role="checkbox"],
    [role="slider"]
  ):where(:not(p *:not([data-chat-card] *), [data-prose] *:not([data-chat-card] *))) {
    position: relative;
  }
  :where(
    button[role="switch"],
    button[role="checkbox"],
    [role="slider"]
  ):where(:not(p *:not([data-chat-card] *), [data-prose] *:not([data-chat-card] *)))::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    width: max(100%, var(--mf-touch-target));
    height: max(100%, var(--mf-touch-target));
    transform: translate(-50%, -50%);
  }
}

/* Tap disclosures without a focus style of their own (lib/input/tap-disclosure.ts; a country flag):
 * the desktop focus ring, for every pointer, since the keyboard reaches them on any device. The
 * only rule outside the coarse layer. */
[data-tap-disclosure]:focus-visible {
  outline: var(--dt-focus, 2px solid #c6ad70) !important;
  outline-offset: 2px;
  border-radius: 3px;
}

