Feature: Language switcher conforms to the official design Background: Given the design in docs/design is the visual spec of record for LanguageSwitcher and UserMenu And the interface supports exactly two languages, English and Français And each language is always written in its own name, whatever the interface language And the language rules of features/spa_internationalization.feature still apply And the language control no longer uses a form select anywhere And no language preference is sent to the server Scenario Outline: Account menu lists Profile, Theme, Language and Log out in design order Given a signed-in user opens the account menu in the Then the entries appear in this order | Profile | | separator | | "Theme" section label | | System, Light, Dark | | separator | | "Language" section label | | English, Français | | separator | | Log out | And the "Theme" and "Language" labels are 11 px, weight 500, uppercase, letter-spacing 0.04em and --ui-text-muted And the label padding is And the menu is And every item row is high Examples: | surface | label_padding | size | row_height | | desktop UserMenu popover | 6px 8px 2px | a 224 px popover above the chip | 32 px | | mobile AccountMenuSheet | 8px 16px 2px | a bottom sheet with grab handle | 48 px | Scenario Outline: Language rows are a radio group naming each language in its own name Given the interface language is When the account menu is open in the Then the "Language" label names a group of two menuitemradio rows And the rows read "English" with lang "en" and "Français" with lang "fr" And the row for has aria-checked "true" and a check icon at the right edge And the other row has aria-checked "false" and no check icon And no Nuxt UI select, combobox or languages icon row is present in the menu Examples: | surface | locale | locale_name | | desktop UserMenu popover | en | English | | desktop UserMenu popover | fr | Français | | mobile AccountMenuSheet | en | English | | mobile AccountMenuSheet | fr | Français | Scenario Outline: Theme rows are radio items that keep the menu open Given the theme preference is When the account menu is open in the Then System, Light and Dark are menuitemradio rows in a group named "Theme" And only the row has aria-checked "true" and a check icon When the user chooses another theme row Then the theme applies at once and the menu stays open Examples: | surface | preference | | desktop UserMenu popover | light | | mobile AccountMenuSheet | dark | Scenario Outline: Choosing a language in the account menu applies it at once and keeps the menu open Given the interface language is en and the user is on /profile with an unsaved display name "Alice B." And the account menu is open in the When the user chooses "Français" Then all interface text, including the menu labels "Thème" and "Langue", is French And the document lang is "fr" and localStorage "renewcms-language" is "fr" And the Français row is checked and the menu stays open And the route, session, theme and unsaved display name are unchanged And no reload, navigation or /v1 request occurs When the user chooses "English" Then the interface is English, the document lang is "en" and the menu stays open Examples: | surface | | desktop UserMenu popover | | mobile AccountMenuSheet | Scenario Outline: Account menu keyboard and dismissal Given the account menu is open in the When the user moves with ArrowDown and ArrowUp Then focus visits Profile, the theme rows, the language rows and Log out in visual order and wraps at the ends When the user presses Enter or Space on a language row Then that language is applied and the menu stays open When the user presses Escape or clicks outside Then the menu closes without changing the language Examples: | surface | | desktop UserMenu popover | | mobile AccountMenuSheet | Scenario Outline: Auth footer shows the language chip beside the theme chip Given the user is signed out on And the interface language is en Then the footer outside the card contains the language chip and the System theme chip aligned right with an 8 px gap And any existing footer slot content stays to the left of the chips And the chip shows the lucide languages icon at 14 px followed by "English" And the chip has a 1 px --ui-border-accented border, --ui-text-toned text, aria-haspopup "menu", aria-expanded "false" and aria-label "Language: English" And no language select is rendered in the card or the footer Examples: | route | | /login | | /register | | /register/pending | | /register/validate | | /register/reset/request | | /register/reset/validate | | /invite/accept | Scenario Outline: Auth footer is always rendered, on mobile too Given the viewport is When an auth screen with no footer slot content renders Then the footer with the language chip and the theme chip is visible And the chip is high Examples: | viewport | chip_height | | 1440 x 900 | 28 px | | 390 x 844 | 44 px | Scenario: Chip reflects the current language Given the stored language is fr When /login renders Then the chip reads "Français" and its aria-label is "Langue : Français" Scenario Outline: Auth language popover Given the user is signed out on /login in When the user activates the language chip Then the chip has aria-expanded "true" and --ui-bg-elevated And a popover with role "menu" named "Language" opens above the chip, right-aligned and 36 px above its bottom edge, 168 px wide, --ui-bg, 1 px --ui-border, --rc-shadow-pop and 4 px padding And it holds menuitemradio rows "English" (lang "en") and "Français" (lang "fr") that are 32 px high on desktop and 44 px on mobile And the current language row has aria-checked "true" and a 16 px check icon at the right edge Examples: | locale_name | | English | | Français | Scenario: Choosing a language in the auth popover closes it Given the auth language popover is open on /login with English current When the user chooses "Français" Then the whole screen is French at once, including the card, the footer and the document title And the document lang is "fr" and localStorage "renewcms-language" is "fr" And the popover closes and focus returns to the language chip And the chip reads "Français" And the form values already typed, the route and the theme are unchanged And no /v1 request occurs Scenario: Dismiss the auth popover without changing language Given the auth language popover is open When the user presses Escape or clicks outside it Then it closes, aria-expanded is "false" and the language is unchanged And focus returns to the chip on Escape Scenario: Auth popover keyboard Given the language chip has focus When the user presses Enter, Space or ArrowDown Then the popover opens with focus on the current language row And ArrowUp and ArrowDown move between the two rows and Enter or Space chooses one Scenario: Language persists across public and signed-in screens Given the user chose Français on /login When they sign in, open the account menu and log out Then Français is checked in the account menu and the /login chip reads "Français" again And a storage failure still lets the choice apply for the current session, as in spa_internationalization Scenario: Catalog parity for the new strings Then the en and fr catalogs contain "Theme" and "Language: {0}" And "Language: {0}" keeps its {0} parameter in French And the strings "English" and "Français" are not translated