/*
 * Copied from nanatam at 36f976d, unchanged. The comments below were written
 * there and tell that history; "the panel" and "Mission Control" are nanatam's.
 */
/*
 * The mail page.
 *
 * A stylesheet rather than a <style> block, and that is a security decision
 * before it is a tidiness one. This app will eventually render mail written by
 * strangers, and the Content-Security-Policy that makes that safe cannot allow
 * `unsafe-inline` - so the page must not need it. It is also why the first
 * version looked unstyled through nginx: the CSP for this host had no
 * exception for inline styles, and quite right too.
 */

/*
 * ## One palette, shared with the operator panel
 *
 * These values are the panel's, copied from
 * packages/mission-control-ui/src/routes/+layout.svelte, and that is the
 * point of this block.
 *
 * They used to be a different palette entirely: a warm cream ground
 * (#f4f1ea), near-black warm ink, a dark-green accent and a rust-orange
 * `--mark` on every primary button, against the panel's cool grey ground and
 * blue accent. Two products, sold as one, with the same word "Bastion" at the
 * top left of both - and an operator moves between them several times a day.
 * Nothing else about this file explained the difference; it was simply
 * written a month apart from the other one by somebody looking at a different
 * screen. The names here are unchanged on purpose, so no rule below had to
 * move: only what the names MEAN has changed.
 *
 * The mapping, for whoever reads this next:
 *   --ink   is the panel's --text        --card  is its --panel
 *   --mark  is its --blue (the one accent) with --on-mark = --on-accent
 *   --accent-soft is its --hover (the row under the pointer)
 *   --danger is its --red, and the three notice trios are its, verbatim.
 *
 * Do not reintroduce a second accent here. A person who has just been looking
 * at a blue Save button in Mission Control should not find an orange one in
 * the mailbox and have to work out whether it means something different.
 */
:root {
  --ink: #14171c;
  --muted: #5b6472;
  --line: #d6dae1;
  --line-soft: #e7eaef;
  --bg: #f6f7f9;
  --card: #ffffff;
  --accent: #1a5fbf;
  --accent-soft: #eef1f6;
  --mark: #1a5fbf;
  --on-mark: #ffffff;
  --danger: #bf3a1d;
  /* This went 10px -> 6px with the palette. A 10px radius on every control
     against the panel's 6px is the other half of "two products": rounder
     corners read as a softer, more consumer product, and these two are the
     same tool. */
  --radius: 6px;
  /*
   * The three notices, named once, exactly as the panel names them. `.ok` and
   * `.bad` below were a hard-coded pink and a re-used accent wash, with the
   * dark variant of one of them living in a media query of its own further
   * down - so "this went wrong" was one colour here and another colour on the
   * other side of the window.
   */
  --good-wash: #edf7f1;
  --good-edge: #a9d6be;
  --good-ink: #11633a;
  --bad-wash: #fdf2ef;
  --bad-edge: #e8b6a8;
  --bad-ink: #a63116;
}

/*
 * Dark, twice, the same way the panel does it: once for a machine that is set
 * to dark and stamps nothing on the document, and once for an explicit
 * choice. The guard on the media query means an explicit LIGHT choice still
 * beats a dark desktop. This client has no theme switch of its own yet; the
 * second block is here so that adding one is a button and not a stylesheet
 * rewrite, and so the two applications answer `data-theme` identically.
 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    --ink: #e6e8ec;
    --muted: #8b93a1;
    --line: #2a2f38;
    --line-soft: #232830;
    --bg: #14161a;
    --card: #1b1e24;
    --accent: #6aa6e8;
    --accent-soft: #1f242c;
    --mark: #6aa6e8;
    /* Dark ink on the accent, not white: the accent inverts to a light blue
       in this theme, and white on it does not carry. */
    --on-mark: #0d1117;
    --danger: #e2603f;
    --good-wash: #16261c;
    --good-edge: #23452f;
    --good-ink: #8fd6ab;
    --bad-wash: #2a1a16;
    --bad-edge: #4a2a20;
    --bad-ink: #f0b6a4;
  }
}

:root[data-theme='dark'] {
  --ink: #e6e8ec;
  --muted: #8b93a1;
  --line: #2a2f38;
  --line-soft: #232830;
  --bg: #14161a;
  --card: #1b1e24;
  --accent: #6aa6e8;
  --accent-soft: #1f242c;
  --mark: #6aa6e8;
  --on-mark: #0d1117;
  --danger: #e2603f;
  --good-wash: #16261c;
  --good-edge: #23452f;
  --good-ink: #8fd6ab;
  --bad-wash: #2a1a16;
  --bad-edge: #4a2a20;
  --bad-ink: #f0b6a4;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; }

/* --- The frame the whole client sits in -------------------------------- */

/*
 * Two shapes, chosen by the class on <body>.
 *
 * `is-app` is the mailbox: a bar across the top and three panes below it that
 * fill the window and scroll independently. That independence is the whole
 * point - a page that scrolls as one document takes the folder list and the
 * toolbar with it the moment a message is long, so the buttons somebody needs
 * are off screen exactly when they have finished reading.
 *
 * `is-doc` is everything else - signing in, settings, writing a message.
 * Those are documents: one column, centred, scrolling normally.
 */
html { height: 100%; }

body.is-app {
  height: 100dvh;
  display: grid;
  grid-template-rows: auto 1fr;
  /* Children of a grid row will not shrink below their content without this,
     which is what makes an inner pane scroll instead of the window. */
  min-height: 0;
  overflow: hidden;
}

.top {
  display: flex;
  align-items: center;
  gap: .8rem;
  padding: .55rem 1rem;
  background: var(--card);
  border-bottom: 1px solid var(--line);
  z-index: 5;
}

/*
 * The backup's warning: a strip under the bar, on the notice colours. In the
 * mailbox the body is a grid of two rows, the bar and the panes, so the strip
 * needs a row of its own or the panes are pushed into an implicit one and
 * stop filling the window.
 */
.backup-warning {
  margin: 0;
  padding: .5rem 1rem;
  font-size: .85rem;
  background: var(--bad-wash);
  color: var(--bad-ink);
  border-bottom: 1px solid var(--bad-edge);
}
body.is-app.has-warning { grid-template-rows: auto auto 1fr; }

.top__mark { font-weight: 700; letter-spacing: .02em; text-decoration: none; flex: none; }
.top__mark-soft { font-weight: 400; color: var(--muted); }
.top__end { display: flex; align-items: center; gap: .35rem; margin-left: auto; }
.top__who { color: var(--muted); font-size: .85rem; margin-right: .3rem; }
.top form { margin: 0; display: flex; }

/* The search, in the bar where every real client puts it. Centred and capped
   so it does not stretch to a metre wide on a big screen. */
.search {
  position: relative;
  flex: 1 1 26rem;
  max-width: 34rem;
  margin: 0 auto;
}

.search__icon {
  position: absolute;
  left: .65rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--muted);
  pointer-events: none;
}

.search input {
  width: 100%;
  padding: .45rem .8rem .45rem 2.2rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg);
  color: inherit;
  font: inherit;
  font-size: .9rem;
}
.search input:focus { outline: 2px solid var(--accent); outline-offset: -1px; }

/*
 * The way out of a search.
 *
 * Made by mail.js, so it never appears without something behind it - and only
 * shown when there is text to clear, because a cross beside an empty box is a
 * control that does nothing. Room is kept for it on the right of the field at
 * all times, so the text does not shift sideways the moment it appears.
 */
.search input { padding-right: 2.2rem; }
.search__clear {
  position: absolute;
  right: .45rem;
  top: 50%;
  transform: translateY(-50%);
  display: none;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}
.search.has-text .search__clear { display: inline-flex; }
.search__clear .icon { width: .9rem; height: .9rem; }
.search__clear:hover,
.search__clear:focus-visible { background: var(--accent-soft); color: var(--ink); }

/* --- Layout ------------------------------------------------------------ */

.is-doc .stage { max-width: 1240px; margin: 0 auto; padding: 1.1rem; }

/*
 * Three panes: folders, list, message. The two widths are custom properties so
 * the drag handles can move them - see the grips below and mail.js. The
 * fallbacks are what somebody who has never dragged anything gets, and what
 * a double-click puts back.
 */
.is-app .stage {
  display: grid;
  grid-template-columns: var(--rail-w, 13.5rem) var(--list-w, 25rem) 1fr;
  /* One row, and it fills rather than being sized to its tallest pane. */
  grid-template-rows: minmax(0, 1fr);
  min-height: 0;
  overflow: hidden;
}

.is-app .stage > * { min-width: 0; min-height: 0; }

/*
 * Something is on its way.
 *
 * Moving between messages no longer reloads the page, which is the point -
 * but it also removes the browser's own spinner, and a screen that sits
 * perfectly still after a click is a screen that looks broken. This is put on
 * only after 150 milliseconds (see mail.js), so on a fast answer it never
 * appears at all and nothing flickers.
 */
body.is-loading::before {
  content: "";
  position: fixed;
  inset: 0 0 auto;
  height: 2px;
  z-index: 40;
  background: var(--mark);
  transform-origin: 0 50%;
  animation: mail-waiting 1.1s ease-in-out infinite;
}
@keyframes mail-waiting {
  0% { transform: scaleX(0); }
  60% { transform: scaleX(.75); }
  100% { transform: scaleX(1); opacity: .3; }
}
/* A person who has asked not to be moved gets the bar without the sweep. */
@media (prefers-reduced-motion: reduce) {
  body.is-loading::before { animation: none; }
}

/* --- The rail ---------------------------------------------------------- */

.rail {
  display: flex;
  flex-direction: column;
  gap: .1rem;
  padding: .8rem .6rem;
  border-right: 1px solid var(--line);
  /* So the grip can be pinned to this pane's own right edge. */
  position: relative;
  /*
   * NOT the scroller, and not clipping either. Two things depend on that: the
   * grip is absolutely positioned in here and would scroll away with the
   * folders, and the Kortkommandon panel is wider than the rail on purpose -
   * `overflow-y: auto` computes `overflow-x` to `auto` as well, which clipped
   * it and put a horizontal scrollbar under the folders. The folder list
   * below does the scrolling instead.
   */
  overflow: visible;
}

/*
 * The drag handles between the panes.
 *
 * Added by mail.js, never in the template: without the script they would be
 * six pixels of nothing that look draggable and are not, which is the same
 * fault as an attach button with no file reader behind it. They sit ON the
 * border rather than in a column of their own, so the layout is identical
 * whether they exist or not.
 *
 * Wider than they look. The visible line is one pixel; the target is eight,
 * because a one-pixel target is a target nobody hits.
 */
.grip {
  position: absolute;
  top: 0;
  bottom: 0;
  /* Inside the pane's own edge, never hanging over it: both panes clip their
     overflow, so a handle sticking out would be cut off - and on the rail,
     which scrolls, it would have added a horizontal scrollbar as well. */
  right: 0;
  width: 8px;
  cursor: col-resize;
  z-index: 6;
  /* No fill of its own: the pane's border IS the line, and this is the handle
     for it. It lights up under the pointer so it can be found at all. */
  background: none;
  border: 0;
  padding: 0;
}
.grip::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  width: 2px;
  background: transparent;
  transition: background .12s;
}
.grip:hover::after,
.grip:focus-visible::after,
.is-dragging .grip.is-active::after { background: var(--mark); }
.grip:focus-visible { outline: none; }

/* While a grip is being dragged, nothing else on the page should be selecting
   text or swallowing the pointer - including the message frame, which is a
   different document and would otherwise eat every move event. */
