/* Screen sizes: four shared steps, the same in both stylesheets (and 720 in js/liveedit.js).
     480    small phones
     720    phones — 721 and up is a tablet or bigger
     1024   tablets — 1025 and up is a desktop
     1280   narrow desktops
   Components that live in a dialog use container queries sized to themselves instead. */

/* Satoshi (Fontshare, self-hosted): one variable file covers weights 300–900, including the 400/500/600 the UI uses. */
@font-face {
  font-family: 'Satoshi';
  src: url('../fonts/Satoshi-Variable.woff2') format('woff2');
  font-weight: 300 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Satoshi';
  src: url('../fonts/Satoshi-VariableItalic.woff2') format('woff2');
  font-weight: 300 900;
  font-style: italic;
  font-display: swap;
}

:root {
  --bg: #f4f4f3;
  --surface: #ffffff;
  --surface-2: #f0f0ef;
  --surface-3: #e6e6e4;
  --line: #e2e2df;
  --text: #141414;
  --text-2: #5c5c58;
  --text-3: #6b6b66; /* ≥4.5:1 on white and on --surface-2 */
  --text-4: #a3a39e; /* headings over a group of links, well back from the links themselves: 2.3:1 on --bg */
  /* FastGen blue and its shades (the redesign, 28 Oct 2026): fields and quiet buttons on the lightest, the inside of a
     number field and a switch that's off on the next, the darkest for a slider's fill and the counts. */
  --accent: #2f6bff;
  --accent-ink: #ffffff;
  --accent-50: #eaf0ff;
  --accent-hover: #d4defc; /* a light-blue button under the pointer (Ahmed, 28 Oct 2026) */
  --accent-100: #d2dfff;
  --accent-200: #cad9ff;
  --accent-300: #9db9ff; /* placeholders in a field */
  --accent-deep: #1e40af;
  --brand: #d7322b; /* FastGen red: logo and brand only, not buttons */
  --focus: #2f6bff;
  --success: #1a8f4c;
  --success-ink: #145c34; /* dark green text */
  --danger: #b3261e; /* error and destructive text */
  --danger-bg: #fdecea; /* error banner and field background */
  --danger-ink: #8c1d18; /* text on --danger-bg */
  --order-bg: #fde8e6; /* Layer order: layers overlap, so it decides what's on top */
  --warn-bg: #fff3d1;
  --warn-ink: #6e4f00;
  --warn-dot: #e0a100;
  --canvas-bg: #ececea;
  --checker: repeating-conic-gradient(#e7e7e7 0 25%, #fff 0 50%);

  /* type scale */
  /* The smallest text anywhere — units, file types, captions — is 12 px: below that it stops being easy to read. */
  --fs-xs: 12px;
  --fs-sm: 12px;
  --fs-md: 13px;
  --fs-base: 14px;
  --fs-lg: 16px;
  --fs-xl: 20px;

  /* corners: 8 px on buttons, fields, chips and nav items, 6 px on the items nested inside them (the UI proposals) */
  --radius-xs: 6px;
  --radius-sm: 8px;
  --radius: 10px;
  --radius-lg: 14px;
  /* The editor's cards (the redesign, 28 Oct 2026). */
  --radius-card: 16px;

  /* spacing */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;

  /* motion */
  --dur: 150ms;
  --ease: cubic-bezier(0.2, 0, 0, 1);

  --font: 'Satoshi', system-ui, -apple-system, 'Segoe UI', sans-serif;
  color-scheme: light;
}

/* iOS corners (Ahmed, 28 Oct 2026: Figma's corner smoothing at 60 %, "iOS"): a superellipse whose curve starts 1.6 times
   the radius out, as Figma's does, so every radius grows by 1.6 where the browser can draw it (Chrome and Edge 139+).
   Elsewhere the corners stay plain and round, at the radii above. Circles and pills keep `corner-shape: round`. */
@supports (corner-shape: superellipse(2)) {
  :root {
    --radius-xs: 9.6px;
    --radius-sm: 12.8px;
    --radius: 16px;
    --radius-lg: 22.4px;
    --radius-card: 25.6px;
  }

  :where(*) {
    corner-shape: superellipse(1.78);
  }
}

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

html,
body {
  margin: 0;
  height: 100%;
}

/* The editor only (Ahmed, 9 Nov 2026): a sideways swipe (a Magic Mouse, a trackpad) never takes the page Back or
   Forward. Chrome, Edge and Firefox keep to this; Safari may still go. */
.editor-page,
.editor-page body {
  overscroll-behavior-x: none;
}

body {
  background: var(--bg);
  color: var(--text);
  font: 14px/1.45 var(--font);
  -webkit-font-smoothing: antialiased;
}

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

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* `hidden` always wins over component display rules. */
[hidden] {
  display: none !important;
}

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

h1, h2, h3, p {
  margin: 0;
}

/* ---------- layout ---------- */

.app {
  display: grid;
  grid-template-columns: 248px minmax(440px, 1fr) minmax(340px, 0.85fr);
  height: 100vh;
}

.sidebar,
.editor,
.preview {
  min-height: 0;
  min-width: 0;
}

.sidebar {
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 20px;
  background: var(--surface);
  border-inline-end: 1px solid var(--accent);
  overflow-y: auto;
}

/* The options: a white bar on top (the name, Undo and Redo, the tabs) over the cards, a blue line beside the poster. */
.editor {
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--bg);
  border-inline-end: 2px solid var(--accent);
  container: editor / inline-size;
}

/* The poster sits on a quiet dotted "desk", like a design tool canvas. */
.preview {
  display: flex;
  flex-direction: column;
  padding: var(--sp-5);
  container: preview / inline-size;
  background-color: var(--canvas-bg);
  background-image: radial-gradient(color-mix(in srgb, var(--canvas-bg), #000 12%) 1px, transparent 1.2px);
  background-size: 16px 16px;
}

/* ---------- sidebar ---------- */

.brand img {
  display: block;
  height: 42px;
  width: auto;
}

/* The logo's lightning bolt on its own, for the tablet rail. */
.brand-mark {
  display: none;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: 9px;
  background: var(--accent);
  color: var(--accent-ink);
}

/* Post size as a menu above the poster: tablets only, where the sidebar is a rail. */
.ratio-menu {
  display: none;
}

.tagline {
  margin-top: 20px;
  font-size: var(--fs-xs);
  font-weight: 500;
  color: var(--text);
  text-wrap: balance;
}

/* Post size, Workspace and Layers: blue headings (Ahmed, 28 Oct 2026). */
.side-title {
  padding: 0 0 10px;
  font-size: var(--fs-xs);
  font-weight: 500;
  text-transform: uppercase;
  color: var(--accent);
}

/* Post size and Workspace: on desktop a heading that opens and closes its section (js/sidebar-sections.js), with a
   line under Post size's and an arrow; on a tablet's rail and a phone's top bar, the plain title it always was. */
.side-heading {
  margin: 0;
  font: inherit;
}

.side-head {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: start;
}

.side-head-text {
  display: grid;
  flex: 1;
  min-width: 0;
}

.side-note,
.side-chevron {
  display: none;
}

@media (min-width: 1025px) {
  /* Post size and Workspace open and close on one curve and time, eased in and out (Ahmed, 24 Oct 2026). */
  .side-section {
    --fold-time: 400ms;
    --fold-ease: cubic-bezier(0.4, 0, 0.2, 1);
  }

  .side-head {
    margin-bottom: 4px;
    padding: 6px var(--sp-2);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background-color var(--dur) var(--ease);
  }

  .side-head:hover {
    background: var(--accent-50);
  }

  .side-head .side-title {
    padding: 0;
  }

  .side-note {
    display: block;
    margin-top: 2px;
    font-size: var(--fs-sm);
    color: var(--text-3);
  }

  .side-chevron {
    display: block;
    flex: none;
    color: var(--accent);
    transition: transform var(--fold-time) var(--fold-ease);
  }

  .side-section:not(.is-closed) .side-chevron {
    transform: rotate(180deg);
  }

  /* Each size in a fold: a one-row grid whose row closes from 1fr to 0fr, so it slides open to its own height, the
     size fading and sliding in with its row, never after it, so the room and the size arrive together. Workspace's
     list slides between the pile's height and its own instead. */
  .ratios .ratio-list {
    gap: 0;
  }

  .ratio-fold {
    display: grid;
    grid-template-rows: 1fr;
    transition: grid-template-rows var(--fold-time) var(--fold-ease);
  }

  .ratio-fold-inner,
  .side-fold > * {
    min-height: 0;
    overflow: hidden;
  }

  .ratio-fold-inner {
    transition: opacity var(--fold-time) var(--fold-ease), transform var(--fold-time) var(--fold-ease), visibility 0s;
  }

  /* The 2px between sizes as margins inside the fold, so a folded size takes no room at all. */
  .ratio-fold-inner > .ratio {
    margin-block: 1px;
  }

  /* Layers' title sits as Post size's and Workspace's do in their headings. */
  .layers-head {
    margin-bottom: 4px;
  }

  .layers-head .side-title {
    padding: 6px var(--sp-2);
  }

  /* Inside a fold the focus ring is drawn inside the row, where the fold doesn't cut it off. */
  .ratios .ratio:has(input:focus-visible) {
    outline-offset: -2px;
  }

  .ratios.is-closed .ratio-fold:not(:has(input:checked)) {
    grid-template-rows: 0fr;
  }

  /* Closed, what's folded away is out of reach too: hidden once it has slid shut. */
  .ratios.is-closed .ratio-fold:not(:has(input:checked)) > .ratio-fold-inner {
    visibility: hidden;
    opacity: 0;
    transform: translateY(-6px);
    transition:
      opacity var(--fold-time) var(--fold-ease),
      transform var(--fold-time) var(--fold-ease),
      visibility 0s linear var(--fold-time);
  }

  /* Closed, Workspace is a pile, as iOS stacks notifications (Ahmed, 23 Oct 2026): its first link — Templates — a card
     on top, so it's plain where the templates are; the next two tucked under it, each smaller, the second's name
     showing faintly, the third just an edge. Each row slides from its place in the list to its place in the pile and
     back (the 100%s are its own height), while the list closes to the pile's height: one row and 32px. The rest of
     the pile opens the list (js/sidebar-sections.js).
     Opening and closing are one motion, eased in and out (Ahmed, 24 Oct 2026): the height slides between the pile's
     and the list's (js/sidebar-sections.js sets it in px for the slide), Templates stays where it is while its edge
     fades, and the cards slide out from under it and back, under it all the way. */
  .side-fold > .workspace-list {
    transition: height var(--fold-time) var(--fold-ease);
  }

  .workspace-list > li {
    position: relative;
    transform-origin: bottom center;
    transition: transform var(--fold-time) var(--fold-ease), opacity var(--fold-time) var(--fold-ease);
  }

  .workspace-list > li:nth-child(1 of :not([hidden])) {
    z-index: 3;
  }

  .workspace-list > li:nth-child(2 of :not([hidden])) {
    z-index: 2;
  }

  .workspace-list > li:nth-child(3 of :not([hidden])) {
    z-index: 1;
  }

  .workspace-list .ws-link {
    transition: background-color var(--dur) var(--ease), box-shadow var(--fold-time) var(--fold-ease),
      color var(--fold-time) var(--fold-ease);
  }

  .workspace-list .ws-link > * {
    transition: opacity var(--fold-time) var(--fold-ease);
  }

  .workspace.is-closed .workspace-list {
    height: calc(36px + 32px);
    cursor: pointer;
  }

  .workspace.is-closed .workspace-list:not(:has(> :nth-child(3 of :not([hidden])))) {
    height: calc(36px + 24px);
  }

  /* The pile's cards: light blue, edged in blue, their words and icons in the text colour (Ahmed, 28 Oct 2026). */
  .workspace.is-closed .workspace-list .ws-link {
    background: var(--accent-50);
    box-shadow: inset 0 0 0 1px var(--accent), 0 1px 2px rgb(0 0 0 / 0.05);
    color: var(--text);
  }

  .workspace.is-closed .workspace-list .ws-link:hover {
    background: var(--accent-100);
  }

  .workspace.is-closed .workspace-list > li:nth-child(n + 2 of :not([hidden])) .ws-link {
    pointer-events: none;
  }

  .workspace.is-closed .workspace-list > li:nth-child(2 of :not([hidden])) {
    transform: translateY(calc(24px - 100% - 2px)) scale(0.92);
  }

  .workspace.is-closed .workspace-list > li:nth-child(2 of :not([hidden])) .ws-link > * {
    opacity: 0.45;
  }

  .workspace.is-closed .workspace-list > li:nth-child(3 of :not([hidden])) {
    transform: translateY(calc(32px - 200% - 4px)) scale(0.84);
  }

  .workspace.is-closed .workspace-list > li:nth-child(n + 3 of :not([hidden])) .ws-link > * {
    opacity: 0;
  }

  /* Further down, under the third and out of sight. */
  .workspace.is-closed .workspace-list > li:nth-child(4 of :not([hidden])) {
    opacity: 0;
    transform: translateY(calc(32px - 300% - 6px)) scale(0.84);
  }

  .workspace.is-closed .workspace-list > li:nth-child(5 of :not([hidden])) {
    opacity: 0;
    transform: translateY(calc(32px - 400% - 8px)) scale(0.84);
  }
}

.ratio-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.ratio {
  position: relative;
  display: flex;
  align-items: center;
  min-height: 48px;
  padding-inline-end: 10px;
  border-radius: var(--radius);
  cursor: pointer;
}

.ratio:hover {
  background: var(--accent-50);
}

.ratio:has(input:checked) {
  background: var(--accent);
  color: var(--accent-ink);
}

.ratio:has(input:focus-visible) {
  outline: 2px solid var(--focus);
  outline-offset: 1px;
}

.ratio-glyph {
  display: grid;
  place-items: center;
  flex: none;
  width: 70px; /* fits the widest thumbnail (16:9), 10 px either side */
  height: 48px;
}

/* Shaped like the post itself; app.js sets the size from the aspect ratio. */
.ratio-icon {
  border: 1px solid var(--accent);
  border-radius: 4px;
  background: var(--accent-50);
}

.ratio:has(input:checked) .ratio-icon {
  border-color: var(--accent-ink);
  background: var(--accent);
}

.ratio-size {
  font-size: var(--fs-xs);
  color: #6f6f6f;
}

.ratio:has(input:checked) .ratio-size {
  color: inherit;
}

.ratio-text {
  display: grid;
  gap: 2px;
}

.ratio-name {
  font-weight: 500;
  font-size: var(--fs-base);
}

/* Workspace: the admin's everyday tools, one row each, with a quiet count at the end where there is one. */
.workspace-list {
  display: grid;
  gap: 2px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ws-link {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 36px;
  padding: 0 4px;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--accent);
  font-size: var(--fs-xs);
  font-weight: 500;
  text-align: start;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease);
}

.ws-link:hover {
  background: var(--accent-50);
}

/* The icons, names and counts all in FastGen blue (Ahmed, 28 Oct 2026). */
.ws-icon {
  flex: none;
  width: 20px;
  height: 20px;
  color: inherit;
}

.ws-icon svg {
  display: block;
  width: 100%;
  height: 100%;
}

.ws-label {
  flex: 1;
  min-width: 0;
}

/* How many there are, at the end of the row in the same words and colour as its name (Ahmed, 28 Oct 2026: no box). */
.ws-count {
  font-size: var(--fs-xs);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

.ws-count:empty {
  display: none;
}

/* ---------- Layers (js/layers-panel.js) ----------
   Main Content's layers and groups, as Figma lists them: a row each, what's in a group indented 16px a level under it.
   On desktop it takes what's left of the sidebar and scrolls on its own; on tablets it floats beside the rail. */

.layers {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 160px;
}

.layers-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

/* The ×: the floating panel's only (tablets). */
.layers .layers-close {
  display: none;
}

.layers-note {
  padding: 0 var(--sp-2) var(--sp-2);
  font-size: var(--fs-sm);
  color: var(--text-3);
}

.layers-tree {
  position: relative;
  display: grid;
  flex: 1 1 auto;
  align-content: start;
  gap: 1px;
  min-height: 0;
  padding-bottom: 24px; /* room to drop after the last row */
  overflow-y: auto;
}

/* With a mouse, the sideways bar is always there (Ahmed, 6 Nov 2026): deep groups and long names push the eyes past the
   edge, and a Windows mouse can't scroll sideways. Drawn by hand so macOS doesn't hide it while still. */
@media (pointer: fine) {
  .layers-tree {
    overflow-x: scroll;
  }

  .layers-tree::-webkit-scrollbar {
    width: 8px;
    height: 8px;
  }

  .layers-tree::-webkit-scrollbar-track {
    border-radius: 4px;
    background: var(--surface-2);
  }

  .layers-tree::-webkit-scrollbar-thumb {
    border-radius: 4px;
    background: color-mix(in srgb, var(--text-3) 45%, transparent);
  }

  .layers-tree::-webkit-scrollbar-thumb:hover {
    background: var(--text-3);
  }

  .layers-tree::-webkit-scrollbar-corner {
    background: transparent;
  }

  @supports not selector(::-webkit-scrollbar) {
    .layers-tree {
      scrollbar-width: thin;
      scrollbar-color: color-mix(in srgb, var(--text-3) 45%, transparent) var(--surface-2);
    }
  }
}

.layer-row {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding-inline: calc(8px + var(--depth, 0) * 16px) 8px;
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: var(--fs-base);
  user-select: none;
  -webkit-user-select: none;
  transition: background-color var(--dur) var(--ease);
}

.layer-row:hover {
  background: var(--surface-2);
}

.layer-row.is-chosen {
  background: var(--accent);
  color: var(--accent-ink);
}

.layer-row:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: -2px;
}

.layer-fold,
.layer-icon {
  display: grid;
  flex: none;
  place-items: center;
  width: 16px;
  height: 16px;
  color: var(--text-2);
}

.layer-row.is-chosen :is(.layer-fold, .layer-icon, .layer-grip) {
  color: inherit;
}

.layer-row.is-group .layer-fold {
  cursor: pointer;
}

.layer-fold svg {
  transition: transform var(--dur) var(--ease);
}

.layer-row[aria-expanded='true'] .layer-fold svg {
  transform: rotate(90deg);
}

:root[dir='rtl'] .layer-row[aria-expanded='false'] .layer-fold svg {
  transform: scaleX(-1);
}

.layer-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.layer-row.is-group .layer-name {
  font-weight: 600;
}

/* A grip to drag by, on touch screens only: a swipe anywhere else on the row scrolls the list. */
.layer-grip {
  display: none;
  flex: none;
  margin-inline-end: -4px;
  padding: 8px 4px;
  color: var(--text-3);
  touch-action: none;
}

@media (pointer: coarse) {
  .layer-grip {
    display: grid;
  }
}

/* The eye at the end of every row: the layer on or off (Ahmed, 31 Oct 2026). Its picture lines up with the row's end. */
.layer-eye {
  display: grid;
  flex: none;
  place-items: center;
  width: 24px;
  height: 24px;
  margin-inline-end: -4px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-xs);
  background: none;
  color: var(--text-2);
  cursor: pointer;
  transition: background-color var(--dur) var(--ease);
}

.layer-eye:hover:not(:disabled) {
  background: var(--accent-hover);
  color: var(--text);
}

.layer-eye:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: -2px;
}

/* Grayed where this person can't change it. */
.layer-eye:disabled {
  opacity: 0.45;
  cursor: default;
}

.layer-row.is-chosen .layer-eye {
  color: inherit;
}

.layer-row.is-chosen .layer-eye:hover:not(:disabled) {
  background: rgb(255 255 255 / 0.2);
  color: inherit;
}

@media (pointer: coarse) {
  .layer-eye {
    width: 32px;
    height: 32px;
    margin-inline-end: 0;
  }
}

/* The lock beside the eye (Ahmed, 10 Nov 2026): an open lock faded gray while the layer is free; locked, a closed lock in
   dark red on light red, in a chosen row too. Only admins can press it; for editors it shows what an admin set. */
.layer-lock {
  display: grid;
  flex: none;
  place-items: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-xs);
  background: none;
  color: var(--text-2);
  opacity: 0.45;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease), opacity var(--dur) var(--ease);
}

