/* dalai-layout.css
 *
 * LAYOUT OVERRIDES FOR THE UPSTREAM CONSOLE. READ THIS BEFORE ADDING ANYTHING.
 *
 * Separate from dalai-tokens.css on purpose:
 *
 *   dalai-tokens.css  redefines design tokens (--primary, --radius, --font-*).
 *                     Token names are stable vocabulary. Effectively
 *                     release-proof.
 *
 *   dalai-layout.css  targets upstream's DOM STRUCTURE. NOT release-proof. An
 *                     upstream header refactor will silently stop these rules
 *                     matching, and the layout will quietly revert.
 *
 * CHECK AFTER EVERY UPSTREAM UPGRADE, at full width: nav roughly centred,
 * action icons hard right, and exactly ONE set of action icons.
 *
 * ===========================================================================
 * TWO RULES, BOTH LEARNED BY BREAKING THE HEADER:
 *
 * 1. NEVER SET `display` ON AN UPSTREAM ELEMENT.
 *    Tailwind hides the mobile action group with `hidden` (display:none). Our
 *    stylesheet is UNLAYERED, and unlayered rules beat Tailwind's layered
 *    utilities regardless of specificity. One `display:flex` here un-hid the
 *    mobile menu on desktop: two sets of action icons and a hamburger appeared,
 *    and it looked like upstream had broken rather than like we had.
 *    Use only properties that are inert when they do not apply: margin,
 *    justify-content, white-space, position.
 *
 * 2. THE HEADER'S REAL STRUCTURE IS NOT WHAT IT LOOKS LIKE.
 *    The bar has three children, and the middle one holds BOTH the nav links
 *    and the action icons in a single flex row:
 *
 *      <nav class="... justify-between">
 *        <Link>                                     logo
 *        <div class="hidden items-center sm:flex">  links AND icons together
 *          <a>Home</a> ... <a>Docs</a>
 *          <div class="bg-border/40 mx-2 h-4 w-px"> the separator
 *          <LanguageSwitcher> <ThemeSwitch> <Notifications> <ProfileDropdown>
 *        </div>
 *        <div class="flex items-center sm:hidden">  mobile actions, hidden here
 *      </nav>
 *
 *    So centring "the nav" centred the icons with it, which is why the icons
 *    sat beside Docs instead of at the right edge. They were never misplaced;
 *    they were being carried along.
 * ===========================================================================
 */

/* ---------------------------------------------------------------------------
 * Split that single group into "links, centred" and "icons, hard right",
 * using nothing but auto margins.
 *
 * An `auto` margin in a flex row absorbs free space. Two of them absorb it
 * equally. So:
 *
 *   logo │←auto→│ Home … Docs │←auto→│ ┊ 文A ☀ 🔔 K
 *
 * The first auto sits before the first link, the second before the separator.
 * Equal shares put the links midway between the logo and the icons, and push
 * everything from the separator onward to the far right.
 *
 * Auto margins are the right instrument here precisely because they are inert
 * on a hidden element, so the mobile group cannot be disturbed by them.
 * ------------------------------------------------------------------------- */

/* WHICH CHILD IS THE GROUP (s1009b, 2026-10-09). The group is matched by its
   first separator, which only it has. Until 2026-10-09 it was matched by
   `:has(> a[href])`, which upstream's logo box (`div.flex.items-center`, a
   wrapper added after these rules were written) also satisfies: the logo box
   got `flex: 1 1 auto` and the logo link the auto margin, so the logo sat about
   190 px in from the bar's left edge instead of at it. The first link is
   matched as `a:first-child` because from f8 a link is no longer the group's
   first child (see the wrapper below), and Sign in, an `a` too, must never
   take the margin.

   Let the combined group claim the width between the logo and the bar's edge.
   Without this it shrink-wraps its content and there is no free space for the
   auto margins to absorb. */
header nav.justify-between > div.items-center:has(> div.mx-2.w-px) {
  flex: 1 1 auto;
  /* Nav labels are short by nature. Never let "Model Square" wrap and grow the
     header. */
  white-space: nowrap;
}

/* Push the links away from the logo. */
header nav.justify-between > div.items-center:has(> div.mx-2.w-px) > a:first-child {
  margin-inline-start: auto;
}

/* FROM RELEASE f8 the links sit in a wrapper of their own
   (feat/header-keep-ltr): `display: contents` while the bar follows the page,
   so the first link is still a flex item of the group; and a flex box with
   dir="rtl" when interface_setting.keep_header_ltr pins the bar left to right
   in a right-to-left page. The pinned bar is always left to right, so the
   wrapper is pushed from the logo with a physical left margin: its own
   margin-inline-start would be its right side. */
