/* ══════════════════════════════════════════════════════════════════════════════
   layout.css — the application shell (§0.3)
   ══════════════════════════════════════════════════════════════════════════════

   Layer 3 of 3.

       ┌──────────────┬─────────────────────────────────────┬──────────────┐
       │  ALICE·OIS   │  breadcrumb                         │   SITE       │
       │  brand card  │  welcome message + sync/alert       │   card       │
       ├──────────────┼─────────────────────────────────────┴──────────────┤
       │  sidebar     │  workspace                                         │
       │  (own color) │                                                    │
       │  nav         │                                                    │
       │  user/lang   │                                                    │
       └──────────────┴────────────────────────────────────────────────────┘
                                 footer

   Three properties of this layout are requirements, not styling choices:

   1. **The shell is an anchor frame; the document itself never scrolls.** Only
      the workspace scrolls. This is what keeps the brand card, the breadcrumb,
      the site card and the sidebar in place while a long clinical form moves —
      so a clinician never loses the identity of the record they are editing.
   2. **Lateral padding is 16px** (§0.3, --gutter). "The horizontal space belongs
      to the workspace."
   3. **The sidebar has its own colour family** and a visible gap separating it
      from the brand card, so it does not read as a downward extension of it.
   ────────────────────────────────────────────────────────────────────────── */

/*
   **The document never scrolls — only .workspace does**, and this is now
   enforced in three places rather than one. The user found the whole
   application scrolling vertically, with dead ground below the footer:

       « THIS SHOULD NOT BE POSSIBLE. THE WHOLE APPLICATION IS STILL SCROLLABLE
         VERTICALY. ONLY THE CONTENT SHOULD BE. »

   `height: 100vh` was the whole of the rule and it is not enough. `vh` is the
   **largest** viewport — it ignores a browser toolbar that comes and goes — so
   on any browser with a dynamic one the shell is taller than what is visible,
   and the page scrolls by exactly the toolbar's height. That is the symptom
   reported: the footer rises and background shows beneath it.

   `dvh` is the *dynamic* viewport and is what was wanted all along; `vh` stays
   first as the fallback for a browser that does not know it. `overflow: hidden`
   on the root and the body is the belt to that pair of braces — if anything
   ever escapes the shell again, it is clipped rather than scrolled.
*/
html,
body {
  overflow: hidden;
  overscroll-behavior: none;
}