.layer-lock:hover:not(:disabled) {
  background: var(--accent-hover);
  opacity: 1;
}

.layer-lock:disabled {
  cursor: default;
}

.layer-row.is-chosen .layer-lock {
  color: inherit;
}

.layer-row.is-chosen .layer-lock:hover:not(:disabled) {
  background: rgb(255 255 255 / 0.2);
}

.layer-lock.is-closed,
.layer-row.is-chosen .layer-lock.is-closed,
.layer-lock.is-closed:hover:not(:disabled) {
  background: var(--danger-bg);
  color: var(--danger);
  opacity: 1;
}

@media (pointer: coarse) {
  .layer-lock {
    width: 32px;
    height: 32px;
  }
}

/* A layer that's off, faded in the list; one off because of its group has its own eye faded too. */
.layer-row.is-off-layer :is(.layer-name, .layer-icon, .layer-fold) {
  opacity: 0.45;
}

.layer-row.is-off-layer .layer-eye:not(.is-shut, :disabled) {
  opacity: 0.45;
}

.layer-rename {
  flex: 1;
  min-width: 0;
  height: 24px;
  padding: 0 6px;
  border: 1px solid var(--focus);
  border-radius: var(--radius-xs);
  background: var(--surface);
  color: var(--text);
  font: inherit;
}

/* On the other tabs: there to see, not to use. */
.layers-tree.is-off .layer-row {
  opacity: 0.55;
  pointer-events: none;
}

.layers-tree.is-off .layer-row.is-chosen {
  background: var(--surface-2);
  color: var(--text);
}

.layer-row.is-dragged {
  opacity: 0.4;
}

.layer-row.drop-into {
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  box-shadow: inset 0 0 0 2px var(--accent);
}

/* Where a dragged layer will land: a line between rows, as deep as the level it lands at. */
.layer-drop-line {
  position: absolute;
  inset-inline: calc(30px + var(--depth, 0) * 16px) 8px;
  height: 2px;
  border-radius: 1px;
  background: var(--accent);
  pointer-events: none;
}

.layer-drop-line::before {
  content: '';
  position: absolute;
  top: -3px;
  inset-inline-start: -7px;
  width: 8px;
  height: 8px;
  border: 2px solid var(--accent);
  border-radius: 50%;
  corner-shape: round;
  background: var(--surface);
  box-sizing: border-box;
}

/* What's being dragged, under the pointer: its name, or how many. */
.layer-ghost {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1000;
  padding: 4px 10px;
  border-radius: var(--radius-xs);
  background: var(--text);
  color: var(--surface);
  font-size: var(--fs-sm);
  white-space: nowrap;
  box-shadow: 0 4px 12px rgb(0 0 0 / 0.2);
  pointer-events: none;
}

/* The rail's Layers button: tablets only. */
.sidebar .layers-rail-btn {
  display: none;
}

@media (min-width: 721px) and (max-width: 1024px) {
  .sidebar .layers-rail-btn {
    display: flex;
  }

  .layers-rail-btn[aria-expanded='true'] {
    background: var(--accent);
    color: var(--accent-ink);
  }

  .layers-rail-btn[aria-expanded='true'] .ws-icon {
    color: inherit;
  }

  /* Layers floats beside the rail, over the options, until it's closed. */
  .layers {
    display: none;
  }

  .layers.is-floating {
    display: flex;
    position: fixed;
    z-index: 40;
    top: 16px;
    inset-inline-start: 72px;
    width: 280px;
    max-height: calc(100dvh - 32px);
    min-height: 0;
    padding: 12px 8px 4px;
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface);
    box-shadow: 0 12px 32px rgb(0 0 0 / 0.14);
  }

  .layers-head .side-title {
    display: block;
  }

  .layers .layers-close {
    display: grid;
  }
}

/* Phones have no option tabs, so no Layers. */
@media (max-width: 720px) {
  .layers {
    display: none;
  }
}

/* The same items in the profile menu are for phones only, where the sidebar has no room for Workspace. */
@media (min-width: 721px) {
  .menu-item.ws-phone {
    display: none;
  }
}

.sidebar-foot {
  margin-top: auto;
  display: grid;
  gap: 10px;
}

/* The trial and the month's AI edits (app.js showAiEdits): a light-blue bar, the count in blue at its end. */
.sidebar .trial-badge {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  min-height: 32px;
  padding: 0 10px;
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--accent-50);
  color: var(--text);
  font-size: var(--fs-xs);
  text-decoration: none;
}

.sidebar .trial-badge:hover {
  background: var(--accent-100);
}

.sidebar .trial-badge-title {
  font-weight: 500;
  color: var(--text);
}

.sidebar .trial-badge-days {
  color: var(--accent);
}

.editor-legal {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 0;
  font-size: 11px;
}

.editor-legal a {
  color: var(--text-3);
  text-decoration: none;
}

.editor-legal a:hover {
  color: var(--text);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.profile-wrap {
  position: relative;
}

.profile {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 52px;
  padding: 0 10px 0 8px;
  border: 0;
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--accent-100);
  cursor: pointer;
  text-align: start;
}

.profile:hover {
  background: var(--accent-50);
}

.profile-chevron {
  flex: none;
  margin-inline-start: auto;
  color: var(--text-3);
  transition: transform var(--dur) var(--ease);
}

.profile[aria-expanded='true'] .profile-chevron {
  transform: rotate(180deg);
}

.avatar {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  flex: none;
  border-radius: 50%;
  corner-shape: round;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: var(--fs-sm);
  font-weight: 600;
}

.profile-text {
  display: grid;
  min-width: 0;
}

/* One line each; a name too long for the sidebar ends in "…" rather than wrapping. */
.profile-name,
.profile-org {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.profile-name {
  font-weight: 500;
  font-size: var(--fs-md);
}

.profile-org {
  font-size: 11px;
  color: var(--text-2);
}

/* The Add a layer menu (the redesign's): light blue, each item a blue-lettered pill, the one under the pointer filled. */
.action-menu.add-menu {
  display: grid;
  gap: 10px;
  padding: 10px;
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--accent-50);
}

:is(#profile-menu, .action-menu.add-menu) .menu-item {
  padding: 14px 10px;
  border-radius: var(--radius);
  background: var(--accent-100);
  color: var(--accent);
  font-size: var(--fs-xs);
  font-weight: 500;
  text-decoration: none;
}

.action-menu.add-menu .menu-item:is(:hover, :focus-visible):not(:disabled) {
  background: var(--accent);
  color: var(--accent-ink);
}

/* The profile menu (Ahmed, 28 Oct 2026): white, edged in blue like the option boxes, its items white and touching,
   light blue under the pointer. */
#profile-menu {
  display: grid;
  gap: 0;
  padding: 10px;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--surface);
}

#profile-menu .menu-item {
  background: var(--surface);
}

#profile-menu .menu-item:is(:hover, :focus-visible):not(:disabled) {
  background: var(--accent-50);
  color: var(--accent);
}

#profile-menu .menu-sep {
  margin: 0;
  border-top: 1px solid var(--accent-200);
}

/* It opens and closes as Workspace's fold does, on the same curve and time (400ms): unrolling from the profile button,
   its items sliding up with it (app.js setMenu adds .is-open, and hides it once it has closed). Where it drops from
   the top instead (phones), it unrolls downward. The clip leaves room round it for its shadow. */
#profile-menu {
  --menu-shut: inset(100% -40px -40px -40px);
  --menu-slide: 10px;
  clip-path: var(--menu-shut);
  opacity: 0;
  /* Closing, it fades only as it finishes rolling up, so the roll shows. */
  transition: clip-path 400ms cubic-bezier(0.4, 0, 0.2, 1), opacity 200ms cubic-bezier(0.4, 0, 0.2, 1) 200ms;
}

#profile-menu.is-open {
  clip-path: inset(-40px);
  opacity: 1;
  transition: clip-path 400ms cubic-bezier(0.4, 0, 0.2, 1), opacity 250ms cubic-bezier(0.4, 0, 0.2, 1);
}

#profile-menu:not(.is-open) {
  pointer-events: none;
}

#profile-menu > * {
  transform: translateY(var(--menu-slide));
  transition: transform 400ms cubic-bezier(0.4, 0, 0.2, 1), background-color var(--dur) var(--ease);
}

#profile-menu.is-open > * {
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  #profile-menu,
  #profile-menu > * {
    transition: none;
  }
}

.menu {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(100% + 6px);
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg); /* the items' 8 px corners, 6 px in */
  box-shadow: 0 12px 32px rgb(0 0 0 / 0.12);
  z-index: 20;
}

.menu-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  padding: 9px 10px;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  font-size: var(--fs-md);
  text-align: start;
  cursor: pointer;
}

.menu-item:hover:not(:disabled) {
  background: var(--surface-2);
}

.menu-item:disabled {
  color: var(--text-3);
  cursor: default;
}

/* ---------- editor ---------- */

/* White, with a blue line under it: the name bar, then the tabs, 20px above the line. */
.editor-top {
  display: grid;
  gap: 10px;
  padding: 10px 10px 20px;
  background: var(--surface);
  border-bottom: 2px solid var(--accent);
}

/* The name at the start, Unsaved changes in the middle, Undo and Redo at the end, on one light-blue bar. */
.editor-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: 10px;
  padding-inline-start: 10px;
  border-radius: var(--radius);
  background: var(--accent-50);
}

/* Always the bar's last column, so Undo and Redo stay at the end once Unsaved changes goes (Ahmed, 28 Oct 2026). */
.history {
  display: flex;
  grid-column: -2;
  justify-self: end;
  gap: 10px;
  padding: 10px;
}

.history-btn {
  width: 24px;
  height: 24px;
  border-radius: 4px;
  background: var(--accent-100);
  color: var(--accent);
}

.history-btn:hover:not(:disabled) {
  background: var(--accent-200);
  color: var(--accent);
}

/* Nothing to undo or redo: the lightest blue, its arrow faint (Ahmed: no grey buttons). */
.history-btn:disabled {
  cursor: default;
  background: var(--accent-50);
  color: var(--accent-300);
}

.history-btn [data-icon],
.history-btn [data-icon] svg {
  display: block;
  width: 16px;
  height: 16px;
}