header nav.justify-between > div.items-center:has(> div.mx-2.w-px) > div.contents > a:first-child {
  margin-inline-start: auto;
}

header nav.justify-between[dir="ltr"] > div.items-center:has(> div.mx-2.w-px) > div[dir="rtl"] {
  margin-left: auto;
}

/* Push the FIRST separator, and therefore every icon after it, to the right.
 *
 * THERE ARE TWO SEPARATORS, and matching both is a bug:
 *
 *   div.mx-2.w-px   before the icon cluster (language, theme, notifications)
 *   div.mx-1.w-px   before the auth section  (profile avatar, or Sign in)
 *
 * A bare `div.w-px` selector gives BOTH an auto margin, so the free space is
 * split three ways and the avatar is pushed off on its own, away from the icons
 * it belongs with. Match only the first, by its Tailwind margin class, and the
 * whole cluster travels right together.
 */
header nav.justify-between > div.items-center:has(> div.mx-2.w-px) > div.mx-2.w-px {
  margin-inline-start: auto;
}

/* Belt and braces: if upstream adds another separator, it must not absorb space
   and split the cluster again. */
header nav.justify-between > div.items-center:has(> div.mx-2.w-px) > div.mx-1.w-px {
  margin-inline-start: 0;
}

/* ===========================================================================
 * THE AUTHENTICATED HEADER IS A DIFFERENT COMPONENT ENTIRELY.
 *
 * Everything above applies to the PUBLIC pages (/, /about, /pricing), rendered
 * by public-header.tsx. The signed-in console (/dashboard/…, /keys, /wallet)
 * uses AppHeader -> Header, which shares no markup with it.
 *
 * NOTE: NEVER WRITE A COMMENT DELIMITER INSIDE A COMMENT, not even as prose or
 * in quotes. A path glob such as "/dashboard" plus a star is literally the two
 * characters that open a comment; a star plus a slash CLOSES one, turning the
 * rest of the prose into live CSS. Both were written here while documenting
 * this file, and 30-deploy-tokens.sh rejected each one by counting delimiters.
 * That check exists because a stray close once silently killed an @font-face.
 * Write "the star form" in words instead.
 *
 *
 *   <header class="sticky top-0 z-40 ...">          no <nav> anywhere
 *     <div class="flex h-full items-center gap-1.5 px-2 sm:gap-2 sm:px-3">
 *       <SidebarTrigger>                            sidebar toggle
 *       <SystemBrand variant="inline">              DaLAi logo
 *       <div class="ms-auto flex items-center gap-1 sm:gap-2">
 *         <div class="me-1 hidden lg:block"><TopNav/></div>   the links
 *         <Search/>                                  the search box
 *         <NotificationPopover/> <LanguageSwitcher/>
 *         <ConfigDrawer/> <ProfileDropdown/>
 *       </div>
 *     </div>
 *   </header>
 *
 * The two selector sets cannot collide: the public header keys off
 * `nav.justify-between`, and this one has no <nav> at all; this one keys off
 * `div.ms-auto`, which the public header does not use.
 *
 * Here the links, the search box and the icons are ALL inside one `ms-auto`
 * group, so upstream packs the whole lot against the right edge. Centring the
 * links means separating them from the rest of that group without moving the
 * search box or the icons, which belong together on the right.
 * =========================================================================== */

/* VERIFIED AGAINST THE SHIPPED BUILD, not inferred from a screenshot. All 100
 * JS chunks were fetched and searched (~50 MB). Findings:
 *
 *   "flex h-full items-center gap-1.5 px-2 sm:gap-2 sm:px-3"   exactly 1 hit
 *   "ms-auto flex items-center gap-1 sm:gap-2"                 exactly 1 hit
 *   "me-1 hidden lg:block"                                     exactly 1 hit
 *
 * and the minified source confirms the nesting is header > div > div > div.
 *
 * Six other class literals in the build contain both "ms-auto" and
 * "items-center" (list badges, a card footer). None is a direct child of the
 * header bar, so the child combinators already exclude them — but `me-1 hidden
 * lg:block` is unique in the ENTIRE build, so anchoring the parent rule on it
 * with :has() makes the match exact rather than merely probable.
 *
 * It also fails safe: if an upstream release renames or drops the TopNav
 * wrapper, :has() stops matching and the group reverts to upstream's own
 * layout, instead of stretching some other element that happens to survive.
 *
 * One upstream escape hatch to know about: the action group is written
 * `rightContent ?? <div class="ms-auto …">`, so a page passing its own
 * rightContent replaces the whole group. These rules then simply do not match.
 * That is the desired behaviour, not a bug to chase.
 */