body.is-dragging { user-select: none; cursor: col-resize; }
body.is-dragging iframe { pointer-events: none; }

/* The part that scrolls, when there are more folders than room. */
.rail__folders {
  display: flex;
  flex-direction: column;
  gap: .1rem;
  margin-top: 1rem;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

.rail a,
.shortcuts summary {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .45rem .7rem;
  border-radius: 8px;
  text-decoration: none;
  font-size: .9rem;
  color: var(--muted);
}

/*
 * One glyph column, whatever is standing in it.
 *
 * "Sen ligger inte alla ikoner i linje på mobil." Measured at 390: a system
 * folder's icon is a 20x20 box, a self-made label's colour dot is 9.6x9.6, so
 * the word after it started 10.4px further left on every label than on every
 * folder. Vertically they were already fine - `align-items: center` on the row
 * had seen to that - and the horizontal difference is what the eye reads as
 * "not in line", because the words are what it is scanning down.
 *
 * The dot keeps its size and is given an icon's FOOTPRINT with margin, rather
 * than being grown to 20px (a dot that big is a bullet) or wrapped in a span
 * (which would mean touching every template that draws one). `--rail-glyph` is
 * declared here and re-declared at the phone breakpoint, where `.icon` is
 * 20px instead of 18px, so the two numbers cannot drift apart.
 */
.rail { --rail-glyph: 18px; }
.rail .dot { margin-inline: calc((var(--rail-glyph) - .6rem) / 2); }

.rail__folders a:hover { background: var(--accent-soft); color: var(--ink); }
.rail__folders .is-here { background: var(--accent-soft); color: var(--ink); font-weight: 600; }
.rail__label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.folder-count { font-size: .78rem; color: var(--muted); }
.folder-count--unread { color: var(--mark); font-weight: 700; }

/* --- Selecting several -------------------------------------------------- */

/*
 * One row, never two. Five words wrapped onto a second line inside a narrow
 * column is what this bar used to do, so the verbs are icons with a real
 * label on each - and `flex-wrap: nowrap` keeps the promise.
 */
.bulk-bar {
  display: flex;
  flex-wrap: nowrap;
  gap: .15rem;
  align-items: center;
  padding: .35rem .5rem;
  border-bottom: 1px solid var(--line-soft);
  /* A last resort, so nothing can be clipped away with no way to reach it. */
  overflow-x: auto;
  scrollbar-width: none;
}
.bulk-bar::-webkit-scrollbar { display: none; }

/*
 * ...and WITHOUT a script it may use a second line after all.
 *
 * The rule above is about the icons: five words wrapped inside a narrow column
 * is what this bar used to do, and `nowrap` is what stopped it. That argument
 * is about the row somebody sees forty times a day, and it still holds - with
 * the script running the two fallbacks are clipped to a pixel and the row is
 * exactly the icons.
 *
 * Without one, the same row also carries a folder picker, an "Etikettera"
 * button, a text box and a "Skapa och etikettera" button. Measured at 1280
 * with a 400px list pane: everything from the picker rightwards sat past the
 * pane's edge, reachable only by a sideways scroll with nothing to say it was
 * there. A control nobody can find is the no-script path failing quietly,
 * which is the whole thing these fallbacks exist to prevent - so on that path,
 * and only on it, two lines beat one.
 */
html:not(.has-js) .bulk-bar { flex-wrap: wrap; row-gap: .3rem; }

.bulk-bar__split { width: 1px; height: 1.1rem; background: var(--line); margin: 0 .35rem; }

/* A square button whose whole job is one icon. Big enough for a thumb. */
.iconbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--muted);
  cursor: pointer;
  text-decoration: none;
}
.iconbtn:hover { background: var(--accent-soft); color: var(--ink); }
.iconbtn--danger:hover { background: var(--danger); color: var(--on-mark); }
.iconbtn.is-on { color: var(--mark); }

.icon { width: 18px; height: 18px; flex: none; }
.sprite { display: none; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  background: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--muted);
  font: inherit;
  font-size: .8rem;
  padding: .25rem .7rem;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
}
.chip:hover { background: var(--accent-soft); color: var(--ink); }
.chip--danger { color: var(--danger); }

/* The checkbox sits beside the row rather than inside the link, or ticking
   one would open it. */
.list li { display: flex; align-items: flex-start; }
.list .pick { margin: .95rem .1rem .95rem .7rem; flex: none; }
.list li > a { flex: 1; min-width: 0; }

.star { color: var(--mark); margin-right: .2rem; }
.answered { color: var(--muted); margin-right: .25rem; }

.scope-note {
  margin: 0;
  padding: .45rem .8rem;
  font-size: .78rem;
  color: var(--muted);
  border-bottom: 1px solid var(--line-soft);
}

/* A full-bleed strip at the top of the list, not a rounded box: .ok and .bad
   come later in this file and would otherwise put a radius and a bottom margin
   inside a bordered strip. */
.flash { padding: .6rem .8rem; font-size: .85rem; border-bottom: 1px solid var(--line-soft); }
.flash.ok, .flash.bad { margin: 0; border-radius: 0; }

.paging {
  display: flex;
  align-items: center;
  padding: .55rem .8rem;
  margin: 0;
  border-top: 1px solid var(--line-soft);
  font-size: .85rem;
}

/* --- The two panes ------------------------------------------------------ */

/*
 * Each is a column that scrolls on its own. The list keeps its toolbar and
 * its pager in view while the messages move; the message keeps its buttons in
 * view while the text moves.
 */
.list-pane {
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--line);
  overflow: hidden;
  position: relative;
}

.list-pane .bulk { display: flex; flex-direction: column; min-height: 0; flex: 1; }

.read-pane { display: flex; flex-direction: column; overflow: hidden; }
.read-pane__scroll { flex: 1; min-height: 0; overflow-y: auto; }
.read-pane__back { display: none; }

.card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.pad { padding: 1.1rem 1.2rem; }







/* --- The list ---------------------------------------------------------- */

/* The scroll is the pane's, not the list's: a max-height here would leave the
   list scrolling inside a column that also scrolls, which is two scrollbars
   and a lost place every time. */
.list { list-style: none; margin: 0; padding: 0; flex: 1; min-height: 0; overflow-y: auto; }
.list li { border-bottom: 1px solid var(--line-soft); }
.list li:last-child { border-bottom: 0; }
.list a { display: block; padding: .6rem .8rem; text-decoration: none; }
.list a:hover { background: var(--accent-soft); }
.list .is-open { background: var(--accent-soft); box-shadow: inset 3px 0 0 var(--mark); }

.row-top { display: flex; align-items: baseline; gap: .5rem; }