.name-wrap {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.save-state {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
  padding: 2px 10px;
  background: var(--warn-bg);
  color: var(--warn-ink);
  font-size: var(--fs-xs);
  font-weight: 500;
  white-space: nowrap;
  animation: pop-in 0.2s var(--ease);
}

.save-state::before {
  content: '';
  width: 7px;
  height: 7px;
  border-radius: 50%;
  corner-shape: round;
  background: var(--warn-dot);
}

@keyframes pop-in {
  from {
    opacity: 0;
    transform: scale(0.9);
  }
}

/* The name's box 10px in from the bar's edge, as far as from its top and bottom; its pencil blue (Ahmed, 30 Oct 2026). */
.name-field {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-width: 0;
  color: var(--accent);
}

.name-edit {
  position: absolute;
  inset-inline-end: 10px;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}

.name-field:hover .name-edit {
  opacity: 1;
}

.name-field:focus-within .name-edit {
  opacity: 0;
}

.save-state[hidden] {
  display: none;
}

.template-name {
  flex: 0 1 auto;
  field-sizing: content;
  min-width: 120px;
  max-width: 100%;
  padding: 6px 10px;
  padding-inline: 10px 32px; /* room for the pencil at the end */
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text);
  font-size: var(--fs-xs);
  font-weight: 500;
  text-overflow: ellipsis;
  cursor: text;
  transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.template-name:hover {
  background: var(--accent-100);
}

/* Editors can't rename templates, so for them the name is plain text: no hover, pencil, or cursor. */
.template-name:read-only {
  pointer-events: none;
  cursor: default;
}

.name-field:has(.template-name:read-only) .name-edit {
  display: none;
}

.template-name:focus {
  outline: none;
  padding-inline-end: 10px;
  border-color: var(--focus);
  background: var(--surface);
}

.tabs {
  display: flex;
  gap: 10px;
  padding: 2px; /* room for focus rings, since the row can scroll */
  overflow-x: auto;
  scrollbar-width: none;
}

/* Where the row has more tabs than room, it fades out at the edge they're hidden past (app.js sets the classes). */
.tabs {
  --tabs-end: right;
}

:root[dir='rtl'] .tabs {
  --tabs-end: left;
}

.tabs.fade-end {
  -webkit-mask-image: linear-gradient(to var(--tabs-end), #000 calc(100% - 48px), transparent);
  mask-image: linear-gradient(to var(--tabs-end), #000 calc(100% - 48px), transparent);
}

.tabs.fade-start {
  -webkit-mask-image: linear-gradient(to var(--tabs-end), transparent, #000 48px);
  mask-image: linear-gradient(to var(--tabs-end), transparent, #000 48px);
}

.tabs.fade-start.fade-end {
  -webkit-mask-image: linear-gradient(to var(--tabs-end), transparent, #000 48px, #000 calc(100% - 48px), transparent);
  mask-image: linear-gradient(to var(--tabs-end), transparent, #000 48px, #000 calc(100% - 48px), transparent);
}

/* The five tabs share the row equally (the redesign): light blue with blue words, the open one filled blue. */
.tab {
  position: relative;
  display: inline-flex;
  justify-content: center;
  align-items: center;
  gap: 10px;
  flex: 1 0 auto;
  min-height: 44px;
  padding: 10px 20px;
  border: 0;
  border-radius: var(--radius);
  background: var(--accent-50);
  font-weight: 500;
  font-size: var(--fs-lg);
  color: var(--accent);
  cursor: pointer;
  white-space: nowrap;
}

.tab:hover:not([aria-selected='true']) {
  background: var(--accent-100);
}

/* A tab whose every option is locked for this editor: there to look at, its options grayed (tabs.js). */
.tab.is-locked:not([aria-selected='true']) {
  color: color-mix(in srgb, var(--accent) 45%, var(--accent-50));
}

.tab-icon {
  display: inline-flex;
  flex: none;
}

.tab-label {
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Tabs that already hold content get a small green dot. */
.tab-dot {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  corner-shape: round;
  background: var(--success);
  box-shadow: 0 0 0 2px var(--surface);
  opacity: 0;
  transform: scale(0.4);
  transition: opacity 0.2s var(--ease), transform 0.2s var(--ease);
}

.tab.has-content .tab-dot {
  opacity: 1;
  transform: none;
}

.tab[aria-selected='true'] {
  background: var(--accent);
  color: var(--accent-ink);
}

.tab[aria-selected='true'] .tab-dot {
  box-shadow: 0 0 0 2px var(--accent);
}

/* Narrower, the tabs give up room before they'd scroll: smaller words, then no icons. */
@container editor (max-width: 900px) {
  .tab {
    gap: 6px;
    padding-inline: 12px;
    font-size: var(--fs-base);
  }
}

@container editor (max-width: 680px) {
  .tab-icon {
    display: none;
  }

  .tab {
    padding-inline: 10px;
  }
}

/* Positioned, so the hidden helpers placed absolutely inside it (labels kept for screen readers, radio buttons) stay
   in the panel and scroll with it; otherwise they sit against the page, and a long tab makes the whole page scroll. */
.panel {
  position: relative;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 10px 10px 40px;
}

.panel-enter > * {
  animation: panel-enter 0.2s var(--ease);
}

@keyframes panel-enter {
  from {
    opacity: 0;
    transform: translateY(4px);
  }
}

.stack {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}

/* ---- the option cards (the redesign, 28 Oct 2026; controls.js card, cardRow, stack) ----
   Each card white, edged in FastGen blue; cards side by side share the row's width and height equally (Ahmed: "the
   same hight for the boxes next to each other"), and a column of several shares its height between them. */

.group {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
  padding: 14px;
  border-radius: var(--radius-card);
  background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--accent);
  container-type: inline-size;
}

.card-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  min-height: 20px;
}

.group-title {
  font-size: var(--fs-md);
  font-weight: 600;
}

/* What sits at the end of a card's head: what it's set to, its switch, one value for all four sides. */
.card-aside {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  font-size: var(--fs-xs);
  color: var(--text-2);
}

.card-body,
.card-rows {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
}

.card-rows {
  gap: 10px;
}

.card-row {
  display: grid;
  grid-template-columns: repeat(var(--cols, 2), minmax(0, 1fr));
  align-items: stretch;
  gap: 10px;
}

.card-stack {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}

.card-stack > * {
  flex: 1 1 auto;
}

/* A light-blue card of cards: the Placement of all Layers, a margin's Placement. */
.group.is-panel {
  background: #f2f6ff;
}

/* The top card of Main Content and Margin Text: what to add, the open one with its bin and Duplicate, its words. */
.group.top-card {
  gap: 10px;
  padding: 10px;
}

/* Narrow, cards stack one per row. */
@container editor (max-width: 620px) {
  .card-row {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* What's above a card's Custom gradient, as its body spaces things (Dark overlay and Mask, tabs.js). */
.card-part {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
}

/* Dark overlay and Mask side by side: their Custom gradients start level (Ahmed, 28 Oct 2026). The row's cards share
   three row lines (the title, what's above Custom gradient, Custom gradient) through subgrid; a card that isn't one of
   them (Blurred fill) spans all three, any height it has beyond them going to the last. Stacked, each is its own. */
@container editor (min-width: 621px) {
  .card-row:has(> .gradient-card) {
    grid-template-rows: auto auto 1fr;
    row-gap: 12px;
  }

  .card-row:has(> .gradient-card) > * {
    grid-row: 1 / -1;
  }

  /* A container's layout containment turns subgrid off, so here the card's two parts are the containers instead: as
     wide as the card's inside, its width rules come out the same. */
  .card-row > .gradient-card {
    display: grid;
    grid-template-rows: subgrid;
    container-type: normal;
  }

  .card-row > .gradient-card > .card-body {
    display: grid;
    grid-row: 2 / -1;
    grid-template-rows: subgrid;
  }

  .card-row > .gradient-card > .card-body > * {
    container-type: inline-size;
  }
}

/* The brush, Extend and Upscale side by side: each part on one level in all three, however long the words above it (Ahmed,
   31 Oct 2026). The cards share a row line per part through subgrid, as the gradient cards do: the title, the words, the
   model menu, the button, what it says while it works, the AI edits left. A part a card doesn't have leaves its row
   empty; the space above a part is its own, so an empty row takes no room. Stacked, each is its own. */
@container editor (min-width: 621px) {
  .card-row:has(> .ai-card) {
    grid-template-rows: repeat(6, auto);
    row-gap: 0;
  }

  .card-row > .ai-card {
    display: grid;
    grid-row: 1 / -1;
    grid-template-rows: subgrid;
    row-gap: 0;
    container-type: normal;
  }

  .card-row > .ai-card > .card-body {
    display: grid;
    grid-row: 2 / -1;
    grid-template-rows: subgrid;
    align-items: start;
    row-gap: 0;
  }

  .card-row > .ai-card > .card-body > * {
    margin-top: 12px;
  }

  .card-row > .ai-card > .card-body > .hint:first-child {
    grid-row: 1;
  }

  .card-row > .ai-card > .card-body > .field.select {
    grid-row: 2;
  }

  .card-row > .ai-card > .card-body > :is(.ai-run, .extend-row) {
    grid-row: 3;
  }

  .card-row > .ai-card > .card-body > .ai-waiting {
    grid-row: 4;
  }

  .card-row > .ai-card > .card-body > .ai-left {
    grid-row: 5;
  }
}

/* ---- grayed: switched off or not in use, and locked for this editor (controls.js inactive) ----
   Still showing what it's set to. A locked one has a lock at its corner, in full strength over the grayed option. */

.is-off,
.is-disabled {
  opacity: 0.45;
}

/* Grayed inside grayed stays as gray as the outer one, not twice as faint. */
:is(.is-off, .is-locked) :is(.is-off, .is-locked) {
  opacity: 1;
}

:is(.is-off, .is-locked) .is-locked > * {
  opacity: 1;
}

:is(.is-off, .is-locked) .is-locked::before {
  display: none;
}

.is-locked {
  position: relative;
}

.is-locked > * {
  opacity: 0.45;
}

.is-locked::before {
  content: '';
  position: absolute;
  z-index: 1;
  top: -6px;
  inset-inline-end: -6px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  corner-shape: round;
  background: var(--surface) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Crect x='5' y='9' width='10' height='7.5' rx='1.6' fill='%235c5c58'/%3E%3Cpath d='M7.2 9V7a2.8 2.8 0 0 1 5.6 0v2' fill='none' stroke='%235c5c58' stroke-width='1.6'/%3E%3C/svg%3E") center / 14px no-repeat;
  box-shadow: 0 0 0 1px var(--accent-200);
}

.group.is-locked::before {
  top: 8px;
  inset-inline-end: 8px;
}

/* ---- the parts of cards ---- */

/* Active on the Fill or Image a background uses, Set active on the other (panels.js activeChip). */
.card-title-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.chip {
  display: inline-flex;
  align-items: center;
  padding: 2px 6px;
  border: 0;
  border-radius: 4px;
  background: none;
  color: var(--text);
  font: inherit;
  font-size: var(--fs-xs);
  font-weight: 500;
}

.chip.is-on {
  gap: 4px;
  padding-inline-start: 3px;
  background: var(--accent);
  color: var(--accent-ink);
}

/* Before Active: a white check in a white ring, as on the logo cards' Active (Ahmed, 30 Oct 2026). */
.check-ring {
  position: relative;
  flex: none;
  width: 16px;
  height: 16px;
  border: 1.5px solid currentColor;
  border-radius: 50%;
  corner-shape: round;
}

.check-ring::after {
  content: '';
  position: absolute;
  left: 4.5px;
  top: 1.5px;
  width: 4px;
  height: 8px;
  border: solid currentColor;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

button.chip {
  box-shadow: inset 0 0 0 1px var(--accent-200);
  cursor: pointer;
}

button.chip:hover {
  background: var(--accent-50);
}

.card-switch {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.sub-title {
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--text);
}

/* Add a layer (with its menu) beside Add group; Add a margin across the card. */
.add-row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}

.add-btn {
  gap: 10px;
  width: 100%;
  min-height: 38px;
}

.add-btn .add-btn-label {
  flex: 1;
  text-align: start;
}

.add-btn .menu-chevron {
  margin-inline: 8px 4px;
}

/* The open layer's (or margin's) name, its bin and Duplicate. */
.item-bar {
  display: flex;
  align-items: stretch;
  gap: 10px;
}

.item-name {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1;
  min-width: 0;
  min-height: 38px;
  padding: 0 12px;
  border-radius: var(--radius-sm);
  background: var(--accent-50);
  font-size: var(--fs-xs);
  font-weight: 500;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.item-bar .menu-field,
.item-bar .item-menu {
  flex: 1;
  min-width: 0;
}

.item-menu.menu-button {
  justify-content: center;
  min-height: 38px;
  color: var(--text);
  font-weight: 500;
}

.duplicate-btn {
  flex: none;
  width: 112px;
  min-height: 38px;
  padding: 0 10px;
  border-color: transparent;
}

.item-bar .btn-danger.is-icon {
  min-height: 38px;
}

/* Main logo or Secondary logo, the two buttons across the top of the Logo tab. */
.logo-picks {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}

.logo-pick {
  min-height: 40px;
  padding: 0 4px;
  border: 0;
  border-radius: var(--radius-xs);
  background: var(--accent-50);
  box-shadow: inset 0 0 0 1px var(--accent);
  color: var(--accent);
  font: inherit;
  font-size: var(--fs-xs);
  font-weight: 500;
  cursor: pointer;
}

.logo-pick:hover:not([aria-selected='true']) {
  background: var(--accent-100);
}

.logo-pick[aria-selected='true'] {
  background: var(--accent);
  box-shadow: none;
  color: var(--accent-ink);
}

/* The logos and their size on the left, where the logo sits on the right. */
.logo-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.77fr) minmax(0, 1fr);
  align-items: start;
  gap: 10px;
}

@container editor (max-width: 620px) {
  .logo-layout {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Graphic layers: Add Layer, the layer menu, the layer's Show switch and its Delete, on one bar. */
.graphic-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
}

.graphic-bar .bar-gap {
  flex: 1;
}

.graphic-bar .menu-button {
  width: auto;
  min-width: 140px;
}

.graphic-bar .toggle {
  min-height: 32px;
  padding-block: 4px;
}

.graphic-bar .btn {
  min-height: 32px;
}

/* A layer's image (or colour) beside its Flip, Zoom and place. */
.graphic-art {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}

/* An icon text's Title and Body side by side. */
.title-body {
  align-items: start;
}

/* Under a slider, a link that puts it back: there to see, grayed while there's nothing to put back. */
/* A card's links (Reset to 100%, + Add color): small and blue. pages.css, loaded after, gives link buttons the text
   size round them, so the panel's say their own. */
.panel .link-btn {
  font-size: var(--fs-xs);
  font-weight: 500;
  color: var(--accent);
}

.zoom-reset.is-off {
  pointer-events: none;
}

.ai-run {
  width: 100%;
}

/* A text's colour, its type and its gradient, one under the other. */
.stack-fields {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* A card's "Custom gradient" heading, with whether it's a preset's beside it. */
.custom-gradient .summary-live {
  font-size: var(--fs-xs);
  color: var(--text-3);
}

.row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 14px;
  align-items: flex-end;
}

.row > * {
  flex: 1 1 160px;
}

/* Rows sit on the same line at the bottom, so a number box beside a labelled field (Size beside Weight) keeps its
   own height instead of stretching around its input. */
.grid-2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  align-items: end;
}

.corner-grid {
  display: grid;
  gap: 8px;
}

/* Each corner's size with its own shape menu under it (the Corners card). */
.corner-part {
  display: grid;
  gap: 4px;
  min-width: 0;
}

/* One value for all four, in a card's head or a field's (Padding, Margins, Corners, a stroke's Width). */
.all-four {
  min-height: 28px;
}

/* A corner moved both ways (controls.js pointField): its name, then a box for left / right and one for up / down, each
   with its arrow, narrow enough for the two to sit beside the name in a column. */
.point-field .point-label {
  font-size: var(--fs-xs);
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.point-field .number-input input {
  width: 36px;
}

.point-arrow {
  display: flex;
  color: var(--text-3);
}

.hint {
  font-size: var(--fs-xs);
  color: var(--text-3);
}

/* What a card or a link is set to, in a line that never wraps (adjust.js's "2 changed", a stroke's "2px · #FFFFFF"). */
.summary-live {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* ---------- fields ---------- */

.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.field-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
}

/* What sits at the row's end: the number, and anything beside it (Fill color's colour box). */
.field-head-end {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* A Box size's own Hug (17 Oct 2026): a small switch in the slider's head, before its value. While it's on the slider
   rests, greyed out, and the switch stays in reach. */
.hug-switch {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-xs);
  color: var(--text-2);
  cursor: pointer;
}

.hug-switch .switch:disabled {
  cursor: default;
  opacity: 0.45;
}

.slider.is-resting input[type='range'],
.slider.is-resting .number-input {
  opacity: 0.45;
}

.field label,
.field-label {
  font-size: var(--fs-xs);
  font-weight: 500;
  color: var(--text);
}

/* The track, then the value in its box (the redesign); a Hug switch or a colour box before the track. */
.slider-row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.slider-row input[type='range'] {
  flex: 1;
  min-width: 0;
}

.slider-value {
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
}

/* A thin light-blue track filled in blue up to the handle (--at, set in slider()). */
input[type='range'] {
  appearance: none;
  width: 100%;
  height: 16px;
  margin: 0;
  background: transparent;
  cursor: pointer;
  --track: linear-gradient(to right, var(--accent) var(--at, 50%), var(--accent-50) var(--at, 50%));
}

input[type='range']::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 2px;
  background: var(--track);
}

input[type='range']::-moz-range-track {
  height: 4px;
  border-radius: 2px;
  background: var(--track);
}

input[type='range']::-webkit-slider-thumb {
  appearance: none;
  width: 14px;
  height: 14px;
  margin-top: -5px;
  border: 2px solid var(--surface);
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), 0 1px 3px rgb(0 0 0 / 0.2);
}

input[type='range']::-moz-range-thumb {
  width: 14px;
  height: 14px;
  border: 2px solid var(--surface);
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), 0 1px 3px rgb(0 0 0 / 0.2);
}

input[type='range']:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: 8px;
}

input[type='range']:disabled {
  cursor: default;
}

/* A slider that goes both ways from 0: filled from the middle to the handle instead of from the left edge. */
input[type='range'].range-centered {
  --track: linear-gradient(to right, var(--accent-50) min(50%, var(--at, 50%)), var(--accent) min(50%, var(--at, 50%)),
    var(--accent) max(50%, var(--at, 50%)), var(--accent-50) max(50%, var(--at, 50%)));
}

/* A number with its name (Top, Left / Right…): the name and the value's box on one blue bar (the redesign). 8px before
   the name and 2px after the value, whichever way the page reads. */
.field.number {
  flex-direction: row;
  align-items: center;
  gap: 8px;
  padding-block: 2px;
  padding-inline: 8px 2px;
  background: var(--accent-200);
  border-radius: var(--radius-sm);
}

/* A side's icon before its name (Margins, Padding, a stroke's Width; controls.js numberField `glyph`): a light-blue
   square with that side drawn thick in FastGen blue (Ahmed, 30 Oct 2026). */
.field.number label:has(.glyph) {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
}

/* A name too long for a narrow card ends in "…" after its icon. */
.label-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Four sides' values are a few digits: a narrower box leaves their names the room their icons take. */
.four-values .number-input input {
  width: 32px;
}

.glyph {
  position: relative;
  flex: none;
  width: 13px;
  height: 13px;
  border: 1.5px solid var(--accent-300);
  border-radius: 2px;
}

.glyph::before {
  content: '';
  position: absolute;
  background: var(--accent);
}

.glyph-top::before { top: -1.5px; left: -1.5px; right: -1.5px; height: 3px; }
.glyph-bottom::before { bottom: -1.5px; left: -1.5px; right: -1.5px; height: 3px; }
.glyph-left::before { left: -1.5px; top: -1.5px; bottom: -1.5px; width: 3px; }
.glyph-right::before { right: -1.5px; top: -1.5px; bottom: -1.5px; width: 3px; }

.field.number.layer-order {
  background: var(--order-bg);
}

/* Its name above its box, in a row of them (the Font card's Size, Line height…). */
.field.number.stacked {
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  padding: 0;
  background: none;
}

.field.number.stacked label {
  flex: none;
}

.field.number label {
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The value's light-blue box, its unit after it. */
.number-input {
  display: flex;
  align-items: center;
  gap: 2px;
  min-height: 30px;
  padding: 0 8px;
  background: var(--accent-50);
  border-radius: var(--radius-xs);
  color: var(--text-2);
  font-size: var(--fs-xs);
}

/* A switch in that same box (Auto spacing): as tall as a value beside it. */
.switch-box {
  display: flex;
  align-items: center;
  min-height: 30px;
  padding: 0 8px;
  background: var(--accent-50);
  border-radius: var(--radius-xs);
}

/* Space between beside Auto spacing, one under the other where a card is too narrow to show their names whole. */
.row.spacing-row {
  gap: 8px;
}

.row.spacing-row > * {
  flex-basis: 190px;
}

.number-input.compact {
  flex: none;
  padding: 0 8px;
}

.number-input.compact input {
  width: 40px;
}

.number-input input {
  width: 44px;
  padding: 4px 0;
  border: 0;
  background: transparent;
  color: var(--text);
  font-size: var(--fs-xs);
  text-align: right;
  font-variant-numeric: tabular-nums;
  -moz-appearance: textfield;
}

.field.number.stacked .number-input input {
  width: 100%;
  min-width: 0;
  text-align: start;
}

.number-input input::-webkit-inner-spin-button {
  display: none;
}

.number-input input:focus {
  outline: none;
}

.number-input:focus-within {
  outline: 2px solid var(--focus);
  outline-offset: 1px;
}

.unit {
  font-size: var(--fs-xs);
  color: var(--text-3);
}

/* Choices side by side, each its own light-blue button with blue words; the chosen one filled blue (the redesign). */
.segmented {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
  gap: 4px;
}

.group .segmented:has(.seg:nth-child(6)) {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.seg {
  position: relative;
  cursor: pointer;
}

.seg input {
  position: absolute;
  opacity: 0;
  inset: 0;
  cursor: pointer;
}

.seg span {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 30px;
  padding: 4px 6px;
  border-radius: var(--radius-xs);
  background: var(--accent-50);
  font-size: var(--fs-xs);
  color: var(--accent);
  text-align: center;
  white-space: nowrap;
}

.seg:hover span {
  background: var(--accent-100);
}

.seg input:checked + span {
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 500;
}

.seg input:focus-visible + span {
  outline: 2px solid var(--focus);
}

/* Position: three to a row. */
.segmented.tiles {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

/* A place something else holds (another margin text): shown, but not to be chosen. */
.seg:has(input:disabled),
.seg input:disabled {
  cursor: not-allowed;
}

.seg input:disabled + span,
.seg:hover input:disabled + span {
  background: var(--accent-50);
  color: var(--text-3);
  opacity: 0.55;
}

.segmented.compact {
  display: inline-grid;
  grid-auto-flow: column;
  grid-template-columns: none;
}

/* Alignment and direction share the row equally, each filling its half. */
.row.align-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
  align-items: end;
}

.align-row .segmented.compact {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
}

.align-row .seg span {
  height: 32px;
}

/* Text alignment: the three alignments, and the three justified ones in a row under them. */
.align-row .segmented.compact.text-align {
  grid-auto-flow: row;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

/* Two rows tall; the direction beside it fills the same height (below). */
.row.align-row:has(.text-align) {
  align-items: stretch;
}

/* Text direction: RTL and LTR in one light-blue track as tall as the alignments' two rows, the chosen one filled blue
   (Ahmed's mock-up, 28 Oct 2026). */
.align-row .segmented.compact.text-dir {
  flex: 1;
  gap: 4px;
  padding: 4px;
  border-radius: var(--radius-sm);
  background: var(--accent-50);
}

.text-dir .seg {
  display: grid;
}

.align-row .text-dir .seg span {
  height: auto;
  background: transparent;
  color: var(--text);
}

.align-row .text-dir .seg:hover span {
  background: var(--accent-100);
}

.align-row .text-dir .seg input:checked + span {
  background: var(--accent);
  color: var(--accent-ink);
}

.align-icon {
  display: inline-flex;
}

/* An icon in a choice (the alignments, the corners' shapes): only its button's colour, none of the look `.seg span`
   gives the button's own words box. */
:is(.seg, .seg:hover, .align-row .seg) span.align-icon {
  min-height: 0;
  height: auto;
  padding: 0;
  background: none;
  color: inherit;
}

/* A switch with its name, on a white bar edged in blue (Show this logo, Clip, Multiple lines), as tall as the Main
   Logo and Secondary Logo buttons (Ahmed, 28 Oct 2026: "make the box thicker"). */
.toggle {
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  gap: 6px;
  min-height: 40px;
  padding-block: 6px;
  padding-inline: 14px 10px;
  background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--accent);
  border-radius: var(--radius-sm);
}

.toggle > label {
  flex: 1;
}

.toggle input,
.switch {
  flex: none;
  appearance: none;
  position: relative;
  width: 30px;
  height: 18px;
  margin: 0;
  border-radius: 9px;
  corner-shape: round;
  background: var(--accent-200);
  cursor: pointer;
  transition: background 0.15s;
}

.toggle input::after,
.switch::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  corner-shape: round;
  background: #fff;
  transition: transform 0.15s;
}

.toggle input:checked,
.switch:checked {
  background: var(--accent);
}

.toggle input:checked::after,
.switch:checked::after {
  transform: translateX(12px);
}

/* Where the words are typed: light blue, no edge until it has the focus (the redesign's text box). */
.text-input,
.select select {
  width: 100%;
  padding: 10px;
  border: 1px solid transparent;
  border-radius: var(--radius-card);
  background: var(--accent-50);
  resize: vertical;
}

.text-input::placeholder {
  color: var(--accent-300);
}

.text-input:focus {
  outline: none;
  border-color: var(--accent);
}

.select label {
  display: grid;
  gap: 6px;
}

.select select {
  padding: 8px 10px;
  color: var(--text);
  font-size: var(--fs-md);
}

.text-input {
  font-size: var(--fs-md);
}

/* ---- FastGen's own dropdown (js/menu.js) ---- */

/* The wrapper is not a box: the button takes the place in the grid or row that the <select> used to hold. */
.menu-field {
  display: contents;
}

/* Sized and bordered like the fields beside it, so it reads as one of them rather than as a button. */
.menu-button {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  /* As a grid or flex item its automatic minimum is the width of its longest word, so a long option name would
     push it out of the column — and out of the card around it — instead of ending in an ellipsis. */
  min-width: 0;
  min-height: 32px;
  padding: 7px 10px;
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--accent-50);
  color: var(--accent);
  font-size: var(--fs-xs);
  text-align: start;
  cursor: pointer;
}

.menu-button:hover {
  background: var(--accent-100);
}

.menu-button[aria-expanded='true'] {
  box-shadow: inset 0 0 0 1px var(--accent);
}

/* min-width lets it shrink inside the button's flex row; without it the text refuses to give way and pushes the
   button wider than whatever it sits in, instead of ending in an ellipsis. */
.menu-value {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.menu-chevron {
  flex: none;
  width: 8px;
  height: 8px;
  margin-inline-end: 2px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
}

.menu-button[aria-expanded='true'] .menu-chevron {
  transform: translateY(1px) rotate(225deg);
}

/* Above the poster and the panel, and above the colour picker's own popover if both were ever open. It is a
   popover element, so `inset` and `margin` are reset from the browser's own centred-in-the-window defaults —
   this one is placed against the button that opened it. */
.menu-pop {
  position: fixed;
  inset: auto;
  z-index: 101;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  width: auto;
  margin: 0;
  padding: 4px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 16px 40px rgb(0 0 0 / 0.18);
}

/* A popover that is not open must not draw. The browser has its own rule saying exactly that, but any `display`
   an author sets overrides it — and this one sets `display: flex` to stack the search box above the list. So the
   hidden state has to be spelled out here, or a menu stays painted on screen after it has closed. */
.menu-pop[popover]:not(:popover-open) {
  display: none;
}

.menu-pop[hidden] {
  display: none;
}

/* The list is what scrolls, so a search box above it stays put while a hundred typefaces go by underneath. */
.menu-list {
  overflow: auto;
  /* A flex child won't shrink past its content without this, and the list would push the box taller than the
     height the popover was placed with. */
  min-height: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  overscroll-behavior: contain;
}

.menu-search-row {
  padding: 2px 2px 6px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--line);
}

.menu-search {
  width: 100%;
  padding: 7px 9px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font-size: var(--fs-md);
}

.menu-search::placeholder {
  color: var(--text-3);
}

.menu-empty {
  padding: 10px 12px;
  color: var(--text-3);
  font-size: var(--fs-md);
  white-space: nowrap;
}

.menu-list:focus-visible {
  outline: none;
}

/* The tick keeps its column whether or not the row is the chosen one, so the labels line up. */
.menu-option {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 12px;
  padding-inline: 8px 12px;
  border-radius: var(--radius-xs);
  font-size: var(--fs-md);
  white-space: nowrap;
  cursor: pointer;
}

.menu-tick {
  flex: none;
  width: 14px;
  height: 14px;
}

.menu-option-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The typeface menu: each name in its own face, big enough to judge it by, with its Arabic sample alongside. */
.menu-pop.is-typefaces .menu-option {
  gap: 12px;
  padding-block: 8px;
  font-size: 17px;
  line-height: 1.3;
}

.menu-option-sample {
  flex: none;
  margin-inline-start: 12px;
}

.menu-option-note {
  flex: none;
  font-family: var(--font);
  font-size: var(--fs-sm);
  font-weight: 400;
  color: var(--text-3);
}

.menu-option.is-active .menu-option-note {
  color: inherit;
  opacity: 0.8;
}

/* Drawn rather than fetched: two borders on a rotated box, the same trick as the chevron above. */
.menu-option[aria-selected='true'] .menu-tick::after {
  content: '';
  display: block;
  width: 4px;
  height: 8px;
  margin: 1px 0 0 4px;
  border-right: 1.6px solid currentColor;
  border-bottom: 1.6px solid currentColor;
  transform: rotate(45deg);
}

/* One highlight, wherever it came from — the pointer moving over a row or the arrow keys walking down them. */
.menu-option.is-active {
  background: var(--accent);
  color: var(--accent-ink);
}

.menu-rule {
  height: 1px;
  margin: 4px 8px;
  background: var(--line);
}

/* A named family of options — "Google Fonts", "Custom fonts". Not reachable by the arrow keys. */
.menu-heading {
  padding: 10px 12px 4px;
  padding-inline: 8px 12px;
  color: var(--text-3);
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: 0.02em;
}

.menu-option.is-disabled {
  color: var(--text-3);
  cursor: default;
}

/* A colour's name and its box on one light-blue bar, as a number's are (Ahmed, 30 Oct 2026: they looked apart). */
.color {
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  padding-block: 2px;
  padding-inline: 8px 2px;
  border-radius: var(--radius-sm);
  background: var(--accent-200);
}

/* The Font card's first row: the typeface, its face, size and spacing, each name above its box, and CAPS at the end as
   tall as the row (Ahmed: "caps button also fill the hight of the continer"). */
.face-row {
  display: grid;
  grid-template-columns: minmax(0, 2.2fr) repeat(5, minmax(0, 1fr)) auto;
  align-items: end;
  gap: 8px;
}

.face-row > .caps-toggle,
.face-row > .is-locked:has(.caps-toggle) {
  align-self: stretch;
}

@container (max-width: 640px) {
  .face-row {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .face-row > :first-child {
    grid-column: 1 / -1;
  }
}

.caps-toggle {
  flex: none;
  min-height: 38px;
  padding: 0 12px;
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--accent);
  color: var(--accent);
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: 0.06em;
  cursor: pointer;
  transition:
    background-color var(--dur) var(--ease),
    color var(--dur) var(--ease),
    transform 80ms var(--ease);
}

.caps-toggle:not([aria-pressed='true']):hover:not(:disabled) {
  background: var(--accent-50);
}

.caps-toggle[aria-pressed='true'] {
  background: var(--accent);
  color: var(--accent-ink);
}

.caps-toggle:active:not(:disabled) {
  transform: scale(0.97);
}

/* Superscript (T¹) and subscript (T₁) in the text toolbar (18 Oct 2026). */
.tt-script {
  min-width: 38px;
  padding: 0 8px;
}

.tt-script :is(sup, sub) {
  font-size: 0.65em;
  line-height: 0;
}

.caps-toggle:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 1px;
}

/* RTL text: Arabic has no capitals, so CAPS is greyed out. */
.caps-toggle:disabled {
  color: var(--text-3);
  opacity: 0.55;
  cursor: not-allowed;
}

/* The hex field's hover outline sits 5 px from the box's top, bottom and end alike. */
.color-input {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 3px;
  padding-inline-end: 5px;
  background: var(--accent-50);
  border-radius: var(--radius-sm);
}

.swatch {
  width: 32px;
  height: 32px;
  flex: none;
  padding: 0;
  border: 1px solid var(--accent-200);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.swatch[aria-expanded='true'] {
  outline: 2px solid var(--focus);
  outline-offset: 1px;
}

.hex-input {
  width: 64px;
  padding: 4px 2px;
  border: 1px solid transparent;
  border-radius: var(--radius-xs);
  background: transparent;
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
  text-transform: uppercase;
}

.hex-input:hover {
  border-color: var(--line);
}

.hex-input:focus {
  outline: none;
  border-color: var(--focus);
  background: var(--surface);
}

.cp-pop {
  position: fixed;
  z-index: 100;
  display: grid;
  /* One column the popover's own width: a row that wants more shrinks, never pushing past its edge. */
  grid-template-columns: minmax(0, 1fr);
  gap: 10px;
  width: 264px;
  padding: 12px;
  background: var(--surface);
  border: 1px solid var(--accent); /* edged in blue as the option cards are (Ahmed, 28 Oct 2026) */
  border-radius: var(--radius);
  box-shadow: 0 16px 40px rgb(0 0 0 / 0.18);
}

.cp-pop[hidden] {
  display: none;
}

.cp-area {
  position: relative;
  height: 150px;
  border-radius: var(--radius-sm);
  background-image: linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, transparent);
  cursor: crosshair;
  touch-action: none;
}

.cp-thumb {
  position: absolute;
  width: 14px;
  height: 14px;
  border: 2px solid #fff;
  border-radius: 50%;
  corner-shape: round;
  box-shadow: 0 0 0 1px rgb(0 0 0 / 0.35);
  transform: translate(-50%, -50%);
  pointer-events: none;
}

/* The eyedropper beside the hue slider, as in Figma's picker; where there's none, the slider is the whole row. */
.cp-hue-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

input.cp-hue {
  flex: 1;
  min-width: 0;
  appearance: none;
  height: 12px;
  margin: 0;
  border-radius: 99px;
  corner-shape: round;
  background: linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00);
}

/* The rainbow is the track itself: input.cp-hue outranks the blue sliders' input[type='range'] rules, which would
   otherwise paint over it with their thin blue track and blue handle. */
input.cp-hue::-webkit-slider-runnable-track {
  height: 12px;
  background: transparent;
}

input.cp-hue::-moz-range-track {
  height: 12px;
  background: transparent;
}

input.cp-hue::-webkit-slider-thumb {
  appearance: none;
  width: 14px;
  height: 14px;
  margin-top: -1px;
  border: 2px solid #fff;
  border-radius: 50%;
  corner-shape: round;
  background: transparent;
  box-shadow: 0 0 0 1px rgb(0 0 0 / 0.35);
}

input.cp-hue::-moz-range-thumb {
  width: 12px;
  height: 12px;
  border: 2px solid #fff;
  border-radius: 50%;
  corner-shape: round;
  background: transparent;
  box-shadow: 0 0 0 1px rgb(0 0 0 / 0.35);
}

.cp-hex-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--fs-sm);
  color: var(--text-2);
}

