/* Box sizing rules */
*,
*::before,
*::after {
  box-sizing: border-box;
}

/* Remove default padding */
ul[class],
ol[class] {
  padding: 0;
}

/* Remove default margin */
body,
h1,
h2,
h3,
h4,
p,
ul[class],
ol[class],
li,
figure,
figcaption,
blockquote,
dl,
dd {
  margin: 0;
}

/* Set core body defaults */
body {
  min-height: 100vh;
  scroll-behavior: smooth;
  text-rendering: optimizeSpeed;
  line-height: 1.5;
}

/* Remove list styles on ul, ol elements with a class attribute */
ul[class],
ol[class] {
  list-style: none;
}

/* A elements that don't have a class get default styles */
a:not([class]) {
  text-decoration-skip-ink: auto;
}

/* Make images easier to work with */
img {
  max-width: 100%;
  display: block;
}

/* Natural flow and rhythm in articles by default */
article > * + * {
  margin-top: 1em;
}

/* Inherit fonts for inputs and buttons */
input,
button,
textarea,
select {
  font: inherit;
}

/* Remove all animations and transitions for people that prefer not to see them */
@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
:root {
  color-scheme: light dark;

  /* Product foundations. Theme stylesheets only need to replace the
     palette below; spacing, type, shape, and component geometry stay
     identical across every visual theme. */
  --font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", monospace;
  --text-xs: 0.75rem;
  --text-sm: 0.8125rem;
  --text-md: 0.875rem;
  --text-lg: 1rem;
  --text-xl: 1.25rem;

  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;

  --radius-sm: 3px;
  --radius-md: 6px;
  --radius-lg: 9px;
  --control-height: 2rem;
  --toolbar-height: 2.75rem;
  --sidebar-width: clamp(10rem, 15vw, 13rem);
  --drawer-width: 15rem;
  --content-list: 90rem;
  --content-form: 46rem;
  --content-reading: 54rem;
  --shadow-raised: 0 1px 2px rgba(0, 0, 0, 0.09), 0 4px 16px rgba(0, 0, 0, 0.04);

  --fg: #000;
  --fg-dim: #555;
  --fg-muted: #666;
  --fg-faint: #999;

  --bg: #fff;          /* chrome and raised surfaces */
  --bg-well: #f6f6f6;  /* content area behind surfaces */
  --bg-inset: #eee;
  --bg-button: #f5f5f5;
  --bg-active: #f8f8f8;

  --border: #e0e0e0;
  --border-light: #eee;
  --border-faint: #f2f2f2;
  --border-strong: #ccc;

  --link: #15c;
  --link-read: #77c;
  --link-unread: #00c;

  --green: #4a4;
  --green-dark: #008d47;
  --bg-green: #f6fff6;
  --border-green: #afa;
  --flag: #bfa300;
  /* The seven colours Apple Mail and Outlook address with three IMAP
     keywords. Ours, not theirs: only the order is shared, so a mail
     flagged elsewhere wears the same colour here. */
  --flag-gold: var(--flag);
  --flag-orange: #e8710a;
  --flag-yellow: #f2ce00;
  --flag-green: #1e8e3e;
  --flag-blue: #1a73e8;
  --flag-purple: #8430ce;
  --flag-grey: #7a7a80;

  --notice-fg: #0c5460;
  --notice-bg: #d1ecf1;
  --notice-border: #bee5eb;
  --alert-fg: #721c24;
  --alert-bg: #f8d7da;
  --alert-border: #f5c6cb;

  /* Semantic aliases consumed by components. */
  /* The first column of the message and contact grids: the row's
     checkbox and the toolbar's select-all stand in the same track. */
  --check-track: 2.2rem;

  --surface: var(--bg);
  /* A menu floats over content it must not let through, which a page
     surface is free to be translucent about. Every popover paints on
     this one, so a theme states its opacity once. */
  --surface-raised: var(--bg);
  --surface-subtle: var(--bg-well);
  --surface-inset: var(--bg-inset);
  --surface-selected: var(--bg-active);
  --surface-accent: var(--bg-green);
  --accent: var(--green-dark);
  --accent-strong: var(--green);
  /* Label drawn on an accent fill; a theme whose background is
     translucent overrides it with an opaque ink of its own. */
  --on-accent: var(--bg);
  --danger: var(--alert-fg);
  --danger-surface: var(--alert-bg);
  /* A verified signature, in the one green the theme already owns; a
     theme that redefines the accent redefines this with it. */
  --sig-good: var(--green-dark);
  --focus-ring: color-mix(in srgb, var(--link) 68%, transparent);
}

/* The dark palette applies with the system preference unless the user
   forced light in the settings, and always when they forced dark. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --fg: #ddd;
    --fg-dim: #a8a8a8;
    --fg-muted: #909090;
    --fg-faint: #6e6e6e;

    --bg: #1f1f1f;
    --bg-well: #141414;
    --bg-inset: #2a2a2a;
    --bg-button: #2c2c2c;
    --bg-active: #363636;

    --border: #3c3c3c;
    --border-light: #333;
    --border-faint: #2a2a2a;
    --border-strong: #555;

    --link: #7aa2f7;
    --link-read: #8d9cd8;
    --link-unread: #a8c7fa;

    --green-dark: #3fb968;
    --bg-green: #17211a;
    --border-green: #2c4a33;
    --flag: #d9b62a;

    --notice-fg: #9ed6e0;
    --notice-bg: #12343b;
    --notice-border: #1c4a54;
    --alert-fg: #f1aeb5;
    --alert-bg: #35181c;
    --alert-border: #58232b;
  }
}

:root[data-theme="light"] {
  color-scheme: light;
}

:root[data-theme="dark"] {
  color-scheme: dark;

  --fg: #ddd;
  --fg-dim: #a8a8a8;
  --fg-muted: #909090;
  --fg-faint: #6e6e6e;

  --bg: #1f1f1f;
  --bg-well: #141414;
  --bg-inset: #2a2a2a;
  --bg-button: #2c2c2c;
  --bg-active: #363636;

  --border: #3c3c3c;
  --border-light: #333;
  --border-faint: #2a2a2a;
  --border-strong: #555;

  --link: #7aa2f7;
  --link-read: #8d9cd8;
  --link-unread: #a8c7fa;

  --green-dark: #3fb968;
  --bg-green: #17211a;
  --border-green: #2c4a33;
  --flag: #d9b62a;

  --notice-fg: #9ed6e0;
  --notice-bg: #12343b;
  --notice-border: #1c4a54;
  --alert-fg: #f1aeb5;
  --alert-bg: #35181c;
  --alert-border: #58232b;
}

/* The system stack has no Persian face of its own, so Persian falls to
   whatever the platform picks. Name the good ones first: any that is
   installed is used, and nothing is downloaded when none is. */
:root:lang(fa) {
  --font-sans: Vazirmatn, Sahel, "IRANSans", "SF Arabic", "Geeza Pro",
    "Segoe UI", "Noto Naskh Arabic", system-ui, sans-serif;
}

/* Persian sits taller than Latin at the same size - the letters carry
   dots above and below the line and run into the row under them at the
   leading Latin is comfortable with. Prose only: on the chrome the same
   change grows every row in the list and thins the page out, which is a
   density decision rather than a reading one. */
:root:lang(fa) .user-text,
:root:lang(fa) main.message pre {
  line-height: 1.8;
}

/* A reader who wants the interface larger says so once. Every font size
   in this stylesheet is already relative, so the whole of it follows
   from the root: nothing else has to know. The default is the size
   everyone had before the choice existed. */
:root[data-text="large"] {
  font-size: 112.5%;
}

:root[data-text="larger"] {
  font-size: 125%;
}

/* Someone who has asked their system for more contrast has asked this
   page too. Only the quiet greys move: the text that was already at
   full strength is already right. */
@media (prefers-contrast: more) {
  :root {
    --fg-dim: #333;
    --fg-muted: #3d3d3d;
    --fg-faint: #565656;
    --border: #a0a0a0;
    --border-strong: #707070;
  }

  :root:not([data-theme="light"]) {
    --fg-dim: #e0e0e0;
    --fg-muted: #d0d0d0;
    --fg-faint: #b0b0b0;
    --border: #808080;
    --border-strong: #a0a0a0;
  }
}

/* Forced colours replace the palette wholesale, and what it cannot see
   it drops: a focus ring mixed from a custom property becomes nothing,
   and a colour flag becomes an unmarked shape. Both are named again in
   the system's own colours, which is the only vocabulary left. */
@media (forced-colors: active) {
  :root {
    --focus-ring: Highlight;
  }

  :where(a, button, input, textarea, select, summary):focus-visible {
    outline: 2px solid Highlight;
    outline-offset: 2px;
  }

  .flag-button,
  .color-dot {
    forced-color-adjust: none;
  }
}

/* The scrollbar's space is reserved whether or not it is there, so a
   page that grows past the fold does not shove every fluid column 15px
   sideways. It is a no-op where scrollbars overlay the content (macOS,
   touch), which is exactly where there was nothing to fix. */
:root { scrollbar-gutter: stable; }

body { font-family: var(--font-sans); font-size: var(--text-md); padding: 0; margin: 0;
  min-height: 100vh; display: flex; flex-direction: column;
  background-color: var(--surface); color: var(--fg);
}

input[type="text"],
input[type="email"],
input[type="password"],
input[type="file"],
input[type="number"],
input[type="date"],
input[type="time"],
textarea,
select {
  margin: 0;
  border: 1px solid var(--border);
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.04);
  border-radius: var(--radius-sm);
  min-height: var(--control-height);
  font-size: inherit;
  padding: var(--space-1) var(--space-2);
  background-color: var(--surface);
  color: var(--fg);
}

.page-wrap {
  display: flex;
  flex: 1 100%;
  flex-direction: row;
}

a { color: var(--link); }
hr { border: 0; border-top: 1px solid var(--border); }
button { font-size: inherit; }

/* The skip link is the exception to visually-hidden: it exists to be
   found by a keyboard, so focusing it has to undo every part of the
   recipe that hid it. The main landmark takes tabindex="-1" so the jump
   moves focus rather than only the viewport. */
.skip-link:focus {
  position: fixed !important;
  inset-block-start: var(--space-2);
  inset-inline-start: var(--space-2);
  width: auto !important;
  height: auto !important;
  margin: 0 !important;
  padding: var(--space-2) var(--space-3) !important;
  overflow: visible !important;
  clip: auto !important;
  z-index: 100;
  background: var(--surface-raised);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

main:focus { outline: none; }

.visually-hidden {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important;
  white-space: nowrap !important;
  border: 0 !important;
}

:where(a, button, input, textarea, select, summary):focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

:where(input, textarea, select):focus-visible {
  border-color: var(--link);
}
header {
  position: relative;
  z-index: 12;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}
header nav {
  min-height: var(--toolbar-height);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-inline: var(--space-2) var(--space-4);
}
footer { padding: var(--space-2) var(--space-4); }
header nav > a {
  align-self: stretch;
  display: inline-flex;
  align-items: center;
  border-bottom: 2px solid transparent;
  text-decoration: none;
}
header nav > a:hover { border-bottom-color: var(--border-strong); }
header nav span { color: var(--fg-muted); }
header nav > div {
  margin-inline-start: auto;
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

header nav .accounts { display: inline-block; position: relative; }
header nav .accounts summary { cursor: pointer; color: var(--fg-muted); }
/* The switcher sits at the end of the header, so the panel hangs from
   its trailing edge and grows inwards; anchored at the start it grows
   towards the page edge and leaves the canvas once two accounts, or
   one long address, make it wide. */
header nav .accounts-menu {
  position: absolute;
  inset-inline-end: 0;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-1);
  min-width: max-content;
  padding: var(--space-2);
  background-color: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-raised);
}
header nav .accounts-menu .active {
  font-weight: 500;
  color: var(--fg);
  cursor: default;
}
header nav .accounts-menu .active::before { content: "\2713 "; }
header nav .accounts-menu button {
  padding: 0;
  border: none;
  background: none;
  color: var(--link);
  text-align: start;
  cursor: pointer;
}
header nav .account-menu-row {
  display: grid;
  grid-template-columns: minmax(9rem, 1fr) auto;
  align-items: center;
  gap: var(--space-3);
}
header nav .account-menu-row > form { margin: 0; }
header nav .account-menu-row > form:first-child { grid-column: 1; }
header nav .account-menu-row .account-signout { grid-column: 2; }
header nav .account-menu-row .account-signout button {
  color: var(--fg-muted);
  font-size: var(--text-sm);
}
header nav .account-menu-row .account-signout button:not(:disabled):not([aria-disabled="true"]):not(.is-disabled):hover { color: var(--danger); }
header a.active {
  font-weight: 650;
  color: var(--fg);
  border-bottom-color: var(--accent);
  text-decoration: none;
}

header .notice {
    color: var(--notice-fg);
    background-color: var(--notice-bg);
    border: 1px solid var(--notice-border);
    padding: var(--space-2);
    text-align: center;
}

footer { text-align: end; }

.container {
  flex: 1 auto;
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  min-width: 0;
}

.container.error {
  max-width: 800px;
  margin: 0 auto;
  padding: 1rem 0;
}

