@charset "UTF-8";

/* mxw-lang-links — a link that LEAVES the language you are reading
 * says so, with a small flag after it.
 *
 * A component, asked for by name like any other:
 *
 *     <m:framework mxw="lang-links"/>          all the flags it knows
 *     <m:framework mxw="lang-links(en,fr)"/>   only these two
 *
 * which also turns on the flag URLs the framework publishes
 * (m:head-lang-flags): the sheet and the values it reads arrive
 * together or not at all, and a site that wants neither pays for
 * neither.
 *
 * No .js. Like mxw-lang-switcher.css it is a CSS contract with markup
 * the framework emits — here the `hreflang` the copy already carries
 * and the `lang` on <html> — and there is nothing to script.
 *
 * Nothing is added to the markup. The copy already carries `hreflang`
 * on links that go elsewhere — the estate has 187 of them, most on
 * www.la-terre-est-ronde — so the rule marks the attribute, not the
 * link, and there is nothing to keep in step by hand.
 *
 * `hreflang`, and never `lang`: `lang` says what language an element's
 * own content is in, which is a different fact from where the link
 * goes. A German book title in a French sentence is `lang="de"` and
 * needs no flag; a French page about it is `hreflang="de"` and does.
 *
 * THREE MODES OVERRIDE THE DECISION, at the foot of this file, and any
 * of them may sit on the LINK or on a CONTAINER:
 *
 *     .mxw-lang-flag-forced   show it whatever the languages are
 *     .mxw-lang-flag-none     hide it
 *     .mxw-lang-flag-auto     the default decision, back again
 *
 * A class on the link beats a class on its container. -auto is only
 * useful under a container that said otherwise; on its own it does
 * what leaving the class off does. They are in `utilities`, so they
 * win by layer order rather than by out-specifying anything here.
 *
 * Layer order is declared once, inline, by MoXoW (m:head-layers in
 * html-head-body.xsl). `components` puts the default behaviour before
 * `theme` and `site`, so both can overrule any of it. */

@layer tokens {

  /* THE FLAG SET -- one token per language, so a site can re-point a
     single flag from anywhere in the ancestry without restating the
     rule that uses it, and so the paths sit together instead of one
     per selector.

     THE BASE PATH REPEATS, and cannot be factored out: CSS has no
     string concatenation. Measured in Chromium 149 rather than
     recalled -- url() takes no var(); src(), which takes a <string>
     that a var() could feed, is not supported at all; two strings
     side by side do not join; url(attr()) is not supported and typed
     attr() is Chrome-only and yields a string anyway. A whole url()
     held in a custom property is the one thing that does work, and it
     is what these are.

     SO THE FRAMEWORK BUILDS THEM. MoXoW emits exactly these
     properties into the head (m:head-lang-flags, core/html-head-body
     .xsl), composed by m:lang-flag-src from $m:flag-path and
     $m:flag-format -- the same function the switcher uses, so the two
     cannot disagree, and a site that moves the flag set or asks for
     the raster one gets the right URLs here without editing this.

     What is below is the FALLBACK, for a page whose framework does not
     emit them: it is @layer tokens, and the head block is unlayered,
     so the framework wins wherever it speaks. Values on :root, not
     rules fighting over an element, which is the one case where
     unlayered winning is the point rather than a trap.

     The file is named by COUNTRY: en takes gb, ja takes jp, cs takes
     cz, mirroring the framework's m:lang-to-flag. The last two of the
     eleven are the legacy jp/cz spellings, which share a flag with ja
     and cs and go when that copy is corrected. */
  :root {
    --mxw-lang-flag-fr: url("/common/images/flags/rectangle/fr.svg");
    --mxw-lang-flag-de: url("/common/images/flags/rectangle/de.svg");
    --mxw-lang-flag-it: url("/common/images/flags/rectangle/it.svg");
    --mxw-lang-flag-es: url("/common/images/flags/rectangle/es.svg");
    --mxw-lang-flag-nl: url("/common/images/flags/rectangle/nl.svg");
    --mxw-lang-flag-th: url("/common/images/flags/rectangle/th.svg");
    --mxw-lang-flag-en: url("/common/images/flags/rectangle/gb.svg");
    --mxw-lang-flag-ja: url("/common/images/flags/rectangle/jp.svg");
    --mxw-lang-flag-cs: url("/common/images/flags/rectangle/cz.svg");
    --mxw-lang-flag-jp: url("/common/images/flags/rectangle/jp.svg");
    --mxw-lang-flag-cz: url("/common/images/flags/rectangle/cz.svg");
  }

} /* @layer tokens */