.row-from {
  font-weight: 600;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.row-when { color: var(--muted); font-size: .78rem; white-space: nowrap; }

.row-subject {
  display: block;
  color: var(--ink);
  font-size: .9rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Enough of the message to know whether it needs opening. One line, clipped:
   a list where every row is two lines of body text is a list nobody scans. */
.row-preview {
  display: block;
  color: var(--muted);
  font-size: .82rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.is-unread .row-from::before {
  content: "";
  display: inline-block;
  width: .45rem;
  height: .45rem;
  border-radius: 50%;
  background: var(--mark);
  margin-right: .45rem;
  vertical-align: middle;
}

.is-unread .row-subject { font-weight: 600; }

.row-out .row-from::after {
  content: "skickat";
  margin-left: .5rem;
  font-size: .7rem;
  font-weight: 400;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0 .4rem;
}

/* --- A message --------------------------------------------------------- */

.msg-head { padding: 1.1rem 1.2rem .8rem; border-bottom: 1px solid var(--line-soft); }
.msg-subject { font-size: 1.15rem; font-weight: 600; margin: 0 0 .35rem; }
.msg-meta { color: var(--muted); font-size: .85rem; }
.msg-body { padding: 1.2rem; white-space: pre-wrap; overflow-wrap: anywhere; }
.msg-body a { color: var(--mark); }

/*
 * The message's own markup, in a frame that has no script to size itself with.
 *
 * That is the cost of the sandbox and it is worth paying: the alternative to a
 * fixed height is `allow-scripts` so the document can measure itself and post
 * its height out, which is exactly the token that must never be on this frame.
 * So the height is a guess - tall enough for a normal message, short enough
 * that a two-line one is not mostly white - and `resize: vertical` hands the
 * disagreement to the reader, who can drag it and is the only one who knows
 * how long the message is.
 */
.msg-frame {
  display: block;
  width: 100%;
  height: 60vh;
  min-height: 320px;
  border: 0;
  /* White, because the document inside is. Without this the frame paints the
     page's dark background for the instant before the message loads, which
     reads as a flash on every click. */
  background: #fff;
  resize: vertical;
  overflow: auto;
}

/* Images withheld, and why. */
.images-off {
  margin: 0;
  padding: .7rem 1.2rem;
  border-bottom: 1px solid var(--line-soft);
  background: var(--accent-soft);
  font-size: .85rem;
}

.images-off a { color: var(--mark); margin-left: .3rem; }
.images-off .hint { display: block; margin-top: .15rem; }

.view-as {
  margin: 0;
  padding: .5rem 1.2rem;
  border-top: 1px solid var(--line-soft);
  font-size: .8rem;
}

.view-as a { color: var(--muted); }

.thread { border-top: 1px solid var(--line-soft); background: var(--bg); }
.thread .msg-body { padding: .3rem 1.2rem .9rem; }
.thread-mark { padding: .7rem 1.2rem 0; color: var(--muted); font-size: .8rem; }

/*
 * One letter in a conversation, with its own handles.
 *
 * The replies used to be four lines of text stacked under each other and not a
 * single control among them, so a back-and-forth turned into one lump: there
 * was no way to forward the third message, or throw away the second, or open
 * one on its own. The buttons appear on hover with a pointer and are simply
 * always there without one, because a control that only exists while you are
 * touching it is a control a touch screen does not have.
 */
.thread-msg { border-top: 1px solid var(--line-soft); }
.thread-msg:first-of-type { border-top: 0; }
.thread-msg:target { background: var(--accent-soft); }

.thread-msg__head {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .8rem 1.2rem .2rem;
}
.thread-msg__who { flex: 1; min-width: 0; }
.thread-msg__tools { display: flex; align-items: center; gap: .1rem; flex: none; }
.thread-msg__tools .iconbtn { width: 1.9rem; height: 1.9rem; }
.thread-msg__tools .icon { width: 16px; height: 16px; }

@media (hover: hover) and (pointer: fine) {
  .thread-msg__tools { opacity: 0; transition: opacity .12s; }
  .thread-msg:hover .thread-msg__tools,
  .thread-msg:focus-within .thread-msg__tools { opacity: 1; }
}

/*
 * The message's own toolbar, at the TOP of the pane and staying there.
 *
 * It used to sit under the reply box at the bottom of the document, which
 * meant forwarding something you had just read was: read, scroll past the
 * thread, scroll past the reply box, then press. Reading is what puts you at
 * the bottom of a message and answering is what you do from there - but
 * archiving, forwarding and throwing away are decisions about the message as
 * a whole, and they belong where you can see them the whole time.
 */
.msg-bar {
  display: flex;
  gap: .3rem;
  align-items: center;
  flex-wrap: nowrap;
  padding: .45rem .7rem;
  border-bottom: 1px solid var(--line);
  background: var(--card);
  /*
   * NOT a scroller on a wide screen, and that is load-bearing rather than
   * tidying: `overflow-x: auto` computes `overflow-y` to auto as well, so it
   * CLIPS anything positioned outside the bar - which is exactly what the
   * colour palette hanging below its button is. It opened, and was invisible.
   *
   * The bar only needs to scroll when the buttons do not fit, which is the
   * narrow case, and the media query below is where that is said.
   */
  overflow: visible;
}
.msg-bar form { margin: 0; display: flex; }
/* What the pane is showing, when there is no subject to say it - writing a
   new message rather than reading one. */
.msg-bar__title { font-weight: 600; font-size: .95rem; margin-left: .3rem; }
.spacer { flex: 1; }

/* The reply box, which is the one thing that does belong at the bottom. */
.reply { padding: 1.1rem 1.2rem; border-top: 1px solid var(--line-soft); }
.reply textarea { min-height: 7rem; }

/* --- Forms ------------------------------------------------------------- */

label { display: block; margin: .9rem 0 .3rem; font-weight: 600; font-size: .88rem; }

input[type=text],
input[type=email],
input[type=password],
textarea {
  width: 100%;
  padding: .6rem .7rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--card);
  color: inherit;
  font: inherit;
}

textarea { min-height: 9rem; resize: vertical; }

/* A rectangle with the shared radius, not a 999px pill. The pill was the
   other half of "two products": every primary button in the operator panel is
   a 6px rectangle, and a person moving between the two several times a day
   should not be told, by the shape of the button, that they have changed
   product. The pills that remain are the search field and the folder rows,
   where a pill is the shape the thing IS rather than a house style. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .45rem;
  background: var(--mark);
  color: var(--on-mark);
  border: 0;
  border-radius: var(--radius);
  padding: .55rem 1.15rem;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
}
.btn:hover { filter: brightness(1.06); }
.btn--quiet { background: none; color: var(--muted); border: 1px solid var(--line); }

/*
 * A button that is really a link: "Avbryt", "Logga in som någon annan".
 *
 * Still here because four templates still say it. It lived in the top bar's
 * block and went out with that block when the bar was rebuilt, which left
 * those four rendering as the browser's default grey box - the rule is worth
 * keeping precisely because the markup that needs it is spread about.
 */
.linkbtn {
  background: none;
  border: 0;
  padding: 0;
  color: var(--muted);
  font: inherit;
  font-size: .85rem;
  cursor: pointer;
  text-decoration: underline;
}
.linkbtn:hover { color: var(--ink); }
.btn--danger { background: none; color: var(--danger); border: 1px solid var(--line); }

.hint { color: var(--muted); font-size: .84rem; margin-top: .4rem; }
/* Centred in whatever holds it. In the reading pane that is a flex column
   filling the window, so `margin: auto` puts "Välj ett meddelande" in the
   middle of the pane rather than at the top of a tall empty box. */
.empty { padding: 3rem 1rem; text-align: center; color: var(--muted); margin: auto; }

/*
 * The two notices, on the notice trios rather than on a literal and a borrowed
 * hover wash.
 *
 * `.bad` was `background: #fbe9e6` - a colour that exists in the light theme
 * only, with a second rule in a `prefers-color-scheme` block near the top of
 * the file supplying its dark twin. Two places, and the dark one was easy to
 * miss. `.ok` was worse: it painted itself with `--accent-soft`, which is the
 * ROW UNDER THE POINTER, so "that worked" looked exactly like "your mouse is
 * here". They each get an edge now, because a notice with a ground and no
 * edge dissolves into the page at the one moment it is being looked for.
 */
.bad {
  background: var(--bad-wash);
  border: 1px solid var(--bad-edge);
  color: var(--bad-ink);
  padding: .7rem .9rem;
  border-radius: var(--radius);
  margin-bottom: 1rem;
}

.ok {
  background: var(--good-wash);
  border: 1px solid var(--good-edge);
  color: var(--good-ink);
  padding: .7rem .9rem;
  border-radius: var(--radius);
  margin-bottom: 1rem;
}



/*
 * The one-message pages: refused, and the mailbox that has no web access.
 *
 * A class, because these carried style="max-width:…" and this host's policy is
 * style-src 'self' with no 'unsafe-inline' - which governs the attribute too,
 * so the width was being dropped by every browser that read the header.
 */
.narrow { max-width: 560px; }

/* --- The second factor, wherever it is shown ---------------------------- */

/*
 * The settings window itself is at the foot of this file, under "Settings,
 * inside the client". What is left here is the enrolment: a code to scan, a
 * key to type and eight recovery codes, all of which are also reachable from
 * the sign-in flow.
 *
 * `.settings` used to be `display: grid; gap: 1rem` right here, with one
 * `.card` per subject in it, and that pairing is what stopped the page
 * scrolling. See the note over `.settings` below.
 */

/* A page's own heading, with whatever belongs beside it. Distinct from
   .msg-head, which is the top of a MESSAGE and was being borrowed for this. */
.page-head { display: flex; align-items: flex-start; gap: 1rem; margin-bottom: .2rem; }
.page-head > div { flex: 1; min-width: 0; }
.page-title { font-size: 1.35rem; font-weight: 600; margin: 0; }
.page-head .hint { margin-top: .15rem; }

/*
 * The enrolment code. White plate under it whatever the page's theme is: a
 * scanner reads dark-on-light, and qrcode-svg draws black on transparent - so
 * on the dark theme it was black on near-black and scanned by nothing.
 */
.qr {
  background: #fff;
  border-radius: 10px;
  padding: .8rem;
  width: max-content;
  margin: .8rem 0;
}
.qr svg { display: block; }

/*
 * The key and the recovery codes: monospaced, spaced out, and big enough to
 * read off a screen while typing into a telephone. Both are shown exactly
 * once, so being hard to read is not a small fault.
 */
.secret-key {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 1.15rem;
  letter-spacing: .12em;
  word-break: break-all;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: .6rem .8rem;
  margin: .3rem 0 1rem;
}

/*
 * The passwords for mail programs: one row a device, with its name, when it
 * was last used, and the button that takes it away.
 */
.devices { list-style: none; margin: 0 0 1rem; padding: 0; }
.devices li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .4rem .8rem;
  padding: .5rem 0;
  border-bottom: 1px solid var(--line-soft);
}
.devices__name { font-weight: 600; }
.devices__when { color: var(--muted); font-size: .85rem; flex: 1 1 14rem; }
.devices form { margin: 0; }
.device-setup { display: grid; grid-template-columns: max-content 1fr; gap: .2rem .9rem; margin: .6rem 0 1rem; font-size: .9rem; }
.device-setup dt { color: var(--muted); }
.device-setup dd { margin: 0; }

.codes {
  list-style: none;
  margin: 1rem 0;
  padding: 0;
  display: grid;
  gap: .4rem;
  /* Wide enough for a whole code on one line: they are read off a screen and
     typed into a telephone, and a code broken across two lines is read wrong
     at exactly the moment there is no second chance. */
  grid-template-columns: repeat(auto-fit, minmax(13.5rem, 1fr));
}

.codes li {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 1rem;
  letter-spacing: .06em;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: .45rem .6rem;
  text-align: center;
}

/* An otpauth: URL is long and has no spaces in it. */
.wrap { word-break: break-all; }

.signin { max-width: 380px; margin: 12vh auto; }
.signin h1 { font-size: 1.2rem; margin: 0 0 1rem; }
/* A label that admits half of itself is optional, without a second line. */
.soft { font-weight: 400; color: var(--muted); }
.signin code { font-size: .85em; background: var(--bg); padding: 0 .2em; border-radius: 3px; }

details summary { cursor: pointer; color: var(--muted); font-size: .85rem; margin-top: 1rem; }

/* --- Compose ------------------------------------------------------------ */

/*
 * The form is the scrolling surface, not a card inside one.
 *
 * `.read-pane__scroll` used to sit between them, and then the send button
 * scrolled away with the text: in a long message you had to scroll back up -
 * or down - to find it. Making the form itself the scroller lets the button
 * row stick to the bottom of the pane, which is where every mail client puts
 * it and where the hand already is.
 */
.compose {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}
.compose-note { margin: .8rem 1.2rem 0; }

/* --- The addressed rows ------------------------------------------------- */

/*
 * Etiketten till vänster, rutan utan ram, hårstreck mellan raderna.
 *
 * A label above each box turns four fields into a form to be filled in. In a
 * mail client they are pre-printed lines to be written after, and the whole
 * difference is where the word sits and whether the box has an outline.
 */
.cfields { border-bottom: 1px solid var(--line-soft); }

.cfield {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: 0 1.2rem;
  border-top: 1px solid var(--line-soft);
  min-height: 2.9rem;
  /* The suggestion list hangs off this row. */
  position: relative;
}
.cfield:first-child { border-top: 0; }

/*
 * `display` and `margin` restated, not inherited.
 *
 * The bare `label` rule above is `display: block; margin: .9rem 0 .3rem`,
 * written for a form where the label sits on its own line above its field.
 * Here it sits IN the line, and that margin pushed every row apart by a third
 * of its own height.
 */
.cfield__name {
  flex: none;
  display: flex;
  align-items: center;
  width: 5.2rem;
  margin: 0;
  color: var(--muted);
  font-weight: 500;
  font-size: .85rem;
}

/*
 * No border, no background, no focus ring of its own.
 *
 * The row is the control. A box drawn around the text puts four rectangles
 * down the top of the pane, which is the blankett this replaced; focus is
 * shown by the row instead, below.
 *
 * Selected through `.cfields` for one reason, and it is worth writing down:
 * `input[type=text]` in the form rules above is specificity (0,1,1), because
 * an attribute selector counts where a bare element name does not - so a plain
 * `.cfield__box` at (0,1,0) LOSES to it, however far down the file it sits.
 * Every box kept its border and its white ground, and the pane still looked
 * like a form.
 */
.cfields .cfield__box {
  flex: 1;
  min-width: 0;
  width: auto;
  border: 0;
  border-radius: 0;
  background: none;
  padding: .55rem 0;
  font: inherit;
  color: var(--ink);
}
.cfields .cfield__box:focus { outline: none; }
.cfields .cfield__box::placeholder { color: var(--muted); opacity: .7; }

/* Focus on the ROW, so it reads as a line being written rather than a box
   being edited. Keyboard-only: a click already shows a caret. */
.cfield:focus-within { background: var(--accent-soft); }

.cfield__aside {
  flex: none;
  display: flex;
  gap: .7rem;
  font-size: .8rem;
}
.cfield__hint { color: var(--muted); }
.cfield__more { font-size: .8rem; }

/*
 * Kopia and Hemlig kopia, closed until asked for.
 *
 * Hidden by the stylesheet only once the script has said it is running -
 * without it the buttons that open them do nothing, and a row you cannot
 * reach is worse than two rows you did not need. `is-open` is set by the
 * template when a draft already has an address in one, so saving a draft with
 * a Cc and reopening it cannot hide the Cc.
 */
.cfields.is-ready .cfield[data-row]:not(.is-open) { display: none; }
/* The link that opens a row goes away with it, not the other one. */
.cfields.is-ready .cfield__more.is-open { display: none; }

/* --- The suggestion list ------------------------------------------------ */

/*
 * Our own list rather than the browser's `<datalist>` popup.
 *
 * The datalist is still in the markup and is still what works without this
 * script - but it cannot show a name above an address, cannot be styled, and
 * on several browsers only appears after a keystroke that matches something.
 * A list of `info@`, `kontakt@` and `bokningar@` with no names is not a
 * suggestion, it is a quiz.
 */
.suggest {
  position: absolute;
  top: 100%;
  /* Under the field, not under the row: padding + label + gap. */
  left: 7rem;
  right: 1.2rem;
  z-index: 40;
  margin: 0;
  padding: .25rem;
  list-style: none;
  max-height: 15rem;
  overflow-y: auto;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: .6rem;
  box-shadow: 0 .6rem 1.6rem rgb(0 0 0 / .18);
}

.suggest__one {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  width: 100%;
  padding: .45rem .6rem;
  border: 0;
  border-radius: .4rem;
  background: none;
  font: inherit;
  color: var(--ink);
  text-align: left;
  cursor: pointer;
}
.suggest__one:hover,
.suggest__one.is-on { background: var(--accent-soft); }
.suggest__name { font-weight: 600; }
.suggest__addr { color: var(--muted); font-size: .85rem; }
/* Whoever has no name gets the address in the strong position instead, so the
   left edge of the list is always the thing you are looking for. */
.suggest__one:not(:has(.suggest__name)) .suggest__addr {
  color: var(--ink);
  font-size: inherit;
}

/* --- The message itself ------------------------------------------------- */

.compose-body {
  flex: 1;
  min-height: 12rem;
  border: 0;
  background: none;
  padding: 1rem 1.2rem;
  font: inherit;
  line-height: 1.55;
  color: var(--ink);
  resize: none;
}
.compose-body:focus { outline: none; }
.compose-body::placeholder { color: var(--muted); opacity: .7; }

/* --- The button row ----------------------------------------------------- */

.compose-bar {
  position: sticky;
  bottom: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: .4rem;
  padding: .7rem 1.2rem;
  background: var(--card);
  border-top: 1px solid var(--line-soft);
}

/* Send is the only thing here that cannot be taken back, so it is the only
   thing here wearing the accent. */
.btn--send { display: inline-flex; align-items: center; gap: .45rem; }

.attach-chips:not(:empty) { padding: 0 1.2rem .6rem; }

/*
 * The paperclip in the bar: icon alone, name kept for anyone not looking at it.
 *
 * `.attach-btn` is drawn by mail.js and carries its own label; in the bar the
 * word is clipped rather than removed, so the button still announces itself.
 */
.attach-slot--bar { display: inline-flex; align-items: center; }
/* Wearing `.iconbtn`, because it stands in a row of them and a pill with a
   border in the middle of four flat icons reads as a different kind of thing. */
.attach-slot--bar .attach-btn {
  width: 2rem;
  height: 2rem;
  padding: 0;
  gap: 0;
  justify-content: center;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--muted);
}
.attach-slot--bar .attach-btn:hover { background: var(--accent-soft); color: var(--ink); }
.attach-slot--bar .attach-btn__text {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Same trick, for a label that only exists for a screen reader. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   The writing toolbar

   Every `.rt-*` rule now lives in @shell/core/public/rich-text.css, served
   beside the script that builds these elements. It moved because the customer's
   own panel loads the same script and had none of the styling: an editable with
   no height, so the message box was not on the screen at all.

   What stays here is the map from that file's six names to this host's palette.
   Nothing else: a rule added below would be a rule the panel does not get, and
   that is exactly the split this was.
   --------------------------------------------------------------------------- */
:root {
  --rt-ink: var(--ink);
  --rt-muted: var(--muted);
  --rt-line: var(--line);
  --rt-accent: var(--accent, #2563eb);
  --rt-hover: var(--accent-soft, rgba(127, 127, 127, .12));
  /*
   * `--bg`, and it has to be a real colour rather than a wash.
   *
   * This said `var(--sunk, rgba(127, 127, 127, .05))` and there is no `--sunk`
   * in this file - there never was - so it always fell through to the
   * translucent grey. That was invisible while the toolbar was ordinary flow
   * and became a fault the day the toolbar became `position: sticky`: the
   * message's own text scrolls underneath it, and through it. The pane's
   * ground is `--card`, so `--bg` is the shade off it that the name asks for
   * and that the panel's own mapping already gives.
   */
  --rt-sunk: var(--bg);
}

/* The signature had a block of its own here and does not any more.

   It was a `<details class="sig">` under the writing area that SHOWED the
   signature and carried a checkbox meaning "add it at send". The words are now
   IN the writing window from the moment it opens - the writer sees what will
   go out under their name, and can change it for this one message - so the
   block had nothing left to display and the checkbox had nothing left to
   decide. The whole of `.sig`, `.sig__summary`, `.sig__who`, `.sig__none`,
   `.sig__text`, `.sig__acts` and `.sig__off` went with it: rules for markup
   nothing emits are rules that look like styling and are not, which is worse
   than none. The reasoning that replaced them is over openingBody() in
   mail-app.js and over the signature button in rich-text.js. */

/* A draft in the list: it is not post, it is unfinished work. */
.row-draft .row-from::after {
  content: "utkast";
  margin-left: .5rem;
  font-size: .7rem;
  font-weight: 400;
  color: var(--mark);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0 .4rem;
}

/* --- What came attached -------------------------------------------------- */

.attached { border-top: 1px solid var(--line-soft); padding: .9rem 1.2rem; }
.attached-h { font-size: .9rem; font-weight: 600; margin: 0 0 .6rem; }
.attached-list { display: flex; flex-wrap: wrap; gap: .8rem; }

.attached-item { margin: 0; max-width: 14rem; }
.attached-item img {
  display: block;
  max-width: 100%;
  height: auto;
  border-radius: 8px;
  border: 1px solid var(--line);
}
.attached-item figcaption { font-size: .78rem; color: var(--muted); margin-top: .25rem; }

/* Everything that is not a photograph: named and sized, and honest about why
   it is not shown. */
.attached-other { flex: 1 1 100%; margin: 0; font-size: .87rem; }
.attached-other .hint { display: block; }

/* A file to save rather than a picture to look at. Reads as a row, not as a
   thumbnail, because there is nothing to see until it is opened. */
.attached-file {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding: .5rem .7rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  text-decoration: none;
  color: var(--ink);
  max-width: 20rem;
}
.attached-file:hover { background: var(--accent-soft); }
.attached-file__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.attached-file .hint { margin: 0; flex: none; }

/* --- The keyboard ------------------------------------------------------- */

/* Where j and k have got to. Distinct from is-open, which is the message
   actually being read - moving the cursor does not open anything. */
.list li.is-cursor { box-shadow: inset 3px 0 0 var(--muted); background: var(--accent-soft); }

/*
 * Picked, which is now something a plain click does.
 *
 * Read off the checkbox rather than kept as a second class in script: the box
 * IS the selection - it is what the bulk form posts - and a class beside it
 * would be a second answer to the same question, wrong the first time
 * somebody ticks one by hand.
 */
.list li:has(input.pick:checked) {
  background: var(--accent-soft);
  box-shadow: inset 3px 0 0 var(--mark);
}

.pick-all { margin-right: .3rem; }

/*
 * The keyboard list, at the foot of the rail rather than loose under the page.
 *
 * It used to be a bare <details> after the layout, which put "Tangentbord" in
 * the whitespace below the folder column looking like something left behind.
 * It belongs with the other things you can press, and its panel floats above
 * the rail so opening it does not push the folders around.
 *
 * Still a <details>: it works with the script absent, and `?` toggles this
 * same element rather than a second mechanism that has to agree with it.
 */
.shortcuts { margin-top: auto; position: relative; }
.shortcuts summary { cursor: pointer; list-style: none; }
.shortcuts summary::-webkit-details-marker { display: none; }
.shortcuts summary:hover { background: var(--accent-soft); color: var(--ink); }

.shortcuts__panel {
  position: absolute;
  bottom: calc(100% + .4rem);
  left: 0;
  width: 17rem;
  max-width: 80vw;
  padding: .7rem .8rem;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 8px 24px rgb(0 0 0 / 14%);
  color: var(--muted);
  font-size: .8rem;
  z-index: 10;
}

.shortcuts ul { list-style: none; margin: 0; padding: 0; }
.shortcuts li { margin: .25rem 0; display: flex; gap: .4rem; align-items: baseline; }
.shortcuts kbd {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 0 .3rem;
  background: var(--bg);
  color: var(--ink);
  flex: none;
}



/* A wide button, for "Skriv nytt" at the head of the rail. */
.rail .btn--wide { width: 100%; color: var(--on-mark); }

/* The attach control, which only exists when the script does. */
.attach-slot:empty { display: none; }
/*
 * Room above, except in the button row.
 *
 * Written as an exclusion rather than overridden further up, because it is the
 * same specificity either way and this rule sits LOWER in the file - so the
 * override lost, silently, and the paperclip hung a third of a line below the
 * icons beside it. An `:not()` here cannot be beaten by where it is written.
 */
.attach-slot:not(.attach-slot--bar) { margin-top: 1rem; }

/*
 * The file input, present and hidden.
 *
 * `Browse… No files selected.` is drawn by the browser, in the operating
 * system's own grey, and there is no property that reaches it - which is why
 * it looked like 1997 next to everything else on this screen. So the input
 * keeps the two jobs only it can do (opening the dialog, and being reachable
 * from the keyboard) and the label below wears the paperclip.
 *
 * Clipped rather than `display: none`: a hidden input is still in the tab
 * order, a display-none one is not, and trading the grey button for a control
 * nobody can reach without a mouse would be the worse bargain.
 */
.attach-picker {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/*
 * A label, and therefore carrying `margin: .9rem 0 .3rem` from the form rules
 * until this line: fourteen pixels above and five below, asymmetric by
 * accident, on a pill that is supposed to sit ON the line. In the button row
 * it made the control fifty-one pixels tall in a row of thirty-two, which is
 * what "konstig placering av ikoner" looked like. The room above belongs to
 * `.attach-slot`, which already has it.
 */
.attach-btn {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  margin: 0;
  padding: .45rem .9rem;
  border: 1px solid var(--line);
  /* Same reason as `.btn`. */
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--ink);
  font-size: .88rem;
  cursor: pointer;
}
.attach-btn:hover { background: var(--accent-soft); border-color: var(--mark); }
/* The ring belongs on what you can see, not on the pixel that has the focus. */
.attach-picker:focus-visible ~ .attach-btn { outline: 2px solid var(--mark); outline-offset: 2px; }

/*
 * One chip per chosen file.
 *
 * The note under the button used to be a sentence naming the files, which
 * reads fine with one and turns into a paragraph with four - and, worse, gave
 * no way to change your mind about the third one without starting the whole
 * choice again. A chip each, with a cross, is the shape every mail client
 * settled on for the same reason.
 */
.attach-list { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .6rem; }
.attach-note { margin: .5rem 0 0; }

.attach-one {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  max-width: 100%;
  padding: .3rem .35rem .3rem .7rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg);
  font-size: .82rem;
  line-height: 1.4;
}
/* A long name shortens; the size and the cross keep their room. */
.attach-one__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.attach-one__size { flex: none; color: var(--muted); font-size: .76rem; }

.attach-one__drop {
  flex: none;
  width: 1.35rem;
  height: 1.35rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}
.attach-one__drop .icon { width: .85rem; height: .85rem; }
.attach-one__drop:hover,
.attach-one__drop:focus-visible { background: var(--accent-soft); color: var(--ink); }

/*
 * Dragging files onto the letter.
 *
 * The outline is on the whole form, because that is what is being dropped
 * onto - "attach this to this message" - and a target the size of a sentence
 * is a target that gets missed. Drawn with an outline rather than a border so
 * nothing moves when it appears.
 */
form.is-dropping {
  outline: 2px dashed var(--mark);
  outline-offset: -4px;
  background: var(--accent-soft);
}

/*
 * The menu a right-click opens on a row.
 *
 * Positioned by script against the pointer, so `position: fixed` and no
 * placement here - the two coordinates are set through the CSSOM, which the
 * policy for this host allows where a `style` attribute in the markup would
 * not be. Everything else about it is a rule, as it should be.
 *
 * These rules used to sit at the very bottom of the file, INSIDE
 * `@media (max-width: 560px)` - a block whose closing brace was missing, so it
 * ran to the end of the file and swallowed them. The menu therefore had no
 * styles at all on any desktop, which is the only place it is ever shown: no
 * `position: fixed`, so it landed in normal flow as a full-width strip at the
 * bottom of the page, and the script's placement did nothing because there was
 * nothing to place. It looked exactly like a menu opening in the wrong corner,
 * and it was reported as one. There is a test that renders the page and reads
 * this rule back.
 */
.rowmenu {
  position: fixed;
  z-index: 60;
  min-width: 11rem;
  padding: .3rem;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 10px 30px rgb(0 0 0 / 22%);
}

.rowmenu__item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  border-radius: 6px;
  padding: .45rem .6rem;
  font: inherit;
  font-size: .88rem;
  color: var(--ink);
  cursor: pointer;
}
.rowmenu__item:hover,
.rowmenu__item:focus-visible { background: var(--accent-soft); outline: none; }
.rowmenu__item--danger { color: var(--danger); }
.rowmenu__item--danger:hover,
.rowmenu__item--danger:focus-visible { background: var(--danger); color: var(--on-mark); }