/* The rail is the width it is declared, on every page. A flex item's
   min-width is auto, so any row that will not truncate makes the rail
   as wide as its longest name - which is why the calendar and contact
   rails were wider than mail's, whose folder rows do truncate. */
aside {
  flex: 0 0 var(--sidebar-width);
  min-width: 0;
  background: var(--surface);
  border-inline-end: 1px solid var(--border-light);
}

/* Every rail row truncates: the name is in the title, and a rail is a
   list of places, not a place for a name to be read in full. */
aside .filter-row,
aside .filter-row > label,
aside > a,
aside li,
aside li a,
aside summary {
  min-width: 0;
}


aside img { display: block; }

aside ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

aside li {
  width: 100%;
  display: flex;
  min-height: 2rem;
  align-items: center;
  padding: var(--space-1) var(--space-2);
  border-inline-start: 2px solid transparent;
}

aside li a {
  flex-grow: 1;
}

aside .active {
  font-weight: bold;
  color: var(--fg);
  text-decoration: none;
}

aside .active,
aside .new.active {
  background-color: var(--surface-selected);
  border-inline-start: 2px solid var(--accent);
}

/* Detail and edit pages put bare links in the aside; give them the
   same rhythm the list asides get from their li rows. */
aside > a {
  display: block;
  min-height: 2rem;
  padding: 0.35rem var(--space-2);
  border-inline-start: 2px solid transparent;
}

/* Unread counts read as quiet metadata on the right, not brackets. */
aside .unseen {
  margin-inline-start: auto;
  color: var(--fg-muted);
  font-size: 0.85em;
}

aside .new.active {
  color: var(--accent);
  background: var(--surface-accent);
}

aside button {
  margin: 0;
  cursor: pointer;
  text-decoration: none;
  text-align: center;
  display: inline-block;
  padding: 0 0.3rem;
  margin-inline-end: 0.3rem;
  border: none;
  background: transparent;
}

aside .active button:not(:disabled):not([aria-disabled="true"]):not(.is-disabled):hover {
  background: var(--bg);
}

aside button:not(:disabled):not([aria-disabled="true"]):not(.is-disabled):hover {
  background: var(--bg-well);
}

main {
  flex: 1 100%;
  display: flex;
  flex-direction: column;
  padding: 0;
  min-width: 0;
  background-color: var(--surface-subtle);
}

main.login {
  justify-content: center;
}

main.login > section {
  align-self: center;
  background: var(--surface);
  width: min(26rem, 100%);
  padding: var(--space-8);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-raised);
}

main.login button {
  width: 100%;
  padding-block: 0.6rem;
  font-weight: bold;
}

main.login .colophon {
  margin-top: 1rem;
  text-align: center;
  color: var(--fg-faint);
  font-size: 0.8rem;
}

main.login .colophon a {
  color: inherit;
  text-decoration: none;
}

main.login .colophon a:hover { text-decoration: underline; }

main.create-update {
  align-items: flex-start;
  padding: var(--space-4);
  background: var(--surface-subtle);
}

main.create-update > form {
  width: min(100%, var(--content-form));
  padding: var(--space-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-raised);
}

main.create-update > form > .context-crumb:first-child {
  min-height: 0;
  margin-bottom: var(--space-4);
  padding: 0 0 var(--space-2);
  background: transparent;
}

main.create-update > form > h1 {
  margin: 0 0 var(--space-4);
  font-size: var(--text-xl);
  line-height: 1.25;
}

/* Compose email form - needs flex to fill space */
main.create-update:has(.headers) {
  flex: 1 auto;
}

main.create-update:has(.headers) form {
  flex: 1 auto;
  display: flex;
  flex-direction: column;
  width: 100%;
}

/* Sieve filter editor - the script pane fills the remaining space */
main.create-update form.filter {
  flex: 1 auto;
  display: flex;
  flex-direction: column;
  width: 100%;
}

main.create-update form.filter > label.script {
  flex: 1 auto;
  display: flex;
  flex-direction: column;
  margin-bottom: 0;
}

main.create-update form.filter > label.script > textarea {
  flex: 1 auto;
  font-family: var(--font-mono);
  resize: none;
}

/* Simple create forms (event, task, contact) */
main.create-update > form > .field,
main.create-update > form > label {
  display: block;
  margin-bottom: var(--space-3);
  color: var(--fg-dim);
  font-size: var(--text-sm);
  font-weight: 600;
}

main.create-update > form > .field > label,
main.create-update > form > label > span {
  display: block;
  margin-bottom: var(--space-1);
}

main.create-update > form > .field > input,
main.create-update > form > .field > select,
main.create-update > form > .field > textarea,
main.create-update > form > label > input,
main.create-update > form > label > select,
main.create-update > form > label > textarea {
  display: block;
  width: 100%;
  box-sizing: border-box;
}

main.create-update > form > .field > textarea,
main.create-update > form > label > textarea {
  min-height: 8rem;
  resize: vertical;
}

main.create-update > form > .field > input[readonly],
main.create-update > form > label > input[readonly] {
  color: var(--fg-muted);
  background: var(--surface-inset);
}

/* Compose form headers grid */
/* A textarea is made taller when its text needs the room, and never
   wider: widening one drags it out of whatever column it sits in and
   the layout around it stops meaning anything. */
textarea {
  resize: vertical;
}

main.create-update .headers {
  display: grid;
  grid-template-columns: auto 1fr auto;
  grid-template-rows: auto auto auto auto;
  grid-gap: 0.5rem;
  align-items: center;
}

main.create-update .headers.no-js {
  grid-template-columns: auto 1fr;
}

main.create-update .headers label {
  grid-column-start: 1;
}

main.create-update .headers input {
  grid-column-start: 2;
  grid-column-end: 3;
}

main.create-update #attachment-list {
  grid-column-start: 3;
  grid-row-start: 1;
  grid-row-end: 7;

  /* Fluid beside the fields; the fixed width forced the page to
     scroll sideways on narrower desktops. */
  width: clamp(10rem, 28vw, 25rem);
  height: 100%;
  background: var(--bg-inset);
  overflow-y: auto;
  scrollbar-gutter: stable;
  border: 1px solid var(--border-light);

  display: flex;
  flex-direction: column;
}

main.create-update #attachment-list .help {
  text-align: center;
  color: var(--fg-dim);
  margin-top: 1rem;
}

main.create-update #attachment-list .upload {
  width: calc(100% - 1rem);
  position: relative;
  display: flex;
  margin: 0.5rem;
  padding: 0.25rem 0.5rem;
  background: var(--bg);
  align-items: center;
}

main.create-update #attachment-list *:not(:last-child) {
  margin-inline-end: 0.25rem;
}

main.create-update #attachment-list .upload .filename {
  flex-grow: 1;
}

main.create-update #attachment-list .upload button {
  padding: inherit;
  min-width: inherit;
}

main.create-update #attachment-list .upload .progress {
  position: absolute;
  height: 5px;
  background: var(--green);
  bottom: 0;
  inset-inline-start: 0;
}

main.create-update #attachment-list .upload .error {
  display: none;
}

main.create-update #attachment-list .upload.error .error {
  display: block;
  color: var(--alert-fg);
}

main.create-update .text {
  flex: 1 auto;
  resize: none;
  margin-top: 1rem;
  position: relative;
}

main.create-update .text textarea {
  width: 100%;
  height: 100%;
}

#send-progress {
  position: absolute;
  inset-inline-start: 0;
  top: 0;
  width: 100%;
  height: 100%;
  font-size: 1.2rem;
  background: rgba(0, 0, 0, 0.2);
  display: flex;
  align-items: center;
  justify-content: center;
}

#send-progress svg {
  height: 1.2rem;
  margin-inline-end: 0.3rem;
  animation: fa-spin 2s infinite linear;
}

#send-progress svg path {
  fill: currentColor;
}

@keyframes fa-spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(1turn);
  }
}

main table { border-collapse: collapse; width: 100%; border: 1px solid var(--border-light); }
main table td {
  padding: 0.3rem;
  overflow-wrap: anywhere;
}
main table tbody tr { border-bottom: 1px solid var(--border-light); }
main table td a { text-decoration: none; }


main table td a:hover { text-decoration: underline; }

main.message table,
main.event table { background-color: var(--bg); }
main.message th,
main.event th { width: 5%;}
main.message h1,
main.event h1 { font-size: 1.2rem; padding: 0.5rem 0; }

/* Every header row starts on the content gutter; the label keeps its
   trailing gap toward the value. */
main.message th,
main.event th { padding-inline: 0.5rem; }
main.message td,
main.event td { padding-inline-start: 0; }

/* A plain-text mail is a column of prose, so it gets a measure. Without
   one a long line runs the width of the window, and in a mixed-script
   mailbox an LTR line then starts a screen away from where the RTL
   lines start. 78ch is the width the format was written for. */
main.message pre {
  padding-inline: 0.5rem;
  max-width: 78ch;
}

/* First columns carry the ink inset; interior cells keep the tighter
   rhythm between columns. The cell stretches like its neighbours, or
   its bottom border draws above theirs and steps the row hairline;
   the input stays centred by the cell's own flex. Rows nest their
   cells in a display:contents wrapper, so this reaches past it - the
   toolbar's select-all is not in the grid and stays centred. */
.list-grid .list-checkbox {
  padding-inline-start: 0.5rem;
  align-self: stretch;
}

.message-grid.unified > .message-list-flags {
  padding-inline-start: 0.5rem;
}
main.message .remote-content {
  background: var(--bg-green);
}

main.message .remote-content td {
  color: var(--fg);
}

main.message .tabs {
  margin: 0.3rem 0 0 0;
  padding: 0;
  border-bottom: 1px solid var(--border-light);
  overflow: visible;
}

main.message .tabs a {
  padding: 0.2rem 0.5rem;
  margin: 0;
  display: inline-block;
  height: calc(100% + 1px);
  margin-bottom: -1px;
  border-style: solid;
  border-color: transparent;
  border-width: 1px;
  text-decoration: none;
  color: var(--fg-dim);
}

main.message .tabs a.active,
main.message .tabs a:hover {
  background-color: var(--bg);
  border-color: var(--border-light) var(--border-light) var(--bg) var(--border-light);
  color: var(--fg);
}

main.message pre,
main.message iframe,
main.event pre {
  flex: 1 auto;
  padding: 1rem;
  margin: 0;
  background-color: var(--bg);
  border-style: solid;
  border-color: var(--border-light);
  border-width: 0 1px 1px 1px;
  max-width: 100%;
}

/* E-mail documents assume a light canvas, whatever the UI theme. */
main.message iframe {
  background-color: white;
}

main.message pre,
main.event pre {
  white-space: pre-wrap;
  word-break: break-all;
}

main.message .message-header {
  display: flex;
  flex-direction: row;
}

main.message .message-header .parts {
  width: 30%;
  margin-inline-start: 0.3rem;
  padding: 0.3rem 0.5rem;
  background: var(--bg);
  border-top: 1px solid var(--border-faint);
}

main.message .message-header .parts ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

main.message .message-header .parts li {
  margin-inline-start: 0;
}

/* View pages (contact, task, event) */
main.contact .details,
main.task .details,
main.event .details {
  width: min(calc(100% - 2 * var(--space-4)), var(--content-reading));
  margin: var(--space-4);
  padding: var(--space-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-raised);
}

main.contact .details h1,
main.task .details h1,
main.event .details h1 {
  margin: 0 0 var(--space-4);
  font-size: var(--text-xl);
  line-height: 1.3;
}

main.contact dl,
main.task dl {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-2) var(--space-4);
  margin: 0;
}

main.contact dl dt,
main.task dl dt {
  font-weight: 500;
  color: var(--fg-muted);
}

main.contact dl dd,
main.task dl dd {
  margin: 0;
}

main.contact dl dd.contact-address {
  white-space: pre-line;
}

main.event .details table {
  border-collapse: collapse;
}

main.event .details th,
main.event .details td {
  padding: 0.3rem 0.5rem;
  text-align: start;
}

main.event .details th {
  color: var(--fg-muted);
  font-weight: 500;
}

.contact-photo {
  float: inline-end;
  width: 120px;
  height: 120px;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 8px;
  margin-inline-start: 1rem;
}

main.create-update .actions {
  display: flex;
  flex-direction: row;
  align-items: center;
  background: transparent;
  padding-inline-start: 0;
  margin-top: 1rem;
}

main.create-update .actions button,
main.create-update .actions .button-link {
  padding: 0.4rem 1rem 0.35rem;
  font-weight: bold;
}

main.create-update .actions > *:not(:last-child) {
  margin-inline-end: 1rem;
}

.actions h3 {
  align-self: center;
  margin-block: 0; margin-inline: 1rem 1.3rem;
}

.message-list-flags {
  display: flex;
  align-items: center;
  justify-content: flex-start;
}

/* Star track: the row's button carries the hit area, so the track is
   its width and the header's mark sits on the same centre. */
.message-list-flags > form {
  flex: 0 0 var(--control-height);
  display: flex;
  justify-content: center;
}

/* The header's star is the same mark as the rows', drawn at the same
   size on the same centre; only its ink follows the header. */
