/* module.monster portal.
 *
 * Layout uses logical properties (inline-start/end, margin-inline) rather
 * than left/right throughout, so adding an RTL locale to
 * i18n.RTL_LOCALES flips the interface without touching this file.
 */

:root {
  --green-900: #1c5c3b;
  --green-800: #226b45;
  --green-700: #2d6e4c;
  --green-600: #35855c;
  --green-500: #46a06f;
  --green-100: #e7f2ea;
  --green-050: #f2f8f4;
  --accent: #a9c94a;

  --ink: #12140f;
  --ink-2: #454d47;
  /* The two muted greys are set by contrast, not by eye: --ink-3 reads on
     the page background (5.4:1) and --ink-4, the fainter of the two, still
     clears 4.5:1 on every surface it lands on - #f1f3f2, white and the
     green tints - because both are used at 13px and smaller, where AA has
     no large-text allowance. --ink-4 doubles as the fill of a disabled or
     busy button, and white on it is the same 5.2:1 ratio. */
  --ink-3: #5c655e;
  --ink-4: #666f68;

  --bg: #f1f3f2;
  --surface: #ffffff;
  --line: #e0e4e1;
  --line-2: #eceeed;
  /* Line weight for a control whose *state* is the thing being drawn - a
     checkbox box, a switch track. 1.4.11 wants 3:1 for those, which --line
     (a divider, not a control) does not have to meet. Measured on the
     darkest surface any of them lands on, the page grey: 3.2:1 there and
     3.6:1 on white. */
  --control-line: #828a84;

  --danger: #a8322a;
  --danger-bg: #fbeceb;

  /* Focus. One ring for the whole portal: --focus clears 3:1 against every
     light surface it is drawn on (white 4.5, page 4.0, green tint 4.2), and
     the dark green sidebar swaps in white, where the green ring would be
     invisible. Always drawn outside the control, so a filled button gets
     its ring against the page rather than against its own fill. */
  --focus: var(--green-600);
  --focus-on-dark: #ffffff;

  /* Text on the sidebar's green. .82 of white is 4.7:1 on --green-700;
     the .62 this used to be was 3.4:1, and every line it painted is 13px
     or smaller. --accent-ink is the same idea for the active row's meta
     line, which the plain --accent (3.2:1) could not carry. */
  --on-dark-muted: rgba(255, 255, 255, .82);
  --accent-ink: #d0e793;

  --radius: 3px;
  --shadow: 0 1px 2px rgba(18, 20, 15, .04), 0 8px 24px rgba(18, 20, 15, .05);

  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
          "Liberation Mono", monospace;

  --sidebar-w: 19rem;
}

* { box-sizing: border-box; }

/* Several components below set `display` on elements that are toggled with
   the `hidden` attribute; without this the attribute loses to the class. */
[hidden] { display: none !important; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: inherit; }

button, input, select, textarea { font: inherit; color: inherit; }

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* `summary` and anything given a tabindex are in the list too: the FAQ is
   built from <details>, and it is focusable whether or not this selector
   remembers it. :where() keeps the specificity at zero so a component can
   move its own ring inside its box (the two text areas below) without
   having to out-specify this rule. */
:where(a, button, input, select, textarea, summary, details, [tabindex]):focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* On the sidebar's green a green ring is invisible (1.4:1). */
.sidebar :focus-visible { outline-color: var(--focus-on-dark); }

/* A control that hides its real input behind a drawn one - the attachment
   chip, the multistore switch - has nothing of its own to ring, so the
   chip carries the focus of whatever is inside it. */