/* ===========================================================================
 * Widths, last in the file and deliberately so.
 *
 * These used to sit in the middle, which quietly broke most of them: a base
 * rule further down the file beats a media query further up at equal
 * specificity, so the phone got two unread dots instead of one, a full-width
 * floating button, and no preview line - each of them written, correct, and
 * overridden fifty lines later. Anything that must win by width goes here.
 *
 * Widest first, narrowest last, so each tier can refine the one above it.
 * ======================================================================== */

/*
 * The rail shows words when the RAIL is wide enough, not when the window is.
 *
 * This used to hang off the window: between 701 and 1100 pixels the rail was
 * pinned to 3.4rem, its labels were hidden and its grip was taken away
 * altogether. Three things went wrong with that, and all three were reported.
 *
 *   - the pin overrode the drag, so a rail somebody had widened collapsed on
 *     its own when the window came in - and stayed collapsed, because
 *   - the grip was gone, so the only way back out was to make the WINDOW
 *     wider, which is not a thing you should have to do to a mail client, and
 *   - "Skriv nytt" was never part of the hiding. Its label kept its words in a
 *     54-pixel rail, wrapped to two lines and spilled out over the list,
 *     which is the mess in the screenshot that started this.
 *
 * A container query asks the right question instead: is THIS COLUMN too narrow
 * for words? Drag it in and the words go at the moment they stop fitting; drag
 * it out and they come back, at any window size. The window no longer has an
 * opinion about it beyond where the rail starts.
 *
 * Not on a telephone, and that is not a detail. `container-type` brings
 * `contain: layout`, which makes the element a containing block for FIXED
 * descendants as well as absolute ones - and on a phone "Skriv nytt" is
 * `position: fixed`, the floating button under the thumb. Containing it here
 * would drop it inside a horizontal strip that clips its own overflow, which
 * is to say it would vanish.
 */