@media (min-width: 1100px) {
  /* Let the right-hand group span the row. It shrink-wraps by default, so there
     is no free space for an auto margin to absorb and nothing would move. */
  header > div.items-center > div.ms-auto.items-center:has(> div.me-1.hidden) {
    flex: 1 1 auto;
  }

  /* Centre the links wrapper inside that group. `margin-inline: auto` puts an
     absorbing margin on BOTH sides: the free space splits equally, which
     centres the links and leaves the search box and every icon packed at the
     end exactly as upstream had them.
     `me-1 hidden lg:block` are Tailwind literals; `hidden` is matched only to
     identify the element, never overridden. */
  header > div.items-center > div.ms-auto.items-center > div.me-1.hidden {
    margin-inline: auto;
  }

  /* The brand sits before the group and must not be stretched with it. */
  header > div.items-center > div.ms-auto.items-center:has(> div.me-1.hidden) {
    min-width: 0;
  }
}

/* Below the width where this is comfortable, hand everything back to upstream's
   own layout: squeezing a centred nav into a narrow bar reads worse than the
   default. Only the auto margins are removed; nothing is hidden or shown.
   The authenticated header needs no counterpart here: its rules are already
   inside a min-width query, and below `lg` upstream hides the links itself. */
@media (max-width: 1100px) {
  header nav.justify-between > div.items-center:has(> div.mx-2.w-px) {
    flex: 0 1 auto;
  }
  header nav.justify-between > div.items-center:has(> div.mx-2.w-px) > a:first-child,
  header nav.justify-between > div.items-center:has(> div.mx-2.w-px) > div.contents > a:first-child,
  header nav.justify-between > div.items-center:has(> div.mx-2.w-px) > div.mx-2.w-px {
    margin-inline-start: 0;
  }
  header nav.justify-between[dir="ltr"] > div.items-center:has(> div.mx-2.w-px) > div[dir="rtl"] {
    margin-left: 0;
  }
}

/* ===========================================================================
 * THE PRICE LIST, /pricing (s1007a, 2026-10-07; context/DECISIONS.md, "The
 * price list: the currency symbol isolated ...").
 *
 * What these rules fix, measured in Chrome through the CDN at upstream main
 * 56758ed: a card gave each price about 104 px in a three-column grid, so a
 * price wrapped; the table broke inside a number; the toolbar's selected
 * toggle differed from the other only by a near-white background; and the
 * Standard/Recharge toggle changes nothing here (one rate is written to both).
 *
 * The ORDER of a price (the symbol, then the number, then the unit) is not
 * fixed here and cannot be: CSS cannot isolate part of a text node. That is
 * the currency symbol's own setting, wrapped in U+2068 and U+2069 by
 * Back-Server/new-api/scripts/70-pricing-sync.py.
 *
 * Every selector is keyed on structure (data-slot, role, a direct child), or
 * on a label in each of the seven languages the build ships. A release that
 * changes the markup makes a rule stop matching, and the page returns to
 * upstream's own look. Read the card in model-card.tsx, the table cell in
 * model-price-cell.tsx and the toolbar in pricing-toolbar.tsx at the deployed
 * commit before changing any of this.
 * ======================================================================== */

/* The card: one price per line, the label left and the price right.
   The price group is the ONLY role=group that is a direct child of the card's
   content (the tags group sits one level deeper), so this matches the price
   grid alone, in any language. Only grid and flex properties: no display. */
[data-slot="card-content"] > [role="group"] > div.grid {
  grid-template-columns: minmax(0, 1fr);
  row-gap: 6px;
}
[data-slot="card-content"] > [role="group"] > div.grid > div {
  flex-direction: row;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
}
[data-slot="card-content"] > [role="group"] > div.grid > div > span.tabular-nums {
  white-space: nowrap;
}

/* The table: a number is never broken across lines. Scoped by :has() to the
   table that holds the price cell's two-column grid, so no other table in the
   console changes. Upstream sets break-words and whitespace-normal on these
   spans; this file is unlayered, so these win. */
table:has(td span.grid > span > span.tabular-nums) td span.tabular-nums {
  white-space: nowrap;
  overflow-wrap: normal;
  word-break: normal;
}

/* The toolbar's toggles: the selected one filled with the brand colour, the
   other muted; the size of the sort button beside them (h-8). Scoped to the
   row that holds toggle groups AND the sort menu's trigger, which is the
   pricing toolbar alone: other toggle groups in the console (notification
   type, token scopes) are cards, and a filled card would be a redesign. */