.chip--file:focus-within,
.chip--toggle:focus-within {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* Straight to the content, past the nav, for anyone arriving by keyboard.
   Off-screen until it takes focus, and it is the first thing in the tab
   order on every page. */
.skiplink {
  position: fixed;
  inset-block-start: .625rem;
  inset-inline-start: .625rem;
  z-index: 100;
  padding: .625rem 1rem;
  background: var(--surface);
  border: 1px solid var(--green-800);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  font-size: .9375rem;
  font-weight: 600;
  text-decoration: none;
  /* Not `display: none` and not `.sr-only`: it has to be reachable by Tab,
     which means it stays in the layout and simply sits off the top. */
  transform: translateY(-250%);
  transition: transform .12s ease;
}

.skiplink:focus { transform: none; }

@media (prefers-reduced-motion: reduce) {
  .skiplink { transition: none; }
}

/* A busy control is not just greyed - it says so (see setBusy() in
   portal.js, which pairs this with an aria-live line). */
[aria-busy="true"] { cursor: progress; }

/* -- Brand ------------------------------------------------------------- */

.brand {
  display: inline-flex;
  align-items: center;
  gap: .625rem;
  text-decoration: none;
  font-weight: 700;
}

.brand__mark {
  width: 1.75rem;
  height: 1.75rem;
  flex: none;
  background: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2032%2032%22%20role%3D%22img%22%20aria-label%3D%22module.monster%22%3E%20%3Cg%20stroke%3D%22%235cc98d%22%20stroke-width%3D%221.8%22%20stroke-linecap%3D%22round%22%20fill%3D%22none%22%3E%20%3Cpath%20d%3D%22M11%2012%20L8.5%205.5%22%2F%3E%20%3Cpath%20d%3D%22M21%2012%20L23.5%205.5%22%2F%3E%20%3C%2Fg%3E%20%3Ccircle%20cx%3D%228.5%22%20cy%3D%224.2%22%20r%3D%222.1%22%20fill%3D%22%235cc98d%22%2F%3E%20%3Ccircle%20cx%3D%2223.5%22%20cy%3D%224.2%22%20r%3D%222.1%22%20fill%3D%22%235cc98d%22%2F%3E%20%3Crect%20x%3D%222%22%20y%3D%229%22%20width%3D%2228%22%20height%3D%2221%22%20rx%3D%226%22%20fill%3D%22%23226b45%22%2F%3E%20%3Ccircle%20cx%3D%2211.5%22%20cy%3D%2217%22%20r%3D%223.5%22%20fill%3D%22%23ffffff%22%2F%3E%20%3Ccircle%20cx%3D%2220.5%22%20cy%3D%2217%22%20r%3D%223.5%22%20fill%3D%22%23ffffff%22%2F%3E%20%3Ccircle%20cx%3D%2212.4%22%20cy%3D%2217.5%22%20r%3D%221.55%22%20fill%3D%22%23123a2a%22%2F%3E%20%3Ccircle%20cx%3D%2221.4%22%20cy%3D%2217.5%22%20r%3D%221.55%22%20fill%3D%22%23123a2a%22%2F%3E%20%3Cpath%20d%3D%22M10%2023.4%20h12%20v0.9%20a6%206%200%200%201%20-12%200%20z%22%20fill%3D%22%23ffffff%22%2F%3E%20%3Cpath%20d%3D%22M12.9%2024.3%20l1.15%202.2%20l1.15%20-2.2%20z%22%20fill%3D%22%23226b45%22%2F%3E%20%3Cpath%20d%3D%22M17.0%2024.3%20l1.15%202.2%20l1.15%20-2.2%20z%22%20fill%3D%22%23226b45%22%2F%3E%20%3C%2Fsvg%3E") center / contain no-repeat;
}

.brand__name { font-size: 1.125rem; letter-spacing: -.015em; }

.brand__beta {
  font-family: var(--mono);
  font-size: .6875rem;
  letter-spacing: .12em;
  color: var(--green-800);
  background: var(--green-100);
  border: 1px solid #cfe4d6;
  border-radius: 2px;
  padding: .1875rem .4375rem;
}

.brand--onDark { color: #fff; }
/* The green head vanishes against the dark sidebar, so the on-dark mark
   inverts it: white head, dark eyes with a green glint. */
.brand--onDark .brand__mark {
  background: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2032%2032%22%20role%3D%22img%22%20aria-label%3D%22module.monster%22%3E%20%3Cg%20stroke%3D%22%235cc98d%22%20stroke-width%3D%221.8%22%20stroke-linecap%3D%22round%22%20fill%3D%22none%22%3E%20%3Cpath%20d%3D%22M11%2012%20L8.5%205.5%22%2F%3E%20%3Cpath%20d%3D%22M21%2012%20L23.5%205.5%22%2F%3E%20%3C%2Fg%3E%20%3Ccircle%20cx%3D%228.5%22%20cy%3D%224.2%22%20r%3D%222.1%22%20fill%3D%22%235cc98d%22%2F%3E%20%3Ccircle%20cx%3D%2223.5%22%20cy%3D%224.2%22%20r%3D%222.1%22%20fill%3D%22%235cc98d%22%2F%3E%20%3Crect%20x%3D%222%22%20y%3D%229%22%20width%3D%2228%22%20height%3D%2221%22%20rx%3D%226%22%20fill%3D%22%23ffffff%22%2F%3E%20%3Ccircle%20cx%3D%2211.5%22%20cy%3D%2217%22%20r%3D%223.5%22%20fill%3D%22%23123a2a%22%2F%3E%20%3Ccircle%20cx%3D%2220.5%22%20cy%3D%2217%22%20r%3D%223.5%22%20fill%3D%22%23123a2a%22%2F%3E%20%3Ccircle%20cx%3D%2212.4%22%20cy%3D%2217.5%22%20r%3D%221.55%22%20fill%3D%22%235cc98d%22%2F%3E%20%3Ccircle%20cx%3D%2221.4%22%20cy%3D%2217.5%22%20r%3D%221.55%22%20fill%3D%22%235cc98d%22%2F%3E%20%3Cpath%20d%3D%22M10%2023.4%20h12%20v0.9%20a6%206%200%200%201%20-12%200%20z%22%20fill%3D%22%23123a2a%22%2F%3E%20%3Cpath%20d%3D%22M12.9%2024.3%20l1.15%202.2%20l1.15%20-2.2%20z%22%20fill%3D%22%23ffffff%22%2F%3E%20%3Cpath%20d%3D%22M17.0%2024.3%20l1.15%202.2%20l1.15%20-2.2%20z%22%20fill%3D%22%23ffffff%22%2F%3E%20%3C%2Fsvg%3E") center / contain no-repeat;
}

/* -- Buttons ----------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  padding: .6875rem 1.125rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  font-weight: 600;
  font-size: .9375rem;
  text-decoration: none;
  cursor: pointer;
  transition: background .12s ease, border-color .12s ease;
}

.btn:hover { border-color: #cdd3ce; }

.btn--primary {
  background: var(--green-800);
  border-color: var(--green-800);
  color: #fff;
}

.btn--primary:hover { background: var(--green-900); border-color: var(--green-900); }
.btn--primary:disabled { background: var(--ink-4); border-color: var(--ink-4); cursor: not-allowed; }

.btn--danger {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
}

.btn--danger:hover { background: #8f2a23; border-color: #8f2a23; }
.btn--danger:disabled {
  background: var(--surface);
  border-color: var(--line);
  color: var(--ink-4);
  cursor: not-allowed;
}

.btn--sm { padding: .5rem .875rem; font-size: .875rem; }
.btn--block { width: 100%; }

.linkbtn {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-size: .8125rem;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.linkbtn--muted { color: var(--ink-3); }

/* -- Top nav ----------------------------------------------------------- */

.topnav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 1.125rem clamp(1rem, 4vw, 3rem);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}

.topnav--bare { border-bottom: 0; background: transparent; }

.topnav__links {
  display: flex;
  align-items: center;
  gap: clamp(.75rem, 2vw, 1.75rem);
  font-size: .9375rem;
}

.topnav__links a { text-decoration: none; }
.topnav__links a:hover { color: var(--green-800); }

.topnav__user {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .375rem .75rem .375rem .375rem;
}

.avatar {
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 2px;
  background: #d7dcd8;
  color: var(--ink-2);
  display: grid;
  place-items: center;
  font-size: .8125rem;
  font-weight: 700;
  flex: none;
}

.avatar--onDark { background: rgba(255, 255, 255, .22); color: #fff; }

.langswitch { display: inline-flex; align-items: center; }

/* Off-screen until it takes focus, like the skip link: the switcher reads
   as a bare select under a mouse, and grows its commit button for whoever
   arrives at it by Tab. */
.langswitch__go {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.langswitch__go:focus {
  position: static;
  width: auto;
  height: auto;
  padding: .5rem .875rem;
  overflow: visible;
  clip-path: none;
  margin-inline-start: .375rem;
  /* Spelled out rather than inherited: this button appears in the topnav,
     in the footer and in the sidebar, and `color: inherit` on the last of
     those would paint white text on the button's white fill. */
  background: var(--surface);
  border-color: var(--green-800);
  color: var(--ink);
}

.langswitch select {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  padding: .4375rem .5rem;
  font-size: .875rem;
  cursor: pointer;
}

/* -- Landing ----------------------------------------------------------- */

.landing {
  max-width: 74rem;
  margin-inline: auto;
  padding: clamp(2.5rem, 7vw, 5.5rem) clamp(1rem, 4vw, 2rem) 4rem;
  text-align: center;
}

.eyebrow {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .75rem;
  margin: 0 0 clamp(1.5rem, 3vw, 2.25rem);
  font-family: var(--mono);
  font-size: .8125rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.eyebrow__dot {
  width: .625rem;
  height: .625rem;
  background: var(--green-600);
  border-radius: 1px;
}

.eyebrow__sep { color: var(--ink-4); }

/* The strapline, above the headline: it names what the product does in
   five words, so it reads first and the headline expands on it. */
.hero__tagline {
  margin: 0 0 .5rem;
  font-family: var(--mono);
  font-size: clamp(.9375rem, 1.6vw, 1.125rem);
  letter-spacing: -.01em;
  color: var(--green-800);
}

.hero__title {
  margin: 0;
  font-size: clamp(2.25rem, 6.2vw, 4.25rem);
  line-height: 1.02;
  letter-spacing: -.035em;
  font-weight: 800;
}

.hero__subtitle {
  max-width: 44rem;
  margin: clamp(1.25rem, 2.5vw, 1.75rem) auto 0;
  font-size: clamp(1rem, 1.4vw, 1.1875rem);
  line-height: 1.55;
  color: var(--ink-2);
}

/* -- Composer ---------------------------------------------------------- */

.composer {
  margin-block: clamp(2rem, 4.5vw, 3.25rem) 0;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  text-align: start;
  position: relative;
}

.composer__input {
  display: block;
  width: 100%;
  border: 0;
  background: none;
  resize: none;
  padding: clamp(1.25rem, 2.5vw, 1.75rem);
  font-size: clamp(1.0625rem, 1.5vw, 1.375rem);
  line-height: 1.45;
  min-height: 5.5rem;
  max-height: 18rem;
}

.composer__input::placeholder { color: var(--ink-4); }

/* The box has no border of its own, so the ring goes inside it rather than
   around the whole composer - but it is still drawn: `outline: none` on a
   text area is exactly the thing a keyboard user cannot work around. */
.composer__input:focus { outline: none; }
.composer__input:focus-visible { outline: 2px solid var(--focus); outline-offset: -4px; }

.composer__bar {
  display: flex;
  align-items: center;
  gap: .625rem;
  flex-wrap: wrap;
  padding: .75rem;
  border-top: 1px solid var(--line-2);
}

.composer__shortcut {
  margin-inline-start: auto;
  font-family: var(--mono);
  font-size: .8125rem;
  color: var(--ink-4);
}

.composer__send {
  flex: none;
  width: 3.25rem;
  height: 2.875rem;
  border: 0;
  border-radius: 2px;
  background: var(--green-800);
  color: #fff;
  cursor: pointer;
  display: grid;
  place-items: center;
}

.composer__send:hover { background: var(--green-900); }

/* Busy, not disabled: the request is in flight and the button keeps its
   place in the tab order (see setBusy() in portal.js). The [disabled] pair
   stays for anything the browser greys out on its own. */
.composer__send[disabled],
.composer__send[aria-busy="true"] { background: var(--ink-4); cursor: progress; }
.composer__send[disabled]:hover,
.composer__send[aria-busy="true"]:hover { background: var(--ink-4); }
.composer__send-icon { font-size: .875rem; }

/* RTL: the play glyph is directional where the rest of the icon set is not. */
[dir="rtl"] .composer__send-icon { transform: scaleX(-1); }

.composer__error,
.convo__error {
  margin: 0 .75rem .75rem;
  padding: .625rem .75rem;
  border-radius: 2px;
  background: var(--danger-bg);
  color: var(--danger);
  font-size: .875rem;
}

.composer__file {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin: 0 .75rem .75rem;
  padding: .5rem .75rem;
  background: var(--green-050);
  border: 1px solid #dbe8df;
  border-radius: 2px;
  font-size: .8125rem;
  color: var(--ink-2);
}

.composer__file-remove {
  margin-inline-start: auto;
  border: 0;
  background: none;
  cursor: pointer;
  font-size: 1.125rem;
  line-height: 1;
  color: var(--ink-3);
}

/* Segmented PrestaShop version picker */

.segmented {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--line);
  border-radius: 2px;
  overflow: hidden;
}

.segmented__label {
  display: grid;
  place-items: center;
  padding-inline: .75rem;
  font-family: var(--mono);
  font-size: .8125rem;
  color: var(--ink-3);
  border-inline-end: 1px solid var(--line);
}

.segmented__item {
  display: inline-flex;
  align-items: center;
  gap: .3125rem;
  border: 0;
  border-inline-end: 1px solid var(--line);
  background: var(--surface);
  padding: .6875rem .8125rem;
  font-family: var(--mono);
  font-size: .875rem;
  cursor: pointer;
}

.segmented__item:last-child { border-inline-end: 0; }
.segmented__item:hover:not(.is-disabled) { background: var(--green-050); }

.segmented__item.is-on { background: var(--green-800); color: #fff; }
.segmented__item.is-on:hover { background: var(--green-900); }

.segmented__tick { font-size: .6875rem; opacity: 0; }
.segmented__item.is-on .segmented__tick { opacity: 1; }

.segmented__item.is-disabled { color: var(--ink-4); cursor: not-allowed; }

/* Toolbar chips */

.chip {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--surface);
  padding: .625rem .8125rem;
  font-size: .9375rem;
  cursor: pointer;
  position: relative;
}

.chip:hover { border-color: #cdd3ce; }

.chip__box {
  width: 1.0625rem;
  height: 1.0625rem;
  border: 1px solid var(--control-line);
  border-radius: 2px;
  flex: none;
}

.chip--file.has-file .chip__box { background: var(--green-600); border-color: var(--green-600); }

/* Two things the menu chip does differently from the other two. Its padding
   lives on the button below, so the whole chip is the hit target rather than
   just the label - on a phone the chrome around a label is most of what a
   thumb lands on. And it stays unpositioned, so the menu hangs off the
   composer instead (see below). */
.chip--menu { padding: 0; position: static; }

.chip__button {
  display: inline-flex;
  align-items: center;
  gap: .625rem;
  border: 0;
  background: none;
  padding: .625rem .8125rem;
  cursor: pointer;
}

.chip__value {
  font-family: var(--mono);
  font-size: .8125rem;
  letter-spacing: .04em;
  color: var(--ink-2);
  text-transform: uppercase;
}

.chip--menu.is-open { border-color: var(--ink); }

.chip--toggle.is-on {
  background: var(--green-050);
  border-color: var(--green-500);
}

.switch {
  width: 2.375rem;
  height: 1.375rem;
  border-radius: 999px;
  background: var(--control-line);
  position: relative;
  transition: background .15s ease;
  flex: none;
}

.switch__dot {
  position: absolute;
  inset-block-start: .1875rem;
  inset-inline-start: .1875rem;
  width: 1rem;
  height: 1rem;
  border-radius: 50%;
  background: #fff;
  transition: inset-inline-start .15s ease;
}

.chip--toggle.is-on .switch { background: var(--green-600); }
.chip--toggle.is-on .switch__dot { inset-inline-start: 1.1875rem; }

/* Language menu */

/* Positioned against the composer, not against the chip that opens it: the
   chip's place in the toolbar moves as the bar wraps, and a panel anchored
   to it runs off the end of the page on any window narrow enough to push
   the chip to the far side. From here it starts at the composer's inline
   edge and can never be wider than the composer is. */
/* A centred modal, not an anchored dropdown: the chat toolbar sits at the
   bottom of a sticky footer, where a dropdown has nowhere to go that is
   neither off the bottom of the window nor on top of the conversation.
   `margin: auto` is what centres a dialog opened with showModal(). */
.langmenu {
  margin: auto;
  width: min(28rem, calc(100vw - 2rem));
  max-height: min(34rem, calc(100vh - 4rem));
  background: var(--surface);
  color: inherit;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 0;
  overflow: hidden;
  cursor: default;
}

/* Only when open, and only here: a bare `display: flex` on .langmenu
   outranks the UA's `dialog:not([open]) { display: none }`, which leaves
   all four pickers rendered inline over the page. */
.langmenu[open] {
  display: flex;
  flex-direction: column;
}

.langmenu::backdrop { background: rgb(12 20 16 / .5); }

.langmenu__head {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1rem 1rem .75rem;
  border-bottom: 1px solid var(--line-2);
}

.langmenu__close {
  margin-inline-start: auto;
  flex: none;
  width: 2rem;
  height: 2rem;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--ink-3);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
}

.langmenu__close:hover { background: var(--green-050); color: var(--ink); }

.langmenu__foot {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: .75rem 1rem 1rem;
  border-top: 1px solid var(--line-2);
}

.langmenu__foot .btn { margin-inline-start: auto; flex: none; }

.langmenu__title {
  margin: 0;
  font-family: var(--mono);
  font-size: .75rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.langmenu__list {
  list-style: none;
  margin: 0;
  padding: .5rem 0;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

.langmenu__row {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: .625rem 1rem;
  cursor: pointer;
}

.langmenu__row:hover { background: var(--green-050); }
.langmenu__row input { position: absolute; opacity: 0; pointer-events: none; }

.langmenu__check {
  width: 1.125rem;
  height: 1.125rem;
  border: 1px solid var(--control-line);
  border-radius: 2px;
  display: grid;
  place-items: center;
  font-size: .75rem;
  color: transparent;
  flex: none;
}

.langmenu__row input:checked + .langmenu__check {
  background: var(--green-500);
  border-color: var(--green-500);
  color: #fff;
}

.langmenu__row input:focus-visible + .langmenu__check {
  outline: 2px solid var(--green-600);
  outline-offset: 2px;
}

.langmenu__name { flex: 1; }

.langmenu__code {
  font-family: var(--mono);
  font-size: .8125rem;
  color: var(--ink-4);
}

/* Sits inside .langmenu__foot, which already draws the rule and the
   padding it used to carry itself. */
.langmenu__note {
  margin: 0;
  font-size: .8125rem;
  color: var(--ink-3);
}

/* -- Suggestions ------------------------------------------------------- */

.suggestions {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: .75rem;
  margin: 1.75rem 0 0;
  padding: 0;
}

.suggestion {
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--surface);
  padding: .8125rem 1.25rem;
  font-size: .9375rem;
  cursor: pointer;
  transition: border-color .12s ease, background .12s ease;
}

.suggestion:hover { border-color: var(--green-500); background: var(--green-050); }

/* -- Landing sections -------------------------------------------------- */

/* The credits explainer and the FAQ, between the suggestion chips and the
   footer. The landing page centres its text, so anything here that runs to
   more than a line - a card body, an answer - sets `text-align: start`
   again rather than reading as centred prose. */

.lsection {
  margin-block-start: clamp(3.5rem, 8vw, 6rem);
  text-align: center;
}

.lsection__title {
  margin: 0;
  font-size: clamp(1.625rem, 3.2vw, 2.25rem);
  letter-spacing: -.025em;
}

.lsection__intro {
  max-width: 44rem;
  margin: .875rem auto 0;
  color: var(--ink-2);
  line-height: 1.6;
}

/* Wraps on its own, like the content-page cards: four across on a desktop,
   one under the other at 360px, with nothing to change in between. */
.lcards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1rem;
  margin-block-start: clamp(1.75rem, 3.5vw, 2.5rem);
  text-align: start;
}

.lcard {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.5rem;
}

.lcard__title { margin: 0 0 .5rem; font-size: 1rem; font-weight: 700; }
.lcard__body { margin: 0; font-size: .9375rem; color: var(--ink-3); line-height: 1.55; }

.lsection__note {
  max-width: 44rem;
  margin: 1.5rem auto 0;
  font-size: .9375rem;
  color: var(--ink-3);
}

.lsection__link {
  font-weight: 600;
  color: var(--green-700);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--line);
}

.lsection__link:hover { color: var(--green-800); text-decoration-color: currentColor; }

.lcta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1.25rem;
  margin-block-start: clamp(1.5rem, 3vw, 2rem);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: clamp(1.25rem, 3vw, 1.75rem);
  text-align: start;
}

.lcta__text { margin: 0; font-size: 1.0625rem; color: var(--ink-2); }

/* -- FAQ --------------------------------------------------------------- */

/* Plain <details>/<summary>: open, close and keyboard operation are the
   browser's, so there is no JS behind this and nothing to go wrong with
   the stylesheet off. */

.faq {
  max-width: 48rem;
  margin: clamp(1.75rem, 3.5vw, 2.5rem) auto 0;
  text-align: start;
  border-block-start: 1px solid var(--line);
}

.faq__item { border-block-end: 1px solid var(--line); }

.faq__q {
  display: flex;
  align-items: baseline;
  gap: .75rem;
  padding: 1.125rem .25rem;
  font-weight: 600;
  cursor: pointer;
  /* Both lines drop the browser's own disclosure triangle - the second is
     what does it in Safari - and it is replaced below by a marker that
     sits on the correct side in an RTL locale. */
  list-style: none;
}

.faq__q::-webkit-details-marker { display: none; }

.faq__q::after {
  content: "+";
  margin-inline-start: auto;
  font-family: var(--mono);
  font-size: 1.125rem;
  line-height: 1;
  color: var(--green-700);
}

.faq__item[open] > .faq__q::after { content: "\2212"; }

.faq__q:hover { color: var(--green-800); }

.faq__a { padding: 0 .25rem 1.375rem; }

.faq__para { margin: 0 0 .75rem; color: var(--ink-2); line-height: 1.65; }
.faq__para:last-child { margin-bottom: 0; }

/* -- Content pages ----------------------------------------------------- */

/* One template renders every page in config.PORTAL_CONTENT_PAGES, so what
   is here is the whole vocabulary those pages get - a page that wants a
   shape it can't build from these blocks wants a new block kind, not a
   stylesheet of its own. */

body.page-content { display: flex; flex-direction: column; }

.contentpage {
  flex: 1;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 52rem;
  margin-inline: auto;
  padding: clamp(1.5rem, 5vw, 3.5rem) clamp(1rem, 4vw, 2rem) 2rem;
}

.content__title {
  margin: 0;
  font-size: clamp(2rem, 4.5vw, 2.75rem);
  line-height: 1.08;
  letter-spacing: -.03em;
  font-weight: 800;
}

.content__intro {
  max-width: 42rem;
  margin: clamp(1rem, 2vw, 1.25rem) 0 0;
  font-size: clamp(1rem, 1.4vw, 1.125rem);
  line-height: 1.6;
  color: var(--ink-2);
}

.content__block { margin-block-start: clamp(2.5rem, 5vw, 3.5rem); }

.content__heading {
  margin: 0 0 1rem;
  font-size: 1.25rem;
  letter-spacing: -.015em;
}

.content__para { margin: 0 0 1rem; color: var(--ink-2); line-height: 1.65; }
.content__para:last-child { margin-bottom: 0; }

.content__list { list-style: none; margin: 0; padding: 0; display: grid; gap: .75rem; }

.content__item {
  position: relative;
  padding-inline-start: 1.5rem;
  color: var(--ink-2);
  line-height: 1.6;
}

.content__item::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: .5625rem;
  width: .375rem;
  height: .375rem;
  background: var(--green-600);
  border-radius: 1px;
}