@media (min-width: 701px) {
  .rail { container-type: inline-size; container-name: rail; }
}

@container rail (max-width: 9rem) {
  /* Icons only. The words live on as the title and the aria-label each row
     already carries, so nothing is lost to somebody reading with the keyboard
     or hovering to check. */
  .rail__label,
  .btn__label,
  .folder-count { display: none; }

  /* The "Etiketter" heading goes with the names it heads. It did not, so a
     window narrow enough to fold the rail showed a section heading standing
     over three unnamed coloured dots - a title for a list whose every entry
     had just been hidden. Nothing is lost: at this width the dots are the
     whole of what the rail can say about a label, and what it cannot say is
     one screen away under Inställningar. */
  .rail__heading { display: none; }

  /* Centred by the children rather than by `align-items` on the rail: a
     container query cannot style its own container, which is the whole point
     of it - the container's size must not depend on what the query does. */
  .rail a,
  .shortcuts summary { justify-content: center; padding: .5rem 0; gap: 0; }
  .rail .btn--wide { padding: .55rem 0; }

  /*
   * The unread count still has to show, or the rail stops answering the one
   * question somebody opens this page for. A dot on the folder rather than a
   * number beside it - there is no room for the number, and "there is
   * something" is what the rail is for; the number is on the list.
   */
  .rail__folders a:has(.folder-count--unread) { position: relative; }
  .rail__folders a:has(.folder-count--unread)::after {
    content: "";
    position: absolute;
    top: .35rem;
    right: .55rem;
    width: .45rem;
    height: .45rem;
    border-radius: 50%;
    background: var(--mark);
  }

  .shortcuts__panel { left: 0; width: 15rem; }
}

/*
 * A narrow window on a desktop, which is not a telephone.
 *
 * Dragging the window in used to drop straight to one pane at 900px, so a
 * half-screen window showed a single column with a back arrow - correct by the
 * rule and wrong by any other measure, because there is plainly room for the
 * list and the message side by side.
 *
 * So there are three widths, not two. Here the rail's DEFAULT becomes the icon
 * width, which buys the ~10rem the second pane needs - a default, not a pin,
 * so a width somebody chose is still theirs. The list narrows too, and the
 * message keeps whatever is left.
 */
@media (max-width: 1100px) and (min-width: 701px) {
  .is-app .stage {
    grid-template-columns: var(--rail-w, 3.4rem) minmax(17rem, var(--list-w, 21rem)) 1fr;
  }
}

/*
 * The phone, built to the shape people already know: Apple Mail.
 *
 * Not a squeezed desktop. On a telephone this is one screen at a time — a list
 * you flick through, and a message that replaces it — and the details are what
 * make it feel native rather than like a website pretending:
 *
 *   - a big title that says which folder you are in, because the rail is gone
 *   - the unread mark is a dot in the left margin, not bold-everything
 *   - three lines a row: who, subject, then two lines of the message, with the
 *     separators inset to where the text starts
 *   - a chevron on the right, so a row reads as something that opens
 *   - writing is a floating button under the thumb
 *   - reading is a full screen with "< Inkorg" top left
 *
 * Which screen is showing comes from `is-reading` in the markup rather than
 * `:has()` in the stylesheet, so it does not depend on how new the browser is.
 */