.shell {
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* ── Top strip ──────────────────────────────────────────────────────────────
   Brand card left, centre stack in the middle, site card right. The two cards
   anchor the extremities of the header (§0.3). */

/* ⚠️ `align-items: center` and NOT `stretch` — D686, and the diagnosis is
   worth keeping because the symptom pointed at the wrong element.

   The user, on an annotated Accueil: « EMPTY SPACE NOT BEAUTIFUL », with the
   mark on the site card. The card's own padding was never the problem. The
   centre column is TWO rows — the breadcrumb (≈37px), the gap, and the welcome
   bar (≈50px) — while the brand and site cards each hold a single ≈36px row;
   under `stretch` each card's BACKGROUND was pulled to the full ≈95px with
   `.brand-card, .site-card`'s own `align-items: center` holding the content in
   the middle of it. So the empty space was inside the cards, above and below
   the logo, and no amount of padding work would have found it.

   ⚠️⚠️ **AND CENTRING WAS TRIED AND REVERTED — D694, the user's own verdict on
   the result: « I WANT TO GO BACK TO THIS CONFIGURATION FOR BOTH. THE SPACE
   AROUND THEM IS WORSE THAN THE PREVIOUS SITUATION. »**

   The diagnosis above is still correct and is kept for it: `stretch` does pull
   each card's background to the centre stack's height. What was wrong was the
   remedy. Centring moves the leftover height OUT of the cards and into the grid
   gutter around them, so instead of two tall cards flush with the breadcrumb
   there are two small ones floating in a band of page — which reads as a gap in
   the header rather than as a roomy card, and is worse. A full-height card is
   the shell's own shape.

   ⚠️ **Do not re-centre this in a later session.** The reasoning that argues for
   it is written above and it loses to how it actually looks. */
.topstrip {
  flex: none;
  display: grid;
  grid-template-columns: 216px 1fr 232px;
  gap: var(--gap);
  padding: var(--gap) var(--gutter) 0;
  align-items: stretch;
}

.brand-card,
.site-card {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 12px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: var(--sh);
}

.brand-mark {
  flex: none;
  width: 36px; height: 36px;
  border-radius: 10px;
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(140deg, var(--brand-lt), var(--brand-dk));
  color: #fff;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 17px;
}

/* Both cards stack a name over a subtitle. These are <span>s in the markup so
   the whole card can be one <a>; without an explicit `display: block` they lay
   out inline and the subtitle runs on after the name. */
.brand-name,
.site-name { display: block; }
.brand-sub,
.site-sub { display: block; }

.brand-name {
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 600;
  color: var(--brand-dk);
  letter-spacing: .2px;
  line-height: 1.1;
}

.brand-sub { font-size: 9.7px; color: var(--ink-3); line-height: 1.25; margin-top: 2px; }

.site-mark {
  flex: none;
  width: 36px; height: 36px;
  border-radius: 9px;
  display: flex; align-items: center; justify-content: center;
  background: var(--accent-lt);
  color: var(--accent-dk);
}

.site-name { font-size: 13px; font-weight: 700; color: var(--ink); line-height: 1.15; }
.site-sub { font-size: 10.3px; color: var(--ink-3); margin-top: 1px; }
.site-card a { color: inherit; }
.site-card a:hover { text-decoration: none; }

/* The card as a link — §8's own way in, build step 13.

   ⚠️ The two rules above target a link INSIDE the card, because the card was a
   `<div>` until this step. The card itself being an `<a>` is a different
   selector, and without it the whole card renders in the browser's link blue
   on every authenticated page.

   ⚠️ It belongs HERE and not in `administration.css`: the card is on every
   authenticated screen and that stylesheet is loaded on §8's alone, which is
   D208's own recorded lesson read the other way round. The card is a link only
   for a reader who may open the module (§9 trap 22) — decided in the template,
   not here. */
.site-card-link {
  color: inherit;
  text-decoration: none;
}

.site-card-link:hover { border-color: var(--brand-lt); }
.site-card-link:hover .site-name { color: var(--brand); }

/* ── Centre stack: breadcrumb over welcome bar ──────────────────────────────*/

.centre-stack {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;   /* lets the grid child shrink instead of forcing overflow */
}

/* Breadcrumb (§0.4) — a path from the selected module down to the current
   view. Every segment except the last is a link. */
.crumbs {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 8px 14px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: var(--sh);
  font-size: 12.2px;
  color: var(--ink-3);
  overflow-x: auto;      /* a deep trail scrolls itself, never the page */
  white-space: nowrap;
}

.crumbs a { color: var(--ink-3); }
.crumbs a:hover { color: var(--brand); text-decoration: none; }
.crumbs .crumb-sep { color: var(--ink-4); }
.crumbs .crumb-current { color: var(--ink); font-weight: 600; }
.crumbs .crumb-home { color: var(--brand); display: inline-flex; }

/* Welcome bar (§0.3) — "The welcome message lives in the header... It replaces
   the repeated page title. The greeting is decoration; the second line is the
   part that earns the space." */
.welcome {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  padding: 10px 14px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: var(--sh);
}

.welcome-text { flex: 1; min-width: 180px; }

.welcome-greeting {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 600;
  color: var(--ink);
}

.welcome-greeting b { color: var(--brand); font-weight: 600; }

/* The workload line. This is the part that earns its place, so it is the one
   that must never be empty — a page with nothing to report says so in words. */
.welcome-workload { font-size: 11.7px; color: var(--ink-3); margin-top: 1px; }

.sync-pill {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 6px 13px;
  border-radius: var(--r-pill);
  border: 1px solid var(--ok-line);
  background: var(--ok-bg);
  color: var(--ok);
  font-size: 11.6px;
  font-weight: 600;
  white-space: nowrap;
}

.sync-pill .sync-ring {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 0 3px rgba(46, 139, 88, .2);
}

/* Build step 16 phase A. The pill was an `<a>` for a reader who may open the
   backup screen and a `<span>` for everyone else, so it must not take the
   link colour or the underline the shell gives every other anchor.

   ⚠️ Kept although the shell no longer renders either element (D686 made it a
   `<button>`): the two forms are what every screenshot before session 67
   shows, and a selector costing nothing is cheaper than re-deriving why it
   was there. */
a.sync-pill { text-decoration: none; }
a.sync-pill:hover { border-color: var(--ok); }

/* ⚠️ D686 — the same class on a BUTTON now, which is §9 trap 13's family read
   once more: a shared primitive meeting a new element type. A button does not
   inherit the page's font (the browser substitutes its own), and it arrives
   with a default cursor, so `.sync-pill`'s own 11.6px/600 would have rendered
   in the UA's font at the UA's size. Only the properties the new use needs are
   touched — never the pill's padding, colour or border. */
button.sync-pill {
  font-family: inherit;
  line-height: inherit;
  cursor: pointer;
}

button.sync-pill:hover { border-color: currentColor; }

/* No backup, or one older than a week (`backup.indicator.STALE_AFTER_DAYS`).
   The tone is decided in Python, beside the threshold it uses, and arrives
   here as a class — never re-derived in the template. */
.sync-pill-warn {
  border-color: var(--warn-line, #d8b45a);
  background: var(--warn-bg, #fdf6e3);
  color: var(--warn, #8a6d1f);
}

.sync-pill-warn .sync-ring {
  box-shadow: 0 0 0 3px rgba(138, 109, 31, .2);
}

/* ── Body: sidebar + workspace ──────────────────────────────────────────────*/

.body {
  flex: 1;
  min-height: 0;   /* without this the grid children refuse to shrink and the
                      workspace's own scrollbar never appears */
  display: grid;
  grid-template-columns: 216px 1fr;
  gap: var(--gap);
  padding: var(--gap) var(--gutter);
}

/* ── §2.3's full view ───────────────────────────────────────────────────────
   « i want the zoom button to expand the work space to all the available
   screen. we just keep the patient profile tab at the top. the alice logo tab,
   the vertical left tabs, and the welcome message tab and the site tab
   disappear. » — the user, choosing proposal A of `docs/mockups/fullview.html`
   (D755).

   ⚠️ **The fil d'Ariane STAYS, and that is the whole of proposal A.** The
   sidebar is the only route to another module, so a full view that took the
   breadcrumb with it would leave the ⤢ as the single way out — an escape only
   through the door you came in by. 48px of height buys an exit the reader can
   see.

   ⚠️ **Hiding the four regions is NOT enough, and this was measured rather
   than assumed.** `.topstrip` and `.body` are grids with FIXED side columns:
   with the children display:none and the templates untouched the workspace
   came out **216px wide** — the sidebar's track, still there and now empty.
   Both templates have to collapse to a single column.

   ⚠️ **NOTHING BELONGING TO THE RECORD IS LISTED, and that is now the whole
   rule rather than an exception to it.** `.pt-identity` is rendered in
   `{% block workspace_pinned %}`, inside the workspace, so it survives
   without being named — « we just keep the patient profile tab at the top ».
   `.pt-tabs` survives too: `.pt-shell.is-zoomed` used to fold it and is gone,
   because the user used the result — « we no longer sees the antecedents …
   you have to toggle back to see what you need ». **What this class takes is
   the CHROME. What the reader came for stays.**

   ⚠️ **`.shell-foot` stays.** D1 puts the full expansion of ALICE·OIS on every
   screen, and a view toggle is not an exemption from a naming rule.

   ⚠️ **The class is on `<html>` and NOT on `.shell`, since D758.** The state
   survives a page load now — every working-panel tab is a link, so it had to
   — and the restore runs inline in `<head>` to beat the first paint. `<html>`
   is the only element that exists that early; `<body>` has not been parsed.
   `alice.js` toggles the same element, so there is still one class and one
   place it lives. */
html.is-fullview .brand-card,
html.is-fullview .welcome,
html.is-fullview .site-card,
html.is-fullview .sidebar { display: none; }

html.is-fullview .topstrip {
  grid-template-columns: 1fr;
  padding-bottom: 0;
}

html.is-fullview .body { grid-template-columns: 1fr; }

/*
   The view control, in the breadcrumb row — the one strip that survives the
   class it sets (`core/_zoom_button.html` carries the argument).

   ⚠️ **`margin-left: auto` on the BUTTON inside a flex row**, which is the
   same arrangement it had at the end of `.pt-work-head`: no caller has to
   arrange it, and the breadcrumb keeps the width it always had because the
   button is the only other child.

   ⚠️ **`align-items: center` on the row and not on the nav.** The crumbs are
   a `<nav>` whose own line-height sets the row's height; centring the button
   against that is what stops it dragging the topstrip taller — a control
   that grows the chrome it exists to remove would be its own argument
   against itself.
*/
.crumb-row {
  display: flex;
  align-items: center;
  /* ⚠️ `var(--gap-s)` and NOT an invented `--gap-2`, which is what the first
     version wrote: §9 trap 49 — an unknown custom property is dropped in the
     same silence as an invalid length, so the row would simply have had no
     gap and nothing would have said so. `sync.test_transport`'s
     `CssVariablesExistTests` caught it, which is the guard doing exactly its
     job on a token invented sixty seconds earlier. */
  gap: var(--gap-s);
}

.crumb-row .crumbs { flex: 1 1 auto; min-width: 0; }
.crumb-row .pt-zoom { margin-left: auto; flex: 0 0 auto; }

.sidebar {
  height: 100%;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  padding: 10px 8px 8px;
  border-radius: var(--r);
  box-shadow: var(--sh);
  /* Its own colour family, per §0.3 — deliberately not a darker --brand. */
  background: linear-gradient(168deg, var(--side) 0%, var(--side-dk) 78%);
}

.nav-item {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 3px;
  padding: 11px 12px;
  border-radius: 9px;
  border-left: 3px solid transparent;
  color: var(--side-txt);
  font-size: 13.2px;
  font-weight: 500;
  transition: background .12s, color .12s;
}

.nav-item:hover { background: var(--side-hover); color: #EDF5F3; text-decoration: none; }

.nav-item.is-active {
  background: var(--side-active);
  border-left-color: var(--accent);
  color: #fff;
  font-weight: 600;
}

.nav-icon {
  flex: none;
  width: 18px; height: 18px;
  display: flex; align-items: center; justify-content: center;
  opacity: .95;
}

.nav-dot { flex: none; width: 6px; height: 6px; border-radius: 50%; margin-left: auto; }
.nav-item.is-active .nav-dot {
  background: var(--accent);
  box-shadow: 0 0 0 3px rgba(199, 125, 46, .28);
}

/* The account block is pinned to the bottom of the sidebar. */
.sidebar-foot {
  margin-top: auto;
  padding-top: 11px;
  border-top: 1px solid rgba(255, 255, 255, .11);
}

/* One row: avatar · name over role · language button. The language switch is a
   separate <form> (a state change needs POST + CSRF, and a <form> cannot sit
   inside an <a>), so the row is a wrapper around the two rather than being the
   link itself. */
.user-row {
  display: flex;
  align-items: center;
  gap: 4px;
}

.user-card {
  flex: 1;                 /* takes the space; the button keeps its own width */
  min-width: 0;            /* lets .user-name's ellipsis actually engage */
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 8px;
  border-radius: 9px;
  color: inherit;
}

.user-card:hover { background: var(--side-hover); text-decoration: none; }

.user-avatar {
  flex: none;
  width: 36px; height: 36px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--brand-lt);
  color: #fff;
  font-size: 12.5px;
  font-weight: 700;
}

.user-meta { flex: 1; min-width: 0; }

/* Name over role, not name then role. These are <span>s so the whole card can be
   one <a>, and an inline span lays the two out on the same line — the same
   defect the brand and site cards had. Anywhere a <span> has to stack, say so. */
.user-name,
.user-role { display: block; }

.user-name {
  font-size: 12.6px;
  font-weight: 700;
  color: #fff;
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Truncated on one line, like the name above it. Role labels vary a lot in
   length — "Interne" against "Médecin généraliste" — and letting the longer ones
   wrap would make the account block a different height for different users, so
   the sidebar's bottom edge would move as you change who is signed in. The full
   label is in the link's title, and the account page shows every role in full. */
.user-role {
  font-size: 10.8px;
  color: var(--side-txt);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.lang-button {
  flex: none;
  padding: 5px 10px;
  border: 1px solid rgba(255, 255, 255, .24);
  border-radius: 7px;
  background: transparent;
  color: var(--side-txt);
  font-size: 11.2px;
  font-weight: 600;
  cursor: pointer;
}

.lang-button:hover { background: rgba(255, 255, 255, .09); color: #fff; }

/* The only scrolling region on an authenticated page. */
.workspace {
  height: 100%;
  min-height: 0;
  min-width: 0;
  overflow-y: auto;
  padding-right: 5px;
}

/* ⚠️ A screen that fits itself, and scrolls INSIDE its own cards (D695).

   The user, on Accueil: « I WANT THE WHOLE WELCOME WORKSPACE TO BE FIX. NO
   SCROLL. FOR THE TABS WITH TOO MUCH INFORMATION IT SHOULD BE SCROLLABLE INSIDE
   OF THE TAB NOT ALL THE WORKSPACE (FOR EXAMPLE WE SCROLL THOUGH THE OPEN
   DECISIONS WITHOUT MOVING THE WHOLE WORKSPACE) » — and the reason is on the
   same screenshot: « AT THE BOTTOM HERE WE SHOULD HAVE THE RAPID ACCESSES BUT
   WE DON'T EVEN SEES THEM ». A worklist page whose own footer is below the fold
   has stopped being a worklist.

   ⚠️ Opt-in, and only Accueil opts in. Every other screen keeps the scrolling
   workspace, because a form or a document genuinely is taller than the viewport
   and must not have its fields hidden in an inner scroller.

   ⚠️ **`overflow-y: hidden` WAS here and is gone (D743).** Hiding the overflow
   is how « no scroll » was said, and it is what made the overflow UNREACHABLE
   the two times this screen was reported — the page cannot scroll either, so
   whatever did not fit was simply lost. « No scroll » is a statement about the
   layout FITTING, not about forbidding the scrollbar, and the floor below says
   it that way: fill the workspace when there is room, and let it scroll when
   the cards have shrunk as far as they may. */

/* ⚠️ **The floor is a CARD MINIMUM, not a viewport breakpoint (D743).**

   A fixed layout needs a floor: below some height the two card rows divide
   into a heading and half a row, which is worse than scrolling. Session 67
   wrote that floor as `@media (max-height: 760px)` — and the number had to be
   calibrated against one page, which is the problem: Accueil's height is set
   by how many ZONES are installed, and `ALICE_TIER` decides that.

   ⚠️ **And the media query never fired anyway, for sixteen days.** It was a
   second `.workspace.is-fixed` rule inside the query in `components.css` —
   identical specificity, **a media query adds no specificity**, and
   `components.css` is linked BEFORE this file, so the `hidden` above won on
   source order. What that produced is the user's own complaint, twice: « AT
   THE BOTTOM HERE WE SHOULD HAVE THE RAPID ACCESSES BUT WE DON'T EVEN SEES
   THEM ». The second time was on a general service, where the stale copy of
   this file had not even had the move applied.

   **So the floor says what it means.** The workspace is always ALLOWED to
   scroll; `.home` fills it whenever there is room; and a card stops shrinking
   at `--home-card-floor` (declared in `base.css`). When the rows hit it the
   content outgrows the box
   and the workspace scrolls — automatically, at whatever height, for however
   many zones. One number, and it is the number the rule is actually about.

   ⚠️ **Measured on the tier-1 page, not reasoned** (viewport → card height,
   fixed layout): 800px fits · 738px → 151px · 660px → 112px · 600px → 82px.
   120px is a heading and one full row, which is the least that still answers
   « what needs me today ». ⚠️ **The tier-3 page could not be measured** — no
   oncology or chemotherapy is installed here — so this wants a look on a full
   installation when tier 2 lands. §0.9 forbids SIDEWAYS scroll, never
   vertical. */
.workspace.is-fixed { overflow-y: auto; }
.workspace.is-fixed > .home { min-height: 100%; }

/* ⚠️ **The floor goes on the ROW, not on the card**, and putting it on the
   card first is the mistake worth recording: `.home .card` already carries
   `min-height: 0` so it can be capped by its row, and a floor there makes the
   card overflow a row that still shrinks to nothing. `.home` then never grows,
   the workspace never scrolls, and the next sibling draws ON TOP of the
   overflowing card — at 600px the « Accès rapides » heading sat across
   « Décisions ouvertes ». Measured in a browser, which is the only place that
   overlap is visible at all.

   On the row it does what it says: `flex: 1 1 0` divides whatever room there
   is, the row stops dividing at the floor, `.home` outgrows the workspace, and
   the workspace scrolls. The card keeps `height: 100%` and fills its row
   exactly, whichever side of the floor it is on. */
/* ⚠️ **`.worklists` left this selector** (D749's correction). The zone-level
   floor is right for `.dgrid`, which holds TWO cards at a 320px minimum and
   therefore cannot wrap — one grid row, so a floor on the zone IS a floor on
   the card. `.worklists` holds THREE: at two columns a third card wraps
   whatever the width, and a floor on the zone was then divided between two
   grid rows. 150px became 70px per card, which is less than the card's own
   chrome — zero pixels of list, and the user reporting that three cards « are
   not scrollable » when the scroller was simply 0px tall.

   Its floor is on the GRID ROW now (`components.css`), with
   `min-height: auto` on the zone so it grows to however many rows `auto-fit`
   produced. Same token, same meaning, applied where the number is actually
   about one card. */
/* ⚠️ **GONE since session 84** (D793). The zone floor was right while
   `.dgrid` held ONE row of two cards — « one grid row, so a floor on the zone
   IS a floor on the card ». The user's drawing stacks the ward list under the
   donut, so it now has a row per left-column card and a single number on the
   zone would be divided between them: D749's own finding, arriving at the
   other zone. `components.css` carries the floor on `grid-auto-rows` there,
   with `min-height: auto` letting the zone grow to however many rows it got —
   which is exactly the pair `.worklists` used to carry. */

.workspace::-webkit-scrollbar { width: 9px; }
.workspace::-webkit-scrollbar-thumb { background: #CFD8D4; border-radius: 9px; }
.workspace::-webkit-scrollbar-track { background: transparent; }

/* ── Footer (§0.1) ──────────────────────────────────────────────────────────
   The full expansion, everywhere. It is the one place a new user learns what
   the acronym means (CLAUDE.md D1). */

/* Tightened after the review: « THIS SPACE IS TO MUCH FOR THE FOOTER ». It is
   a one-line legal notice and was taking the vertical room of a form field. */
.shell-foot {
  flex: none;
  padding: 4px var(--gutter) 6px;
  text-align: center;
  font-size: 10.8px;
  color: var(--ink-4);
}

/* ══════════════════════════════════════════════════════════════════════════════
   Unauthenticated pages — login, password recovery, session conflict
   ══════════════════════════════════════════════════════════════════════════════ */

.auth-page {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--gutter);
  padding: var(--gutter);
}

.auth-card { width: 100%; max-width: 400px; padding: 24px; }
.auth-brand { display: flex; align-items: center; gap: 11px; margin-bottom: 18px; }
.auth-foot { font-size: 10.8px; color: var(--ink-4); text-align: center; }

/* ══════════════════════════════════════════════════════════════════════════════
   Responsive (§0.9)
   ══════════════════════════════════════════════════════════════════════════════

   "Desktop: primary target. Tablet: fully supported — this is the real
   bedside/clinic use case, design for it deliberately. Phone: out of scope —
   drop the mobile breakpoint and the code that serves it."

   So there is exactly one breakpoint, and it is the tablet one. There is
   deliberately no phone breakpoint below it. */

@media (max-width: 1080px) {
  /* On a tablet the sidebar narrows to icons; the labels would otherwise eat a
     quarter of a screen that the workspace needs. The label stays in the DOM as
     the accessible name, so the nav is still usable by screen reader and the
     title attribute gives a sighted user a tooltip. */
  .topstrip { grid-template-columns: 64px 1fr 200px; }
  .body { grid-template-columns: 64px 1fr; }

  .brand-name, .brand-sub { display: none; }
  .brand-card { justify-content: center; padding: 11px 8px; }

  .nav-item { justify-content: center; padding: 12px 8px; gap: 0; }
  .nav-item .nav-label, .nav-item .nav-dot { display: none; }

  /* Only the avatar survives at this width; the name, role and language switch
     move to the account page, which the avatar still links to.
     ⚠ That last clause was **false when it was written** — the account page had
     no language switch at all, so on the tablet §0.9 designs for there was no
     way to change language. Found by the user. `accounts/account.html` now
     carries one, and this comment is true; corrected forward rather than
     deleted (D82's reading of « immutable »). */
  .user-card { justify-content: center; padding: 9px 4px; }
  .user-meta, .lang-form { display: none; }

  /* A two-column form is unreadable at this width — one column, full height. */
  .form-grid, .form-grid-3 { grid-template-columns: 1fr; }
}

/* The account page's language switch — the one that survives the tablet
   breakpoint above, where `.lang-form` in the sidebar does not. A sentence
   saying which language is current beside the control that changes it, because
   a lone « Passer en anglais » does not tell a reader what they are on. */
.account-lang {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gutter);
  flex-wrap: wrap;
}

.account-lang .hint { margin: 0; }

/* The institution's own logo in the site card, where one is uploaded.
   Bounded to the glyph's own box so a wide logo cannot stretch the header —
   the image is whatever the department uploaded, at whatever aspect ratio. */
.site-logo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}