.list-header.message-list-flags .flag-sort {
  width: var(--control-height);
  justify-content: center;
  font-size: 13pt;
  line-height: 1;
}

/* Sorted, the arrow trails outside the star's track: centring the pair
   would slide the star out from under the ones in the rows. */
.list-header.message-list-flags .flag-sort:has(.sort-arrow) {
  width: auto;
  justify-content: flex-start;
}

.list-header.message-list-flags .flag-sort .sort-label {
  display: inline-block;
  width: var(--control-height);
  text-align: center;
}

.message-list-flags .flag-button {
  margin: 0;
  cursor: pointer;
  text-decoration: none;
  text-align: center;
  display: inline-block;
  padding: 0;
  border: none;
  color: var(--flag-set, var(--flag));
  font-size: 13pt;
  background: transparent;
}

/* The star on the message page rides the subject line and stands at
   its trailing end, which is the end the subject does not start from:
   beside the first character it would sit where a subject's own leading
   emoji sits, and the two would read as one mark. */
/* The row lives inside the cell, not as the cell: a th told to be a
   flex container stops being a table cell, and the subject collapsed to
   the width of its longest word. */
main.message .subject-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
}

main.message .subject-row > h1 {
  flex: 1 1 auto;
  min-width: 0;
}

main.message .subject-row > .flag-button,
.icon-group .flag-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--control-height);
  min-width: var(--control-height);
  height: var(--control-height);
  padding: 0;
  line-height: 1;
  color: var(--flag-set, var(--flag));
  font-size: 13pt;
}

/* A star wears the colour it was flagged in, here or in whichever
   client set it. The colour arrives as a custom property rather than as
   a `color` of its own: the star and the swatches sit inside rules that
   set their colour at two classes deep, and a one-class rule loses to
   those - by specificity in the row, and by nothing but source order in
   the menu, which is the worse way to lose. An inherited variable is
   read by whichever rule wins, so there is no argument to have. */
.flag-gold { --flag-set: var(--flag-gold); }
.flag-orange { --flag-set: var(--flag-orange); }
.flag-yellow { --flag-set: var(--flag-yellow); }
.flag-green { --flag-set: var(--flag-green); }
.flag-blue { --flag-set: var(--flag-blue); }
.flag-purple { --flag-set: var(--flag-purple); }
.flag-grey { --flag-set: var(--flag-grey); }

/* The colour picker: one swatch per colour and one that clears, each a
   submit of its own, so it needs no script and no select. */
.flag-colors {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  padding: 0.3rem;
}

/* The colour menu: one line per colour, the star in it beside its name.
   The star carries the colour on a span of its own rather than on the
   button, so it needs to win no argument with the menu's own button
   rules - which it would lose, since they are written later. */
.flag-colors {
  min-width: 10rem;
}

.overflow-menu.flag-colors button {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  text-align: start;
}

.flag-star {
  color: var(--flag-set, var(--fg-muted));
  font-size: 1.1rem;
  line-height: 1;
}

/* The toolbar's own entry: the star it is currently set to, and a
   chevron saying there is more. Open, it lights up like the menu it
   belongs to. */
.flag-menu > summary {
  width: auto;
  gap: 0.1rem;
  padding-inline: 0.2rem;
}

.flag-menu[open] > summary {
  background: var(--surface-selected);
}

.flag-menu > summary .flag-star { font-size: 13pt; }

/* Nested inside the overflow on a narrow screen, it is a submenu: it
   opens in place and grows the menu around it. */
.overflow-menu .flag-menu { display: block; }
.overflow-menu .flag-menu > .overflow-menu {
  position: static;
  border: 0;
  box-shadow: none;
  padding-inline: var(--space-3);
}

.message-list-unread.message-list-flags .flag-button:not(:disabled):not([aria-disabled="true"]):not(.is-disabled):hover {
  background: var(--bg-active);
}

.message-list-subject a { color: var(--link-read); }


.message-list-unread.message-list-sender,
.message-list-unread.message-list-subject,
.message-list-unread.message-list-date { font-weight: bold; }

.message-list-unread.message-list-sender,
.message-list-unread.message-list-date { color: var(--fg);}

.message-list-unread.message-list-subject {
  font-weight: normal;
  color: var(--fg-dim);
}

.message-list-date {
  text-align: start;
}

/* An identifier is one left-to-right island whatever the page runs:
   an address, a domain, a host, a size with its unit, a phone number,
   the version string. Isolated, it orders its own runs; left to the
   paragraph, an RTL page prints "246 B" as "B 246" and "ex.org" as
   "yt.43" - a number run and a Latin run with a neutral between them,
   which the paragraph puts in its own order. Markup takes the "ltr"
   define; the classes below are the sites that already had one.
   Dates and the message header are not here: they are translated now
   and follow the page like the rest of it. */
.ltr,
.colophon,
.message-list-size,
.contact-list-phone {
  direction: ltr;
  unicode-bidi: isolate;
}


/* One pair of date fields at a time: the checkbox says whether the
   event occupies whole days, and the pair that does not apply is not
   shown. Both stay in the form - the handler reads the pair the
   checkbox names - so no script is needed to swap them.

   The checkbox is a sibling of the fields it governs rather than a
   child of its own caption, so the plain sibling combinator reaches
   them: :has() would read better and is one browser version away from
   showing both pairs at once, which is exactly the failure this has to
   not have. The drawer toggle is built the same way. */
/* The box and its caption are one flex line, so their centres are level
   by construction rather than by two inline-blocks agreeing about what
   the middle of a line is - which they do not, and which left the
   caption sitting a few pixels high whatever heights they were given.

   The control stays a sibling of the fields it governs, since that is
   what lets the sibling combinator reach them; it is the caption that
   draws the box, from the control's own state. The real input keeps the
   focus and the keyboard - it is placed under the box rather than
   removed, so a reader who tabs to it still sees where they are. */
main.create-update > form > .allday-toggle {
  position: absolute;
  width: 1rem;
  height: 1rem;
  margin: 0;
  opacity: 0;
}

main.create-update > form > .allday-label {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
  color: var(--fg-dim);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
}

main.create-update > form > .allday-label::before {
  content: "";
  flex: none;
  width: 1rem;
  height: 1rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
}

.allday-toggle:checked + .allday-label::before {
  border-color: var(--accent);
  background: var(--accent) center / 0.7rem no-repeat
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}

.allday-toggle:focus-visible + .allday-label::before {
  outline: 2px solid var(--link);
  outline-offset: 1px;
}

.allday-toggle:checked ~ .field.timed-field,
.allday-toggle:not(:checked) ~ .field.allday-field {
  display: none;
}

/* The receiving server could not confirm the sender. Shown only when it
   failed, so the mark means something when it appears. */
.auth-failed {
  margin: 0 0 var(--space-1);
  color: var(--red, #b3261e);
  font-weight: 600;
}

.auth-list {
  margin: 0;
  padding-inline-start: 1.1rem;
  color: var(--fg-muted);
  font-size: var(--text-sm);
}

/* An invitation is the message, not a note about it, so it is a card
   rather than another row of headers: what it says has to be readable
   before the decision it asks for. */
.invitation {
  margin: var(--space-3) 0;
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-md, 6px);
  background: var(--surface-raised);
}

.invitation > h2 {
  margin: 0 0 var(--space-2);
  font-size: var(--text-base, 1rem);
}

.invitation dl {
  display: grid;
  grid-template-columns: minmax(5rem, max-content) 1fr;
  gap: 0.2rem var(--space-3);
  margin: 0;
}

.invitation dt { color: var(--fg-dim); font-size: var(--text-sm); }
.invitation dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }

.invitation .attendee-list { margin: 0; padding: 0; list-style: none; }

.attendee-status {
  margin-inline-start: 0.4rem;
  color: var(--fg-faint);
  font-size: var(--text-sm);
}

/* The three answers, and what this account has already said. */
.invitation-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.invitation-mine { color: var(--fg-dim); font-size: var(--text-sm); }

/* Who answered, and what they said. The separator is the element's own
   mark rather than a character in the string: a literal dash between an
   LTR address and a translated word lands wherever the bidi algorithm
   puts it. */
.invitation-answer { margin: 0 0 var(--space-2); }

.answer-status::before {
  content: "\00B7";
  margin-inline: 0.4rem;
  color: var(--fg-faint);
}

@media (width <= 600px) {
  /* The label above its value: two columns leave neither enough room. */
  .invitation dl { grid-template-columns: 1fr; }
  .invitation dt { margin-top: var(--space-2); }
}

/* A field that most messages do not want. Closed it is one line in the
   form's own type; open it is the field and its explanation, and it
   opens itself whenever it already holds something. */
/* The headers are a two-column grid of label and field. A disclosure is
   neither, so it spans both: as a single item it took the label column
   and pushed every field after it into the wrong one. */
main.create-update .headers > .compose-more {
  grid-column: 1 / -1;
}

.compose-more > summary {
  cursor: pointer;
  color: var(--fg-dim);
  font-size: var(--text-sm);
  font-weight: 600;
}

.compose-more > input { width: 100%; box-sizing: border-box; }

/* What exists, one per row: the name, a taste of the text, and the two
   things that can be done to it. */
.signature-list {
  margin: 0 0 var(--space-3);
  padding: 0;
  list-style: none;
}

.signature-list > li {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--border-light);
}

.signature-entry {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;
  flex: 1 1 auto;
}

/* One line of it, which is enough to tell two apart; the form below is
   where it is read in full. */
.signature-preview {
  color: var(--fg-muted);
  font-size: var(--text-sm);
  white-space: pre;
  overflow: hidden;
  text-overflow: ellipsis;
}

.signature-row-actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: none;
}

.signature-row-actions form { display: inline; }