@layer components {

  /* THE SCOPE is the content column and text links inside it. Chrome
     and Firefox in mind, `:has()` does the exclusion that used to take
     a list of prose containers: a link wrapping a picture is a picture,
     and a flag stuck on the corner of a logo is not a cue, it is
     litter. (nacelles' /flight/ has exactly that — the balloon
     operator's logo is an <a hreflang="fr"> around an <img>.)
     `.mxw-content`, the class and not the id: a shared sheet should be
     easy to overrule, and MoXoW emits both. */
  .mxw-content a[hreflang]:not(:has(img, svg, picture, video, canvas)) {

    /* THE MARK — a no-break space, with the flag painted over the
       padding beside it. Inert until something gives it `content`: a
       pseudo-element with no content generates no box, so these
       declarations reach only the links actually marked.

       The obvious shape — an empty inline-block sized in em — puts the
       mark on its own line: an atomic inline is a break opportunity,
       and measured on nacelles' English /flight/, "Brindas airfield"
       ended a justified line and its flag started the next one. A
       no-break space is CONTENT, so the line breaker cannot break
       before it, and the padding gives the background its room without
       anything needing a size.

       The cost is that a link's underline, where the site draws one,
       runs under the mark: an atomic inline is exactly the thing text
       decoration does not propagate into, and this is not one. Kept —
       the flag belongs to the link, and a line stopping short of it
       read as a mark that had come loose.

       1.125em x 0.75em is 3:2, the ratio the `rectangle` set is cut to,
       and in `em` because the mark belongs to the words it follows. A
       BACKGROUND takes the size it is given, so the SVG's
       width="900" height="600" — which lays an <img> out at 900x600,
       and which mxw-lang-switcher.css exists to correct — cannot arise
       here.

       AND A HAIRLINE ROUND IT, which is not decoration. Both flags this
       estate reaches for most run white to their own edge, and on a pale
       page the white simply stops existing: measured on www.alux.fr
       (#f3f3f3), the tricolore read as a blue bar and a red bar with a
       gap where the middle should be. The exhibition wall hid the
       problem by being dark; a shared sheet cannot assume either.

       Two background layers, not a border and not a box-shadow: those
       would ring the whole inline box — no-break space and all — where
       what needs an edge is the flag. The lower layer is a solid
       rectangle at the full size; the flag sits 1px inside it on every
       side, `right 1px center` against the backing layer's `right
       center` giving the same inset left and right, and `center` on both
       giving it above and below. --mxw-lang-mark-edge is read but never set,
       so a theme can tune it from anywhere in the ancestry and the
       default here cannot get in the way. */
    &::after {
      content: var(--mxw-lang-mark-auto, none);
      padding-inline-end: 1.125em;
      background:
        var(--mxw-lang-flag)
          right 1px center / calc(1.125em - 2px) calc(0.75em - 2px) no-repeat,
        linear-gradient(var(--mxw-lang-mark-edge, rgb(128 128 128 / 0.7)),
                        var(--mxw-lang-mark-edge, rgb(128 128 128 / 0.7)))
          right center / 1.125em 0.75em no-repeat;
    }

    /* ================================================================
       WHICH FLAG — one line per language, from the target's own tag and
       nothing else. Set on the LINK, inherited by the mark.

       Deliberately independent of whether the mark will be shown. The
       two questions used to share a line, which was shorter and made
       `.mxw-lang-flag-forced` below impossible to write: a link the page's own
       language matched had no flag to show, because the flag was only
       named in the rule that had already decided not to show it.

       The path is not here. Each line names the TOKEN its language
       uses, and the tokens are together at the top of the file, where
       a site can re-point one without restating the rule that reads
       it. Which also means a language wants two lines rather than
       one -- the token, and this mapping -- and that is the price of
       not writing a URL into a selector.

       The mapping is not the identity: en takes gb, ja takes jp, cs
       takes cz, because the files are named by country and a language
       is not one. It mirrors the framework's own m:lang-to-flag
       (helpers.xsl), and the two have to agree.

       `|=`, so a regional tag (`en-GB`, `pt-BR`) counts as its language.
       ================================================================ */
    &[hreflang|="fr"] { --mxw-lang-flag: var(--mxw-lang-flag-fr); }
    &[hreflang|="de"] { --mxw-lang-flag: var(--mxw-lang-flag-de); }
    &[hreflang|="it"] { --mxw-lang-flag: var(--mxw-lang-flag-it); }
    &[hreflang|="es"] { --mxw-lang-flag: var(--mxw-lang-flag-es); }
    &[hreflang|="nl"] { --mxw-lang-flag: var(--mxw-lang-flag-nl); }
    &[hreflang|="th"] { --mxw-lang-flag: var(--mxw-lang-flag-th); }
    /* English has no country of its own and `en` is not a country code,
       so a set named by ISO 3166 has no file for it: the Union flag, as
       m:lang-to-flag decides for the switcher. */
    &[hreflang|="en"] { --mxw-lang-flag: var(--mxw-lang-flag-en); }
    /* Japanese and Czech, whose language codes are nothing like their
       countries'. */
    &[hreflang|="ja"] { --mxw-lang-flag: var(--mxw-lang-flag-ja); }
    &[hreflang|="cs"] { --mxw-lang-flag: var(--mxw-lang-flag-cs); }

    /* LEGACY TAGS, and they are wrong. `jp` and `cz` are the COUNTRY
       codes; the language subtags are `ja` and `cs`, and `jp`/`cz` are
       not registered as language subtags at all — so `hreflang="jp"` is
       not merely unidiomatic, it says nothing a browser or a crawler
       can use. 34 links on www.la-terre-est-ronde say `jp` and 6 say
       `cz`, written before the distinction was noticed.

       Carried so those forty links are marked rather than silently
       skipped, and to be DELETED once the copy is corrected — the
       correction is the real fix and these lines are the reminder that
       it has not happened. Exact match, not `|=`: nothing should grow a
       region on top of a tag that is already wrong. */
    &[hreflang="jp"] { --mxw-lang-flag: var(--mxw-lang-flag-jp); }
    &[hreflang="cz"] { --mxw-lang-flag: var(--mxw-lang-flag-cz); }

    /* ================================================================
       WHETHER TO SHOW IT — when the document is in another language.

       RECORDED, not applied: these set --mxw-lang-mark-auto, and the
       mark above reads it. Written straight into `content` there was
       nothing for .mxw-lang-flag-auto to put back — the decision
       existed only as a rule that had already fired or not.

       `:root[lang]:not(:lang(xx))` is the comparison. CSS cannot ask
       whether an attribute differs from an ancestor's, but it can ask
       whether the DOCUMENT is in that language, which is the same
       question put the other way round and needs no per-page-language
       list. `:root` and not a bare `:not(:lang(xx))`, so a `lang` on the
       anchor itself — the German book title above — cannot suppress the
       mark.

       Note the `[lang]` every line carries. A PAGE THAT DOES NOT SAY
       WHAT LANGUAGE IT IS IN gets no marks at all: each line asks
       whether the document is in some language, and `:lang()` answers no
       to all of them when <html> has no `lang`, so without the attribute
       test a page would wear a flag on EVERY hreflang link, including
       the ones going nowhere out of the reader's language. Marking
       everything says what marking nothing says, only louder and
       wrongly.

       Written into each selector rather than as one suppressing rule
       after them: that rule was tried and lost, being a class less
       specific than the lines it had to beat. A condition every line
       must satisfy belongs in every line.

       This was not hypothetical. MoXoW used to COPY m:moxow/@lang onto
       the element (core/html.xsl), and a copy of an absent attribute is
       nothing, so six of www.sdalu.com's eighty page sources — its home
       page among them — served <html> with no lang at all. Fixed at the
       source on 2026-08-01: the element now falls back to $m:lang, the
       answer the framework had been using all along, so every page on
       the estate states a language and this guard fires nowhere. It
       stays because the sheet is shared and cannot assume its host.

       Deliberately NOT defaulting to English to match $m:lang: guessing
       the content's language from a stylesheet is how a French page that
       forgot the attribute gets labelled English by a second party. The
       attribute is the fix; this makes its absence show up as "no flags"
       rather than as "flags everywhere".
       ================================================================ */
    :root[lang]:not(:lang(fr)) &[hreflang|="fr"],
    :root[lang]:not(:lang(de)) &[hreflang|="de"],
    :root[lang]:not(:lang(it)) &[hreflang|="it"],
    :root[lang]:not(:lang(es)) &[hreflang|="es"],
    :root[lang]:not(:lang(nl)) &[hreflang|="nl"],
    :root[lang]:not(:lang(th)) &[hreflang|="th"],
    :root[lang]:not(:lang(en)) &[hreflang|="en"],
    :root[lang]:not(:lang(ja)) &[hreflang|="ja"],
    :root[lang]:not(:lang(cs)) &[hreflang|="cs"],
    /* the two legacy spellings again */
    :root[lang]:not(:lang(ja)) &[hreflang="jp"],
    :root[lang]:not(:lang(cs)) &[hreflang="cz"] {
      --mxw-lang-mark-auto: "\00a0";
    }
  }

  /* A language this sheet has no line for gets NO mark rather than an
     empty gap: nothing sets `content` for it, so no box is generated and
     the declarations at the top of the block reach nothing. Adding one
     is two lines above — the flag, and the language in the list.

     The fact stays in `hreflang`, where a machine reads it. The flag is
     the sighted reader's copy of it, decorative, and forced-colors drops
     it with every other background image — nothing is lost there that the
     attribute did not already carry.

     A flag names a country and not a language, which is the standing
     objection to using one this way. It is answered by the language
     switcher, which already makes these flags stand for these languages
     at the top of every page of the estate: a second vocabulary in the
     body of the same page would be the confusing choice, not this. */

} /* @layer components */