/* Wraps on its own rather than at a breakpoint, so a block of two cards
   and a block of five both look deliberate. */
.content__cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1rem;
}

.content__card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.5rem;
}

.content__card-title { margin: 0 0 .5rem; font-size: 1rem; font-weight: 700; }
.content__card-body { margin: 0; font-size: .9375rem; color: var(--ink-3); line-height: 1.5; }

/* Follow-on links for a paragraph that has to send the reader elsewhere.
   Sits tight under the prose block above it rather than a block apart. */
.content__block--links { margin-block-start: 1.25rem; }

.content__links {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1.5rem;
}

.content__link {
  font-weight: 600;
  color: var(--green-700);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--line);
}

.content__link:hover { color: var(--green-800); text-decoration-color: currentColor; }

.content__cta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1.25rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: clamp(1.25rem, 3vw, 1.75rem);
}

.content__cta-text { margin: 0; font-size: 1.0625rem; color: var(--ink-2); }

/* -- App shell --------------------------------------------------------- */

.shell { display: flex; min-height: 100vh; }

.sidebar {
  width: var(--sidebar-w);
  flex: none;
  background: var(--green-700);
  color: #fff;
  display: flex;
  flex-direction: column;
  padding: 1.5rem 1.25rem 1.25rem;
  gap: 1.25rem;
  position: sticky;
  top: 0;
  height: 100vh;
}

