/**
 * Quantum look-and-feel over Elastic.
 *
 * Hand-written and loaded AFTER the two generated sheets, so `make` never
 * touches it. styles.css and themes.css carry the palette; this file carries
 * the SHAPE -- the part that made the skin still read as Elastic even once the
 * colours were right.
 *
 * Four things define Q-Mail's surface, and each maps onto Elastic without
 * touching a single template:
 *
 *   1. Columns are PANEL CARDS: var(--panel) at 10px radius, floating on a
 *      var(--gap) ground with a 7px channel between them. Elastic butts its
 *      columns edge to edge and separates them with 1px borders.
 *   2. Navigation runs ALONG THE TOP, not down the left edge.
 *   3. Headers are tall and quiet: 18px of air, a 19px/700 title.
 *   4. Controls are pills -- 8px for buttons, 999px for toggles.
 *
 * Everything here is layout and typography. No colour is invented: every value
 * is a token that themes.css already swaps per theme, so all twelve themes
 * keep working unchanged.
 */

/* ----------------------------------------------------------- 0. typography */
/* THE SEMIBOLD THAT WAS NOT THERE. Elastic sets the whole interface in Roboto
   and ships exactly four faces: 400 and 700, upright and italic. Every
   `font-weight: 600` below therefore had nothing to match and resolved UP to
   Bold -- so the labels, the selected folder and the selected subject were all
   drawn in the 700 face at 13px, where its stems are two pixels wide and land
   badly on the grid. That, and not the size, is why bold text read as rough
   beside the regular text around it.
   Roboto's fifth weight is MEDIUM 500; there is no 600 in the family at all,
   which is why no amount of asking for one produced one. The face below is the
   one this design was always describing, and every weight in this file is now
   stated as 500. Same Roboto 2.137 as Elastic's own files -- identical metrics
   (2048upem, 1900/-500, zero line gap) and identical character coverage, all
   896 codepoints -- so nothing reflows and no glyph falls back.
   It ships in this skin's own fonts/ rather than being borrowed from
   ../../elastic/fonts/ like the other four, because Elastic does not have it.
   Apache 2.0; see NOTICE and fonts/LICENSE-Roboto.txt.
   `local()` first, exactly as Elastic does for 400 and 700: an OS that has a
   real Roboto Medium (every Android does) uses its own hinted copy and
   downloads nothing. */
@font-face {
	font-family: 'Roboto';
	font-style: normal;
	font-weight: 500;
	src: local('Roboto Medium'), local('Roboto-Medium'),
	     url('../fonts/roboto-medium.woff2') format('woff2'),
	     url('../fonts/roboto-medium.woff') format('woff');
}

/* GRAYSCALE ANTIALIASING, the second half of the same complaint. Nothing in
   Elastic sets this for body text -- it sets it only inside its icon-font
   mixin -- so the interface rendered with subpixel antialiasing, which is what
   thickens small text on Apple hardware and is the other reason this skin did
   not look like the app it is modelled on. Native apps get grayscale by
   default; this asks for the same. Ignored on Windows and Android, where the
   platform decides, so it can only help. */
html,
body {
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

/* ---------------------------------------------------------------- 1. cards */

#layout {
	background: var(--gap, #e9e9ee);
	gap: 7px;
	padding: 7px;
	/* Elastic sets height:100%; the padding has to come out of that or the
	   last column is pushed 14px past the fold. */
	box-sizing: border-box;
}

#layout > div#layout-sidebar,
#layout > div#layout-list,
#layout > div#layout-content {
	background: var(--panel, #fff);
	border-radius: 10px;
	/* The radius only reads as a card if the children are clipped by it --
	   Elastic's headers and lists both paint to their own edges. */
	overflow: hidden;
	border: 0;
}

/* The 1px rules between columns are what the 7px channel replaces. Left in,
   they draw a seam right next to the gap. */
#layout-sidebar,
#layout-list,
#layout-content {
	border-left: 0 !important;
	border-right: 0 !important;
}

/* ----------------------------------------------------------- 2. top nav */
/* EVERY WIDTH. Elastic turns the task menu into a slide-in drawer below its
   touch breakpoint, which on a phone put the app's identity behind a hamburger
   and moved the whole bar off to the left. Quantum's bar is a bar at every
   size; §2c below is only what has to shrink. `@media screen` is kept because
   print.css wants none of this. */
@media screen {
	#layout-menu {
		position: fixed;
		top: 0;
		left: 0;
		right: 0;
		width: auto;
		/* .mf-topbar: 60px, sharing the page ground so there is no seam
		   between bar and columns. Not a white panel with a rule under it. */
		height: 60px;
		z-index: 20;
		background: var(--gap, #e9e9ee);
		border-bottom: 0;
		/* ⚠ display:flex on the BAR, not just on #taskmenu. #layout-menu holds
		   two children -- .popover-header (the logo) and #taskmenu -- and as a
		   block they stack: the logo took the 56px band and pushed the whole
		   task row down onto the column headers, where "Schreiben" sat on top
		   of the sidebar's title. They have to sit side by side. */
		display: flex;
		flex-direction: row;
		align-items: center;
		flex-wrap: nowrap;
		overflow: visible;
		/* .mf-topbar's own padding. It used to come from the logo block. */
		padding: 0 16px;
		box-sizing: border-box;
		/* The wordmark and About/Logout are both positioned against this. */
		position: fixed;
	}

	#layout-menu,
	#layout-menu #taskmenu,
	#layout-menu #taskmenu .action-buttons,
	#layout-menu #taskmenu .special-buttons {
		background-color: var(--gap, #e9e9ee);
	}

	/* ⚠ display:flex, explicitly. Elastic leaves #taskmenu as a block and lets
	   its children stack as block-level anchors, so flex-direction and the
	   margin-left:auto that pushes About/Logout right were both no-ops --
	   the pair rendered underneath the bar instead of inside it. */
	#layout-menu #taskmenu {
		display: flex;
		flex-direction: row;
		align-items: center;
		justify-content: center;
		height: 60px;
		flex: 1 1 auto;
		width: auto;
		min-width: 0;
		overflow: visible;
		background: transparent;
		gap: 4px;
	}

	/* Elastic stacks icon over label in a 5.6em column. Along a row there is
	   width to spare and height to save, so they sit side by side. */
	#layout-menu #taskmenu a {
		flex: none;
		display: inline-flex;
		flex-direction: row;
		align-items: center;
		gap: 7px;
		width: auto;
		/* ⚠ max-width AND overflow, on the BUTTON. Elastic caps it at the rail
		   width (~84px) and clips what does not fit -- correct for a vertical
		   rail, fatal along a row: "Einstellungen" arrives as "Einstell".
		   Undoing it on the inner label alone is not enough, which is exactly
		   how this survived the first pass -- the harness had no Bootstrap, and
		   without Bootstrap's border-box the cap happened not to bite. */
		max-width: none;
		overflow: visible;
		/* .mf-appbtn, exactly: 40px tall, 0 12px, 9px radius, --bar-ic. */
		height: 40px;
		padding: 0 12px;
		border-radius: 9px;
		font-size: 13px;
		font-weight: 500;
		color: var(--bar-ic, #444);
		background: transparent;
	}

	/* ⚠ OPTICAL SIZING, not nominal. One font-size does NOT give one apparent
	   size: at 20px these five FontAwesome glyphs render wildly different ink
	   heights, because each fills a different share of its em box --
	   measured envelope 15.0px, people 17.5, gear 19.5, question 20.0,
	   power 19.8. Set to a single number, About and Logout read a third larger
	   than E-Mail, which is exactly how it looked.
	   The per-glyph sizes below even the INK out at ~17.5px. Q-Mail has no
	   such problem: its icons are SVGs drawn to one optical weight on a 24px
	   grid, so nominal and apparent size are the same thing there.
	   tests/skin.py measures the rendered pixels and fails if these drift. */
	#layout-menu #taskmenu a.mail:before {
		font-size: 23px;
	}

	#layout-menu #taskmenu a.contacts:before {
		font-size: 20px;
	}

	#layout-menu #taskmenu a.settings:before {
		font-size: 18px;
	}

	#layout-menu #taskmenu a.about:before,
	#layout-menu #taskmenu a.logout:before {
		font-size: 17.5px;
	}

	/* The shared box; the sizes above override only the glyph.
	   ⚠ 17.5px, not 20. The five named glyphs above are each sized to land on
	   ~17.4px of INK; this is the value everything else gets, and everything
	   else means a PLUGIN's task icon -- menu.html's `taskbar` container takes
	   any number of them and this file cannot know which glyph arrives. At 20
	   a full-box glyph (the calendar, measured 20.0px of ink) came out a third
	   larger than E-Mail beside it. At 17.5 an unknown glyph can equal the row
	   but never tower over it. */
	#layout-menu #taskmenu a:before {
		margin: 0;
		font-size: 17.5px;
		line-height: 1;
		width: auto;
		height: auto !important;
		color: inherit;
	}

	/* Elastic clips the label to the rail's fixed width and hides the overflow
	   ("Einstellungen" arrives as "Einstellunge"). Along a row the button sizes
	   to its text, so the clipping has to be undone as well as the width. */
	#layout-menu #taskmenu a .inner {
		display: inline;
		font-size: 13px;
		/* ⚠ Elastic's `.menu span.inner { font-weight: normal }` beats the 600
		   set on the anchor, so the weight has to be restated on the span. */
		font-weight: 500;
		line-height: 1;
		text-transform: none;
		letter-spacing: 0;
		overflow: visible;
		text-overflow: clip;
		white-space: nowrap;
		max-width: none;
		width: auto;
	}

	#layout-menu #taskmenu a:hover {
		background: var(--bar-ic-hover, #eef0f4);
	}

	/* .mf-appbtn.active -- the same hover fill, with the text going strong.
	   Not an accent-tinted pill: the app marks the current page by weight of
	   colour, not by hue. */
	#layout-menu #taskmenu a.selected,
	#layout-menu #taskmenu a.selected:hover {
		background: var(--bar-ic-hover, #eef0f4);
		color: var(--text-strong, #111);
	}

	/* ⚠ Compose is NOT styled here any more. On desktop it is either in the
	   folder column's head (§8c dresses it there) or still in the bar and
	   hidden -- the two states §8c defines. Styling it for a third state it
	   never reaches was ~25 lines that could not be seen to be wrong. */

	/* Elastic pins this to the bottom-left corner of the vertical rail and
	   gives it the rail's own dark fill. Along a row that lands it on top of
	   the compose button as a dark block -- the first thing that goes wrong
	   when the bar is turned. */
	#layout-menu #taskmenu .special-buttons {
		position: static;
		background: transparent;
		margin-left: auto;
		display: flex;
		align-items: center;
	}

	/* The rail's buttons are white-on-dark by default; on a light bar they
	   have to follow the theme's text colour instead. `a.logout` carries
	   !important in Elastic, so matching it needs the same weight. */
	#layout-menu #taskmenu a.logout {
		color: var(--pill-bad, #b23320) !important;
	}

	/* The bar is out of the flow, so the columns need the height back.
	   ⚠ Exactly the bar's height, with no channel added on top of it. The bar
	   is 60px holding a 40px button, so there is already 10px of ground under
	   that button; adding the 7px channel as well made the gap below the
	   buttons 17px against 10px above them, and the bar read as if it were
	   sitting too low. The ground the bar paints IS the channel. */
	#layout {
		padding-top: 60px;
	}

	/* Elastic hides the logo header inside the menu; along the top it is the
	   one thing that anchors the bar to the product. §8e places and paints it;
	   this is only its box inside the bar. */
	#layout-menu .popover-header {
		display: flex !important;
		align-items: center;
		flex: 0 0 auto;
		height: 60px;
		border: 0;
		/* ⚠ !important is required: Elastic sets
		   `background-color: @color-taskmenu-background !important` here, which
		   this skin maps to --panel. While the logo block was ~90px wide that
		   white slab hid behind the logo; once it grew to take the bar's left
		   zone it painted a white third of the bar. */
		background: transparent !important;
		padding-left: 10px;
	}

	/* ⚠ THE `<img>` AND THE `<a>` ARE GONE FROM HERE ON PURPOSE. §8e hides both
	   with `display: none !important` -- the wordmark is painted as the
	   header's background image instead, from a per-theme file. Sizing a
	   display:none image is work nobody sees, and a max-height in this block
	   read as if the logo were still an element. */
}

/* ------------------------------------------------- 2b. the escape hatch */
/* ⚠ DO NOT WEAKEN. If the bar is broken, Settings is the only way back to
   the skin selector -- lose it and the only remaining fix is editing
   config.inc.php on the server. Every rule above is layout; these are the
   invariants that keep the way out reachable no matter what layout does.
   (Belt and braces: /?_task=settings&_action=preferences&_section=general
   reaches the same page with no chrome at all.) */
#taskmenu a.settings,
#taskmenu a.logout,
#taskmenu a.mail {
	visibility: visible !important;
	opacity: 1 !important;
	pointer-events: auto !important;
	clip-path: none !important;
}

/* The bar is a flex row at every width now (§2), so the escape hatch has to
   hold it open at every width too -- this used to stop at 1025px because
   below that the menu was Elastic's drawer. */
@media screen {
	#layout-menu,
	#layout-menu #taskmenu,
	#layout-menu #taskmenu a.settings {
		display: flex !important;
	}

	#layout-menu #taskmenu a.settings {
		display: inline-flex !important;
	}
}

/* -------------------------------------------------------------- 3. headers */

#layout > div > .header {
	height: auto;
	min-height: 56px;
	line-height: normal;
	display: flex;
	align-items: center;
	/* Elastic already sets this (it anchors the search bar); stated here
	   because the overflow panel in §8d hangs off it too.
	   ⚠ And `overflow: visible` with it -- Elastic clips the head, which would
	   cut the panel off at the header's own bottom edge. The toolbar inside
	   keeps its own clipping; that is what the overflow is measured against. */
	position: relative;
	overflow: visible;
	/* Elastic centres the column head's contents. The app aligns its controls
	   to the content they act on -- the column's left edge. */
	justify-content: flex-start;
	/* ⚠ No rule under the head, and no min-height beyond what the 40px buttons
	   need. Elastic draws a 1px border here and pads to 4.2rem, which put a
	   visible band between the toolbar and the content it acts on -- the
	   message list started ~20px lower than it had to. The panel card's own
	   edge already separates the head from what is outside it. */
	padding: 8px 12px;
	border-bottom: 0;
	background: var(--panel, #fff);
	font-weight: normal;
}

/* ⚠ Elastic centres this with `margin: 0 -280px` and text-align:center, so it
   can sit optically centred between the two flanking buttons. Left as is under
   a left-aligned Quantum header it starts 280px off the left edge of the
   column -- measured at x=-249 before this rule existed. */