@media (max-width: 700px) {
  /*
   * One column, and rows that size to their content.
   *
   * Without the rows the grid stretched them: the folder strip and the list
   * split the window between them, so the strip sat in the middle of a
   * hundred pixels of nothing above and below it. `auto 1fr` says what is
   * actually meant - the strip is as tall as a row of chips, the list gets
   * the rest.
   */
  .is-app .stage { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }
  /*
   * Reading, though, is ONE row - and getting that wrong left a third of the
   * screen empty.
   *
   * `auto 1fr` is right for the list screen, where the first row is the folder
   * strip. On the message screen the strip is `display: none` and so is out of
   * the grid entirely, which makes the open message the FIRST item and puts it
   * in the `auto` row: sized to its own content, with the page's background
   * below it wherever the message was shorter than the window. It went
   * unnoticed until the compose pane grew a button row stuck to its bottom
   * edge, because that edge was suddenly a line you could see.
   */
  .is-app .stage.is-reading { grid-template-rows: minmax(0, 1fr); }
  .grip { display: none; }

  /*
   * No bulk bar on a phone.
   *
   * The row checkboxes are hidden here - selecting twenty things is a desk
   * job, and on a 390px screen the box was a 13px target eating the width the
   * preview needs. A toolbar that acts on a selection you cannot make is
   * worse than no toolbar, so it goes with them. Archiving and throwing away
   * live on the message's own toolbar, one tap further in.
   */
  .bulk-bar { display: none; }

  /* The bar gives its room to the search. The brand is a link to the inbox and
     the folder strip right below it already has one.

     `.is-app` and not the bare class, and the reason is what it looked like
     without it: the sign-in page renders the same header, has no search to
     give the room to and no folder strip underneath, so hiding the wordmark
     left an empty dark bar above an unnamed form. On a telephone that is the
     FIRST thing anybody sees of this product, and it did not say what it was.

     The argument for hiding it only holds where the things it defers to
     actually exist, which is inside the client. */
  .top { padding: .45rem .7rem; gap: .5rem; }
  .is-app .top__mark, .is-app .top__who { display: none; }
  .search { flex: 1 1 auto; margin: 0; max-width: none; }
  /* A thumb, not a pointer. The circle stays the size it looks; what grows is
     what counts as a press. */
  .search__clear { width: 2.4rem; height: 2.4rem; right: .15rem; }
  .search input { padding-right: 2.6rem; }

  /* Folders as a strip that scrolls sideways, with no scrollbar eating a row
     of height. */
  .rail {
    flex-direction: row;
    align-items: center;
    gap: .3rem;
    padding: .4rem .6rem;
    border-right: 0;
    border-bottom: 1px solid var(--line);
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
  }
  .rail::-webkit-scrollbar { display: none; }
  .rail__folders { flex-direction: row; margin-top: 0; gap: .3rem; overflow: visible; flex: 0 0 auto; }
  .rail__folders a { border-radius: 999px; padding: .45rem .8rem; white-space: nowrap; }

  /*
   * --- The strip, measured and put on one line -------------------------
   *
   * "Sen ligger inte alla ikoner i linje på mobil." Three separate faults were
   * in that one photograph, and here is each with the number that proved it,
   * read out of Chromium at 390 wide before anything was changed.
   *
   * 1. The self-made labels sat 8.5px BELOW every system folder - top 78.8
   *    against 70.3. `.rail__mine` carries `margin-top .5rem; padding-top
   *    .5rem; border-top 1px` to put a rule above the labels in the vertical
   *    rail; flattened into a row that is 17px of vertical box, and
   *    `align-items: center` then centres the taller box and leaves its
   *    contents low. So the rule is turned on its side here: it becomes a
   *    LEFT border, which is what a separator between two groups in a row
   *    actually is, and the vertical box goes to nought.
   *
   * 2. `ETIKETTER` sat inline among the folders at top 84.7, height 17.4 -
   *    a heading pretending to be a fourth tab, and one that does nothing when
   *    tapped. It is hidden here rather than restyled. The rail already makes
   *    exactly this decision in the other axis: when it is folded to icons
   *    (`@container rail (max-width: 9rem)`) the word goes and the border
   *    above the labels carries the meaning on its own. A separator says
   *    "these are yours" in a strip; a word says it in a column. Not
   *    `visibility: hidden` and not `aria-hidden` - the group keeps its name
   *    for anybody listening, which is the reader who cannot see the border.
   *
   * 3. The strip was 925px wide inside 390 (and inside 430) and gave no sign
   *    of it: the operator's screenshot is scrolled, with Arkiv cut in half at
   *    the left edge and nothing to say Inkorg was off to that side. The mask
   *    fades both edges, so a pill running under a fade reads as "there is
   *    more this way" in whichever direction there is more. It is painted, not
   *    scripted, so it survives JavaScript being off - and it is on the
   *    scroller itself, which means the fade stays at the window's edge while
   *    the content moves under it.
   */
  .rail { --rail-glyph: 20px; }
  /*
   * Both classes, because the base `.rail__mine` rule is further DOWN this
   * file than this media block is - it sits with the dot swatches, hundreds of
   * lines below - and at equal specificity the later rule wins whatever the
   * media query says. Written as `.rail__mine` alone this override silently did
   * nothing and the labels stayed 8.5px low, which is precisely the fault being
   * fixed and would have shipped looking fixed. The element genuinely carries
   * both classes (`class="rail__folders rail__mine"`), so this is a truthful
   * selector rather than a specificity trick.
   */
  .rail__folders.rail__mine {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
    margin-inline-start: .3rem;
    padding-inline-start: .6rem;
    border-inline-start: 1px solid var(--line-soft);
  }
  .rail__heading { display: none; }

  /*
   * ...and the third fault: 1002px of strip inside 390, with nothing saying so.
   *
   * ## Not a mask, and that is worth writing down
   *
   * The first version of this faded both edges with `mask-image` on `.rail`,
   * which is the obvious way and is wrong here for a reason that took a
   * screenshot to find: a mask applies to the whole painted SUBTREE, and
   * `.rail .btn--wide` - the floating "Skriv nytt" button - is a fixed-position
   * child of the rail sitting at the bottom right of the window. The gradient
   * tiles, so at the button's x it was transparent, and the button simply
   * vanished from the phone. Everything measured correctly: its rect was still
   * 49.5px tall at the bottom of the viewport. It was painted and then masked
   * away, which no geometry test would have caught.
   *
   * So the two faults are answered separately, by the two things that actually
   * cause them:
   *
   *   - a pill CUT IN HALF at the left edge is a scroll that stopped
   *     anywhere, so the strip snaps. `proximity` rather than `mandatory`:
   *     mandatory fights a flick that meant to go further, and this is a strip
   *     somebody swipes rather than pages through.
   *   - "there is more to the right" is said by a fade that is a sticky
   *     pseudo-element rather than a mask - it paints its own gradient over
   *     the pills instead of subtracting from the subtree, so nothing else on
   *     the page can be erased by it. Negative margin so it takes no width,
   *     `pointer-events: none` so it is not a dead 22px strip over a tab.
   *
   * Right only, deliberately. A sticky left fade sits at the left edge at
   * scroll position nought too, where there is nothing more to the left - a
   * permanently half-faded Inkorg that looks like a rendering fault. The snap
   * is what answers the left edge, by never leaving half a pill there.
   */
  .rail { scroll-snap-type: x proximity; scroll-padding-inline-start: .6rem; }
  .rail__folders a { scroll-snap-align: start; }
  .rail::after {
    content: "";
    position: sticky;
    right: 0;
    z-index: 2;
    flex: 0 0 1.4rem;
    align-self: stretch;
    margin-inline-start: -1.4rem;
    pointer-events: none;
    background: linear-gradient(to left, var(--bg), transparent);
  }

  /*
   * Writing floats where a thumb already is. In the rail it would be a
   * full-width bar stealing a row from the folders, and on a phone the folders
   * are the thing that has to stay scannable.
   */
  .rail .btn--wide {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    width: auto;
    z-index: 20;
    padding: .85rem 1.25rem;
    box-shadow: 0 6px 20px rgb(0 0 0 / 28%);
  }

  /* Kortkommandon are for keyboards. */
  .shortcuts { display: none; }

  /* One screen at a time. */
  .read-pane { display: none; }
  .stage.is-reading .read-pane { display: flex; }
  .stage.is-reading .rail,
  .stage.is-reading .list-pane { display: none; }
  .stage.is-reading .rail .btn--wide { display: none; }
  .read-pane__back { display: inline-flex; }
  .list-pane { border-right: 0; }

  /*
   * Room under the WHOLE pane, not just the rows: the pager is the last child
   * of the pane, so padding on the list alone left "Äldre →" under the
   * floating button and page two out of reach of a thumb.
   */
  .list-pane { padding-bottom: 4.25rem; }

  /* --- The rows, the way a phone draws them -------------------------------
   *
   * Three lines, a dot in the margin, a chevron on the right, and the hairline
   * starting where the text does. The checkbox is what has to give: selecting
   * several is a desk job, and on a phone it was a 13px target stealing the
   * width that the preview needs.
   */
  .list .pick { display: none; }

  .list li {
    position: relative;
    /* The dot's column, then the text. Matches where the separator starts. */
    padding-left: 1.4rem;
  }

  .list a {
    padding: .7rem 1.9rem .75rem 0;
    border-bottom: 1px solid var(--line-soft);
    gap: .1rem;
  }
  .list li { border-bottom: 0; }
  .list li:last-child a { border-bottom: 0; }

  /* The unread dot, in the margin rather than on the sender's name. */
  .is-unread .row-from::before { content: none; }
  .list li.is-unread::before {
    content: "";
    position: absolute;
    left: .5rem;
    top: 1.15rem;
    width: .5rem;
    height: .5rem;
    border-radius: 50%;
    background: var(--mark);
  }

  /* The chevron, so a row reads as something that opens. */
  .list a::after {
    content: "";
    position: absolute;
    right: .75rem;
    top: 50%;
    width: .45rem;
    height: .45rem;
    margin-top: -.28rem;
    border-right: 2px solid var(--line);
    border-bottom: 2px solid var(--line);
    transform: rotate(-45deg);
  }

  .row-from { font-weight: 600; font-size: .98rem; }
  .row-when { font-size: .78rem; }
  .row-subject { font-size: .9rem; font-weight: 400; }
  .is-unread .row-subject { font-weight: 600; }

  /* Two lines of the message, which is what makes a list worth flicking
     through instead of opening everything. */
  .row-preview {
    white-space: normal;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    line-height: 1.35;
  }

  /* Bigger targets everywhere a finger goes. */
  .iconbtn { width: 2.5rem; height: 2.5rem; }
  .icon { width: 20px; height: 20px; }
  .chip { padding: .45rem .85rem; }
  .bulk-bar { padding: .3rem .4rem; }

  /* The message takes the screen rather than a fixed slice of it: `resize`
     does nothing on a phone, so a short frame is a small window on a long
     letter. */
  .msg-frame { height: auto; min-height: 70vh; resize: none; }
  .msg-head, .msg-body, .reply { padding-left: .9rem; padding-right: .9rem; }
  .msg-subject { font-size: 1.3rem; line-height: 1.25; }
}

/*
 * A narrow phone. The toolbar keeps every button and the two that carry words
 * drop the words — the icon and the tooltip stay, and the row stops scrolling
 * sideways to reach "Kasta".
 */
@media (max-width: 560px) {
  /* Here the buttons genuinely do not fit, so the bar scrolls - and the
     palette is pinned to the window rather than to the bar, or it would be
     clipped by the very thing that lets the buttons be reached. */
  .msg-bar { gap: .15rem; padding: .4rem .4rem; overflow-x: auto; }
  .tintpick { position: static; }
  .tintpick__panel { position: fixed; top: auto; left: .7rem; right: .7rem; width: auto; }
  .msg-bar .chip { padding: .45rem .55rem; }
  .chip__label { display: none; }
  .is-doc .stage { padding: .8rem .7rem; }

  /*
   * The addressed rows, on a screen where 5.2rem of label is a third of it.
   *
   * "Kopia" and "Hemlig kopia" do not fit beside an address field here, so
   * they wrap to a line of their own rather than being hidden: a control that
   * only exists on the wide layout is a control nobody can find on the device
   * they are actually holding.
   */
  .cfield { padding: 0 .7rem .3rem; gap: .4rem; flex-wrap: wrap; }
  .cfield__name { width: 3.6rem; font-size: .78rem; }
  /* The width half only. The SIZE half of this rule used to live here and
     never once applied - see the block below, which is where all four sizes
     are now said and why. */
  .cfield__box { flex-basis: 60%; }
  .cfield__more { font-size: .76rem; }
  .suggest { left: 4.7rem; right: .7rem; }
  .compose-body { padding: .8rem .7rem; }
  .compose-bar { padding: .6rem .7rem; }
  .attach-chips:not(:empty) { padding: 0 .7rem .5rem; }
}

/*
 * THE SIZES THIS HOST'S OWN FIELDS NEED, SO A PHONE DOES NOT ZOOM THEM
 *
 * "När man klickar i skrivfönstret zoomas bilden in en bit och verktygsfältet
 *  längst försvinner." Reported twice, the second time with photographs from
 *  both Safari iOS and Firefox iOS - which are the same engine, which is why
 *  they agreed.
 *
 * The rule iOS applies is one line: a focused editable computing under 16px
 * zooms the page. The argument for answering it here rather than by moving
 * `body` to 16px is in rich-text.css, over the matching block; this file only
 * has to cover the elements that are THIS host's and not the shared editor's.
 *
 * ## Why the old rule did nothing, which is the part worth keeping
 *
 * `@media (max-width: 560px) { .cfield__box { font-size: 16px } }` was written
 * above, with a comment naming this exact bug, and it never applied to
 * anything. `.cfields .cfield__box` further up the file is (0,2,0) and carries
 * `font: inherit`; the rule in the media query is `.cfield__box`, (0,1,0). A
 * media query adds no specificity whatever, so the base rule won at every
 * width and every one of the four addressed rows stayed at the body's 15px.
 * Measured in Chromium at 390 wide, before this block: To, Kopia, Hemlig kopia
 * and Ämne all 15px, `.search input` 14.4px.
 *
 * That is the second time a rule in this file has been silently beaten by a
 * neighbour written further down or with one more class - the `.rail__mine`
 * note at the phone breakpoint is the first, and it says the same thing. The
 * shape of the fix is the same too: match the selector that is actually
 * winning, truthfully, rather than reaching for `!important`.
 *
 * ## The gate
 *
 * The same two clauses as rich-text.css. `pointer: coarse` is the real
 * question - "does this device put a keyboard over the bottom of the screen" -
 * and a width cannot ask it: an iPhone turned sideways is 852px wide and zooms
 * exactly the same. The width clause covers a desk browser dragged narrow, and
 * means neither clause failing on its own can leave this unfixed a third time.
 *
 * `.reply textarea` is in here for the same reason as the rest even though it
 * is on the reading screen: it is an editable somebody types into on a phone,
 * and it was 15px.
 */
@media (pointer: coarse), (max-width: 700px) {
  /*
   * EVERY field, and not only the four that were named.
   *
   * "iPhone-zoomen är ju också för mail.bastion.nu." Reported after the fix
   * below had shipped, and correct: this block named the compose rows, the
   * no-script box, the reply box and the search field - the four on the
   * screens the original complaint was about - and every OTHER field on this
   * host went on zooming. The sign-in password, the address on the sign-in
   * form, the settings screen's text boxes and its two pickers, renaming a
   * folder, a contact's name. Eleven `type="text"` inputs in these templates,
   * and none of them was covered.
   *
   * They set no size of their own at all: `textarea` carries `font: inherit`
   * and the inputs inherit too, so all of them compute to `body`'s 15px -
   * under the sixteen WebKit needs, by one pixel, on a host whose whole point
   * is being read on a phone. A rule per field is how that happened; a rule
   * per ELEMENT is how it stops happening for the next field somebody adds.
   *
   * Bare element selectors, (0,0,1), on purpose. It is the weakest thing that
   * can be written here, so every named rule below still wins - and they all
   * say 16px too, so there is nothing to win. What this covers is exactly the
   * set nobody has named, which is the set that keeps being forgotten.
   */
  input, select, textarea { font-size: 16px; }
  /* Two classes, matching `.cfields .cfield__box` above, because that is the
     rule this has to beat and it is (0,2,0). Written at the same specificity
     and later in the file, so it wins by position - the honest version, on a
     selector the markup genuinely has. */
  .cfields .cfield__box { font-size: 16px; }
  /* The no-script writing box. It is `hidden` whenever rich-text.js has run,
     so this is the size that matters on the path with JavaScript off - which
     is a path this whole panel is built to keep working. */
  .compose-body { font-size: 16px; }
  .reply textarea { font-size: 16px; }
  /* The search box in the top bar. It is on the compose screen too - the bar
     is outside the panes that change - so it is one more thing a thumb can
     land in without leaving the message. */
  .search input { font-size: 16px; }
}