.sidebar__new {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: .875rem 1rem;
  background: rgba(255, 255, 255, .12);
  border: 1px solid rgba(255, 255, 255, .18);
  border-radius: 2px;
  text-decoration: none;
  font-size: 1.0625rem;
  font-weight: 600;
}

.sidebar__new:hover { background: rgba(255, 255, 255, .18); }
.sidebar__plus { font-size: 1.125rem; opacity: .8; }

.sidebar__scroll { flex: 1; overflow-y: auto; margin-inline: -1.25rem; padding-inline: 1.25rem; }

.sidebar__group + .sidebar__group { margin-top: 1.75rem; }

.sidebar__heading {
  margin: 0 0 .75rem;
  font-family: var(--mono);
  font-size: .6875rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  font-weight: 400;
  color: var(--on-dark-muted);
}

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

.sidebar__item.is-hidden { display: none; }
.sidebar.is-expanded .sidebar__item.is-hidden { display: block; }

.sidebar__item {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: .5rem;
}

.sidebar__link {
  display: block;
  flex: 1;
  min-width: 0;
  padding: .625rem .875rem;
  margin-inline: -.875rem;
  border-inline-start: 3px solid transparent;
  text-decoration: none;
  border-radius: 2px;
}

.sidebar__item a.sidebar__link:hover { background: rgba(255, 255, 255, .08); }