/* ====================================================================
   The two overrides.

   `utilities` comes after `components` in the order MoXoW declares, so
   these win on LAYER rather than on specificity — which is the whole
   reason they can be one class long against selectors carrying `:root`,
   `:not()` and an attribute test. Winning is a utility's job; needing
   `!important` or a longer selector to do it is the smell this avoids.

   Both need the link to carry an `hreflang` the sheet has a flag line
   for. That is not a limitation to work around: the class decides
   whether the mark is SHOWN, and it cannot invent which flag to show.
   On a link with no `hreflang` neither class does anything, the mark's
   box rule not matching at all; on one whose language has no line, the
   `var(--mxw-lang-flag)` substitution fails, which takes the whole
   `background` shorthand with it — so the result is an invisible space
   rather than an empty rectangle.
   ==================================================================== */

@layer utilities {

  /* THREE MODES, and any of them may sit on the LINK or on something
     CONTAINING it. A section that says .mxw-lang-flag-none quiets every
     link inside it; a link inside that section says
     .mxw-lang-flag-auto and gets its own decision back.

     That is what -auto is for, and it is the only thing it is for: on
     a link with nothing above it, it does exactly what leaving the
     class off does. It exists so a container-level decision has an
     exception, and because "this one is deliberately automatic" is
     worth being able to say in markup.

     The container rules are wrapped in :where() so they weigh nothing,
     and the link rules name the anchor, so a class on the link always
     beats a class on its container however deep either sits. Ordinary
     specificity would have made that a question of who was written
     last. */

  /* Whatever the languages are. For a sentence that OFFERS a choice —
     "the full record is in English and in French" wants both flags,
     the flag there being the answer offered rather than a warning
     about where a link leads. Marking only the French one reads as
     though the other were not a language choice at all. */
  :where(.mxw-lang-flag-forced) .mxw-content a[hreflang]::after,
  .mxw-content :where(.mxw-lang-flag-forced) a[hreflang]::after,
  .mxw-content a[hreflang].mxw-lang-flag-forced::after {
    content: "\00a0";
  }

  /* Never. For links whose language the reader has already been told —
     a list under a "in French:" heading, where a flag on every row is
     noise — or one where the mark lands badly. */
  :where(.mxw-lang-flag-none) .mxw-content a[hreflang]::after,
  .mxw-content :where(.mxw-lang-flag-none) a[hreflang]::after,
  .mxw-content a[hreflang].mxw-lang-flag-none::after {
    content: none;
  }

  /* The default decision, back again: whatever the comparison recorded
     in --mxw-lang-mark-auto, which is nothing at all where the
     languages match. Only useful under a container that said
     otherwise. */
  .mxw-content a[hreflang].mxw-lang-flag-auto::after {
    content: var(--mxw-lang-mark-auto, none);
  }

} /* @layer utilities */