.cp-hex-label {
  display: flex;
  flex: 1;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.cp-add {
  flex: none;
  padding: 6px 8px;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--accent-50);
  color: var(--accent);
  font-size: var(--fs-sm);
  cursor: pointer;
}

.cp-add:hover {
  background: var(--accent-100);
}

/* The organization's colours across the top: one click puts one on. */
.cp-brand {
  display: grid;
  gap: 8px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--accent);
}

.cp-brand[hidden] {
  display: none;
}

.cp-brand-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
}

.cp-brand-title {
  overflow: hidden;
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-overflow: ellipsis;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--text-2);
}

.cp-brand-edit {
  color: var(--accent);
}

.cp-swatches {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.cp-brand-chip {
  position: relative;
}

.cp-swatch {
  display: block;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.cp-swatch[aria-pressed='true'] {
  border: 2px solid var(--accent);
  box-shadow: inset 0 0 0 2px var(--surface);
}

.cp-brand.is-editing .cp-swatch {
  cursor: text;
}

.cp-remove {
  position: absolute;
  top: -6px;
  inset-inline-end: -6px;
  display: grid;
  place-items: center;
  width: 18px;
  height: 18px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  corner-shape: round;
  background: var(--surface);
  color: var(--text);
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
}

.cp-empty {
  font-size: var(--fs-sm);
  color: var(--text-3);
}

/* The last few colours chosen, along the foot. */
.cp-recent {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-sm);
  color: var(--text-3);
}

.cp-recent[hidden] {
  display: none;
}

.cp-recent-label {
  margin-inline-end: 2px;
}

.cp-recent-swatches {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.cp-recent-swatch {
  width: 20px;
  height: 20px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 5px;
  cursor: pointer;
}

/* A brand colour's name beside its field. */
.color-name {
  max-width: 96px;
  overflow: hidden;
  font-size: var(--fs-sm);
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--success-ink);
}

/* Light blue, edged and lettered in blue, like the option fields (Ahmed, 28 Oct 2026). */
.cp-hex {
  flex: 1;
  min-width: 0;
  padding: 8px 12px;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--accent-50);
  color: var(--accent);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  text-transform: uppercase;
}

.cp-hex:focus {
  outline: none;
  box-shadow: 0 0 0 2px var(--accent-200);
}

/* ---------- gradient editor ---------- */

.gradient-bar {
  position: relative;
  height: 32px;
  margin: 2px 9px 6px; /* room for the handles at 0% and 100% */
  border-radius: var(--radius-sm);
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.1);
  background-color: #fff;
  background-image: var(--checker);
  background-size: auto, 12px 12px;
}

.stop-handle {
  position: absolute;
  top: 50%;
  width: 18px;
  height: 40px;
  padding: 0;
  border: 3px solid #fff;
  border-radius: 6px;
  background: var(--stop-color, #000);
  box-shadow: 0 0 0 1px rgb(0 0 0 / 0.25), 0 2px 6px rgb(0 0 0 / 0.2);
  transform: translate(-50%, -50%);
  cursor: ew-resize;
  touch-action: none;
}

.stop-handle.is-selected {
  box-shadow: 0 0 0 2px var(--text), 0 2px 8px rgb(0 0 0 / 0.25);
  z-index: 1;
}

.stop-handle:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.stops {
  display: grid;
  gap: 4px;
}

/* Each stop a row of separate boxes, as wide as the bar above them and Angle below: no colour behind the row, and a
   column for ✕ only where there is one (Ahmed, 28 Oct 2026: the boxes looked joined, and fell short of the edge). */
.stops-head,
.stop {
  display: grid;
  grid-template-columns: minmax(118px, 1.4fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: 6px;
  align-items: center;
}

/* Every stop as tall as one with a colour (its 32px swatch and the 3px round it), a mask's too, so Dark overlay and
   Mask side by side keep their stops and Direction level (Ahmed, 30 Oct 2026). */
.stop {
  min-height: 38px;
}

.stops.can-remove :is(.stops-head, .stop) {
  grid-template-columns: minmax(118px, 1.4fr) minmax(0, 1fr) minmax(0, 1fr) 28px;
}

/* A mask's stops have no colour: only how much shows, and where. */
.gradient.no-colors :is(.stops-head, .stop) {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}

.gradient.no-colors .stops.can-remove :is(.stops-head, .stop) {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 28px;
}

.stops-head {
  font-size: var(--fs-xs);
  color: var(--text-2);
}

/* The stop chosen on the bar: its boxes edged in blue. */
.stop.is-selected .number-input {
  box-shadow: inset 0 0 0 1px var(--accent-300);
}

.stop .color-input {
  gap: 6px;
  padding-inline-end: 2px;
  background: transparent;
}

.stop .hex-input {
  width: 56px;
}

/* A gradient stop's row has no room for the name. */
.stop .color-name {
  display: none;
}

.stop .field.number {
  position: relative;
  padding: 0;
  background: transparent;
}

.stop .field.number label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
}

.stop .number-input {
  flex: 1;
  justify-content: flex-end;
  padding: 0 5px;
}

.stop .number-input input {
  width: 100%;
  min-width: 0;
}

/* Its column's width (.icon-btn, later in this file, would make it 32px and push it past the row's edge). */
.stop .stop-remove {
  width: 28px;
  height: 28px;
}

.number-input.compact {
  padding: 0 6px;
}

.number-input.compact input {
  width: 44px;
  padding: 3px 0;
}

.direction-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
}

/* Angle at the row's far end, the room between it and the arrows whatever is left (Ahmed, 28 Oct 2026). */
.direction-row .field.number {
  flex: 0 0 auto;
  margin-inline-start: auto;
  padding: 0;
  background: transparent;
}

.direction-row .field.number label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
}

/* Each arrow a 32px square to click, 4px apart (Ahmed, 28 Oct 2026); in a narrow card they close up to 24px, so all
   8 directions stay on one line. */
.dir-buttons {
  display: grid;
  grid-template-columns: repeat(8, minmax(24px, 32px));
  gap: 4px;
  padding: 3px;
  border-radius: var(--radius-sm);
  background: var(--accent-50);
}

.dir-btn {
  display: grid;
  place-items: center;
  min-width: 0;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-xs);
  background: transparent;
  color: var(--accent);
  cursor: pointer;
}

.dir-btn svg {
  transition: transform var(--dur) var(--ease);
}

.dir-btn:hover {
  color: var(--text);
}

.dir-btn[aria-pressed='true'] {
  background: var(--accent);
  color: var(--accent-ink);
}

/* Flip (23 Oct 2026): two buttons that stay down while on, looking as the choices beside them do. */
.flip-buttons {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 4px;
}

.flip-btn {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 6px;
  min-width: 0;
  min-height: 32px;
  padding: 4px 8px;
  border: 0;
  border-radius: var(--radius-xs);
  background: var(--accent-50);
  color: var(--accent);
  font: inherit;
  font-size: var(--fs-xs);
  white-space: nowrap;
  cursor: pointer;
}

.flip-icon {
  display: grid;
  flex: none;
}

.flip-flipV .flip-icon svg {
  transform: rotate(90deg);
}

.flip-btn:hover {
  background: var(--accent-100);
}

.flip-btn:is([aria-pressed='true'], [aria-pressed='mixed']) {
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 500;
}

.flip-btn[aria-pressed='mixed'] {
  opacity: 0.6;
}

.flip-btn:focus-visible {
  outline: 2px solid var(--focus);
}

@container (max-width: 360px) {
  .stops-head,
  .stop {
    grid-template-columns: minmax(104px, 1fr) minmax(0, 1fr) minmax(0, 1fr) 28px;
    gap: 4px;
  }
}

/* ---------- image tab ---------- */

.upload-hint {
  font-size: var(--fs-xs);
  color: var(--text-2);
}

.upload.is-dragover,
.photo-card.is-dragover {
  border-color: var(--focus);
  border-style: dashed;
  background: color-mix(in srgb, var(--focus) 6%, var(--surface));
}

.stage.is-dragover::after {
  content: 'Drop to use this photo';
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  border: 2px dashed var(--focus);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--focus) 10%, transparent);
  color: var(--focus);
  font-weight: 600;
  pointer-events: none;
  z-index: 5;
}

/* The background photo's card: its preview, one line on how sharp it is, and Replace with the bin beside it under
   them (24 Oct 2026). */
.photo-card {
  display: grid;
  align-content: start;
  gap: var(--sp-2);
  padding: var(--sp-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}

/* The Background tab's two choices, the background image or a fill colour, as the logos' slots (18 Oct 2026), tidied
   on 24 Oct 2026 (Ahmed): both cards built alike, so their previews and lines sit level. */
.bg-choices {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

/* Generate Image takes the room its prompt needs, Image and Color less (Ahmed's Background frames). */
.logo-slot.bg-choice.bg-generate {
  flex: 2 1 260px;
}

.logo-slot.bg-choice .slot-state,
.logo-slot.bg-choice.is-active .slot-state {
  padding: 0;
  background: none;
  color: var(--text);
}

/* Spaced and rounded as the tab's other boxes (.group); .logo-slot's own come later in this file, hence both classes.
   Side by side while each has 180px (three across from a 1400px screen, Generate Image among them), the rest
   wrapping under in a narrower column (a tablet's). */
.logo-slot.bg-choice {
  flex: 1 1 150px;
  /* One column no wider than the card: a long model name ends in an ellipsis rather than pushing the row out. */
  grid-template-columns: minmax(0, 1fr);
  align-content: start;
  gap: 10px;
  padding: 10px;
  border-radius: var(--radius-card);
}

.bg-choice:not(.is-active) {
  cursor: pointer;
}

.bg-choice .photo-card {
  padding: 0;
  border: 0;
}

/* A preview the same size in both cards: the whole photo, or the fill colour from edge to edge. */
.bg-preview,
.bg-choice .upload {
  height: 112px;
  min-height: 0;
  overflow: hidden;
  border-radius: var(--radius);
}

.bg-preview {
  display: grid;
  grid-template: 100% / 100%;
  place-items: center;
  border: 0;
  background: var(--accent-50);
}

.bg-preview .hint {
  margin: 0;
  padding: var(--sp-2);
  text-align: center;
}

.photo-thumb {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.fill-choice {
  display: grid;
  gap: var(--sp-2);
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.bg-choice.is-active .fill-choice {
  cursor: default;
}

.bg-choice:not(.is-active) :is(.photo-thumb, .fill-swatch) {
  opacity: 0.55;
}

/* Generate Image (25 Oct 2026): its name beside the AI sparkle where the others have their check, a line under it,
   the prompt box the height of the others' previews, then the model menu and Generate. */
.bg-generate-title {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  font-size: var(--fs-xs);
  font-weight: 600;
}

.bg-generate-icon {
  display: grid;
  flex: none;
  color: var(--accent);
}

.bg-generate-note {
  margin: -8px 0 0; /* 4px under its name */
  font-size: var(--fs-sm);
  color: var(--text-2);
}

.bg-prompt {
  height: 112px;
  padding: 10px 12px;
  font-size: var(--fs-md);
  resize: none;
}

.bg-soon {
  color: var(--text-3);
  font-size: var(--fs-md);
  font-weight: 500;
}

/* Generate under the model menu, across the card, 8px from it as the Image card's line is from its preview. */
.bg-generate > .btn {
  width: 100%;
  margin-top: -4px;
}

.bg-generate .hint {
  margin: 0;
}

/* An image layer's Generate with AI before the platform switches it on (25 Oct 2026): Coming soon where the prompt
   goes, in a box like the Background tab's. */
.generate-soon {
  display: grid;
  place-items: center;
  height: 112px;
  border: 0;
  border-radius: var(--radius);
  background: var(--accent-50);
  color: var(--text-3);
  font-size: var(--fs-md);
  font-weight: 500;
}

.photo-size {
  font-size: var(--fs-sm);
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
}

.photo-quality {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-sm);
  font-weight: 500;
}

.photo-quality::before {
  content: '';
  width: 7px;
  height: 7px;
  flex: none;
  border-radius: 50%;
  corner-shape: round;
  background: currentColor;
}

.photo-quality.is-ok {
  color: var(--success);
}

.photo-quality.is-warn {
  color: var(--warn-ink);
}

.photo-quality.is-warn::before {
  background: var(--warn-dot);
}

.photo-actions {
  display: flex;
  align-items: center;
  gap: 4px;
}

/* The background photo's: Replace across the card, the bin at its end (24 Oct 2026), 12px under the line above them
   (the card's 8px gap and 4px), the line itself kept close to its preview. */
.photo-card .photo-actions {
  flex-wrap: wrap;
  gap: 6px;
  margin: 4px 0 0;
}

.photo-card .photo-actions > label {
  flex: 1;
}

.photo-actions label {
  position: relative;
}

.photo-actions label:has(input:focus-visible) {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.zoom {
  display: grid;
  gap: 4px;
}

.zoom-reset {
  justify-self: start;
}

.zoom-reset[hidden] {
  display: none;
}

.overlay-presets {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--sp-2);
  max-width: 420px;
}

.preset {
  display: grid;
  gap: 6px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text-2);
  font-size: var(--fs-xs);
  cursor: pointer;
  text-align: center;
}

.preset-thumb {
  position: relative;
  overflow: hidden;
  aspect-ratio: 1;
  border-radius: var(--radius-sm);
  background: var(--photo, linear-gradient(160deg, #a9b6c2 0%, #d8c7a8 55%, #7f8c6e 100%));
  background-size: cover;
  background-position: center;
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.08);
  transition: box-shadow var(--dur) var(--ease), transform 80ms var(--ease);
}

/* A mask's tile: the photo fading over a checkerboard, as it will on the poster (FastGen-mask-plan.md). */
.mask-thumb {
  background: #fff var(--checker) 0 0 / 10px 10px;
}

.mask-thumb::before {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--photo, linear-gradient(160deg, #a9b6c2 0%, #d8c7a8 55%, #7f8c6e 100%)) center / cover;
  -webkit-mask-image: var(--mask, none);
  mask-image: var(--mask, none);
}

.mask-thumb::after {
  display: none;
}

/* The overlay is shown at a typical strength, like it will look on the poster. */
.preset-thumb::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--overlay, none);
  opacity: 0.7;
}

.preset:hover .preset-thumb {
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.08), 0 0 0 2px var(--line);
}

.preset[aria-checked='true'] {
  color: var(--text);
  font-weight: 600;
}

.preset[aria-checked='true'] .preset-thumb {
  box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--text);
}