.sidebar__item.is-active .sidebar__link {
  background: rgba(255, 255, 255, .12);
  border-inline-start-color: var(--accent);
}

.sidebar__download {
  flex: none;
  margin-top: .5rem;
  padding: .25rem .5rem;
  font-size: .75rem;
  font-weight: 600;
  color: var(--green-900);
  background: #fff;
  border-radius: 2px;
  text-decoration: none;
  white-space: nowrap;
}

.sidebar__download:hover { background: var(--green-050); }

.sidebar__name { display: block; font-size: 1.0625rem; }

.sidebar__meta {
  display: block;
  margin-top: .1875rem;
  font-family: var(--mono);
  font-size: .8125rem;
  color: var(--on-dark-muted);
}

.sidebar__item.is-active .sidebar__meta { color: var(--accent-ink); }

.sidebar__more {
  margin-top: 1.5rem;
  background: none;
  border: 0;
  padding: 0;
  color: var(--on-dark-muted);
  font-size: .9375rem;
  cursor: pointer;
}

.sidebar__more:hover { color: #fff; }

.sidebar__empty { color: var(--on-dark-muted); font-size: .9375rem; }
.sidebar__empty-title { margin: 0 0 .25rem; font-weight: 600; color: #fff; }
.sidebar__empty-body { margin: 0; }

/* A group with nothing in it says so rather than vanishing: "Delivered"
   with one line under it is how someone watching a first build knows where
   the module will appear. */
.sidebar__group-empty {
  margin: 0;
  font-size: .875rem;
  color: var(--on-dark-muted);
}

/* The 15s sidebar refresh fails silently for one tick - a dropped poll on
   a flaky connection is not news - and says so on the second, so a stale
   build state is never mistaken for a stalled one. */
.sidebar__offline {
  margin: .75rem 0 0;
  font-size: .8125rem;
  color: var(--accent-ink);
}

.sidebar__foot { border-top: 1px solid rgba(255, 255, 255, .16); padding-top: 1rem; }

.sidebar__user {
  display: flex;
  align-items: center;
  gap: .75rem;
  text-decoration: none;
  padding: .375rem;
  margin: -.375rem;
  border-radius: 2px;
}

.sidebar__user:hover { background: rgba(255, 255, 255, .08); }
.sidebar__user-text { display: flex; flex-direction: column; }
.sidebar__user-name { font-size: .9375rem; }

.sidebar__user-credits {
  font-family: var(--mono);
  font-size: .8125rem;
  color: var(--on-dark-muted);
}

.sidebar__footlinks {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  margin-top: .875rem;
  color: var(--on-dark-muted);
}

.sidebar__footlinks select {
  background: rgba(255, 255, 255, .1);
  border-color: rgba(255, 255, 255, .2);
  color: #fff;
  font-size: .8125rem;
  padding: .3125rem .375rem;
}

.sidebar__footlinks select option { color: var(--ink); }
.sidebar__footlinks .linkbtn { color: var(--on-dark-muted); }
.sidebar__footlinks .linkbtn:hover { color: #fff; }

.sidebar__opener {
  display: none;
  position: fixed;
  inset-block-start: 1rem;
  inset-inline-start: 1rem;
  z-index: 40;
  width: 2.5rem;
  height: 2.5rem;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--surface);
  cursor: pointer;
}

/* Column, so the footer can be pushed to the bottom of a short page. */
.shell__main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  padding: clamp(2rem, 5vw, 4.5rem) clamp(1rem, 4vw, 3rem);
}

.shell__center { max-width: 60rem; margin-inline: auto; text-align: center; }
.shell__center--narrow { max-width: 34rem; text-align: start; }

.page__title { margin: 0 0 1.5rem; font-size: 1.75rem; letter-spacing: -.02em; }

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.5rem;
}

.notice {
  margin: 1.5rem 0 0;
  padding: 1.25rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  text-align: start;
}