.signature-default {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.settings-heading {
  margin: 0 0 var(--space-2);
  font-size: var(--text-base, 1rem);
}

/* The chooser stands with the body it rewrites, above the row that
   sends: it is not one of the actions that finish the message. */
.signature-pick {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-block: var(--space-2);
}

/* The way out to the page that owns them, at the end of the row: it is
   a way out, not one of the choices. */
.signature-edit {
  margin-inline-start: auto;
  color: var(--fg-dim);
  font-size: var(--text-sm);
}

.signature-pick > label {
  color: var(--fg-dim);
  font-size: var(--text-sm);
  font-weight: 600;
}

/* Code is written left to right in every language: a sieve script, a
   header, a URL. The field says so itself rather than following the
   interface, and it is monospaced because alignment is part of it. */
.code,
textarea.code {
  direction: ltr;
  unicode-bidi: isolate;
  text-align: left;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* Text somebody typed keeps the breaks they typed, wraps a long line
   rather than widening the page, and reads each paragraph in that
   paragraph's own script. dir=auto resolves the block's alignment,
   plaintext resolves each line's direction inside it. */
.user-text {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  unicode-bidi: plaintext;
}

/* Text somebody else wrote is its own bidi paragraph: plaintext applies
   the Unicode algorithm's own base-direction rules per paragraph, so a
   trailing period, question mark or bracket lands on the end its
   language puts it, whichever direction the page runs in. dir=auto
   resolves the same direction for `direction`, which is what start
   below aligns against. */
main.message h1,
main.message pre {
  unicode-bidi: plaintext;
}

/* User-written titles order their own script (dir=auto) but align with
   the interface's edge by default, so Latin and Arabic rows cluster
   cleanly. Said outright rather than through start/match-parent: those
   resolve against a direction the row itself may have overridden, and
   which edge a row clusters to is a property of the page, not the row. */
:root:dir(rtl) .list .list-summary,
:root:dir(rtl) .message-list-subject a,
:root:dir(rtl) .message-list-sender a,
:root:dir(rtl) .contact-list-name a {
  text-align: right;
}

:root:dir(ltr) .list .list-summary,
:root:dir(ltr) .message-list-subject a,
:root:dir(ltr) .message-list-sender a,
:root:dir(ltr) .contact-list-name a {
  text-align: left;
}

main.message h1 {
  text-align: match-parent;
}

/* The other reading: each line sits on the edge its own script starts
   from. :dir() matches the direction the browser calculated for the
   element - what dir=auto resolved from the text - so the rule follows
   the row's own script rather than the page's. */
:root[data-align="script"] .message-list-subject a:dir(ltr),
:root[data-align="script"] .message-list-sender a:dir(ltr) {
  text-align: left;
}

:root[data-align="script"] .message-list-subject a:dir(rtl),
:root[data-align="script"] .message-list-sender a:dir(rtl) {
  text-align: right;
}

/* dir=auto resolves LTR while a field is empty, pushing a translated
   placeholder to the wrong edge; while only the placeholder shows,
   follow the UI direction. */
[dir="rtl"] input[dir="auto"]:placeholder-shown,
[dir="rtl"] textarea[dir="auto"]:placeholder-shown {
  direction: rtl;
}

.message-list-unread.message-list-subject a { color: var(--link-unread); }

.message-list-unread {
  background-color: var(--bg);
  opacity: 1;
}

/* Every list row nests its cells in a display:contents wrapper: the
   cells stay in the grid's tracks, while the row is one element to
   style and, on a phone, a grid of its own. */
.list-row {
  display: contents;
}

.message-list-unread:first-child {
  border-inline-start: 1px solid var(--border-faint);
}

.message-list-unread:last-child {
  border-inline-end: 1px solid var(--border-faint);
}

aside .new {
  color: var(--green-dark);
  font-weight: bold;
  /*background-color: #f5fcf2;*/
  text-decoration: none;
}

/* The create links of the DAV sections sit directly under the aside,
   without the list rows' padding the mail sidebar gets. */
aside > a.new {
  display: block;
  padding: 0.4rem 0.5rem;
}

main table th {
  text-align: start;
  padding: 0.3rem; font-weight: normal;
}

main table thead {
  border-bottom: 1px solid var(--border);
  background-color: var(--bg);
}
main table tfoot {
  border-top: 1px solid var(--border);
  background-color: var(--bg);
}

.message-list,
.contact-list {
  display: flex;
}

/* Rows stay scannable on wide monitors: the cap bounds the eye travel
   from subject to date, as the reading view's 800px cap already does. */
.message-list section,
.contact-list section,
.list section {
  width: 100%;
  max-width: var(--content-list);
}

.actions {
  display: flex;
  position: relative;
  flex-direction: row;
  align-items: center;
  min-height: var(--toolbar-height);
  background-color: var(--surface);
  /* Two lines rule the page: containers sit on the outer edge, ink
     starts one step in - the same inset the tab captions use. */
  padding: var(--space-1) var(--space-2);
}

/* Spacing rides the checkbox itself rather than its cell: sizing the
   cell to the grid's first track boxed the control into less room than
   a checkbox takes in every engine, and it went missing. The leading
   margin is the row checkbox's own default one, so the two line up. */
/* The select-all commands the tick column and stands in it: one rule
   for every list, because every list is the same grid. */
.actions #action-checkbox-all {
  margin-inline: 0.25rem 0.5rem;
}

/* What is attached, named under the subject inside the subject's own
   cell. Not a row of its own: a row of its own is outside the row's box,
   so it takes neither its hover nor its hairline and has to be stitched
   back on with a border hack. A cell that holds two lines is simply a
   taller row. */
/* Names are shown only where the subject column has room for a name.
   Below that they shrink into bubbles a character wide, which say less
   than the clip mark already on the row - so they are not shown at all,
   and the message page names them. The threshold is the window, since
   without a script that is what the page can measure. */
.attach-line { display: none; }

/* The column is the second line's layout, so it lives where the second
   line does. Applied at every width it also reached the rows showing no
   names, where the subject is the only item and the shared ellipsis rule
   gives it flex: 1 1 auto - which in a column grows it down the whole
   cell, leaving the text at the top of a row that centres every other. */
@media (width >= 60rem) {
  .message-list-subject.has-attach {
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: 0.15rem;
  }

  /* The subject had the whole cell to itself and still does: the names
     are under it, never beside it. */
  .message-list-subject.has-attach > a { max-width: 100%; }

  .attach-line {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    max-width: 100%;
    min-width: 0;
    overflow: hidden;
  }
}

/* A name wide enough to tell one file from another and no wider: past
   that it is a snake across the row and says nothing more, so it
   truncates and the whole name is in the tooltip. */
.attach-chip {
  flex: 0 1 auto;
  /* Never narrower than a name reads: a chip that cannot show one is
     not shown. */
  min-width: 5rem;
  max-width: 8rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 0.05rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: 1rem;
  color: var(--fg-muted);
  font-size: var(--text-sm);
  line-height: 1.4;
}

/* What did not fit, counted rather than named. A round box for one
   digit and a pill for two: the number decides the width, so it always
   fits inside it. */
.attach-more {
  flex: none;
  min-width: 1.5rem;
  height: 1.5rem;
  padding: 0 0.35rem;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--fg-faint);
  font-size: var(--text-sm);
  line-height: 1;
}

.attach-chip:not(:disabled):not([aria-disabled="true"]):not(.is-disabled):hover {
  color: var(--fg);
  border-color: var(--border-strong);
  text-decoration: none;
}

/* A control that acts on a selection and cannot carry the disabled
   attribute - a menu, a form - says so the way a disabled button does:
   dim, and the pointer does not reach it, so no hover state of any kind
   can fire on it. Every hover rule on a control is guarded against the
   three ways a control is off (:disabled, aria-disabled, .is-disabled);
   an unguarded hover rule written below its own counter-rule is how the
   trash icon came to redden while it was dead. */
.overflow.is-disabled > summary,
.actions-search.is-disabled {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}

/* A colour is a swatch, not a field: at full width it reads as a bar
   the length of the form and says nothing more than the small one. */
input[type="color"] {
  width: 4rem;
  min-width: 4rem;
  padding: 2px;
  cursor: pointer;
}

/* What a delete would take, stated under the form that edits the thing
   and set apart from it: the button beside Save must not read as one
   more way to save. */
.danger-zone {
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border-light);
}

.danger-zone h3 {
  margin: 0 0 var(--space-1);
  font-size: var(--text-sm);
  color: var(--fg-muted);
}

.danger-zone p {
  margin: 0 0 var(--space-2);
  color: var(--fg-dim);
  font-size: var(--text-sm);
}

/* A line added where the object is read, not behind an edit form. */
.note-box {
  margin-top: var(--space-4);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
}

.note-box label {
  color: var(--fg-muted);
  font-size: var(--text-sm);
  font-weight: 600;
}

.note-box textarea {
  width: 100%;
  box-sizing: border-box;
  resize: vertical;
}

/* One collection's row: the tick that remembers, and the link that
   looks without remembering. The link keeps its place and shows on
   hover or focus, so a rail of them is a list of names rather than a
   list of names and verbs. */
aside .filter-row {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  position: relative;
}

/* The two hover affordances lie over the end of the row rather than in
   it. In the row they held their width while invisible, so a rail of
   180px gave the name 76 and truncated it against a third of the rail
   standing empty. Over it, the name has the whole row and the links
   still appear in the same place, on a surface of their own so they
   are readable above it. */
aside .filter-actions {
  position: absolute;
  inset-inline-end: 0;
  top: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  gap: var(--space-1);
  padding-inline-start: var(--space-2);
  background: var(--surface);
  pointer-events: none;
}

aside .filter-row:hover .filter-actions,
aside .filter-actions:focus-within { pointer-events: auto; }

aside .filter-row > label { flex: 1 1 auto; min-width: 0; }

aside .filter-only,
aside .filter-edit {
  flex: none;
  padding-inline: var(--space-1);
  color: var(--fg-faint);
  font-size: var(--text-sm);
  opacity: 0;
}

/* The group is invisible with its links, or it paints a blank patch
   over the end of every name. */
aside .filter-actions { opacity: 0; }

aside .filter-row:hover .filter-actions,
aside .filter-actions:has(:focus-visible) { opacity: 1; }

aside .filter-row:hover .filter-only,
aside .filter-row:hover .filter-edit,
aside .filter-only:focus-visible,
aside .filter-edit:focus-visible { opacity: 1; }

/* The same explanations a tooltip carries, on a tap. Quiet by default:
   it is there when wanted and silent when not. */
.info-explain {
  margin-top: var(--space-2);
}

.info-explain > summary {
  cursor: pointer;
  color: var(--link);
  list-style: none;
}

.info-explain > summary::-webkit-details-marker { display: none; }

.info-explain > summary::before {
  content: "\203A";
  display: inline-block;
  width: 0.75rem;
  transition: transform 0.1s ease-out;
}

.info-explain[open] > summary::before { transform: rotate(90deg); }

.info-explain dl {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: 0.2rem var(--space-4);
  margin: var(--space-2) 0 0;
}

.info-explain dt { color: var(--fg-faint); }
.info-explain dd { margin: 0; min-width: 0; }

/* Carried on the row only where there is no column to carry it. */
.card-only { display: none; }

/* Facts about a server that nobody can change: a card, so they read as
   a panel of their own rather than as a tail on the page. */
.info-card {
  margin-top: var(--space-5);
  padding: var(--space-3) var(--space-4);
  background: var(--surface);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  color: var(--fg-muted);
  font-size: var(--text-sm);
}

.info-card h2 {
  margin: 0 0 var(--space-2);
  font-size: var(--text-sm);
  font-weight: 650;
  color: var(--fg-muted);
}

.info-card dl {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: 0.2rem var(--space-4);
  margin: 0;
}

.info-card dt { color: var(--fg-faint); }
.info-card dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }

/* What the server can do, ticked or not: the reason a page behaves the
   way it does, in the same two-state grammar everywhere. */
.info-abilities {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem var(--space-4);
}

.info-abilities li::before {
  margin-inline-end: 0.35rem;
  font-weight: 650;
}

.info-abilities .has::before { content: "\2713"; color: var(--accent); }

.info-abilities .has-not {
  color: var(--fg-faint);
}

.info-abilities .has-not::before { content: "\2013"; }

/* Each group inside the card is announced and set off, or the ticks
   read as one more row of the list above them. */
.info-group-title {
  margin: var(--space-3) 0 var(--space-1);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border-light);
  color: var(--fg-faint);
}

/* What a filter may use, as a wrap of quiet chips rather than a
   paragraph of comma-separated protocol. */
.info-tags {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
}

.info-tags li {
  padding: 0 0.4rem;
  border: 1px solid var(--border);
  border-radius: 1rem;
  font-size: var(--text-xs);
  line-height: 1.5;
}

/* When a stored thing was written and last changed, said once, quietly,
   under what it is about. */
.object-dates {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-top: var(--space-4);
  padding-top: var(--space-2);
  border-top: 1px solid var(--border-light);
  color: var(--fg-muted);
  font-size: var(--text-sm);
}

/* What a signature says, beside the sender it is a claim about. Two
   states only: verified, and present-but-not. An unsigned message says
   nothing here, because a mark on every message teaches people to
   ignore marks. */
.sig {
  margin-inline-start: 0.4rem;
  padding: 0.05rem 0.4rem;
  border: 1px solid;
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  white-space: nowrap;
  cursor: help;
}

.sig-good {
  color: var(--sig-good);
  border-color: color-mix(in srgb, var(--sig-good) 40%, transparent);
}

.sig-bad {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 40%, transparent);
}

/* A view chooser: every option the same box, the chosen one wearing a
   border the others draw transparent, so choosing moves nothing. Bold
   on the chosen one would change its width as it is chosen, which is
   the same defect wearing a different hat. */
.toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.15rem;
}

.toggle-option {
  padding: 0.15rem 0.6rem;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--link);
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
}

.toggle-option.is-current {
  border-color: var(--border-strong);
  background: var(--surface-selected);
  color: var(--fg);
}

.toggle-option:not(.is-current):hover {
  background: var(--surface-selected);
  text-decoration: none;
}

/* What a field needs said, said under it in one voice: the page's
   small type, the page's quiet ink, on its own line. No box, no rule,
   no second type scale - a hint that looks like a quotation reads as
   content rather than as guidance, and one that opens has to escape
   whatever layout the form is, which in a two-column grid put it under
   the next input. */
.hint {
  display: block;
  margin-top: 0.15rem;
  color: var(--fg-muted);
  font-size: var(--text-sm);
  line-height: 1.4;
}

/* The example is literal syntax: set as code, and isolated, or an RTL
   paragraph reorders the address inside it. */
.hint code {
  font-size: inherit;
  direction: ltr;
  unicode-bidi: isolate;
}

/* In a label/control grid the hint takes a row of its own under the
   control's column: inside the control's cell it would grow the row
   and pull the caption off the line it names. */
main.create-update .headers > .hint {
  grid-column: 2 / 3;
  margin-top: -0.25rem;
}

/* The picture the card has, beside the field that replaces it. */
.photo-field .photo-current {
  display: block;
  width: 5rem;
  height: 5rem;
  object-fit: cover;
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-2);
}

/* The picture and the button that takes it away, side by side: the
   button acts at once and belongs beside what it acts on, not among
   the fields that wait for Save. */