.preset:focus-visible {
  outline: none;
}

.preset:focus-visible .preset-thumb {
  box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--focus);
}

.preset:active .preset-thumb {
  transform: scale(0.97);
}

@container (max-width: 250px) {
  .grid-2 {
    grid-template-columns: minmax(0, 1fr);
  }

  .field.number label {
    white-space: normal;
    line-height: 1.2;
  }

  .seg span {
    white-space: normal;
    line-height: 1.2;
  }
}

/* ---------- uploads ---------- */

.upload {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-height: 72px;
  padding: 12px;
  border: 1px dashed var(--accent-200);
  border-radius: var(--radius);
  background: var(--surface);
  cursor: pointer;
  font-size: var(--fs-xs);
  text-align: center;
}

.upload:hover,
.upload:has(input:focus-visible) {
  border-color: var(--accent);
}

.upload-label {
  font-weight: 500;
}

.upload-types {
  font-size: var(--fs-xs);
  color: var(--text-3);
}

.upload.has-file {
  flex-direction: row;
  justify-content: flex-start;
  gap: 12px;
  border-style: solid;
  border-color: var(--accent-200);
  cursor: default;
}

.upload-thumb {
  width: 48px;
  height: 48px;
  object-fit: contain;
  border-radius: var(--radius-sm);
  background: repeating-conic-gradient(#e7e7e7 0 25%, #fff 0 50%) 0 0 / 12px 12px;
}

.upload-replace {
  position: relative;
  margin-inline-start: auto;
  padding: 6px 12px;
  border-radius: var(--radius-sm);
  background: var(--accent-50);
  color: var(--accent);
  font-size: var(--fs-xs);
  font-weight: 500;
  cursor: pointer;
}

.upload-replace:has(input:focus-visible) {
  outline: 2px solid var(--focus);
}

/* Paste beside Replace (touch screens): the same look, as a button. */
button.upload-replace {
  border: 0;
  color: inherit;
  font-family: inherit;
}

.upload-replace ~ .upload-replace {
  margin-inline-start: 0;
}

/* Paste in an empty picture box (touch screens), under its types. */
.upload-paste {
  margin-top: var(--sp-2);
}

/* A logo's choices, three to a row that slides sideways once there are more (24 Oct 2026): each card snaps into
   place, and the arrows (tabs.js) take the place of a scrollbar. 2px around them, inside the row, for the active
   card's ring and the focus rings. */
.logo-row {
  position: relative;
}

.logo-slots {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: calc((100% - 2 * 8px) / 3);
  gap: 8px;
  margin: -2px;
  padding: 2px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: 2px;
  scrollbar-width: none;
  container-type: inline-size;
}

.logo-slots::-webkit-scrollbar {
  display: none;
}

.logo-slots > * {
  scroll-snap-align: start;
}

/* Add logo: last in the row, a card's size, dashed as an upload box is. */
.logo-add {
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 2px;
  min-width: 0;
  padding: 7px;
  border: 1px dashed var(--accent-200);
  border-radius: var(--radius-card);
  background: var(--surface);
  color: var(--text-2);
  font: inherit;
  font-size: var(--fs-md);
  cursor: pointer;
}

.logo-add:hover {
  border-color: var(--accent);
  background: var(--accent-50);
  color: var(--accent);
}

.logo-add:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: -2px;
}

.logo-add-plus {
  font-size: 22px;
  font-weight: 300;
  line-height: 1;
}

.logo-scroll {
  position: absolute;
  top: 50%;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  margin-top: -14px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  corner-shape: round;
  background: var(--surface);
  color: var(--text);
  box-shadow: 0 1px 3px rgb(0 0 0 / 0.12);
  cursor: pointer;
}

.logo-scroll:hover {
  background: var(--accent-hover);
}

.logo-scroll:active {
  background: var(--accent);
  color: var(--accent-ink);
}

.logo-scroll[hidden] {
  display: none;
}

.logo-scroll.is-prev {
  inset-inline-start: -12px;
}

.logo-scroll.is-next {
  inset-inline-end: -12px;
}

:root[dir='rtl'] .logo-scroll svg {
  transform: scaleX(-1);
}

/* Narrow: inactive cards show just the ring; the label stays for screen readers. The background's two choices keep
   their names, Image and Color. */
@container (max-width: 330px) {
  .logo-slot:not(.is-active, .bg-choice) .slot-state {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
  }
}

/* A logo's card (and the Background's choices): white, edged in blue, Active or Set active at its top (the redesign). */
.logo-slot {
  display: grid;
  gap: 6px;
  min-width: 0;
  padding: 10px;
  border: 0;
  border-radius: var(--radius-card);
  background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--accent-200);
  transition: box-shadow 0.15s ease;
}

.logo-slot.is-active {
  box-shadow: inset 0 0 0 1px var(--accent);
}

.slot-select {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  font-size: var(--fs-xs);
  font-weight: 500;
  color: var(--text);
  cursor: pointer;
}

.logo-slot.is-active .slot-select {
  cursor: default;
}

/* Active or Set active, a chip: filled blue on the one in use. */
.slot-state {
  padding: 2px 6px;
  border-radius: 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.logo-slot.is-active .slot-state {
  background: var(--accent);
  color: var(--accent-ink);
}

.logo-slot:not(.bg-choice) .slot-dot {
  display: none;
}

/* The logo in use: a white check in a white ring before Active, both on the blue chip (Ahmed, 28 Oct 2026: the
   previous UI's circle with a check, back). The chip is the label's here, so the ring sits inside it. */
.logo-slot.is-active:not(.bg-choice) .slot-select {
  justify-self: start;
  gap: 4px;
  padding: 2px 6px 2px 3px;
  border-radius: 4px;
  background: var(--accent);
  color: var(--accent-ink);
}

.logo-slot.is-active:not(.bg-choice) .slot-state {
  padding: 0;
  background: none;
}

.logo-slot.is-active:not(.bg-choice) .slot-dot {
  display: block;
  border-color: var(--accent-ink);
  background: transparent;
}

/* Set active darkens under the pointer; Active keeps its white words on the blue (Ahmed, 28 Oct 2026). */
.logo-slot:not(.is-active) .slot-select:hover .slot-state {
  color: var(--text);
}

/* Inactive: empty ring. Active: filled circle with a check mark. */
.slot-dot {
  position: relative;
  flex: none;
  width: 16px;
  height: 16px;
  border: 1.5px solid #b9b9b4;
  border-radius: 50%;
  corner-shape: round;
  background: var(--surface);
}

.slot-select:hover .slot-dot {
  border-color: var(--text-2);
}

.logo-slot.is-active .slot-dot {
  border-color: var(--accent);
  background: var(--accent);
}

.logo-slot.is-active .slot-dot::after {
  content: '';
  position: absolute;
  left: 4.5px;
  top: 1.5px;
  width: 4px;
  height: 8px;
  border: solid var(--accent-ink);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

.slot-select:has(input:focus-visible) .slot-dot {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.slot-thumb {
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 1;
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: repeating-conic-gradient(#ececea 0 25%, #fafaf9 0 50%) 0 0 / 12px 12px;
  cursor: pointer;
}

.slot-thumb img {
  width: 80%;
  height: 80%;
  object-fit: contain;
}

.logo-slot:not(.is-active) .slot-thumb img {
  opacity: 0.55;
}

.slot-thumb:has(input:focus-visible) {
  outline: 2px solid var(--focus);
  outline-offset: 1px;
}

.slot-empty {
  display: grid;
  justify-items: center;
  font-size: var(--fs-xs);
  color: var(--text-2);
}

.slot-empty span {
  font-size: 20px;
  line-height: 1;
}

.slot-thumb:hover .slot-empty {
  color: var(--text);
}

.slot-foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  min-height: 24px;
  font-size: var(--fs-xs);
  color: var(--text-2);
}

.slot-remove {
  width: 24px;
  height: 24px;
  font-size: var(--fs-xs);
}

/* ---------- buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 38px;
  padding: 9px 14px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font-weight: 500;
  font-size: var(--fs-md);
  white-space: nowrap;
  text-decoration: none; /* a .btn is sometimes a link, e.g. "Contact us" on the Plans page */
  cursor: pointer;
}

.btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.btn-block {
  width: 100%;
}

.btn-primary {
  background: var(--accent);
  color: var(--accent-ink);
}

.btn-primary:hover:not(:disabled) {
  background: color-mix(in srgb, var(--accent), #000 15%);
}

/* Light blue with a blue edge and blue words (the redesign's Paint on the background, Share, Save). */
.btn-secondary {
  background: var(--accent-50);
  border-color: var(--accent);
  color: var(--accent);
}

.btn-secondary:hover:not(:disabled) {
  background: var(--accent-100);
}

.btn-ghost {
  background: transparent;
  color: var(--accent);
}

.btn-ghost:hover {
  background: var(--accent-50);
}

/* Delete: light red with red words; an icon on its own (the bin beside a layer's name) is solid red. */
.btn-danger {
  background: var(--danger-bg);
  color: var(--danger);
  border-color: transparent;
  min-height: 34px;
}

.btn-danger:hover {
  background: color-mix(in srgb, var(--danger) 16%, var(--surface));
}

.btn-danger.is-icon {
  width: 38px;
  padding: 0;
  background: var(--danger);
  color: #fff;
}

.btn-danger.is-icon:hover {
  background: color-mix(in srgb, var(--danger), #000 15%);
}

.btn [data-icon],
.btn .btn-icon,
.btn .btn-icon svg {
  display: block;
  flex: none;
  width: 16px;
  height: 16px;
}

.icon-btn {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  flex: none;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-2);
  cursor: pointer;
}

/* Buttons that were grey (Ahmed, 28 Oct 2026): #EAF0FF where they have a fill, #D4DEFC under the pointer, FastGen
   blue with white words while pressed. */
.icon-btn:hover {
  background: var(--accent-hover);
  color: var(--text);
}

.icon-btn:active:not(:disabled) {
  background: var(--accent);
  color: var(--accent-ink);
}

.link-btn {
  border: 0;
  background: none;
  padding: 2px 0;
  font-size: var(--fs-sm);
  font-weight: 500;
  cursor: pointer;
}

.link-btn:hover {
  text-decoration: underline;
}

/* ---------- sub tabs & blocks ---------- */

.subtabs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.subtab {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  padding: 6px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text-2);
  cursor: pointer;
}

.subtab:hover:not([aria-selected='true']) {
  border-color: var(--surface-3);
  color: var(--text);
}

.subtab-hint {
  font-size: var(--fs-xs);
  font-weight: 400;
  color: var(--text-3);
}

.subtab[aria-selected='true'] {
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}

.subtab[aria-selected='true'] .subtab-hint {
  color: color-mix(in srgb, var(--accent) 70%, var(--surface));
}

.block-head {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
}

/* The add and duplicate buttons on a row of their own, always above the tabs they add to (Main Text, Margin Text,
   Graphic layers), however many tabs there are. */
.block-bar {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
}

.block-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

/* + Add text and Duplicate: as tall as the text chips beside them, in plain weight. */
.block-actions .btn {
  min-height: 0;
  padding: 6px 12px;
  font-weight: 400;
}

.btn.is-confirming {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
}

/* The list fills its card down to the bottom, as tall as the cards beside it, and scrolls past that (Ahmed, 28 Oct
   2026: "as long as it has space under it it should fill the box"). */
.features-card > .card-body {
  flex: 1 1 auto;
  min-height: 0;
}

.features {
  display: grid;
  align-content: start;
  gap: 6px;
  flex: 1 1 0;
  min-height: 160px;
  overflow-y: auto;
}

.feature {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) minmax(0, 1.2fr);
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.feature.is-on {
  border-color: var(--accent);
  background: var(--surface-2);
}

.feature:has(input:disabled) {
  opacity: 0.5;
  cursor: not-allowed;
}

.feature input {
  margin: 0;
  accent-color: var(--accent);
}

.feature-meta {
  display: grid;
  min-width: 0;
}

.feature-tag {
  font-size: var(--fs-xs);
  font-weight: 600;
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
}

.feature-name {
  font-size: var(--fs-sm);
  color: var(--text-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.feature-sample {
  font-size: 20px;
  line-height: 1.5;
  text-align: end;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

@container (max-width: 340px) {
  .feature {
    grid-template-columns: auto minmax(0, 1fr);
  }

  .feature-sample {
    grid-column: 1 / -1;
    text-align: start;
  }
}

/* ---------- preview ---------- */

/* Poster and its footer are centered together; app.js sizes the poster to the room left. */
.stage {
  position: relative;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-4); /* keep in sync with STAGE_GAP in app.js */
}

.poster-foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--sp-2) var(--sp-3);
  width: 100%;
  max-width: 620px;
}

.poster-size {
  font-size: var(--fs-sm);
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
}

.ph-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.ph {
  position: absolute;
  display: grid;
  place-items: center;
  border: 1.5px dashed rgb(255 255 255 / 0.45);
  border-radius: var(--radius-sm);
}

.ph-image {
  border-color: rgb(255 255 255 / 0.18);
  border-radius: var(--radius-xs);
}

.ph-btn {
  pointer-events: auto;
  padding: 8px 14px;
  border: 0;
  border-radius: 99px;
  corner-shape: round;
  background: rgb(255 255 255 / 0.12);
  color: rgb(255 255 255 / 0.85);
  font-size: var(--fs-md);
  font-weight: 500;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.ph-image .ph-btn {
  background: var(--surface);
  color: var(--text);
  box-shadow: 0 4px 16px rgb(0 0 0 / 0.25);
}

/* Headline and logo spots: the whole dashed area is the button. */
.ph-clickable .ph-btn {
  width: 100%;
  height: 100%;
  padding: 4px;
  border-radius: var(--radius-xs);
  background: transparent;
  font-size: var(--fs-sm);
  line-height: 1.2;
}

.ph-clickable:hover {
  border-color: rgb(255 255 255 / 0.85);
}

.ph-clickable .ph-btn:hover,
.ph-image .ph-btn:hover {
  background: rgb(255 255 255 / 0.1);
  color: #fff;
}

.ph-image .ph-btn:hover {
  background: var(--accent-hover);
  color: var(--text);
}

.ph-image .ph-btn:active {
  background: var(--accent);
  color: var(--accent-ink);
}

.ph-btn:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* The photo's button and the line under it ("or paste with ⌘V"). */
.ph-stack {
  display: grid;
  justify-items: center;
  gap: var(--sp-2);
}

.ph-tip {
  font-size: var(--fs-sm);
  color: rgb(255 255 255 / 0.75);
  text-shadow: 0 1px 6px rgb(0 0 0 / 0.5);
}

/* Touch screens: Upload a photo and Paste side by side. */
.ph-row {
  display: flex;
  gap: var(--sp-2);
}

.live-hover {
  position: absolute;
  outline: 1.5px dashed rgb(255 255 255 / 0.85);
  box-shadow: 0 0 0 1.5px rgb(47 107 255 / 0.9);
  border-radius: 3px;
  pointer-events: none;
}

.live-editor {
  position: absolute;
  margin: 0;
  border: 0;
  border-radius: 3px;
  background: transparent;
  outline: 2px solid var(--focus);
  outline-offset: 1px;
  resize: none;
  overflow: hidden;
  font-kerning: normal;
}

.live-editor[hidden],
.live-hover[hidden] {
  display: none;
}

/* The outlines' holder over the stage, cut to the poster (liveedit.js clipMarks): an outline only where the poster is. */
.live-marks {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* The selected text's width and height in poster pixels (liveedit.js syncSize), under its box in the editor's blue. */
.live-size {
  position: absolute;
  z-index: 20;
  padding: 2px 6px;
  border-radius: 4px;
  background: var(--focus);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  line-height: 16px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  pointer-events: none;
}

/* The image layer picked on a phone (liveedit.js markPicked): outlined for as long as the bar's tools work on it. */
.live-picked {
  outline-style: solid;
}

/* The lines chosen in a Multi-style text's panel (liveedit.js markLines). */
.live-lines {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.live-line {
  outline-style: solid;
  outline-color: rgb(47 107 255 / 0.95);
}

/* A masked image whose corners are moved (FastGen-mask-corners-plan.md): its outline follows the shape, in the hover
   outline's blue and dashed white — picked, or its row hovered in Layers. */
.live-hover.has-shape {
  outline: none;
  box-shadow: none;
}

.live-hover svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.live-hover .shape-under {
  fill: none;
  stroke: rgb(47 107 255 / 0.9);
  stroke-width: 3;
}

.live-hover .shape-over {
  fill: none;
  stroke: rgb(255 255 255 / 0.85);
  stroke-width: 1.5;
  stroke-dasharray: 4 3;
}

/* The words chosen stay marked while the toolbar has the focus (liveedit.js markSelection), in the system's own
   selection colour, so it reads as the same selection. */
::highlight(fastgen-words) {
  background-color: Highlight;
}

/* The editor on the poster shows each styled word in its own look (spans, liveedit.js). */
.live-editor {
  cursor: text;
  overflow-wrap: break-word;
}

/* Over words selected on the poster: saved styles, and typeface, weight, size and colour where allowed. Looks like
   the editor's menus: the same surface, line, corners and shadow. */
.text-toolbar {
  position: absolute;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px;
  padding-inline-start: 20px; /* the grip's */
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 16px 40px rgb(0 0 0 / 0.18);
  /* Never wider than the poster's area: on a narrow screen its controls go onto a second row. */
  flex-wrap: wrap;
  max-width: calc(100% - 8px);
  white-space: nowrap;
}

.text-toolbar[hidden] {
  display: none;
}

.text-toolbar .menu-button {
  width: auto;
  min-height: 32px;
  padding: 4px 8px;
  font-size: var(--fs-sm);
}

.text-toolbar .tt-family .menu-value {
  max-width: 140px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tt-size-field {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 0 8px;
  min-height: 32px;
  cursor: text; /* it puts the caret in the size, not picks up the toolbar */
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--text-2);
  font-size: var(--fs-sm);
}

.tt-size {
  width: 44px;
  border: 0;
  background: none;
  color: var(--text);
  font: inherit;
  text-align: end;
}

.tt-size:focus {
  outline: none;
}

.tt-size-field:focus-within {
  outline: 2px solid var(--focus);
  outline-offset: 1px;
}

/* Just the swatch here: the hex code and the colour's name are in its picker. */
.text-toolbar .color-input .hex-input,
.text-toolbar .color-input .color-name {
  display: none;
}

/* The side panel's CAPS button, at the toolbar's height. */
.text-toolbar .caps-toggle {
  height: 32px;
  padding: 0 10px;
}

.tt-icon {
  display: inline-grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text-2);
  cursor: pointer;
}

.tt-icon:hover {
  background: var(--accent-hover);
  color: var(--text);
}

.tt-icon:active {
  background: var(--accent);
  color: var(--accent-ink);
}

.tt-sep {
  width: 1px;
  height: 20px;
  background: var(--line);
}

/* ---- the blur brush (brush.js) ---- */

/* Over the poster while the brush is open: it takes the mouse or the finger (no page scrolling under a finger). */
.brush-layer {
  position: absolute;
  z-index: 35;
  touch-action: none;
  cursor: none;
}

/* The brush's size, following the pointer. */
.brush-cursor {
  position: absolute;
  border: 2px solid #fff;
  border-radius: 50%;
  corner-shape: round;
  box-shadow: 0 0 0 1px rgb(0 0 0 / 0.45), inset 0 0 0 1px rgb(0 0 0 / 0.45);
  pointer-events: none;
}

/* Above the poster (Ahmed's Brush bar, 28 Oct 2026): a white card edged in blue — the tools across the top, Size and
   Strength side by side, the buttons, then a line on what AI Remove does. It moves where it's dragged (below). */
.brush-bar {
  position: absolute;
  top: var(--sp-3);
  left: 50%;
  z-index: 40;
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: min(760px, calc(100% - 16px));
  padding: 0 14px 14px; /* the grip is the room at its top */
  transform: translateX(-50%);
  background: var(--surface);
  border-radius: var(--radius-card);
  box-shadow: inset 0 0 0 1px var(--accent), 0 8px 24px rgb(0 0 0 / 0.16);
  user-select: none;
}

/* The brush bar and the text toolbar go where they're dragged, inside the preview (js/drag-bar.js): by mouse from
   anywhere but their controls, by finger from their grip (Ahmed, 28 Oct 2026). */
:is(.brush-bar, .text-toolbar) {
  cursor: grab;
  touch-action: none;
}

:is(.brush-bar, .text-toolbar).is-dragging {
  cursor: grabbing;
}

/* Where it was dragged to: drag-bar.js sets its left and top (and the brush bar's width). */
.brush-bar.is-moved {
  right: auto;
  bottom: auto;
  transform: none;
}

/* Its own contents' width wherever it's put, rather than squeezed against the stage's far edge. */
.text-toolbar.is-moved {
  width: max-content;
}

/* The grip: a short line where a finger picks the bar up, a target of its own so the touch isn't taken for the
   button beside it. */
.bar-grip {
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: none;
  cursor: inherit;
}

.bar-grip::before {
  content: '';
  width: 36px;
  height: 4px;
  border-radius: 2px;
  corner-shape: round;
  background: var(--accent-200);
}

.bar-grip:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: -2px;
  border-radius: var(--radius-sm);
}

/* The brush bar's: a strip across its top, overlapping the gap under it, so the tools sit as close to the top as
   before plus a few pixels. */
.brush-bar .bar-grip {
  height: 18px;
  margin-bottom: -8px;
}

/* The text toolbar's: an upright strip at its start, as tall as the toolbar however many rows it wraps to. */
.text-toolbar .bar-grip {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  width: 20px;
}

.text-toolbar .bar-grip::before {
  width: 4px;
  height: 20px;
}

/* A touch screen's own menu (Cut, Copy…) comes up over the words chosen, above anything of ours, so the toolbar waits at the
   bottom of what's on screen instead, above the keyboard when it's up, the page sliding the words clear of it
   (page-slide.js). Dragged, it stays where it's put, as it does elsewhere. */
@media (max-width: 720px), (pointer: coarse) {
  .app {
    position: relative;
    top: 0;
    transition: top 0.32s cubic-bezier(0.22, 1, 0.36, 1);
  }

  .text-toolbar {
    position: fixed;
    left: 16px;
    right: 16px;
    bottom: calc(16px + var(--keyboard-room, 0px) + env(safe-area-inset-bottom));
    width: fit-content;
    max-width: calc(100vw - 32px);
    margin-inline: auto;
    gap: 6px 5px;
    animation: toolbar-dock 0.32s cubic-bezier(0.22, 1, 0.36, 1);
  }

  /* A size of up to four figures still fits, and the eraser joins the row above on most phones. */
  .tt-size {
    width: 36px;
  }

  .text-toolbar.is-moved {
    right: auto;
    bottom: auto;
    margin-inline: 0;
    animation: none;
  }
}

@keyframes toolbar-dock {
  from {
    opacity: 0;
    transform: translateY(24px);
  }
}

/* AI Remove's marks, in red, over the poster until Remove sends them. */
.brush-marks {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0.5;
  pointer-events: none;
}

.brush-cursor.is-remove {
  border-color: #ef4444;
}

.brush-hint {
  margin: 0;
  font-size: var(--fs-xs);
  color: var(--text-3);
}

.brush-layer[hidden],
.brush-cursor[hidden],
.brush-bar[hidden],
.brush-bar [hidden] {
  display: none;
}

/* The tools as the panel's choices: four equal buttons, the one in use filled blue. */
.brush-tools {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 4px;
}

.brush-tool {
  min-height: 30px;
  padding: 4px 6px;
  white-space: nowrap;
  border: 0;
  border-radius: var(--radius-xs);
  background: var(--accent-50);
  box-shadow: inset 0 0 0 1px var(--accent);
  color: var(--accent);
  font: inherit;
  font-size: var(--fs-xs);
  cursor: pointer;
}

.brush-tool[aria-pressed='true'] {
  background: var(--accent);
  box-shadow: none;
  color: var(--accent-ink);
  font-weight: 500;
}

.brush-tool:disabled {
  opacity: 0.45;
  cursor: default;
}

.brush-sliders {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
}

.brush-label {
  font-size: var(--fs-xs);
  font-weight: 500;
  color: var(--text);
}

.brush-value {
  justify-content: flex-end;
  min-width: 56px;
}

.brush-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

.brush-actions .bar-gap {
  flex: 1;
}

/* Saved styles, managed: one row each, its name to rename it, and what can be done to it. */
.style-dialog {
  width: min(520px, calc(100vw - 32px));
}

.style-list {
  display: grid;
  gap: 8px;
  margin: 12px 0;
}

.style-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.style-row .text-input {
  flex: 1;
  min-width: 0;
}

.live-target {
  animation: live-target 1.4s ease-out;
}

@keyframes live-target {
  0%,
  40% {
    box-shadow: 0 0 0 3px var(--focus);
  }
  100% {
    box-shadow: 0 0 0 3px transparent;
  }
}

@media (prefers-reduced-motion: reduce) {
  .live-target {
    animation: none;
    box-shadow: 0 0 0 3px var(--focus);
  }
}

#canvas {
  display: block;
  flex: none;
  border-radius: var(--radius-xs);
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.16);
  /* Shows where a mask makes the background see-through (FastGen-mask-plan.md); covered everywhere else. */
  background-color: #fff;
  background-image: var(--checker);
  background-size: 16px 16px;
}

/* Waiting for the AI. It sits exactly over the poster, dimmed rather than hidden, so the photo being worked on
   stays visible. Positioned from the canvas in JS, because the canvas is centred and resizes with the window. */
.ai-busy {
  position: absolute;
  z-index: 5;
  display: grid;
  place-items: center;
  border-radius: var(--radius-xs);
  background: rgb(16 16 16 / 0.38);
}

/* Over the blur brush too, while AI Remove works. */
.stage.is-brushing .ai-busy {
  z-index: 36;
}

.ai-busy[hidden] {
  display: none;
}

.ai-busy-card,
.busy-pill {
  display: flex;
  align-items: center;
  gap: 9px;
  margin: 0;
  padding: 9px 16px;
  border-radius: 999px;
  corner-shape: round;
  background: rgb(22 22 22 / 0.92);
  color: #fff;
  font-size: var(--fs-md);
  font-weight: 500;
  box-shadow: 0 6px 20px rgb(0 0 0 / 0.28);
}

.ai-busy-mark {
  flex: none;
}

/* Three sparks catching the light in turn. Only opacity and transform move, so it costs the compositor nothing. */
.ai-spark {
  transform-box: fill-box;
  transform-origin: center;
  animation: ai-twinkle 1.6s ease-in-out infinite;
}

.ai-spark-2 {
  animation-delay: 0.45s;
}

.ai-spark-3 {
  animation-delay: 0.9s;
}

@keyframes ai-twinkle {
  0%,
  100% {
    transform: scale(0.72);
    opacity: 0.4;
  }

  50% {
    transform: scale(1);
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ai-spark {
    animation: none;
    opacity: 1;
  }
}

/* Still working: a ring turning, for a save or an open that takes a while online (showWorking() in js/dom.js adds
   is-working once it has taken more than a moment). Only transform moves, so it turns however busy the page is. */
.spinner,
.btn.is-working::before {
  content: '';
  flex: none;
  width: 16px;
  height: 16px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  corner-shape: round;
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(1turn);
  }
}

/* A working button shows the ring where its icon was, at full strength although it can't be pressed meanwhile. */
.btn.is-working {
  cursor: progress;
}

.btn.is-working:disabled {
  opacity: 1;
}

.btn.is-working > :is(svg, .btn-icon) {
  display: none;
}

/* Over the poster while a template arrives: the poster dims and says it is opening. */
.poster-opening {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: none;
  place-items: center;
  pointer-events: none;
}

.stage.is-working .poster-opening {
  display: grid;
}

.stage.is-working #canvas {
  opacity: 0.4;
}