/* --- Folders somebody made, and colours somebody chose ------------------- */

/*
 * Six names rather than free hex, and each one defined for both themes.
 *
 * A colour typed by hand is a colour that is unreadable on one of the two
 * about half the time - which is why mailbox.js takes a name off a list and
 * nothing else. These are the list.
 */
:root {
  --tint-red: #b4451f;
  --tint-amber: #a5761a;
  --tint-green: #3f7d4e;
  --tint-blue: #35618f;
  --tint-purple: #6b4a8f;
  --tint-grey: #6d6659;
}

@media (prefers-color-scheme: dark) {
  :root {
    --tint-red: #e08a5a;
    --tint-amber: #d6ab52;
    --tint-green: #6fb583;
    --tint-blue: #7aa6d6;
    --tint-purple: #a68cc8;
    --tint-grey: #9a9285;
  }
}

.dot {
  flex: none;
  width: .6rem;
  height: .6rem;
  border-radius: 50%;
  background: var(--line);
}
.dot--red { background: var(--tint-red); }
.dot--amber { background: var(--tint-amber); }
.dot--green { background: var(--tint-green); }
.dot--blue { background: var(--tint-blue); }
.dot--purple { background: var(--tint-purple); }
.dot--grey { background: var(--tint-grey); }
/* A folder with no colour still gets a dot, so the labels line up with the
   ones that have one. A row that shifts sideways by six pixels depending on a
   choice somebody made is a list that is harder to read down. */
.dot--none { background: transparent; box-shadow: inset 0 0 0 1px var(--line); }

/* The named folders sit under the built-in ones with a rule between, because
   they are a different kind of thing - a place rather than a question about
   the stamps. */
.rail__mine {
  margin-top: .5rem;
  padding-top: .5rem;
  border-top: 1px solid var(--line-soft);
}

/*
 * The correspondent's colour, as a stripe down the left of the row.
 *
 * Not the row's background: an unread row is already tinted, the open one is
 * tinted differently, and a third background would have three states fighting
 * over the same pixels. A stripe is orthogonal to all of them and survives
 * being both.
 */
.list .tint > a { box-shadow: inset .18rem 0 0 0 var(--tint); }
.tint--red { --tint: var(--tint-red); }
.tint--amber { --tint: var(--tint-amber); }
.tint--green { --tint: var(--tint-green); }
.tint--blue { --tint: var(--tint-blue); }
.tint--purple { --tint: var(--tint-purple); }
.tint--grey { --tint: var(--tint-grey); }

.moveto select {
  max-width: 9rem;
  padding: .3rem .4rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--card);
  color: var(--ink);
  font: inherit;
  font-size: .82rem;
}
/* The plain-form fallback. With the script running the select posts on its
   own, so the button beside it is one control too many. */
html.has-js .moveto__go { display: none; }
/* And the select itself, once the icon has replaced it.

   It stays in the DOM because it is what actually posts - the menu sets its
   value and dispatches `change`, so there is one path out and not two - and
   because without a script it is the only working control. `visibility` rather
   than `display: none`: a hidden-by-display form control is still submitted,
   but a select nobody can see must not be reachable by Tab either. */
html.has-js .moveto {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

/*
 * "Ny etikett…" without a script: a box and a button, in the toolbar.
 *
 * The same arrangement as the select above it and hidden the same way, for
 * the same reason: the control is real, it posts on its own, and the script
 * only replaces it with something smaller. `wireLabelMenu`'s menu entry writes
 * into this box and presses this button, so with the script running there is
 * still exactly one thing that posts.
 *
 * Clipped rather than `display: none`, and the difference matters twice over:
 * a display-none field is still submitted - which is what the menu depends on
 * - but a box nobody can see must not be reachable by Tab either, or the
 * toolbar grows two invisible stops between the star and the read marks.
 */
/*
 * `flex: none` on both, because the bar is `flex-wrap: nowrap` and will happily
 * shrink a text box to nothing rather than overflow. Measured with JavaScript
 * off at 1280: the field came out 24px wide - present, focusable, submitted,
 * and far too narrow to type a label name into. It read as a rendering fault
 * rather than as a control, which for the one path that has to work without a
 * script is the worst place to have one.
 *
 * The bar scrolls sideways rather than wrapping if that runs it out of room -
 * five words wrapped onto a second line is the fault `nowrap` exists to stop,
 * and it is only ever reachable with the script off, where the two fallbacks
 * are the widest things on the row.
 */
.newlabel { display: inline-flex; align-items: center; gap: .3rem; flex: none; }
/*
 * Two classes, and this file has now been bitten by the reason three times -
 * the notes above `.cfields .cfield__box` and `.search input` are the other
 * two. `input[type=text]` in the form rules near the top is specificity
 * (0,1,1), because an attribute selector counts where a bare element name does
 * not, so a plain `.newlabel__name` at (0,1,0) LOSES to it however far down the
 * file it sits. Measured with JavaScript off at 1280: the box came out 369x44
 * - the form rule's `width: 100%` and `padding: .6rem .7rem` - rather than the
 * 9rem pill written here, so it filled the toolbar and pushed the read marks
 * off the end of it.
 */
.newlabel .newlabel__name {
  flex: none;
  width: 9rem;
  padding: .3rem .55rem;
  border: 1px solid var(--line);
  /* `--card`, which is what every other box on this bar is filled with. It
     said `--paper` for one draft - a token this stylesheet does not define, so
     the box rendered transparent over whatever was behind it. Nothing failed
     and nothing logged, which is exactly the fault the class-versus-rule test
     further down exists for, and that test only reads class names. */
  background: var(--card);
  color: var(--ink);
  font: inherit;
  font-size: .82rem;
}
.newlabel .newlabel__name::placeholder { color: var(--muted); opacity: .8; }
/* Three words on one line. The bar is `flex-wrap: nowrap` on purpose - five
   words wrapped inside a narrow column is what it used to do - so a fallback
   that broke in the middle would put the fault straight back. */
.newlabel__go { white-space: nowrap; }
html.has-js .newlabel {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}
/* Just the button's place in the row. It used to be the menu's containing
   block as well - see below for why that was the bug rather than the fix. */
.labelwrap { display: inline-flex; }
/*
 * IT MUST NOT BE ANCHORED INSIDE THE TOOLBAR.
 *
 * "Inget kommer upp alls när jag trycker på ikonen för etiketter."
 *
 * This was `position: absolute; top: calc(100% + .3rem)` against a positioned
 * `.labelwrap`, with a comment arguing that a popover escaping a scrolling
 * pane is a popover clipped in half. The argument is right; the box it was
 * applied to was wrong. `.bulk-bar` above is `overflow-x: auto` so the toolbar
 * can scroll sideways on a phone, and a box with overflow on ONE axis clips on
 * BOTH - so the nearest clipping ancestor was the toolbar itself, not the
 * pane. The menu opens below the toolbar, which put all of it outside the
 * toolbar's padding box: laid out, never painted, in every browser.
 *
 * `position: fixed` is positioned against the viewport, so no ancestor's
 * overflow can reach it, and mail.js places it from the button's own rectangle
 * when it opens - the same mechanism the right-click menu has always used.
 * The inherited `position: fixed` from `.rowmenu` is therefore left alone
 * rather than overridden, and only the things that differ are set here.
 *
 * If this ever goes back to `absolute`, the test that catches it is
 * `test/mail-label-menu.test.js`, which reads these rules; the one that PROVED
 * it is elementFromPoint at the menu's own centre in a real browser, because
 * a laid-out rectangle tells you nothing about whether anything was painted.
 */
.labelmenu {
  z-index: 40;
  min-width: 12rem;
}
.labelmenu[hidden] { display: none; }

/*
 * The colour of a label, as colours.
 *
 * These are radio buttons with `appearance: none`, not buttons: the swatch IS
 * the input, so the row posts a colour with no script anywhere near it. What
 * stood here was a `<select>` listing `red`, `amber`, `grey` - the schema's
 * words, in English, in front of somebody choosing a colour.
 *
 * `.swatch.is-on` used to carry the ring and nothing ever set the class. The
 * ring now hangs off `:checked`, which is a fact about the form rather than a
 * promise somebody has to keep in JavaScript.
 */
.swatches { display: flex; flex-wrap: wrap; gap: .3rem; align-items: center; }

.swatch {
  appearance: none;
  -webkit-appearance: none;
  width: 1.15rem;
  height: 1.15rem;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  flex: none;
}
/* A ring outside the circle, so the swatch keeps its own colour rather than
   having a tick drawn over it. Two shadows: the gap, then the ring. */
.swatch:checked { box-shadow: 0 0 0 2px var(--card), 0 0 0 3.5px var(--ink); }
.swatch:focus-visible { outline: 2px solid var(--mark); outline-offset: 3px; }

.swatch--none { background: none; box-shadow: inset 0 0 0 1px var(--line); }
.swatch--none:checked { box-shadow: inset 0 0 0 1px var(--line), 0 0 0 2px var(--card), 0 0 0 3.5px var(--ink); }
.swatch--red { background: var(--tint-red); }
.swatch--amber { background: var(--tint-amber); }
.swatch--green { background: var(--tint-green); }
.swatch--blue { background: var(--tint-blue); }
.swatch--purple { background: var(--tint-purple); }
.swatch--grey { background: var(--tint-grey); }

/* A marker class, not a look: the row menu reads it to decide whether "out of
   the folder" is an entry worth offering. Declared so the stylesheet test can
   see it is deliberate. */
.list .in-folder { --in-folder: 1; }

/* The rail's own small heading over the labels, so the group has a name
   without a folder icon pretending to be one. */
.rail__heading {
  margin: .8rem .55rem .2rem;
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--muted);
}

/* --- Settings, inside the client ---------------------------------------- */

/*
 * The pane that stands where the list and the message do.
 *
 * `span 2` rather than a layout of its own: the rail keeps the width somebody
 * dragged it to, and settings get everything else - which is what a mail
 * client does with a screen that is one thing rather than two.
 */