.photo-field .photo-shown {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.photo-field .photo-remove {
  flex: none;
  width: auto;
  white-space: nowrap;
}


/* Disabled means disabled to the eye too: a checkbox the browser has
   greyed is easy to miss beside a row of live marks, and this one is
   disabled for a reason worth noticing - there is nothing to select, or
   there is no script to select it with. */
#action-checkbox-all:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.message-list .actions:first-child,
.contact-list .actions:first-child,
.list .actions,
main.calendar .actions {
  border-bottom: 1px solid var(--border);
}

.message-list .actions:last-child,
.contact-list .actions:last-child {
  border-top: 1px solid var(--border);
}

.actions input[type="text"] {
  /* The explicit width shrinks the intrinsic size the toolbar wraps
     on; flex-grow then widens the field when there is room. */
  flex: 1;
  width: 6rem;
  min-width: 3.5rem;
  margin: 0;
}

.actions input[type="text"] + button {
  border-start-start-radius: 0;
  border-end-start-radius: 0;
}

.actions input[type="text"] {
  border-start-end-radius: 0;
  border-end-end-radius: 0;
}

/* One toolbar, one line: a wrapping flex container breaks on the items'
   full content width, so the title's ellipsis would never get a turn.
   The narrow layout re-declares wrap and keeps its two rows. */
.actions-wrap {
  display: flex;
  flex-grow: 1;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}

/* Buttons hold their label on one line; the title yields the width. */
.actions button,
.actions .button-link {
  white-space: nowrap;
}

/* Search asks for a comfortable width and gives it back before anything
   else does: it is the widest thing on the row and the least harmed by
   being narrow. */
.actions-search {
  display: flex;
  flex-direction: row;
  flex: 0 1 24rem;
  width: min(24rem, 42vw);
  min-width: 7rem;
  max-width: 30rem;
  margin-inline-start: auto;
  order: 100;
}



.actions-search button {
  padding-inline: 0.5rem;
}

.actions-pagination {
  margin-inline-start: 0.5rem;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.5rem;
}

/* The chosen state of a toggle and the states beside it hold the same
   box: same weight, same padding, one border drawn and the others
   transparent. Bolding the chosen one instead changes its width as it
   is chosen, which moves every control after it out from under the
   pointer that just chose it. */
.actions-pagination > strong,
.actions-pagination > .button-link,
.actions-pagination > span[aria-disabled],
.agenda-span > strong,
.agenda-span > .button-link {
  min-height: var(--control-height);
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  padding: var(--space-1) var(--space-2);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  font-weight: 500;
}

.actions-pagination > strong,
.agenda-span > strong {
  border-color: var(--border);
  background: var(--surface-selected);
  color: var(--fg);
}

/* Which slice of the month the agenda lists, on the gutter the list
   itself starts from. */
.agenda-span {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-4);
}

.actions-wrap > .actions-pagination:first-child {
  margin-inline-start: 0;
}

.actions-wrap > .actions-pagination:last-child {
  margin-inline-start: auto;
}

.actions-pagination h3 {
  margin: 0;
  font-weight: 500;
}

/* The count is secondary: it shrinks first and goes entirely before
   anything that can be clicked. */
.pagination-range {
  color: var(--fg-muted);
  white-space: nowrap;
  font-size: 0.85rem;
}

.action-group:not(:last-child) {
  margin-bottom: 1rem;
}

/* Conditional action groups render empty; keep them out of the gaps. */
.action-group:not(:has(*)) {
  display: none;
}

.actions-wrap .action-group {
  margin-inline-start: 0.3rem;
  margin-bottom: 0;
}

.actions-wrap .actions-message > .action-group:first-child {
  margin-inline-start: 0;
}

/* The group already spaces its members. */
.followups .action-group {
  margin-inline-start: 0;
}

.action-group.grow {
  flex-grow: 1;
}

.action-group label,
.action-group .group-label,
.action-group input,
.action-group textarea,
.action-group select[multiple] {
  display: block;
  width: 100%;
}

.action-group .checkbox input {
  display: inline;
  width: 1rem;
  float: inline-start;
}

/* A set of exclusive choices: the legend reads as the field's label,
   and the options stack under it like any other group's rows. */
.action-group.choices {
  border: 0;
  padding: 0;
  margin: 0;
}

.action-group.choices legend {
  padding: 0;
  color: var(--fg-dim);
}

.action-group.choices .checkbox {
  font-weight: normal;
}

.action-group select[multiple] {
  height: 10rem;
}

.actions-message {
  display: flex;
  flex: 1 1 auto;
  flex-direction: row;
  align-items: center;
  min-width: 0;
}



/* The title takes the room it needs and yields it under pressure: a
   reserved track wastes it on short names and refuses to give it back
   on a narrow window, which is how controls came to overlap. It keeps
   a floor so it never collapses to nothing, and truncates past it. */
.actions-message h1:not(:last-child) {
  flex: 0 1 auto;
  min-width: 5rem;
  max-width: 22rem;
}

.actions-message h1 {
  margin: 0;
  flex-grow: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--text-lg);
  line-height: 1.25;
  font-weight: 650;
}

.list-checkbox {
  align-self: center;
}

.message-list .messages,
.contact-list .contacts {
  flex-grow: 1;
}

.message-grid {
  display: grid;
  /* Sender column tracks the longest name up to a cap instead of a fixed
     share, so names are rarely truncated. */
  /* Fixed tracks: sorting or paging must not shift the headers under
     the pointer. */
  /* Every column yields, not the subject alone. A track that cannot
     shrink hands the whole loss to its neighbour, which is why the
     subject was crushed to nothing between the widest desktop and the
     phone layout while the sender kept fourteen rems it did not need.
     The marks are the one exception: they are glyphs, not text. */
  /* Every column yields, not the subject alone. A capped track hands
     the whole loss to the flexible one beside it, which is how the
     subject came to be crushed to nothing while the sender kept
     fourteen rems it did not need. The marks are the one exception:
     they are glyphs, not text. */
  grid-template-columns:
    var(--check-track) 4.4rem 14rem
    minmax(0, 1fr) 8rem 5.5rem;
  grid-template-rows: auto;
}

/* Merged desktop rows stay dense; ownership becomes dim secondary text in
   the mobile card layout and remains explicit on detail pages. */
.message-grid.unified {
  grid-template-columns: 4.4rem 14rem minmax(0, 1fr) 8rem 5.5rem;
}

/* Between the widest desktop and the phone the sender takes a share of
   what is left rather than a fixed slab, so the two text columns narrow
   together; and the size steps aside first, being the column the design
   already calls least important. */
@media (width < 75rem) {
  .message-grid {
    grid-template-columns:
      var(--check-track) 4.4rem minmax(6rem, 0.45fr)
      minmax(0, 1fr) minmax(5rem, 8rem) minmax(4rem, 5.5rem);
  }

  .message-grid.unified {
    grid-template-columns:
      4.4rem minmax(6rem, 0.45fr) minmax(0, 1fr)
      minmax(5rem, 8rem) minmax(4rem, 5.5rem);
  }
}

/* The attachment pane is a third column only where there is room for
   one; below that it goes under the fields, as it already did on a
   phone. Kept at 10rem it pushed the form past the window. */
@media (width < 60rem) {
  main.create-update .headers { grid-template-columns: auto minmax(0, 1fr); }

  main.create-update #attachment-list {
    grid-column: 1 / -1;
    grid-row: auto;
    width: auto;
    height: 8rem;
  }
}

@media (width < 60rem) {
  .message-grid {
    grid-template-columns:
      var(--check-track) 4.4rem minmax(6rem, 0.45fr)
      minmax(0, 1fr) minmax(5rem, 8rem);
  }

  .message-grid.unified {
    grid-template-columns:
      4.4rem minmax(6rem, 0.45fr) minmax(0, 1fr) minmax(5rem, 8rem);
  }

  /* Scoped by the grid that owns it: .list-cell sets display on every
     cell and is written below this, so a bare .message-list-size loses
     the tie and the column kept its space while the row shifted. */
  .message-grid .message-list-size { display: none; }
}

/* One header style for every list table: muted bold labels on one
   border, links inheriting ink, cells padded like their columns. */
.list-header {
  font-weight: bold;
  color: var(--fg-muted);
  border-bottom: 1px solid var(--border-strong);
  /* Match the row cells so labels align with their columns, and pin
     the empty checkbox header's border to the same baseline. */
  min-height: 2rem;
  padding: 0.4rem var(--space-2);
  align-self: stretch;
}

.list-header a { color: inherit; }

/* A sort direction is part of its label. It may overflow a narrow
   column, but the arrow never detaches onto a second line. */
.sort-header {
  display: inline-flex;
  align-items: baseline;
  white-space: nowrap;
}

.sort-header strong {
  display: inline-flex;
  align-items: baseline;
  gap: 0.25em;
  white-space: nowrap;
}

/* Ownership follows the sender's name and yields before it does: the
   sender cell already answers "who", and "which of mine" is the same
   question. The subject would be the wrong host - it is the widest,
   most-truncated cell in the row.

   It cannot vanish, since min-width keeps a few characters, and a
   domain truncates better than a name does: its tail is the part that
   repeats. */
.message-list-sender .row-account {
  flex: 0 4 auto;
  min-width: 3ch;
  max-width: 45%;
  margin-inline: 0.4rem 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  /* Ownership is not part of what unread emphasises. */
  font-weight: 400;
}

/* The name has the first claim on the room and yields last. */
.message-list-sender > a {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A cut word is a defect; a cut sentence ending in an ellipsis is a
   layout. The cells are flex containers, where text-overflow never
   reaches the text, so the ellipsis rides the item that holds it. */
.message-list-sender a,
.message-list-subject a,
.contact-list-name a,
.contact-list-email,
.contact-list-phone,
.task-list-summary a,
.task-list-calendar,
.event-summary a,
.event-owner {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
  /* Fills the cell rather than shrinking to its text: a flex item sized
     to its content sits wherever the *cell's* direction starts, and
     text-align then has no room to place anything. */
  flex: 1 1 auto;
}

/* Attachment, replied and forwarded share the star's track: the star
   holds its leading edge, the marks the trailing one, so both read as
   columns down the list instead of drifting with the row's contents. */
/* A secondary calendar's date is a gloss on the primary one, not a
   second fact: dimmer, and only where both fit. */
.date-secondary {
  color: var(--fg-muted);
  font-size: 0.9em;
}

.message-mark {
  flex: 0 0 auto;
  margin-inline-start: 0.2rem;
  font-size: 0.8em;
  color: var(--fg-dim);
}

.message-mark:first-of-type {
  margin-inline-start: auto;
}

/* The opt-in color mark: a short rounded pip at the head of the line
   that names the sender, the height of that line, the way k-9 marks a
   row. It reads as a mark rather than a table border, and it sits with
   the account it stands for instead of at an edge of its own. The color
   is set on the row and inherits through the display:contents wrapper;
   the pip is positioned, so switching the marks on never re-flows the
   list. */
.message-grid.account-colors .message-list-sender {
  position: relative;
  padding-inline-start: 0.6rem;
}

.message-grid.account-colors .message-list-sender::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 1.05em;
  border-radius: 999px;
  background: var(--account, transparent);
}

/* Nested folders indent their content, not their rows: every row spans
   the sidebar, so the active highlight runs edge to edge. */
aside li.folder-li { padding: 0; display: block; }
aside details.folder-branch summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  padding: 0.4rem 0.5rem;
  border-inline-start: 2px solid transparent;
}
aside details.folder-branch summary a { flex-grow: 1; }
aside details.folder-branch > ul > li,
aside details.folder-branch > ul li.folder-li > details > summary {
  padding-inline-start: 1.3rem;
}
aside details.folder-branch details.folder-branch > ul > li {
  padding-inline-start: 2.1rem;
}

/* The mail list's shape: the same checkbox track, then fixed tracks so
   sorting cannot shift a header under the pointer. */
.contact-grid {
  display: grid;
  grid-template-columns:
    var(--check-track) 2.5rem minmax(8rem, 1fr) minmax(6rem, 1fr)
    minmax(6rem, 8rem) minmax(6rem, 7rem) minmax(6rem, 0.7fr);
  grid-template-rows: auto;
}

/* The contacts list drops the date first, then the address: a name and
   a number are what the column is for. Written below the base rule it
   overrides, since a media query adds no specificity and the later of
   two equal rules wins. */
/* The date goes first and the address second; the book stays, because
   it is what says whose list a row is on. */
@media (width < 60rem) {
  .contact-grid {
    grid-template-columns:
      var(--check-track) 2.5rem minmax(8rem, 1fr) minmax(6rem, 1fr)
      minmax(6rem, 8rem) minmax(6rem, 0.7fr);
  }

  .contact-grid .contact-list-changed { display: none; }
}

@media (width < 48rem) {
  .contact-grid {
    grid-template-columns:
      var(--check-track) 2.5rem minmax(8rem, 1fr)
      minmax(6rem, 8rem) minmax(6rem, 0.7fr);
  }

  .contact-grid .contact-list-email { display: none; }
}

/* Event lists - a day, or a month's agenda - share the aligned grammar
   of the mail, contacts and task tables: when, what, and whose, each in
   its own track, so the owner never trails a summary of random length. */
.event-grid {
  display: grid;
  grid-template-columns: 6rem minmax(12rem, 1fr) minmax(8rem, 16rem);
}

.event-header-row,
.event-row { display: contents; }

.event-header-row > *,
.event-row > * {
  min-height: 2.5rem;
  padding: 0.4rem var(--space-2);
  border-bottom: 1px solid var(--border-light);
  display: flex;
  align-items: center;
  min-width: 0;
}

.event-row:hover > * { background: var(--surface-selected); }

.event-time,
.event-owner { color: var(--fg-muted); }