/* The hints and outlines of the poster being replaced step aside meanwhile, so nothing is put into it. */
.stage.is-working :is(.ph-layer, .live-lines) {
  visibility: hidden;
}

/* Save and Export sit in a toolbar above the canvas. */
.preview-toolbar {
  display: flex;
  justify-content: flex-end;
  margin-bottom: var(--sp-4);
}

/* Export and Share at the start, Save at the end (the redesign). */
.actions {
  display: flex;
  flex: 1;
  flex-wrap: wrap;
  gap: 10px;
}

/* Phones keep Save first in their floating bar. */
@media (min-width: 721px) {
  .actions > .save-split {
    order: 1;
    margin-inline-start: auto;
  }
}

.preview-toolbar .btn-secondary {
  background: var(--surface);
}

.preview-toolbar .btn-secondary:hover:not(:disabled) {
  background: var(--accent-50);
}

/* A group only on phones (the floating bar); elsewhere its buttons sit in the row like the others. */
.action-group,
.action-row {
  display: contents;
}

.phone-only {
  display: none;
}

.photo-float {
  display: none;
}

.split {
  position: relative;
  display: flex;
  min-width: 0;
}

.split-main {
  flex: 1;
  min-width: 0;
  padding-inline: 10px;
  border-start-end-radius: 0;
  border-end-end-radius: 0;
}

.split-arrow {
  flex: none;
  width: 34px;
  padding: 0;
  border-inline-start: 1px solid rgb(255 255 255 / 0.2);
  border-start-start-radius: 0;
  border-end-start-radius: 0;
}

.export-menu {
  top: calc(100% + 6px);
  bottom: auto;
  inset-inline-start: auto;
  inset-inline-end: 0;
  min-width: 190px;
}

/* A row for each format: its name, then its sizes as small buttons (23 Oct 2026). The format Export makes is in bold,
   its size filled in. */
.export-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 5px 5px 5px 10px;
  border-radius: var(--radius-sm);
}

:root[dir='rtl'] .export-row {
  padding: 5px 10px 5px 5px;
}

.export-name {
  font-size: var(--fs-md);
}

.export-row.is-current .export-name {
  font-weight: 600;
}

.scale-chips {
  display: flex;
  gap: 4px;
}

.export-menu .menu-item.scale-chip {
  justify-content: center;
  width: auto;
  min-width: 36px;
  height: 30px;
  padding: 0 8px;
  border-radius: var(--radius-xs);
  background: var(--accent-50);
  color: var(--text-2);
  font-size: var(--fs-sm);
  font-weight: 500;
}

.export-menu .menu-item.scale-chip:hover {
  background: var(--accent-hover);
  color: var(--text);
}

.export-menu .menu-item.scale-chip:active {
  background: var(--accent);
  color: var(--accent-ink);
}

.export-menu .menu-item.scale-chip[aria-checked='true'] {
  background: var(--accent);
  color: var(--accent-ink);
}

.export-menu .menu-item.scale-chip:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 1px;
}

/* Big enough for a finger. */
@media (pointer: coarse) {
  .export-menu .menu-item.scale-chip {
    min-width: 44px;
    height: 40px;
  }
}

/* Save's arrow sits on a white button, so the rule between the halves is the button's own border. */
.save-split .split-main {
  padding-inline: 14px;
}

.save-split .split-arrow {
  margin-inline-start: -1px;
}

/* An editor's Save (a copy to Recent Edits) has no menu: one whole button. */
.save-split.is-recent .split-main {
  border-radius: var(--radius-sm);
}

.save-menu {
  top: calc(100% + 6px);
  bottom: auto;
  inset-inline-start: auto;
  inset-inline-end: 0;
  width: 300px;
}

/* A menu item that says what it will do under its name. */
.menu-item.menu-item-rich {
  display: grid;
  gap: 2px;
  padding-block: 10px;
}

.menu-item-line {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  font-size: var(--fs-base);
  font-weight: 600;
}

.menu-kbd {
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--text-3);
}

.menu-item-note {
  font-size: var(--fs-sm);
  color: var(--text-2);
}

.menu-item:disabled .menu-item-note {
  color: var(--text-3);
}

.menu-note {
  margin-inline-start: auto;
  padding-inline-start: 12px;
  font-size: var(--fs-xs);
  font-weight: 400;
  color: var(--text-3);
}

/* ---------- dialogs ---------- */

.dialog {
  width: min(360px, calc(100vw - 32px));
  padding: 24px;
  border: 0;
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--text);
  box-shadow: 0 24px 64px rgb(0 0 0 / 0.25);
}

.dialog::backdrop {
  background: rgb(20 20 20 / 0.45);
}

.dialog-wide {
  width: min(640px, calc(100vw - 32px));
}

.dialog h2 {
  font-size: var(--fs-xl);
  font-weight: 600;
}

.dialog-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;
}

.dialog-sub {
  margin: 4px 0 16px;
  color: var(--text-2);
  font-size: var(--fs-md);
}

.dialog form {
  display: grid;
  gap: 8px;
}

/* FastGen's own confirm (dialogs.js choose): a warning mark, the question, what it means, and the answers. */
.choose-dialog {
  width: min(500px, calc(100vw - 32px));
}

.choose-dialog[open] {
  display: grid;
  gap: 12px;
}

.choose-icon {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius);
  background: var(--warn-bg);
  color: var(--warn-ink);
}

.choose-dialog h2 {
  font-size: 18px;
}

.choose-text {
  color: var(--text-2);
  line-height: 1.5;
}

/* "It's done" rather than "are you sure": a green tick in place of the warning. */
.choose-icon.is-done {
  background: color-mix(in srgb, var(--success) 14%, var(--surface));
  color: var(--success-ink);
}

/* Text to send on, as it will be pasted: sign-in details. */
.choose-detail {
  margin: 0;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  font: var(--fs-md) / 1.55 ui-monospace, 'SF Mono', Menlo, monospace;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  user-select: all;
}

/* Before fonts are uploaded (16 Oct 2026): the files, why a font's licence matters, a box to tick, Cancel and Upload. */
.licence-dialog {
  width: min(500px, calc(100vw - 32px));
}

.licence-dialog[open] {
  display: grid;
  gap: 12px;
}

.licence-dialog .dialog-head,
.licence-dialog .dialog-sub {
  margin: 0;
}

.licence-dialog .dialog-sub {
  line-height: 1.5;
}

.licence-files {
  display: grid;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.licence-files li {
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  font-size: var(--fs-md);
  overflow-wrap: anywhere;
}

.licence-files .licence-more {
  padding: 0 12px;
  background: none;
  color: var(--text-2);
}

/* The words wrap beside the box, which stays at the first line; the hint lines up under the words, either side. */
.licence-check {
  align-items: flex-start;
  line-height: 1.45;
}

.licence-check input {
  flex: none;
  margin-top: 2px;
}

.licence-dialog .check-field .form-hint {
  padding-left: 0;
  padding-inline-start: calc(16px + var(--sp-2));
}

.licence-dialog .dialog-actions {
  margin-top: 4px;
}

.choose-actions {
  flex-wrap: wrap;
  padding-top: 4px;
}

.choose-actions .btn {
  font-weight: 600;
}

/* The answer that loses something: outlined in red rather than filled, so it never reads as the default. */
.btn-destructive {
  background: var(--surface);
  border-color: var(--danger);
  color: var(--danger);
}

.btn-destructive:hover:not(:disabled) {
  background: var(--danger-bg);
}

/* ---------- the template library ---------- */

.library {
  width: min(1200px, calc(100vw - 32px));
  height: min(820px, calc(100vh - 32px));
  max-width: none;
  max-height: none;
  padding: 0;
  overflow: hidden;
  container: library / inline-size;
}

.library[open] {
  display: flex;
  flex-direction: column;
}

.library-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 12px;
  padding: 16px 20px;
  border-bottom: 1px solid var(--line);
}

.library-head h2 {
  margin-inline-end: 4px;
}

.library-search {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 1 280px;
  min-width: 180px;
  height: 38px;
  padding: 0 12px;
  border: 1px solid #d6d6d2;
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text-2);
  cursor: text;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

.library-search:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgb(47 107 255 / 0.18);
}

.library-search input {
  flex: 1;
  min-width: 0;
  height: 100%;
  padding: 0;
  border: 0;
  outline: none;
  background: none;
  color: var(--text);
  font-size: var(--fs-base);
}

.library-sizes {
  display: flex;
  gap: 2px;
  padding: 3px;
  border-radius: var(--radius-sm);
  background: var(--accent-50);
  font-size: var(--fs-md);
}

.size-chip span {
  display: block;
  padding: 5px 10px;
  border-radius: var(--radius-xs);
  color: var(--text-2);
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}

.size-chip span:hover {
  color: var(--text);
}

/* A size with nothing in this folder stays, so the row doesn't shift, but reads as quieter. */
.size-chip span.is-empty {
  color: var(--text-3);
}

.size-chip input:checked + span {
  background: var(--surface);
  color: var(--text);
  font-weight: 600;
  box-shadow: 0 1px 2px rgb(0 0 0 / 0.08);
}

.size-chip input:focus-visible + span {
  outline: 2px solid var(--focus);
  outline-offset: 1px;
}

.library-sort .menu-button {
  min-height: 38px;
}

.library-gap {
  flex: 1;
}

.library-head label.btn:has(input:focus-visible) {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.library-scope {
  margin: 12px 20px 0;
}

.library-body {
  display: flex;
  flex: 1;
  min-height: 0;
}

/* ---- folders down the side ---- */

.library-folders {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: none;
  width: 220px;
  padding: 14px 12px;
  border-inline-end: 1px solid var(--line);
  overflow-y: auto;
}

.folder-list {
  display: grid;
  gap: 2px;
}

.folder-row {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 36px;
  padding: 8px 10px;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text);
  font-size: var(--fs-base);
  text-align: start;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}

.folder-row:hover {
  background: var(--surface-2);
}

.folder-row[aria-current='true'] {
  background: color-mix(in srgb, var(--accent) 10%, var(--surface));
  color: var(--accent);
  font-weight: 600;
}

.folder-row svg {
  flex: none;
  color: var(--text-2);
}

.folder-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.folder-count {
  flex: none;
  font-size: var(--fs-sm);
  font-weight: 400;
  font-variant-numeric: tabular-nums;
  color: var(--text-3);
}

.folder-row[aria-current='true'] .folder-count {
  color: inherit;
}

.folder-rename {
  width: 100%;
  height: 36px;
  padding: 0 10px;
  border: 2px solid var(--focus);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-size: var(--fs-base);
  font-weight: 500;
}

.folder-rename:focus {
  outline: none;
}

.new-folder {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
  padding: 8px 10px;
  border: 1px dashed #d6d6d2;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-2);
  font-size: var(--fs-md);
  cursor: pointer;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}

.new-folder:hover:not([aria-disabled='true']) {
  background: var(--accent-hover);
  color: var(--text);
}

.new-folder:active:not([aria-disabled='true']) {
  background: var(--accent);
  color: var(--accent-ink);
}

.new-folder[aria-disabled='true'] {
  opacity: 0.45;
  cursor: not-allowed;
}

.library-trash {
  display: grid;
  gap: 2px;
  margin-top: auto;
  padding-top: 8px;
  border-top: 1px solid var(--line);
}

.folder-note {
  padding: 2px 10px;
  font-size: var(--fs-sm);
  line-height: 1.4;
  color: var(--text-3);
}

/* Recent Edits, an editor's own copies on this computer: light red (as Layer order is), apart from the library. */
.recent-folder {
  background: var(--order-bg);
}

.recent-folder:hover {
  background: color-mix(in srgb, var(--order-bg), var(--danger) 8%);
}

.recent-folder[aria-current='true'] {
  background: var(--order-bg);
  color: var(--danger-ink);
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--danger) 45%, transparent);
}

.recent-folder[aria-current='true'] svg {
  color: inherit;
}

/* ---- the cards ---- */

.library-main {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}

.library-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 12px;
  min-height: 48px;
  padding: 12px 20px 0;
}

.library-summary {
  font-size: var(--fs-md);
  color: var(--text-2);
}

/* The plan's templates all used (saved.js renderBar). */
.library-limit.is-full {
  color: var(--warn-ink);
}

.folder-toolbar {
  display: flex;
  gap: 6px;
}

.library-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  align-content: start;
  gap: 16px;
  flex: 1;
  min-height: 0;
  padding: 12px 20px 88px;
  overflow-y: auto;
}