.settings-pane {
  grid-column: span 2;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/*
 * Two columns under the bar: the sections, and the section.
 *
 * WHY THIS IS NOT A GRID ANY MORE, because the fault is invisible in a diff
 * and cost this screen its scrolling:
 *
 *   .settings was `display: grid; gap: 1rem` with `flex: 1; min-height: 0;
 *   overflow-y: auto` - which reads as a correct scroller and is not one. Its
 *   rows were implicit, so they were sized `auto`; `align-content` defaults to
 *   `normal`, and in a grid container with a DEFINITE height that stretches
 *   auto rows to fill it. So two 370px cards in a 762px box were stretched to
 *   381px each, scrollHeight came out exactly equal to clientHeight, and the
 *   scroller had nothing to scroll. `.card` carries `overflow: hidden`, so the
 *   part that no longer fitted was clipped rather than merely hidden: the
 *   Spara button under the signature was cut through the middle and the last
 *   two preferences were unreachable at any window size.
 *
 * Measured, not guessed - scrollHeight against clientHeight on every element
 * from <html> down. Nothing in the CSS reads as wrong; only the numbers do.
 *
 * The scroller is now `.settings__body`, which is `display: block`. A block
 * container's children keep their own heights and overflow it, which is the
 * whole reason a document scrolls.
 */
.settings {
  flex: 1;
  min-height: 0;
  display: flex;
}

/*
 * The sections, in a column - not tabs over a stack of cards.
 *
 * Same shape as the folder rail on the other side of the window: rounded
 * highlight on the one you are in, muted when you are not. One idiom for "the
 * thing you are looking at" in both navigations.
 */
.tabs {
  flex: none;
  width: 14.5rem;
  padding: .8rem .55rem;
  border-right: 1px solid var(--line);
  overflow-y: auto;
}
.tabs a {
  display: block;
  padding: .45rem .7rem;
  border-radius: 8px;
  color: var(--muted);
  text-decoration: none;
}
.tabs a:hover { background: var(--accent-soft); color: var(--ink); }
.tabs a.is-here { background: var(--accent-soft); color: var(--ink); }
.tabs__name { display: block; font-size: .9rem; font-weight: 500; }
.tabs a.is-here .tabs__name { font-weight: 600; }
/* What is behind the word, for somebody who does not yet know which of three
   names holds the signature. */
.tabs__note { display: block; font-size: .76rem; line-height: 1.35; color: var(--muted); }

.settings__body {
  flex: 1;
  min-width: 0;
  min-height: 0;
  overflow-y: auto;
  /* Room at the foot so the last row is not flush against the window edge
     when it is scrolled to. */
  padding: 1.3rem 0 4rem;
}

.settings__title {
  font-size: 1.25rem;
  font-weight: 600;
  margin: 0 0 .2rem;
  padding: 0 1.4rem;
}

/* Prose is the one thing on this surface that gets a narrow measure: a line
   of text 1500px wide is unreadable, a row of settings 1500px wide is not. */
.settings__lede,
.settings__empty {
  max-width: 62ch;
  margin: .3rem 0 1.2rem;
  padding: 0 1.4rem;
  color: var(--muted);
  font-size: .88rem;
}
.settings__empty { color: var(--ink); }

.msg-bar__who { color: var(--muted); font-size: .85rem; padding-right: .4rem; }

/*
 * A settings row: the name on the left, what it is set to on the right.
 *
 * The same `.cfields` / `.cfield` idiom as the addressed rows in the compose
 * pane - a name IN the line and a hairline under it - widened, because
 * "Markera som läst när jag öppnar ett meddelande" does not fit in the 5.2rem
 * the address labels use, and because a setting needs the sentence that says
 * why anybody would change it.
 */
.cfields--set { border-bottom: 0; margin-bottom: 1.4rem; }

/*
 * Direct children only, every time.
 *
 * Written as a descendant selector first, and the enrolment paid for it: the
 * code field sits INSIDE a settings row, so `.cfields--set .cfield` reshaped
 * that inner row into the same two-column grid and put the button on a line of
 * its own, half the pane wide. A modifier should describe its own rows and
 * nothing that happens to be nested inside one.
 */
.cfields--set > .cfield {
  display: grid;
  grid-template-columns: minmax(12rem, 26rem) 1fr;
  gap: .4rem 2rem;
  align-items: center;
  padding: .75rem 1.4rem;
  min-height: 0;
}
/* A textarea or a whole enrolment beside its name: the name goes to the top
   of the row rather than floating in the middle of it. */
.cfields--set > .cfield--tall { align-items: start; }
/* The first row keeps its rule, unlike the compose rows: it is what separates
   the section's lines from its heading. `.cfield:first-child` would otherwise
   decide it by whether a hidden CSRF input happens to come first, which is not
   a thing a layout should hang on. */
.cfields--set > .cfield:first-child { border-top: 1px solid var(--line-soft); }
/* The tint on a focused row is for a one-line address in the compose pane. On
   a row as tall as an enrolment it is a hand-sized block of colour, and the
   caret already says where the focus is. */
.cfields--set > .cfield:focus-within { background: none; }
.cfield__side > .cfield__name {
  width: auto;
  display: block;
  color: var(--ink);
  font-size: .9rem;
  font-weight: 600;
}
.cfield__side { min-width: 0; }
.cfield__note {
  max-width: 56ch;
  margin: .15rem 0 0;
  color: var(--muted);
  font-size: .82rem;
  line-height: 1.45;
}
.cfield__set { min-width: 0; }
/* A value that is read rather than set - the address you sign in with. No box
   round it: nothing here is editable, and a box would say otherwise. */
.cfield__value { font-size: .9rem; }
.cfield__do { display: flex; align-items: center; gap: .8rem; margin: .7rem 0 0; }
/* The save at the foot of a form has no name of its own on the left; the row
   above it is what it saves. */
.cfields--set > .cfield--done { padding-top: .2rem; border-top: 0; }

/* A writing surface, so this one keeps its border: a signature is composed,
   not picked. `min-height` restated because the bare `textarea` rule asks for
   nine rems, which is a third of the pane. */
.cfields--set > .cfield .cfield__area { max-width: 42rem; min-height: 6rem; }

/* One of a short list. Sized to its content rather than to the column, so a
   three-number menu is not a 700px-wide box. */
.pick-one {
  padding: .35rem .5rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--card);
  color: var(--ink);
  font: inherit;
  font-size: .88rem;
  min-width: 9rem;
}

/*
 * On and Off, as two words with one of them filled in.
 *
 * A checkbox says its state with a tick, and a tick is easy to misread in a
 * row somebody is skimming. Two named choices say it in a word - and they
 * always post one: an unticked checkbox sends nothing at all, so a setting
 * built on one can be turned on and never off.
 */
.twoway { display: flex; gap: 1.1rem; align-items: center; }
.twoway__one {
  display: flex;
  align-items: center;
  gap: .35rem;
  margin: 0;
  font-size: .88rem;
  font-weight: 400;
  cursor: pointer;
}
.twoway__one input { margin: 0; }

/*
 * A state: a coloured dot and a word, the same shape everywhere.
 *
 * Colour alone cannot be read by everybody and a word alone has nothing to
 * catch the eye, so it is always both. Used for what a thing IS - two-step
 * verification on or off - and for what just happened, beside the control
 * that did it rather than as a banner at the top of a scrolled page.
 */
.state {
  display: flex;
  align-items: baseline;
  gap: .45rem;
  margin: 0;
  font-size: .88rem;
}
.state .dot { flex: none; position: relative; top: .05rem; }
.state--warn { color: var(--ink); }
.state--bad { color: var(--danger); }
.settings__body > .state { padding: 0 1.4rem 1rem; }

/*
 * The labels, one per row.
 *
 * Two forms on a row - saving and removing post to different addresses - so
 * the row is the grid and the forms are `display: contents`, which lets the
 * names and the colours line up in columns all the way down. The hidden CSRF
 * inputs are `display: none` already and take no cell.
 */
.labels { list-style: none; margin: 0; padding: 0; }

.labelrow {
  display: grid;
  grid-template-columns: minmax(9rem, 20rem) auto 1fr auto auto;
  align-items: center;
  gap: .8rem;
  padding: .3rem 1.4rem;
  min-height: 2.6rem;
  border-top: 1px solid var(--line-soft);
}
.labelrow:hover { background: var(--accent-soft); }
.labelrow__edit { display: contents; }

/*
 * A junk sender's row: an address, when it was decided, and the way back.
 *
 * Its own column template because it has three cells rather than five - a
 * label row carries a name box, a swatch strip and two buttons, and none of
 * those exist here. Sharing the five-column grid would leave two empty tracks
 * and push the address away from the edge everything else lines up on.
 */
.labelrow--junk { grid-template-columns: minmax(9rem, 24rem) 1fr auto; }
/* The address in the ink the rest of the client reads in, not the muted grey
   the row's secondary text uses: it is the thing being decided about. */
.labelrow__addr { color: var(--ink); overflow-wrap: anywhere; }

/*
 * The name, written on the row rather than in a box on it.
 *
 * Selected through `.labels` on purpose, the same trap the compose rows
 * document: `input[type=text]` above is (0,1,1) because an attribute selector
 * counts, so a bare `.labelrow__name` at (0,1,0) loses to it however far down
 * the file it sits - and every label kept its outline and its white ground.
 */
.labels .labelrow__name,
.labelrow--new .labelrow__name {
  width: 100%;
  border: 0;
  border-radius: 0;
  background: none;
  padding: .35rem 0;
  font: inherit;
  font-size: .9rem;
  color: var(--ink);
}
.labels .labelrow__name:focus { outline: none; }
.labelrow:focus-within { background: var(--accent-soft); }

.labelrow__held {
  color: var(--muted);
  font-size: .82rem;
  font-variant-numeric: tabular-nums;
}

/*
 * The row's own actions, out of the way until the row is wanted.
 *
 * A Spara and a Ta bort standing to attention on every row for ever is four
 * buttons on a screen with four labels, and none of them is the thing you came
 * to look at. Hover shows them; `:focus-within` shows them for a keyboard,
 * which reaches them a moment after the name field they precede.
 *
 * `visibility` rather than `display`, so the columns do not jump when a row is
 * pointed at.
 */
.rowdo { visibility: hidden; }
.labelrow:hover .rowdo,
.labelrow:focus-within .rowdo { visibility: visible; }
.labelrow .btn--quiet { padding: .3rem .8rem; font-size: .85rem; }

/* The new one, on the line under the last of them - same columns, so it reads
   as the next row rather than as a second form. Its button is always there:
   with no labels at all it is the only thing on the screen to press. */
.labelrow--new { border-top: 1px solid var(--line); margin-top: .6rem; padding-top: .55rem; padding-bottom: .55rem; }
.labelrow--new .labelrow__name::placeholder { color: var(--muted); opacity: .75; }

/* Six digits, typed once. Wide enough for them and no wider - the bare
   `input[type=text]` rule asks for the full width of whatever holds it. */
.code-box { max-width: 8rem; font-size: 1.05rem; letter-spacing: .1em; }
/* In the line with the field and the button, so the bare `label` rule's
   `display: block` and top margin do not push it onto a line of its own. */
.totp-ask { margin: 0; font-size: .88rem; }

@media (max-width: 900px) {
  /*
   * The sections stop being a column and become a row of names across the top:
   * below this width a 14rem rail plus a settings row is two things in the
   * space for one. Still one scroller, and still not the whole surface - the
   * names stay put while the section under them moves, which is what a person
   * switching between two settings needs.
   */
  .settings { flex-direction: column; }
  .tabs {
    display: flex;
    gap: .2rem;
    width: auto;
    padding: .4rem .6rem;
    border-right: 0;
    border-bottom: 1px solid var(--line);
    overflow-x: auto;
    overflow-y: hidden;
  }
  .tabs a { flex: none; }
  .tabs__note { display: none; }
  .cfields--set > .cfield { grid-template-columns: 1fr; gap: .5rem; }
  /* No hover on a telephone, so the row's actions are simply there. */
  .rowdo { visibility: visible; }
  /* Two columns: the name and its colours, then the count and the two things
     that can be done to it, each hard against the right edge rather than
     stretched across half the row. */
  .labelrow { grid-template-columns: 1fr auto; row-gap: .4rem; }
  .labelrow__edit > .rowdo { justify-self: end; }
  .labelrow > .rowdo { grid-column: 2; justify-self: end; }
}

@media (max-width: 700px) {
  .settings-pane { grid-column: 1; }
  .settings__title,
  .settings__lede,
  .settings__empty,
  .settings__body > .state { padding-left: .9rem; padding-right: .9rem; }
  .cfields--set > .cfield,
  .labelrow { padding-left: .9rem; padding-right: .9rem; }
}