.event-summary a {
  color: inherit;
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.event-summary a:hover { text-decoration: underline; }

/* Each day of the agenda opens a band across every track. */
.event-grid .event-day-header {
  grid-column: 1 / -1;
  margin: 0;
  padding: 0.4rem var(--space-2);
  font-size: var(--text-sm);
  font-weight: 650;
  color: var(--fg-muted);
  background: var(--surface-subtle);
  border-bottom: 1px solid var(--border);
}

.event-grid .empty-list { grid-column: 1 / -1; }

/* Tasks use the same aligned, sortable table grammar as mail and contacts.
   The first track is only the completion action, not a verbose status field. */
.task-list {
  flex-grow: 1;
  display: flex;
  flex-direction: column;
}

.task-list .tasks { flex-grow: 1; }

/* The summary is the column with something to read in it, so it holds a
   floor and the metadata beside it takes shares of what is left. Capped
   tracks take their maximum before a bare fr track gets anything, which
   is how the summary came to be squeezed to nothing while the calendar
   kept fourteen rems. */
.task-grid {
  display: grid;
  grid-template-columns:
    2.75rem minmax(8rem, 1fr) minmax(5rem, 0.5fr)
    minmax(4.5rem, 7rem) minmax(4.5rem, 6rem);
}



/* Metadata steps aside before the two columns a task list is for. */
@media (width < 60rem) {
  .task-grid {
    grid-template-columns:
      2.75rem minmax(8rem, 1fr) minmax(5rem, 0.5fr) minmax(4.5rem, 7rem);
  }

  .task-grid .task-list-added { display: none; }
}

/* The due date goes before the calendar: most tasks carry no due date,
   so the column is mostly empty, while the calendar says whose list a
   row is on and is never empty. */
@media (width < 52rem) {
  .task-grid {
    grid-template-columns: 2.75rem minmax(8rem, 1fr) minmax(5rem, 0.5fr);
  }

  .task-grid .task-list-due { display: none; }
}



.task-row > * {
  min-height: 2.5rem;
  padding: 0.4rem var(--space-2);
  border-bottom: 1px solid var(--border-light);
  display: flex;
  align-items: center;
  min-width: 0;
}

.task-row:hover > * { background: var(--surface-selected); }

.task-list-summary a {
  color: inherit;
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.task-list-summary a:hover { text-decoration: underline; }

.task-row.completed .task-list-summary a {
  color: var(--fg-faint);
  text-decoration: line-through;
}

.task-list-status { gap: var(--space-2); }

/* The status column sorts through a glyph, like the mail flag column:
   the track stays fixed so the arrow never widens it. */
.task-grid .task-list-status .status-sort {
  width: 1rem;
  display: inline-flex;
  justify-content: center;
}

.task-grid .task-list-status .status-sort:has(.sort-arrow) { width: auto; }
.task-list-status .task-checkbox { margin: 0; }
.task-list-calendar,
.task-list-due {
  color: var(--fg-muted);
  font-size: var(--text-sm);
}

.contact-list-photo {
  display: flex;
  align-items: center;
  justify-content: center;
}

/* A flex item may shrink below its width, and this one did - 16 wide by
   24 tall, a circle drawn as an ellipse. It keeps its square, and crops
   rather than stretches whatever shape the card carries. */
.contact-list-photo img {
  flex: none;
  width: 1.5rem;
  height: 1.5rem;
  aspect-ratio: 1;
  border-radius: 50%;
  object-fit: cover;
}



/* Rows are not hyperlink soup: links show their nature on hover, as
   the tables these grids replaced did. */
.list-grid a {
  text-decoration: none;
}

.list-grid a:hover {
  text-decoration: underline;
}

/* Every list shares the same row rhythm: a hairline between rows, as
   the task and filter lists already do. */
.list-cell {
  border-bottom: 1px solid var(--border-light);
}

.list-cell {
  white-space: nowrap;
  min-height: 2.35rem;
  padding: 0.4rem var(--space-2);
  overflow: hidden;
  text-overflow: ellipsis;
  display: flex;
  align-items: center;
}

.followups a:not(:first-child) {
  margin-inline-start: 0.3rem;
}

.empty-list {
  text-align: center;
  grid-column-start: 1;
  grid-column-end: 10;
  font-size: var(--text-md);
  color: var(--fg-dim);
  margin: var(--space-8) var(--space-4);
  padding: var(--space-6);
  border: 1px dashed var(--border);
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--surface) 52%, transparent);
}

main.calendar .dates {
  flex-grow: 1;
  padding: 0;
  display: grid;
  /* minmax(0, 1fr), not 1fr: a bare fr track is at least as wide as its
     content, so one long event title pushed the month past the window
     and the page scrolled sideways. */
  grid-template-columns: repeat(7, minmax(0, 1fr));
  grid-template-rows: min-content;
  grid-auto-rows: 1fr;
  gap: 1px;
  background: var(--border-light);
}

main.calendar .dates .weekday {
  text-align: center;
  padding: var(--space-2);
  font-size: var(--text-sm);
  font-weight: 650;
  color: var(--fg-muted);
  background: var(--surface);
}

/* A month cell is mostly event titles: the day label and the grid keep
   the space they need, everything else is given back to the text. */
main.calendar .date {
  border: 0;
  padding: var(--space-1);
  background: var(--surface);
  display: flex;
  flex-direction: column;
  position: relative;
  min-width: 0;
}

main.calendar .date:hover { background: var(--surface-selected); }

main.calendar .date.active {
  background-color: var(--surface-accent);
  box-shadow: inset 0 0 0 1px var(--border-green);
}

main.calendar .date .date-link {
  position: absolute;
  inset: 0;
}

/* A day either side of the month is still a day, and what is on it is
   what tells you whether the month you are looking at is the one you
   want. Dimmed rather than emptied, which is what every calendar does. */
main.calendar .date.extra {
  background: var(--surface-subtle);
  opacity: 0.58;
}

main.calendar .date ul {
  list-style: none;
  margin: 0;
  padding-inline-start: 0;
}

main.calendar .date .events {
  min-width: 0;
  overflow: hidden;
}

main.calendar .date .events li {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--text-xs);
  line-height: 1.45;
}

main.calendar .date .events .color-dot {
  width: 6px;
  height: 6px;
  margin-inline-end: 3px;
}

main.calendar .events .overflow {
  color: var(--fg-dim);
  text-align: end;
}

/* The header of a cell is a two-by-two: the week and the day across, the
   primary and the secondary calendar down. Both calendars therefore sit
   on one row apiece and align against that row rather than against each
   other, which is what stops a 16px week and a 13px day from drifting
   apart - and gives both the same gap to the number underneath. */
main.calendar .date h3 {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: start;
  row-gap: 0.15rem;
  column-gap: var(--space-1);
}

/* Each row is one line box whatever size the text in it is, so the two
   calendars start at the same height and leave the same gap under
   themselves. Sized by baseline instead, the larger number would sit
   higher than the smaller and the gaps beneath them would differ. */
main.calendar .date h3 .weeknum,
main.calendar .date h3 .day-label {
  line-height: 1.4rem;
}

main.calendar .date h3 .weeknum-secondary,
main.calendar .date h3 .day-secondary {
  line-height: 1rem;
}

/* The week is the coarsest thing here, so it is the largest and the
   faintest: read once per row, never scanned. The Solar Hijri count
   under it is a different reckoning - that year starts at Nowruz and its
   weeks on Saturday - not the same number in other digits. */
main.calendar .date h3 .weeknum {
  grid-area: 1 / 1;
  justify-self: start;
  color: var(--fg-faint);
  font-size: var(--text-base, 1rem);
  font-weight: 400;
  font-variant-numeric: tabular-nums;
}

main.calendar .date h3 .weeknum-secondary {
  grid-area: 2 / 1;
  justify-self: start;
  color: var(--fg-faint);
  font-size: var(--text-xs);
  opacity: 0.75;
}

main.calendar .date h3 .day-label {
  grid-area: 1 / 2;
  justify-self: end;
  padding-inline: 0.25rem;
  font-size: var(--text-sm);
  white-space: nowrap;
}

/* The secondary calendar is a gloss on the Gregorian date, never a
   competitor: same line, quieter ink, smaller. */
main.calendar .date h3 .day-secondary {
  grid-area: 2 / 2;
  justify-self: end;
  padding-inline: 0.25rem;
  font-size: var(--text-xs);
  color: var(--fg-faint);
}

.title-secondary {
  margin-inline-start: 0.4rem;
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--fg-muted);
}

main.calendar .date.active h3 .day-label {
  border-radius: 999px;
  background: var(--green);
  color: white;
}

aside .aside-filter { display: flex; flex-direction: column; }

.color-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 2px;
  margin-inline-end: 4px;
  vertical-align: middle;
}

main.settings form {
  width: min(calc(100% - 2 * var(--space-4)), var(--content-form));
  margin: var(--space-4);
  padding: var(--space-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-raised);
}

/* The settings page flattens its own controls; the primary Save keeps
   the accent it declares, which this rule would otherwise repaint in
   the page background - the same color its label is drawn in. */
main.settings button:not(.button-primary) {
  background-color: var(--bg);
}

main.settings button:not(.button-primary):not(:disabled):not([aria-disabled="true"]):not(.is-disabled):hover {
  background-color: var(--bg-active);
}

input[type="submit"],
.button,
button,
.button-link {
  margin: 0;
  cursor: pointer;
  text-decoration: none;
  text-align: center;
  display: inline-block;
  min-height: var(--control-height);
  padding: var(--space-1) var(--space-2);
  min-width: var(--control-height);
  font-size: var(--text-sm);
  line-height: 1.25;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  vertical-align: middle;
  color: var(--fg);
  background-color: var(--bg-button);
}

.button:not(:disabled):not([aria-disabled="true"]):not(.is-disabled):hover,
button:not(:disabled):not([aria-disabled="true"]):not(.is-disabled):hover,
.button-link:not(:disabled):not([aria-disabled="true"]):not(.is-disabled):hover {
  background-color: var(--surface-selected);
  border-color: var(--border-strong);
  text-decoration: none;
}

.button-link,
.button-quiet {
  border-color: transparent;
  background: transparent;
}

.button-primary {
  color: var(--on-accent);
  background: var(--accent);
  border-color: var(--accent);
  font-weight: 650;
}

.button-primary:not(:disabled):not([aria-disabled="true"]):not(.is-disabled):hover {
  color: var(--on-accent);
  background: var(--accent-strong);
  border-color: var(--accent-strong);
}

.button-danger {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 35%, var(--border));
  background: transparent;
}

.button-danger:not(:disabled):not([aria-disabled="true"]):not(.is-disabled):hover {
  color: var(--danger);
  border-color: var(--danger);
  background: var(--danger-surface);
}

button[disabled] {
  color: var(--fg-muted);
  background-color: var(--bg-button);
  border-color: var(--border-light);
  cursor: default;
}

/* Overflow menus keep secondary actions behind a ⋮, in the same
   dropdown shape as the account menu. */
details.overflow {
  position: relative;
  display: inline-block;
}

.actions-wrap details.overflow {
  margin-inline-start: 0.3rem;
}

details.overflow > summary {
  list-style: none;
  user-select: none;
}

details.overflow > summary::-webkit-details-marker {
  display: none;
}

/* The sprite is markup, not a picture: it draws nothing itself. */
.overflow-menu .narrow-only { display: none; }

.icon-sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

.icon {
  width: 1.15rem;
  height: 1.15rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  pointer-events: none;
}

/* A step of a pager that carries a word: the mark sits against the word
   rather than in a target of its own. The word holds a track wide
   enough for the longest it can be, so the chevron you are about to
   click does not move because next month has a longer name. */
.pager-link {
  display: inline-flex;
  align-items: center;
  gap: 0.15rem;
}

/* No fixed track under the word: it held each month name in the middle
   of a box wide enough for the longest one, which put a gap between the
   two names and made them read as unrelated. The chevron shifts a little
   as the names change length, which is the trade. */

/* An arrow means "back towards the sender" or "on to someone else",
   which swap sides with the script; a box or a bin does not. */
[dir="rtl"] .icon-reply,
[dir="rtl"] .icon-reply-list,
[dir="rtl"] .icon-reply-all,
[dir="rtl"] .icon-forward,
[dir="rtl"] .icon-prev,
[dir="rtl"] .icon-next {
  transform: scaleX(-1);
}

/* An icon control is a square target with no border of its own: the
   toolbar reads as marks on a surface rather than a row of boxes. Its
   width does not follow any language, which is the point. The scoped
   selector outweighs the shared button rule's frame. */
.actions .icon-button,
.icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--control-height);
  height: var(--control-height);
  min-width: var(--control-height);
  min-height: var(--control-height);
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--fg-dim);
  cursor: pointer;
}

.actions .icon-button:not(:disabled):not([aria-disabled="true"]):not(.is-disabled):hover,
.icon-button:not(:disabled):not([aria-disabled="true"]):not(.is-disabled):hover {
  background: var(--surface-selected);
  color: var(--fg);
}

/* A control that is present but has nowhere to go: a button says so
   with disabled, a link with aria-disabled, and both read the same. */
.actions .icon-button:disabled,
.icon-button:disabled,
.actions [aria-disabled="true"],
[aria-disabled="true"] {
  color: var(--fg-faint);
  cursor: default;
  background: transparent;
}

.actions .icon-button.icon-danger:not(:disabled):not([aria-disabled="true"]):not(.is-disabled):hover,
.icon-button.icon-danger:not(:disabled):not([aria-disabled="true"]):not(.is-disabled):hover {
  color: var(--danger);
  background: var(--danger-surface);
}

/* Actions of one kind sit together; the gap between groups is the only
   thing that separates them. */