.library-empty {
  grid-column: 1 / -1;
  display: grid;
  justify-items: center;
  gap: 12px;
  padding: 48px 24px;
  text-align: center;
  color: var(--text-3);
}

.tpl-card {
  position: relative;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

.tpl-card:hover {
  border-color: #c9c9c5;
}

.tpl-card:has(.tpl-more[aria-expanded='true']) {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgb(47 107 255 / 0.15);
}

.tpl-body {
  display: grid;
  gap: 10px;
  width: 100%;
  padding: 10px;
  border: 0;
  border-radius: inherit;
  background: none;
  color: inherit;
  font: inherit;
  text-align: start;
}

.tpl-open {
  cursor: pointer;
}

.tpl-thumb {
  position: relative;
  display: grid;
  place-items: center;
  height: 180px;
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: var(--canvas-bg);
}

.tpl-thumb img {
  display: block;
  max-width: calc(100% - 20px);
  max-height: calc(100% - 20px);
  border-radius: 2px;
  box-shadow: 0 1px 3px rgb(0 0 0 / 0.12);
}

/* "Opening…" over the card's poster while its template arrives (whileOpening() in js/saved.js); the others wait. */
.tpl-opening {
  position: absolute;
  inset: 0;
  display: none;
  place-items: center;
  background: rgb(16 16 16 / 0.38);
}

.tpl-card.is-working .tpl-opening {
  display: grid;
}

.library:has(.tpl-card.is-working) .tpl-open {
  cursor: progress;
}

.tpl-meta {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.tpl-name {
  font-size: var(--fs-base);
  font-weight: 600;
  overflow-wrap: anywhere;
}

.tpl-sub {
  font-size: var(--fs-sm);
  color: var(--text-3);
}

.tpl-card.is-deleted .tpl-thumb {
  opacity: 0.7;
}

.tpl-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 0 10px 10px;
}

/* The ••• button shows on hover and focus; on touch screens, which have no hover, it is always there. */
.tpl-more {
  position: absolute;
  top: 18px;
  inset-inline-end: 18px;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font-size: 11px;
  letter-spacing: 1px;
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--dur) var(--ease), background-color var(--dur) var(--ease);
}

.tpl-card:is(:hover, :focus-within) .tpl-more,
.tpl-more[aria-expanded='true'] {
  opacity: 1;
}

.tpl-more:hover {
  background: var(--accent-hover);
}

.tpl-more:active {
  background: var(--accent);
  color: var(--accent-ink);
}

@media (hover: none) {
  .tpl-more {
    opacity: 1;
  }
}

/* The menu a ••• button opens (action-menu.js): on template cards and in the Team list. A popover in the top
   layer, so it is placed by hand; like the dropdown's, its closed state has to be spelled out. */
.action-menu {
  position: fixed;
  inset: auto;
  z-index: 101;
  display: grid;
  gap: 1px;
  width: 240px;
  margin: 0;
  padding: 6px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg); /* the items' 8 px corners, 6 px in */
  background: var(--surface);
  color: var(--text);
  box-shadow: 0 12px 32px rgb(0 0 0 / 0.14);
}

.action-menu[popover]:not(:popover-open),
.action-menu[hidden] {
  display: none;
}

.action-menu .menu-item {
  font-size: var(--fs-base);
}

.action-menu .menu-item.is-danger {
  color: var(--danger);
}

.action-menu .menu-item.is-current {
  font-weight: 600;
}

.action-menu .menu-item.is-current::after {
  content: '';
  width: 5px;
  height: 9px;
  margin-inline-start: 8px;
  border: solid var(--text);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg) translateY(-2px);
}

/* A ••• button in a row, such as a teammate's. */
.more-btn {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font-size: 11px;
  letter-spacing: 1px;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease);
}

.more-btn:hover,
.more-btn[aria-expanded='true'] {
  background: var(--accent-hover);
}

.more-btn:active {
  background: var(--accent);
  color: var(--accent-ink);
}

/* Messages inside a modal dialog (the library, Team), with Undo where there is something to undo. A toast would
   be drawn behind the dialog. */
.dialog-note {
  position: absolute;
  inset-inline-start: 20px;
  bottom: 20px;
  z-index: 4;
  display: flex;
  align-items: center;
  gap: 12px;
  max-width: calc(100% - 40px);
  padding: 12px 14px;
  border-radius: var(--radius);
  background: var(--text);
  color: #fff;
  font-size: var(--fs-md);
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.16);
  animation: pop-in 0.2s var(--ease);
}

.dialog-note-btn {
  flex: none;
  padding: 5px 10px;
  border: 1px solid rgb(255 255 255 / 0.4);
  border-radius: var(--radius-sm);
  background: transparent;
  color: #fff;
  font-size: var(--fs-md);
  font-weight: 600;
  cursor: pointer;
}

.dialog-note-btn:hover {
  background: rgb(255 255 255 / 0.12);
}

/* Narrow screens: the folders become a row that scrolls sideways, and the cards go two across. */
@container library (max-width: 760px) {
  /* Title and ✕ on top, then the search, the sizes, and the sort with Import and Download on one last row. */
  .library-head {
    padding: 14px 16px;
  }

  .library-head h2 {
    order: 1;
    flex: 1;
  }

  .library-head [data-close] {
    order: 2;
  }

  .library-search {
    order: 3;
    flex: 1 1 100%;
  }

  .library-sizes {
    order: 4;
    flex: 0 1 auto;
    max-width: 100%;
    overflow-x: auto;
    scrollbar-width: none;
  }

  .library-sort {
    order: 5;
  }

  .library-gap {
    order: 6;
  }

  #import-templates,
  #export-templates {
    order: 7;
  }

  .library-body {
    flex-direction: column;
  }

  .library-folders {
    flex-direction: row;
    align-items: center;
    gap: 6px;
    width: auto;
    padding: 10px 16px;
    border-inline-end: 0;
    border-bottom: 1px solid var(--line);
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
  }

  .folder-list {
    display: flex;
    gap: 6px;
  }

  .folder-row,
  .folder-rename {
    flex: none;
    width: auto;
    max-width: 200px;
    border: 1px solid var(--line);
  }

  .new-folder {
    flex: none;
    margin: 0;
  }

  .library-trash {
    display: flex;
    margin: 0;
    padding: 0;
    border: 0;
  }

  .folder-note {
    display: none;
  }

  .library-bar {
    padding: 10px 16px 0;
  }

  .library-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
    padding: 10px 16px 88px;
  }

  .tpl-thumb {
    height: 140px;
  }

  .tpl-more {
    top: 14px;
    inset-inline-end: 14px;
  }

  .dialog-note {
    inset-inline: 16px;
    max-width: none;
  }
}

.btn-small {
  min-height: 32px;
  padding: 4px 10px;
  font-size: var(--fs-sm);
}

.empty {
  padding: 24px;
  text-align: center;
  color: var(--text-3);
  list-style: none;
}

/* How many typefaces are left. Turns to a warning once the plan's allowance is used up. */
.font-count {
  font-size: var(--fs-md);
  color: var(--text-2);
}

.font-count.is-full {
  color: var(--danger-ink);
  font-weight: 500;
}

.font-scope {
  margin-bottom: var(--sp-3);
}

.retry {
  justify-self: center;
}

.font-scope {
  display: flex;
  gap: var(--sp-2);
  margin: 0 0 var(--sp-4);
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--success) 9%, var(--surface));
  color: var(--success-ink);
  font-size: var(--fs-sm);
}

.font-scope.is-local {
  background: var(--warn-bg);
  color: var(--warn-ink);
}

.upload[hidden] {
  display: none;
}

.font-list {
  display: grid;
  gap: 6px;
  margin: 16px 0 0;
  padding: 0;
  list-style: none;
}

.font-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 8px 8px 8px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.font-info {
  flex: 1;
  display: grid;
  gap: 2px;
  min-width: 0;
}

.font-sample {
  font-size: 22px;
}

.font-name {
  font-weight: 600;
  overflow-wrap: anywhere;
}

/* The font new templates and new texts start in (Set default). */
.font-default {
  display: inline-block;
  margin-inline-start: 8px;
  padding: 1px 6px;
  border-radius: 4px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: var(--fs-xs);
  font-weight: 500;
  vertical-align: 2px;
}

.font-weights {
  font-size: var(--fs-sm);
  color: var(--text-3);
  overflow-wrap: anywhere;
}

.toast {
  position: fixed;
  left: 50%;
  bottom: 24px;
  /* A long message wraps instead of running off the screen; one line still reads as a pill. */
  max-width: min(560px, calc(100vw - 32px));
  padding: 10px 16px;
  border-radius: 20px;
  text-align: center;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: var(--fs-md);
  transform: translate(-50%, 20px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s, transform 0.2s;
  z-index: 50;
}

.toast.show {
  opacity: 1;
  transform: translate(-50%, 0);
  /* Resting the pointer on a message holds it open (js/toast.js). */
  pointer-events: auto;
}

/* A link inside a message ("Templates › Recent Edits"): the message's own words, underlined. */
.toast-link {
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.toast-link:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
  border-radius: 2px;
}

/* ---------- responsive ---------- */

/* The preview column's own width decides how much the toolbar shows. Phones have their floating bar instead. */
@media (min-width: 721px) {
  /* A narrow preview column (small desktops, tablets): Share shows just its icon, so the actions stay on one row. */
  @container preview (max-width: 600px) {
    .actions .btn {
      padding-inline: 12px;
    }

    .actions .split-arrow {
      padding-inline: 0;
    }

    #share-btn {
      width: 40px;
      padding: 0;
    }

    #share-btn .btn-label {
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip: rect(0 0 0 0);
      white-space: nowrap;
    }
  }
}

/* Tablets, where the post size menu shares the toolbar: narrower still (an upright iPad), Save shows just its
   icon, Export drops the format word, and Share — not available yet — steps aside, so everything fits on one row. */
@media (min-width: 721px) and (max-width: 1024px) {
  @container preview (max-width: 470px) {
    .actions {
      flex-wrap: nowrap;
    }

    .export-format,
    #share-btn {
      display: none;
    }

    #save-btn .btn-label {
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip: rect(0 0 0 0);
      white-space: nowrap;
    }

    #save-btn {
      width: 40px;
      padding: 0;
    }
  }
}

@media (max-width: 1024px) {
  /* Controls for a designer sitting at a desk: too many parts to be worth the room on a tablet or a phone.
     What they set still draws on the poster everywhere — only the controls are desktop's. */
  .desktop-only {
    display: none;
  }

  .tabs {
    display: flex;
    overflow-x: auto;
  }

  .tab {
    padding: 14px 12px;
  }
}

/* Tablets keep the desktop's side-by-side layout, so every change shows on the poster as it's made. The sidebar
   becomes a rail of icons (the logo's bolt, Workspace, the profile picture), the post sizes move to a menu above
   the poster, and the options and the poster share what's left. */
@media (min-width: 721px) and (max-width: 1024px) {
  .app {
    grid-template-columns: 64px minmax(320px, 45fr) minmax(0, 55fr);
    height: 100vh;
    height: 100dvh;
  }

  .sidebar {
    align-items: center;
    gap: 6px;
    padding: 16px 0 14px;
    overflow: visible;
  }

  .brand {
    padding: 0;
    margin-bottom: 14px;
  }

  .brand img,
  .tagline,
  .ratios,
  .side-heading,
  .side-title,
  .ws-count,
  .sidebar .trial-badge,
  .editor-legal {
    display: none;
  }

  .brand-mark {
    display: grid;
  }

  .workspace-list {
    gap: 6px;
  }

  .ws-link {
    justify-content: center;
    width: 44px;
    height: 44px;
    padding: 0;
    border-radius: var(--radius);
  }

  .ws-icon {
    width: 20px;
    height: 20px;
  }

  .ws-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  .sidebar-foot {
    justify-items: center;
  }

  /* Just the picture; its menu opens beside the rail rather than above a 64px-wide button. */
  .profile {
    width: auto;
    min-height: 0;
    padding: 0;
    border: 0;
    border-radius: 50%;
    corner-shape: round;
    background: none;
    box-shadow: none;
  }

  .profile-chevron {
    display: none;
  }

  .profile-text {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  #profile-menu {
    inset-inline-start: calc(100% + 10px);
    inset-inline-end: auto;
    bottom: 0;
    width: 240px;
  }

  .preview {
    padding: 14px 16px 16px;
  }

  .preview-toolbar {
    align-items: center;
    gap: 8px;
    margin-bottom: 12px;
  }

  .ratio-menu {
    display: block;
    flex: none;
  }

  .ratio-menu .menu-button {
    width: auto;
    min-height: 40px;
  }
}

/* Phones: an app-style screen that fits the height of the display with nothing to scroll. A top bar (logo,
   profile picture, post sizes), the template name, the poster, and a floating bar of actions at the bottom.
   The option tabs are hidden: texts are edited on the poster itself and a tap on the photo changes it. */
@media (max-width: 720px) {
  .app {
    display: flex;
    flex-direction: column;
    height: 100vh;
    height: 100dvh;
    min-height: 0;
    overflow: hidden;
  }

  .sidebar {
    position: relative;
    z-index: 30;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      'brand profile'
      'ratios ratios';
    align-items: center;
    gap: 12px 16px;
    height: auto;
    padding: calc(12px + env(safe-area-inset-top)) 16px 12px;
    overflow: visible;
    border-inline-end: 0;
    border-bottom: 1px solid var(--line);
  }

  .brand {
    grid-area: brand;
  }

  /* As tall as the profile picture beside it, so the top bar is no taller. */
  .brand img {
    height: 36px;
  }

  .tagline {
    display: none;
  }

  .ratios {
    grid-area: ratios;
    min-width: 0;
  }

  .ratio-list {
    flex-direction: row;
    overflow-x: auto;
    gap: 6px;
    padding-bottom: 4px;
  }

  .ratio {
    flex: none;
    min-height: 44px;
    box-shadow: inset 0 0 0 1px var(--accent-100);
  }

  .ratio-glyph {
    width: 44px;
    height: 44px;
  }

  .ratio-name {
    white-space: nowrap;
  }

  /* Workspace moves into the profile menu, Templates to the bottom bar, and the trial reminder stays on bigger screens. */
  .sidebar-foot {
    display: contents;
  }

  /* The sidebar is a top bar here; the same links sit in the profile menu instead. */
  .editor-legal {
    display: none;
  }

  .sidebar-foot > .trial-badge,
  .workspace {
    display: none;
  }

  /* Profile: just the picture, top right. Its menu drops over the page instead of pushing it down. */
  .profile-wrap {
    grid-area: profile;
    position: static;
  }

  .profile {
    width: auto;
    min-height: 0;
    padding: 0;
    border: 0;
    border-radius: 50%;
    corner-shape: round;
    background: none;
    box-shadow: none;
  }

  .profile-chevron {
    display: none;
  }

  .profile-text {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  #profile-menu {
    top: calc(56px + env(safe-area-inset-top));
    bottom: auto;
    left: 5%;
    right: 5%;
    box-shadow: 0 16px 40px rgb(0 0 0 / 0.18);
    /* Dropping from the top bar, it unrolls downward. */
    --menu-shut: inset(-40px -40px 100% -40px);
    --menu-slide: -10px;
  }

  #profile-menu .menu-item {
    padding-block: 12px;
  }

  .editor {
    flex: none;
    padding: 8px;
    border-inline-end: 0;
  }

  /* The name bar on its own: no white band or blue line round it, the name taking the room Undo and Redo leave. */
  .editor-top {
    padding: 0;
    background: none;
    border-bottom: 0;
  }

  .editor-head {
    grid-template-columns: minmax(0, 1fr) auto auto;
  }

  .editor .tabs,
  .editor .panel {
    display: none;
  }

  .template-name {
    font-size: var(--fs-md);
  }

  /* Undo and redo as clear, outlined buttons. */
  .history-btn {
    width: 40px;
    height: 40px;
    border: 1px solid var(--line);
    background: var(--surface);
    color: var(--text);
    box-shadow: 0 1px 2px rgb(0 0 0 / 0.06);
  }

  .history-btn svg {
    width: 20px;
    height: 20px;
  }

  .history-btn:disabled {
    opacity: 0.55;
    background: var(--surface);
  }

  /* Room at the bottom for the floating bar and photo sliders (app.js measures them), so they never cover the poster. */
  .preview {
    flex: 1;
    padding: 8px 16px calc(88px + var(--photo-float-room, 0px) + env(safe-area-inset-bottom));
    border: 0;
  }

  .stage {
    flex: 1;
    height: auto;
  }

  .poster-foot {
    display: none;
  }

  .preview-toolbar {
    margin: 0;
  }

  /* The floating bar: Save Template on the left, the other actions grouped in the middle. */
  .actions {
    position: fixed;
    left: 16px;
    right: 16px;
    bottom: calc(16px + env(safe-area-inset-bottom));
    z-index: 25;
    flex-wrap: nowrap;
    justify-content: center;
    align-items: center;
    margin: 0;
    pointer-events: none;
    transition: transform 0.32s cubic-bezier(0.22, 1, 0.36, 1);
  }

  /* While words are being styled their toolbar has this place, and the bar slides away below the screen. Visibility, not
     display, for what floats above it: gone from the layout, it would leave room the poster then took (app.js). */
  body:has(.text-toolbar:not([hidden])) .actions {
    visibility: hidden;
    transform: translateY(calc(100% + 24px + env(safe-area-inset-bottom)));
    transition: transform 0.32s cubic-bezier(0.22, 1, 0.36, 1), visibility 0s linear 0.32s;
  }

  body:has(.text-toolbar:not([hidden])) .photo-float {
    visibility: hidden;
  }

  .actions > * {
    pointer-events: auto;
  }

  /* Seven icons rarely fit across a phone, so the pill scrolls sideways rather than shrinking them or
     wrapping onto a second row. */
  .action-group {
    display: flex;
    max-width: 100%;
    padding: 5px;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-padding-inline: 5px; /* a tool brought into view keeps the pill's padding before it */
    scrollbar-width: none;
    border: 1px solid var(--line);
    /* The buttons' corners plus the padding and border around them, so the two curves run parallel. */
    border-radius: calc(var(--radius-sm) + 6px);
    background: var(--surface);
    box-shadow: 0 10px 28px rgb(0 0 0 / 0.16);
  }

  .action-group::-webkit-scrollbar {
    display: none;
  }

  .action-group > * {
    flex: none;
  }

  /* Every item as wide as the widest (a labelled photo tool, usually): equal columns, the row as wide as its
     content, and the pill around it scrolling when that is wider than the screen. */
  .action-row {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 4px;
    width: max-content;
  }

  /* While painting, the brush's bar takes the bottom bar's place, so the whole poster is free to paint on. */
  .brush-bar {
    position: fixed;
    top: auto;
    left: 16px;
    right: 16px;
    bottom: calc(16px + env(safe-area-inset-bottom));
    width: auto;
    max-width: none;
    gap: 10px;
    padding: 0 10px 10px;
    transform: none;
  }

  /* A fingertip's height on phones. */
  .brush-bar .bar-grip {
    height: 24px;
  }

  .brush-sliders {
    grid-template-columns: minmax(0, 1fr);
    gap: 8px;
  }

  body.is-brushing .actions,
  body.is-brushing .photo-float {
    display: none;
  }

  /* The icon whose slider is showing, so it's clear where the slider came from. */
  .action-group .float-btn.is-open,
  .action-group .float-btn.is-open:hover:not(:disabled) {
    background: var(--accent);
    color: #fff;
  }

  .phone-only {
    display: inline-flex;
  }

  .actions .btn {
    width: 100%;
    min-width: 48px;
    min-height: 48px;
    height: 48px;
    padding: 0;
    border-radius: var(--radius-sm);
  }

  .action-group .btn-secondary,
  .action-group .btn-secondary:hover:not(:disabled) {
    border-color: transparent;
    background: transparent;
    box-shadow: none;
  }

  .action-group .btn-secondary:hover:not(:disabled) {
    background: var(--accent-hover);
  }

  .action-group .btn-secondary:active:not(:disabled) {
    background: var(--accent);
    color: var(--accent-ink);
  }

  .actions svg {
    width: 20px;
    height: 20px;
  }

  /* Saving a template is an Admin's job on a proper screen, so the phone doesn't carry the button — but an editor's
     Save, a copy to Recent Edits, is there: their Recent Edits folder is on phones too. */
  #save-split {
    display: none;
  }

  /* It keeps its size, and the pill beside it gives way and scrolls: the pill never reaches under it. */
  #save-split.is-recent {
    display: flex;
    flex: none;
  }

  /* Icons only; the words stay for screen readers. Export uses the last chosen format. */
  .actions .btn-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  /* Except the photo tools: no one knows those icons by sight, and a phone has no tooltips, so their names
     show under them. The bar already scrolls sideways when it runs out of room. */
  .actions .photo-tool {
    flex-direction: column;
    gap: 1px;
    width: 100%;
    min-width: 56px;
    padding: 0 10px;
    border-radius: var(--radius-sm);
  }

  .actions .photo-tool svg {
    width: 18px;
    height: 18px;
  }

  .actions .photo-tool .btn-label {
    position: static;
    width: auto;
    height: auto;
    overflow: visible;
    clip: auto;
    font-size: 12px;
    line-height: 1.2;
  }

  .split-main {
    flex: none;
    border-radius: var(--radius-sm);
  }

  .split-arrow {
    display: none;
  }

  /* So Export opens its format menu instead, floating above the bar where the photo sliders do: fixed, because the
     pill scrolls sideways and would cut off anything that stuck out of it. */
  .export-menu {
    position: fixed;
    top: auto;
    left: 50%;
    right: auto;
    bottom: calc(86px + env(safe-area-inset-bottom));
    z-index: 30;
    min-width: 220px;
    transform: translateX(-50%);
  }

  /* Photo position and zoom, floating just above the bottom bar when the poster has a photo. */
  .photo-float {
    position: fixed;
    left: 50%;
    bottom: calc(86px + env(safe-area-inset-bottom));
    z-index: 25;
    display: grid;
    gap: 6px;
    width: min(calc(100% - 32px), 320px);
    transform: translateX(-50%);
  }

  .float-slider {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 4px 4px 4px 14px;
    border: 1px solid var(--line);
    border-radius: calc(var(--radius-sm) + 5px); /* the value box's corners, plus the padding and border */
    background: var(--surface);
  }

  .float-slider-icon {
    display: grid;
    flex: none;
    color: var(--text-2);
  }

  .float-slider input[type='range'] {
    flex: 1;
    min-width: 0;
  }

  .float-slider-value {
    flex: none;
    min-width: 54px;
    height: 30px;
    padding: 0 8px;
    border: 0;
    border-radius: var(--radius-sm);
    background: var(--surface-2);
    font-size: var(--fs-sm);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    cursor: pointer;
  }

  .float-slider-value:disabled {
    background: transparent;
    color: var(--text-2);
    cursor: default;
  }

  /* The image layer whose tools the bar shows (app.js), by name, with ✕ to go back to the background's. */
  .float-picture {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 4px;
    padding-inline-start: 14px;
    border: 1px solid var(--line);
    border-radius: calc(var(--radius-sm) + 5px); /* the button's corners, plus the padding and border */
    background: var(--surface);
    animation: menu-in 0.3s var(--ease); /* it comes up as the tools slide in below it (bar-slide.js) */
  }

  .float-picture-close {
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    border: 0;
    border-radius: var(--radius-sm);
    background: var(--accent-50);
    color: var(--text);
    cursor: pointer;
  }

  .float-picture-close:hover {
    background: var(--accent-hover);
  }

  .float-picture-close:active {
    background: var(--accent);
    color: var(--accent-ink);
  }

  /* A logo's choices, side by side, the active one outlined and ticked as on the Logo tab's cards. */
  .float-logos {
    display: grid;
    gap: 10px;
    padding: 10px 10px 12px 14px;
    border: 1px solid var(--line);
    border-radius: calc(var(--radius-sm) + 5px);
    background: var(--surface);
  }

  .float-logos-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
  }

  .float-logos-title {
    font-size: var(--fs-sm);
    font-weight: 600;
  }

  .float-logos-list {
    display: flex;
    gap: 8px;
  }

  .float-logo {
    position: relative;
    display: grid;
    place-items: center;
    width: 64px;
    aspect-ratio: 1;
    padding: 0;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: repeating-conic-gradient(#ececea 0 25%, #fafaf9 0 50%) 0 0 / 12px 12px;
    cursor: pointer;
  }

  .float-logo img {
    width: 80%;
    height: 80%;
    object-fit: contain;
  }

  .float-logo.is-active {
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent);
  }

  .float-logo-check {
    display: none;
    position: absolute;
    top: 4px;
    inset-inline-start: 4px; /* the corner reading starts from: top right in Arabic */
    width: 16px;
    height: 16px;
    border-radius: 50%;
    corner-shape: round;
    background: var(--accent);
  }

  .float-logo.is-active .float-logo-check {
    display: block;
  }

  .float-logo-check::after {
    content: '';
    position: absolute;
    left: 5.5px;
    top: 2.5px;
    width: 4px;
    height: 8px;
    border: solid var(--accent-ink);
    border-width: 0 2px 2px 0;
    transform: rotate(45deg);
  }

  .toast {
    bottom: calc(96px + var(--photo-float-room, 0px) + env(safe-area-inset-bottom));
  }

  .grid-2 {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ---------- interaction states & motion ---------- */

.btn,
.icon-btn,
.tab,
.subtab,
.seg span,
.ratio,
.menu-item,
.link-toggle,
.upload,
.logo-slot,
.profile {
  transition:
    background-color var(--dur) var(--ease),
    border-color var(--dur) var(--ease),
    color var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease),
    transform 80ms var(--ease);
}

