/* Geist, self-hosted. Generated by packages/ui/scripts/kit.mjs - do not edit.
   Link this BEFORE tokens.css. Inside the monorepo you do not need it: next/font loads the
   same family and sets the same two variables. */

@font-face {
  font-family: 'Geist';
  src: url('fonts/Geist-Variable.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Geist Mono';
  src: url('fonts/GeistMono-Variable.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
:root {
  --font-geist-sans: 'Geist';
  --font-geist-mono: 'Geist Mono';
}

/* mixtape.works design tokens — generated from packages/ui/src/tokens.css.
   DO NOT EDIT. Run `pnpm --filter @mixtape/ui kit` after changing the source.

   This is the plain-CSS build, for products with no Tailwind and no build step. It is the
   same palette, the same type, the same radii and the same motion as every other product -
   that is the entire point of it. Add a colour to the source, never here, and never to a
   product's own stylesheet. */

:root {
  --font-sans: var(--font-geist-sans), var(--font-script, ui-sans-serif), system-ui, sans-serif;
  --font-mono: var(--font-geist-mono), ui-monospace, "SF Mono", Menlo, monospace;
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 18px;
  --radius-xl: 26px;
  --ease-out-soft: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --animate-rise: rise 380ms cubic-bezier(0.22, 1, 0.36, 1) both;
  --animate-fade: fade 200ms cubic-bezier(0.22, 1, 0.36, 1) both;
  --animate-pop: pop 200ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
  --animate-pulse-soft: pulse-soft 2.4s cubic-bezier(0.22, 1, 0.36, 1) infinite;
}
/* mixtape.works design tokens — one system, every product.

   Two layers on purpose:
   1. `@theme inline` maps Tailwind utilities onto CSS variables *by reference*, so
      `bg-canvas` resolves at paint time rather than being frozen at build time.
   2. The palettes below re-point those variables. Swap `data-theme` on <html> and the
      whole app re-themes with no re-render and no flash.

   Add a colour here, never in an app. If a product needs its own accent, override
   `--c-accent*` on a wrapper element; everything else inherits. */



/* ── The page's width, for every mixtape page ──────────────────────────────
   One number: the landing column, a product's dashboard, Studio and the account all stop
   here. 90rem is 1440px at the default size, so a 1440 screen is filled edge to edge (less
   the gutters) and a wider one keeps the column at 1440 rather than stretching lines across
   it (Chris, 2026-09-21: "every page including dashboards on 1440px screens should fill out
   the whole width content-wise (for bigger windows dont)"). It was 1200px on the landing
   layer, 80rem in Studio and 72rem in the account, and none of them agreed. */
:root { --page-max: 90rem; }

/* ── Dark: the studio default and the brand's home ─────────────────────── */
:root,
:root[data-theme='dark'] {
  color-scheme: dark;

  --c-canvas: #0a0b0d;
  --c-surface: #101216;
  --c-surface-2: #171a21;
  --c-line: #23272f;
  --c-line-strong: #323843;

  --c-ink: #eceef1;
  --c-ink-muted: #a3abb6;
  --c-ink-subtle: #6d7583;

  --c-accent: #f2b544;
  --c-accent-strong: #ffc85c;
  --c-accent-ink: #0a0b0d;
  --c-accent-soft: #f2b5441f;

  --c-ok: #3ecf8e;
  --c-rec: #f0604d;
  /* A destructive button's fill: the light theme's red in BOTH themes, because the dark theme's
     #f0604d carries a white label at only 3.2:1. White on #c3402c is 5.1:1, on the hover 6.6:1,
     and the button still stands at 3.8:1 against the dark page (2026-10-05, task #1304). Defined
     here once; the light blocks inherit it. */
  --c-danger: #c3402c;
  --c-danger-strong: #a8331f;
  --c-danger-ink: #ffffff;
  --c-warn: #e9b949;
  --c-go: #6fbf5e;
  --c-go-strong: #5aa84a;
  --c-go-ink: #0a0b0d;
  --c-idea: #b892f6;
  --c-plan: #4cc4e0;
  --c-build: #5b8def;
  --c-review: #f0916d;
  --c-surface-sunk: #0c0d10;

  --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23a3abb6' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  /* Elevation for a window over the page (a confirm, a dialog): a colour, never #000 - deep in the dark, ink on paper. */
  --shadow-lift: 0 24px 60px color-mix(in srgb, var(--c-canvas) 55%, transparent);
}

/* ── Light: warm paper, not clinical white ─────────────────────────────── */
:root[data-theme='light'] {
  color-scheme: light;

  --c-canvas: #fbfaf8;
  --c-surface: #ffffff;
  --c-surface-2: #f4f2ee;
  --c-line: #e6e2db;
  --c-line-strong: #d3cec4;

  --c-ink: #17191c;
  --c-ink-muted: #565c66;
  --c-ink-subtle: #868d99;

  /* The brand orange, on paper. Two corrections, in order.
     First, the light accent used to be a dark brown-gold chosen so *white* text could sit on
     it; dark ink on a brighter amber - the relationship dark mode already has - reads better
     and looks like the brand. Then on 2026-09-16 Chris said the buttons and badges still
     looked brownish here, and the cause was hue rather than lightness: #c8871f is a
     low-chroma gold, and a filled patch of it reads as mustard. #d97706 is the same
     lightness at a purer orange, so it costs nothing - it is fractionally better as text
     (3.05:1 against paper, up from 2.90) while dark ink on it stays at 5.7:1. Small accent
     labels remain the deliberate cost of one token doing both jobs; splitting fill from text
     would mean touching 73 call sites to buy a tenth of a ratio. */
  --c-accent: #d97706;
  --c-accent-strong: #b45309;
  --c-accent-ink: #1c1405;
  --c-accent-soft: #d977061f;

  --c-ok: #17864f;
  --c-rec: #c3402c;
  --c-warn: #a67908;
  --c-go: #3f8f31;
  --c-go-strong: #2f7524;
  --c-go-ink: #ffffff;
  --c-idea: #7c4dcc;
  --c-plan: #0f7d99;
  --c-build: #2f5fd0;
  --c-review: #c2551f;
  --c-surface-sunk: #f1f2f4;

  --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23565c66' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  /* Elevation for a window over the page (a confirm, a dialog): a colour, never #000 - deep in the dark, ink on paper. */
  --shadow-lift: 0 24px 60px color-mix(in srgb, var(--c-ink) 22%, transparent);
}

/* Follow the OS when the user hasn't chosen. `data-theme` always wins. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) {
    color-scheme: light;

    --c-canvas: #fbfaf8;
    --c-surface: #ffffff;
    --c-surface-2: #f4f2ee;
    --c-line: #e6e2db;
    --c-line-strong: #d3cec4;

    --c-ink: #17191c;
    --c-ink-muted: #565c66;
    --c-ink-subtle: #868d99;

    --c-accent: #d97706;
    --c-accent-strong: #b45309;
    --c-accent-ink: #1c1405;
    --c-accent-soft: #d977061f;

    --c-ok: #17864f;
    --c-rec: #c3402c;
    --c-warn: #a67908;
    --c-go: #3f8f31;
    --c-go-strong: #2f7524;
    --c-go-ink: #ffffff;
    --c-idea: #7c4dcc;
    --c-plan: #0f7d99;
    --c-build: #2f5fd0;
    --c-review: #c2551f;
    --c-surface-sunk: #f1f2f4;

    --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23565c66' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
    --shadow-lift: 0 24px 60px color-mix(in srgb, var(--c-ink) 22%, transparent);
  }
}

/* ── Phones in dark: the page is black ───────────────────────────────────
   Chris, 2026-09-22: "on mobile only, when on dark mode, the bg color of the page (outside of
   our cards/boxes etc) should be totally black". A phone screen is mostly OLED, where black is
   the panel switched off and the cards read as lit objects on it; on a desktop monitor the
   near-black stays. Only the canvas moves - surfaces, lines and ink keep their values, so the
   cards look exactly as they do everywhere else. Both ways a page can be dark: chosen
   (data-theme) and followed from the OS (no stamp). syncThemeColor reads --c-canvas, so the
   status bar follows by itself. */
@media (max-width: 767.98px) {
  :root[data-theme='dark'] { --c-canvas: #000; }
}
@media (max-width: 767.98px) and (prefers-color-scheme: dark) {
  :root:not([data-theme]) { --c-canvas: #000; }
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
@keyframes fade {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
@keyframes pop {
  from {
    opacity: 0;
    transform: scale(0.94);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
@keyframes pulse-soft {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.45;
  }
}

@layer base {
  html {
    -webkit-tap-highlight-color: transparent;
    /* The root element, not just body: any moment where the page is between renders paints
       this, and without it the browser falls back to `color-scheme` - which is dark by
       default here, so light mode flashed dark. */
    background-color: var(--c-canvas);
  }

  /* Interface scale. The middle step is the size the UI is designed at, so it sets no
     attribute at all; the other two nudge it either way. A phone gets a smaller spread
     because its layout already runs close to the screen edges.

     The root's font size, not `zoom` on the body - and the difference is not cosmetic.
     **CSS `zoom` breaks `position: fixed` on iOS**: a bar with `bottom: 0` under a zoomed
     body resolves against a scaled coordinate space and lands short of the bottom edge by
     the difference, so at 0.94 on a 932pt screen the tab bar floated about 55pt up the
     screen. It looked correct on any page long enough to scroll, because scrolling drags a
     mis-positioned fixed element back into place, which is what made it look like a
     content-height problem for weeks. The docked chat card is fixed too, and had the same
     flaw waiting.

     Every size and every spacing step in this system is already in `rem` - `--spacing` is
     .25rem, the type scale is rem throughout - so moving the root font size scales the whole
     interface with no zoom anywhere and no coordinate space to get wrong. Values written in
     px deliberately stay put: a 16px field has to stay 16px or iOS zooms the page on focus,
     and a touch target should not shrink because somebody likes small text. */
  :root[data-scale='s'] {
    font-size: 15px;
  }
  :root[data-scale='l'] {
    font-size: 17.25px;
  }

  @media (min-width: 768px) {
    :root[data-scale='s'] {
      font-size: 14.4px;
    }
    :root[data-scale='l'] {
      font-size: 18.4px;
    }
  }

  /* Colour changes are animated so the theme toggle dissolves rather than jump-cuts.
     Scoped to colour only, so transforms and animations stay crisp. */
  body,
  header,
  main,
  aside,
  nav,
  section,
  article,
  li,
  input,
  textarea,
  select,
  button {
    transition:
      background-color 380ms cubic-bezier(0.22, 1, 0.36, 1),
      border-color 380ms cubic-bezier(0.22, 1, 0.36, 1),
      color 380ms cubic-bezier(0.22, 1, 0.36, 1);
  }

  ::selection {
    background: var(--color-accent);
    color: var(--color-accent-ink);
  }

  /* Anything focusable gets the same visible ring — keyboard and switch users included. */
  :focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
  }

  /* Honour the OS setting. Motion is decoration in an app; it is never load-bearing.
     A marketing page opts out with `data-motion="always"` on <html>, because there the
     motion IS the product - and because older Windows machines have animations disabled
     globally, so honouring the setting there shows a stranger a dead page. Owner's standing
     decision. Scoping it here rather than trying to undo it further down is deliberate:
     `animation-duration: revert` rolls back to the user-agent value, which is 0s, so an
     "un-disable" rule would disable the animations just as thoroughly. */
  @media (prefers-reduced-motion: reduce) {
    :root:not([data-motion='always']) *,
    :root:not([data-motion='always']) *::before,
    :root:not([data-motion='always']) *::after {
      animation-duration: 0.01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.01ms !important;
      scroll-behavior: auto !important;
    }
  }
}

/* The shared settings element (elements/settings.ts) paints nothing until its script has
   defined it. Hold its exact footprint meanwhile - 44px square, pulled flush with the edge -
   so the header does not shift sideways the moment the cog appears. */
mixtape-settings:not(:defined) {
  display: inline-block;
  width: 44px;
  height: 44px;
  margin-inline-end: var(--mx-cog-edge, -0.5rem);
}

/* Native <select> anchors its arrow to the padding box, so adding padding-right only
   squeezes the label — it never moves the arrow off the border. Suppress the native
   control and draw our own chevron at a fixed inset instead. Colour comes from a
   per-theme variable because a data: URI can't read currentColor. */


/* Minimum comfortable touch target — 44px is the Apple/WCAG floor. */


/* Physical feedback on tap. Cheap, and it makes the whole app feel alive. */


/* Children rise in sequence; set --i on each child for the stagger. */


/* iOS safe areas. `viewport-fit=cover` deliberately extends the page under the notch and
   the home indicator so backgrounds reach the screen edges - these keep *content* out of
   them. Applied to the sticky header and the fixed tab bar, never to <html>, because
   padding there breaks sticky positioning. */




/* The tab bar's own version: enough to clear a home indicator, not a whole band of it.
   Four items with labels already stand well clear, and the full inset left a strip of empty
   canvas under them that read as the bar floating rather than sitting on the edge. */
/* While the keyboard is up, everything pinned to the bottom stands aside. There is nothing
   to gain from a tab bar and a usage dock squeezed between the text you are writing and the
   keyboard you are writing it with. */
:root[data-kb] [data-overlay-bar] {
  display: none;
}

/* ...and the thing you are typing into comes to the keyboard, rather than the page being
   scrolled until it happens to be visible.

   Four rounds went into scrolling that card into the right place: correcting the browser,
   then racing it, then giving up and trusting it - and it still left the field under the
   keyboard on a phone. Docking removes the question. `bottom: var(--kb)` is the top of the
   keyboard on both platforms: zero on Android, where the layout viewport already shrank,
   and the keyboard's height on iOS, where it did not.

   `--kb-bar` is the editor toolbar Android floats over the page - arrows and a Done tick -
   which no measurement includes and no attribute refuses. It is reserved *inside* the card
   rather than by lifting the card, so the strip it covers is the card's own background
   instead of the conversation showing through underneath. Zero on iOS, which draws no such
   bar for a `contenteditable`. See Keyboard.tsx. */
:root[data-kb] [data-docks-to-keyboard] {
  position: fixed;
  left: 0;
  right: 0;
  bottom: var(--kb, 0px);
  padding-bottom: var(--kb-bar, 0px);
  z-index: 45;
  border-radius: 0;
  border-left: 0;
  border-right: 0;
  max-height: 100%;
}

/* A field you can tap without the page jumping.

   iOS zooms the whole page when you focus a control whose text is under 16px, and every
   measurement taken afterwards is in a scaled viewport - which is how the chat card once
   ended up somewhere else entirely. 16px on a phone is the guard.

   The trigger is the *device*, not the window width, and tying it to a `sm:` breakpoint got
   that wrong: a phone in landscape and a tablet are both wider than any breakpoint and both
   still zoom. So ask what is pointing at the screen. A mouse gets the smaller, tidier text;
   anything you touch gets 16px, at any width. */
.tap-text {
  font-size: 13px;
}
@media (pointer: coarse) {
  .tap-text {
    font-size: 16px;
  }

  /* No compensation for the interface scale here any more. It used to be a `zoom` on the
     body, which rendered this 16px at 15.04 and let iOS zoom the page anyway; the scale is
     the root font size now and this is px, so 16 stays 16 at every setting. */
}

/* A strip that scrolls sideways in step with another one, so only one of them should show
   a scrollbar. The board's headings mirror the columns below them; two bars would be two
   controls for one axis. */
[data-no-scrollbar] {
  scrollbar-width: none;
}
[data-no-scrollbar]::-webkit-scrollbar {
  display: none;
}

/* Chrome that gets out of the way.

   Scrolling down is the one time the two bottom bars are worth less than the rows they are
   covering, so they leave - and come back the moment you scroll up, which is when you are
   looking for them. Only past a threshold, so a short nudge does not flap them. Never while
   the keyboard is up: those bars are already hidden then, and a transform on top of that is
   a second answer to a settled question.

   The header goes too, but only on a phone: on a desktop it carries the nav and there is
   screen to spare. It is `sticky`, so by the time it leaves - a hundred and twenty pixels
   down - the place it holds in the flow is already scrolled past, and nothing opens a gap
   where it was. The board's sticky column headings move up to take its place, or they would
   hang below a bar that is no longer there. */
[data-chrome-bar] {
  transition: transform 220ms var(--ease-out-soft);
}
:root[data-chrome-away] [data-chrome-bar='bottom'] {
  transform: translateY(100%);
}
@media (max-width: 767px) {
  :root[data-chrome-away] header[data-chrome-bar] {
    transform: translateY(-100%);
  }
  /* Up into the space the header left - all the way, and filling the notch itself rather
     than starting under it. Stopping at the inset leaves a band of status bar with the
     board scrolling behind it, which is what the header had been covering. */
  :root[data-chrome-away] [data-board-headings] {
    top: 0;
    padding-top: env(safe-area-inset-top);
  }
}
@media (prefers-reduced-motion: reduce) {
  [data-chrome-bar] {
    transition: none;
  }
}



/* Landscape on a notched phone puts the cutout on one side. */


/* The studio floor. Two-frame sprite animation done the pixel-art way: cross-fade nothing,
   just swap which frame is visible on a step timing function, so it reads as real frames
   rather than a smooth tween. */
@keyframes bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-1.5%); }
}
@keyframes frame-a {
  0%, 49.9% { opacity: 1; }
  50%, 100% { opacity: 0; }
}
@keyframes frame-b {
  0%, 49.9% { opacity: 0; }
  50%, 100% { opacity: 1; }
}
.frame-a { animation: frame-a 0.44s steps(1, end) infinite; }
.frame-b { animation: frame-b 0.44s steps(1, end) infinite; }

/* The two frames are sibling <g> elements at the same coordinates inside one SVG, so they
   already overlap - opacity alone swaps which one you see. CSS positioning does not apply
   to SVG groups, so do not reach for it here. */

/* Somebody who does not want motion still gets the colour and the bubble, which carry the
   same information - the animation is emphasis, never the only signal. */
@media (prefers-reduced-motion: reduce) {
  .frame-a, .frame-b { animation: none; }
  .frame-b { opacity: 0; }
}

/* ── No zoom-lurch when a field is focused ──────────────────────────────────────────────
 *
 * iOS Safari zooms the whole page in when you tap into a control whose text is smaller
 * than 16px, and zooms back out when you leave it. Every text field in this app is 14px,
 * so typing anywhere meant the page jumping in and out.
 *
 * The usual fix - `maximum-scale=1, user-scalable=no` in the viewport meta - takes
 * pinch-zoom away from everybody permanently to solve a typing annoyance. Giving the
 * controls the 16px Safari is asking for costs nothing and leaves zoom alone.
 *
 * Deliberately unlayered: Tailwind's utilities live in a cascade layer, and anything
 * layered loses to unlayered rules regardless of order - which is the only way to beat the
 * `text-sm` that every control here carries.
 */
@media (pointer: coarse) {
  input:not([type='checkbox']):not([type='radio']):not([type='color']):not([type='range']),
  select,
  textarea {
    font-size: 16px;
  }

  /* The pre-divided 17.05px that used to sit here is gone with the `zoom` it compensated
     for. 16px is 16px at every interface scale now. */
}

/* An always-dark surface - the photo viewer - and what sits on it, the same in both themes. Named
   here so the app layer names no colour of its own (the gate's colour check reads a product's
   vendored app.css; RentKeeper went red on the viewer's literal #050505/#fff, 2026-10-05). */
:root {
  --c-scrim: #050505;
  --c-scrim-ink: #ffffff;
}

/* ── The base every page wears ──────────────────────────────────────────────
   The landing and app layers are written for border-box, a body with no margin, and the kit's
   face on the body - and until 2026-10-05 the kit never said so: Dayrule wrote it into its own
   sheet, and RentKeeper did not, so it went live in the browser's Times with every box a little
   wider than its column. Here once, for every product. `:where()` keeps it at zero
   specificity: a product's own body rule, Tailwind's preflight (Studio) or a class still wins. */
:where(*, *::before, *::after) { box-sizing: border-box; }
/* `hidden` always hides. The browser's own [hidden] rule loses to any layout that sets display - a
   table restacked as rows on a phone (`tr { display: block }`) showed every row the kit's filter had
   hidden, while the laptop showed two (2026-10-05). The kit's filter, sort bar and photo picker all
   hide with the attribute, so it has to win. */
[hidden]:not([hidden='until-found']) { display: none !important; }
:where(body) {
  margin: 0;
  background: var(--c-canvas);
  color: var(--c-ink);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
}

/* ── Every script of the kit's 42 languages (mixtape-i18n.mjs LANGUAGES, 2026-10-05) ──────────
   The device's own fonts for the scripts no design downloads, in the regional form the page's
   language asks for: one Han character is drawn differently in Japan, Korea, mainland China and
   Taiwan, and only lang tells the browser which. */
:lang(ja) { --font-script: 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', 'Yu Gothic UI', 'Yu Gothic', Meiryo, 'Noto Sans JP', 'Noto Sans CJK JP'; }
:lang(ko) { --font-script: 'Apple SD Gothic Neo', 'Malgun Gothic', 'Noto Sans KR', 'Noto Sans CJK KR'; }
:lang(zh-Hans) { --font-script: 'PingFang SC', 'Microsoft YaHei', 'Noto Sans SC', 'Noto Sans CJK SC'; }
:lang(zh-Hant) { --font-script: 'PingFang TC', 'Microsoft JhengHei', 'Noto Sans TC', 'Noto Sans CJK TC'; }
:lang(th) { --font-script: Thonburi, 'Leelawadee UI', 'Noto Sans Thai'; }
:lang(hi) { --font-script: 'Kohinoor Devanagari', 'Nirmala UI', 'Noto Sans Devanagari'; }
:lang(ar) { --font-script: 'Geeza Pro', 'Segoe UI', 'Noto Sans Arabic'; }

/* Letter-spacing tears Arabic's joined letters apart and breaks Hindi's and Thai's stacked marks,
   and spreads Chinese, Japanese and Korean thin: the kit's spaced capitals (labels, eyebrows,
   badges) and tight display headings are Latin habits. In these scripts, the font's own spacing. */
:where(:lang(ja), :lang(ko), :lang(zh-Hans), :lang(zh-Hant), :lang(th), :lang(hi), :lang(ar)) * { letter-spacing: normal !important; }
/* Marks above and below the line need room a Latin heading's tight line-height does not give. */
:where(:lang(th), :lang(hi), :lang(ar)) :is(h1, h2, h3, .lp-h1, .lp-h2, .lp-h3, .mx-h1, .mx-h2) { line-height: 1.4 !important; }
/* Korean breaks lines between words, as English does - not inside one. */
:lang(ko) { word-break: keep-all; }
:where(:lang(ja), :lang(zh-Hans), :lang(zh-Hant)) { line-break: strict; }

/* Right to left (Arabic): the kit lays out with start and end, and these are the few things that
   only have a direction - a slide, an arrow. --mx-dir reaches inside the menus' shadow roots,
   which cannot see the page's dir attribute. */
:root { --mx-dir: 1; --mx-end: right; }
[dir='rtl'] { --mx-dir: -1; --mx-end: left; }