.notice--warn { border-color: #e6d9a8; background: #fdf8e8; }
.notice__title { margin: 0 0 .375rem; font-size: 1.0625rem; }
.notice__body { margin: 0 0 1rem; color: var(--ink-2); font-size: .9375rem; }

/* -- Conversation ------------------------------------------------------ */

.shell__main--chat { width: 100%; max-width: none; margin-inline: 0; }

/* Holds the thread and its sticky composer. The composer sticks to the
   bottom of this wrapper, so it stops at the end of the conversation
   rather than sliding over the footer below. */
.convo__body { flex: 1; display: flex; flex-direction: column; min-height: 0; }

.convo__head { border-bottom: 1px solid var(--line); padding-bottom: 1.25rem; }

.convo__back { font-size: .875rem; color: var(--ink-3); text-decoration: none; }
.convo__back:hover { color: var(--green-800); }

.convo__title { margin: .5rem 0 .375rem; font-size: 1.75rem; letter-spacing: -.02em; }

/* The build settings, kept on the chat page. It borrows .composer__bar for
   the control row, so all it adds is the frame and the title above it. */
.settingsbar {
  margin-block-start: 1rem;
  border: 1px solid var(--line-2);
  border-radius: 12px;
}

.settingsbar__title {
  margin: 0;
  padding: .625rem .75rem 0;
  font-size: .8125rem;
  color: var(--ink-3);
}

.settingsbar .composer__bar { border-top: 0; }

.settingsbar__status {
  margin-inline-start: auto;
  font-size: .8125rem;
  color: var(--ink-3);
}

.convo__facts {
  display: flex;
  flex-wrap: wrap;
  gap: .375rem 1rem;
  margin: 0;
  font-family: var(--mono);
  font-size: .8125rem;
  color: var(--ink-3);
}

/* Only ever inside .build now, under the step strip's own spacing. */
.progress { margin: 0; }

.progress__bar {
  height: .375rem;
  background: var(--line);
  border-radius: 999px;
  overflow: hidden;
}

.progress__bar span {
  display: block;
  height: 100%;
  background: var(--green-600);
  transition: width .4s ease;
}

.progress__label {
  margin: .5rem 0 0;
  font-family: var(--mono);
  font-size: .8125rem;
  color: var(--ink-3);
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .75rem 1rem;
}

.progress__label .btn { font-family: var(--sans); }

/* -- Build tracker ----------------------------------------------------- */

/* Takes the composer's place in the sticky foot once the module has been
   sent to the pipeline: from that point the build is the thing the
   customer is waiting on, so it is the thing that stays in view. */
.build {
  padding: .875rem 1rem 1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

/* Nine steps do not fit across a phone, so the row scrolls on its own
   rather than shrinking the circles to nothing; portal.js keeps the
   current one in view. */
.build__steps {
  display: flex;
  list-style: none;
  margin: 0 0 .875rem;
  padding: 0 0 .375rem;
  overflow-x: auto;
  overscroll-behavior-inline: contain;
}

.build__step {
  position: relative;
  /* Basis, not `auto`: sized from the label's content the steps stretch
     to whatever the longest one says and shove the last ones off the end.
     They never shrink below it, so a narrow screen scrolls instead. */
  flex: 1 0 4.5rem;
  min-width: 4.5rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .375rem;
  text-align: center;
}

/* The rail joining a circle back to the one before it. Drawn from the
   circle's centre backwards, so it follows the writing direction and the
   first step never has one. */
.build__step::before {
  content: "";
  position: absolute;
  inset-inline-end: 50%;
  inset-block-start: .5rem;
  width: 100%;
  height: 2px;
  background: var(--line);
}

.build__step:first-child::before { content: none; }

.build__dot {
  position: relative;
  z-index: 1;
  width: 1.125rem;
  height: 1.125rem;
  border-radius: 50%;
  background: var(--surface);
  border: 2px solid var(--control-line);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: .625rem;
  line-height: 1;
  color: #fff;
}

/* Full width of its step, so a two-word label wraps under its circle
   instead of stretching the step and pushing the last ones off the end. */
.build__name {
  width: 100%;
  padding-inline: .125rem;
  font-size: .6875rem;
  line-height: 1.3;
  color: var(--ink-4);
}

.build__step.is-done .build__dot,
.build__step.is-current .build__dot,
.build__step.is-failed .build__dot,
.build__step.is-blocked .build__dot { border-color: transparent; }

.build__step.is-done .build__dot { background: var(--green-600); }
.build__step.is-done .build__dot::after { content: "\2713"; }
.build__step.is-done::before,
.build__step.is-current::before,
.build__step.is-failed::before,
.build__step.is-blocked::before { background: var(--green-600); }

.build__step.is-current .build__dot { background: var(--green-500); }
.build__step.is-current .build__name { color: var(--ink-2); font-weight: 600; }

/* The one being worked on, breathing so a step that takes ten minutes
   still looks like something is happening. */
.build__step.is-active .build__dot { animation: build-pulse 1.6s infinite; }

.build__step.is-failed .build__dot { background: var(--danger); }
.build__step.is-failed .build__dot::after { content: "!"; }
.build__step.is-failed .build__name { color: var(--danger); font-weight: 600; }

.build__step.is-blocked .build__dot { background: var(--accent); color: var(--ink); }
.build__step.is-blocked .build__dot::after { content: "\2026"; }
.build__step.is-blocked .build__name { color: var(--ink-2); font-weight: 600; }

@keyframes build-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(53, 133, 92, .45); }
  70%  { box-shadow: 0 0 0 .5rem rgba(53, 133, 92, 0); }
  100% { box-shadow: 0 0 0 0 rgba(53, 133, 92, 0); }
}

/* The rotating line under the status. It fades between messages rather
   than snapping, so the eye reads it as one thing being updated. */
.build__note {
  margin: .375rem 0 0;
  font-size: .8125rem;
  line-height: 1.45;
  color: var(--ink-3);
  min-height: 1.2rem;
  transition: opacity .25s ease;
}

.build__note.is-fading { opacity: 0; }

/* Delivered / failed: the zip is ready, so the tracker shrinks and the
   reply box (for revisions) stays in the sticky foot. */
.page-module main[data-status="done"] .build__steps,
.page-module main[data-status="failed"] .build__steps,
.page-module main[data-status="done"] .progress,
.page-module main[data-status="failed"] .progress,
.page-module main[data-status="done"] .build__note,
.page-module main[data-status="failed"] .build__note { display: none; }

.page-module main[data-status="done"] .build,
.page-module main[data-status="failed"] .build {
  padding: .5rem .75rem;
  margin-bottom: .75rem;
}

.convo__revise-lead {
  margin: 0 0 .625rem;
  font-size: .9375rem;
  color: var(--ink-2);
}

.convo__actions { margin: 1.25rem 0 0; }

.convo {
  list-style: none;
  margin: 0;
  padding: 1.75rem 0;
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

.bubble { max-width: none; width: 100%; }

.bubble__role {
  font-family: var(--mono);
  font-size: .6875rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-4);
  margin: 0 0 .375rem;
}

.bubble__body {
  margin: 0;
  padding: 1rem 1.125rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  line-height: 1.55;
}