#layout > div > .header .header-title,
#layout > div > .header .username {
	/* ⚠ `!important`, against an INLINE style. `screen_resize_headers()` in
	   ui.js measures the visible header children on either side of the title
	   and writes `margin-left` / `margin-right` straight onto the element to
	   centre it optically. Kontakte has a ⋯ after its title and Einstellungen
	   has nothing, so ui.js pushed "Gruppen" 40px right and left
	   "Einstellungen" flush -- the same stylesheet, two different positions,
	   and no CSS rule could win because the margin was inline. */
	margin: 0 !important;
	text-align: left;
	flex: 1 1 auto;
	min-width: 0;
	/* 15px/700: a step above the 13px action labels beside it, because it names
	   the column rather than acting on it -- but well short of Elastic's
	   19px/700 page title, which had "Gruppen" and "Einstellungen" shouting
	   over the controls in the same row.
	   ⚠ NO left padding. "Gruppen" and "Einstellungen" are column names, and a
	   name that does not line up with the column's own content edge reads as a
	   stray indent -- which is exactly how the pair looked side by side. Zero
	   here puts both on the header's content box, the same x the ⋯ button on
	   the right is measured from, and makes them agree by construction rather
	   than by whichever hidden buttons happen to precede each one. */
	font-size: 15px;
	font-weight: 700;
	/* ⚠ Aligned with the ROW LABELS below it, not with the column edge and not
	   with the row icons. Measured from the column's own left edge:
	       6px  row anchor margin (§5)
	     + 10px row anchor padding-left (§5)
	     + 29px the .listing.iconized icon box and its gap
	     = 45px, less the header's own 12px padding = 33px.
	   A name that starts anywhere else reads as a stray indent against the
	   list it names -- which is exactly how "Gruppen" and "Einstellungen"
	   looked side by side. */
	padding-left: 33px;
	color: var(--text, #222);
	line-height: 22px;
	/* A long address is the common case in the sidebar head. */
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

#layout > div > .footer {
	border-top: 1px solid var(--line-2, #eee);
	background: var(--panel, #fff);
	font-weight: normal;
	font-size: 12.5px;
	color: var(--muted, #666);
}

/* ------------------------------------------------------------- 4. controls */

/* ⚠ Header-button geometry lives in §8b, and ONLY there. An earlier pass set
   `height: 34px` here on `#layout .header .toolbar a.button` -- three classes,
   so it quietly out-ranked §8b's 40px box for exactly the buttons that carry a
   `button` class. Which is Kontakte's "Auswahl" and nothing else in mail, so
   one column in one task was 6px shorter than every other. Colour only here. */
#layout .header .toolbar a,
#layout .header a.button {
	color: var(--text, #222);
}

/* --------------------------------------------------------------- 5. lists */

/* ⚠ `width: auto`. Elastic gives the row anchor `width: 100%`, which is the
   LI's full width -- the 6px margins then push it 6px past the column's right
   edge, where the scroller clips it. The left corners were rounded and inset
   and the right ones were off-screen. Letting the block size itself puts the
   same 6px and the same radius on both ends. */
#layout-sidebar .listing li > a,
#layout-sidebar .listing li > div > a {
	font-size: 13.5px;
	border-radius: 9px;
	margin: 1px 6px;
	padding-left: 10px;
	width: auto;
}

/* ⚠ NO `color` HERE. The same selector at §8i sets `--text-strong` with
   `!important` and wins, which is the only reason this never shipped: an
   accent on `--sel` measures 4.12:1, and the ⚠ beside that rule says so. A
   reader who trusted this block would have carried the contrast bug forward. */
#layout-sidebar .listing li.selected > a {
	background: var(--sel, #eceaf7);
}

.messagelist td.subject .fromto,
.messagelist td.subject .date {
	font-size: 12.5px;
	color: var(--muted, #666);
}

/* ⚠ THE DATE NEVER SHRINKS. `td.subject` is a flex row and Elastic lets both
   children flex, so the sender -- which can be any length -- took the space and
   left the date two pixels short of its own text: every row read "2026-0…".
   Worse below 1024, where `html.touch` turns the whole <tr> into a flex row and
   pulls `td.flags` out of the flow on top of it. The date is a fixed string;
   it is the sender that has room to give. */
.messagelist td.subject .date {
	flex: 0 0 auto;
	white-space: nowrap;
	padding-left: 10px;
}

.messagelist td.subject .fromto {
	flex: 1 1 auto;
	min-width: 0;
}

/* `td.flags` is absolutely positioned at the right edge below 1024px (Elastic,
   html.touch) with its flag hidden -- an invisible 35px box sitting over the
   end of the row. The cell has to reserve it, or the date runs underneath. */
html.touch .messagelist td.subject,
html.layout-phone .messagelist td.subject {
	padding-right: 38px;
}

/* ⚠ The toast stack is pinned to the bottom right of the window, which below
   the desktop is where the tab bar is -- and at phone size Elastic stretches it
   edge to edge, straight over it. Lifted clear.
   ⚠ THE SAME QUERY AS §2c, both halves of it. This is the bar's height read in
   a second place; if the tab bar shows and this does not, the toast sits on top
   of it. */
@media screen and (max-width: 1024px),
       screen and (max-width: 1279px) and (pointer: coarse) {
	#messagestack {
		/* ⚠ In step with the tab bar's own height in §2c. */
		bottom: calc(52px + .5em);
	}
}

.messagelist td.subject span.subject {
	font-size: 13.5px;
}

/* Rows: a FULL-BLEED band, edge to edge. The rounded-block treatment the
   folder rail uses is right for a rail of short pills and wrong for a dense
   list -- a radius at the right edge of every selected message reads as a
   card that has come loose from the column.
   ⚠ `width: 100%` on the table, or the band stops wherever the columns happen
   to end: Elastic lets .messagelist size to its content, so the fill ended
   72px short of the column's right edge and "the whole row" was never marked. */
.messagelist {
	width: 100%;
}

.messagelist tr.message > td {
	border-bottom: 1px solid var(--line-2, #eee);
	padding-top: 7px;
	padding-bottom: 7px;
	background: transparent;
}

.messagelist tr.message:last-child > td {
	border-bottom: 0;
}

.messagelist tr.message:hover > td {
	background: var(--hover, #f5f5f8);
}

.messagelist tr.message.selected > td {
	background: var(--sel, #eceaf7);
	border-bottom-color: transparent;
}

/* ⚠ The subject is a LINK, so Elastic paints it @color-link -- every row's
   subject in the theme's brand colour. In the app the subject is body text and
   only the accent-coloured things are actionable, which is what makes an
   unread row's weight readable as the signal it is. */
.messagelist td.subject span.subject a {
	color: var(--text, #222);
}

.messagelist tr.message.selected td.subject span.subject a {
	color: var(--text-strong, #111);
}

.messagelist tr.unread td.subject span.subject a {
	color: var(--text-strong, #111);
	font-weight: 700;
}

/* Elastic reserves 1.5em of left padding on the sender for a flag slot the
   app does not use, which leaves every row visibly indented from its date. */
.messagelist td.subject span.fromto {
	padding-left: 0;
}

/* -------------------------------------------------------------- 6. search */

/* ⚠ THE ONE COLOUR IN THIS SKIN THAT WAS NEVER THEMED. Elastic marks the
   focused row with
       html:not(.touch) .listing li > a:focus,
       html:not(.touch) .listing.focus tbody tr.focused > td:first-child, …
           { border-left: 2px solid #86d820 }
   and that literal appears once in styles.css and NOT ONCE in themes.css: it
   is not derived from any LESS colour variable, so the per-declaration diff
   had nothing to map and the bar stayed Roundcube's green in all twelve
   schemes -- including the three dark ones, where it is the only green pixel
   on the screen.
   ⚠ `--marker`, which is `--compose-btn` on eleven of the twelve: the compose
   button's fill is the one colour each theme uses to mean "this one", which is
   what the marker says.
   ⚠ GREEN IS THE EXCEPTION, AND IT IS THE SAME EXCEPTION AS THE WORDMARK. Its
   `--compose-btn` is #17293a navy -- the only theme whose "this one" colour is
   not its own hue -- so the marker came out as a dark bar that read as a
   border rather than a highlight. Green alone points `--marker` at #78be20,
   the house green it now shares with "Quantum" (§8e) and with the selected
   folder icon (§4a). This is a restoration: the marker WAS green here, and was
   given up when the rule was unified on `--compose-btn`. The token is what
   lets one theme differ without the other eleven being restated.
   ⚠ Why a token and not a scoped rule: `:root` IS `html`, so a green-only
   override written there would cascade into all twelve. `test_selection_marker`
   pins both halves.
   ⚠ The selector is Elastic's own, restated -- `html:not(.touch)` is (0,1,1)
   plus the rest, so anything shorter loses.
   ⚠ AND `td.selection + td` IS THE ONE THAT ACTUALLY PAINTS. Roundcube renders
   a selection column in the message and contact lists and Elastic HIDES it
   while the list is not in selection mode, which is why its third selector
   reads `.listing.focus:not(.withselection) tbody tr.focused > td.selection +
   td` -- the marker sits on the cell AFTER the hidden one, flush at the row's
   left edge, and `td:first-child` is a cell nobody can see. The first cut of
   this rule restated only the first two selectors and the harness had no
   selection cell, so the suite went green while every real install kept
   Roundcube's green bar. Measured: `td:first-child` #4338ca, `td.selection +
   td` #86d820, in the same row. */
/* ⚠ EXCEPT ON THE RAIL. `#layout-sidebar` is the folder list and the address
   books, where the selected row is already a filled panel across its whole
   width -- a second mark on its left edge read as a stray bracket, because
   Elastic's 2px border sits inside a 9px radius and a rounded left border IS a
   bracket. The colour is dropped, not the border: the 2px is what keeps every
   row's text on the same left edge, marked or not.
   ⚠ Keyboard focus is unaffected -- that is the `:focus-visible` ring in §11,
   a different mark for a different question. */
#layout-sidebar .listing li > a,
#layout-sidebar .listing li.selected > a,
#layout-sidebar .listing li > a:focus,
#layout-sidebar .treelist li > a,
#layout-sidebar .treelist li.selected > a,
#layout-sidebar .treelist li > a:focus {
	border-left-color: transparent;
}

html:not(.touch) .listing li > a:focus,
html:not(.touch) .listing.focus tbody tr.focused > td:first-child,
html:not(.touch) .listing.focus li.focused:not(.selected) > a,
html:not(.touch) .listing.focus li.focused > div > a,
html:not(.touch) .listing.focus tbody tr.focused.selected > td:first-child,
html:not(.touch) .listing li.selected > a,
html:not(.touch) .listing tbody tr.selected > td:first-child,
html:not(.touch) .listing.focus:not(.withselection) tbody tr.focused > td.selection + td,
html:not(.touch) .listing:not(.withselection) tbody tr.selected > td.selection + td {
	border-left-color: var(--marker, var(--compose-btn, var(--brand, #4f7f13)));
}

/* ⚠ THE BOX IS §8f's, not this rule's. `#layout > div > .searchbar` is (1,1,1)
   against this (1,1,0) and states padding, border and background there; the
   three declarations that used to be here were computed by nobody. What is
   left is the row itself, which §8f does not set. */
#layout-list .searchbar {
	display: flex;
	align-items: center;
	gap: 4px;
}

/* ⚠ THE ICONS NEED ROOM OF THEIR OWN. Elastic hangs the magnifier off the
   form's `:before` with `margin: 0 0 0 .75rem` and gives the two buttons on the
   right a `:before` of `width: 2rem; height: 36px` -- three glyphs with no box
   and no gap, so the pair on the right sat hard against the panel's edge and
   the magnifier crowded the pill. Each becomes a square target, the row gaps
   them, and the field gives up the width. */
#layout-list .searchbar form {
	display: flex;
	align-items: center;
	flex: 1 1 auto;
	min-width: 0;
	gap: 2px;
}

/* ⚠ NO MAGNIFIER, at either end. Elastic prints one as `form:before` and
   ships a second as `a.button.search`; the field already says "Suchen …", and
   two icons for one idea is what made this row six controls wide. Removing
   `content` rather than the element keeps Elastic's own box arithmetic intact.
   ⚠ `a.search` is Elastic's SUBMIT, and it hides it too (`.searchbar a.reset,
   .searchbar a.search { display: none }`) -- the blanket `> a` rule below is
   what brought it back, so it is put down again explicitly. Enter still
   submits the form. */
#layout-list .searchbar form:before {
	content: none;
}

#layout-list .searchbar > a.search {
	display: none !important;
}

/* ⚠ THE RESET SITS INSIDE THE FIELD, and it is a SIBLING of the form, not a
   child -- Roundcube builds the row as `form, reset, options, search` and
   ui.js splices the unread button in before options. So it is pulled back over
   the field with a negative margin rather than positioned absolutely: 34px
   wide against `-38px` (its width plus the row's 4px gap) advances the row by
   exactly zero, so the buttons after it stand precisely where they would if it
   were not there -- while the ✕ paints over the last 34px of the pill.
   ⚠ Only while there is something to reset. Elastic shows it on
   `.searchbar.active`, which `searchbar_init()` maintains; the blanket `> a`
   rule below out-ranked that and put an ✕ on an empty field. */
/* ⚠ `:not(.active)`, not a bare `a.reset`. The reset carries `button` in its
   class list, so `.searchbar > a.button` below TIES with it on specificity
   and wins on order -- an ✕ on an empty field, which is exactly the state
   Elastic hides it in. The state class puts this rule a class ahead. */
#layout-list .searchbar:not(.active) > a.reset {
	display: none;
}

#layout-list .searchbar.active > a.reset {
	display: inline-flex;
	position: relative;
	z-index: 1;
	margin-left: -38px;
}

#layout-list .searchbar > a.button,
#layout-list .searchbar > a {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: none;
	width: 34px;
	height: 34px;
	border-radius: 9px;
	color: var(--bar-ic, #444);
}

#layout-list .searchbar > a:hover {
	background: var(--bar-ic-hover, var(--hover, #eef0f4));
	color: var(--text, #222);
}

#layout-list .searchbar a:before {
	width: auto;
	height: auto;
	margin: 0;
	line-height: 1;
}

#layout-list .searchbar input {
	height: 36px;
	border-radius: 999px;
	/* The app calls it --search-bd. (The plan document writes --search-line;
	   the stylesheet is the authority.) */
	border: 1px solid var(--search-bd, var(--line, #ddd));
	background: var(--search-bg, var(--app-bg, #fafafa));
	/* ⚠ Room on the right for the ✕ that now sits inside the field -- and
	   stated as one shorthand, because a `padding-right` written before this
	   line would be reset by it. */
	padding: 0 40px 0 14px;
	font-size: 13.5px;
	color: var(--text, #222);
}

#layout-list .searchbar input::placeholder {
	color: var(--muted-2, #888);
}

/* ------------------------------------------------------- 7. reading pane */

/* ⚠ The reading pane is a FRAME. message.html renders inside
   `<html class="iframe">` with no `#layout` wrapper, so every rule below is
   written without one -- and `.frame-content`'s own 14px sits on top of
   whatever this header pads, which is how the subject came to start 32px
   below the toolbar. The frame supplies no padding of its own here; the
   header and the body each state theirs. */
body.task-mail .frame-content {
	padding: 0;
}

#message-header {
	padding: 10px 20px 12px;
	border-bottom: 1px solid var(--line-2, #eee);
	margin: 0;
}

#message-header h2.subject {
	font-size: 20px;
	font-weight: 700;
	line-height: 1.35;
	color: var(--text-strong, #111);
	margin: 0 0 12px;
}

#message-header .header {
	display: flex;
	align-items: flex-start;
	gap: 12px;
	/* Section 3 styles #layout > div > .header -- the column heads. This one is
	   nested inside the reading pane and must not inherit that 60px chrome. */
	min-height: 0;
	padding: 0;
	border: 0;
	background: transparent;
}

#message-header .contactphoto {
	width: 40px;
	height: 40px;
	flex: none;
	border-radius: 50%;
	overflow: hidden;
	background: var(--sel, #eceaf7);
}

#message-header .contactphoto img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

#message-header .header-summary {
	font-size: 13.5px;
	color: var(--text, #222);
}

#message-header .header-summary .name {
	font-weight: 500;
	color: var(--text-strong, #111);
}

#message-header .header-headers,
#message-header .header-summary .date {
	font-size: 12.5px;
	color: var(--muted, #666);
}

#message-header .header-links a {
	font-size: 12.5px;
	color: var(--muted, #666);
}

#message-header .header-links a:hover {
	color: var(--accent, #4a3fbf);
}

#message-content,
#messagebody {
	padding: 4px 20px 20px;
}

/* ⚠ `@color-attachmentlist-background` and `-border` are LITERALS in Elastic's
   colours.less (#fcfcfc / #f4f4f4) -- they derive from nothing the theme build
   maps, so the per-declaration diff finds them identical in all twelve themes
   and emits nothing. The strip stayed white on every dark theme, and the
   attachment popup opening over it looked ringed in white. */
.attachmentslist {
	background: var(--hover, #f5f5f8);
	border-color: var(--line-2, #eee);
}

.attachmentslist .attachment-size {
	color: var(--muted, #666);
}

/* ------------------------------------------------- 7b. framed documents ---
 * Roundcube renders the reading pane, the contact card and every preferences
 * section INSIDE an iframe. layout.html emits `<div id="layout">` only when
 * `!env:framed`, so in those documents there is no `#layout` at all and every
 * `#layout …` rule in this file matches nothing.
 *
 * ⚠ That is not a cosmetic gap. Bootstrap's reboot puts `background:#fff` on
 * body and `#212529` on `.form-control-plaintext`; Elastic's own colours come
 * through the theme layer. Framed, the two halves disagreed -- themed text on
 * an unthemed ground -- which is what made the preferences form unreadable in
 * the dark themes. The frame has to be painted explicitly.
 */
html.iframe,
html.iframe body {
	background: var(--panel, #fff);
	color: var(--text, #222);
}

html.iframe .form-control,
html.iframe input.form-control,
html.iframe select.form-control,
html.iframe textarea.form-control {
	border-radius: 8px;
	border: 1px solid var(--line, #ddd);
	background: var(--search-bg, var(--panel, #fff));
	color: var(--text, #222);
	font-size: 13.5px;
}

html.iframe .form-control:focus {
	border-color: var(--accent, #4a3fbf);
	box-shadow: none;
}

/* ⚠ `.form-control` IS PUT ON BY JAVASCRIPT, AND EVERY RULE ABOVE WAITS FOR IT.
   Roundcube's PHP emits a bare `<select>` and a bare `<input>`; Elastic's
   `bootstrap_style()` runs at rcmail 'init' and only then does
       $('select', context).addClass('form-control custom-select')
   (and `pretty_select()` on top of that). Until it does, the element has no
   class this skin styles, so the browser paints its own control -- a WHITE box
   on a dark page, for the second or so a preferences frame takes to boot. The
   user sees the settings form flash white and then go dark, on every section.
   ⚠ Painted here on the ELEMENT, and withdrawn by `:not(.form-control)` the
   moment ui.js decorates it, so there is one appearance and never two rules
   arguing over the same box.
   ⚠ `html`, not `#layout` or `html.iframe`: preferences are framed, compose is
   not, and this has to hold in both. Buttons, checkboxes, radios and file
   inputs are excluded -- they are not text boxes and Elastic styles them as
   controls of their own. */
html select:not(.form-control),
html textarea:not(.form-control),
html .propform input:not(.form-control):not(.button):not([type=button]):not([type=checkbox]):not([type=radio]):not([type=file]),
html .formcontent input:not(.form-control):not(.button):not([type=button]):not([type=checkbox]):not([type=radio]):not([type=file]) {
	border-radius: 8px;
	border: 1px solid var(--line, #ddd);
	background: var(--search-bg, var(--panel, #fff));
	color: var(--text, #222);
	font-size: 13.5px;
}

html.iframe .formcontent {
	padding: 16px 20px;
}

html.iframe fieldset {
	border: 0;
	padding: 0;
	margin: 0 0 18px;
}

html.iframe legend {
	font-size: 13px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: .04em;
	color: var(--muted, #666);
	padding: 0 0 8px;
	border: 0;
	width: auto;
}

/* ⚠ `.form-control-plaintext` is BOOTSTRAP's, not Elastic's -- it carries a
   hardcoded #212529 that no theme layer can reach. Read-only preference values
   ("Auto", "07:30", "every 1 minute") are all rendered with it. */
html.iframe .col-form-label,
html.iframe .form-control-plaintext,
html.iframe td.title label,
html.iframe .propform label {
	color: var(--text, #222);
	font-size: 13.5px;
}

html.iframe .propform td {
	padding: 5px 8px 5px 0;
	border: 0;
	vertical-align: middle;
}

/* The skin picker in Preferences and Roundcube's own skin radio list sit in
   this document too, and their names are plain text. */
html.iframe .skinselection .skinname,
html.iframe .skinselection .skinlicense,
html.iframe .skinselection .skinauthor {
	color: var(--text, #222);
}

/* ------------------------------------------------------------ 8. toolbars */

/* Elastic centres the toolbar in the column head. The app aligns its controls
   to the content they act on, which for a list is its left edge. */
/* ⚠ `.header.toolbar` matches nothing: the content head is
   `<div class="header" role="toolbar">` with the real toolbar NESTED inside it
   (includes/mail-menu.html). Written as a compound selector the reading pane's
   toolbar was never made a flex row at all. */
/* ⚠ `:not(.popupmenu)`. Below 1200px ui.js gives this same <ul> the
   `popupmenu` class, and Elastic hides a closed popup with `display: none` on
   exactly that class -- which a `display: flex` here out-ranks. The list
   column's menu was therefore stuck open across the message list on every
   tablet and phone. */
/* ⚠ ALIGNMENT AND SIZE ONLY. `display: flex`, `align-items` and `gap` for this
   same match set are stated in §8f, which is later and therefore the one that
   computes -- they were written here too, and a duplicate that the
   no-duplicate-rules guard cannot see (it keys on the normalised prelude, and
   the two `:not()`s are in the opposite order there). */
#layout > div > .header .toolbar:not(.popupmenu):not(.quantum-morepanel) {
	justify-content: flex-start;
	margin-left: 0;
	/* ⚠ Clips, rather than scrolling. quantum.js measures the row against this
	   box and moves what does not fit into the "more" panel, so a scrollbar
	   here would hide buttons the panel is meant to catch. (An earlier
	   `overflow-x: auto` did exactly that, and stayed behind after the panel
	   landed.) */
	overflow: hidden;
	flex: 1 1 auto;
	min-width: 0;
}


/* ------------------------------------------ 8b. one box for every header button */
/*
 * Taken from the app, not invented here. Every labelled control in Q-Mail --
 * .mf-appbtn in the header, .mf-act-primary in the reading toolbar,
 * .mf-setnav-btn in settings -- is the SAME shape:
 *
 *     height 40px | padding 0 12px | gap 7px | radius 9px
 *     icon 19-20px (View::icon($name, 19))
 *     label 13px / 600, on the RIGHT of the icon
 *
 * Elastic instead stacks a 20px icon over a 90%-size label in a 4.2rem-tall
 * column, and it sizes the three kinds of header button three different ways:
 *
 *   .menu.toolbar a      4.2rem box, content top-aligned
 *   a.toolbar-button     auto height, centred   (the refresh button, which
 *                        lives OUTSIDE .toolbar -- see mail.html)
 *   a.button.icon        auto height, label hidden   (⋮, back buttons)
 *
 * Centred in one head those three disagree by ~7px -- the refresh icon sat
 * visibly lower than its neighbours -- and the tallest overflows the head and
 * clips its own label, which is how "Auswahl" lost its bottom half in Kontakte.
 * One row-shaped box for all three settles the alignment and the app's look at
 * the same time.
 */
/* ⚠ Descendant combinators, not `>`. The buttons are not always direct
   children of .toolbar: mail-menu.html wraps the reply-all and forward pairs
   in `<span class="dropbutton">`, and Elastic's ui.js wraps the select button
   in an `<li>`. Written with `>` this rule skipped exactly those, so the two
   dropbuttons kept Elastic's icon-over-label stack while their neighbours
   turned into rows. `:not(.popupmenu)` keeps it off the overflow menu ui.js
   builds from the same markup. */
#layout > div > .header .toolbar:not(.popupmenu) a,
#layout > div > .header > a.button,
#layout > div > .header > a.toolbar-button {
	display: inline-flex;
	flex-direction: row;
	align-items: center;
	justify-content: center;
	gap: 7px;
	height: 40px;
	padding: 0 12px;
	border-radius: 9px;
	line-height: 1;
	text-align: left;
	/* ⚠ Three separate things clip a label here, and all three have to go:
	   flex-shrink (the row squeezes its children rather than scrolling),
	   `.menu.toolbar a { max-width: 6rem }` (the rail's own cap, 96px --
	   "Allen antworten" needs 155), and the ellipsis overflow that hides what
	   the cap cut. Undo one or two and the label still arrives truncated. */
	flex: none;
	max-width: none;
	overflow: visible;
}

/* Icon-only buttons (⋮, the mobile back arrows) are the app's square
   .mf-iconbtn: 40x40, no label, so no padding to hold one. */
#layout > div > .header > a.button.icon:not(.toolbar-button),
#layout > div > .header .toolbar a.icon {
	width: 40px;
	padding: 0;
	justify-content: center;
}

/* ⚠ The rule above sets `display`, and `#layout > div > .header > a.button`
   out-ranks the bare `a.back-list-button { display: none }` Elastic uses to
   hide its mobile-only header buttons on desktop -- so it brought back the ‹
   back arrows, the ☰ task button and the list menu on a 1600px window. These
   restate that hiding at Elastic's own breakpoints. Change one, change both. */
@media screen and (min-width: 481px) {
	#layout > div > .header > a.task-menu-button {
		display: none;
	}
}

@media screen and (min-width: 769px) {
	#layout > div > .header > a.toolbar-menu-button {
		display: none;
	}
}

/* ⚠ ONE OVERFLOW BUTTON PER HEAD, and quantum.js picks which. Below 769px
   Elastic's own choice is the opposite of this skin's: it hides
   `a.toolbar-list-button` and keeps `a.toolbar-menu-button`, whose menu is the
   READING PANE's toolbar plus copies of the list's -- so a phone's inbox ⋯
   listed Antworten, Weiterleiten, Löschen and Markieren, actions on a message
   that is not open, while a tablet's listed the three that belong to a list.
   `oneMore()` marks the redundant one instead (see the note there); this is
   only what the mark does. */
.quantum-dup {
	display: none !important;
}

/* ⚠ 1201 IS ELASTIC'S NUMBER AND IT IS NOT §2c's 1025. These three are the
   small-screen navigation, and what makes them redundant is not this skin's bar
   but Elastic showing all three columns at once -- which it only does in
   `layout-large`, from 1201 up. Between 1025 and 1200 the desktop bar is
   painted while Elastic still hides the folder column, so `back-sidebar-button`
   is the ONLY way to the folders there: lower this to match §2c and a laptop
   window at 1100px loses its folder list entirely. §8b gives it the ordinary
   header-button box, which is what it should look like in a 60px bar. */
@media screen and (min-width: 1201px) {
	#layout > div > .header > a.back-list-button,
	#layout > div > .header > a.back-sidebar-button,
	#layout > div > .header > a.toolbar-list-button {
		display: none;
	}
}

/* ⚠ OPTICAL SIZING, as in the bar (§2). At one nominal 19px these glyphs
   render 13.0px of ink (a reply arrow) to 18.8px (a trash can) -- so the
   refresh and delete buttons read visibly larger than their neighbours while
   every computed style matched. Measured, then solved for ~16px of ink:

       reply / reply-all  13.0 -> 23px      forward   16.8 -> 18px (as is)
       refresh            18.5 -> 16.5px    delete    18.8 -> 16px

   The ellipsis is exempt: three dots are 5px of ink by design, and scaling
   them to match would give a comically large "more". tests/skin.py measures
   the rendered pixels and fails if these drift. */
/* ⚠ 21px, not the 23px that matched their ink BOX.
   The reply arrow puts less ink in its em than its neighbours, so sizing it
   until the bounding boxes agreed also scaled its STROKE by 23/19 -- the box
   matched and the glyph still read heavier, which is exactly what was
   reported. Measured both ways: at 23px the box was 14.0x15.8 (in line with
   everything else) while the ink AREA was 117px^2 against forward's 106 and
   refresh's 104. 21px brings the weight into the group and gives up ~1.5px of
   box, which is the side the eye forgives. */
/* ⚠ `:not(.popupmenu)` kept in the selector -- without it these carry three
   classes against the shared rule's three, and the shared rule wins on source
   order because it is written later. The sizes silently reverted to 19px. */
#layout > div > .header .toolbar:not(.popupmenu) a.reply:before,
#layout > div > .header .toolbar:not(.popupmenu) a.reply-all:before {
	font-size: 21px;
}

/* A solid glyph carries far more ink than an outline one at the same size --
   the plus-square measured 174px^2 against the reply arrow's 117. */
#layout > div > .header .toolbar:not(.popupmenu) a.create:before {
	font-size: 17px;
}

#layout > div > .header .toolbar:not(.popupmenu) a.delete:before {
	font-size: 16px;
}

#layout > div > .header > a.toolbar-button.refresh:before {
	font-size: 16.5px;
}

/* View::icon($name, 19). ⚠ `height` needs !important -- Elastic sets
   `height: 1.75rem !important` on toolbar icons, and without matching weight
   the glyph keeps a 28px box that the row then centres around. */
#layout > div > .header .toolbar:not(.popupmenu) a:before,
#layout > div > .header > a.button:before,
#layout > div > .header > a.toolbar-button:before {
	font-size: 19px;
	line-height: 19px;
	height: 19px !important;
	width: auto !important;
	float: none !important;
	margin: 0 !important;
	color: var(--muted, #666);
}

/* .mf-act-lbl / .mf-appbtn .lbl: 13px, 600, beside the icon. */
#layout > div > .header .toolbar:not(.popupmenu) a span.inner,
#layout > div > .header > a.toolbar-button span.inner {
	font-size: 13px;
	font-weight: 500;
	line-height: 1;
	overflow: visible;
	white-space: nowrap;
	max-width: none;
	width: auto;
	text-transform: none;
	letter-spacing: 0;
}

#layout > div > .header .toolbar:not(.popupmenu) a:hover,
#layout > div > .header > a.button:hover,
#layout > div > .header > a.toolbar-button:hover {
	background: var(--hover, #f3f3f6);
}

/* ⚠ .dropbutton is an inline-block wrapper, so the two anchors inside it are
   laid out on a TEXT BASELINE rather than as flex items -- which put the
   Antworten/Weiterleiten pair 2px off every other button in the head. */
#layout > div > .header .toolbar .dropbutton {
	display: flex;
	align-items: center;
}

/* The dropdown caret beside Antworten/Weiterleiten is a hint, not a button. */
#layout > div > .header .toolbar .dropbutton > a.dropdown {
	width: 18px;
	min-width: 18px;
	padding: 0 4px;
	gap: 0;
}

#layout > div > .header .toolbar .dropbutton > a.dropdown:before {
	font-size: 11px;
	height: 11px !important;
	line-height: 11px;
}

/* ⚠ EVERY BUTTON IS LABELLED, and the label sits to the RIGHT of its icon.
 * There is no icon-only column head: the box in §8b already sizes to its
 * content, so a label simply widens the button.
 *
 * The one exception is Elastic's own icon-only class, `a.button.icon` -- the ⋮
 * and the mobile back arrows, whose labels are screen-reader text and were
 * never meant to be drawn. Elastic hides those with
 * `a.button.icon > span.inner { display: none }`, which is left alone.
 *
 * ⚠ The refresh button carries that class too (`button icon toolbar-button`),
 * and Elastic re-shows ITS label only above 1200px. Restated here without the
 * width condition, so it does not sit unlabelled between labelled neighbours
 * on a 1100px window. */
#layout > div > .header > a.toolbar-button span.inner {
	display: inline !important;
}

/* ⚠ THE PAGE-JUMP BOX GOES. Elastic ends the list footer with a text input to
   type a page number into -- already hidden on a phone by its own stylesheet --
   between the ‹ › and « » buttons that do the same job one page at a time. In a
   seven-thread inbox it is a box with a 1 in it and nothing to do.
   ⚠ Scoped to a COLUMN FOOTER. The same `.menu.pagenav` markup is used inside
   the recipient dialog, where the list can be long and the field earns its
   place. */
#layout > div > .footer.menu.pagenav input {
	display: none;
}

/* ---- 5d. the refresh button leads the list head ------------------------
 * mail.html puts it AFTER the toolbar -- the far right of the head -- and it
 * reads as the primary control of that column, so it goes first.
 *
 * ⚠ `order`, NOT A DOM MOVE. quantum.js used to relocate the element itself
 * (`placeRefresh()`), which meant the browser painted the head once as authored
 * and again after the script ran: on every task switch the button appeared at
 * the right edge for a frame and then jumped left. `order` is resolved in the
 * same layout pass as the first paint, so there is nothing to see. The head is
 * a flex row at every width (Elastic), which is what makes this available. */
#layout > div#layout-list > .header > a.toolbar-button.refresh {
	order: -1;
}

/* A row of labelled buttons can outgrow a narrow column -- Elastic caps the
   message list at 30% of the window, which is 410px on a 1531px screen while
   its four German labels need about 500px. quantum.js measures each toolbar
   and moves what does not fit into a "more" panel; this box is what it
   measured against; see §8 for the box itself. */
/* -------------------------------- 8d. the "more" panel ------------------- */
/* Built by quantum.js when a toolbar overflows. Deliberately its own panel and
   not Roundcube's popup machinery: `#toolbar-menu` is assembled by Elastic's
   smart_toolbar(), which only runs on small layouts, so on desktop there is no
   menu to hand the overflow to. */
/* ⚠ THE BOX IS §8g's. That section pins every more-button in the skin --
   Elastic's and this one -- to the same 40x40 with `!important`, and it is
   later, so the width/height/display/colour/radius written here computed to
   nothing. What is left is what §8g does not say: where this button sits in
   its row. Its `:hover` was stated in both places with the same two values,
   and §8g's carries `!important`. */
.quantum-more {
	/* ⚠ Pushed to the column's right edge. Everything before it in the head is
	   left-aligned, so an auto margin here is what separates "the controls" on
	   the left from "what did not fit" on the right. */
	margin-left: auto;
	flex: none;
	cursor: pointer;
}

.quantum-more[hidden] {
	display: none !important;
}

.quantum-morepanel {
	list-style: none;
	margin: 0;
	position: absolute;
	top: 50px;
	/* ⚠ INSIDE ITS COLUMN, and that is not a preference. `#layout-list` is
	   `overflow: hidden` (Elastic, for its rounded corners), so anything that
	   reaches past the column's right edge is CLIPPED -- an attempt to centre
	   the panel on its button, the way Bootstrap centres a popover, put 38px of
	   it behind that edge. Roundcube's own menus escape because Bootstrap
	   appends them to <body>; this one is built into the head, where the
	   command elements it holds already live. So it stays right-aligned, and
	   the arrow below is centred on the PANEL rather than on the button. */
	right: 8px;
	z-index: 30;
	/* ⚠ THE POPOVER'S BOX, not a box of its own. Roundcube's menus open in a
	   Bootstrap popover, and the pair sit in the same header row -- so they
	   share its rules rather than each having their own.
	   ⚠ SIZED BY ITS CONTENT between a floor and the popover's own cap. Content
	   alone made a three-item menu ("Auswahl", "Konversationen", "Optionen")
	   140px wide beside a 194-220px popover; a 240px floor, tried before that,
	   pinned it at the 276px cap and made it the wider of the two. 220 is the
	   popover's own working width, so the pair match at a glance and neither is
	   padded out to a number it does not need. */
	min-width: 220px;
	max-width: 276px;
	width: max-content;
	padding: 6px;
	background: var(--panel, #fff);
	border: 1px solid var(--line, #ddd);
	border-radius: 10px;
	box-shadow: 0 8px 24px rgba(0, 0, 0, .14);
	display: flex;
	flex-direction: column;
	gap: 2px;
}

/* …and the popover's arrow. Bootstrap draws it as two stacked triangles -- an
   outline behind a fill -- pointing back at the button that opened the menu.
   ⚠ Both are absolutely positioned, so the panel's `flex-direction: column`
   does not take them as rows.
   ⚠ AIMED AT THE ⋯, not centred on the panel. Both are pinned to the same
   right edge, so the offset is arithmetic rather than a guess: the panel sits
   8px from the head's right edge, the head pads 12px, and the ⋯ is 40px wide
   -- so the button's centre is 32px in from that edge and 24px in from the
   panel's. A 16px triangle placed at `right: 16px` centres exactly there.
   An earlier cut centred it on the panel and it pointed at empty header. */
.quantum-morepanel:before,
.quantum-morepanel:after {
	content: "";
	position: absolute;
	bottom: 100%;
	left: auto;
	right: 16px;
	margin-left: 0;
	width: 0;
	height: 0;
	border-left: 8px solid transparent;
	border-right: 8px solid transparent;
}

.quantum-morepanel:before {
	border-bottom: 8px solid var(--line, #ddd);
}

.quantum-morepanel:after {
	border-bottom: 8px solid var(--panel, #fff);
	margin-bottom: -1px;
}

/* The glyph, since the rule that carries it is scoped to `.popover`. Same
   codepoint, same font, so the two menus cannot drift apart. */
.quantum-morepanel li > a[aria-haspopup] {
	display: flex;
}

/* ⚠ GLYPH AND FONT ONLY. Colour, size, margin and min-width are stated for
   all three menus a few hundred lines down, in §8h -- restating them here was
   the same declaration twice, and the file's own guard said so. */
.quantum-morepanel li > a[aria-haspopup]:after {
	content: "\f054";
	font-family: 'Icons';
	font-weight: 900;
	text-align: right;
}

/* ⚠ It is a <ul> whose rows are <li> -- ui.js wraps every toolbar button, so
   what gets moved in here is the wrapper, not the anchor. An <li> in a <div>
   keeps `display: list-item` and the browser drew a BULLET beside it. */
.quantum-morepanel > li {
	display: flex !important;
	width: 100%;
	height: auto !important;
	margin: 0;
	padding: 0;
	list-style: none !important;
	border: 0 !important;
}

/* ⚠ `> li.spacer` only. The panel receives the <li> WRAPPERS ui.js builds, so
   the bare `.dropbutton` and `.spacer` selectors an earlier cut carried could
   never match anything. */
.quantum-morepanel > li.spacer {
	display: none !important;
}

/* ⚠ The panel's rows are the SAME elements the toolbar held -- moved, because
   Roundcube binds each command to its own element and a copy would do nothing.
   So they arrive carrying the toolbar's own geometry and have to be re-shaped
   into full-width rows here. */
.quantum-morepanel a,
.quantum-morepanel > li > a {
	display: flex !important;
	flex-direction: row;
	align-items: center;
	justify-content: flex-start !important;
	gap: 7px;
	width: 100% !important;
	max-width: none !important;
	min-width: 0 !important;
	height: 40px;
	padding: 0 10px !important;
	border-radius: 8px;
	white-space: nowrap;
}


.quantum-morepanel a span.inner {
	display: inline !important;
	font-size: 13px;
	font-weight: 500;
}

/* ⚠ The icon rules in §8b are scoped to `.header .toolbar a`, and a button
   moved in here is no longer inside a toolbar -- so its :before lost the icon
   font and the glyph rendered as nothing at all. Restated for the panel. */
.quantum-morepanel a:before {
	font-family: 'Icons';
	font-weight: 900;
	font-style: normal;
	font-size: 19px;
	line-height: 19px;
	height: 19px !important;
	width: 19px !important;
	text-align: center;
	float: none !important;
	margin: 0 !important;
	flex: none;
	color: var(--muted, #666);
}

/* Elastic colours a toolbar button by state -- `.menu a.selected` goes green,
   a disabled one goes grey. In a list those read as arbitrary highlights, so
   the panel states its own text colour. */
.quantum-morepanel a,
.quantum-morepanel a.selected,
.quantum-morepanel a.active {
	color: var(--text, #222) !important;
}

.quantum-morepanel a.disabled {
	opacity: .45;
}

.quantum-morepanel a:hover {
	background: var(--hover, #f3f3f6);
}


/* The dropdown carets beside Antworten and Weiterleiten keep their narrow box
   -- a caret is a hint on the button next to it, not a button with a name. */
#layout > div > .header .toolbar .dropbutton > a.dropdown span.inner {
	display: none !important;
}

/* ---------------------- 8g. one shape for every "more" -------------------
 * SIX buttons open a menu of leftovers: the folder rail's ⋮, the contacts
 * groups rail's ⋮, the message list's and the contacts list's, the reading
 * pane's "Mehr", and the contacts content toolbar's. Elastic draws them at
 * three different sizes, two glyphs (ellipsis-v and ellipsis-h) and two
 * colours. They are one control, so they get one appearance -- listed together
 * here so a change cannot reach five of the six. */
#layout > div > .header .toolbar a.more,
#layout > div > .header > a.more,
#layout > div > .header > a.sidebar-menu,
#layout > div > .header > a.toolbar-menu-button,
#layout > div > .header > a.toolbar-list-button,
.quantum-more {
	/* ⚠ NO `display` here. .toolbar-menu-button and .toolbar-list-button are
	   Elastic's mobile-only menu buttons, hidden on desktop by §8b's
	   breakpoint rules -- an important `display` in this shared block un-hid
	   them on a 1600px window. The three that are actually shown set it
	   themselves, below. */
	align-items: center;
	justify-content: center;
	width: 40px !important;
	min-width: 40px !important;
	height: 40px !important;
	padding: 0 !important;
	border-radius: 9px;
	/* ⚠ ONE colour for all six. Elastic gives .sidebar-menu the list colour and
	   toolbar buttons @color-toolbar-button, which are not the same value. */
	color: var(--muted, #666) !important;
	background: transparent;
}

#layout > div > .header .toolbar a.more:hover,
#layout > div > .header > a.more:hover,
#layout > div > .header > a.sidebar-menu:hover,
#layout > div > .header > a.toolbar-menu-button:hover,
#layout > div > .header > a.toolbar-list-button:hover,
.quantum-more:hover {
	background: var(--hover, #f3f3f6);
	color: var(--text, #222) !important;
}

#layout > div > .header .toolbar a.more,
#layout > div > .header > a.more,
#layout > div > .header > a.sidebar-menu,
.quantum-more {
	display: inline-flex !important;
}

#layout > div > .header .toolbar a.more span.inner,
#layout > div > .header > a.more span.inner,
#layout > div > .header > a.sidebar-menu span.inner {
	display: none !important;
}

/* ⚠ AND THE RIGHT EDGE OF THE HEAD. Elastic never positions these: it relies
   on `.header-title` being a `flex: 1` element between the refresh button and
   them, which pushes them right for free. Below 1201px the list head's title is
   empty, and on a phone this skin hides it outright -- so the ⋯ fell back
   against Aktualisieren at the LEFT of the head, which is not where anyone
   looks for it. An auto margin does the pushing itself, whatever the title is
   doing. Harmless where the title still flexes: the slack is already gone.
   ⚠ !important, because Elastic's own touch block ships
   `#layout > div > .header a.button { margin: 0 .3rem !important }` -- an
   important SHORTHAND, which sets margin-left at that weight too. Without the
   weight this worked at 700px and did nothing at 390. */
#layout > div > .header > a.toolbar-menu-button,
#layout > div > .header > a.toolbar-list-button {
	margin-left: auto !important;
}

/* ⚠ The same GLYPH too. `.sidebar-menu` and the toolbar menu buttons use
   ellipsis-v (\f142); the skin's own ⋮ and Roundcube's "Mehr" use the
   horizontal one. Three dots side by side, everywhere. */
#layout > div > .header > a.sidebar-menu:before,
#layout > div > .header > a.toolbar-menu-button:before,
#layout > div > .header > a.toolbar-list-button:before {
	content: "\f141" !important;
}

/* Both draw the same glyph at the same weight, so the pair cannot drift. */
/* ⚠ `.quantum-more:before`, not `.quantum-more`. The skin's own button drew
   its ellipsis as TEXT, so the rules meant for a glyph landed on the BUTTON and
   collapsed it to 40x17 -- and having no :before at all, it could never match
   the other five whatever these rules said. It draws the same pseudo-element
   as they do now. */
.quantum-more:before {
	content: "\f141";
	font-family: 'Icons';
	font-weight: 900;
	font-style: normal;
	display: block;
}

#layout > div > .header .toolbar a.more:before,
#layout > div > .header > a.more:before,
#layout > div > .header > a.sidebar-menu:before,
#layout > div > .header > a.toolbar-menu-button:before,
#layout > div > .header > a.toolbar-list-button:before,
.quantum-more:before {
	font-size: 17px !important;
	line-height: 17px !important;
	height: 17px !important;
	width: auto !important;
	margin: 0 !important;
	float: none !important;
}

/* Compose: "Abbrechen" sits beside "Senden" as the quiet half of the pair.
   ⚠ `!important` on the two colours. Bootstrap and Elastic both paint buttons
   in .formbuttons from selectors carrying an id, so the skin's own fill and
   text lost and the button rendered grey-on-grey -- 1.03:1 in Lightblue,
   which is invisible, not merely low. */
#layout .formbuttons .quantum-cancel,
.formbuttons .quantum-cancel {
	height: 40px;
	margin-left: 8px;
	padding: 0 16px;
	border-radius: 9px;
	border: 1px solid var(--line, #ddd);
	background: var(--panel, #fff) !important;
	color: var(--text, #222) !important;
	font-size: 13px;
	font-weight: 500;
	opacity: 1;
}

#layout .formbuttons .quantum-cancel:hover,
.formbuttons .quantum-cancel:hover {
	background: var(--hover, #f3f3f6) !important;
}

/* ⚠ Elastic's .btn-link derives its colour from @color-link through a LESS
   function, so remapping @color-link alone did not reach it -- "Neues Fenster"
   stayed at 3.6:1 on a dark panel. Stated directly instead. */
#layout .formbuttons .btn-link,
#layout .formbuttons a.extwin {
	color: var(--accent, #4f7f13) !important;
}

/* -------------------- 8h. every popup menu is the more panel -------------
 * Roundcube opens half a dozen popups off these buttons -- #mailboxoptions-menu,
 * #groupoptions-menu, #message-menu, #markmessage-menu, the rebuilt
 * #toolbar-list-menu. They are the same thing as the skin's own overflow
 * panel, so they take its appearance rather than Elastic's.
 * ⚠ `.popupmenu` only carries that class while it is CLOSED as a popup; the
 * inline toolbars are the same elements with the class stripped, so these
 * rules are scoped to `.popupmenu` and cannot reach a toolbar in a head. */
/* ⚠ THE CARD IS THE `.popover`, NOT THE `.popupmenu`. Roundcube opens its own
   menus by moving the list into a Bootstrap popover, so what the user sees
   around #message-menu is the wrapper's border, radius and caret -- none of
   which a rule on the inner list can reach. Styling only the list left the
   skin's own panel and Roundcube's menus visibly different at the edges.
   One card here; the inner list is flattened below. */
.popupmenu,
.popover,
.popover > .popup {
	background: var(--panel, #fff);
	border: 1px solid var(--line, #ddd);
	border-radius: 10px;
	box-shadow: 0 8px 24px rgba(0, 0, 0, .14);
	padding: 6px;
}

.popover .popupmenu,
.popover .popover-body {
	background: transparent;
	border: 0;
	border-radius: 0;
	box-shadow: none;
	padding: 0;
}

/* Bootstrap draws the caret as two stacked triangles -- an outline behind a
   fill. Both follow the card, or the arrow keeps Elastic's grey outline over
   the theme's panel. */
.popover > .arrow:before {
	border-top-color: var(--line, #ddd);
	border-bottom-color: var(--line, #ddd);
}

.popover > .arrow:after {
	border-top-color: var(--panel, #fff);
	border-bottom-color: var(--panel, #fff);
}

.popupmenu ul.menu,
.popupmenu ul.listing,
.popover ul.menu.listing {
	list-style: none;
	margin: 0;
	padding: 0;
	background: transparent;
}

.popupmenu li,
.popover ul.menu.listing li {
	border: 0 !important;
	list-style: none;
}

.popupmenu li > a,
.popover ul.menu.listing li > a {
	display: flex;
	align-items: center;
	gap: 7px;
	height: 40px;
	padding: 0 10px;
	border-radius: 8px;
	font-size: 13px;
	font-weight: 500;
	color: var(--text, #222);
	white-space: nowrap;
}

/* ⚠ `font-size` HAS TO CARRY WEIGHT HERE. ui.js gives every popup <ul> the
   classes `menu toolbar popupmenu listing iconized`, and Elastic sizes its
   glyphs through `.listing.iconized li a:before { font-size: 1.25em }` --
   (0,4,2) against this rule's (0,2,1), so it won and the size below was never
   the size on screen. `em` at that, against a 14px row: every glyph rendered
   at 17.5px, and the ROW of them therefore rendered at whatever ink each one
   happens to carry. Measured on the tablet menu: the trash can and the
   ellipsis read a size larger than the arrows beside them. */
/* ⚠ AND THE PAIR INSIDE `<span class="dropbutton">` take the row's type as
   well. mail-menu.html wraps reply-all and forward with their carets, so those
   two are `li > span > a` and every `li > a` rule walked past them: in the
   phone menu their labels came out at Elastic's own size, measurably taller
   than the four rows around them (18px of cap against 15px). */
.popupmenu li .dropbutton > a,
.popover ul.menu.listing li .dropbutton > a {
	font-size: 13px;
	font-weight: 500;
	color: var(--text, #222);
}

/* ⚠ …AND THE CARET BESIDE THEM IS A CHEVRON, like every other row that opens
   something. Elastic gives `.dropbutton > a.dropdown` a filled block of
   @color-popover-mobile-dropbutton-background inside the menu and draws its
   glyph at 1.25em -- a grey slab at the end of two rows out of seven, with a
   caret larger than the chevrons on the rows below. The row keeps one shape
   whichever way it opens. */
.popupmenu li .dropbutton,
.popover ul.menu.listing li .dropbutton {
	display: flex;
	align-items: center;
	width: 100%;
}

.popupmenu li .dropbutton > a:not(.dropdown),
.popover ul.menu.listing li .dropbutton > a:not(.dropdown) {
	flex: 1 1 auto;
	min-width: 0;
}

.popupmenu li .dropbutton > a.dropdown,
.popover ul.menu.listing li .dropbutton > a.dropdown {
	flex: none;
	width: auto;
	min-width: 0;
	padding: 0 4px 0 0;
	background: transparent !important;
}

.popupmenu li .dropbutton > a.dropdown span.inner,
.popover ul.menu.listing li .dropbutton > a.dropdown span.inner {
	display: none;
}

/* The same 12px as the `:after` chevron §8h draws on an `aria-haspopup` row. */
.popupmenu li .dropbutton > a.dropdown:before,
.popover ul.menu.listing li .dropbutton > a.dropdown:before {
	font-size: 12px !important;
	width: auto !important;
	color: var(--muted, #666);
}

.popupmenu li > a:before,
.popover ul.menu.listing li > a:before {
	font-size: 17px !important;
	line-height: 17px;
	height: 17px !important;
	width: 19px !important;
	margin: 0 !important;
	float: none !important;
	text-align: center;
	color: var(--muted, #666);
}

/* ⚠ AND THE SAME OPTICAL CORRECTIONS THE ROW GETS IN §8b, scaled from that
   block's 19px baseline to the 17px used here. §8b's rules are written
   `.toolbar:not(.popupmenu)` -- deliberately, so they cannot reach the menu --
   which meant the menu had no correction at all and its glyphs came out at one
   nominal size and five different ink weights:

       reply / reply-all  21/19 -> 19px      delete   16/19 -> 14.5px
       create             17/19 -> 15px

   The ellipsis stays exempt, as it is in §8b: three dots are 5px of ink by
   design and scaling them to match gives a comically large "more". */
.popupmenu li > a.reply:before,
.popupmenu li > a.reply-all:before,
.popover ul.menu.listing li > a.reply:before,
.popover ul.menu.listing li > a.reply-all:before {
	font-size: 19px !important;
}

.popupmenu li > a.delete:before,
.popover ul.menu.listing li > a.delete:before {
	font-size: 14.5px !important;
}

.popupmenu li > a.create:before,
.popover ul.menu.listing li > a.create:before {
	font-size: 15px !important;
}

/* ⚠ AND THE PAIR INSIDE `<span class="dropbutton">`. mail-menu.html wraps
   reply-all and forward with their carets, so those two are `li > span > a`
   and every `li > a` rule above walked straight past them -- in a menu of six
   rows, two came out at Elastic's 17.5em-derived size and four at 17px.
   `:not(.dropdown)` keeps the caret itself out of it: it is a hint at 11px
   (§8b), not a button glyph. */
.popupmenu li .dropbutton > a:not(.dropdown):before,
.popover ul.menu.listing li .dropbutton > a:not(.dropdown):before {
	font-size: 17px !important;
}

.popupmenu li .dropbutton > a.reply-all:before,
.popover ul.menu.listing li .dropbutton > a.reply-all:before {
	font-size: 19px !important;
}

/* ⚠ A "MEHR" INSIDE A "MEHR". toolbar_init() MOVES the reading pane's whole
   toolbar into `#toolbar-menu` -- and that toolbar ends with mail-menu.html's
   own `a.more`, which opens `#message-menu`. So below 769px, where the list
   head's ⋯ opens `#toolbar-menu`, the first thing in the menu was another more
   button. One level of "the rest of it" is a menu; two is a maze.
   ⚠ Scoped by `.popupmenu`, which is the class ui.js puts on a head toolbar
   only while it IS a popup. Above that width the same <ul> renders inline as
   the reading pane's button row, where `a.more` is a real control and stays. */
/* ⚠ NO RULE HIDES `a.more` ANY MORE, and that is deliberate. It used to be
   hidden inside a popup because the INBOX's ⋯ opened the reading pane's
   toolbar, so the menu carried a "Mehr" that opened a second menu. The inbox
   now opens its own list menu (see .quantum-dup above), which never held one --
   and inside the READING pane's menu `a.more` is exactly what it is on the
   desktop: the rest of the message actions, behind one more step. Hiding it
   there only put Drucken, Herunterladen and Verschieben out of reach on a
   phone. */

/* ⚠ The submenu chevron is an `:after`, and Elastic's font-icon mixin FLOATS
   it left. In a flex row the float is ignored for placement but the glyph
   still refuses to share the line box with a label that has taken the width,
   so "Download…", "Verschieben…" and "Markieren" each wore their arrow on a
   second line. Unfloated and pushed to the right edge, where a submenu
   indicator belongs. */
/* ⚠ AND IN THE SKIN'S OWN PANEL. Elastic draws the submenu chevron from
   `.popover .menu li a[aria-haspopup]:after` -- scoped to the popover, which
   this panel is not -- so a row that opens a menu of its own ("Konversationen")
   looked identical to one that just runs a command, and its submenu then
   appeared over the panel with nothing having said it would. */
.quantum-morepanel li > a[aria-haspopup]:after,
.popupmenu li > a:after,
.popover ul.menu.listing li > a:after {
	float: none !important;
	margin: 0 0 0 auto !important;
	font-size: 12px;
	line-height: 1;
	width: auto !important;
	min-width: 0;
	height: auto !important;
	color: var(--muted, #666);
}

/* …which only works if the label yields the space. Elastic lets `.inner` size
   itself, so it filled the row and left the arrow nowhere to go. */
.quantum-morepanel li > a[aria-haspopup] > span.inner,
.popupmenu li > a > span.inner,
.popover ul.menu.listing li > a > span.inner {
	flex: 1 1 auto;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
}

.popupmenu li > a:hover,
.popover ul.menu.listing li > a:hover {
	background: var(--hover, #f3f3f6);
}

.popupmenu li > a.disabled,
.popover ul.menu.listing li > a.disabled {
	opacity: .45;
}

/* -------------------- 8f. the toolbar is a <ul>, not a <div> -------------
 *
 * ⚠ ui.js REBUILDS every column-head toolbar at startup, on all screen sizes:
 * it wraps each button in `<li role="menuitem">`, deletes the template's
 * `<div class="toolbar menu">` outright, and appends
 *     <ul id="toolbar-list-menu" class="menu toolbar popupmenu listing iconized">
 * (`popupmenu` is stripped again on desktop). So the row is a LIST, and
 * `.listing li { border-bottom: 1px solid }` is the hairline that appeared
 * under the buttons in the message list and the reading pane -- one segment
 * per button, which is why it never spanned the whole column.
 */
/* ⚠ `:not(.quantum-morepanel)` -- the panel is given the toolbar's own class
   list so Elastic's icon rules keep matching, which also made these row rules
   match it and laid the panel out horizontally.
   ⚠ `:not(.popupmenu)` too, and this one only shows up below 1200px. Elastic
   keeps a CLOSED popup as `display: none` on the toolbar itself; forcing
   `display: flex` on every toolbar in a head therefore pinned the list column's
   menu permanently open, spilling Auswahl/Konversationen/Optionen across the
   message list on every tablet and phone. An OPEN one is moved into a
   `.popover` and out of this selector's reach, where §8h dresses it. */
#layout > div > .header .toolbar:not(.quantum-morepanel):not(.popupmenu),
#layout > div > .header ul.menu.toolbar:not(.quantum-morepanel):not(.popupmenu) {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: row;
	align-items: center;
	gap: 2px;
}

#layout > div > .header .toolbar:not(.quantum-morepanel):not(.popupmenu) > li {
	display: flex;
	align-items: center;
	height: auto;
	margin: 0;
	padding: 0;
	list-style: none;
	/* The separator lines. */
	border: 0 !important;
}

/* ⚠ `<span class="spacer">` becomes `<li class="spacer">` with `width: 1em`.
   It is Elastic's way of grouping reply/forward apart from delete, and it is
   what put a visibly wider gap between Weiterleiten and Löschen than between
   any other pair.
   ⚠ THE TWO `:not()`s ARE LOAD-BEARING, not copy-paste. The rule above sets
   `display: flex` on `.toolbar:not(.quantum-morepanel):not(.popupmenu) > li` --
   (1,4,2) against the (1,3,2) this selector had, so the spacer went on being a
   14px flex item and the gap this rule exists to remove was still there.
   Measured: 36.7px of ink-to-ink lead before Löschen against 26.7 for every
   other pair in the row. Matching the negations makes it (1,5,2), and later. */
#layout > div > .header .toolbar:not(.quantum-morepanel):not(.popupmenu) > li.spacer,
#layout > div > .header .toolbar:not(.quantum-morepanel):not(.popupmenu) > .spacer {
	display: none;
}

/* ⚠ EVERY separator inside the three panels, in one place. The only rules the
   app keeps are the ones between messages in the list -- items 1, 8, 11, 12.
   `.listing` puts a border on li, td AND tr, and Elastic adds its own on the
   search bar, the message header and the column footers, so naming just one
   of them leaves the others behind (which is how this came back three times). */
/* ⚠ The folder tree's own `li` and `ul` are NOT in this list: §8i states the
   same two rules for them a few hundred lines down, together with the nested
   `ul li` and the treelist twins that only that section knows about. Naming
   them here as well was the same declaration twice. */
#layout-sidebar .listing td,
#layout-sidebar .listing tr,
#layout-sidebar > .footer,
#layout-list > .header,
#layout-list > .footer,
#layout-list .searchbar,
#layout-list .listing > ul,
#layout-content > .header,
#layout-content .searchbar,
#layout-content #message-header,
#layout-content .message-part,
#layout-content .message-htmlpart,
#layout-content .header-content {
	border-top: 0 !important;
	border-bottom: 0 !important;
}

/* The first message row keeps no rule above it either -- the search field's
   own spacing already separates them (item 7). */
.messagelist tr.message:first-child > td {
	border-top: 0 !important;
}

/* ⚠ The search field had padding above and none below, so the first message
   sat tight against it. Elastic gives .searchbar a fixed 36px and no padding
   at all; this makes the breathing space symmetrical. */
/* ⚠ NO padding above. Both column heads are 56px, so the first row of each
   column starts at the same y -- and the search field is the message list's
   first row, exactly as "Posteingang" is the folder rail's. Six pixels of top
   padding pushed it 5px below its neighbour, and everything under it with it.
   The breathing space goes underneath, as a margin, where it separates the
   field from the first message instead of shifting the whole column down. */
/* ⚠ THE COLUMN HAS ONE GUTTER, and this row has to use it. The head pads 12px
   and the list rows start on the same line, so a search bar padded 10px put
   the field 2px left of the refresh button above it and the chevron 2px past
   the ⋯ -- a misalignment small enough to read as blur rather than as an
   error, on the one row that sits between two things it should agree with. */
#layout > div > .searchbar {
	height: auto;
	min-height: 0;
	padding: 0 12px;
	margin-bottom: 10px;
	background: transparent;
	overflow: visible;
}

/* ------------------------------------- 2d. the frame before ui.js runs ----
 * ⚠ THE FLASH BETWEEN FIRST PAINT AND `screen_resize_*()`, and what it looks
 * like is a thin coloured stripe down the middle of a phone screen.
 *
 * Roundcube reloads the whole document on every task switch, and which columns
 * a small screen shows is decided by JAVASCRIPT: `screen_resize_small_all()`
 * puts `hidden` on the ones that do not fit, and `screen_resize_html()` puts
 * `layout-phone` / `layout-small` on <html>. Until those run, the three
 * columns are ordinary flex siblings -- measured at 390px: sidebar at x=7
 * (260 wide), list at x=274 (260 wide), and between them the 7px `--gap`
 * CHANNEL of §2, painted in the page ground. That channel is the artifact:
 * a "separator" between two columns that are not supposed to be on screen at
 * the same time, visible for one frame every couple of task switches,
 * depending on how much of the page came from cache.
 *
 * ⚠ KEYED ON THE ABSENCE OF ui.js's OWN CLASS, not on a timer and not on a
 * class of ours. `html:not([class*="layout-"])` is true exactly while the
 * layout is unclassified, and stops being true the moment `layout-phone`,
 * `layout-small`, `layout-normal` or `layout-large` lands -- so this styles
 * the gap in time and nothing after it. A `quantum-boot`-style class of our
 * own could not do that: it would have to be removed by a script, and a
 * script is the thing that is late.
 *
 * ⚠ WHAT IT SHOWS is what ui.js is about to show, so the correction is not
 * visible: on a phone the list column, full width; where there is no list
 * (compose, the contact editor) the content column, which is why `:has()`
 * decides rather than a hardcoded list of body classes. Nothing is hidden that
 * would then have to appear -- the frame simply starts where it ends up. */
@media screen and (max-width: 768px) {
	html:not([class*="layout-"]) #layout > div#layout-sidebar,
	html:not([class*="layout-"]) #layout:has(> div#layout-list) > div#layout-content {
		display: none !important;
	}

	html:not([class*="layout-"]) #layout > div {
		flex: 1 1 100%;
		min-width: 0;
	}
}

/* 769-1200px is Elastic's `normal` mode: list AND content, no folder column.
   The channel between those two is meant to be there; the one beside the
   sidebar is not.
   ⚠ 1200, WHICH IS ELASTIC'S NUMBER, not §2c's. This rule predicts what ui.js
   is about to do so the pre-classified frame matches it; ui.js goes `large` at
   1201 and shows the folder column there, so a cap of 1279 hid a column that
   was about to appear -- a flash in the other direction, in the one band
   (1201-1279) where this used to be wrong. */
@media screen and (min-width: 769px) and (max-width: 1200px) {
	html:not([class*="layout-"]) #layout > div#layout-sidebar {
		display: none !important;
	}
}

/* ⚠ AND THE SAME FRAME PAINTS A LONE CHEVRON, which is the second artifact
 * this section exists for and the one that only shows on a TABLET.
 *
 * These four buttons are Elastic's small-screen navigation -- back to the
 * list, back to the folders, and the two menu buttons -- and every one of them
 * is a bare glyph with no label. ui.js decides whether they belong on screen
 * from the width it measures at `rcmail:init`; above 768px the answer is no.
 * Until it runs they are ordinary flex children of a column head, so the
 * unclassified frame paints, for example, `#layout-content`'s back-chevron
 * (\f053) in the middle of a tablet screen at x=286 -- measured at 834x1112 --
 * and then takes it away again. No label, no box, nothing to identify it: just
 * a chevron in the middle of the page, on roughly every fifth task switch.
 *
 * ⚠ `visibility`, NOT `display`. quantum.js measures the head's content box to
 * decide what fits behind the ⋯; removing children from the flow would make it
 * measure a row that is about to change, which is the bug §14 is about.
 * ⚠ ONLY ABOVE 768px. Below that these buttons are exactly what ui.js DOES
 * show, and the columns carrying them are already hidden by the block above --
 * hiding them here would be a flash in the other direction. */
@media screen and (min-width: 769px) {
	html:not([class*="layout-"]) #layout > div > .header > a.back-list-button,
	html:not([class*="layout-"]) #layout > div > .header > a.back-sidebar-button,
	html:not([class*="layout-"]) #layout > div > .searchbar > a.button.options {
		visibility: hidden;
	}
}

/* ⚠ AND THE OVERFLOW BUTTONS, AT EVERY WIDTH -- reported on a phone as "a
 * more button in the middle of the screen when switching between mail and
 * contacts".
 *
 * There are TWO ⋯ in the list head before anything runs: Elastic's
 * `a.toolbar-list-button`, built by `toolbar_init()`, and this skin's own
 * `.quantum-more`. Exactly one of them survives -- `oneMore()` in quantum.js
 * marks the redundant one `.quantum-dup` -- but which one is not decided until
 * that pass runs, so the unclassified frame paints both, 44px apart, and then
 * one of them disappears from a spot that afterwards holds nothing.
 *
 * ⚠ §14 ALREADY HIDES `.quantum-more`, and is not enough. `quantum-boot` is
 * cleared when quantum.js STARTS, while ui.js rebuilds the head toolbars later
 * on `rcmail:init` -- so there is a second window after the boot class is
 * gone. This one is keyed on ui.js's own class and closes exactly when it
 * lands.
 *
 * ⚠ THIS ONE DOES HIDE SOMETHING THAT COMES BACK, unlike the rest of §2d, and
 * that is the lesser of the two: the survivor appears in the position it
 * keeps, where a vanishing twin leaves a control blinking out of empty
 * space. */
html:not([class*="layout-"]) #layout > div > .header > a.toolbar-list-button,
html:not([class*="layout-"]) #layout > div > .header > a.toolbar-menu-button,
html:not([class*="layout-"]) .quantum-more {
	visibility: hidden;
}

/* ⚠ AND THE WHOLE HEAD TOOLBAR, below 769px. Found by the differential sweep
 * rather than by anyone noticing it: on compose at 390-768px the entire
 * `ul#toolbar-menu` -- Speichern, Anhang, Signatur, 210 to 436px of buttons --
 * paints at y=60 in the unclassified frame and is gone once ui.js has moved it
 * behind the menu button. §14 already hides it, but only while `quantum-boot`
 * is set, and that class is cleared when quantum.js STARTS while ui.js does its
 * work later. Same window, same `visibility`, same reason. */
@media screen and (max-width: 768px) {
	html:not([class*="layout-"]) #layout > div > .header
		.toolbar:not(.popupmenu):not(.quantum-morepanel) {
		visibility: hidden;
	}
}

/* ⚠ AND THE SAME FRAME PAINTS A LONE CHEVRON, which is the second artifact
 * this section exists for and the one that only shows on a TABLET.
 *
 * These four buttons are Elastic's small-screen navigation -- back to the
 * list, back to the folders, and the two menu buttons -- and every one of them
 * is a bare glyph with no label. ui.js decides whether they belong on screen
 * from the width it measures at `rcmail:init`; above 768px the answer is no.
 * Until it runs they are ordinary flex children of a column head, so the
 * unclassified frame paints, for example, `#layout-content`'s back-chevron
 * (\f053) in the middle of a tablet screen at x=286 -- measured at 834x1112 --
 * and then takes it away again. No label, no box, nothing to identify it: just
 * a chevron in the middle of the page, on roughly every fifth task switch.
 *
 * ⚠ `visibility`, NOT `display`. quantum.js measures the head's content box to
 * decide what fits behind the ⋯; removing children from the flow would make it
 * measure a row that is about to change, which is the bug §14 is about.
 * ⚠ ONLY ABOVE 768px. Below that these buttons are exactly what ui.js DOES
 * show, and the columns carrying them are already hidden by the block above --
 * hiding them here would be a flash in the other direction. */

/* --------------------------- 8c. compose and the account, swapped over */
/*
 * quantum.js moves two elements to where the app keeps them: "Schreiben" into
 * the folder column's head, and the signed-in address into the header bar.
 * The rules below dress them for their new homes. Each is keyed on WHERE the
 * element is -- `#layout-sidebar > .header .action-buttons`, `#taskmenu >
 * .header-title.username` -- so it applies exactly when the move has happened
 * and never depends on a class arriving in time.
 */
/* ⚠ HIDDEN UNTIL MOVED -- this is what stops the flicker.
 *
 * The swap is a DOM move, so without this both elements paint once in
 * Elastic's positions and then jump: compose flashed in the bar on every task
 * switch, and outside mail it appeared and vanished again. Gating on a class
 * the script adds does not help -- the class lands after the paint that showed
 * the flash.
 *
 * Keying on WHERE THE ELEMENT IS removes the timing question altogether: in
 * the bar compose is invisible, in the folder column it is shown, and no
 * intermediate state is ever painted. The address is the same rule reversed.
 * It also settles "compose only in mail" for free -- outside mail the script
 * simply leaves it in the bar, where it is hidden.
 *
 * The cost: with quantum.js broken, compose is unreachable on desktop. That is
 * the deliberate trade -- Settings stays reachable regardless (§2b), and a
 * button that flashes on every page load is a defect seen every time.
 *
 * ⚠ 1025 IS §2c's WIDTH, and the overlap is harmless rather than accidental.
 * On a coarse-pointer tablet §2c's band runs past this one (to 1279) and both
 * apply -- but §2c hides the bar's compose with !important and the folder
 * column's copy is shown by an unmedia'd rule below, so the two agree on every
 * width where they meet.
 */
@media screen and (min-width: 1025px) {
	#layout-menu #taskmenu .action-buttons {
		display: none;
	}

	#layout-sidebar > .header .action-buttons {
		display: flex;
	}

	#layout-sidebar > .header > .header-title.username {
		display: none;
	}
}

#layout-sidebar > .header .action-buttons {
	display: flex;
	align-items: center;
	flex: 1 1 auto;
	min-width: 0;
}

/* The app's "Neue E-Mail": the full width of the folder column, flat compose
   fill, label beside the icon. */
/* ⚠ SYMMETRIC 40px OF PADDING, not 14. The folder panel's ⋯ is lifted out of
   the flex row and laid over this button's right end (below), so the button
   can take the column's full width; the label stays optically centred only if
   the space the ⋯ occupies is matched on the left. */
#layout-sidebar > .header .action-buttons a.compose {
	display: inline-flex;
	flex-direction: row;
	align-items: center;
	justify-content: center;
	gap: 7px;
	flex: 1 1 auto;
	width: auto;
	max-width: none;
	min-width: 0;
	height: 40px;
	padding: 0 40px;
	border-radius: 9px;
	background: var(--compose-btn, #17293a);
	color: var(--compose-btn-fg, #fff);
	font-size: 13px;
	font-weight: 500;
	overflow: visible;
}

#layout-sidebar > .header .action-buttons a.compose:before,
#layout-sidebar > .header .action-buttons a.compose span.inner {
	color: var(--compose-btn-fg, #fff);
	display: inline;
	font-size: 13px;
	font-weight: 500;
	line-height: 1;
	max-width: none;
	width: auto;
	overflow: visible;
	text-overflow: clip;
	white-space: nowrap;
}

#layout-sidebar > .header .action-buttons a.compose:before {
	font-size: 19px;
	height: 19px !important;
	line-height: 19px;
	width: auto !important;
	float: none !important;
	margin: 0 !important;
}

/* ---- the folder panel's ⋯, inside the compose button --------------------
 * ⚠ ABSOLUTE, so compose can be the full width of the column. Left in the flex
 * row the two shared the head and compose stopped 40px short of the panel's
 * right edge -- the one control the column exists for, sized by what sits
 * beside it. Out of the flow it takes the whole width, exactly as it does
 * below the desktop (§2c), and the ⋯ sits at its right end.
 * ⚠ AND IT IS THEN ON A DARK FILL, so it takes the compose foreground colour
 * rather than §8g's `--muted`: the same grey that reads on a panel disappears
 * on `--compose-btn` in every theme. Its hover is a translucent white for the
 * same reason -- `--hover` is a panel tint and would show as a light block. */
#layout > div#layout-sidebar > .header > a.sidebar-menu {
	position: absolute;
	right: 12px;
	top: 50%;
	transform: translateY(-50%);
	z-index: 2;
	margin: 0;
	background: transparent !important;
	color: var(--compose-btn-fg, #fff) !important;
}

#layout > div#layout-sidebar > .header > a.sidebar-menu:before {
	color: var(--compose-btn-fg, #fff);
}

#layout > div#layout-sidebar > .header > a.sidebar-menu:hover {
	background: rgba(255, 255, 255, .16) !important;
}

/* In the bar the address is a quiet label sitting to the right of the tasks,
   not a column title -- so it drops the title type and the flex:1 that would
   otherwise push About/Logout off the edge. */
/* ⚠ Centring the task links used to be free: .action-buttons carried
   `margin-right: auto` and did it as a side effect. Moving compose out took
   that with it and the tasks slid to the left edge, against the logo. Two auto
   margins -- one before the first task, one before the address -- split the
   free space evenly again, which is the app's three-zone header
   (.mf-headleft and .mf-topright both flex:1 1 0). */
/* ------------------------------- 8e. the wordmark ------------------------
 * The "ARMIN SCHNEIDER .COM | Quantum Mail" lockup, one file per theme.
 *
 * ⚠ THE TWELVE FILES ARE ONE PICTURE IN TWELVE COLOURS. They are generated
 * from a single source -- same monogram, same type, same viewport -- with only
 * the paint colour changing, taken from that theme's own `--wordmark` token in
 * styles/tokens.css. Every file is 834x245 with the same ink box, so one
 * `background-size` gives one lockup on all twelve and the bar cannot shift
 * with the colour scheme. `test_wordmark_geometry` fails the build if a file
 * ever differs; the render half measures the painted box per theme.
 *
 * ⚠ Painted as a background on the block, with Roundcube's own <img> hidden.
 * The <img>'s src is written by the `logo` template object and cannot be
 * changed per theme from a stylesheet; a background-image can, and it swaps
 * with data-theme like every other themed value. */
#layout-menu .popover-header {
	display: flex !important;
	/* ⚠ Out of the flow, like the buttons on the right. A 190px wordmark taking
	   flow space at the left pushes #taskmenu's centre 95px off the bar's --
	   measured, at every width. Both side zones have to be absolute for the
	   middle to sit on the bar's true centre. */
	position: absolute;
	left: 16px;
	top: 0;
	bottom: 0;
	width: 330px;          /* logo + divider + "Quantum Mail" */
	height: auto;
	padding: 0;
	align-items: center;
	/* ⚠ `background-color`, never the `background` SHORTHAND. Elastic sets
	   `background-color: … !important` here, so the reset has to be important
	   too -- but an important shorthand also resets background-size to `auto`
	   at that same weight, which then outranks the longhand below and the
	   1006x330 lockup rendered at full size, clipped to a corner. */
	background-color: transparent !important;
	background-image: url('../images/armin-logo-navy.png') !important;
	background-repeat: no-repeat !important;
	background-position: left center !important;
	/* ⚠ SIZED BY WIDTH, not by height, and ONE width for every theme.
	   `auto var(--logo-h)` cannot be used: the app's own --logo-h is 30px on
	   nine themes and 32px on the three dark ones, so a height-sized mark came
	   out a different WIDTH depending on the colour scheme, and the gap to the
	   divider moved with it. That was the visible inconsistency in the old
	   branding -- green against the rest.
	   One width, and every file has the same aspect (834x245 = 3.404), so all
	   twelve paint the same box: 92px wide, 27.0px tall. Change this and
	   change `.quantum-brand`'s margin-left below -- they are one measurement
	   in two places. */
	background-size: 92px auto !important;
}

#layout-menu .popover-header img,
#layout-menu .popover-header a {
	display: none !important;
}

/* The rest of the lockup, built by quantum.js: LOGO | Quantum Mail.
   .mf-brand-divider and .mf-brand-name in the app, same values. */
/* ⚠ The offset lives on the BRAND, not as padding on the block. The logo is a
   background image, and `background-position: left` is resolved against the
   padding box -- padding does not push it. Padding on the parent therefore
   moved the text and left the logo where it was, which is how "Quantum Mail"
   came to be printed straight over the lockup. A margin on the text does what
   was meant.
   ⚠ ONE NUMBER, for every theme. The lead used to be measured per geometry
   from each file's alpha box, which meant the dark themes carried a different
   value and the spacing around the divider changed with the colour scheme.
   The images are trimmed to their ink and painted at a fixed 92px width
   (above), so the mark stops at 92px on all twelve.
   ⚠ 92 + 14, and the `gap` below is 14 too. An earlier cut ran 14 before the
   rule and 10 after it on the theory that a dense two-line lockup needs more
   air than a single large word; measured side by side it just read as two
   different gaps. Equal is equal. */
.quantum-brand {
	display: flex;
	align-items: center;
	/* ⚠ 11px against the 14px on the logo side, and that IS the balanced pair.
	   The rule has a dense two-line lockup on its left and a single large word
	   set in a heavier face on its right, so the word crowds the rule harder
	   than the small caps do -- measured equal at 14/14, it read wider on the
	   text side. Optical, not geometric; the numbers are meant to differ. */
	gap: 11px;
	margin-left: 106px;
}

.quantum-brand-divider {
	width: 1px;
	height: 24px;
	flex: none;
	background: var(--brand-divider, rgba(0, 0, 0, .2));
}

.quantum-brand-name {
	font-weight: 500;
	font-size: 19px;
	letter-spacing: .04em;
	color: var(--text-strong, #111);
	white-space: nowrap;
	/* Shrinks and ellipsises rather than pushing into whatever is beside it.
	   ⚠ `min-width: 0` on the flex parent too, or a nowrap child refuses to
	   shrink below its content and the ellipsis never happens. */
	overflow: hidden;
	text-overflow: ellipsis;
	min-width: 0;
}

/* ⚠ "Quantum" is FLAT, in --qm-word.
   It used to take --qm-grad clipped to the glyphs, as the app does. That
   needs `-webkit-text-fill-color: transparent`, and BOTH of those have to go
   together: leave the transparent fill behind and the word disappears
   entirely, because a solid `color` never reaches the glyphs.
   ⚠ --qm-word, NOT --wordmark, and the extra token is not ceremony. On eleven
   themes the two are the same value -- the product name is drawn in the same
   ink as the lockup beside it, and the brand reads as one thing. The GREEN
   theme is the one where the lockup's colour is not the theme's colour: its
   --wordmark is #17293a navy, which lands beside "Mail" at --text-strong
   #111111. Two near-blacks that differ by just enough to look like a paint
   bug rather than a brand. So green alone points --qm-word at #78be20, the
   theme's --brand -- the selected-folder green (§4a) and the middle stop of
   the --qm-grad this used to be clipped to.
   ⚠ THAT VALUE IS 1.91:1 ON THE --gap BAR, and 19px/500 is not WCAG "large",
   so it is under AA. Chosen deliberately after the number was measured and
   put in front of the owner: it is the house green and this is the one place
   the brand outranks the ratio. Everything else on the bar stays above 4.5,
   and `test_wordmark_colour` pins both the value and the exception so neither
   can drift silently. Do not copy this to a second element. */
.quantum-brand-name b {
	font-weight: 500;
	color: var(--qm-word, var(--wordmark, var(--text-strong, #111)));
}

html:where([data-theme="access-it"]) #layout-menu .popover-header {
	background-image: url('../images/armin-logo-access-it.png') !important;
}
html:where([data-theme="q-mail"]) #layout-menu .popover-header {
	background-image: url('../images/armin-logo-q-mail.png') !important;
}
html:where([data-theme="violet"]) #layout-menu .popover-header {
	background-image: url('../images/armin-logo-violet.png') !important;
}
html:where([data-theme="lightblue"]) #layout-menu .popover-header {
	background-image: url('../images/armin-logo-lightblue.png') !important;
}
html:where([data-theme="rot"]) #layout-menu .popover-header {
	background-image: url('../images/armin-logo-rot.png') !important;
}
html:where([data-theme="schiefergrau"]) #layout-menu .popover-header {
	background-image: url('../images/armin-logo-schiefergrau.png') !important;
}
html:where([data-theme="bernstein"]) #layout-menu .popover-header {
	background-image: url('../images/armin-logo-bernstein.png') !important;
}
html:where([data-theme="petrol"]) #layout-menu .popover-header {
	background-image: url('../images/armin-logo-petrol.png') !important;
}
html:where([data-theme="indigo"]) #layout-menu .popover-header {
	background-image: url('../images/armin-logo-indigo.png') !important;
}
html:where([data-theme="enzian"]) #layout-menu .popover-header {
	background-image: url('../images/armin-logo-enzian.png') !important;
}
html:where([data-theme="enzian-nacht"]) #layout-menu .popover-header {
	background-image: url('../images/armin-logo-enzian-nacht.png') !important;
}

/* ⚠ The signed-in address is not shown in the bar (item 4). Hidden wherever
   it sits, so it cannot reappear if the swap ever puts it back. */
#layout-menu #taskmenu > .header-title.username,
#layout-sidebar > .header > .header-title.username {
	display: none !important;
}

/* ⚠ THREE ZONES, and the task links must sit on the BAR's centre -- not
   halfway between whatever happens to flank them.
   Two earlier attempts failed on the same point. Auto margins split the FREE
   space evenly, which centres the tasks between two zones of different widths
   (measured 280px off). Matching `flex: 1 1 0` on both sides works only while
   both sides EXIST -- and they do not: Kontakte and Einstellungen have no
   address to show, so the right zone alone would have absorbed the slack and
   shoved the tasks against the left edge.
   Taking both side zones out of the flow removes the dependency entirely:
   whatever they contain, `justify-content: center` centres the middle on the
   bar. The widths are capped so a long address cannot reach the tasks. */
#layout-menu #taskmenu {
	position: relative;
}

/* ⚠ Two ids in the prefix. §2 pins this with `#layout-menu #taskmenu
   .special-buttons { position: static }` to undo Elastic's bottom-left
   anchoring, and a body class cannot outrank a second id -- so `position:
   absolute` was dropped while `transform: translateY(-50%)` still applied,
   lifting About/Logout ten pixels out of the top of the bar. */
#layout-menu #taskmenu > .special-buttons {
	position: absolute;
	/* ⚠ `left: auto` and `bottom: auto` are not tidying-up. Elastic anchors this
	   to the rail's bottom-left with `position:absolute; bottom:0; left:0`;
	   §2 neutralised that by forcing `position: static`, which also made those
	   offsets inert. Turning absolute positioning back ON revives them, and
	   left:0 together with this rule's right:0 stretched the box across the
	   whole bar -- an invisible 1368px slab lying over every task link,
	   Settings included. */
	left: auto;
	bottom: auto;
	right: 0;
	top: 50%;
	transform: translateY(-50%);
	margin-left: 0;
}

/* ⚠ The sidebar head shows the logged-in address, and Elastic gives it the
   same 19px/700 as a section title -- at which size a full address dominates
   the column. It is a label, so it takes the label type from the top bar. */
#layout > div > .header .header-title.username {
	font-size: 13.5px;
	font-weight: 500;
	line-height: 20px;
	color: var(--text, #222);
}

/* ⚠ A SELECTED ROW KEEPS ITS TEXT COLOUR; only its icon takes the theme.
 *
 * Elastic paints the whole selected row in the accent, which lands --accent on
 * --sel: 4.12:1 in the default theme, under the 4.5 small text needs, in the
 * one row the user looks at most. The app does not do that either --
 * `.mf-folder.active` keeps --text and tints only `.mf-folder-ic`.
 *
 * (Q-Mail's settings rail, `.mf-setnav-btn[aria-current]`, DOES colour its
 * label with --accent and carries the same 4.12:1. One readable treatment for
 * every rail was the better of the two, so this deviates from the app there,
 * deliberately.)
 */
#layout-sidebar .listing li.selected > a,
#layout-sidebar .listing li.selected > a span,
#layout-sidebar .listing tr.selected > td,
#layout-list .listing tr.selected > td,
#settings-menu li.selected > a {
	color: var(--text-strong, #111) !important;
	font-weight: 500;
}

#layout-sidebar .listing li.selected > a:before,
#layout-sidebar .listing tr.selected > td:before,
#settings-menu li.selected > a:before {
	color: var(--brand, #4f7f13) !important;
}

/* ------------------------------------------------- 4b. keyboard focus -----
 * ⚠ The skin repaints every button Elastic ships, and one of the things that
 * got repainted away was the focus cue: `#taskmenu a` sets a transparent
 * background, which is precisely what Elastic used to mark `a:focus`. Measured
 * across all twelve themes, a focused task button was indistinguishable from
 * an unfocused one -- in a mail client, which people drive from the keyboard.
 *
 * The app's own rule, copied exactly:
 *     :focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:6px}
 * :focus-visible rather than :focus, so a mouse click does not draw a ring.
 */
#layout-menu #taskmenu a:focus-visible,
#layout > div > .header a:focus-visible,
#layout > div > .header .toolbar a:focus-visible,
#layout-sidebar .listing a:focus-visible,
.quantum-more:focus-visible,
.quantum-swatch:focus-visible,
.quantum-morepanel a:focus-visible,
.popupmenu li > a:focus-visible,
.formbuttons .quantum-cancel:focus-visible {
	outline: 2px solid var(--accent, #4f7f13) !important;
	outline-offset: 2px;
	border-radius: 6px;
}

/* ------------------------------------------------- 5b. the folder rail */
/*
 * .mf-folder in the app: a 44px row with a 9px radius, a 16px icon and a 10px
 * gap to the name, selected by fill and weight. No rules between rows --
 * hover and selection do the separating.
 *
 * ⚠ `#layout-sidebar .listing li` alone is not enough. Elastic nests the
 * subtree as a <ul> INSIDE the parent <li>, and puts a rule on that <ul> as
 * well as on each <li> -- so expanding a folder drew a line the flat list
 * never showed. Both need clearing.
 */
#layout-sidebar .listing li,
#layout-sidebar .listing ul,
#layout-sidebar .listing ul li,
#layout-sidebar .treelist li,
#layout-sidebar .treelist ul,
#layout-sidebar .listing li > a,
#layout-sidebar .listing li > div {
	border-bottom: 0 !important;
	border-top: 0 !important;
}

/* .mf-tree-spacer is 18px wide and one is added per level, so a child sits
   exactly 18px in from its parent. */
#layout-sidebar .listing li ul {
	padding-left: 18px;
}

/* ------------------------------------------- 5c. the settings section list */
/* The settings sections are the app's .mf-setnav-btn: a plain row, selected
   by fill, with no rule under it. Roundcube renders them as a .listing, which
   carries Elastic's row borders. */
/* ⚠ `li` alone misses it. `sectionslist` renders as a TABLE
   (settings.html -> table.listing), and Elastic rules `.listing tbody td` as
   well as `.listing li` -- so the section rows kept their separators while the
   folder rail lost them. */
body.task-settings #layout-list .listing li,
body.task-settings #layout-list .listing li > a,
body.task-settings #layout-list .listing tbody td,
body.task-settings #layout-list .listing tbody tr,
body.task-settings #layout-list .listing ul {
	border-bottom: 0 !important;
	border-top: 0 !important;
}

/* ------------------------------------------------------------- 9. compose */

/* Send is the primary action of the whole screen, so it takes the theme's flat
   compose fill -- the same token the app's own send button uses. Elastic
   inherits Bootstrap's green .btn-primary, which belongs to no theme here. */
.formbuttons .btn-primary,
.formbuttons .btn-primary:hover,
.formbuttons .btn-primary:focus {
	background: var(--compose-btn, #17293a);
	border-color: transparent;
	color: var(--compose-btn-fg, #fff);
	border-radius: 10px;
	min-height: 40px;
	padding: 0 18px;
	font-weight: 500;
	box-shadow: none;
}

.formbuttons .btn-secondary,
.formbuttons .btn-secondary:hover {
	background: var(--panel, #fff);
	border: 1px solid var(--line, #ddd);
	color: var(--text, #222);
	border-radius: 10px;
	min-height: 40px;
	padding: 0 18px;
	font-weight: 500;
}

/* ⚠ `.formcontainer .formbuttons` too. Elastic pads the button row from the
   container (`padding: .5rem 1rem`), two classes to this one -- so the bare
   selector lost and Send started 6px left of the editor box above it.
   ⚠ AND NO RULE ABOVE THE ROW. Compose and the contact editor both draw their
   content as a panel with its own edge, so a line here was a second border
   3px under the first -- two separators for one boundary. The padding is the
   separation. */
.formbuttons,
.formcontainer .formbuttons {
	padding: 14px 20px;
	gap: 8px;
	border-top: 0;
}

/* ⚠ THE ADDON BOXES BESIDE AN INPUT, which the theme build reached HALF of.
 * `.input-group-text` is the little square at the end of a field -- the pencil
 * beside Von, the two recipient buttons beside An, the bin at the end of every
 * row in the contact editor. Elastic paints it
 *
 *     .input-group-text { color: #191919; background-color: #f1f3f4 }
 *
 * from two LESS variables, and only the first has a Q-Mail token behind it: the
 * per-declaration diff emitted `color` for all twelve themes and left the
 * background at its literal. So in the three dark themes the box kept a light
 * grey fill -- the white squares at the right of every field -- and, worse, it
 * then carried the theme's LIGHT text on it.
 *
 * ⚠ The same fill as the field it is attached to. Elastic gives the addon a
 * slightly different shade from the input; here the pair read as one control
 * with a hairline between them, which is what the app does. */
/* ⚠ `html`, NOT `#layout` -- THE CONTACT EDITOR IS A FRAME. addressbook.html
 * puts `contactedit.html` in an iframe, and layout.html emits `<div id=
 * "layout">` only when `!env:framed`: there is no #layout inside it. Scoped to
 * #layout these rules reached the compose screen (a full page) and missed the
 * one place the user was looking at -- the same trap §7b is about. `html` is a
 * type selector, so it also outranks Elastic's own bare `.input-group-text`
 * and Bootstrap's `.nav-tabs .nav-link.active` while matching in both. */
html .input-group-text,
html .input-group-append .input-group-text,
html .input-group-prepend .input-group-text {
	background-color: var(--search-bg, var(--panel, #fff));
	border-color: var(--line, #ddd);
	color: var(--muted, #666);
}

html .input-group-text:hover {
	background-color: var(--hover, #f3f3f6);
	color: var(--text, #222);
}

/* ⚠ BOOTSTRAP'S, NOT ELASTIC'S -- `deps/bootstrap.min.css` is loaded beside the
 * skin and is no part of the theme build at all, the same blind spot jQuery UI
 * has in §12. `.nav-tabs .nav-link.active { color: #495057; background-color:
 * #fff }` is why "Eigenschaften" came up as a white tab with near-black text in
 * the middle of a dark contact editor, beside two links that were themed. */
html .nav-tabs .nav-link {
	color: var(--muted, #666);
	background-color: transparent;
	border-color: transparent;
}

html .nav-tabs .nav-link.active {
	color: var(--text-strong, #111);
	background-color: var(--panel, #fff);
	border-color: var(--line, #ddd) var(--line, #ddd) var(--panel, #fff);
}

html .nav-tabs {
	border-bottom-color: var(--line, #ddd);
}

/* ⚠ `width: 8rem` is Elastic's, and 8rem is 128px: "Feld hinzufügen" needs 150
 * and came out as "Feld hinzufüg". A control that names an action has to be as
 * wide as the name in whatever language it is read in, so the floor stays and
 * the width follows the text. */
html .propform .addfield select,
html .addfield select {
	width: auto;
	min-width: 8rem;
	max-width: 100%;
}

/* Inputs across compose, contacts and settings share one shape. */
#layout .form-control,
#layout input.form-control,
#layout select.form-control,
#layout textarea.form-control {
	border-radius: 8px;
	border: 1px solid var(--line, #ddd);
	background: var(--search-bg, var(--panel, #fff));
	color: var(--text, #222);
	font-size: 13.5px;
}

#layout .form-control:focus {
	border-color: var(--accent, #4a3fbf);
	box-shadow: none;
}

/* ------ compose: THE MESSAGE TAKES THE ROOM ------------------------------
 *
 * ⚠ NOTHING SIZES THIS. Elastic gives `#composebodycontainer` no rules at all;
 * the textarea is `rows="20"` and TinyMCE is started with `min_height: 400`
 * and no height, so the editor is ~400px whatever the window is. Measured on a
 * 1400x850 desktop: a 727px column holding 509px of form, with the button row
 * at 556 and 218px of bare panel under it. That band is the "wasted space",
 * and the space it wastes belongs to the message.
 *
 * The column becomes a flex column, the form takes the slack, and inside the
 * form the slack goes to the body rather than to a gap above the buttons.
 *
 * ⚠ `height: auto !important` on `.tox-tinymce`: TinyMCE writes its computed
 * height INLINE, so nothing without weight can hand the box back to flex. Its
 * own inside is already a flex column (toolbar, then `.tox-sidebar-wrap` at
 * `flex: 1`), so the edit area follows on its own once the outer box has a
 * height to give.
 *
 * ⚠ At EVERY width. This started as a phone fix and was scoped to the
 * small-screen block, which is how a 218px band survived on the desktop the
 * whole time. */
#layout > div > .content.formcontainer {
	display: flex;
	flex-direction: column;
}

#layout > div > .content.formcontainer > form,
#layout > div > .content.formcontainer > .formcontent {
	flex: 1 1 auto;
}

body.action-compose #layout > div > .content.formcontainer > form.formcontent {
	display: flex;
	flex-direction: column;
}

body.action-compose #composebodycontainer {
	display: flex;
	flex-direction: column;
	flex: 1 1 auto;
	min-height: 0;
}

body.action-compose #composebodycontainer > div.tox.tox-tinymce,
body.action-compose #composebodycontainer > textarea#composebody {
	flex: 1 1 auto;
	min-height: 0;
	height: auto !important;
}

/* ------ compose: ONE LEFT EDGE ------------------------------------------
 * The toolbar's first button, the From/To/Subject labels, the editor box and
 * the Send/Cancel pair are four separate mechanisms that all have to start at
 * the same x, and Elastic gives each of them a different one: Bootstrap's grid
 * pulls the labels 15px left of their container, `.header`'s own padding puts
 * the toolbar's icon 4px right of the content, and only the body and the
 * buttons ever agreed. 20px from the column edge, stated four times, because
 * there is no single box to state it once on. */
/* ⚠ 4px ABOVE, not 14. The column head already ends in its own padding, so a
   second 14px lead under it put the From row 14px below a line that was
   already clear -- the gap at the top of every compose screen. The other three
   sides are what the note above is about and are unchanged.
   ⚠ And it is stated HERE rather than in a rule of its own: this selector is
   (1,1,0) and later in the file than any `#compose-content .compose-headers`,
   so a second rule aimed at the same box would simply lose. */
#compose-headers .compose-headers {
	padding: 4px 20px;
}

/* ⚠ Bootstrap's gutter, cancelled. `.row` carries `margin: 0 -15px` and each
   `.col-*` pays it back as padding -- which nets to zero only when both are
   in play. Roundcube's compose rows lost the column half somewhere in the
   cascade, so the label sat 15px LEFT of the padding that was meant to place
   it, i.e. left of everything else in the column. */
#compose-headers .compose-headers > .row,
#compose-headers .compose-headers > .form-group.row {
	margin-left: 0;
	margin-right: 0;
}

#compose-headers .compose-headers > .row > [class*="col-"] {
	padding-left: 0;
}

/* ⚠ AND THE FORM'S OWN 1rem, which is where the last 14px came from.
   compose.html closes `</form>` after #composebodycontainer, so the headers
   and the editor are both INSIDE `composeFormHead`'s
   `<form class="formcontent scroller">` -- and Elastic pads every
   `.formcontent:not(.popupmenu)` by 1rem. `.formbuttons` is outside that form,
   which is why Send and the toolbar looked right while the two things between
   them sat 14px further in. Each of the two supplies its own 20px below, so
   the wrapper supplies none. */
body.action-compose #compose-content > .formcontent,
body.action-compose #compose-content > form.formcontent {
	padding: 0;
}

/* The head's own padding is what puts the first button's ICON 4px right of the
   content edge: 12px of header plus 12px of button against the content's 20.
   Pulled back by exactly that difference, on compose only -- the other column
   heads front a list, not a form, and have nothing to line up with. */
body.action-compose #layout > div#layout-content > .header {
	padding-left: 8px;
}

/* The options column is the same problem on the other side: §3 indents a
   sidebar heading by 33px so it lines up with the icon list underneath it, and
   on compose there is no list -- just the upload block. 8px puts
   "Optionen und Anhänge" on the same 20px edge as everything below it. */
body.action-compose #layout > div#layout-sidebar > .header .header-title {
	padding-left: 8px;
}

body.action-compose #layout-sidebar > .scroller {
	padding: 0 20px;
}

/* ⚠ `#compose-options` is a `.formcontent`, and Elastic pads every
   `.formcontent:not(.popupmenu)` by 1rem -- so "Return receipt", "Priority"
   and the rest sat 14px right of the drop zone above them, which is the same
   1rem that pushed the compose headers in. The column states its 20px once,
   on the scroller. */
body.action-compose #layout-sidebar .formcontent {
	padding: 0;
}

body.action-compose #layout-sidebar .formcontent .row {
	margin-left: 0;
	margin-right: 0;
}

body.action-compose #layout-sidebar .formcontent .row > [class*="col-"] {
	padding-left: 0;
}

/* ⚠ A MARGIN, not padding: Elastic gives `.file-upload` `margin: 0 1rem`, so
   zeroing its padding moved nothing. */
body.action-compose #layout-sidebar > .scroller > #compose-attachments,
body.action-compose #layout-sidebar > .scroller .file-upload {
	margin-left: 0;
	margin-right: 0;
	padding-left: 0;
	padding-right: 0;
}

#compose-headers .col-form-label,
#compose-options .col-form-label {
	font-size: 13px;
	font-weight: 500;
	color: var(--muted, #666);
}

#composebodycontainer {
	padding: 6px 20px 0;
}

#composebodycontainer #composebody {
	border-radius: 10px;
	min-height: 320px;
	padding: 12px 14px;
	line-height: 1.55;
}

#compose-attachments .upload-form .btn,
.file-upload .btn-secondary {
	background: var(--panel, #fff);
	border: 1px solid var(--line, #ddd);
	color: var(--text, #222);
	border-radius: 8px;
	font-weight: 500;
}

/* ------------------------------------------------------------ 10. contacts */

.contact-header {
	display: flex;
	align-items: center;
	gap: 14px;
	padding: 18px 20px;
	border-bottom: 1px solid var(--line-2, #eee);
}

/* ⚠ `min-width` TOO, or the circle is an ellipse. Elastic ships
   `.contact-header .contact-photo { min-width: 112px }` for its own 112x135
   portrait, which out-argues a 56px `width` -- the box came out 112 wide and 56
   tall, and `border-radius: 50%` on a box like that draws exactly the oval that
   was reported. A radius of 50% is only a circle on a square. */
.contact-photo,
#contactphoto {
	width: 56px;
	height: 56px;
	min-width: 56px;
	max-width: 56px;
	flex: none;
	border-radius: 50%;
	overflow: hidden;
	background: var(--sel, #eceaf7);
}

/* The editor nests an upload widget in there: fieldset#contactphoto >
   #contactpic.image-upload > img. Elastic sizes that inner box 112x135 and
   gives it a `background-color: #f4f4f4` literal -- another light square in a
   dark theme, and this one clipped to an oval. */
.contact-photo #contactpic,
#contactphoto #contactpic {
	width: 100%;
	height: 100%;
	min-width: 0;
	max-width: none;
	border-radius: 50%;
	display: block;
}

.image-upload {
	background-color: var(--sel, #eceaf7);
}

.contact-photo img,
#contactphoto img {
	width: 100%;
	height: 100%;
	max-width: none;
	max-height: none;
	object-fit: cover;
}

.contact-head .namefield,
#contacthead .namefield {
	font-size: 19px;
	font-weight: 700;
	color: var(--text-strong, #111);
}

.contactlist tr.contact > td {
	padding: 9px 12px;
	border-bottom: 1px solid var(--line-2, #eee);
}

.contactlist tr.contact > td a {
	color: var(--text, #222);
	font-size: 13.5px;
}

.contactlist tr.contact:hover > td {
	background: var(--hover, #f5f5f8);
}

/* ⚠ SQUARE, LIKE THE MESSAGE LIST. A selected contact used to be a rounded
   block while a selected message was a full-bleed band -- two lists in one
   app, marked two ways, and the radius turned the 2px theme marker on its left
   edge into a bracket. The message list is the one people look at all day, so
   it is the one the contact list follows. */
.contactlist tr.contact.selected > td {
	background: var(--sel, #eceaf7);
	border-radius: 0;
	border-bottom-color: transparent;
}

.contactlist tr.contact.selected > td a {
	color: var(--text-strong, #111);
	font-weight: 500;
}

/* ------------------------------------------------------------ 11. settings */

#layout .formcontent fieldset.propform,
#layout #contacttabs fieldset.propform {
	border: 0;
	padding: 16px 20px;
	margin: 0;
}

#layout .formcontent fieldset.propform legend,
#layout #contacttabs fieldset.propform legend {
	font-size: 13px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: .04em;
	color: var(--muted, #666);
	padding: 0 0 8px;
	border: 0;
	width: auto;
}

#layout .formcontent .row.form-group {
	margin-bottom: 12px;
	align-items: center;
}

#layout .formcontent .col-form-label {
	font-size: 13.5px;
	color: var(--text, #222);
}

/* ⚠ `#contacttabs` as well. A read-only contact field is a
   `.form-control-plaintext`, which is BOOTSTRAP's class carrying a hardcoded
   #212529 -- dark grey on a dark panel. The contact card is normally framed
   (§7b catches it there), but it is rendered in the page in an ext-window. */
#layout .formcontent .form-control-plaintext,
#layout #contacttabs .form-control-plaintext {
	font-size: 13.5px;
	color: var(--text, #222);
	padding: 0;
}


/* ==========================================================================
 * ⚠ THE SMALL-SCREEN BLOCKS LIVE AT THE END OF THIS FILE, NOT BESIDE §2.
 *
 * They override §8e, which places the wordmark and centres the task row with
 * UNMEDIA'D rules on the same two selectors -- `#layout-menu .popover-header`
 * and `#layout-menu #taskmenu`. Same specificity, so the later rule wins, and
 * written up beside §2 these lost every declaration §8e also sets: the logo
 * stayed absolute and 330px wide, and the tab row never left the top of the
 * bar. Reading order here IS the cascade.
 * ========================================================================== */

/* --------------------------------------- 2c. below desktop: the app's own UX
 *
 * ⚠ THE BREAKPOINTS ARE Q-MAIL'S, NOT ELASTIC'S, because what is being copied
 * here is the app's responsive design and not Elastic's. From
 * `Webmail/assets/style.css`:
 *
 *     <= 1279   .mf-appnav hidden, .mf-tabbar shown, ☰ opens the folder pane
 *     <= 1023   two columns: the message replaces the list
 *     <=  640   .mf-topbar 52px, .mf-brand-name 16px
 *     <=  600   .mf-brand img and .mf-brand-divider hidden -- wordmark only
 *
 * So the task links leave the header for a bottom tab bar well before the phone
 * widths: the app treats tablet and phone as one design, and an earlier cut of
 * this section that only changed at 480px left every tablet looking like a
 * squeezed desktop. Elastic's own numbers (480/768/1200) still govern Elastic's
 * own behaviour -- its more-buttons and its column hiding -- and are matched
 * where this file talks to those, in §8g and in quantum.js.
 *
 * ⚠ 1024 AND A COARSE POINTER, NOT A BARE 1280, and the difference is a
 * SUPPORT REPORT. The app's 1279 was taken at face value here, and 1280 CSS
 * pixels is not a tablet number -- it is what a 1920px 13" laptop reports at
 * Windows' 150% display scaling, which is the default that ships on that class
 * of machine. Sitting exactly ON the boundary, anything that took a few pixels
 * more -- a browser zoom of 110%, a window not quite maximised, 175% scaling --
 * dropped a full laptop into the phone design: a 52px bar, a bottom tab row and
 * the folders behind a hamburger, on a screen with room for all three columns.
 * Reported by a user on a 13" laptop who had never seen the desktop layout.
 *
 * So the band is stated twice:
 *
 *     max-width: 1024px                    every window narrow enough that the
 *                                          desktop bar is worth giving up --
 *                                          Elastic's own `touch` threshold, one
 *                                          pixel past Q-Mail's <= 1023, and an
 *                                          iPad in landscape to the pixel
 *     <= 1279px AND (pointer: coarse)      a REAL tablet held in landscape --
 *                                          an 11" iPad is 1180-1194 CSS px --
 *                                          which still wants the app's design
 *
 * `pointer: coarse` is the primary pointing device, so a touchscreen laptop
 * with a trackpad reports `fine` and keeps the desktop; a keyboard-less Surface
 * reports `coarse` and keeps the tab bar. The width alone cannot tell those two
 * apart, which is exactly why the single number was wrong.
 *
 * ⚠ 1025 IS NOT THE NARROWEST THE BAR FITS, and the margin is deliberate.
 * Measured at 1025 with a plugin's task in the row as well (lockup, four links,
 * About/Logout): 27px between the lockup and the first link, 89px between the
 * last and the pair. The links are centred, so each pixel of window is half a
 * pixel of that first gap -- it closes at about 971. The bar is therefore not
 * being asked to do anything marginal at the breakpoint, which is the whole
 * point of putting it above the widths where it would be. tests/skin.py walks
 * 1024 and 1025 and fails on a spill or an overlap at either.
 *
 * ⚠ DESKTOP HERE IS NOT ELASTIC'S `large`. Above 1024 this skin paints the
 * desktop bar, but Elastic keeps `layout-normal` up to 1200 -- list and content,
 * no folder column -- so between 1025 and 1200 the folders live behind the list
 * head's `back-sidebar-button`, which §8b dresses like any other header button
 * and the `min-width: 1201px` block in §8 deliberately does NOT hide. That is
 * the one thing to check when touching either number.
 *
 * ⚠ ui.js CLASSES, not just widths. `screen_resize_phone()` calls
 * `app_menu(false)`, which puts `hidden` on #layout-menu and, when the
 * hamburger opens it, `popover` -- a 272px drawer pinned to the left edge.
 * That is "the header bar moves to the left". Both are landed by JavaScript at
 * runtime, so no width query alone can undo them: they have to be named. */
@media screen and (max-width: 1024px),
       screen and (max-width: 1279px) and (pointer: coarse) {
	/* ⚠ ONE BAR HEIGHT FOR EVERY WIDTH BELOW THE DESKTOP -- .mf-topbar's 52px,
	   which used to start at 640 and leave tablets on a 60px bar. Everything
	   pinned into the bar is placed against this number, so it cannot be set in
	   one place and read in another.
	   ⚠ AND THE `.hidden` / `.popover` FORMS, or it never applies on a real
	   phone: `screen_resize_phone()` calls `app_menu(false)`, which puts
	   `hidden` on #layout-menu -- and `#layout-menu.hidden` is one class more
	   specific than a bare `#layout-menu`. */
	#layout-menu,
	#layout-menu.hidden,
	#layout-menu.popover {
		display: flex !important;
		position: fixed !important;
		top: 0;
		left: 0 !important;
		right: 0;
		bottom: auto !important;
		width: auto !important;
		height: 52px;
		padding: 0 8px;
		box-shadow: none;
		border: 0;
	}

	/* app_menu(true) drops a full-page scrim behind the drawer. There is no
	   drawer to dim any more. */
	#menu-overlay {
		display: none !important;
	}

	/* The hamburger's only job was opening that drawer. */
	#layout > div > .header > a.task-menu-button {
		display: none !important;
	}

	/* ⚠ ui.js BUILDS A FLOATING ACTION BUTTON below 769px -- it clones every
	   `[data-fab]` into `.floating-action-buttons` and drops it in the bottom
	   right of the list column. In mail that is a second "Neue E-Mail" on top
	   of the one at the head of the folder panel, and in Kontakte a second
	   "Erstellen" beside the one in the ⋯ menu. One action, one place. */
	#layout .floating-action-buttons {
		display: none !important;
	}

	/* ---- .mf-tabbar -------------------------------------------------------
	 * 58px, on --panel, one hairline above it, icon over a 10.5px label,
	 * --muted going --accent for the current tab. The app's values exactly.
	 *
	 * ⚠ The three zones are ONE element between them. #taskmenu holds the task
	 * links, `.action-buttons` (compose) and `.special-buttons` (about/logout)
	 * as siblings, and Roundcube binds its commands to those elements -- a copy
	 * in the bar would look right and do nothing. So the row moves to the
	 * bottom and the two groups are pinned back up into the header with
	 * `position: fixed`, which resolves against the viewport and not against
	 * their parent. No DOM surgery, and nothing to undo on the way back up. */
	#layout-menu #taskmenu {
		position: fixed;
		left: 0;
		right: 0;
		bottom: 0;
		top: auto;
		/* ⚠ THE TOP BAR'S HEIGHT, and that is the point: the two bars frame the
		   page, so they are the same 52px. `box-sizing` is what makes the
		   padding air UNDER the row rather than a shorter bar -- a bar that
		   ended exactly where its labels did read as if the page had been cut
		   off, and on a handset it put "Einstellungen" within a few pixels of
		   the bottom edge of the screen. 52 - 8 = a 44px row. */
		height: 52px;
		box-sizing: border-box;
		z-index: 44;
		background: var(--panel, #fff) !important;
		/* ⚠ NO TOP BORDER. The bar used to sit flush against the column above
		   it and needed a hairline to read as separate; it now floats on the
		   same 7px of page ground the columns keep to their left and right, and
		   a rule on top of that gap read as extra space -- the bottom gap
		   measured the same 7px as the sides and still looked bigger. */
		border-top: 0;
		justify-content: space-around;
		gap: 0;
		padding: 0 4px 8px;
	}

	/* ⚠ `> a`, not three named classes. menu.html carries
	   `<roundcube:container name="taskbar">` between Kontakte and
	   Einstellungen, and any plugin -- a calendar, notes, tasks -- drops its
	   own task link in there. Named individually, a fourth task fell out of
	   the tab row and rendered as a stray unstyled link. The two <span>
	   groups (.action-buttons, .special-buttons) are not `> a`, so they keep
	   their own treatment. */
	#layout-menu #taskmenu > a {
		flex: 1 1 0;
		min-width: 0;
		flex-direction: column;
		justify-content: center;
		gap: 3px;
		height: 44px;
		min-height: 44px;
		padding: 0 2px;
		border-radius: 0;
		background: transparent !important;
		color: var(--muted, #666);
		max-width: none;
	}

	#layout-menu #taskmenu > a .inner {
		display: inline;
		font-size: 10.5px;
		font-weight: 500;
		max-width: 100%;
		overflow: hidden;
		text-overflow: ellipsis;
	}

	/* .mf-tab.active -- the app marks the current tab with the accent, not
	   with a fill. */
	#layout-menu #taskmenu a.selected,
	#layout-menu #taskmenu a.selected:hover {
		color: var(--accent, #4a3fbf);
		background: transparent !important;
	}

	/* ⚠ COMPOSE IS NOT IN THE HEADER AT ANY WIDTH. quantum.js moves it to the
	   top of the folder column and leaves it there (see swapComposeAndAccount),
	   which is where the app keeps it and where the desktop has it -- so the
	   button does not change places when the window crosses a breakpoint. This
	   hides the empty span it leaves behind in the task row, and covers the
	   pages where the move does not happen (Kontakte, Einstellungen, and
	   compose itself, which offers no second "Neue E-Mail"). */
	#layout-menu #taskmenu .action-buttons {
		display: none !important;
	}

	/* ⚠ `bottom: auto` as well as `top`. Elastic pins .special-buttons to the
	   bottom of its vertical rail; left standing, top AND bottom both applied
	   and the pair stretched out of the header. */
	/* ⚠ `transform: none` as well. §8e centres this pair in the DESKTOP bar with
	   `top: 50%; transform: translateY(-50%)`, and that transform is not part of
	   what an overriding `top` replaces -- it kept applying to the fixed box
	   here and pulled About/Logout half their own height (20px) above the bar,
	   where the glyphs were clipped by the top of the window. Measured: icon
	   centre at y=7 in a 52px bar. */
	#layout-menu #taskmenu .special-buttons {
		position: fixed;
		top: 6px;
		bottom: auto;
		right: 4px;
		z-index: 21;
		margin: 0;
		height: 40px;
		align-items: center;
		transform: none;
	}

	/* ⚠ Icon-only, and the label has to be HIDDEN, not just unpadded. These
	   three are pinned into a 60px header beside a wordmark: "Schreiben",
	   "Über" and "Abmelden" need ~260px between them, so the labels ran off
	   the right edge and over each other at every width below about 900. */
	#layout-menu #taskmenu .action-buttons a,
	#layout-menu #taskmenu .special-buttons a {
		width: 44px;
		max-width: 44px;
		padding: 0;
		justify-content: center;
		overflow: hidden;
	}

	#layout-menu #taskmenu .action-buttons a .inner,
	#layout-menu #taskmenu .special-buttons a .inner {
		display: none;
	}

	/* ---- ☰ ---------------------------------------------------------------
	 * .mf-menu-toggle. Roundcube's own "show the folder column" button, which
	 * is what the app's hamburger does.
	 * ⚠ NOT `a.task-menu-button`: that one opens Elastic's app-menu drawer,
	 * the thing this section just did away with. Taken from the list head
	 * rather than invented, so `show_sidebar()` stays bound to it. */
	/* ⚠ ONE BOX, THREE BUTTONS, and they are three because Roundcube hides a
	 * different one in each state -- not because they look different. The
	 * list's own `back-sidebar-button` opens the folder panel; the sidebar's
	 * `back-list-button` closes it again (`show_sidebar()` hides the column
	 * the first one lives in, taking it with it); and with a message open both
	 * columns are gone and the CONTENT column's `back-list-button` takes the
	 * slot. Same 40x40 at the same fixed point, so the glyph never moves.
	 * ⚠ Written out three times until this merge, which is how they came to
	 * disagree about z-index for a while. The stacking order is the only real
	 * difference and it is stated once, below.
	 * ⚠ The content column's button is NOT in the `:before` list: it keeps
	 * Elastic's back arrow. A hamburger there would say "open the folders"
	 * while doing "return to the list".
	 * ⚠ `html.layout-small` / `html.layout-phone` on that third selector is
	 * scoping by ui.js's CLASS, not by this block's width, and it is the one
	 * rule in the phone set that could not simply move up with the rest:
	 * `screen_resize_normal()` (769-1200) shows list AND content together and
	 * hides that button with jQuery's inline `display: none`, which an
	 * `!important` here would override -- putting a second control in the same
	 * fixed box as the list's hamburger. */
	#layout > div > .header > a.back-sidebar-button,
	#layout-sidebar > .header > a.back-list-button,
	html.layout-small #layout > div#layout-content > .header > a.back-list-button,
	html.layout-phone #layout > div#layout-content > .header > a.back-list-button {
		display: inline-flex !important;
		position: fixed;
		top: 6px;
		left: 8px;
		z-index: 21;
		width: 40px;
		height: 40px;
		margin: 0;
		align-items: center;
		justify-content: center;
		border-radius: 9px;
		color: var(--bar-ic, #444);
	}

	/* Above the list's hamburger: both are painted at the same point, and the
	   one that closes the panel has to be the one that receives the click. */
	#layout-sidebar > .header > a.back-list-button,
	html.layout-small #layout > div#layout-content > .header > a.back-list-button,
	html.layout-phone #layout > div#layout-content > .header > a.back-list-button {
		z-index: 22;
	}

	#layout > div > .header > a.back-sidebar-button:hover,
	#layout-sidebar > .header > a.back-list-button:hover,
	html.layout-small #layout > div#layout-content > .header > a.back-list-button:hover,
	html.layout-phone #layout > div#layout-content > .header > a.back-list-button:hover {
		background: var(--bar-ic-hover, #eef0f4);
	}

	#layout > div > .header > a.back-sidebar-button span.inner,
	#layout-sidebar > .header > a.back-list-button span.inner,
	html.layout-small #layout > div#layout-content > .header > a.back-list-button span.inner,
	html.layout-phone #layout > div#layout-content > .header > a.back-list-button span.inner {
		display: none;
	}

	#layout > div > .header > a.back-sidebar-button:before,
	#layout-sidebar > .header > a.back-list-button:before {
		content: "\f0c9";
		font-size: 19px;
		margin: 0;
		float: none;
		width: auto;
		height: auto !important;
	}

	/* The header holds the ☰ now, so the lockup starts after it.
	   ⚠ `padding-left` alone, and `!important`. Elastic ships
	   `#layout-menu .popover-header { padding: 0 !important }` above 480px --
	   same specificity, so nothing without the same weight can reach it and
	   the wordmark printed straight over the hamburger. A LONGHAND, because an
	   important shorthand resets the other three sides at that weight too and
	   Elastic's own zero is the right value for them.
	   ⚠ The `html.layout-phone` twin is not belt and braces either: Elastic
	   also ships `html.layout-phone #layout-menu .popover-header
	   { justify-content: center; padding: 0 .5rem }` -- one element more, so it
	   wins on specificity wherever this sits. Matched selector for matched
	   selector, this one is later. */
	/* ⚠ THE RIGHT ZONE IS RESERVED, not assumed. Compose and About/Logout are
	   `position: fixed`, so they are out of the flow and the wordmark beside
	   them has no idea they are there -- a long enough lockup, or a narrow
	   enough phone, and the text simply runs underneath them. The padding is
	   the cluster's own width: 88 for the About/Logout pair at right:4, plus a
	   gap. (Compose used to be pinned here too and cost another 52; it lives in
	   the folder column at every width now.) */
	#layout-menu .popover-header,
	html.layout-phone #layout-menu .popover-header {
		position: static;
		flex: 1 1 auto;
		width: auto;
		min-width: 0;
		justify-content: flex-start;
		padding-right: 100px;
		padding-left: 44px !important;
		/* ⚠ …and the LOGO has to move with the padding. `background-position:
		   left` resolves against the PADDING box, so the mark stayed at x=8
		   and the hamburger was drawn straight on top of it while the text
		   beside it moved. `content-box` is what makes the padding mean
		   "after the ☰" for the image as well as for the words.
		   ⚠ !important, and NOT for weight against Elastic. §2 resets this
		   block with the `background` SHORTHAND at important weight
		   (`background: transparent !important`, needed against Elastic's own
		   important background-color) -- and a shorthand sets EVERY longhand it
		   covers, so it also lands `background-origin: padding-box` at that
		   weight. §8e re-states colour, image, repeat, position and size as
		   important longhands; origin was the one nobody re-stated, so the mark
		   kept painting from the padding edge while the words moved, and the
		   hamburger was drawn straight on top of the logo at every tablet
		   width. Same trap as background-size in §8e. */
		background-origin: content-box !important;
	}

	/* ⚠ 92 + 14, the same pair as the desktop rule in §8e -- change the
	   wordmark's `background-size` and BOTH margins move with it. */
	#layout-menu .quantum-brand {
		margin-left: 106px;
		min-width: 0;
	}

	/* Compose owns the whole folder-column head below the desktop, and every
	   width here is a touch width: 44px of target rather than the desktop 40. */
	#layout-sidebar > .header .action-buttons a.compose {
		height: 44px;
	}

	/* Both bars are out of the flow, so the columns owe both heights back.
	   ⚠ The tab bar's FULL height, padding included, PLUS the channel -- and
	   the channel is `#layout`'s own 7px side padding, the same gap the list
	   panel keeps to the left and right of the window. The columns used to stop
	   exactly where the bar started, so the list ran into the tab row with a
	   hairline between them and nothing else.
	   The top does not need it: the bar there paints the page ground itself
	   (§2), while the tab bar is a --panel surface and needs the ground shown
	   between the two. */
	#layout {
		padding-top: 52px;
		padding-bottom: 59px;
	}

	/* ⚠ THE ☰ HAS TO SURVIVE ITS OWN CLICK. It is Roundcube's
	 * `back-sidebar-button`, which lives in the LIST column's head -- and
	 * `show_sidebar()` hides that whole column, taking the button with it, so
	 * the hamburger vanished the moment it was pressed. The sidebar's own
	 * `back-list-button` is the mirror control (`show_list()`), so it is given
	 * the same box in the same place: one glyph that never moves and toggles
	 * the folder panel both ways.
	 * That also settles "no back button inside the folder list" -- it is not
	 * in the list any more, it is the header's hamburger.
	 * The box itself is stated once, with the other two, in §2c above. */

	/* The folder list's own ⋯ is desktop furniture: below the desktop the panel
	   is a drawer with one job, and its actions are reachable from Settings. */
	/* ⚠ Two ids. §8g pins every more-button's shape with
	   `#layout > div > .header > a.sidebar-menu`, which is one element longer
	   than the obvious `#layout-sidebar > .header > …` and carries !important
	   besides -- so the plain form lost and the ⋯ stayed. */
	#layout > div#layout-sidebar > .header > a.sidebar-menu {
		display: none !important;
	}

	/* ⚠ THE LIST HEAD LOSES ITS TITLE. Elastic prints the folder name there
	   below 1201px, and in a head shared with Aktualisieren and the ⋯ it is the
	   one thing already said elsewhere -- the folder panel is one tap away and
	   marks the open folder. The CONTENT head's title is a different thing (the
	   subject of the open message) and stays. */
	#layout > div#layout-list > .header > .header-title {
		display: none !important;
	}

	/* ⚠ AND THE READING VIEW'S BACK BUTTON, in the same slot. Where only one
	 * column is on screen, whichever of the three is showing owns the header's
	 * left control: ☰ over the list, ☰ over the folder panel, "back" over an
	 * open message. Without this the reading view had NO header control at all
	 * -- the list column that carries the hamburger is hidden while a message
	 * is open, and the header's left third was empty.
	 *
	 * ⚠ SCOPED BY ui.js's CLASS, NOT BY THIS BLOCK'S WIDTH, and that is the one
	 * rule in the phone set that could not simply move up with the
	 * rest. `screen_resize_normal()` (769-1200) shows the list AND the content
	 * together and hides this button with jQuery's inline `display: none` --
	 * which an `!important` here overrides, putting a second control in the
	 * same fixed 40x40 box as the list's hamburger. `layout-small` and
	 * `layout-phone` are exactly the modes that show one column at a time.
	 * Stated with the other two, in §2c above. */

	/* ---- form footers stay ------------------------------------------------
	 * ⚠ Elastic HIDES `.formbuttons` on a small screen
	 * (`html.layout-phone .formcontainer .formbuttons { display: none }`) and
	 * relies on ui.js cloning the buttons into a `.frame-nav` strip instead --
	 * a mechanism that drops anything classed `.cancel` on the way. The result
	 * was a compose screen with no way to send and no way to abandon, and a
	 * contact form with no Save. Q-Mail keeps a real footer on the phone
	 * (`.mf-compose-foot`), so this does too, and the clone strip stands down
	 * rather than showing the same buttons twice. */
	html.layout-phone .formcontainer .formbuttons,
	html.layout-small .formcontainer .formbuttons,
	.formcontainer .formbuttons {
		display: flex;
		position: sticky;
		bottom: 0;
		z-index: 5;
		flex: none;
		background: var(--panel, #fff);
	}


	/* ⚠ THE WHOLE STRIP, not just the buttons inside it. `.frame-nav` is a
	   58px flex row that Roundcube puts at the foot of the content column for
	   prev/next and a clone of the form's buttons. This skin hides the clones
	   (the real footer is sticky above) and Roundcube marks the strip
	   `hide-nav-buttons` when prev/next do not apply -- so on compose it was an
	   empty 58px row sitting under the Send/Cancel line, which is the band of
	   white at the bottom of the compose screen. Hiding only its contents left
	   the row itself taking the height. */
	#layout > div > .content-frame-navigation > .buttons {
		display: none;
	}

	#layout > div > .content-frame-navigation.hide-nav-buttons {
		display: none !important;
	}

	/* "Open in new window" is meaningless on a handset -- Roundcube marks it
	   `data-hidden="small"` and hides it from JavaScript; stated here so it is
	   gone before that runs rather than wrapping onto a second line first. */
	.formbuttons .extwin,
	.formbuttons [data-hidden="small"] {
		display: none !important;
	}
}