div:has(> [data-slot="toggle-group"] ~ [data-slot="dropdown-menu-trigger"]) > [data-slot="toggle-group"] > [data-slot="toggle-group-item"] {
  height: 32px;
  min-width: 40px;
  padding-inline: 12px;
  font-size: 14px;
  color: var(--muted-foreground);
}
div:has(> [data-slot="toggle-group"] ~ [data-slot="dropdown-menu-trigger"]) > [data-slot="toggle-group"] > [data-slot="toggle-group-item"][aria-pressed="true"] {
  background-color: var(--primary);
  border-color: var(--primary);
  color: var(--primary-foreground);
  font-weight: 650;
}
div:has(> [data-slot="toggle-group"] ~ [data-slot="dropdown-menu-trigger"]) > [data-slot="toggle-group"] > [data-slot="toggle-group-item"][aria-pressed="false"]:hover {
  color: var(--foreground);
}

/* Standard/Recharge: hidden. 70-pricing-sync.py writes the same rate to Price
   and custom_currency_exchange_rate, so both modes show the same numbers.
   Matched by its aria-label in each shipped language (en, zh, zh-TW, fr, ru,
   ja, vi, and fa since the fork's release f2), never by position, so the
   unit toggle beside it cannot be hidden by mistake. This is the one display
   rule in the file. The rule at the top
   forbids display because a display value can REVEAL what Tailwind hides;
   none can be revealed by this one. If the two rates ever differ, delete
   this rule. */
[data-slot="toggle-group"]:is(
  [aria-label="Price display mode"],
  [aria-label="价格显示模式"],
  [aria-label="價格顯示模式"],
  [aria-label="Mode d'affichage des prix"],
  [aria-label="Режим отображения цены"],
  [aria-label="価格表示モード"],
  [aria-label="Chế độ hiển thị giá"],
  [aria-label="حالت نمایش قیمت"]
) {
  display: none;
}

/* Model descriptions are Persian (56-catalogue-labels.py). In a left-to-right
   page a Persian sentence would end with its full stop on the wrong side and
   align left. plaintext takes each paragraph's direction from its first
   letter, so a Persian description reads and aligns right to left and an
   English one is unchanged. The card's description, then the one at the top
   of the model details panel (model-details.tsx). */
[data-slot="card-content"] > div > p.line-clamp-2,
header.pb-4 > p.leading-relaxed {
  unicode-bidi: plaintext;
  text-align: start;
}

/* ===========================================================================
 * THE PRICE LIST WITHOUT OUR COST OR THE MARKUP (s1007a, 2026-10-07;
 * context/DECISIONS.md, "The price list stops showing our cost and the 1.5x
 * ratio"; root OPEN-ITEMS 4.15).
 *
 * Upstream shows a model's BASE PRICE (the price at ratio 1, which is our cost
 * at the day's rate) and each group's ratio (1.5x), so anyone could read the
 * markup. The price a customer pays stays: the group table's input, output and
 * cache columns. Read model-details.tsx and pricing-sidebar.tsx at the deployed
 * commit before changing any of this. These are display rules only: the public
 * /api/pricing still carries model_ratio and group_ratio, and the signed-in
 * console (API keys, usage logs) still shows the ratio.
 *
 * Hiding only, the same exception to the display rule as the Standard/Recharge
 * group above: nothing upstream hides can be revealed by display: none.
 * ======================================================================== */

/* The details panel's Base Price block. The card is a section with an h2
   title, then Base Price, then Pricing by Group, which holds the card's only
   table. So: the section straight after the title, if it holds no table. When
   upstream leaves Base Price out, the section after the title is the group
   table's, and :not(:has(table)) keeps it. */
[data-slot="tabs-content"] > section.rounded-xl > h2:first-child + section:not(:has(table)) {
  display: none;
}

/* The group table's Ratio column, the second cell of every row. Only the token
   pricing table sits in the -mx-4 sm:mx-0 wrapper (ProviderGroupPricingSection);
   the per-tier tables of dynamic pricing use another wrapper and are untouched. */
[data-slot="tabs-content"] > section.rounded-xl div.\-mx-4.sm\:mx-0 [data-slot="table-container"] tr > :nth-child(2) {
  display: none;
}

/* The ratio on the sidebar's group chips ("default x1.5"). The Groups filter is
   the first of the five filter sections; the other sections' chips use the same
   span for their counts, which stay. */
div.space-y-1:has(> [data-slot="collapsible"]:nth-child(5)) > [data-slot="collapsible"]:first-child button[aria-pressed] > span.rounded-md {
  display: none;
}