.icon-group {
  display: inline-flex;
  align-items: center;
  gap: 0.1rem;
  margin-inline-end: var(--space-2);
}

/* The rule after the title is the toolbar's fixed point: what follows it
   begins in the same place whatever the title says. */
.toolbar-rule {
  flex: none;
  width: 1px;
  align-self: stretch;
  margin-inline: var(--space-2);
  background: var(--border);
}

/* Search is a mark like the others until it is asked for: a field wide
   enough to type in would take a third of the row and push everything
   else about as the window resizes. Focusing it lays the field over the
   whole toolbar, which needs no script - focus is native. */
.actions-search {
  margin-inline-start: auto;
  flex-grow: 0;
  width: auto;
  min-width: 0;
}

.actions-end .actions-search { margin-inline-start: auto; }

.actions-search input[type="text"] {
  width: var(--control-height);
  height: var(--control-height);
  min-width: 0;
  padding: 0;
  border: none;
  border-radius: var(--radius-sm);
  box-shadow: none;
  color: transparent;
  cursor: pointer;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='1.6' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M20 20l-4.4-4.4'/%3E%3C/svg%3E") center / 1.15rem no-repeat;
}

.actions-search input[type="text"]:not(:disabled):not([aria-disabled="true"]):not(.is-disabled):hover { background-color: var(--surface-selected); }

.actions-search input::placeholder { color: transparent; }
.actions-search button { display: none; }

/* Asked for, the field grows where it stands, pushing the row leftwards
   rather than covering it, so nothing appears to change places. It gives
   width back before the marks do, and only the count steps aside. */
.actions-search:focus-within {
  flex: 0 1 24rem;
  min-width: 7rem;
  gap: 0.3rem;
}

.actions:has(.actions-search:focus-within) .actions-pagination {
  display: none;
}

.actions-search:focus-within input {
  flex: 1;
  width: 100%;
  height: auto;
  cursor: text;
  color: inherit;
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--border);
  background: var(--bg);
}

.actions-search:focus-within input::placeholder { color: var(--fg-faint); }
.actions-search:focus-within button { display: inline-block; }

/* The trailing group shrinks with its contents rather than holding a
   width the row does not have; nothing here may overlap its neighbour. */
.actions-end {
  display: flex;
  flex: 0 1 auto;
  min-width: 0;
  align-items: center;
  justify-content: flex-end;
  gap: 0.3rem;
  margin-inline-start: auto;
}

/* The page range is the first thing to go: the arrows still page. */
@media (width <= 45rem) {
  .pagination-range { display: none; }
}

/* The toolbar is one row at every width. Wrapping was the old answer to
   a row that would not fit, but the break lands on the items' full
   content width, so the title never gets to truncate and a control
   appearing - the second pager chevron - moves the whole page down.
   Nothing here needs a second row once the title yields and the marks
   keep their fixed tracks. */
@media (width <= 52rem) {
  .actions-wrap {
    flex-wrap: nowrap;
  }

  /* A narrow row buys its width from the title's floor and the rule's
     gaps, in that order: both are spacing, and everything else is a
     target that must not shrink. */
  .actions-message h1:not(:last-child) {
    min-width: 4rem;
  }

  .toolbar-rule {
    margin-inline: var(--space-1);
  }

  /* The title already names the month, so a narrow pager keeps only its
     chevrons - and with no word to hold, no track to hold it in. */
  .pager-link > span { display: none; }
  .pager-link { min-width: var(--control-height); justify-content: center; }

  /* Chips buy the last of the row's width back from their own padding
     before the title is asked to give up any more of its floor. */
  .actions-pagination > strong,
  .actions-pagination > .button-link,
  .actions-pagination > span[aria-disabled] {
    padding-inline: var(--space-1);
  }
}

.actions-end .actions-search {
  margin-inline-start: 0;
}

/* The message toolbar's trailing group hugs the right edge; the list
   pages already get this from their growing search field. */
main.message .actions-end {
  justify-content: flex-end;
}

/* The sort menu duplicates the column headers on desktop; it serves
   the narrow layout, which has no headers. */
.actions .sort-overflow {
  display: none;
}

/* The count of rows belongs beside the paging it changes. It is the
   least of the trailing group, so it is the first of them to go when
   the row runs out of room. */
.actions .ipp-overflow { display: inline-block; }

@media (width < 48rem) {
  .actions .ipp-overflow { display: none; }

}

/* The count in force is named rather than linked, and holds the same
   box as the ones beside it: the same weight, one border drawn and the
   rest transparent. Bolding it would change its width as it is chosen,
   which is the defect this rule exists to prevent. */
.overflow-menu .is-current {
  color: var(--fg);
  background: var(--surface-selected);
  border-color: var(--border-strong);
}

.overflow-menu {
  position: absolute;
  inset-inline-end: 0;
  top: calc(100% + 2px);
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.25rem;
  min-width: max-content;
  padding: 0.5rem;
  background-color: var(--surface-raised);
  border: 1px solid var(--border);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
}


.overflow-menu > strong {
  padding: 0.3rem;
}

.overflow-menu form {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.overflow-menu button,
.overflow-menu .button-link {
  width: 100%;
  text-align: start;
}

.button:active,
button:active,
.button-link:active {
  color: var(--border-strong);
  background-color: var(--bg-active);
}

.alert {
  padding: 0.5rem;
  border: 1px solid transparent;
  margin: 1rem 0;
  background: var(--alert-bg);
  color: var(--alert-fg);
  border-color: var(--alert-border);
  max-width: 600px;
}

/* Grouped lists (tasks, calendar agenda, filters) */
.list {
  flex-grow: 1;
  display: flex;
  flex-direction: column;
}

.list .list-items {
  padding: 0;
}

.list-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 2.5rem;
  padding: 0.45rem var(--space-2);
  border-bottom: 1px solid var(--border-light);
}

.list-item:hover { background: var(--surface-selected); }

.list-item.completed .list-summary {
  text-decoration: line-through;
  color: var(--fg-faint);
}

.task-checkbox {
  margin-inline-end: 0.5rem;
}

/* A task's completion toggle is a checkbox in every way but the
   element: it must not inherit the button box, which is twice the
   size of the checkboxes the rest of the app draws. */
.checkbox-button {
  width: 1rem;
  height: 1rem;
  min-width: 0;
  min-height: 0;
  border: 1px solid var(--border-strong);
  border-radius: 3px;
  background: var(--bg);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.7rem;
  line-height: 1;
  padding: 0;
}

.checkbox-button.checked {
  background: var(--green);
  border-color: var(--green);
  color: var(--bg);
}

.list-content {
  flex-grow: 1;
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  min-width: 0;
}