/* ------- 8h(b). the same card on a phone as on the desktop ----------------
 *
 * ⚠ ELASTIC REBUILDS EVERY POPOVER BELOW 769px. `html.layout-small .popover`
 * and its `layout-phone` twin turn the card §8h paints into a 272px drawer
 * pinned to the RIGHT EDGE of the window, full height, square, shadowless, its
 * arrow hidden and a black title bar across the top of it -- Roundcube's own
 * mobile chrome, and the last thing in this skin that still looked like stock
 * Elastic. The card comes back, put where the button that opened it is.
 *
 * ⚠ POSITIONED BY THIS FILE, not by Popper. Elastic's block carries
 * `transform: none !important`, which beats the inline `translate3d()`
 * Bootstrap's positioner writes -- and no stylesheet can hand an inline value
 * back. So the offsets are stated here instead: the head is the only thing
 * these menus ever open from, and it is a known height.
 *
 * ⚠ `:not(#layout-menu)` -- the TASK MENU is a .popover too (`app_menu()` puts
 * the class on it), and §2c has already made that one the top bar.
 * ⚠ `:not(.select-menu)` is Elastic's own exclusion, kept: a <select> popup is
 * sized and placed against its field, not against a column head. */
@media screen and (max-width: 768px) {
	html.layout-small .popover:not(.select-menu):not(#layout-menu),
	html.layout-phone .popover:not(.select-menu):not(#layout-menu) {
		/* ⚠ EVERY OFFSET !important, and that is not belt and braces. Popper
		   writes `top`/`left` INLINE (usually 0) and carries the real placement
		   in a `transform` that Elastic's block then kills with
		   `transform: none !important` -- so an ordinary `top` here would lose
		   to an inline zero and the card would hang off the top of the window.
		   52px bar + 56px column head + the 7px channel. */
		top: 115px !important;
		right: 8px !important;
		left: auto !important;
		bottom: auto !important;
		width: 288px;
		max-width: calc(100vw - 16px);
		max-height: calc(100vh - 190px);
		border: 1px solid var(--line-2, #eee);
		border-radius: 14px;
		box-shadow: 0 12px 32px rgba(15, 18, 25, .18);
		background: var(--panel, #fff);
		overflow: hidden;
	}

	/* The black bar is Elastic's mobile title chrome. The desktop card has no
	   title either -- the button it points at is the title. */
	html.layout-small .popover:not(.select-menu):not(#layout-menu) > .popover-header,
	html.layout-phone .popover:not(.select-menu):not(#layout-menu) > .popover-header {
		display: none !important;
	}

	/* Elastic gives the body `max-height: 100% !important` for a full-height
	   drawer; a card scrolls inside itself instead. */
	html.layout-small .popover:not(.select-menu):not(#layout-menu) .popover-body,
	html.layout-phone .popover:not(.select-menu):not(#layout-menu) .popover-body {
		max-height: none !important;
		overflow-y: auto;
	}

	/* ⚠ The overlay STAYS. It is not decoration: it is what closes the menu on
	   a tap outside it. Only its weight changes -- a full-page dim belongs to a
	   drawer, not to a 288px card. */
	html.layout-small .popover-overlay,
	html.layout-phone .popover-overlay {
		background-color: rgba(15, 18, 25, .12);
	}
}


/* ⚠ WHAT IS LEFT HERE IS THE ONLY THING THAT IS STILL PHONE-ONLY. Everything
   else this block used to carry -- the 52px bar, the hamburger, the folder
   panel, the sticky form footers -- now runs from §2c's band down, because a
   tablet that behaved like a squeezed desktop for half of it and like a phone
   for the other half was two designs pretending to be one. */
@media screen and (max-width: 640px) {
	/* .mf-brand-name is 16px on a phone; 19px does not leave room for the
	   pinned pair beside it below about 400px. */
	#layout-menu .quantum-brand-name {
		font-size: 16px;
	}
}

/* ⚠ A DIALOG THAT FITS, at every width below the desktop.
 *
 * jQuery UI writes the box's `width`, `top` and `left` INLINE, from a size
 * Roundcube asks for (the discard prompt asks for 500) and a position measured
 * against the window -- so on a 484px phone the "Save this message as a draft?"
 * prompt hung off both edges with its buttons out of reach. Elastic only deals
 * with this at 480px, where it makes the dialog full screen; between 481 and the
 * desktop nothing did.
 *
 * ⚠ THE OFFSETS NEED !important AND THE CAPS DO NOT. An inline style beats a
 * stylesheet declaration but not an important one, so `left`/`right`/`width`
 * have to carry weight to displace what jQuery UI wrote; `max-height` has no
 * inline counterpart. `.ui-widget.ui-dialog` (0,2,0) also outranks Elastic's
 * own `.ui-dialog { box-sizing: initial }`, which would otherwise let the
 * padding push the box back past the cap it was just given.
 *
 * One card at every small width rather than Elastic's full-screen phone
 * treatment: the prompt is three lines and two buttons, and a page-sized sheet
 * for it reads as a navigation, not as a question.
 *
 * ⚠ THE SAME QUERY AS §2c. `top: 60px` clears the top bar at BOTH its heights
 * (52 below the desktop, 60 above it), but the 520px cap is only right where
 * the window is small enough for jQuery UI's own number to overflow it -- so
 * this band has to be the band where the app design runs, not a third one. */
@media screen and (max-width: 1024px),
       screen and (max-width: 1279px) and (pointer: coarse) {
	.ui-widget.ui-dialog {
		left: 12px !important;
		right: 12px !important;
		width: auto !important;
		/* Clear of the top bar rather than centred on a window whose middle is
		   under the keyboard half the time. */
		top: 60px !important;
		/* Wide enough for the prompt, never wider than the window. `left` and
		   `right` together with an auto width make the auto margins centre it
		   once the cap bites -- a 1200px-wide "are you sure?" is not a dialog,
		   it is a page. */
		max-width: 520px;
		margin-left: auto !important;
		margin-right: auto !important;
		max-height: calc(100vh - 132px);
		height: auto !important;
		box-sizing: border-box;
		display: flex;
		flex-direction: column;
	}

	/* The box is capped, so the part that can be long is the part that
	   scrolls -- not the buttons underneath it. */
	.ui-dialog .ui-dialog-content {
		flex: 1 1 auto;
		min-height: 0;
		overflow: auto;
	}

	.ui-dialog .ui-dialog-titlebar,
	.ui-dialog .ui-dialog-buttonpane {
		flex: none;
		background: var(--panel, #fff);
		color: var(--text, #222);
	}

	/* ⚠ Elastic's own phone rules (<=480) make the dialog `width: 100%
	   !important; height: 100% !important` and hide the close button. The size
	   is displaced above (`height` included); the close button is put back,
	   because a card has a corner to close from and no full-screen "back" to
	   leave by. */
	.ui-dialog .ui-dialog-titlebar-close {
		display: block !important;
	}
}

/* `.mf-brand img, .mf-brand-divider { display: none }` -- below 600 the app
   drops the mark and keeps the name. 92px of logo, a hamburger, a wordmark and
   three buttons do not share a 390px bar. */
@media screen and (max-width: 600px) {
	/* ⚠ `html` IS LOAD-BEARING, and its absence was a bug in eleven themes out
	   of twelve. The per-theme wordmarks in §8e are written
	       html:where([data-theme="indigo"]) #layout-menu .popover-header
	   -- `:where()` contributes nothing, but the `html` TYPE selector still
	   counts, so those rules are (1,1,1) against this one's (1,1,0). Both
	   important; the extra element decides. The DEFAULT theme has no
	   `[data-theme]` rule at all -- it takes §8e's plain (1,1,0) selector,
	   which this one ties and then wins on order -- so green hid its mark on a
	   narrow window and the other eleven did not, keeping a 92px logo under
	   the hamburger and behind the wordmark.
	   ⚠ The `layout-phone` twin did not save it either: ui.js only sets that
	   class at 480, so between 481 and 600 nothing matched but the losing
	   selector. Adding the type makes this (1,1,1) and later in the file. */
	html #layout-menu .popover-header,
	html.layout-phone #layout-menu .popover-header {
		background-image: none !important;
	}

	#layout-menu .quantum-brand {
		margin-left: 0;
	}

	/* A rule only reads as a divider with something on its left. */
	#layout-menu .quantum-brand-divider {
		display: none;
	}
}

/* ------------------------------------------------- 12. jQuery UI dialogs ---
 * "Are you sure…", the headers dialog, the folder pickers.
 *
 * ⚠ THE THEME LAYER CANNOT REACH THESE. Roundcube loads jQuery UI's own
 * stylesheet (meta.json: `jquery_ui_colors_theme`), which is not Elastic LESS
 * and so is not in the per-declaration diff -- `.ui-widget-content` keeps its
 * hardcoded `#fff` on `#333` in all twelve themes. The discard prompt came up
 * as a white slab in the middle of every dark theme.
 *
 * ⚠ `.ui-widget.ui-dialog`, two classes, on the dialog itself: jQuery UI paints
 * the box with the single class `.ui-widget-content`, and a one-class selector
 * here would be decided by which stylesheet Roundcube happened to load last. */
.ui-widget.ui-dialog,
.ui-dialog .ui-dialog-content,
.ui-dialog .ui-dialog-titlebar,
.ui-dialog .ui-dialog-buttonpane {
	background: var(--panel, #fff);
	color: var(--text, #222);
	border-color: var(--line, #ddd);
}

.ui-widget.ui-dialog {
	border: 1px solid var(--line, #ddd);
	border-radius: 12px;
	box-shadow: 0 18px 50px rgba(0, 0, 0, .28);
	overflow: hidden;
}

.ui-dialog .ui-dialog-titlebar {
	border-bottom: 1px solid var(--line-2, #eee);
}

.ui-dialog .ui-dialog-title {
	color: var(--text-strong, #111);
	font-size: 16px;
	font-weight: 700;
}

.ui-dialog .ui-dialog-titlebar-close {
	color: var(--muted, #666);
	background: transparent;
}

.ui-dialog .ui-dialog-buttonpane {
	border-top: 1px solid var(--line-2, #eee);
}

/* The prompt's own text -- jQuery UI leaves it on `.ui-widget-content`'s
   colour, which is the #333 this section exists to replace. */
.ui-dialog .ui-dialog-content p,
.ui-dialog .ui-dialog-content div,
.ui-dialog .ui-dialog-content label {
	color: var(--text, #222);
}

/* The destructive answer is the primary one here, so it takes the same flat
   fill "Senden" uses; everything else is the quiet half of the pair. */
.ui-dialog .ui-dialog-buttonset button.mainaction,
.ui-dialog .ui-dialog-buttonset button.btn-primary {
	background: var(--compose-btn, #17293a);
	border-color: transparent;
	color: var(--compose-btn-fg, #fff);
	border-radius: 9px;
	font-weight: 500;
}

.ui-dialog .ui-dialog-buttonset button:not(.mainaction):not(.btn-primary) {
	background: var(--panel, #fff);
	border: 1px solid var(--line, #ddd);
	color: var(--text, #222);
	border-radius: 9px;
	font-weight: 500;
}

/* Elastic dims the page behind a dialog with a token that is light in every
   theme, which over a dark panel reads as a haze rather than a scrim. */
.ui-widget-overlay {
	background: rgba(0, 0, 0, .45);
}

/* ------------------------------------------------- 13. the message stack ---
 * ⚠ ONLY `.loading`. Roundcube stacks every notice in `#messagestack`, and the
 * one it shows on a task switch is a bare spinner that appears and vanishes
 * inside a few hundred milliseconds -- a flicker in the corner on every click
 * of E-Mail / Kontakte / Einstellungen, carrying no information the page does
 * not already show. Errors, warnings and confirmations keep their toast; the
 * class is what separates them. */
#messagestack div.loading {
	display: none !important;
}

/* Everything else takes the theme rather than Elastic's fixed slate. */
#messagestack div {
	border-radius: 10px;
	background-color: var(--card-bg, #fff);
	color: var(--text, #222);
	box-shadow: 0 8px 24px rgba(0, 0, 0, .18);
	border: 1px solid var(--line, #ddd);
}

/* --------------------------------------- 14. no flash before the overflow ---
 * ⚠ THE TOOLBAR IS MEASURED TWICE, AND THE SECOND TIME IS AFTER A PAINT.
 * quantum.js runs its first pass at defer-time, but ui.js REBUILDS every
 * column-head toolbar on `rcmail:init` -- putting every button back in the row
 * -- and the browser paints that row before the second pass moves the overflow
 * into the ⋮. So on every task switch the buttons that belong in the menu
 * appeared in the head for a frame first.
 *
 * The row is held invisible (not `display: none` -- the geometry has to be
 * measurable) from the first byte of the document until the sync has run.
 * `quantum-boot` is set by the pre-paint script in layout.html and cleared by
 * quantum.js; a timeout clears it too, so a broken script cannot leave the
 * heads blank. */
html.quantum-boot #layout > div > .header .toolbar:not(.popupmenu):not(.quantum-morepanel),
html.quantum-boot .quantum-more {
	visibility: hidden;
}

/* ⚠ AND ELASTIC'S OWN TWO HEAD BUTTONS, which §2d already hides -- but only
 * while the layout is UNCLASSIFIED, and that is one step too short.
 *
 * Traced on the live install, 390px, contacts (times from the document's own
 * clock, sampled every 6ms):
 *
 *     t=54   html "quantum-boot js chrome webkit mobile"   hidden   @327,60
 *     t=75   html "... layout-phone touch"                 VISIBLE  @151,60
 *     t=87   display:none
 *
 * ui.js writes `layout-phone` and §2d's mask releases on that same tick -- but
 * the DOM work that puts the button where it belongs is script, and it lands
 * about 12ms later. In between, `a.toolbar-menu-button` paints 40x40 at x=151
 * of a 390px screen: dead centre. That is the "more button flickering in the
 * middle of the screen when switching between e-mail and contacts", reported
 * three times, and it is NOT `.quantum-more` -- which never exists at phone
 * width on any page. Every fix aimed at `.quantum-more` missed for that reason.
 *
 * The class arriving is not the same event as the work being finished, so the
 * mask has to hang on the flag that means finished. `quantum-boot` is exactly
 * that flag: set before first paint, cleared by quantum.js once its pass has
 * run (and by layout.html's own timeout if the script never does, so a broken
 * build cannot leave the head blank).
 *
 * ⚠ BOTH BUTTONS, as §2d lists them. Only the menu button was caught painting
 * mid-row; the list button happened to sit at its final x. That is timing
 * luck, not a difference in kind -- verified on the live page with the rule
 * injected: the mid-screen paint disappears, and the list button keeps its
 * steady-state position throughout. */
html.quantum-boot #layout > div > .header > a.toolbar-list-button,
html.quantum-boot #layout > div > .header > a.toolbar-menu-button {
	visibility: hidden;
}

/* ------------------------------------------------ 15. scrollbars ----------
 * ⚠ ELASTIC'S ARE HARDCODED, so the theme build never touched them.
 * `global.less` writes `scrollbar-color: #c1c1c1 #f1f1f1` and a matching pair
 * of `::-webkit-scrollbar-*` rules with literal greys -- not LESS colour
 * variables, so the per-declaration diff that builds themes.css had nothing to
 * map. Every dark theme therefore drew a light grey scrollbar down the side of
 * a near-black message list, in every column and every frame.
 *
 * ⚠ `color-mix`, not a token. There is no "scrollbar thumb" token, and none of
 * the existing ones works in both directions: --muted is a strong slate that
 * would darken the light themes, and --line-2 is an rgba hairline that vanishes
 * on dark ones. A share of --text is by construction the opposite of --panel in
 * every theme, which is exactly what a thumb has to be. The plain grey stays as
 * the first declaration, for anything that cannot parse the second.
 *
 * ⚠ NO `:not(.touch)` ON THE STANDARD PROPERTY, and that guard was a bug.
 * `screen_resize_html()` puts `touch` on <html> for every width <= 1024, so on
 * a tablet -- and on any desktop window narrowed past 1024 -- these rules
 * stopped matching. Elastic's own does not: `global.less` writes the literal
 * on a BARE `html` with no guard at all, so what was left was Elastic's light
 * grey down the side of a near-black list, which is exactly the bug this
 * section exists to fix, still there below 1024.
 * ⚠ And `color-scheme: dark` cannot rescue it: an inherited explicit
 * `scrollbar-color` beats the colours a colour scheme implies.
 * Bare `html` is (0,0,1), the same as Elastic's, and this file loads after
 * styles.css -- order decides, and order is ours. */
html,
html textarea,
html select,
html .popover-body,
html .popupmenu,
html .frame-content,
html .formcontent,
html .scroller {
	scrollbar-color: #c1c1c1 transparent;
	scrollbar-color: color-mix(in srgb, var(--text, #222) 28%, transparent) transparent;
}

/* ⚠ THE WEBKIT PAIR STILL NEEDS BOTH PREFIXES, for the opposite reason: here
   Elastic's rules ARE guarded (`html:not(.touch) ::-webkit-scrollbar-thumb`,
   (0,1,1)+pseudo), so below 1024 there is nothing to outrank and above it
   there is. One selector per state, each matching Elastic's weight. */
html:not(.touch) ::-webkit-scrollbar-track,
html:not(.touch) .webkit-scroller::-webkit-scrollbar-track,
html.touch ::-webkit-scrollbar-track,
html.touch .webkit-scroller::-webkit-scrollbar-track {
	background: transparent;
}

html:not(.touch) ::-webkit-scrollbar-thumb,
html:not(.touch) .webkit-scroller::-webkit-scrollbar-thumb,
html.touch ::-webkit-scrollbar-thumb,
html.touch .webkit-scroller::-webkit-scrollbar-thumb {
	background: #c1c1c1;
	background: color-mix(in srgb, var(--text, #222) 28%, transparent);
	border-radius: 6px;
}

html:not(.touch) ::-webkit-scrollbar-thumb:hover,
html:not(.touch) .webkit-scroller::-webkit-scrollbar-thumb:hover,
html.touch ::-webkit-scrollbar-thumb:hover,
html.touch .webkit-scroller::-webkit-scrollbar-thumb:hover {
	background: color-mix(in srgb, var(--text, #222) 45%, transparent);
}

/* Elastic sizes the bar only in its non-touch block; below 1024 it went back
   to the platform's full-width scrollbar mid-resize. */
html.touch ::-webkit-scrollbar {
	width: 6px;
}

/* ⚠ AND THE ONE SCROLLBAR NO STYLESHEET REACHES: the message body. Roundcube
   shows an HTML message inside its own iframe, whose document is the mail --
   none of this file is loaded there, only `embed_css_location`. `color-scheme`
   is inherited, so setting it on the frame ELEMENT hands the embedded document
   a dark canvas and a dark scrollbar without anything being injected into it.
   ⚠ ON THE SCROLLERS AND THE FRAMES, NOT ON `html`. `color-scheme: dark` at the
   root also switches every form control the browser draws for itself to its
   dark metrics, which is a layout change in a skin that already places those
   controls by hand -- more than is needed to colour a scrollbar.
   ⚠ THE LIST IS DERIVED, NOT CHOSEN -- these are the themes whose --app-bg is
   dark (luminance < .5 in styles/tokens.css). tests/skin.py recomputes it from
   the tokens and fails if a new dark theme is added without being named here,
   because there is no way to ask CSS whether a custom property is dark. */
html[data-theme="access-it"] .scroller,
html[data-theme="access-it"] .frame-content,
html[data-theme="access-it"] iframe,
html[data-theme="enzian-nacht"] .scroller,
html[data-theme="enzian-nacht"] .frame-content,
html[data-theme="enzian-nacht"] iframe,
html[data-theme="indigo"] .scroller,
html[data-theme="indigo"] .frame-content,
html[data-theme="indigo"] iframe {
	color-scheme: dark;
}

/* ------------------------- 15b. the sheet the mail is printed on -----------
 * ⚠ AN HTML MESSAGE IS NOT FRAMED, and that is the whole problem. Roundcube
 * washes it and drops it straight into the page --
 * `program/actions/mail/show.php:675` picks the container class, `html` parts
 * getting `.message-htmlpart` -- so the sender's own declarations land in this
 * document with their colours intact. Nearly every mail ever written assumes a
 * white page: the report that opened this was body text at #333 and a footer
 * note at #999, measured on a #10121a ground at 1.4:1.
 *
 * ⚠ THE GROUND MOVES, NOT THE TEXT. Forcing a colour onto `.message-htmlpart`
 * would repaint someone else's document -- it would also wreck every mail that
 * sets its own background, which is the half of the problem a `color`
 * override cannot even see. Putting the mail on paper fixes both, and is what
 * Thunderbird and Apple Mail do with a dark UI.
 *
 * ⚠ PLAIN-TEXT PARTS ARE DELIBERATELY LEFT ALONE. `.message-part` carries no
 * sender styling at all, so it is safe -- and readable -- in the theme's own
 * ink. Only the parts that bring their own CSS get the sheet.
 *
 * ⚠ DARK THEMES ONLY, and by the same derived list as `color-scheme` above --
 * `test_message_paper` recomputes it from --app-bg and fails if a new dark
 * theme is added without being named. On the nine light themes --paper is
 * already the panel's white, so a rule here would paint white on white and
 * change nothing except to indent every message by the padding.
 * ⚠ `color-scheme: light` is not decoration. It is inherited from
 * `.frame-content` above, so without this reset the browser draws the mail's
 * unstyled links, form controls and scrollbars in their DARK colours -- on a
 * white sheet. The sheet is light; it has to say so. */
html[data-theme="access-it"] #messagebody .message-htmlpart,
html[data-theme="enzian-nacht"] #messagebody .message-htmlpart,
html[data-theme="indigo"] #messagebody .message-htmlpart {
	color-scheme: light;
	background: var(--paper, #fff);
	color: var(--paper-ink, #2c2c2c);
	border-radius: 8px;
	padding: 16px 18px;
}

/* ⚠ AND THE LINKS INSIDE IT, which the sheet breaks if nothing is said.
 * Elastic paints every anchor `@color-link`, which the theme build maps to
 * --accent -- a colour chosen to read on a DARK ground (#67b2d4, #9784ff,
 * #818cf8). Put that on a white sheet and it lands at 2.4-3.0:1; the contrast
 * probe caught all three the moment the sheet went in.
 *
 * ⚠ REVERTED, NOT RECOLOURED, and the specificity is the point. `revert`
 * drops the author declaration entirely so the browser's own link colour
 * applies -- which, with `color-scheme: light` above, is the blue that has
 * always been legible on paper. Both halves are wrapped in `:where()` and the
 * `#messagebody` id is left out, so this whole selector weighs (0,1,0): it
 * beats Elastic's bare `a` at (0,0,1), and it LOSES to the mail's own rules,
 * which washtml rewrites under the part's id at (1,0,1) or better. A mail that
 * colours its links still decides; one that does not gets the browser's.
 *
 * ⚠ ONE SELECTOR PER THEME, not one `:where()` with a comma list inside it.
 * The rule-coverage guard splits a selector on commas to probe each form, so
 * a comma nested in a functional pseudo-class hands it two broken halves and
 * the whole rule reads as invalid CSS. Same shape as the `color-scheme` list
 * above, for the same reason. */
:where(html[data-theme="access-it"]) .message-htmlpart :where(a),
:where(html[data-theme="enzian-nacht"]) .message-htmlpart :where(a),
:where(html[data-theme="indigo"]) .message-htmlpart :where(a) {
	color: revert;
}

/* --------------------------- 16. hover, in the dark themes ----------------
 * ⚠ THE THEME BUILD DARKENED THESE TO BLACK. Elastic writes its hover fills as
 * LESS transforms of a base colour -- `darken(@color-toolbar-background, N%)`
 * and friends -- and tools/build.py maps them by compiling each theme and
 * diffing the result. On a light theme that gives a slightly deeper grey. On a
 * theme whose ground is already #05080d it bottoms out:
 *
 *     :where(html[data-theme="access-it"]) .menu.toolbar .dropbutton:not(.disabled):hover
 *         { background-color: #010102 }
 *     :where(html[data-theme="access-it"]) .menu.toolbar .dropbutton a.dropdown:hover
 *         { background-color: #000000 }
 *
 * -- which is why hovering "Exportieren" turned parts of it black, and only in
 * the three dark themes. The rest of the toolbar escaped because §8b's rules
 * carry an id and `:where()` adds no specificity at all; the `.dropbutton`
 * WRAPPER and its caret had no rule of ours to win.
 *
 * ⚠ `html` as the prefix, and it is doing arithmetic: `:where(...)` contributes
 * nothing, so these have to beat a bare (0,5,0) / (0,4,1) selector on their own.
 * A type selector is the cheapest way over the line and it matches in a frame,
 * which `#layout` does not (§7b).
 *
 * ⚠ The wrapper paints NOTHING. Each of the two anchors inside it draws its own
 * hover (§8b), so a fill on the span behind them is a third rectangle nobody
 * asked for -- it is what made the black visible as a band around the pair
 * rather than as a button. */
html .menu.toolbar .dropbutton:not(.disabled):hover,
html .menu.toolbar .dropbutton:not(.disabled):focus-within {
	background-color: transparent;
}

html .menu.toolbar .dropbutton > a.dropdown:hover,
html .menu.toolbar .dropbutton > a.dropdown:focus {
	background-color: var(--bar-ic-hover, var(--hover, #f3f3f6));
	color: var(--text, #222);
}

/* The attachment drop zone is the same story: `.file-upload.droptarget.hover`
   and `.active` are darken() of the panel, black in the same three themes --
   so dragging a file over it turned the target into a hole. */
html .file-upload.droptarget.hover,
html .file-upload.droptarget.active {
	background-color: var(--sel, #eceef0);
	border-color: var(--accent, #4a3fbf);
}

/* ------------------------------------------------------ 17. the login page ---
 *
 * ⚠ THE LOGIN PAGE ALREADY RUNS THIS STYLESHEET, and that is not obvious.
 * Elastic's `login.html` opens with `<roundcube:include file="includes/
 * layout.html" />`, and Roundcube resolves includes through the skin search
 * path -- so OUR layout.html is what the login page loads, with tokens.css,
 * themes.css, this file, the pre-paint theme script and quantum.js. A visitor
 * who has chosen a theme before gets it here too, from localStorage, before
 * they have a session.
 *
 * What was NOT ours until this section: the submit button rendered as a raw
 * user-agent button, the fields were Elastic's, and `#login-footer > div`
 * carried Elastic's hardcoded `#f1f3f4` -- a white slab across all three dark
 * themes, the same defect class as the attachment strip.
 *
 * ⚠ NO NEW TEMPLATE. Everything here hangs off `body.task-login` and the ids
 * Roundcube's own `loginform` object emits, so there is no second template to
 * keep in step with upstream.
 *
 * ⚠ AND NO NEW WORDS. `content:` cannot reach localization/*.inc, so the only
 * text this section puts on the page is a proper noun -- "Quantum Mail" reads
 * the same in all nine languages. A connective ("von", "by") would need the
 * template after all. */

/* ---- the form: the app's own controls ---- */
.task-login #login-form input.form-control {
	height: 44px;
	border-radius: 8px;
	padding: 0 14px;
	font-size: 15px;
	background: var(--search-bg, #fff);
	color: var(--text, #222);
	border: 1px solid var(--search-bd, #ddd);
	box-shadow: none;
	width: 100%;
}

.task-login #login-form input.form-control:focus {
	border-color: var(--brand, #4f7f13);
	outline: 2px solid var(--brand, #4f7f13);
	outline-offset: 1px;
}

/* ⚠ Roundcube builds the form as a TABLE (rcmail_output_html::login_form), so
   the label and its field are two cells of one row. Stacked, they have to be
   turned into blocks -- there is no markup here to change. */
.task-login #login-form table,
.task-login #login-form tbody,
.task-login #login-form tr,
.task-login #login-form td {
	display: block;
	width: 100%;
	text-align: left;
}

.task-login #login-form tr {
	margin: 0 0 14px;
}

.task-login #login-form td.title {
	padding: 0;
}

.task-login #login-form td.title label {
	display: block;
	margin: 0 0 5px;
	font-size: 12px;
	font-weight: 500;
	letter-spacing: .02em;
	color: var(--muted, #666);
}

.task-login #rcmloginsubmit {
	display: block;
	width: 100%;
	height: 46px;
	margin: 4px 0 0;
	border: 0;
	border-radius: 9px;
	cursor: pointer;
	background: var(--compose-btn, #17293a);
	color: var(--compose-btn-fg, #fff);
	font-size: 15px;
	font-weight: 500;
	letter-spacing: .01em;
}

.task-login #rcmloginsubmit:hover {
	filter: brightness(1.08);
}

.task-login .formbuttons {
	margin: 0;
}

/* ⚠ `#f1f3f4` is Elastic's, hardcoded, with no Q-Mail token behind it -- so
   the theme diff cannot reach it and it stayed white on every dark theme. */
.task-login #login-footer {
	margin-top: 22px;
	color: var(--muted, #666);
	font-size: 12.5px;
	text-align: center;
}

.task-login #login-footer > div {
	margin-top: 9px;
	padding: 11px 14px;
	border: 1px solid var(--line, #e7e7e7);
	border-radius: 9px;
	background: var(--hover, #f4f5f6) !important;
	color: var(--muted, #666);
}

/* ---- the split: brand panel beside the form ---- */
/* ⚠ THE TWO HALVES ARE ALREADY IN THE MARKUP. login.html puts the logo and
   the form side by side inside #layout-content, so a flex row is the whole
   composition -- the panel is a pseudo-element, not a new element. */
.task-login #layout {
	padding: 0;
}

.task-login #layout-content {
	display: flex;
	flex-direction: row;
	align-items: stretch;
	width: 100%;
	height: 100%;
	border-radius: 0;
	background: var(--panel, #fff);
	overflow: hidden;
}

/* ⚠ background-COLOR, not the `--compose-bg` shorthand. That token is a flat
   colour on nine of the twelve themes, and a flat colour is not a valid
   background-image -- the whole layered declaration would be dropped and the
   panel would vanish on exactly those nine.
   ⚠ THE SCRIM IS A CONTRAST FIX, not a mood. Measured white against each
   theme's --compose-btn: five of the twelve fall under 4.5:1, Lightblue worst
   at 2.64:1. A 35% black scrim lifts the worst case to 5.63:1 and every other
   theme above 6.5:1, so the lockup reads on all twelve rather than on seven. */
/* ⚠ THE PANEL CARRIES BOTH NAMES, and the order is the point: Quantum Mail
   is what someone is signing in to, so it is set large and centred; the
   lockup signs it at the foot. One pseudo-element does both -- the product as
   `content`, the lockup as a background layer.
   ⚠ AND THE ONLY TEXT IS A PROPER NOUN. `content:` cannot reach
   localization/*.inc, so "Quantum Mail" is safe in all nine languages where a
   connective ("von", "by") would need the template overridden. */
.task-login #layout-content:before {
	content: "Quantum Mail";
	flex: 1 1 46%;
	display: flex;
	align-items: center;
	justify-content: center;
	font-family: Roboto, sans-serif;
	font-weight: 500;
	font-size: clamp(30px, 3.9vw, 46px);
	letter-spacing: .04em;
	color: var(--compose-btn-fg, #fff);
	background-color: var(--compose-btn, #17293a);
	background-image: linear-gradient(rgba(0, 0, 0, .35), rgba(0, 0, 0, .35)),
		url('../images/lockup-onbrand.png');
	background-repeat: no-repeat;
	background-position: center, center bottom 46px;
	background-size: auto, min(38%, 184px) auto;
}

/* Roundcube's own <img id="logo"> is hidden the same way the header's is: the
   src is written by the `logo` template object and cannot be re-pointed per
   theme from a stylesheet. The page still announces itself -- login.html
   carries an <h1 class="voice"> for screen readers. */
.task-login #logo {
	display: none;
}

.task-login #login-form {
	position: static;
	flex: 1 1 54%;
	width: auto;
	max-width: none;
	margin: 0;
	align-self: center;
	padding: 0 clamp(28px, 6vw, 84px);
}

/* ⚠ NO HEADING OVER THE FORM. The product is named once, on the panel; a
   second "Quantum Mail" here read as a page that could not decide which half
   was the title. */

/* Below 820px the two halves stack: the panel becomes a banner and the form
   takes the rest. Elastic's own login breakpoints are not used -- it has none
   that matter here. */
@media screen and (max-width: 820px) {
	.task-login #layout-content {
		flex-direction: column;
	}

	.task-login #layout-content:before {
		flex: none;
		height: 208px;
		font-size: 27px;
		background-position: center, center bottom 24px;
		background-size: auto, auto 38px;
	}

	.task-login #login-form {
		padding: 28px 26px 34px;
	}
}

/* --------------------- 18. Elastic literals with no token --------------------
 *
 * Both of these are the same defect class as the attachment strip and the
 * login footer: a colour Elastic hardcodes, with no Q-Mail token behind it, so
 * the per-declaration theme diff had nothing to map and every dark theme kept
 * the light value.
 *
 * ⚠ 1. THE BRACKET UNDER ? AND ⏻. `#layout-menu .special-buttons a` carries a
 * 1px border in `#f1f3f4` with a 9px radius. On the light themes that is the
 * bar's own ground and invisible; on the three dark ones it draws a pale arc
 * under each button -- reported as "a bracket at the bottom of the logout and
 * about buttons". Measured on a 390px phone in Indigo: borderBottom 1px
 * rgb(241, 243, 244) against a bar painted rgb(5, 5, 13).
 * It is removed rather than themed: these two are bare glyphs on a bar, and
 * nothing else in the bar is boxed. */
#layout-menu .special-buttons a,
#layout-menu .special-buttons a:hover,
#layout-menu .special-buttons a:focus {
	border: 0 !important;
}

/* ⚠ 2. THE ABOUT DIALOG'S PLUGIN TABLE. `#pluginlist` is a bare table --
 * neither Elastic's stylesheet nor jQuery UI's gives it a colour (checked:
 * jQuery UI sets `.ui-widget-content { background-color: #fff }` and no text
 * colour at all, and `records-table` appears nowhere in Elastic's LESS), so it
 * inherits, and anything that dims its container dims the whole table with it.
 * Stated explicitly against the tokens so it cannot inherit its way to grey on
 * grey, and pinned opaque so a container's fade cannot take it with it.
 *
 * ⚠ SCOPED TO THE PLUGIN LIST, not to tables. `.frame-content` is also the
 * reading pane, and a rule on tables there would repaint the tables inside a
 * MESSAGE -- someone else's HTML, which this skin does not touch. */
html #pluginlist,
html #pluginlist th,
html #pluginlist td {
	color: var(--text, #222) !important;
	opacity: 1 !important;
}

html #pluginlist th {
	color: var(--muted, #666) !important;
}

html #pluginlist a {
	color: var(--accent, #4f7f13) !important;
}

html #pluginlist,
html #pluginlist tr,
html #pluginlist td,
html #pluginlist th {
	border-color: var(--line, #e7e7e7) !important;
}

/* ⚠ 3. THE PREV/NEXT BAR UNDER A MESSAGE. Elastic puts a
 * `.footer.content-frame-navigation` at the foot of the content column and
 * hides it from 769px up -- so it is a PHONE-ONLY control, and on a phone it
 * is the one width where vertical space is scarcest. Measured on a 390x844
 * screen it takes 56px of a 640px reading area, about 9%, to duplicate
 * something the list is one tap away from.
 *
 * ⚠ At EVERY width, not only where Elastic shows it. The rule is stated
 * unconditionally so it cannot come back at a breakpoint nobody tested -- and
 * because a tablet in portrait can measure under 769px, which is how this was
 * reported as "also in tablet mode". */
#layout > div > .footer.content-frame-navigation {
	display: none !important;
}

/* ⚠ 4. THE WHITE LINE UNDER THE BOTTOM TAB BAR. Same literal as the bracket
 * above, in a different place and with a different cause. Elastic's
 * `@media screen and (max-width: 480px)` block restyles `#taskmenu a` as rows
 * of a VERTICAL POPOVER list -- 47px tall, left-aligned, `color: #191919` --
 * and separates them with `border-bottom: 1px solid #f1f3f4 !important`.
 *
 * This skin turns #taskmenu into the horizontal bottom TAB BAR instead, so
 * those separators stop being lines between stacked rows and become one line
 * along the bottom edge of the bar: four abutting 1px borders, drawn across
 * the full width because the tabs sit side by side. On the light themes it is
 * near-invisible; on the dark ones it is a white rule across the foot of the
 * screen. Measured on the report's 1440px screenshot: 3 device px tall at
 * DPR 3.7 (= 1 CSS px), rgb(242,243,247), running x 15-1424 -- which is the
 * four 96px tabs from 4px to 387px, not a full-bleed element.
 *
 * ⚠ THE COLOUR IS DROPPED, NOT THEMED. A separator at the very bottom edge of
 * the bar separates the bar from nothing -- there is no next row under it, and
 * the tabs are already divided by their own spacing. Painting it `--line`
 * would put a themed hairline under the bar on all twelve; transparent lets
 * the bar's own `--gap` ground through, which is what "dark, depending on
 * theme" actually means here.
 * ⚠ The 1px is KEPT, and `!important` is not optional: Elastic's declaration
 * carries it, so a plain override loses. Keeping the border and changing only
 * its colour also keeps the 47px row box exactly as measured -- the overflow
 * pass and the tab hit targets are both sized off it. */
#taskmenu a,
#taskmenu a:hover,
#taskmenu a:focus {
	border-bottom-color: transparent !important;
}

/* ⚠ 5. THE RECIPIENT CHIP. Reported as "after email addresses are entered they
 * are in a white box and unreadable", and it is §8's `.input-group-text` story
 * told again one element to the left.
 *
 * `recipient_input()` in Elastic's ui.js replaces the An field with a
 * `<ul class="form-control recipient-input">` and turns every finished address
 * into an `li.recipient` -- a pill holding the display name, the address itself
 * pushed out of sight, and a ✕. Elastic paints that pill
 *
 *     .recipient-input li.recipient { border: 1px solid #efefef;
 *                                     background-color: #f1f3f4 }
 *
 * and the two come from different LESS variables. Only the border's has a
 * Q-Mail token behind it, so the per-declaration diff emitted `border` for all
 * twelve themes -- themes.css has `border: 1px solid rgba(129,140,248,.16)` for
 * Indigo -- and left the fill at the literal. The ✕ was themed too, from the
 * same pass, to that theme's `--text`.
 *
 * So in the three dark themes the pill kept a near-white fill and then carried
 * the theme's LIGHT ink on it. Measured in Indigo before this rule: name and ✕
 * at rgb(230,230,245) on rgb(241,243,244) -- 1.05:1, which is the address being
 * invisible rather than merely low-contrast.
 *
 * ⚠ `--sel`, NOT `--panel` or `--hover`. The pill sits INSIDE a field that is
 * already `--search-bg` (§9), so a fill equal to the panel would dissolve the
 * chip into the input and "three recipients" would read as one run of text. The
 * selection tint is the token for "this is one picked thing among others",
 * which is exactly what a chip is, and it is the fill the folder rail and the
 * message list already use for the same idea. On the nine light themes it lands
 * within a shade or two of the #f1f3f4 it replaces, so nothing visibly moves
 * there.
 *
 * ⚠ THE NAME IS STATED, not left to inherit. Elastic gives `.name` no colour at
 * all -- it inherits from the form, which is why it went light with the theme
 * while the fill stayed put. Naming it here keeps the pair a pair.
 * ⚠ `html`, for the same reason §8's addon rule takes it: themes.css writes
 * these as `:where(html[data-theme="…"]) .recipient-input li.recipient`, and
 * `:where()` contributes nothing, so that rule is (0,2,1) -- exactly what the
 * bare selector here would be, decided on file order alone. The type selector
 * makes it (0,2,2) and takes the question out of load order.
 * ⚠ `body > li.recipient.ui-sortable-helper` IS THE SAME PILL. jQuery UI lifts
 * a dragged chip out of the list and appends the helper to <body>, where no
 * ancestor of the widget matches any more -- Elastic restates every one of its
 * rules for that case, and a fix that covers only the first selector leaves the
 * white box back on screen for the length of the drag. */
html .recipient-input li.recipient,
html body > li.recipient.ui-sortable-helper {
	background-color: var(--sel, #eceef0);
	color: var(--text, #222);
}

/* ⚠ THE TAGEDIT PILL IS THE SAME ELEMENT BY ANOTHER NAME. `.tagedit-list` is
 * the jQuery UI widget Roundcube still uses where the recipient input is not
 * available -- the contact editor's groups field is the one a user meets -- and
 * Elastic gives it the identical pair of literals from the identical pair of
 * LESS variables, with the identical half reached by the build. Fixed in the
 * same breath rather than waiting for its own report.
 *
 * ⚠ AND IT IS THE ONE RULE HERE THE SUITE DOES NOT MEASURE. tests/skin.py
 * models the recipient chip because Elastic's `recipient_input()` is the code
 * that builds it and the markup could be read off that; nothing in this
 * repository shows the markup Roundcube emits for tagedit, and the harness's
 * rule is that it reproduces Roundcube's own markup rather than a plausible
 * guess at it. So this is symmetry, not a verified fix: if the groups field is
 * ever seen with a light pill in a dark theme, start by checking that
 * `li.tagedit-listelement-old` is really the class it carries. */
html .tagedit-list li.tagedit-listelement-old {
	background-color: var(--sel, #eceef0);
	color: var(--text, #222);
}

/* ⚠ 6. THE SECTION HEAD INSIDE A POPUP MENU. Reported on compose's Responses
 * button, whose menu is two labelled groups -- "Antwort einfügen" over the
 * list, "Antworten verwalten" over Bearbeiten -- and both labels arrived as
 * light grey bars across a dark card.
 *
 *     .popover .menu li.separator { color: #737677; background-color: #f1f3f4 }
 *
 * ⚠ THE BUILD EMITTED NEITHER DECLARATION, which makes this the worst of the
 * unmapped literals rather than the usual half: items 1-5 above each had one
 * side themed and one side left behind, and this rule has no per-theme
 * counterpart in themes.css at all. Both values are Elastic's on all twelve.
 *
 * ⚠ AND IT IS NOT ONLY A DARK-THEME DEFECT. #737677 on #f1f3f4 is 4.14:1 at
 * 0.75rem -- under AA for small text on the NINE LIGHT themes too, where it
 * merely looks unremarkable instead of looking broken. The dark themes are
 * where it got noticed, not where it started.
 *
 * ⚠ THE BAR GOES, THE LABEL STAYS -- which is Roundcube's own answer (its
 * `html.dark-mode` rule sets `background-color: transparent` and keeps the
 * text) and the only one this card can take. §8h gives the popover 6px of
 * padding and its rows an 8px radius, so a full-bleed fill has no edge to
 * reach: it would render as a grey slab floating inside a rounded card,
 * 6px short of it on three sides. A heading in `--muted` over the card's own
 * ground is what the skin uses for secondary text everywhere else.
 *
 * ⚠ THE AIR IS A MARGIN, NOT A RULE. A hairline between groups is available --
 * Elastic draws one in dark mode -- but §18.4 is the same argument in the other
 * direction: this file does not add a themed line where spacing says the same
 * thing more quietly. `li + li.separator` is a head that FOLLOWS a row, so the
 * first one in the menu gets no dead space above it.
 * ⚠ `margin`, and `border: 0 !important` in §8h is why it cannot be padding on
 * a border: that rule carries weight and would have to be fought for a line
 * this deliberately does not draw.
 *
 * ⚠ `html`, and all three menu roots. §8h styles the popover card, the
 * `.popupmenu` a head toolbar becomes while it is a popup, and the skin's own
 * `.quantum-morepanel`; a separator can be handed to any of them, because what
 * puts it there is the menu's own markup and not the container it is shown in.
 */
html .popover .menu li.separator,
html .popupmenu li.separator,
html .quantum-morepanel li.separator {
	background-color: transparent;
	color: var(--muted, #666);
	letter-spacing: .02em;
}

html .popover .menu li.separator label,
html .popupmenu li.separator label,
html .quantum-morepanel li.separator label {
	color: inherit;
}

html .popover .menu li + li.separator,
html .popupmenu li + li.separator,
html .quantum-morepanel li + li.separator {
	margin-top: 6px;
}