.bubble--user { margin-inline-start: 0; }
.bubble--user .bubble__role { text-align: start; }
.bubble--user .bubble__body { background: var(--green-050); border-color: #dbe8df; }
.bubble--error .bubble__body { background: var(--danger-bg); border-color: #f0d3d1; color: var(--danger); }

.bubble--pending .bubble__body { color: var(--ink-4); }

.dots span {
  display: inline-block;
  width: .3125rem;
  height: .3125rem;
  margin-inline-end: .25rem;
  border-radius: 50%;
  background: var(--ink-4);
  animation: blink 1.2s infinite;
}

.dots span:nth-child(2) { animation-delay: .2s; }
.dots span:nth-child(3) { animation-delay: .4s; }

@keyframes blink { 0%, 60%, 100% { opacity: .25; } 30% { opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  .dots span { animation: none; opacity: .5; }
  .progress__bar span { transition: none; }
  .build__step.is-active .build__dot { animation: none; }
  .build__note { transition: none; }
}

/* The thread arrives from /api/modules/<id>/messages, so between first
   paint and first reply there is an empty <ol>. This line stands in for it:
   loading to begin with, then either "nothing here yet" or the reason the
   fetch failed. */
.convo__state {
  margin: 0;
  padding: 1.75rem 0 0;
  color: var(--ink-3);
  font-size: .9375rem;
}

.convo__state--error { color: var(--danger); }

.convo__foot { position: sticky; bottom: 0; background: var(--bg); padding-bottom: 1.5rem; }

.convo__approve {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: .875rem;
  padding: 1rem 1.125rem;
  background: var(--green-050);
  border: 1px solid #dbe8df;
  border-radius: var(--radius);
}

.convo__approve-text { margin: 0; flex: 1; font-size: .9375rem; }

/* Why the build button will not start anything, sitting where the button
   is rather than in the sidebar's credit line. */
.convo__approve-note {
  margin: 0;
  flex-basis: 100%;
  color: var(--danger);
  font-size: .875rem;
}

.replybox {
  display: flex;
  align-items: flex-end;
  gap: .625rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .625rem;
  box-shadow: var(--shadow);
}

.replybox__input {
  flex: 1;
  border: 0;
  resize: vertical;
  padding: .5rem;
  font-size: 1rem;
  line-height: 1.45;
  min-height: calc(1.45em * 3 + 1rem);
  max-height: 16rem;
}

.replybox__input:focus { outline: none; }
.replybox__input:focus-visible { outline: 2px solid var(--focus); outline-offset: -3px; }

.replybox__send {
  border: 0;
  border-radius: 2px;
  background: var(--green-800);
  color: #fff;
  padding: .6875rem 1.125rem;
  font-weight: 600;
  cursor: pointer;
}

.replybox__send:hover { background: var(--green-900); }
.replybox__send[disabled],
.replybox__send[aria-busy="true"] { background: var(--ink-4); cursor: progress; }
.replybox__send[disabled]:hover,
.replybox__send[aria-busy="true"]:hover { background: var(--ink-4); }

/* -- Auth & account ---------------------------------------------------- */

.authpage {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(2rem, 5vw, 3rem);
  padding: clamp(1.5rem, 5vw, 3.5rem) 1rem 2rem;
  /* Footer sits under the card, pushed to the bottom on a tall window. */
  min-height: calc(100vh - 5rem);
}

.backlink {
  display: inline-flex;
  align-items: center;
  gap: .4375rem;
  margin-bottom: 1.25rem;
  font-size: .875rem;
  color: var(--ink-3);
  text-decoration: none;
}

.backlink:hover { color: var(--green-800); }
.backlink__arrow { font-size: 1rem; line-height: 1; }
[dir="rtl"] .backlink__arrow { transform: scaleX(-1); }

/* Google sign-in */

.gbtn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .75rem;
  width: 100%;
  padding: .6875rem 1rem;
  border: 1px solid #cdd3ce;
  border-radius: var(--radius);
  background: var(--surface);
  font-size: .9375rem;
  font-weight: 600;
  text-decoration: none;
  transition: background .12s ease, border-color .12s ease;
}

.gbtn:hover { background: #f7f9f8; border-color: #b9c1bb; }
.gbtn__logo { display: grid; place-items: center; flex: none; }

.ordiv {
  display: flex;
  align-items: center;
  gap: .875rem;
  margin: 1.25rem 0;
  color: var(--ink-4);
  font-size: .8125rem;
  text-transform: lowercase;
}

.ordiv::before,
.ordiv::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
}

/* Site footer */

/* One footer partial closes every page, so it takes the width of whatever
   column it lands in: the auth card's by default, the shell's centre
   column inside the app, the full width of the landing page. `margin-block-start:
   auto` drops it to the bottom wherever its parent is a flex column with
   room to spare. */
.sitefoot {
  width: 100%;
  max-width: 38rem;
  margin-block-start: auto;
  margin-inline: auto;
  padding-block-start: 1.5rem;
  text-align: center;
}

.landing > .sitefoot,
.contentpage > .sitefoot { max-width: none; }
.shell__main > .sitefoot { max-width: 60rem; }

.landing > .sitefoot,
.contentpage > .sitefoot,
.shell__main > .sitefoot { padding-block-start: clamp(2.5rem, 6vw, 4rem); }

/* The error page has no padded container of its own. */
.page-error .sitefoot { padding-inline: 1rem; padding-block-end: 2rem; }

.sitefoot__links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: .625rem 1.5rem;
  margin-bottom: 1.25rem;
  font-size: .875rem;
}

.sitefoot__links a { color: var(--ink-3); text-decoration: none; }
.sitefoot__links a:hover { color: var(--green-800); text-decoration: underline; text-underline-offset: 3px; }

.sitefoot__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding-top: 1.125rem;
  border-top: 1px solid var(--line);
}

.sitefoot__copy { margin: 0; font-size: .8125rem; color: var(--ink-4); }

/* Without the language switcher beside it the copyright is the bar. */
.sitefoot__copy:only-child { margin-inline: auto; }

@media (max-width: 30rem) {
  .sitefoot__bar { justify-content: center; }
}

.authcard {
  width: min(26rem, 100%);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: clamp(1.5rem, 4vw, 2.25rem);
}

.authcard__title { margin: 0 0 .375rem; font-size: 1.625rem; letter-spacing: -.02em; }
.authcard__subtitle { margin: 0 0 1.75rem; color: var(--ink-3); font-size: .9375rem; }

.authcard__error {
  margin: 0 0 1.25rem;
  padding: .75rem .875rem;
  border-radius: 2px;
  background: var(--danger-bg);
  color: var(--danger);
  font-size: .875rem;
}

.authcard__flash {
  margin: 0 0 1.25rem;
  padding: .75rem .875rem;
  border-radius: 2px;
  background: var(--green-100);
  color: var(--green-900);
  font-size: .875rem;
}

.authform { display: grid; gap: 1.125rem; }

.field { display: grid; gap: .375rem; }

.field__label { font-size: .875rem; font-weight: 600; color: var(--ink-2); }

.field input,
.field select {
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: .6875rem .75rem;
  background: var(--surface);
  width: 100%;
}

.field input:disabled { background: var(--bg); color: var(--ink-3); }
.field__hint { font-size: .8125rem; color: var(--ink-3); }

/* Same trick as .field__error: a hint that an input points at with
   aria-describedby lives outside the wrapping label, so it is a
   description rather than part of the field's name. */
.field__hint--after { display: block; margin-top: -.75rem; }

.field__static {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin: 0;
  padding-top: .25rem;
}

.authcard__switch { margin: 1.5rem 0 0; font-size: .875rem; color: var(--ink-3); text-align: center; }
.account__section { margin-top: 1.5rem; }
.account__section-title { margin: 0; font-size: 1.0625rem; }
.account__note { margin: 0; color: var(--ink-3); font-size: .875rem; }
.account__list { margin: 0; padding-inline-start: 1.125rem; color: var(--ink-3); font-size: .875rem; }
.account__list li + li { margin-top: .25rem; }

.account__sessions { list-style: none; margin: 0; padding: 0; display: grid; gap: .75rem; }

.account__session {
  display: grid;
  gap: .125rem;
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: .625rem .75rem;
}

.account__session.is-current { border-color: var(--green-500); background: var(--green-050); }
.account__session-line { font-size: .875rem; }
.account__session-seen { font-size: .8125rem; color: var(--ink-3); }

.account__badge {
  display: inline-block;
  margin-inline-start: .375rem;
  border-radius: 2px;
  padding: .0625rem .375rem;
  background: var(--green-100);
  color: var(--green-900);
  font-size: .75rem;
  font-weight: 600;
}

.account__empty { margin: 0; color: var(--ink-3); font-size: .875rem; }