.list-content .list-summary {
  flex-grow: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.list-summary {
  display: block;
  color: inherit;
  text-decoration: none;
}

.list-summary:hover {
  text-decoration: underline;
}

.list-meta {
  font-size: var(--text-xs);
  color: var(--fg-muted);
}

main.task .details h1.completed {
  text-decoration: line-through;
  color: var(--fg-faint);
}



/* Account headings in the asides. */
aside .aside-account {
  color: var(--fg-muted);
  font-size: 0.85rem;
  margin: 0.6rem 0 0.2rem;
}

aside .aside-account:first-of-type {
  margin-top: 0;
}

aside .source-account-toggle {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  margin-top: 0.6rem;
  padding-inline: var(--space-2);
  cursor: pointer;
}

aside .source-account-toggle input {
  width: auto;
  margin: 0;
}

aside .source-account-toggle span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Account is the namespace; indent its collections instead of repeating
   that namespace in every collection label. A collection is one row:
   the box, then the name, truncated - a name that wraps under its own
   checkbox reads as two entries. The title carries the full name. */
aside .filter-row > label {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

aside form[data-source-group] > .filter-row {
  padding-inline-start: var(--space-2);
}

/* The indent is the account heading's, so it exists only where one
   does: a single account has no namespace to nest under, and the
   collections stood indented beneath nothing. The template says which,
   since :has() is not answered by every browser alborz serves. */
aside form[data-source-group] > .filter-row.nested {
  padding-inline-start: calc(var(--space-2) + 1.25rem);
}

aside .filter-row > label input {
  flex: none;
  margin: 0;
}

aside .filter-row > label span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Per-account folder sections in the mail aside. */
aside details.acct-folders > summary.aside-account {
  cursor: pointer;
  list-style: none;
  padding: 0.4rem 0.5rem 0.2rem;
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

aside details.acct-folders > summary::-webkit-details-marker {
  display: none;
}

aside details.acct-folders > summary::before {
  content: "›";
  display: inline-block;
  width: 0.75rem;
  text-align: center;
  transition: transform 0.1s ease-out;
}

aside details.acct-folders[open] > summary::before { transform: rotate(90deg); }

aside details.acct-folders li {
  padding-inline-start: 1rem;
}

/* Branch rows inside a section line up with the leaves; the wrapper
   stays flat and the summary carries the section indent. */
aside details.acct-folders li.folder-li {
  padding: 0;
}

aside details.acct-folders li.folder-li > details > summary {
  padding-inline-start: 1rem;
}

aside details.acct-folders details.folder-branch > ul > li {
  padding-inline-start: 1.8rem;
}

aside details.acct-folders details.folder-branch details.folder-branch > ul > li {
  padding-inline-start: 2.6rem;
}

/* The create entry closes the list it adds to, quiet where Compose is
   the aside's one accent. */
aside a.aside-create {
  color: var(--fg-muted);
  text-decoration: none;
}

aside a.aside-create:hover { color: var(--fg); }

aside ul.merged-roles {
  border-bottom: 1px solid var(--border-light);
  padding-bottom: 0.3rem;
  margin-bottom: 0.3rem;
}

.row-account {
  color: var(--fg-muted);
  font-size: 0.85em;
  margin-inline-end: 0.35rem;
}

/* The separator is an element's own mark, not a glyph inside a string:
   between two runs of different direction the bidi algorithm puts a
   literal dash wherever it likes. Every column that carries a
   collection and its owner reads the same. */
.task-list-calendar .row-account::before,
.contact-list-book .row-account::before {
  /* A middle dot, not an em dash: a dash sets a clause apart, and these
     are two facts of the same rank - the collection and whose it is. */
  content: "\00B7";
  margin-inline: 0.4rem;
  color: var(--fg-faint);
}

/* Page context, breadcrumb-weight: which account and container the
   page acts on, between the toolbar and the content. */
/* The narrow layout's own crumb: the header cannot hold the switcher
   there, so the page names the account instead. Wide windows have the
   switcher in view and do not need it repeated. */
.narrow-crumb { display: none; }

.context-crumb {
  color: var(--fg-muted);
  font-size: var(--text-sm);
  line-height: 1.4;
  min-height: 2rem;
  padding: 0.4rem var(--space-2);
  background: color-mix(in srgb, var(--surface) 58%, var(--surface-subtle));
  border-bottom: 1px solid var(--border-light);
}

/* Calendar groups indent under their account heading. */
/* Folding is the native details element; the marker is hidden so the
   headers look exactly as before, the pointer being the only hint. */
.show-completed-toggle {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.85rem;
  color: var(--fg-muted);
  margin-top: 1rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--border-light);
}

.show-completed-toggle input {
  width: auto;
}

/* Small screens: the desktop minimum width no longer applies and the
   sidebar becomes an off-canvas drawer, driven by the checkbox in the
   header so no script is involved. */
.sidebar-toggle,
.sidebar-button,
.sidebar-backdrop {
  display: none;
}

@media (width <= 600px) {
  /* A phone cell holds a day number and little else. */
  main.calendar .date h3 .weeknum { display: none; }

  /* The row's actions go under its name rather than beside it. */
  .signature-list > li { flex-direction: column; align-items: stretch; gap: var(--space-1); }

  body { min-width: 0; font-size: var(--text-md); }

  input[type="text"],
  input[type="email"],
  input[type="password"],
  input[type="file"],
  input[type="number"],
  input[type="date"],
  input[type="time"],
  textarea,
  select { font-size: 16px; }

  .sidebar-button {
    display: inline-block;
    cursor: pointer;
    padding: 0 0.3rem;
    margin-inline-end: 0.5rem;
    font-size: 1.2rem;
  }

  /* Error pages have no sidebar, so no button to open one. */
  body:not(:has(aside)) .sidebar-button { display: none; }

  /* The header is a single swipeable row; the account block leaves it
     and docks at the bottom of the open drawer instead. */
  header nav {
    display: flex;
    align-items: center;
    overflow-x: auto;
    white-space: nowrap;
    gap: var(--space-3);
    min-height: 3rem;
    padding-inline: var(--space-2);
  }

  header nav > a { min-height: 3rem; }

  header nav > div { float: none; display: none; }

  /* The account block docks to the drawer as a fixed element, and says
     so with a z-index of its own - which counted for nothing, because
     the header's z-index makes a stacking context its children cannot
     leave, and the backdrop and the drawer painted over it. Settings
     was unreachable on a phone. Dropping the header out of the
     stacking order while the drawer is open lets the dock rise on its
     own, and leaves the header itself where it was, under both. */
  .sidebar-toggle:checked ~ header { z-index: auto; }

  .sidebar-toggle:checked ~ header nav > div {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.5rem;
    position: fixed;
    bottom: 0;
    inset-inline-start: 0;
    width: var(--drawer-width);
    z-index: 21;
    padding: 0.75rem;
    background-color: var(--bg);
    border-top: 1px solid var(--border);
    border-inline-end: 1px solid var(--border);
  }

  .sidebar-toggle:checked ~ header nav > div > a { margin-inline-start: 0; }

  .sidebar-toggle:checked ~ header .accounts-menu {
    position: static;
    padding-block: 0;
    padding-inline: 1rem 0;
    border: 0;
    box-shadow: none;
  }

  aside {
    position: fixed;
    inset-block: 0;
    inset-inline-start: calc(-1 * var(--drawer-width));
    width: var(--drawer-width);
    z-index: 20;
    overflow-y: auto;
    padding-bottom: 10rem; /* room for the docked account block */
    background-color: var(--bg);
    border-inline-end: 1px solid var(--border);
    transition: inset-inline-start 0.15s ease-out;
  }

  .sidebar-toggle:checked ~ .page-wrap aside { inset-inline-start: 0; }

  body:has(.sidebar-toggle:checked) { overflow: hidden; }

  .sidebar-backdrop {
    position: fixed;
    inset: 0;
    z-index: 15;
    background-color: rgba(0, 0, 0, 0.4);
  }

  .sidebar-toggle:checked ~ .sidebar-backdrop { display: block; }

  /* Lists and toolbars run edge to edge; standalone forms keep an inset. */
  main { padding: 0; }
  main.create-update { padding: var(--space-2); }
  main.settings { padding: 0; }

  /* Task columns collapse into a compact two-line row. Completion stays
     beside the title; list, account, and due metadata remain explicit. */
  /* The event grid becomes a card per event: summary on top, when and
     whose on the line below, the same shape the task rows take. */
  .event-grid { display: block; }

  .event-header-row { display: none; }

  .event-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: var(--space-2);
    padding: var(--space-2);
    border-bottom: 1px solid var(--border-light);
  }

  .event-row > * {
    min-height: 0;
    padding: 0;
    border: 0;
    background: transparent;
  }

  .event-row:hover > * { background: transparent; }

  .event-row .event-summary {
    grid-column: 1 / -1;
    grid-row: 1;
    font-weight: 600;
  }

  .event-row .event-time { grid-column: 1; grid-row: 2; }

  .event-row .event-owner {
    grid-column: 2;
    grid-row: 2;
    justify-self: end;
  }

  .task-grid { display: block; }

  .task-header-row { display: none; }

  .task-row {
    display: grid;
    grid-template-columns: 2rem minmax(0, 1fr) auto;
    grid-template-rows: auto auto;
    column-gap: var(--space-2);
    padding: var(--space-2);
    border-bottom: 1px solid var(--border-light);
  }

  .task-row > * {
    min-height: 0;
    padding: 0;
    border: 0;
    background: transparent;
  }

  .task-row:hover > * { background: transparent; }

  .task-row .task-list-status {
    grid-column: 1;
    grid-row: 1 / span 2;
    align-self: start;
  }


  .task-row .task-list-summary {
    grid-column: 2 / -1;
    grid-row: 1;
    font-weight: 600;
  }

  /* The card has a second line for it, whatever the narrow-desktop
     step decided. */
  .task-row .task-list-calendar {
    display: flex;
    grid-column: 2;
    grid-row: 2;
  }

  .task-row .task-list-due { display: block; }

  .task-row .task-list-due {
    grid-column: 3;
    grid-row: 2;
    justify-self: end;
  }

  /* A phone row has two lines for what matters; when a task was added
     is on the task's own page. */
  .task-row .task-list-added { display: none; }


  main.create-update > form {
    padding: var(--space-3);
    border-radius: var(--radius-md);
  }

  main.settings form,
  main.contact .details,
  main.task .details,
  main.event .details {
    width: calc(100% - 2 * var(--space-2));
    margin: var(--space-2);
    padding: var(--space-3);
  }

  main.login > section { width: 100%; }

  /* The mailbox becomes two-line rows: sender and date on top, subject
     and flags below. */
  .message-grid,
  .contact-grid { display: block; }

  /* Date and star share the trailing column, the date above the star,
     both flush to the same edge. The star used to sit inside the second
     line beside the attachment mark, so a row carrying one pushed it
     out of line; the marks now order to its left and it holds that
     edge whatever else the row has. */
  .message-row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) 6rem;
    grid-template-areas:
      "check sender  date"
      "check subject flags";
    column-gap: 0.5rem;
    padding-inline: 0.5rem;
    padding-block: 0.3rem;
    border-bottom: 1px solid var(--border-light);
  }

  /* Merged rows have no checkbox; their grid drops the empty slot. */
  .message-grid.unified .message-row {
    grid-template-columns: minmax(0, 1fr) 6rem;
    grid-template-areas:
      "sender  date"
      "subject flags";
  }

  .message-row:has(.message-list-unread) { background-color: var(--bg); }

  /* Two lines is what a phone row has room for; the names are on the
     message itself. */
  .attach-line { display: none; }

  /* A contact is the same two-line card: name over phone, the photo
     beside the tick and spanning both lines. */
  .contact-row {
    display: grid;
    grid-template-columns: auto 2.5rem minmax(0, 1fr);
    grid-template-areas:
      "check photo name"
      "check photo phone"
      "check photo email";
    column-gap: 0.5rem;
    padding-inline: 0.5rem;
    padding-block: 0.3rem;
    border-bottom: 1px solid var(--border-light);
    min-height: 2.75rem;
  }

  .contact-row > .contact-list-checkbox { grid-area: check; }
  .contact-row > .contact-list-photo { grid-area: photo; }
  .contact-row > .contact-list-name { grid-area: name; }
  .contact-row > .contact-list-phone { grid-area: phone; }

  /* A phone row has the room for it, and an address is what a contact
     is usually looked up for. */
  .contact-row > .contact-list-email {
    grid-area: email;
    display: flex;
  }

  /* The book column is a desktop column; on a card the account rides
     the name, opposite it, where a mail row puts the date. */
  .contact-row > .contact-list-changed,
  .contact-row > .contact-list-book { display: none; }

  .contact-row .card-only { display: inline; }

  .contact-row > .contact-list-name {
    flex-direction: row;
    align-items: baseline !important;
    justify-content: space-between;
    gap: var(--space-2);
  }

  /* The two-line row draws one separator on its wrapper. The desktop
     min-height applies per cell, which on a two-line row bought two of
     them: a third of the row's height was empty. The text's own line
     box sets it here; the row, not the cell, is the tap target. */
  .list-cell {
    padding: 0.1rem 0;
    min-height: 0;
    border-inline: 0;
    border-bottom: 0;
  }

  /* The checkbox cell stretches with the two-line row. */
  .message-list-checkbox { grid-area: check; align-self: stretch; }
  /* The sender is a from-search link, but the subject is the row's real
     action; only it keeps the link color. */
  .message-list-sender { grid-area: sender; }
  .message-list-sender a { color: inherit; }
  .message-list-date {
    grid-area: date;
    justify-content: flex-end;
    font-size: 0.8rem;
  }
  .message-list-subject { grid-area: subject; }

  /* Star first, marks after it, both pinned to the row's trailing edge
     and centred over the two lines. */
  .message-list-flags {
    grid-area: flags;
    align-self: center;
    justify-content: flex-end;
    padding-inline-end: 0.15rem;
  }

  /* The narrow row zeroes the cells' side padding, which is the gap the
     account pip stands in: without it the mark touches the name. */
  .message-grid.account-colors .message-list-sender { padding-inline-start: 0.6rem; }

  /* The star sits on one line now rather than spanning both, so the
     control height it carries would be charged to that line. The glyph
     matches the text and the button widens instead, which keeps the row
     short and the target easy to hit. */
  .message-list-flags .flag-button {
    min-height: 0;
    padding-inline: 0.35rem;
  }

  .message-list-flags form { order: 2; }
  .message-mark { order: 1; margin-inline-start: 0.25rem; }
  /* The two-line mobile rows keep the inline account label instead of
     the header row and account column. */
  .list-header { display: none; }
  .message-list .messages .message-list-size { display: none; }
  .actions .sort-overflow { display: inline-block; }
  /* Outweighs the shared flex-item rule, which carries three classes. */
  .message-list .messages .message-list-account { display: none; }

  /* Toolbars compact to two rows: the buttons and the count share the
     first, the search gets the second. */
  .actions {
    flex-wrap: wrap;
    row-gap: 0.3rem;
    column-gap: 0.3rem;
    padding: 0.3rem 0.5rem;
  }

  /* The select-all checkbox shares the first toolbar row, in the same
     column as the row checkboxes below it. */
  .message-list .actions,
  .contact-list .actions {
    flex-wrap: nowrap;
    align-items: flex-start;
    /* Both edges keep a gutter. The start used to be given over to the
       select-all checkbox, which left the folder's name against the
       screen wherever there is no checkbox to stand there - every
       merged view, and every list once the sidebar is gone. */
    padding-inline: 0.5rem 0.3rem;
  }

  /* The drawer toggle stays focusable for the keyboard, just unseen. */
  .sidebar-toggle {
    display: block;
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
    opacity: 0;
  }

  .sidebar-toggle:focus-visible ~ header .sidebar-button {
    outline: 2px solid var(--link);
  }

  .actions { position: relative; }

  /* The card rows inset their tick by the row's own padding; the
     toolbar's box takes the same inset so the two stand in one
     column. */
  .actions #action-checkbox-all {
    margin-block: 0.4rem;
    margin-inline: 0.75rem 0.5rem;
  }

  .actions button,
  .actions .button-link {
    white-space: nowrap;
    min-height: 2.25rem;
  }

  .actions-wrap { min-width: 0; }
  .actions-message { column-gap: 0.3rem; }
  /* Too little width to reserve a track; the title takes what is left. */
  .actions-message h1 { flex: 1 1 auto; max-width: none; }


  .actions-wrap .action-group { margin-inline-start: 0; }
  .actions-pagination { margin-inline-start: auto; }
  .actions-pagination h3 { font-size: 0.8rem; }

  aside li,
  aside > a,
  aside details.folder-branch summary {
    min-height: 2.75rem;
    align-items: center;
  }

  /* Narrow toolbars carry one row: the disposal marks step aside and
     the overflow spells them out instead, which is what the room is
     for. */
  .actions .icon-group.wide-only,
  .actions > .wide-only,
  .actions-wrap .wide-only { display: none; }
  .overflow-menu .narrow-only { display: block; }
  .narrow-crumb { display: block; }

  .list-item { min-height: 2.75rem; }

  /* A mail row is two lines and is itself the tap target, so the touch
     minimum belongs to the row. Applied per cell it was charged twice,
     and a third of the row's height was empty. */
  .message-row { min-height: 2.75rem; }

  /* Message view: header fields wrap and the parts pane stacks below. */
  main.message .message-header { flex-direction: column; }
  main.message .message-header .parts {
    width: auto;
    margin-inline-start: 0;
    margin-top: 0.3rem;
  }
  main.message th, main.event th { width: auto; }
  main.message td {
    white-space: normal;
    max-width: none;
    overflow-wrap: anywhere;
  }

  /* Compose: single header column, attachments below the fields. */
  main.create-update .headers { grid-template-columns: auto minmax(0, 1fr); }
  main.create-update #attachment-list {
    grid-column: 1 / -1;
    grid-row: auto;
    width: auto;
    height: 8rem;
  }

  /* Contacts: name and phone only; the e-mail column and the date wait
     for the detail page. */
  .contact-row > .contact-list-changed { display: none; }
  .contact-list-name {
    flex-direction: column;
    align-items: flex-start !important;
    justify-content: center;
  }
  .contact-row .row-account {
    margin: 0;
    font-size: 0.7rem;
    line-height: 1.2;
  }
  .contact-list-phone { font-size: 0.8rem; }

  /* Calendar: a compact month with single-letter weekdays and events
     reduced to their color dots; the day link opens the full list. */
  main.calendar .dates { grid-gap: 2px; padding: 0; }
  main.calendar .dates .weekday { font-size: 0; }
  main.calendar .dates .weekday::first-letter { font-size: 0.8rem; }
  main.calendar .date { padding: 2px; min-height: 3rem; }
  main.calendar .date h3 { font-size: 0.7rem; min-height: 1.35rem; }
  main.calendar .date h3 .day-label { font-size: 0.75rem; min-height: 1.35rem; padding-inline: 2px; }
  /* The cell is barely wider than its own number here; the secondary
     shrinks with it and is clipped rather than pushing the grid out. */
  main.calendar .date h3 { overflow: hidden; white-space: nowrap; }
  main.calendar .date h3 .day-secondary { font-size: 0.6rem; }
  main.calendar .date ul { padding-inline-start: 0; }
  main.calendar .events li { display: inline; font-size: 0; }
  main.calendar .events li .color-dot {
    width: 6px;
    height: 6px;
    margin-inline-end: 2px;
  }
  main.calendar .events .overflow { font-size: 0.7rem; }
}