.btn:active:not(:disabled),
.icon-btn:active:not(:disabled),
.tab:active,
.subtab:active,
.link-toggle:active,
.seg:active span {
  transform: scale(0.97);
}

.subtab:hover:not([aria-selected='true']) {
  background: var(--accent-hover);
}

.dialog[open] {
  animation: dialog-in 0.18s var(--ease);
}

.dialog[open]::backdrop {
  animation: fade-in 0.18s var(--ease);
}

.menu:not([hidden]) {
  animation: menu-in 0.14s var(--ease);
}

@keyframes dialog-in {
  from {
    opacity: 0;
    transform: translateY(6px) scale(0.98);
  }
}

@keyframes fade-in {
  from {
    opacity: 0;
  }
}

@keyframes menu-in {
  from {
    opacity: 0;
    transform: translateY(4px);
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* AI Extend, in the Image tab. */
.extend-btn {
  width: 100%;
}

.extend-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
}

.extend-row .hint {
  margin: 0;
}

/* ---------- the monthly poster allowance is used up ---------- */

/* Everything goes quiet: the panel, the tabs, the sidebar's post sizes and the action buttons. The poster
   itself stays on screen, readable — the work isn't taken away, only the ability to add to it. Saving a
   template is left alone, so nothing already done is stranded. */
body.limit-reached .panel,
body.limit-reached .tabs,
body.limit-reached .ratios,
body.limit-reached .photo-float,
body.limit-reached .action-group {
  opacity: 0.45;
  pointer-events: none;
  filter: grayscale(1);
}

body.limit-reached .stage {
  opacity: 0.7;
}

.limit-message {
  margin-bottom: var(--sp-4);
  color: var(--text-2);
  line-height: 1.55;
}

/* The plans, as a row of the essentials — name, price, posters. Enough to choose by; the full cards are one
   click away on the Plans page. */
.limit-plans {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--sp-3);
}

.limit-plan {
  display: grid;
  gap: 4px;
  padding: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-2);
}

.limit-plan-name {
  font-weight: 600;
}

.limit-plan-price {
  font-size: var(--fs-lg);
  font-weight: 600;
}

.limit-plan-posters {
  color: var(--text-2);
  font-size: var(--fs-md);
}

/* ---------- the interface in Arabic (js/i18n.js) ---------- */

/* Satoshi has no Arabic letters, so they come from Alyamama, which lang-boot.js asks for; the Latin ones — names,
   HEX codes, "JPEG" — stay in Satoshi, which is first in the list. */
:root[lang='ar'] {
  --font: 'Satoshi', 'Alyamama', system-ui, -apple-system, 'Segoe UI', sans-serif;
}

/* Arabic letters join, and spacing them apart breaks the words. The text edited on the poster sets its own spacing
   inline, which this doesn't reach. */
:root[lang='ar'] * {
  letter-spacing: 0;
}

/* The language menu, above the profile: each language named in itself, beside its flag. */
/* In the sidebar: white, a light-blue edge, the language in the text colour (the redesign's switcher). */
.lang-button {
  gap: 8px;
  min-height: 34px;
  background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--accent-100);
  color: var(--text);
  font-weight: 500;
}

.lang-button:hover {
  background: var(--accent-50);
}

.lang-button .menu-chevron {
  color: var(--text-3);
}

.lang-button .menu-value {
  flex: 1;
}

.menu-icon {
  display: flex;
  flex: none;
}

/* A flag: cropped to one 4:3 tile whatever its proportions, with a hairline so white edges don't melt away. */
.flag {
  position: relative;
  display: inline-flex;
  flex: none;
  width: 20px;
  height: 15px;
  overflow: hidden;
  border-radius: 3px;
}

.flag svg {
  width: 100%;
  height: 100%;
}

.flag::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.1);
}

.menu-option .flag {
  margin-inline-end: 2px;
}

.menu-item.lang-item {
  justify-content: flex-start;
  gap: 10px;
}

:is(.lang-button, .menu-pop, .lang-item) [lang='ar'] {
  font-family: 'Alyamama', var(--font);
}

/* The menu is for the full sidebar; the rail and the phone's top bar have the languages in the profile menu instead. */
@media (max-width: 1024px) {
  .lang-switch {
    display: none;
  }
}

@media (min-width: 1025px) {
  .menu-item.lang-item {
    display: none;
  }
}

/* Left and right on the poster stay left and right in an Arabic interface: where a text is aligned, which side of
   an icon it sits, the position tiles, the direction arrows, and sliders that move the photo or text sideways. */
:root[dir='rtl'] :is(.segmented.tiles, .segmented.spatial, .dir-buttons, input[type='range']) {
  direction: ltr;
}

/* Undo points back the way Arabic reads. */
:root[dir='rtl'] .history-btn svg {
  transform: scaleX(-1);
}

/* "Move to folder ›" opens toward the way the line reads; in Arabic that is to the left. */
:root[dir='rtl'] .action-menu .menu-note {
  transform: scaleX(-1);
}

/* Addresses, passwords and codes are written left to right whatever the page around them; kept to the right edge
   so the form still lines up. */
:root[dir='rtl'] :is(input[type='email'], input[type='password'], input[type='url'], .mono-input, .hex-input, .cp-hex) {
  direction: ltr;
  text-align: right;
}

/* ---------- pop-ups (popup-view.js): the welcome and announcements ---------- */

/* Image, title, text, button, top to bottom; whatever is empty isn't there. The same card is the Super Admin's
   preview, so it has no size of its own beyond what holds it. */
.popup-card {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--surface);
  text-align: start;
}

.popup-image {
  display: block;
  width: 100%;
  max-height: 280px;
  object-fit: cover;
}

.popup-body {
  display: grid;
  gap: var(--sp-3);
  padding: var(--sp-5);
}

/* Room for the ✕ beside whatever is at the top when there's no picture under it. */
.popup-card.no-image .popup-body > :first-child {
  margin-inline-end: 36px;
}

.popup-title {
  font-size: var(--fs-xl);
  font-weight: 600;
  line-height: 1.3;
}

.popup-text {
  color: var(--text-2);
  font-size: var(--fs-base);
  line-height: 1.6;
  white-space: pre-line;
  overflow-wrap: anywhere;
}

.popup-button {
  margin-top: var(--sp-1);
  width: 100%;
}

/* Over the picture when there is one, so it sits on a white disc to be seen against any photo. */
.popup-close {
  position: absolute;
  top: 12px;
  inset-inline-end: 12px;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: 50%;
  corner-shape: round;
  background: rgb(255 255 255 / 0.92);
  color: var(--text);
  box-shadow: 0 1px 4px rgb(0 0 0 / 0.18);
  cursor: pointer;
}

.popup-close:hover {
  background: var(--surface);
}

.popup-card.no-image .popup-close {
  box-shadow: none;
  background: var(--accent-50);
}

.popup-card.no-image .popup-close:hover {
  background: var(--accent-hover);
}

.popup-close:active {
  background: var(--accent);
  color: var(--accent-ink);
}

.dialog.popup-dialog {
  width: min(440px, calc(100vw - 32px));
  max-height: calc(100dvh - 32px);
  padding: 0;
  overflow-y: auto;
}

/* Multi-style text (17 Oct 2026): a chip for each line, the chosen ones in the accent colour like the layer tabs. */
/* A Multi-style text's Lines stay in sight while its lines' settings scroll under them (Ahmed, 30 Oct 2026): 10px
   under the tabs, that band painted in the panel's colour so nothing shows through it. */
.group.lines-card {
  position: sticky;
  top: 0;
  z-index: 3;
}

.group.lines-card::after {
  content: '';
  position: absolute;
  inset-inline: -1px;
  bottom: 100%;
  height: 10px;
  background: var(--bg);
}

.line-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 6px;
}

.line-chip {
  padding: 5px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text-2);
  font: inherit;
  font-size: var(--fs-sm);
  cursor: pointer;
}

.line-chip:hover:not(.is-on) {
  border-color: var(--surface-3);
  color: var(--text);
}

.line-chip.is-on {
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}

/* Lines whose gradient, stroke or colour differ: "Mixed", with a button to make them the same. */
.mixed-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.mixed-row .field-label {
  flex: 1;
}

.mixed-note {
  color: var(--text-3);
  font-size: var(--fs-sm);
}

.color-input.is-mixed .swatch {
  background: repeating-conic-gradient(var(--surface-3) 0 25%, var(--surface) 0 50%) 0 0 / 8px 8px;
}

/* Typing a Multi-style text on the poster: plain words on a light box, readable over every line's colours; each line
   takes its own style when the typing ends. */
.live-editor.is-multi {
  background: rgb(255 255 255 / 0.94);
  color: #111111 !important;
  caret-color: #111111 !important;
  border-radius: 4px;
}

/* The colour picker's eyedropper, beside the hue slider: blue, like the HEX box under it (Ahmed, 28 Oct 2026). */
.cp-eyedrop {
  display: grid;
  flex: none;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--accent-50);
  color: var(--accent);
  cursor: pointer;
}

.cp-eyedrop[hidden] {
  display: none;
}

.cp-eyedrop:hover {
  background: var(--accent-100);
}

/* ---- The zoom view (Ahmed, 9 Nov 2026, js/zoom-view.js): a text layer's Stroke, Underline, Corners, background
   Stroke, Shadow and Padding have a magnifier beside their title; it brings the poster out large in the middle of the
   screen with the card floating over it. Desktop only. */
.card-title-zoom {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.zoom-btn {
  display: grid;
  flex: none;
  place-items: center;
  width: 26px;
  height: 26px;
  /* no taller a head than its neighbours' (20 px): the cards in a row start their settings level */
  margin-block: -3px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-xs);
  background: var(--accent-50);
  color: var(--accent);
  cursor: pointer;
}

.zoom-btn:hover {
  background: var(--accent-hover);
}

.zoom-btn[aria-pressed='true'] {
  background: var(--accent);
  color: var(--accent-ink);
}

@media (max-width: 1024px), (hover: none) {
  .zoom-btn {
    display: none;
  }
}

/* The card's spot in the panel while it floats. */
.zoom-placeholder {
  border-radius: var(--radius-card);
  box-shadow: inset 0 0 0 1px var(--accent-200);
  background: var(--accent-50);
}

.zoom-view {
  position: fixed;
  inset: 0;
  z-index: 90; /* over the editor; under the colour picker and menus (100+) the floating card opens */
  background: rgb(20 20 20 / 0);
  -webkit-backdrop-filter: blur(0);
  backdrop-filter: blur(0);
  transition: background 0.32s ease, backdrop-filter 0.32s ease, -webkit-backdrop-filter 0.32s ease;
}

.zoom-view.is-open {
  background: rgb(20 20 20 / 0.7);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}

.zoom-stage {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.zoom-view.can-pan.is-space .zoom-stage {
  cursor: grab;
}

.is-zoom-panning,
.is-zoom-panning * {
  cursor: grabbing !important;
}

.zoom-poster {
  position: absolute;
  top: 50%;
  left: 50%;
  border-radius: 2px;
  box-shadow: 0 24px 80px rgb(0 0 0 / 0.45);
  transform-origin: center;
  will-change: transform;
}

.zoom-poster.is-gliding {
  transition: transform 0.32s cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* The zoom's buttons, down the far left; the ✕ at the top right. */
.zoom-bar,
.zoom-close {
  opacity: 0;
  transition: opacity 0.2s ease;
}

.zoom-view.is-open .zoom-bar,
.zoom-view.is-open .zoom-close {
  opacity: 1;
}

.zoom-bar {
  position: absolute;
  top: 50%;
  left: 16px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 6px;
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.25);
  transform: translateY(-50%);
}

.zoom-tool,
.zoom-level,
.zoom-close {
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--accent-50);
  color: var(--accent);
  font: inherit;
  cursor: pointer;
}

.zoom-tool {
  width: 40px;
  height: 40px;
  font-size: 20px;
  line-height: 1;
}

.zoom-level {
  width: 40px;
  height: 32px;
  background: transparent;
  font-size: var(--fs-xs);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.zoom-tool:hover,
.zoom-level:hover,
.zoom-close:hover {
  background: var(--accent-hover);
}

.zoom-close {
  position: absolute;
  top: 16px;
  right: 16px;
  width: 40px;
  height: 40px;
  background: var(--surface);
  font-size: 16px;
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.25);
}

/* The card, floating: laid out at its panel width and scaled; dragged by its head, sized from its corners. */
.zoom-card {
  position: absolute;
  overflow: hidden auto;
  box-sizing: border-box;
  /* a border, not an inset shadow: the card's white inside would cover a shadow drawn under it */
  border: 1px solid var(--accent);
  border-radius: calc(var(--radius-card) * 0.85);
  background: var(--surface);
  box-shadow: 0 16px 48px rgb(0 0 0 / 0.35);
  animation: zoom-card-in 0.32s ease both;
}

@keyframes zoom-card-in {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
}

.zoom-card-inner {
  transform-origin: top left;
}

/* The frame draws the card's line; the card starts close under the grip, as the brush bar's tools do. */
.zoom-card-inner > .group {
  padding-top: 4px;
  box-shadow: none;
}

/* Its grip: the brush bar's line across the top (.bar-grip), kept in sight while a tall card scrolls; it and the
   card's head take the hand that picks it up. */
.zoom-grip {
  position: sticky;
  top: 0;
  z-index: 1;
  height: 16px;
  background: var(--surface);
}

.zoom-card :is(.zoom-grip, .card-head) {
  cursor: grab;
}

.zoom-card .card-head :is(button, input, label, .card-switch) {
  cursor: pointer;
}

.zoom-card.is-moving,
.zoom-card.is-moving * {
  cursor: grabbing !important;
}

.zoom-card.is-moving,
.zoom-card.is-resizing {
  user-select: none;
}

.zoom-handle {
  position: absolute;
  z-index: 1;
  width: 12px;
  height: 12px;
}

.zoom-handle.is-nw { top: 0; left: 0; cursor: nwse-resize; }
.zoom-handle.is-ne { top: 0; right: 0; cursor: nesw-resize; }
.zoom-handle.is-sw { bottom: 0; left: 0; cursor: nesw-resize; }
.zoom-handle.is-se { right: 0; bottom: 0; cursor: nwse-resize; }

@media (prefers-reduced-motion: reduce) {
  .zoom-view,
  .zoom-bar,
  .zoom-close,
  .zoom-poster.is-gliding {
    transition: none;
  }

  .zoom-card {
    animation: none;
  }
}

/* The options panel is built anew on each change and puts its own scroll back (app.js buildPanel); the browser's scroll
   anchoring then moved it again by the Lines card kept in sight at its top, a little further each time a Multi-style
   text's line was chosen (9 Nov 2026). */
#panel {
  overflow-anchor: none;
}