.account__danger { border-color: #f0d3d1; }
.account__danger .account__section-title { color: var(--danger); }

/* Held rather than removed: `disabled` takes the button out of the tab
   order, so a keyboard user meets a sentence about typing their address
   and then nothing to press. aria-disabled leaves it reachable and
   portal.js answers the click with the reason. */
.btn[aria-disabled="true"] {
  background: var(--surface);
  border-color: var(--line);
  color: var(--ink-3);
  cursor: not-allowed;
}

.btn[aria-disabled="true"]:hover { border-color: var(--line); }

/* setBusy() sets both while a request is in flight, and a working button
   is not the same thing as a refused one. */
.btn[aria-busy="true"] { cursor: progress; }

/* A refused field says why next to itself, not only in the summary at the
   top of the card. It sits after the <label> rather than inside it: text
   inside a wrapping label joins the input's accessible name, and this
   belongs to aria-describedby instead. The negative margin pulls it back
   out of the form grid's row gap, so it reads as part of the field above
   it rather than as a row of its own. */
.field__error {
  display: block;
  margin: -.75rem 0 0;
  color: var(--danger);
  font-size: .8125rem;
}

.field input[aria-invalid="true"] { border-color: var(--danger); }

/* -- Errors ------------------------------------------------------------ */

body.page-error { display: flex; flex-direction: column; }

.errorpage { flex: 1; text-align: center; padding: clamp(3rem, 10vw, 7rem) 1rem; }
.errorpage__code { margin: 0; font-family: var(--mono); font-size: .875rem; color: var(--ink-4); }
.errorpage__title { margin: .75rem 0 .5rem; font-size: 2rem; letter-spacing: -.025em; }
.errorpage__body { margin: 0 0 1.75rem; color: var(--ink-3); }

/* -- Responsive -------------------------------------------------------- */

/* Three widths are checked by hand: 1280 (the layout above), 768 - where
   the sidebar is already a drawer and the pointer is likely a finger - and
   360, the narrowest phone the portal is expected on. Nothing below may
   scroll sideways, and nothing anyone taps may be under about 40px on its
   shortest side. */

@media (max-width: 52rem) {
  .sidebar {
    position: fixed;
    z-index: 50;
    /* Never the whole width: the opener stays visible beside the open
       drawer, so the way back out is always on screen. */
    width: min(var(--sidebar-w), calc(100vw - 3.5rem));
    inset-block: 0;
    inset-inline-start: 0;
    transform: translateX(-100%);
    /* Off-screen is not enough: a drawer that is only translated away keeps
       every link inside it in the tab order, so the first Tab on a phone
       used to walk through a dozen invisible controls before reaching the
       page. `visibility` takes them out of it, and rides the same
       transition so the drawer still slides rather than blinking. */
    visibility: hidden;
    transition: transform .2s ease, visibility .2s ease;
  }

  [dir="rtl"] .sidebar { transform: translateX(100%); }

  /* `none`, not `translateX(0)`: a transformed element is the containing
     block for anything fixed inside it, which would shrink the scrim below
     to the drawer's own box. */
  .sidebar.is-open { transform: none; visibility: visible; }

  /* Dims the page behind the open drawer. A pseudo-element inside the
     sidebar's own stacking context, so it covers the shell without any
     extra markup; it takes no pointer events - portal.js closes the drawer
     on a tap outside it. */
  .sidebar.is-open::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    background: rgba(18, 20, 15, .45);
    pointer-events: none;
  }

  /* Above the drawer, so the button that opened it also closes it, and
     stepped aside to the drawer's edge while it is open rather than sitting
     on top of the brand inside it. */
  .sidebar__opener {
    display: block;
    z-index: 60;
    transition: inset-inline-start .2s ease;
  }

  .sidebar.is-open ~ .shell__main .sidebar__opener {
    inset-inline-start: calc(min(var(--sidebar-w), 100vw - 3.5rem) + .5rem);
  }

  .shell__main { padding-top: 4.5rem; }
  .composer__shortcut { display: none; }

  /* With the keyboard hint gone nothing pushes the send button along, so
     it takes the end of whichever row it wraps onto. */
  .composer__send { margin-inline-start: auto; }

  /* Touch sizing. Everything below is a control that reads fine under a
     mouse at 13-15px but lands under the ~40px a thumb needs, so it gets
     the height back from padding rather than from a larger font. */
  .brand,
  .backlink,
  .convo__back,
  .sidebar__more,
  .topnav__links a,
  .sitefoot__links a,
  .content__links .content__link { min-height: 2.625rem; }

  .backlink,
  .convo__back,
  .topnav__links a,
  .sitefoot__links a,
  .content__links .content__link {
    display: inline-flex;
    align-items: center;
  }

  /* A two- or three-letter label is under 40px wide however tall it is, so
     the short ones get their width from padding as well. */
  .topnav__links a,
  .sitefoot__links a { padding-inline: .375rem; }

  .langmenu__row { padding-block: .75rem; }

  /* Taller rows in a panel that is now the width of the composer: scroll
     the page rather than a 20rem box inside it. */
  .langmenu__list { max-height: 60vh; }

  .composer__file-remove {
    display: grid;
    place-items: center;
    min-width: 2.625rem;
    min-height: 2.625rem;
    margin-block: -.625rem;
  }

  .sidebar__more { display: block; }

  /* min-width as well as padding: "Esci" is four letters wide in Italian. */
  .linkbtn { padding-block: .6875rem; min-width: 2.625rem; }
  .btn--sm { padding-block: .6875rem; }
  .langswitch select { min-height: 2.625rem; }
  .sidebar__footlinks select { min-height: 2.625rem; }

  /* The rows keep their old rhythm now that each link carries its own
     padding. */
  .sitefoot__links { gap: 0 1.125rem; margin-bottom: .75rem; }
  .content__links { gap: 0 1.5rem; }
  .backlink { margin-bottom: .75rem; }
}

@media (max-width: 34rem) {
  /* The brand plus five nav items do not fit on one 360px row, so the nav
     takes a row of its own and wraps inside it. */
  .topnav {
    flex-direction: column;
    align-items: flex-start;
    gap: .75rem;
    padding-block: .875rem;
  }

  .topnav__links { width: 100%; flex-wrap: wrap; row-gap: .5rem; }
  .topnav--bare { padding-block: 1rem; }
  .langswitch select { max-width: 100%; }

  .eyebrow { flex-wrap: wrap; }

  /* A compound noun in German or Polish is longer than 360px of headline;
     let it break rather than push the page sideways. */
  .hero__title,
  .content__title,
  .convo__title,
  .page__title,
  .lsection__title,
  .authcard__title,
  .errorpage__title,
  .sidebar__name,
  .field__label { overflow-wrap: break-word; }

  /* Cards give back some of their padding - 3rem of chrome out of 360 is
     a third of the line length. */
  .card,
  .lcard,
  .content__card,
  .authcard,
  .notice { padding: 1.125rem; }

  .composer__input { padding: 1rem; }
  .composer__bar { padding: .625rem; gap: .5rem; }

  /* The version picker takes the whole row and shares it out, so the three
     buttons are the same width instead of one narrow disabled sliver. */
  .segmented { display: flex; width: 100%; }
  .segmented__item { flex: 1; justify-content: center; padding-inline: .5rem; }

  .chip--file,
  .chip--menu,
  .chip--toggle { flex: 1; }

  .chip--menu .chip__button { width: 100%; justify-content: space-between; }
  .chip--toggle { justify-content: space-between; }

  /* Approve reads as a decision, not as a button squeezed beside a
     paragraph two words wide. */
  .convo__approve { flex-direction: column; align-items: stretch; }
  .convo__approve .btn { width: 100%; }

  .field__static { flex-wrap: wrap; gap: 0 1rem; }

  .lcta,
  .content__cta { flex-direction: column; align-items: stretch; }

  .lcta .btn,
  .content__cta .btn { justify-content: center; }
}
