/* Cross Stitch Pattern Maker
   ---------------------------------------------------------------------------
   The palette is a floss list: every color below is a real shade of thread,
   named by its brand and floss number, so the interface is made of the same
   material as the charts it produces. Cool navy ink over a pale ground keeps it reading as what a
   chart actually is — a technical drawing — rather than as generic craft warmth.

   Type carries the rest: a plain system sans for prose, and tracked-out
   monospace for every piece of chart data (floss numbers, sizes, counts). Chart
   data IS tabular data, and that contrast is the whole identity.               */

:root {
  --cs-ink:        #1B2853;  /* DMC 939  Navy Blue Very Dark */
  --cs-ink-soft:   #46527A;
  --cs-muted:      #6C7796;
  --cs-faint:      #98A2BC;
  --cs-paper:      #EEF1F7;
  --cs-surface:    #FFFFFF;  /* DMC B5200 Snow White */
  --cs-aida:       #F0EADA;  /* DMC Ecru — the bare fabric */
  --cs-thread:     #C72B3B;  /* DMC 321  Red */
  --cs-thread-dp:  #970B23;  /* DMC 816  Garnet */
  --cs-sage:       #396F52;  /* DMC 501  Blue Green Dark */
  --cs-gold:       #D0A53E;  /* DMC 729  Old Gold Medium */
  --cs-line:       #DBE1EE;
  --cs-line-soft:  #EAEEF6;

  --cs-radius:     14px;
  --cs-radius-sm:  9px;
  --cs-shadow:     0 1px 2px rgba(27, 40, 83, .06), 0 8px 22px rgba(27, 40, 83, .07);
  --cs-shadow-lg:  0 2px 6px rgba(27, 40, 83, .08), 0 22px 50px rgba(27, 40, 83, .14);

  --cs-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --cs-mono: ui-monospace, "Cascadia Mono", "SF Mono", "Roboto Mono", Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }

/* Class rules below set display on several elements that also get toggled with
   the hidden attribute, and a class beats the UA [hidden] rule. Settle it once. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  background: var(--cs-paper);
  color: var(--cs-ink);
  font-family: var(--cs-sans);
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
}

a { color: inherit; text-decoration: none; }
button { font: inherit; }

:focus-visible {
  outline: 2px solid var(--cs-thread);
  outline-offset: 2px;
  border-radius: 4px;
}

/* Bare fabric. Shows through wherever a chart has no stitch, and behind every
   preview, so an image always sits on the cloth it will be worked on. */
.cs-aida {
  background-color: var(--cs-aida);
  background-image:
    repeating-linear-gradient(0deg, rgba(90, 74, 44, .10) 0 1px, transparent 1px 7px),
    repeating-linear-gradient(90deg, rgba(90, 74, 44, .10) 0 1px, transparent 1px 7px);
}

/* --- Shell ---------------------------------------------------------------- */

.cs-shell {
  max-width: 1120px;
  margin: 0 auto;
  padding: clamp(1rem, 3vw, 2rem) clamp(1rem, 4vw, 2rem) 5rem;
}

.cs-top {
  display: flex;
  align-items: center;
  gap: .85rem;
  margin-bottom: clamp(1.5rem, 4vw, 2.5rem);
}

.cs-back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px; height: 38px;
  flex: none;
  border-radius: 50%;
  background: var(--cs-surface);
  border: 1px solid var(--cs-line);
  color: var(--cs-ink);
  transition: background .15s, transform .15s;
}
.cs-back:hover { background: #F7F9FD; transform: translateX(-2px); }
.cs-back svg { width: 17px; height: 17px; }

.cs-brand { display: flex; align-items: center; gap: .6rem; min-width: 0; }
.cs-mark { width: 30px; height: 30px; flex: none; }
.cs-brand-text { min-width: 0; }
.cs-brand-name {
  font-size: 1.02rem;
  font-weight: 650;
  letter-spacing: -.015em;
  line-height: 1.15;
  white-space: nowrap;
}
.cs-brand-sub {
  font-family: var(--cs-mono);
  font-size: .62rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--cs-faint);
  margin-top: .15rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cs-top-spacer { flex: 1 1 auto; }

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

.cs-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .45rem;
  padding: .62rem 1.05rem;
  border-radius: 999px;
  border: 1px solid transparent;
  font-size: .875rem;
  font-weight: 600;
  letter-spacing: -.005em;
  cursor: pointer;
  transition: background .15s, border-color .15s, color .15s, transform .1s, box-shadow .15s;
  white-space: nowrap;
}
.cs-btn:active { transform: translateY(1px); }
.cs-btn svg { width: 16px; height: 16px; flex: none; }
.cs-btn[disabled] { opacity: .45; cursor: not-allowed; transform: none; }

.cs-btn-primary {
  background: var(--cs-thread);
  color: #fff;
  box-shadow: 0 1px 2px rgba(151, 11, 35, .25);
}
.cs-btn-primary:hover:not([disabled]) { background: var(--cs-thread-dp); }

.cs-btn-ghost {
  background: var(--cs-surface);
  border-color: var(--cs-line);
  color: var(--cs-ink);
}
.cs-btn-ghost:hover:not([disabled]) { border-color: var(--cs-faint); background: #F8FAFE; }

.cs-btn-quiet { background: transparent; color: var(--cs-muted); }
.cs-btn-quiet:hover:not([disabled]) { color: var(--cs-ink); background: rgba(27, 40, 83, .05); }

.cs-btn-danger { background: transparent; color: var(--cs-thread-dp); border-color: transparent; }
.cs-btn-danger:hover:not([disabled]) { background: rgba(199, 43, 59, .09); }

/* --- Data type ------------------------------------------------------------ */

.cs-meta {
  font-family: var(--cs-mono);
  font-size: .705rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--cs-muted);
}
/* Tracked-out caps are for labels of a few words. Anything sentence-length
   stays in mono but drops the shouting. */
.cs-meta-plain {
  font-family: var(--cs-mono);
  font-size: .715rem;
  line-height: 1.6;
  letter-spacing: 0;
  text-transform: none;
  color: var(--cs-muted);
}
.cs-label {
  display: block;
  font-family: var(--cs-mono);
  font-size: .655rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--cs-faint);
  margin-bottom: .45rem;
}
.cs-floss-no {
  font-family: var(--cs-mono);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* --- Page heads ----------------------------------------------------------- */

.cs-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.4rem;
}
.cs-title {
  margin: 0;
  font-size: clamp(1.5rem, 4vw, 2rem);
  font-weight: 660;
  letter-spacing: -.03em;
  line-height: 1.1;
}
.cs-title-note { margin: .45rem 0 0; }

/* --- Gallery -------------------------------------------------------------- */

/* Two cards abreast even on a narrow phone — a gallery of one card per screen
   makes it impossible to compare patterns at a glance. */
.cs-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
  gap: clamp(.7rem, 2vw, 1.25rem);
}
@media (min-width: 620px) {
  .cs-grid { grid-template-columns: repeat(auto-fill, minmax(196px, 1fr)); }
}

.cs-card {
  display: flex;
  flex-direction: column;
  background: var(--cs-surface);
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius);
  overflow: hidden;
  box-shadow: var(--cs-shadow);
  transition: transform .16s, box-shadow .16s, border-color .16s;
}
.cs-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--cs-shadow-lg);
  border-color: #C3CCE0;
}

.cs-card-canvas {
  aspect-ratio: 4 / 3;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: .7rem;
  border-bottom: 1px solid var(--cs-line);
}
/* Charts are a handful of pixels wide, so they have to scale up to the tile —
   nearest-neighbour, because every stitch should stay a readable square. */
.cs-card-canvas img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  image-rendering: pixelated;
  display: block;
}

.cs-card-body { padding: .7rem .8rem .8rem; }
.cs-card-name {
  font-size: .93rem;
  font-weight: 600;
  letter-spacing: -.01em;
  line-height: 1.25;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cs-card-meta { margin-top: .35rem; display: flex; flex-wrap: wrap; gap: .1rem .5rem; }
.cs-card-meta span { white-space: nowrap; }
.cs-card-meta .cs-dim { color: var(--cs-ink-soft); }

/* New-pattern tile: a piece of blank fabric waiting for a chart. */
.cs-card-new {
  border-style: dashed;
  border-width: 2px;
  border-color: #C6CFE4;
  box-shadow: none;
  align-items: stretch;
}
.cs-card-new:hover { border-color: var(--cs-thread); box-shadow: var(--cs-shadow); }
.cs-card-new .cs-card-canvas {
  flex: 1 1 auto;
  flex-direction: column;
  gap: .6rem;
  border-bottom: 0;
  color: var(--cs-ink-soft);
}
.cs-card-new-plus {
  width: 38px; height: 38px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--cs-surface);
  border: 1px solid var(--cs-line);
  color: var(--cs-thread);
}
.cs-card-new-plus svg { width: 18px; height: 18px; }
.cs-card-new-text { font-size: .86rem; font-weight: 600; }

/* --- Empty state ---------------------------------------------------------- */

.cs-empty {
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius);
  background: var(--cs-surface);
  box-shadow: var(--cs-shadow);
  overflow: hidden;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
}
.cs-empty-swatch {
  min-height: 190px;
  display: grid;
  place-items: center;
  border-bottom: 1px solid var(--cs-line);
}
.cs-empty-swatch svg { width: 128px; height: 128px; }
.cs-empty-body { padding: clamp(1.4rem, 4vw, 2.2rem); }
.cs-empty-body h2 {
  margin: 0 0 .5rem;
  font-size: 1.28rem;
  font-weight: 650;
  letter-spacing: -.02em;
}
.cs-empty-body p {
  margin: 0 0 1.3rem;
  color: var(--cs-muted);
  font-size: .93rem;
  line-height: 1.55;
  max-width: 46ch;
}
@media (min-width: 720px) {
  .cs-empty { grid-template-columns: 300px minmax(0, 1fr); }
  .cs-empty-swatch { border-bottom: 0; border-right: 1px solid var(--cs-line); }
}

/* --- Panels --------------------------------------------------------------- */

.cs-panel {
  background: var(--cs-surface);
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius);
  box-shadow: var(--cs-shadow);
}
.cs-panel-pad { padding: clamp(1rem, 2.5vw, 1.4rem); }

/* A piece of cloth the picture sits on. The box takes the chart's own aspect
   ratio (set inline, or by the wizard as the size changes) so the preview fills
   it edge to edge instead of floating in the middle. */
.cs-stage {
  display: grid;
  place-items: center;
  padding: clamp(.7rem, 2vw, 1.1rem);
  border-radius: var(--cs-radius);
  border: 1px solid var(--cs-line);
  aspect-ratio: var(--cs-ar, 4 / 3);
  max-height: 64vh;
  overflow: hidden;
}
.cs-stage img,
.cs-stage canvas {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}
.cs-stage .cs-pixels { image-rendering: pixelated; }
.cs-stage.is-busy img,
.cs-stage.is-busy canvas { opacity: .45; transition: opacity .12s; }

/* --- Detail --------------------------------------------------------------- */

.cs-detail {
  display: grid;
  gap: clamp(1rem, 2.5vw, 1.5rem);
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}
@media (min-width: 900px) {
  .cs-detail { grid-template-columns: minmax(0, 1.65fr) minmax(280px, .95fr); }
}

.cs-stats { display: grid; grid-template-columns: 1fr 1fr; gap: .1rem 1rem; }
.cs-stat { padding: .55rem 0; border-bottom: 1px solid var(--cs-line-soft); }
.cs-stat:nth-last-child(-n+2) { border-bottom: 0; }
.cs-stat-value {
  font-family: var(--cs-mono);
  font-size: 1.02rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.01em;
}
.cs-stat-key {
  display: block;
  font-family: var(--cs-mono);
  font-size: .6rem;
  letter-spacing: .15em;
  text-transform: uppercase;
  color: var(--cs-faint);
  margin-top: .15rem;
}

.cs-actions { display: flex; flex-direction: column; gap: .5rem; margin-top: 1.1rem; }
.cs-actions .cs-btn { width: 100%; }

/* --- Individual color areas ----------------------------------------------- */
/* A reading, not a stat block: it belongs directly under the smoothing slider,
   which is the control it answers for, so it is boxed as an inset panel narrow
   enough to live inside the settings column. The detail page gets the same box
   at full width. */

.cs-regions {
  margin-top: .9rem;
  padding: .75rem .85rem .8rem;
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius-sm);
  background: #F7F9FD;
}
.cs-regions.is-wide { margin-top: 1.1rem; }
.cs-regions-title {
  display: block;
  font-family: var(--cs-mono);
  font-size: .6rem;
  letter-spacing: .15em;
  text-transform: uppercase;
  color: var(--cs-faint);
}
.cs-regions-figures {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: .5rem;
  margin: .55rem 0;
}
.cs-regions-figures.is-three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.cs-regions-figures.is-four { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 520px) {
  .cs-regions-figures.is-four { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.cs-region-fig {
  padding: .45rem .5rem .4rem;
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius-sm);
  background: var(--cs-surface);
  text-align: center;
}
.cs-region-fig b {
  display: block;
  font-family: var(--cs-mono);
  font-size: 1.02rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.01em;
}
.cs-region-fig span {
  display: block;
  font-family: var(--cs-mono);
  font-size: .555rem;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--cs-faint);
  margin-top: .1rem;
}
.cs-regions-note {
  margin: 0;
  font-size: .76rem;
  line-height: 1.45;
  color: var(--cs-muted);
}
.cs-regions-note + .cs-regions-note { margin-top: .45rem; }
/* A nudge rather than a fault: the chart is fine, it is just harder work than
   it needs to be. */
.cs-regions-warn { color: #8A6D0B; }
.cs-regions-hint { color: #2E5AA8; }
/* Standing on its own on the detail page it has the width to run the figures
   beside the words rather than above them. */
@media (min-width: 720px) {
  .cs-regions.is-wide {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-rows: auto auto;
    align-items: center;
    column-gap: 1.4rem;
    padding: .9rem 1.1rem;
  }
  .cs-regions.is-wide .cs-regions-figures {
    grid-column: 2;
    grid-row: 1 / span 2;
    margin: 0;
    grid-template-columns: repeat(2, 7.2rem);
  }
  .cs-regions.is-wide .cs-regions-note { margin-top: .25rem; }
  .cs-regions.is-wide .cs-regions-title { display: flex; align-items: center; gap: .5rem; }
}
@media (min-width: 980px) {
  .cs-regions.is-wide .cs-regions-figures.is-four {
    grid-template-columns: repeat(4, 7rem);
  }
}

/* --- Floss list ----------------------------------------------------------- */

.cs-floss-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin: 2.2rem 0 .8rem;
}
.cs-floss-head h2 {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 650;
  letter-spacing: -.02em;
}

/* Three options rather than the two the floss box offers, so the control is
   given room to keep each label on one line. */
.cs-sort-seg { grid-auto-columns: auto; }
.cs-sort-seg .cs-seg-opt > span { padding-inline: .7rem; }
@media (max-width: 620px) {
  .cs-sort-seg { grid-auto-columns: 1fr; width: 100%; }
  .cs-sort-seg .cs-seg-opt > span { padding-inline: .4rem; }
}

.cs-floss-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
  gap: 0 1.6rem;
  background: var(--cs-surface);
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius);
  padding: .35rem clamp(.8rem, 2vw, 1.2rem);
  box-shadow: var(--cs-shadow);
}
.cs-floss-row {
  display: grid;
  grid-template-columns: 30px 3.4rem minmax(0, 1fr) auto;
  align-items: center;
  gap: .7rem;
  padding: .48rem 0;
  border-bottom: 1px solid var(--cs-line-soft);
}
.cs-floss-row:last-child { border-bottom: 0; }

/* The swatch repeats exactly what a cell looks like on the chart: floss color
   plus its symbol, in the same contrast the PDF uses. */
.cs-swatch {
  width: 30px; height: 30px;
  border-radius: 5px;
  display: grid;
  place-items: center;
  box-shadow: inset 0 0 0 1px rgba(27, 40, 83, .16);
}
.cs-swatch .cs-symbol { width: 19px; height: 19px; display: block; }
.cs-swatch.is-light .cs-symbol { color: #000; }
.cs-swatch.is-dark  .cs-symbol { color: #fff; }

.cs-floss-name {
  font-size: .855rem;
  line-height: 1.25;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--cs-ink-soft);
}
.cs-floss-count {
  font-family: var(--cs-mono);
  font-size: .76rem;
  font-variant-numeric: tabular-nums;
  color: var(--cs-muted);
  text-align: right;
}

/* Which brand a number belongs to. Ninety-seven Anchor numbers are also DMC
   numbers and name quite different shades, so the moment a list holds both the
   number alone stops being an answer — and the moment it holds one, saying so
   on every row is noise the heading above already covers.
   It sits *over* the number rather than beside it, in the same column: a floss
   list is mostly shade names, and they are the first thing a third column would
   take the width out of. */
.cs-floss-id { display: grid; align-content: center; min-width: 0; }
.cs-floss-brand {
  display: none;
  font-size: .6rem;
  font-weight: 650;
  line-height: 1.25;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--cs-faint);
}
.is-mixed .cs-floss-brand { display: block; }
/* The one place the brand always shows: confirming what a typed number is
   about to add is the whole job of that row. */
.cs-match .cs-floss-brand { display: block; }
.cs-floss-share {
  grid-column: 2 / -1;
  height: 3px;
  border-radius: 2px;
  background: var(--cs-line-soft);
  overflow: hidden;
  margin-top: -.28rem;
}
.cs-floss-share i { display: block; height: 100%; border-radius: 2px; }

/* --- The chart's floss list, once it can be edited ------------------------ */
/* Two controls per row and they are always drawn, on or off: a lock that only
   appears under the pointer is a lock half the stitchers never find, and on a
   phone there is no pointer to appear under. The two are kept apart by colour
   as much as by position — sage for the one that keeps a colour, the list's
   own red for the one that takes it away. */

.cs-palette-row { grid-template-columns: 26px 30px 3.4rem minmax(0, 1fr) auto 26px; }
.cs-palette-row .cs-floss-share { grid-column: 3 / -1; }
.cs-floss-list.is-busy { opacity: .55; pointer-events: none; }

.cs-lock {
  width: 26px; height: 26px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--cs-faint);
  cursor: pointer;
  transition: background .15s, color .15s;
}
.cs-lock svg { width: 15px; height: 15px; }
.cs-lock:hover { background: rgba(27, 40, 83, .07); color: var(--cs-ink-soft); }
.cs-lock.is-on { color: var(--cs-sage); }
.cs-lock.is-on:hover { background: rgba(57, 111, 82, .12); color: var(--cs-sage); }

/* Held colors gather at the head of the list whichever order the rest is read
   in, so they are given a ground of their own: the block at the top is the part
   of the palette the stitcher decided rather than the picture. */
.cs-palette-row.is-locked {
  background: rgba(57, 111, 82, .06);
  margin-inline: -.5rem;
  padding-inline: .5rem;
  border-radius: 7px;
  border-bottom-color: transparent;
}

/* Locked, but the picture holds nothing near it, so no stitch is worked in it.
   The row is here for one reason: the lock is on it. */
.cs-palette-row.is-unused .cs-swatch,
.cs-palette-row.is-unused .cs-floss-no,
.cs-palette-row.is-unused .cs-floss-name { opacity: .55; }
.cs-palette-row.is-unused .cs-floss-count {
  font-size: .62rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--cs-faint);
}

/* --- Below the list: what was taken out, and what to put in --------------- */

/* The last thing on the page, and the navigation is sticky over the foot of it,
   so it is given room to sit clear of it. */
.cs-palette-tools {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: .9rem 1.4rem;
  margin: .95rem 0 .6rem;
}
.cs-strips { display: grid; gap: .7rem; min-width: 0; flex: 1 1 14rem; }
.cs-removed { display: grid; gap: .4rem; min-width: 0; }
.cs-removed-head {
  font-family: var(--cs-mono);
  font-size: .62rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--cs-muted);
}
.cs-removed-strip { display: flex; flex-wrap: wrap; gap: .35rem; }
/* Dashed and drained of its color: a skein set aside, not one on the table. */
.cs-removed-chip {
  display: inline-flex;
  align-items: center;
  gap: .38rem;
  padding: .22rem .55rem .22rem .26rem;
  border: 1px dashed var(--cs-line);
  border-radius: 999px;
  background: var(--cs-surface);
  color: var(--cs-muted);
  font-family: var(--cs-mono);
  font-size: .74rem;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: border-color .15s, color .15s, border-style .15s;
}
.cs-removed-chip i {
  width: 16px; height: 16px;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px rgba(27, 40, 83, .16);
  opacity: .45;
  transition: opacity .15s;
}
.cs-removed-chip:hover {
  border-style: solid;
  border-color: var(--cs-sage);
  color: var(--cs-ink);
}
.cs-removed-chip:hover i { opacity: 1; }

/* Not struck out, only outbid: it is still in the palette, so it keeps a solid
   edge, and clicking it holds it rather than restoring it. */
.cs-spare-chip { border-style: solid; }
.cs-spare-chip:hover { border-color: var(--cs-sage); }

.cs-add-color { display: grid; gap: .45rem; justify-items: start; flex: 0 1 20rem; min-width: 0; }
.cs-add-color .cs-add-field { width: 100%; }
/* Opening upward: this sits at the very foot of the page, under a sticky bar,
   so a list dropped below it would be a list nobody can reach. */
.cs-add-color .cs-suggest { top: auto; bottom: calc(100% + 6px); }
.cs-mini-btn { padding: .34rem .72rem; font-size: .8rem; }
.cs-mini-btn svg { width: 14px; height: 14px; }

/* A control that would choose the colors again, while a palette chosen by hand
   is standing. It still works — it just costs that palette, and the rule is
   what stops the confirmation being a surprise when it arrives. */
[data-soft-lock].is-soft-locked {
  border-left: 2px solid rgba(208, 165, 62, .6);
  padding-left: .8rem;
  margin-left: -.8rem;
}

/* --- Wizard progress ------------------------------------------------------ */
/* Five cells of the chart itself. Each completed step is worked with a stitch. */

.cs-steps {
  display: flex;
  align-items: center;
  gap: .3rem;
  margin-bottom: clamp(1.2rem, 3vw, 1.8rem);
}
.cs-step { display: flex; align-items: center; gap: .3rem; min-width: 0; }
.cs-step-cell {
  width: 26px; height: 26px;
  flex: none;
  border: 1.5px solid var(--cs-line);
  border-radius: 3px;
  background: var(--cs-surface);
  display: grid;
  place-items: center;
  transition: border-color .2s, background .2s;
}
.cs-step-cell svg { width: 100%; height: 100%; display: block; }
.cs-step-cell .cs-stitch { stroke: var(--cs-thread); stroke-width: 15; stroke-linecap: round; fill: none;
  opacity: 0; transition: opacity .25s; }
.cs-step.is-done .cs-step-cell { border-color: var(--cs-thread); background: #FDF3F4; }
.cs-step.is-done .cs-stitch { opacity: 1; }
.cs-step.is-current .cs-step-cell { border-color: var(--cs-ink); box-shadow: 0 0 0 3px rgba(27, 40, 83, .09); }
.cs-step-name {
  font-family: var(--cs-mono);
  font-size: .62rem;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--cs-faint);
  padding-right: .35rem;
  white-space: nowrap;
}
.cs-step.is-current .cs-step-name { color: var(--cs-ink); }
.cs-step-rule { flex: 1 1 14px; min-width: 8px; height: 1px; background: var(--cs-line); }
@media (max-width: 700px) {
  .cs-step-name { display: none; }
  .cs-step.is-current .cs-step-name { display: block; }
}

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

.cs-wizard-grid {
  display: grid;
  gap: clamp(1rem, 2.5vw, 1.5rem);
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}
@media (min-width: 880px) {
  .cs-wizard-grid { grid-template-columns: minmax(0, 1.5fr) minmax(300px, 1fr); }
  .cs-wizard-grid.is-wide { grid-template-columns: minmax(0, 1.9fr) minmax(280px, 1fr); }
}

.cs-step-panel { display: none; }
.cs-step-panel.is-active { display: block; }

.cs-step-lede { margin: 0 0 1.2rem; color: var(--cs-muted); font-size: .93rem; line-height: 1.55; max-width: 58ch; }

/* --- Drop zone ------------------------------------------------------------ */

.cs-drop {
  position: relative;
  border: 2px dashed #C6CFE4;
  border-radius: var(--cs-radius);
  padding: clamp(1.6rem, 5vw, 3rem) 1.2rem;
  text-align: center;
  cursor: pointer;
  transition: border-color .15s, background .15s;
  display: block;
  width: 100%;
}
.cs-drop:hover, .cs-drop.is-over { border-color: var(--cs-thread); }
.cs-drop.is-over { background: #FDF3F4; }
.cs-drop input[type="file"] { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.cs-drop-icon { color: var(--cs-thread); margin-bottom: .7rem; display: block; }
.cs-drop-icon svg { width: 32px; height: 32px; }
.cs-drop-title { font-weight: 600; font-size: .98rem; }
.cs-drop-hint { margin-top: .3rem; font-size: .82rem; color: var(--cs-muted); }

/* Once there is a picture the crop is what matters, so the invitation to choose
   one shrinks to a line and hands the screen over to it — which on a phone is
   the difference between the crop being on screen and being below the fold. */
.cs-drop.is-compact {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .65rem .9rem;
  text-align: left;
  border-width: 1px;
}
.cs-drop.is-compact .cs-drop-icon { margin-bottom: 0; line-height: 0; }
.cs-drop.is-compact .cs-drop-icon svg { width: 19px; height: 19px; }
.cs-drop.is-compact .cs-drop-title { font-size: .86rem; }
.cs-drop.is-compact .cs-drop-hint { display: none; }

/* --- Form controls -------------------------------------------------------- */

.cs-field { margin-bottom: 1.15rem; }
.cs-field:last-child { margin-bottom: 0; }
/* A hidden field is still the last child, so the one above it would keep a gap
   below it that now has nothing under it. */
.cs-field:has(+ .cs-field[hidden]:last-child) { margin-bottom: 0; }

.cs-input {
  width: 100%;
  padding: .6rem .75rem;
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius-sm);
  background: var(--cs-surface);
  color: var(--cs-ink);
  font-size: .95rem;
  font-family: inherit;
  transition: border-color .15s, box-shadow .15s;
}
.cs-input:focus {
  outline: none;
  border-color: var(--cs-ink);
  box-shadow: 0 0 0 3px rgba(27, 40, 83, .1);
}

.cs-size-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: end;
  gap: .55rem;
}
.cs-num {
  font-family: var(--cs-mono);
  font-variant-numeric: tabular-nums;
  text-align: center;
  padding: .55rem .35rem;
}
/* The link is a statement now, not a switch: the crop chose the proportions in
   step one, so width and height can only move together. */
.cs-size-link {
  display: grid;
  place-items: center;
  width: 30px; height: 38px;
  color: var(--cs-thread);
}
.cs-size-link svg { width: 17px; height: 17px; }

/* A circle has one measurement, so the pair collapses to a single field. */
.cs-size-row.is-round { grid-template-columns: minmax(0, 1fr); }
.cs-size-row.is-round > .cs-size-link,
.cs-size-row.is-round > #cs-height-col { display: none; }

.cs-switch-row {
  display: flex;
  align-items: flex-start;
  gap: .75rem;
  padding: .7rem 0;
  border-top: 1px solid var(--cs-line-soft);
}
.cs-switch-copy { flex: 1 1 auto; min-width: 0; }
.cs-switch-title { font-size: .89rem; font-weight: 600; }
.cs-switch-note { font-size: .78rem; color: var(--cs-muted); line-height: 1.45; margin-top: .12rem; }

.cs-switch { position: relative; flex: none; width: 42px; height: 24px; margin-top: .1rem; }
.cs-switch input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.cs-switch i {
  display: block;
  width: 100%; height: 100%;
  border-radius: 999px;
  background: #C9D2E4;
  transition: background .18s;
  pointer-events: none;
}
.cs-switch i::after {
  content: "";
  position: absolute;
  top: 3px; left: 3px;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(27, 40, 83, .3);
  transition: transform .18s;
}
.cs-switch input:checked + i { background: var(--cs-thread); }
.cs-switch input:checked + i::after { transform: translateX(18px); }
.cs-switch input:focus-visible + i { box-shadow: 0 0 0 3px rgba(199, 43, 59, .3); }

.cs-slider-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .75rem;
  margin-bottom: .35rem;
}
.cs-slider-value {
  font-family: var(--cs-mono);
  font-size: .92rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.cs-range { width: 100%; -webkit-appearance: none; appearance: none; background: transparent; margin: .3rem 0; }
.cs-range::-webkit-slider-runnable-track { height: 4px; border-radius: 2px; background: #D4DCEC; }
.cs-range::-moz-range-track { height: 4px; border-radius: 2px; background: #D4DCEC; }
.cs-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 19px; height: 19px;
  margin-top: -7.5px;
  border-radius: 50%;
  background: var(--cs-thread);
  border: 3px solid #fff;
  box-shadow: 0 1px 4px rgba(151, 11, 35, .4);
  cursor: pointer;
}
.cs-range::-moz-range-thumb {
  width: 13px; height: 13px;
  border-radius: 50%;
  background: var(--cs-thread);
  border: 3px solid #fff;
  box-shadow: 0 1px 4px rgba(151, 11, 35, .4);
  cursor: pointer;
}
.cs-range-ends {
  display: flex;
  justify-content: space-between;
  font-family: var(--cs-mono);
  font-size: .6rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--cs-faint);
}

/* --- Wizard footer -------------------------------------------------------- */

.cs-nav {
  position: sticky;
  bottom: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: .6rem;
  margin: 1.6rem calc(-1 * clamp(1rem, 4vw, 2rem)) 0;
  padding: .85rem clamp(1rem, 4vw, 2rem) max(.85rem, env(safe-area-inset-bottom));
  background: rgba(238, 241, 247, .93);
  backdrop-filter: blur(8px);
  border-top: 1px solid var(--cs-line);
}
.cs-nav-spacer { flex: 1 1 auto; min-width: 0; }
.cs-nav .cs-btn-primary { min-width: 9rem; }
/* A phone in portrait has room for two buttons and the score disc between them,
   and only if the buttons give up their comfortable width first. Nothing here
   is allowed to wrap onto a second line: the footer is what the thumb aims at. */
@media (max-width: 560px) {
  .cs-nav { gap: .35rem; }
  .cs-nav .cs-btn { padding: .58rem .8rem; font-size: .84rem; min-width: 0; }
  .cs-nav .cs-btn-primary { min-width: 0; }
  .cs-score-dial { width: 34px; height: 34px; }
  .cs-score-thumb { width: 18px; height: 18px; }
}

/* --- Notices -------------------------------------------------------------- */

.cs-note {
  display: flex;
  gap: .55rem;
  align-items: flex-start;
  padding: .7rem .85rem;
  border-radius: var(--cs-radius-sm);
  font-size: .84rem;
  line-height: 1.5;
  margin-bottom: 1rem;
}
.cs-note-warn { background: #FDF3F4; color: var(--cs-thread-dp); border: 1px solid #F3D3D7; }
.cs-note-info { background: #F1F5FC; color: var(--cs-ink-soft); border: 1px solid #DBE4F4; }
.cs-note svg { width: 15px; height: 15px; flex: none; margin-top: .12rem; }

/* --- Busy ----------------------------------------------------------------- */

.cs-busy {
  display: grid;
  place-items: center;
  gap: .9rem;
  padding: 3.5rem 1rem;
  text-align: center;
}
.cs-spinner {
  width: 30px; height: 30px;
  border-radius: 50%;
  border: 3px solid var(--cs-line);
  border-top-color: var(--cs-thread);
  animation: cs-spin .8s linear infinite;
}
@keyframes cs-spin { to { transform: rotate(360deg); } }

/* --- Dialog --------------------------------------------------------------- */

.cs-dialog {
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius);
  box-shadow: var(--cs-shadow-lg);
  padding: 0;
  max-width: 27rem;
  width: calc(100% - 2rem);
  color: var(--cs-ink);
  background: var(--cs-surface);
}
.cs-dialog::backdrop { background: rgba(27, 40, 83, .35); backdrop-filter: blur(2px); }
.cs-dialog-body { padding: 1.3rem 1.4rem 1rem; }
.cs-dialog-body h3 { margin: 0 0 .5rem; font-size: 1.05rem; font-weight: 650; letter-spacing: -.02em; }
.cs-dialog-body p { margin: 0; color: var(--cs-muted); font-size: .89rem; line-height: 1.55; }
.cs-dialog-foot {
  display: flex;
  justify-content: flex-end;
  gap: .5rem;
  padding: .9rem 1.4rem 1.2rem;
}

/* --- Download panel ------------------------------------------------------- */

/* The pages are a choice, not a list, so each one is a switch you can see the
   state of at a glance: worked (on) or bare (off), the same language the rest of
   the app speaks. The last one on is disabled rather than merely refused. */
.cs-pdf-pages { display: grid; gap: .45rem; margin: 1.2rem 0 .55rem; text-align: left; }
.cs-pdf-page { display: block; position: relative; cursor: pointer; }
.cs-pdf-page input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.cs-pdf-page-face {
  display: flex;
  gap: .7rem;
  align-items: flex-start;
  padding: .55rem .8rem;
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius-sm);
  background: var(--cs-surface);
  font-size: .875rem;
  transition: border-color .15s, background .15s, opacity .15s;
}
.cs-pdf-page-tick {
  width: 19px; height: 19px;
  flex: none;
  margin-top: .06rem;
  display: grid;
  place-items: center;
  border: 1.5px solid var(--cs-line);
  border-radius: 3px;
  color: transparent;
  transition: border-color .15s, background .15s, color .15s;
}
.cs-pdf-page-tick svg { width: 13px; height: 13px; display: block; }
.cs-pdf-page-text { display: grid; gap: .1rem; min-width: 0; }
.cs-pdf-page-text b { font-weight: 600; }
.cs-pdf-page-text span { color: var(--cs-muted); font-size: .8rem; line-height: 1.45; }
.cs-pdf-page input:checked + .cs-pdf-page-face { border-color: #C9D3E8; }
.cs-pdf-page input:checked + .cs-pdf-page-face .cs-pdf-page-tick {
  border-color: var(--cs-thread);
  background: var(--cs-thread);
  color: #fff;
}
.cs-pdf-page input:not(:checked) + .cs-pdf-page-face { opacity: .58; }
.cs-pdf-page input:disabled { cursor: default; }
.cs-pdf-page input:disabled + .cs-pdf-page-face { cursor: default; }
.cs-pdf-page input:focus-visible + .cs-pdf-page-face {
  outline: 2px solid var(--cs-thread);
  outline-offset: 1px;
}
.cs-pdf-page:hover input:not(:disabled) + .cs-pdf-page-face { border-color: var(--cs-faint); }

.cs-done-mark { display: grid; place-items: center; margin-bottom: 1rem; }
.cs-done-mark svg { width: 54px; height: 54px; }

/* --- Compact top-bar buttons ---------------------------------------------- */
/* Two actions have to fit beside the brand on a phone, so below the breakpoint
   they drop to their icons. The count stays: it is the whole point of the
   button, and it is one glyph wide. */

.cs-count-pill {
  font-family: var(--cs-mono);
  font-size: .68rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  padding: .06rem .4rem;
  border-radius: 999px;
  background: rgba(27, 40, 83, .08);
  color: var(--cs-ink-soft);
}
@media (max-width: 560px) {
  .cs-btn-compact .cs-btn-text { display: none; }
  .cs-btn-compact { padding-left: .8rem; padding-right: .8rem; }
  .cs-top { gap: .5rem; }
}

/* --- Control groups ------------------------------------------------------- */
/* The settings panel now carries three kinds of decision — how big, how the
   picture is treated, what it is stitched in. Hairline groups keep them from
   reading as one long undifferentiated list. */

.cs-group {
  margin-top: 1.4rem;
  padding-top: 1.15rem;
  border-top: 1px solid var(--cs-line);
}
.cs-group-head {
  display: block;
  font-family: var(--cs-mono);
  font-size: .6rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--cs-faint);
  margin-bottom: .8rem;
}

.cs-quiet-link {
  font-family: var(--cs-mono);
  font-size: .655rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--cs-thread);
  white-space: nowrap;
}
.cs-quiet-link:hover { color: var(--cs-thread-dp); text-decoration: underline; }

/* --- Segmented control ---------------------------------------------------- */

.cs-seg {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 3px;
  padding: 3px;
  border-radius: 999px;
  background: #E6EBF5;
  border: 1px solid var(--cs-line);
}
.cs-seg-opt { position: relative; min-width: 0; }
.cs-seg-opt input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.cs-seg-opt > span {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .35rem;
  padding: .42rem .55rem;
  border-radius: 999px;
  font-size: .82rem;
  font-weight: 600;
  color: var(--cs-muted);
  white-space: nowrap;
  transition: background .15s, color .15s, box-shadow .15s;
}
.cs-seg-opt input:checked + span {
  background: var(--cs-surface);
  color: var(--cs-ink);
  box-shadow: 0 1px 3px rgba(27, 40, 83, .16);
}
.cs-seg-opt input:disabled { cursor: not-allowed; }
.cs-seg-opt input:disabled + span { opacity: .42; }
.cs-seg-opt input:focus-visible + span { outline: 2px solid var(--cs-thread); outline-offset: 1px; }
.cs-seg-opt b {
  font-family: var(--cs-mono);
  font-size: .68rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  padding: .04rem .34rem;
  border-radius: 999px;
  background: rgba(27, 40, 83, .09);
}
.cs-seg-opt input:checked + span b { background: var(--cs-thread); color: #fff; }

/* The owned flosses themselves, under the choice that uses them. */
.cs-owned-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  margin-top: .6rem;
  max-height: 48px;
  overflow: hidden;
}
.cs-owned-strip i {
  width: 13px; height: 13px;
  border-radius: 3px;
  box-shadow: inset 0 0 0 1px rgba(27, 40, 83, .18);
}

/* --- Bipolar slider ------------------------------------------------------- */
/* A tick behind the track marks the untouched middle; the slider snaps to it. */

.cs-range-center {
  background-image: linear-gradient(var(--cs-faint), var(--cs-faint));
  background-size: 2px 11px;
  background-position: center center;
  background-repeat: no-repeat;
}
.cs-reset {
  border: 0;
  padding: 0 .4rem;
  background: transparent;
  font-family: var(--cs-mono);
  font-size: .6rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--cs-thread);
  cursor: pointer;
}
.cs-reset:hover { color: var(--cs-thread-dp); text-decoration: underline; }
.cs-inline-swatch { display:inline-block; width:1.35em; height:.8em; margin:0 .12em; border-radius:2px; box-shadow:inset 0 0 0 1px rgba(27,40,83,.25); vertical-align:-.08em; }

/* --- Step two preview ----------------------------------------------------- */

/* Caption and view control on one line: the caption says what is being shown,
   so it doubles as the switch's own feedback. */
.cs-preview-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .8rem;
  margin-top: .65rem;
}
.cs-preview-bar .cs-meta-plain { margin: 0; min-width: 0; }

.cs-inline-switch {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  flex: none;
  cursor: pointer;
}
.cs-inline-switch > span:first-child {
  font-family: var(--cs-mono);
  font-size: .625rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--cs-faint);
  white-space: nowrap;
  transition: color .15s;
}
.cs-inline-switch:has(input:checked) > span:first-child { color: var(--cs-ink); }
.cs-inline-switch .cs-switch { width: 36px; height: 21px; margin-top: 0; }
.cs-inline-switch .cs-switch i::after { width: 15px; height: 15px; }
.cs-inline-switch .cs-switch input:checked + i::after { transform: translateX(15px); }
/* --- Keeping the picture in sight ----------------------------------------- */
/*
   Every control in steps two and three is judged by what it does to the picture
   beside it, so the picture has to stay on screen while they move. A sticky box
   can only travel inside its own containing block, and getting that block right
   is the whole of this:

   * The picture and the controls it answers to are one box — `.cs-wizard-grid`
     in step two, `.cs-chart-main` in step three. Sticky inside that box means
     the picture is held for exactly as long as there is a control left to move,
     and lets go at the floss list, which is not a control at all.
   * On a phone that box is plain block flow — NOT a one-column grid. A grid
     item's containing block is its own grid area, which at one column is
     exactly its own height, so a sticky preview inside one cannot move at all.
     (`display: contents` on the grid would hand the children to the panel and
     work in Chromium, but WebKit drops sticky positioning when it does, which
     is precisely the phones this matters most on.)
   * On a desktop the box is a two-column grid. Chromium clamps a sticky grid
     item to the grid *container* rather than to its own grid area, so anything
     laid out after the two columns has to be outside that container or the
     pinned picture rides down over it.

   The picture's wrapper is full width and opaque either way, so the settings
   pass *under* it rather than beside it — the chart is sized by its own aspect
   ratio and rarely fills the column it sits in. */

.cs-preview-col .cs-stage,
.cs-chart-col .cs-stage { margin-inline: auto; }

.cs-chart-main {
  display: grid;
  gap: clamp(1rem, 2.5vw, 1.5rem);
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}

@media (max-width: 879px) {
  .cs-step-panel > .cs-wizard-grid,
  .cs-chart-main { display: block; }
  .cs-step-panel > .cs-wizard-grid > * + *,
  .cs-chart-main > * + * { margin-top: clamp(1rem, 2.5vw, 1.5rem); }

  .cs-preview-col,
  .cs-chart-col {
    position: sticky;
    position: -webkit-sticky;
    top: 0;
    z-index: 3;
    background: var(--cs-paper);
    padding: .4rem 0 .55rem;
    margin-top: -.4rem;
  }
  .cs-preview-col .cs-stage,
  .cs-chart-col .cs-stage { max-height: 34vh; }
  /* What passes under the pinned picture fades out rather than being sliced
     through the middle of a line. */
  .cs-preview-col::after,
  .cs-chart-col::after {
    content: "";
    position: absolute;
    left: 0; right: 0; top: 100%;
    height: 12px;
    background: linear-gradient(var(--cs-paper), rgba(238, 241, 247, 0));
    pointer-events: none;
  }
}

@media (min-width: 880px) {
  .cs-chart-main { grid-template-columns: minmax(0, 1.9fr) minmax(280px, 1fr); }
  .cs-preview-col,
  .cs-chart-col { position: sticky; top: 1rem; }
  .cs-preview-col .cs-stage,
  .cs-chart-col .cs-stage { max-height: min(60vh, calc(100vh - 6rem)); }
}

/* --- Upload progress ------------------------------------------------------ */
/* A big picture on a thin line takes long enough that silence reads as a broken
   page. Three stages are named because they take very different lengths of
   time, and a bar that only moves during one of them looks stuck for the rest. */

.cs-upload {
  margin-top: 1rem;
  padding: .8rem .9rem .7rem;
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius);
  background: var(--cs-surface);
  box-shadow: var(--cs-shadow);
}
.cs-upload-top { display: flex; align-items: center; gap: .7rem; }
.cs-upload-top .cs-spinner { width: 20px; height: 20px; border-width: 2.5px; flex: none; }
.cs-upload-text { flex: 1 1 auto; min-width: 0; }
.cs-upload-name {
  display: block;
  font-size: .875rem;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cs-upload-text .cs-meta-plain { display: block; }
.cs-upload-pct {
  flex: none;
  font-family: var(--cs-mono);
  font-size: .9rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.cs-upload-track {
  height: 5px;
  margin: .6rem 0 .45rem;
  border-radius: 3px;
  background: var(--cs-line-soft);
  overflow: hidden;
}
.cs-upload-track i {
  display: block;
  height: 100%;
  width: 0;
  border-radius: 3px;
  background: var(--cs-thread);
  transition: width .2s ease-out;
}
/* Between "sent" and "the server has read it" there is no number to report, so
   the bar says so by moving rather than by sitting full. */
.cs-upload-track.is-waiting i {
  width: 100% !important;
  background: linear-gradient(90deg,
    var(--cs-thread) 0 25%, #E8949D 25% 50%, var(--cs-thread) 50% 75%, #E8949D 75% 100%);
  background-size: 200% 100%;
  animation: cs-slide 1.1s linear infinite;
}
@keyframes cs-slide { to { background-position: -200% 0; } }
.cs-upload-foot { display: flex; align-items: center; justify-content: space-between; gap: .8rem; }

/* --- Crop ----------------------------------------------------------------- */
/* The stage carries the picture's own aspect ratio, so the crop rectangle can
   be positioned in plain percentages and every number on screen is a fraction
   of the image with no letterboxing to correct for. */

.cs-crop-frame {
  width: 100%;
  max-width: calc(52vh * var(--cs-arn, 1.3333) + 30px);
  margin: 1rem auto 0;
  padding: 15px;
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius);
}
.cs-crop-stage {
  position: relative;
  width: 100%;
  aspect-ratio: var(--cs-ar, 4 / 3);
  touch-action: none;          /* a drag crops, it does not scroll the page */
  cursor: crosshair;
  user-select: none;
  -webkit-user-select: none;
}
.cs-crop-stage img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  display: block;
  -webkit-user-drag: none;
}

/* The dimming is one shadow around a box tracking the crop, in a layer that
   clips: the four surrounding panels never have to be kept in step, and the
   handles stay in the unclipped layer above so a full-frame crop still has
   something to take hold of. */
.cs-crop-shade { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.cs-crop-shade i { position: absolute; box-shadow: 0 0 0 9999px rgba(15, 22, 46, .5); }

.cs-crop-box {
  position: absolute;
  cursor: move;
  outline: 1px solid rgba(255, 255, 255, .95);
}
.cs-crop-box:focus-visible { outline: 2px solid var(--cs-thread); outline-offset: 0; }

.cs-crop-thirds {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: .55;
  background-image:
    linear-gradient(rgba(255, 255, 255, .8) 0 100%),
    linear-gradient(rgba(255, 255, 255, .8) 0 100%),
    linear-gradient(rgba(255, 255, 255, .8) 0 100%),
    linear-gradient(rgba(255, 255, 255, .8) 0 100%);
  background-repeat: no-repeat;
  background-size: 100% 1px, 100% 1px, 1px 100%, 1px 100%;
  background-position: 0 33.33%, 0 66.66%, 33.33% 0, 66.66% 0;
}

.cs-crop-grip {
  position: absolute;
  width: 22px; height: 22px;
  touch-action: none;
}
.cs-crop-grip::after {
  content: "";
  position: absolute;
  inset: 6px;
  background: var(--cs-surface);
  border-radius: 2px;
  box-shadow: 0 1px 3px rgba(15, 22, 46, .5);
}
.cs-crop-grip[data-grip="nw"] { top: -11px; left: -11px; cursor: nwse-resize; }
.cs-crop-grip[data-grip="ne"] { top: -11px; right: -11px; cursor: nesw-resize; }
.cs-crop-grip[data-grip="sw"] { bottom: -11px; left: -11px; cursor: nesw-resize; }
.cs-crop-grip[data-grip="se"] { bottom: -11px; right: -11px; cursor: nwse-resize; }
.cs-crop-grip[data-grip="n"] { top: -11px; left: 50%; margin-left: -11px; cursor: ns-resize; }
.cs-crop-grip[data-grip="s"] { bottom: -11px; left: 50%; margin-left: -11px; cursor: ns-resize; }
.cs-crop-grip[data-grip="w"] { left: -11px; top: 50%; margin-top: -11px; cursor: ew-resize; }
.cs-crop-grip[data-grip="e"] { right: -11px; top: 50%; margin-top: -11px; cursor: ew-resize; }
.cs-crop-edge::after { inset: 8px 4px; }
.cs-crop-grip[data-grip="w"]::after,
.cs-crop-grip[data-grip="e"]::after { inset: 4px 8px; }
/* A fingertip is nowhere near as precise as a cursor, so the targets grow
   without the marks themselves getting heavier. */
@media (pointer: coarse) {
  .cs-crop-grip { width: 34px; height: 34px; }
  .cs-crop-grip[data-grip="nw"], .cs-crop-grip[data-grip="ne"] { top: -17px; }
  .cs-crop-grip[data-grip="sw"], .cs-crop-grip[data-grip="se"] { bottom: -17px; }
  .cs-crop-grip[data-grip="nw"], .cs-crop-grip[data-grip="sw"] { left: -17px; }
  .cs-crop-grip[data-grip="ne"], .cs-crop-grip[data-grip="se"] { right: -17px; }
  .cs-crop-grip[data-grip="n"] { top: -17px; margin-left: -17px; }
  .cs-crop-grip[data-grip="s"] { bottom: -17px; margin-left: -17px; }
  .cs-crop-grip[data-grip="w"] { left: -17px; margin-top: -17px; }
  .cs-crop-grip[data-grip="e"] { right: -17px; margin-top: -17px; }
  .cs-crop-grip::after { inset: 12px; }
  .cs-crop-edge::after { inset: 14px 8px; }
  .cs-crop-grip[data-grip="w"]::after,
  .cs-crop-grip[data-grip="e"]::after { inset: 8px 14px; }
}
.cs-crop-stage.is-dragging .cs-crop-box { outline-color: #fff; }

.cs-crop-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .5rem .8rem;
  margin-top: .7rem;
}
.cs-crop-bar .cs-meta-plain { flex: 1 1 auto; min-width: 0; }
.cs-crop-bar .cs-reset { flex: none; }
.cs-ratio-seg { flex: none; grid-auto-columns: auto; }
.cs-ratio-seg .cs-seg-opt > span { padding: .3rem .6rem; font-size: .76rem; }
@media (max-width: 520px) {
  .cs-crop-bar .cs-meta-plain { order: 3; flex-basis: 100%; }
  .cs-ratio-seg { flex: 1 1 auto; grid-auto-columns: 1fr; }
}

/* --- Fabric: aida count and the finished size ----------------------------- */

.cs-chips { display: flex; flex-wrap: wrap; gap: .35rem; }
.cs-chip { position: relative; }
.cs-chip input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.cs-chip > span {
  display: block;
  min-width: 2.4rem;
  padding: .34rem .5rem;
  border: 1px solid var(--cs-line);
  border-radius: 999px;
  background: var(--cs-surface);
  font-family: var(--cs-mono);
  font-size: .78rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: center;
  color: var(--cs-muted);
  transition: background .15s, color .15s, border-color .15s;
}
.cs-chip input:checked + span {
  background: var(--cs-thread);
  border-color: var(--cs-thread);
  color: #fff;
}
.cs-chip input:focus-visible + span { outline: 2px solid var(--cs-thread); outline-offset: 2px; }

/* Two units, side by side with the label they measure. */
.cs-unit-seg { grid-auto-columns: auto; padding: 2px; }
.cs-unit-seg .cs-seg-opt > span {
  padding: .18rem .5rem;
  font-family: var(--cs-mono);
  font-size: .7rem;
  letter-spacing: .06em;
}

/* The answer the whole panel is for, so it is set as a sentence rather than as
   another line of muted metadata. */
.cs-finished {
  margin: .75rem 0 0;
  font-size: .84rem;
  line-height: 1.5;
  color: var(--cs-ink-soft);
}
.cs-finished b {
  font-family: var(--cs-mono);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--cs-ink);
}

/* --- My colors: adding ---------------------------------------------------- */

/* The brand sits beside the label rather than under it: it is part of the
   question being asked, not a setting somewhere else on the page. */
.cs-add-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .8rem;
  flex-wrap: wrap;
}
.cs-add-head .cs-label { margin-bottom: 0; }
.cs-brand-seg { width: auto; flex: 0 0 auto; }

.cs-add-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: .55rem;
  align-items: start;
}
.cs-add-field { position: relative; min-width: 0; }
.cs-code-input {
  font-family: var(--cs-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: .04em;
}
.cs-add-btn { height: 100%; }

.cs-suggest {
  position: absolute;
  top: calc(100% + 6px);
  left: 0; right: 0;
  z-index: 20;
  max-height: 16.5rem;
  overflow-y: auto;
  background: var(--cs-surface);
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius-sm);
  box-shadow: var(--cs-shadow-lg);
}
.cs-suggest-opt {
  display: grid;
  grid-template-columns: 26px 3.4rem minmax(0, 1fr) auto;
  align-items: center;
  gap: .6rem;
  width: 100%;
  padding: .45rem .7rem;
  border: 0;
  border-bottom: 1px solid var(--cs-line-soft);
  background: transparent;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.cs-suggest-opt:last-child { border-bottom: 0; }
.cs-suggest-opt:hover,
.cs-suggest-opt.is-active { background: #F1F5FC; }
.cs-suggest-opt.is-owned { opacity: .68; }
.cs-suggest-opt .cs-swatch { width: 26px; height: 26px; }
.cs-suggest-tag,
.cs-match-tag {
  font-family: var(--cs-mono);
  font-size: .58rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--cs-sage);
  white-space: nowrap;
}

/* The answer to "what color is 3855?", shown before anything is saved. */
.cs-match {
  display: flex;
  align-items: center;
  gap: .7rem;
  margin-top: .8rem;
  padding: .5rem .7rem;
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius-sm);
  background: #F7F9FD;
}
.cs-match.is-miss { background: #FDF3F4; border-color: #F3D3D7; color: var(--cs-thread-dp); }
.cs-match-text {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .05rem .55rem;
  min-width: 0;
  font-size: .88rem;
}
.cs-match-tag { margin-left: auto; }
.cs-swatch.is-miss {
  background: repeating-linear-gradient(45deg, #F5D9DD 0 5px, #FDF3F4 5px 10px);
  box-shadow: inset 0 0 0 1px rgba(151, 11, 35, .2);
}

/* --- My colors: the box --------------------------------------------------- */

.cs-box-head { flex-wrap: wrap; align-items: center; }
.cs-box-tools { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.cs-filter {
  width: 8.5rem;
  padding: .38rem .6rem;
  font-size: .85rem;
}
.cs-clear-btn { padding-left: .7rem; padding-right: .7rem; }
@media (max-width: 620px) {
  .cs-box-tools { width: 100%; }
  .cs-box-tools .cs-seg { flex: 1 1 11rem; }
  .cs-filter { flex: 1 1 6rem; width: auto; min-width: 0; }
}

/* Multi-column rather than the grid the chart's floss list uses: this list is
   sorted, and a grid would run it across the columns, so reading it in order
   would mean zig-zagging. Columns flow top to bottom, the way a list is read. */
.cs-owned-list {
  display: block;
  columns: 3 264px;
  column-gap: 1.6rem;
}
.cs-owned-row {
  grid-template-columns: 30px 3.4rem minmax(0, 1fr) 26px;
  break-inside: avoid;
}
.cs-owned-row.is-going { opacity: .35; pointer-events: none; }
.cs-remove {
  width: 26px; height: 26px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--cs-faint);
  cursor: pointer;
  transition: background .15s, color .15s;
}
.cs-remove svg { width: 14px; height: 14px; }
.cs-remove:hover { background: rgba(199, 43, 59, .1); color: var(--cs-thread-dp); }

.cs-empty-box {
  display: grid;
  gap: 1rem;
  place-items: center;
  padding: 2.1rem 1.2rem;
  text-align: center;
  border: 2px dashed #C6CFE4;
  border-radius: var(--cs-radius);
}
.cs-empty-box p {
  margin: 0;
  max-width: 44ch;
  color: var(--cs-muted);
  font-size: .9rem;
  line-height: 1.55;
}
.cs-empty-box-mark {
  width: 76px; height: 76px;
  display: grid;
  place-items: center;
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius-sm);
}
.cs-empty-box-mark svg { width: 48px; height: 48px; }
.cs-no-hits { margin: 1.1rem 0 0; text-align: center; }

/* --- The stitch editor ----------------------------------------------------- */
/* It takes the whole window, because what it is for is looking closely at one
   chart: anything left on screen beside it is something competing with the
   stitch being worked on. Three bands — tools, the cloth, a status line — with
   the palette down the side, which is where the hand already is. */

body.cs-editing { overflow: hidden; }

.cs-editor {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  grid-template-rows: auto auto minmax(0, 1fr) auto;
  background: var(--cs-paper);
  color: var(--cs-ink);
}
.cs-editor.is-busy { cursor: progress; }

.cs-ed-bar {
  display: flex;
  align-items: center;
  gap: .5rem;
  flex-wrap: wrap;
  padding: .6rem clamp(.7rem, 2vw, 1.1rem);
  background: var(--cs-surface);
  border-bottom: 1px solid var(--cs-line);
}
.cs-ed-bar-spacer { flex: 1 1 auto; }
.cs-ed-sep { width: 1px; align-self: stretch; margin: .1rem .15rem; background: var(--cs-line); }
.cs-ed-tools, .cs-ed-group { display: flex; align-items: center; gap: .3rem; }

.cs-ed-tool, .cs-ed-icon {
  position: relative;
  width: 36px; height: 36px;
  display: grid;
  place-items: center;
  border: 1px solid transparent;
  border-radius: var(--cs-radius-sm);
  background: transparent;
  color: var(--cs-ink-soft);
  cursor: pointer;
  transition: background .14s, color .14s, border-color .14s;
}
.cs-ed-tool svg, .cs-ed-icon svg { width: 19px; height: 19px; display: block; }
/* Hover must not outrank the tool in hand: it has more specificity than a class
   on its own, and a filled button going pale under the pointer while its icon
   stays white reads as no tool being held at all. */
.cs-ed-tool:hover:not([disabled]):not(.is-on),
.cs-ed-icon:hover:not([disabled]) {
  background: #F1F5FC;
  border-color: var(--cs-line);
}
.cs-ed-tool.is-on {
  background: var(--cs-ink);
  border-color: var(--cs-ink);
  color: #fff;
}
.cs-ed-icon[disabled] { opacity: .32; cursor: not-allowed; }

/* A tool with settings of its own says so in the corner, the way a menu that
   opens does. Picking the tool opens the docket; picking it again puts it away. */
.cs-ed-tool.has-docket::after {
  content: "";
  position: absolute;
  right: 3px; bottom: 3px;
  border: 3.5px solid transparent;
  border-right-color: currentColor;
  border-bottom-color: currentColor;
  opacity: .45;
}
.cs-ed-tool.has-docket:hover::after,
.cs-ed-tool.has-docket.is-on::after { opacity: .85; }

.cs-ed-zoom {
  min-width: 3.4rem;
  padding: .38rem .5rem;
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius-sm);
  background: var(--cs-surface);
  color: var(--cs-ink);
  font-family: var(--cs-mono);
  font-size: .74rem;
  letter-spacing: .04em;
  cursor: pointer;
}
.cs-ed-zoom:hover { background: #F1F5FC; }

/* One row, holding only the options of the tool in hand. It keeps its height
   whatever is in it, so picking up a different tool does not shuffle the cloth
   underneath by a few pixels. */
.cs-ed-options {
  display: flex;
  align-items: center;
  min-height: 42px;
  gap: .6rem;
  flex-wrap: wrap;
  padding: .25rem clamp(.7rem, 2vw, 1.1rem);
  background: var(--cs-surface);
  border-bottom: 1px solid var(--cs-line);
}
.cs-ed-opt { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.cs-ed-opt-label {
  font-family: var(--cs-mono);
  font-size: .62rem;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--cs-faint);
}
.cs-ed-opt-note { color: var(--cs-muted); font-size: .82rem; }
.cs-ed-seg { max-width: 13rem; }

.cs-ed-sizes { display: flex; align-items: center; gap: .25rem; }
.cs-ed-size {
  min-width: 34px; height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .22rem;
  padding: 0 .3rem;
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius-sm);
  background: var(--cs-surface);
  color: var(--cs-muted);
  cursor: pointer;
}
.cs-ed-size i {
  display: block;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}
.cs-ed-size span { font-family: var(--cs-mono); font-size: .66rem; }
.cs-ed-size.is-on { border-color: var(--cs-ink); background: var(--cs-ink); color: #fff; }

.cs-ed-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(220px, 260px);
  min-height: 0;
}

/* Not the aida texture: inside the chart the cloth is drawn by the canvas at the
   zoom the stitches are at, and a second, fixed weave behind it would read as a
   moiré rather than as fabric. */
.cs-ed-stage {
  position: relative;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  background: #E3E8F2;
  touch-action: none;
}
.cs-ed-stage canvas { display: block; touch-action: none; }
.cs-ed-stage[data-tool="pencil"] canvas,
.cs-ed-stage[data-tool="brush"] canvas { cursor: crosshair; }
.cs-ed-stage[data-tool="fill"] canvas { cursor: cell; }
.cs-ed-stage[data-tool="select"] canvas,
.cs-ed-stage[data-tool="crop"] canvas { cursor: crosshair; }
.cs-ed-stage[data-tool="zoom"] canvas { cursor: zoom-in; }
.cs-ed-stage.is-panning canvas { cursor: grab; }

/* Four bands, and only one of them scrolls. A chart may hold sixty colors, which
   is a list taller than the panel on any screen — so if the whole panel scrolled,
   the two colors in hand would slide off the top and "Add a color" would sit
   below sixty chips. The two that say what is loaded and the one that loads
   something new stay put; the list between them is the part that gives. */
.cs-ed-side {
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  padding: .8rem;
  display: grid;
  grid-template-rows: auto auto minmax(0, 1fr) auto;
  gap: .7rem;
  background: var(--cs-surface);
  border-left: 1px solid var(--cs-line);
}

/* The two colors in hand, with the number and the name of each: on a chart of
   forty greens the swatch alone does not say which skein is loaded. */
.cs-ed-wells {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: .4rem;
  position: relative;
}
.cs-ed-well {
  display: grid;
  gap: .25rem;
  padding: .5rem .55rem;
  text-align: left;
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius-sm);
  background: var(--cs-surface);
  cursor: pointer;
  min-width: 0;
  transition: border-color .15s, box-shadow .15s;
}
.cs-ed-well.is-active { border-color: var(--cs-ink); box-shadow: 0 0 0 2px rgba(27, 40, 83, .1); }
.cs-ed-well-key {
  font-family: var(--cs-mono);
  font-size: .56rem;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--cs-faint);
}
.cs-ed-well-swatch {
  display: block;
  height: 24px;
  border-radius: 4px;
  border: 1px solid rgba(27, 40, 83, .18);
}
.cs-ed-well-swatch.is-bare {
  background-image:
    repeating-linear-gradient(0deg, rgba(90, 74, 44, .18) 0 1px, transparent 1px 5px),
    repeating-linear-gradient(90deg, rgba(90, 74, 44, .18) 0 1px, transparent 1px 5px);
}
.cs-ed-well-text { display: grid; gap: .05rem; min-width: 0; }
.cs-ed-well-text b {
  font-family: var(--cs-mono);
  font-size: .78rem;
  letter-spacing: .02em;
}
.cs-ed-well-text span {
  color: var(--cs-muted);
  font-size: .68rem;
  line-height: 1.3;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.cs-ed-swap {
  position: absolute;
  left: 50%;
  top: 0;
  transform: translate(-50%, -30%);
  width: 24px; height: 24px;
  display: grid;
  place-items: center;
  border: 1px solid var(--cs-line);
  border-radius: 50%;
  background: var(--cs-surface);
  color: var(--cs-muted);
  cursor: pointer;
  z-index: 1;
}
.cs-ed-swap svg { width: 13px; height: 13px; }
.cs-ed-swap:hover { color: var(--cs-ink); border-color: var(--cs-faint); }

.cs-ed-side-head { display: flex; align-items: baseline; justify-content: space-between; gap: .5rem; }

.cs-ed-palette {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(46px, 1fr));
  align-content: start;
  gap: .3rem;
  min-height: 0;
  overflow-y: auto;
  /* The bar keeps its width whether or not the list is long enough to need a
     scrollbar, so the chips do not shift as colors are added and taken away. */
  scrollbar-gutter: stable;
}
.cs-ed-chip {
  display: grid;
  gap: .15rem;
  justify-items: center;
  padding: .22rem .1rem .18rem;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  cursor: pointer;
  min-width: 0;
}
.cs-ed-chip i {
  display: block;
  width: 100%;
  height: 24px;
  border-radius: 3px;
  border: 1px solid rgba(27, 40, 83, .18);
}
/* One letter in front of the number, on a chart worked in both brands. A chip
   is too narrow for "Anchor", and without it two chips can read 518 and be
   quite different skeins. The well and the tooltip spell it out. */
.cs-ed-chip-brand {
  margin-right: .2em;
  font-weight: 700;
  color: var(--cs-faint);
}
.cs-ed-chip.is-bare i {
  background-image:
    repeating-linear-gradient(0deg, rgba(90, 74, 44, .18) 0 1px, transparent 1px 5px),
    repeating-linear-gradient(90deg, rgba(90, 74, 44, .18) 0 1px, transparent 1px 5px);
}
.cs-ed-chip span {
  font-family: var(--cs-mono);
  font-size: .6rem;
  letter-spacing: .02em;
  color: var(--cs-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
  white-space: nowrap;
}
.cs-ed-chip:hover { background: #F1F5FC; }
/* Which button reaches a color, said on the chip itself: a solid ring for the
   one the left button lays, a dashed one for the right. */
.cs-ed-chip.is-fg { border-color: var(--cs-ink); background: #F1F5FC; }
.cs-ed-chip.is-fg span { color: var(--cs-ink); }
.cs-ed-chip.is-bg { border-style: dashed; border-color: var(--cs-thread); }

/* Pinned under the list it adds to, and told apart from it by a rule: the list
   above ends where the rule is, however far it has been scrolled. */
.cs-ed-add {
  justify-items: stretch;
  padding-top: .55rem;
  border-top: 1px solid var(--cs-line-soft);
}
.cs-ed-add .cs-add-open { width: 100%; justify-content: center; }
.cs-ed-add .cs-suggest { top: auto; bottom: calc(100% + 6px); }
/* The typed field and the card are two ways of answering one question, so they
   are one control: the field, and under it the way to the card. */
.cs-ed-add .cs-add-field { display: grid; gap: .4rem; }
.cs-add-card { width: 100%; justify-content: center; }

.cs-ed-status {
  display: flex;
  align-items: center;
  gap: .9rem;
  padding: .45rem clamp(.7rem, 2vw, 1.1rem) max(.45rem, env(safe-area-inset-bottom));
  background: var(--cs-surface);
  border-top: 1px solid var(--cs-line);
  font-family: var(--cs-mono);
  font-size: .68rem;
  letter-spacing: .04em;
  color: var(--cs-muted);
  min-height: 30px;
}
.cs-ed-status-cell { white-space: nowrap; }
#cs-ed-hint { color: var(--cs-thread-dp); white-space: normal; text-align: right; }

/* On a phone the palette goes under the cloth rather than beside it: a 220px
   column would leave the chart with nothing worth looking at. Everything else
   here is the same fight — the cloth is what the editor is for, so every band
   around it gives up whatever height it can and the palette scrolls. */
@media (max-width: 760px) {
  .cs-ed-body { grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) auto; }
  .cs-ed-side {
    border-left: 0;
    border-top: 1px solid var(--cs-line);
    max-height: 40vh;
    padding: .5rem .6rem .6rem;
    gap: .5rem;
  }

  .cs-ed-bar { gap: .3rem; padding: .4rem .55rem; }
  .cs-ed-tool, .cs-ed-icon { width: 34px; height: 34px; }
  .cs-ed-sep { display: none; }
  .cs-ed-tools { gap: .1rem; flex-wrap: wrap; }
  .cs-ed-zoom { min-width: 3rem; padding: .3rem .4rem; }

  .cs-ed-options { min-height: 0; padding: .35rem .55rem; gap: .4rem; }
  .cs-ed-opt { gap: .4rem; }
  /* The segment already says what the sizes are for; the label was only ever
     there to separate two controls that no longer have room to be apart. */
  .cs-ed-opt-label { display: none; }
  .cs-ed-seg { max-width: 9.5rem; }
  .cs-ed-opt-note { font-size: .76rem; }
  .cs-ed-size { min-width: 28px; height: 27px; gap: .15rem; }
  .cs-ed-size i { max-width: 13px; max-height: 13px; }
  .cs-ed-size span { font-size: .6rem; }

  /* The two colors stay side by side and lose their second line instead: which
     skein is loaded is a glance, and a glance does not want to scroll. */
  .cs-ed-wells { gap: .35rem; }
  .cs-ed-well { padding: .35rem .45rem; gap: .18rem; }
  .cs-ed-well-key { font-size: .52rem; }
  .cs-ed-well-swatch { height: 17px; }
  .cs-ed-well-text b { font-size: .74rem; }
  .cs-ed-well-text span {
    -webkit-line-clamp: 1;
    white-space: nowrap;
    font-size: .64rem;
  }
  .cs-ed-swap { width: 21px; height: 21px; }
  .cs-ed-swap svg { width: 11px; height: 11px; }

  .cs-ed-palette { grid-template-columns: repeat(auto-fill, minmax(40px, 1fr)); gap: .25rem; }
  .cs-ed-chip i { height: 20px; }
  .cs-ed-chip span { font-size: .56rem; }
  .cs-ed-add .cs-btn { padding: .38rem .7rem; }

  .cs-ed-status { gap: .55rem; padding: .35rem .55rem max(.35rem, env(safe-area-inset-bottom)); }
  .cs-ed-status-cell { font-size: .62rem; }
}

/* --- How a chart will stitch ----------------------------------------------- */
/* Five bands, worst to best, and one place they are defined. Everything that
   carries a grade — a figure, a dot, a pill, the disc in the footer — takes its
   ink and its wash from the tier class, so a color always means the same thing.
   The words go with the colors everywhere, because a color alone is not a
   reading anyone can act on and not everyone can see the difference. */

.cs-tier { --tier-ink: var(--cs-muted); --tier-wash: #F1F5FC; }
.cs-tier.is-green       { --tier-ink: #2E7D4F; --tier-wash: #E7F4EC; }
.cs-tier.is-green-yellow{ --tier-ink: #5B8A2B; --tier-wash: #EEF5E2; }
.cs-tier.is-yellow      { --tier-ink: #8A6D0B; --tier-wash: #FBF3DA; }
.cs-tier.is-orange      { --tier-ink: #B4650F; --tier-wash: #FBEEDF; }
.cs-tier.is-red         { --tier-ink: var(--cs-thread-dp); --tier-wash: #FDF3F4; }
/* Not a grade at all: redundant colors are a suggestion about the shopping
   list, so they get their own blue rather than a place on the scale. */
.cs-tier.is-hint        { --tier-ink: #2E5AA8; --tier-wash: #EDF3FD; }

b.cs-tier, .cs-region-fig b.cs-tier { color: var(--tier-ink); }

.cs-tier-dot {
  width: 8px; height: 8px;
  flex: none;
  border-radius: 50%;
  background: var(--tier-ink);
}

.cs-quality {
  margin-top: 1.1rem;
  padding: .75rem .85rem .8rem;
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius-sm);
  background: #F7F9FD;
}
.cs-quality-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .6rem;
}
.cs-quality-score {
  padding: .18rem .5rem;
  border-radius: 999px;
  background: var(--tier-wash);
  color: var(--tier-ink);
  font-family: var(--cs-mono);
  font-size: .62rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  white-space: nowrap;
}
.cs-quality-rows { margin: .6rem 0 0; display: grid; gap: .3rem; }
.cs-quality-row {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .3rem .45rem;
  border-radius: 7px;
  background: var(--cs-surface);
  border: 1px solid var(--cs-line);
}
.cs-quality-name { flex: 1 1 auto; font-size: .78rem; color: var(--cs-ink-soft); }
.cs-quality-value {
  font-family: var(--cs-mono);
  font-size: .74rem;
  font-variant-numeric: tabular-nums;
  color: var(--tier-ink);
  white-space: nowrap;
}
.cs-quality-note { margin-top: .55rem; }

/* --- The score, between the two buttons in the footer ---------------------- */
/* On a phone this sits between "Size & colors" and "Next", so it is a disc and
   nothing else: everything it has to say is behind the tap. The thumb turns
   over as the score falls, which is the reading anyone can take at a glance. */

.cs-score-dial {
  flex: none;
  width: 38px; height: 38px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: var(--tier-ink);
  color: #fff;
  cursor: pointer;
  box-shadow: 0 1px 3px rgba(27, 40, 83, .25);
  transition: transform .14s;
}
.cs-score-dial:hover { transform: scale(1.06); }
.cs-score-thumb {
  width: 20px; height: 20px;
  display: block;
  transition: transform .2s;
}
.cs-score-dial[data-rank="3"] .cs-score-thumb { transform: rotate(45deg); }
.cs-score-dial[data-rank="2"] .cs-score-thumb { transform: rotate(90deg); }
.cs-score-dial[data-rank="1"] .cs-score-thumb { transform: rotate(135deg); }
.cs-score-dial[data-rank="0"] .cs-score-thumb { transform: rotate(180deg); }

.cs-score-sheet {
  position: fixed;
  z-index: 6;
  left: 50%;
  bottom: calc(4.6rem + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  width: min(24rem, calc(100vw - 1.6rem));
  padding: .85rem .95rem 1rem;
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius);
  background: var(--cs-surface);
  box-shadow: var(--cs-shadow-lg);
}
.cs-score-sheet-head {
  display: flex;
  align-items: center;
  gap: .5rem;
}
.cs-score-sheet-head .cs-regions-title { flex: 1 1 auto; }
.cs-score-sheet-head .cs-ed-icon { width: 28px; height: 28px; }
.cs-score-sheet-head .cs-ed-icon svg { width: 15px; height: 15px; }

/* --- Advanced settings ----------------------------------------------------- */

.cs-advanced {
  margin-top: 1.3rem;
  border-top: 1px solid var(--cs-line);
  padding-top: .4rem;
}
.cs-advanced > summary {
  display: flex;
  align-items: center;
  gap: .4rem;
  padding: .5rem 0;
  list-style: none;
  cursor: pointer;
  color: var(--cs-ink-soft);
}
.cs-advanced > summary::-webkit-details-marker { display: none; }
.cs-advanced-title {
  font-family: var(--cs-mono);
  font-size: .62rem;
  letter-spacing: .15em;
  text-transform: uppercase;
  color: var(--cs-faint);
}
.cs-advanced-mark {
  width: 15px; height: 15px;
  transition: transform .16s;
  color: var(--cs-faint);
}
.cs-advanced[open] .cs-advanced-mark { transform: rotate(180deg); }
.cs-advanced > summary:hover .cs-advanced-title,
.cs-advanced > summary:hover .cs-advanced-mark { color: var(--cs-ink); }

/* --- Which kind of picture this is ----------------------------------------- */

.cs-kind { display: grid; gap: .45rem; }
.cs-kind-opt { position: relative; display: block; cursor: pointer; }
.cs-kind-opt input {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
}
.cs-kind-face {
  display: grid;
  grid-template-columns: 2.2rem 1fr;
  column-gap: .65rem;
  padding: .55rem .7rem .6rem;
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius-sm);
  background: var(--cs-surface);
  transition: border-color .14s, background .14s;
}
.cs-kind-face b {
  display: flex;
  align-items: center;
  gap: .4rem;
  font-size: .86rem;
  font-weight: 600;
}
.cs-kind-face > span {
  display: block;
  grid-column: 2;
  margin-top: .15rem;
  font-size: .75rem;
  line-height: 1.45;
  color: var(--cs-muted);
}
.cs-kind-icon {
  grid-row: 1 / span 2;
  width: 2.2rem;
  height: 2.2rem;
  padding: .35rem;
  border-radius: .45rem;
  background: #E8EDF7;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linejoin: round;
}
.cs-kind-opt:last-child .cs-kind-icon { fill: currentColor; stroke: none; }
.cs-smoothing-advanced { margin-top: .7rem; }
.cs-smoothing-advanced > summary { cursor: pointer; color: var(--cs-muted); font-size: .75rem; font-weight: 600; }
.cs-advanced-sliders { display: grid; gap: .35rem; margin-top: .75rem; padding: .8rem; border-radius: var(--cs-radius-sm); background: #F7F8FB; }
.cs-advanced-sliders .cs-label:not(:first-child) { margin-top: .75rem; }
#cs-smoothing { direction: rtl; }
.cs-kind-opt input:checked + .cs-kind-face {
  border-color: var(--cs-ink);
  background: #F1F5FC;
}
.cs-kind-opt input:focus-visible + .cs-kind-face { outline: 2px solid var(--cs-thread); }
.cs-kind-tag {
  padding: .1rem .38rem;
  border-radius: 999px;
  background: #E7F4EC;
  color: #2E7D4F;
  font-family: var(--cs-mono);
  font-size: .55rem;
  font-style: normal;
  letter-spacing: .12em;
  text-transform: uppercase;
}

/* --- Starting from nothing ------------------------------------------------- */

.cs-scratch {
  display: grid;
  justify-items: center;
  gap: .5rem;
  margin-top: 1rem;
  text-align: center;
}
.cs-scratch .cs-btn { width: 100%; max-width: 20rem; }
.cs-scratch-rule {
  display: flex;
  align-items: center;
  gap: .7rem;
  width: 100%;
  font-family: var(--cs-mono);
  font-size: .62rem;
  letter-spacing: .15em;
  text-transform: uppercase;
  color: var(--cs-faint);
}
.cs-scratch-rule i { flex: 1 1 auto; height: 1px; background: var(--cs-line); }
.cs-scratch-size { margin-top: .9rem; }

/* --- Notices, continued ---------------------------------------------------- */

.cs-note-hint { background: #EDF3FD; color: #2E5AA8; border: 1px solid #D6E3F8; }

/* --- The editor: confetti, the score, and the color card ------------------- */

.cs-ed-spot { position: relative; }

/* The tools, with whatever hangs off one of them. Both panels below are placed
   by script — the bar wraps, so which row the tool ends up on is not something
   the stylesheet can know — and both point their arrow at it. */
.cs-ed-belt { position: relative; display: flex; align-items: center; min-width: 0; }

/* Said once, when it is true, and attached to the control it is about — a
   notice at the top of the screen would be about nothing in particular. */
.cs-ed-bubble, .cs-ed-docket {
  position: absolute;
  z-index: 4;
  top: calc(100% + 9px);
  left: 0;
  padding: .75rem .85rem .8rem;
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius);
  background: var(--cs-surface);
  box-shadow: var(--cs-shadow-lg);
}
.cs-ed-bubble { width: min(21rem, calc(100vw - 1.6rem)); }
.cs-ed-bubble::before, .cs-ed-docket::before {
  content: "";
  position: absolute;
  top: -6px;
  left: var(--cs-arrow, 50%);
  width: 11px; height: 11px;
  transform: translateX(-50%) rotate(45deg);
  background: var(--cs-surface);
  border-left: 1px solid var(--cs-line);
  border-top: 1px solid var(--cs-line);
}
.cs-ed-bubble p {
  margin: 0 1.1rem .55rem 0;
  font-size: .8rem;
  line-height: 1.5;
  color: var(--cs-ink-soft);
}
.cs-ed-bubble-acts { display: flex; flex-wrap: wrap; gap: .35rem; }

/* The confetti tool's own settings, expanded from the tool. */
.cs-ed-docket {
  width: min(19.5rem, calc(100vw - 1.6rem));
  display: grid;
  gap: .5rem;
}
.cs-ed-docket-body { display: grid; gap: .5rem; }
/* Nothing stranded left in the chart: the reach and the sweep are choices about
   nothing, so the docket says why it is empty rather than offering them. */
.cs-ed-docket-none {
  margin: 0 1.1rem .1rem 0;
  font-size: .8rem;
  line-height: 1.5;
  color: var(--cs-ink-soft);
}
.cs-ed-docket-head { display: flex; align-items: baseline; gap: .5rem; margin-right: 1.2rem; }
.cs-ed-docket-head b { font-size: .86rem; font-weight: 650; }
.cs-ed-docket-label {
  font-family: var(--cs-mono);
  font-size: .58rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--cs-faint);
}
.cs-ed-docket-act { justify-content: center; width: 100%; }

/* The two ways of holding the tool, and how far it may go. The reach used to be
   three named settings; it is a line to put somewhere, so it is a line. */
.cs-ed-docket .cs-ed-seg { max-width: none; }
.cs-ed-reach { display: grid; gap: .25rem; }
.cs-ed-reach-label {
  font-family: var(--cs-mono);
  font-size: .58rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--cs-faint);
}
.cs-ed-reach-slider {
  width: 100%;
  margin: 0;
  accent-color: var(--cs-thread);
  cursor: pointer;
}
/* Three names under the track, the middle one over the middle of it: the dial
   rests there, so where "balanced" is has to be visible rather than remembered.
   Equal columns rather than space-between, or the middle label would drift with
   the length of the two beside it. */
.cs-ed-reach-ends {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: .3rem;
  font-size: .72rem;
  color: var(--cs-muted);
}
.cs-ed-reach-ends span:nth-child(2) { text-align: center; }
.cs-ed-reach-ends span:last-child { text-align: right; }

/* Marking the stranded stitches is a way of looking rather than a tool, so it
   reads as a switch and never as a second tool held at the same time. */
.cs-ed-switch {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .38rem .55rem;
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius-sm);
  background: var(--cs-paper);
  color: var(--cs-ink-soft);
  font-size: .78rem;
  text-align: left;
  cursor: pointer;
}
.cs-ed-switch i {
  position: relative;
  flex: none;
  width: 30px; height: 17px;
  border-radius: 999px;
  background: #C9D2E4;
  transition: background .14s;
}
.cs-ed-switch i::after {
  content: "";
  position: absolute;
  top: 2px; left: 2px;
  width: 13px; height: 13px;
  border-radius: 50%;
  background: #fff;
  transition: transform .14s;
}
.cs-ed-switch.is-on { border-color: var(--cs-ink); color: var(--cs-ink); }
.cs-ed-switch.is-on i { background: var(--cs-ink); }
.cs-ed-switch.is-on i::after { transform: translateX(13px); }
.cs-ed-bubble-close {
  position: absolute;
  top: .35rem; right: .35rem;
  width: 22px; height: 22px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--cs-faint);
  cursor: pointer;
}
.cs-ed-bubble-close svg { width: 11px; height: 11px; }
.cs-ed-bubble-close:hover { background: #F1F5FC; color: var(--cs-ink); }

.cs-ed-score {
  display: flex;
  align-items: center;
  gap: .4rem;
  padding: .34rem .6rem;
  border: 1px solid var(--cs-line);
  border-radius: 999px;
  background: var(--tier-wash);
  color: var(--tier-ink);
  font-family: var(--cs-mono);
  font-size: .64rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  cursor: pointer;
}
.cs-ed-score i {
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--tier-ink);
}
.cs-ed-score:hover { border-color: var(--tier-ink); }

/* Placed by script, like the panels on the tools: the score sits at the end of
   a bar that wraps, and on a phone a panel hung off it lands half off the
   screen whichever side the stylesheet is told to pin it to. These are only the
   starting values, used until the first measurement. */
.cs-ed-score-panel {
  position: absolute;
  z-index: 3;
  top: calc(100% + 10px);
  right: 0;
  width: min(20rem, calc(100vw - 1.6rem));
  padding: .75rem .85rem .8rem;
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius);
  background: var(--cs-surface);
  box-shadow: var(--cs-shadow-lg);
}

/* The whole card, laid out the way a card is: hue across, light to dark down,
   the neutrals in their own column at the end. It is a lot of colors, so the
   cells are small and the block scrolls rather than the page. */
.cs-ed-card {
  position: absolute;
  z-index: 70;
  inset: auto 0 0 0;
  top: 0;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  background: rgba(238, 241, 247, .97);
}
.cs-ed-card-head {
  display: flex;
  align-items: center;
  gap: .8rem;
  padding: .7rem clamp(.7rem, 2vw, 1.1rem);
  background: var(--cs-surface);
  border-bottom: 1px solid var(--cs-line);
}
.cs-ed-card-head > div { display: grid; }
.cs-ed-card-head b { font-size: .92rem; font-weight: 650; }
.cs-ed-card-head .cs-meta-plain { font-size: .72rem; }
.cs-ed-card-read {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: .45rem;
  min-height: 20px;
  font-family: var(--cs-mono);
  font-size: .72rem;
  letter-spacing: .03em;
  color: var(--cs-ink-soft);
}
.cs-ed-card-read i {
  width: 16px; height: 16px;
  border-radius: 4px;
  border: 1px solid rgba(27, 40, 83, .2);
}
/* One box per brand, stacked. Two brands cannot sit side by side — forty
   columns is wider than any screen this opens on — and stacking keeps each
   box's columns as tall as the other's, so the two read as one card in two
   halves. A single brand is a single box and looks exactly as it always did. */
.cs-ed-card-grid {
  --cs-card-cell: 19px;
  display: grid;
  align-content: start;
  gap: clamp(.6rem, 2vw, 1.1rem);
  padding: clamp(.6rem, 2vw, 1.1rem);
  overflow: auto;
}
.cs-ed-card-box { display: grid; gap: .4rem; justify-items: center; }
.cs-ed-card-box-head {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  font-size: .655rem;
  font-weight: 650;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--cs-faint);
}
.cs-ed-card-box-head span { letter-spacing: .04em; text-transform: none; font-weight: 500; }
.cs-ed-card-cols {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 4px;
}
/* Three blocks down each column — strong, muted, nearly gray — with the gap
   between them doing the saying. */
.cs-ed-card-col { display: grid; align-content: start; gap: 9px; }
.cs-ed-card-block { display: grid; gap: 2px; }
/* The neutrals have no hue to sort by, so they stand apart rather than
   pretending to be one more step around the circle. */
.cs-ed-card-col.is-neutral {
  margin-left: 10px;
  padding-left: 10px;
  border-left: 1px dashed var(--cs-line);
}
.cs-ed-card-cell {
  width: var(--cs-card-cell);
  height: var(--cs-card-cell);
  padding: 0;
  border: 1px solid rgba(27, 40, 83, .14);
  border-radius: 3px;
  cursor: pointer;
}
.cs-ed-card-cell:hover {
  transform: scale(1.45);
  border-color: var(--cs-ink);
  box-shadow: 0 2px 8px rgba(27, 40, 83, .3);
}
/* Already on the palette: a tick would need contrast against every shade on the
   card, so the mark is the frame instead. */
.cs-ed-card-cell.is-on {
  outline: 2px solid var(--cs-ink);
  outline-offset: 1px;
}

@media (max-width: 760px) {
  .cs-ed-card-grid { --cs-card-cell: 15px; gap: .5rem; }
  .cs-ed-card-box { justify-items: start; }
  .cs-ed-card-cols { gap: 3px; justify-content: flex-start; }
  .cs-ed-card-head { gap: .5rem; padding: .5rem .6rem; }
  .cs-ed-card-head .cs-meta-plain { display: none; }
  .cs-ed-score { padding: .3rem .45rem; }
  .cs-ed-score span { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* ===========================================================================
   Accounts, and the difference an account makes
   ---------------------------------------------------------------------------
   Everything below serves one distinction: a pattern kept in this browser
   versus a pattern kept in an account. The app never withholds work over it, so
   the interface never shouts about it either — it is a note in the margin, in
   the same navy as everything else, saying what would be lost and offering the
   one thing that fixes it.                                                    */

.cs-account { position: relative; flex: none; }
.cs-account > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
}
.cs-account > summary::-webkit-details-marker { display: none; }
.cs-account-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px; height: 38px;
  border-radius: 50%;
  background: var(--cs-ink);
  color: #fff;
  font-size: .95rem;
  font-weight: 650;
  border: 1px solid var(--cs-ink);
  transition: transform .15s;
}
.cs-account[open] .cs-account-mark,
.cs-account > summary:hover .cs-account-mark { transform: scale(1.05); }

.cs-account-menu {
  position: absolute;
  top: calc(100% + .5rem);
  right: 0;
  z-index: 30;
  min-width: 13rem;
  padding: .45rem;
  background: var(--cs-surface);
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius-sm);
  box-shadow: var(--cs-shadow-lg);
}
.cs-account-who {
  margin: .2rem .55rem .45rem;
  font-size: .78rem;
  color: var(--cs-muted);
  word-break: break-all;
}
.cs-account-item {
  display: block;
  padding: .5rem .55rem;
  border-radius: 7px;
  font-size: .9rem;
  font-weight: 550;
}
.cs-account-item:hover { background: rgba(27, 40, 83, .06); }

/* --- The note about an unconfirmed address -------------------------------- */

.cs-confirm-note {
  display: flex;
  align-items: flex-start;
  gap: .7rem;
  flex-wrap: wrap;
  margin: -1rem 0 1.6rem;
  padding: .8rem 1rem;
  background: var(--cs-surface);
  border: 1px solid var(--cs-line);
  border-left: 3px solid var(--cs-gold);
  border-radius: var(--cs-radius-sm);
}
.cs-confirm-note > svg { width: 19px; height: 19px; flex: none; margin-top: .1rem; color: var(--cs-gold); }
.cs-confirm-note p { margin: 0; font-size: .875rem; line-height: 1.5; flex: 1 1 16rem; }
.cs-confirm-said { font-size: .8rem; color: var(--cs-muted); align-self: center; }

.cs-linkish {
  background: none;
  border: 0;
  padding: 0;
  font-size: inherit;
  font-weight: 600;
  color: var(--cs-thread);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.cs-linkish:hover { color: var(--cs-thread-dp); }
.cs-linkish[disabled] { opacity: .5; cursor: default; }

/* --- "These are only in this browser" ------------------------------------- */

.cs-local-note {
  display: flex;
  align-items: flex-start;
  gap: .8rem;
  flex-wrap: wrap;
  margin: 0 0 1.6rem;
  padding: .95rem 1.1rem;
  background: var(--cs-surface);
  border: 1px solid var(--cs-line);
  border-left: 3px solid var(--cs-thread);
  border-radius: var(--cs-radius-sm);
}
.cs-local-note-body { flex: 1 1 18rem; min-width: 0; }
.cs-local-note h3 {
  margin: 0 0 .25rem;
  font-size: .95rem;
  font-weight: 650;
  letter-spacing: -.01em;
}
.cs-local-note p { margin: 0; font-size: .865rem; line-height: 1.5; color: var(--cs-ink-soft); }
.cs-local-note-actions { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
.cs-local-note > svg { width: 20px; height: 20px; flex: none; margin-top: .15rem; color: var(--cs-thread); }

/* Compact enough to sit under a chart on the detail page without competing
   with it. Same words, fewer of them. */
.cs-local-note.is-slim { margin: 1.1rem 0 0; padding: .75rem .9rem; }
.cs-local-note.is-slim h3 { font-size: .875rem; }
.cs-local-note.is-slim p { font-size: .82rem; }

/* --- Sign in and sign up -------------------------------------------------- */

.cs-auth { max-width: 27rem; margin: clamp(1rem, 5vw, 3rem) auto 0; }
.cs-auth-title {
  margin: 0 0 .55rem;
  font-size: 1.3rem;
  font-weight: 650;
  letter-spacing: -.022em;
}
.cs-auth-lede {
  margin: 0 0 1.4rem;
  font-size: .9rem;
  line-height: 1.55;
  color: var(--cs-ink-soft);
}
.cs-auth-error {
  margin: 0 0 1.1rem;
  padding: .65rem .85rem;
  border-radius: var(--cs-radius-sm);
  background: rgba(199, 43, 59, .08);
  border: 1px solid rgba(199, 43, 59, .25);
  color: var(--cs-thread-dp);
  font-size: .865rem;
}
.cs-auth-form { display: grid; gap: .95rem; }
.cs-auth-alt {
  margin: 1.2rem 0 0;
  font-size: .865rem;
  color: var(--cs-muted);
  text-align: center;
}
.cs-auth-alt a { color: var(--cs-thread); font-weight: 600; }

.cs-field { display: grid; gap: .3rem; }
.cs-field-label {
  font-family: var(--cs-mono);
  font-size: .655rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--cs-muted);
}
.cs-field-hint { font-size: .78rem; color: var(--cs-faint); }
.cs-input {
  width: 100%;
  padding: .68rem .8rem;
  font: inherit;
  font-size: .95rem;
  color: var(--cs-ink);
  background: var(--cs-surface);
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius-sm);
}
.cs-input:focus { outline: 2px solid var(--cs-thread); outline-offset: 1px; border-color: transparent; }

.cs-btn-wide { width: 100%; }

.cs-confirm-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 54px; height: 54px;
  margin: 0 auto 1rem;
  border-radius: 50%;
  background: rgba(199, 43, 59, .1);
  color: var(--cs-thread);
}
.cs-confirm-mark-good { background: rgba(57, 111, 82, .12); color: var(--cs-sage); }
.cs-confirm-mark svg { width: 26px; height: 26px; }

/* A card for a pattern this browser is holding. The same card, marked. */
.cs-card-local .cs-card-name::after {
  content: "in this browser";
  display: block;
  margin-top: .2rem;
  font-family: var(--cs-mono);
  font-size: .6rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--cs-faint);
  font-weight: 400;
}

/* The preview the finished-pattern step ends on. It replaced a generic mark,
   because the one thing worth looking at there is the chart itself. */
.cs-done-preview {
  display: block;
  width: 100%;
  max-width: 15rem;
  margin: 0 auto 1.1rem;
  padding: .5rem;
  border-radius: var(--cs-radius-sm);
  border: 1px solid var(--cs-line);
  background: var(--cs-aida);
}
.cs-done-preview img {
  display: block;
  width: 100%;
  height: auto;
  image-rendering: pixelated;
}

/* The share link, once there is one to copy. */
.cs-share-link {
  display: flex;
  gap: .4rem;
  align-items: center;
  margin: .6rem 0 0;
}
.cs-share-link input {
  flex: 1 1 auto;
  min-width: 0;
  padding: .5rem .65rem;
  font-family: var(--cs-mono);
  font-size: .78rem;
  color: var(--cs-ink-soft);
  background: var(--cs-paper);
  border: 1px solid var(--cs-line);
  border-radius: var(--cs-radius-sm);
}

/* --- A face ---------------------------------------------------------------
   The picture is stored as the square the circle was cut from, and shown as
   the circle. Everywhere it can be missing, the same round mark carries the
   first letter of the name instead, so the shape of the thing never moves. */

.cs-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  overflow: hidden;
  border-radius: 50%;
  background: var(--cs-ink);
  color: #fff;
  font-weight: 650;
  line-height: 1;
}
.cs-avatar-img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* The stand-in is one letter, and it is a mark rather than text: it should not
   be selectable, and it is capitalised wherever it came from. */
.cs-avatar-letter { text-transform: uppercase; user-select: none; }
.cs-avatar-lg { width: 88px; height: 88px; font-size: 2rem; }
.cs-avatar-md { width: 46px; height: 46px; font-size: 1.05rem; }
.cs-avatar-note { width: 40px; height: 40px; font-size: .95rem; }
.cs-account-photo { object-fit: cover; padding: 0; }

/* --- My profile ----------------------------------------------------------- */

.cs-profile { display: grid; gap: 1rem; max-width: 40rem; }
.cs-profile-head {
  margin: 0 0 .9rem;
  font-size: .95rem;
  font-weight: 650;
  letter-spacing: -.01em;
}
.cs-profile-note {
  margin: 0 0 .9rem;
  font-size: .85rem;
  line-height: 1.5;
  color: var(--cs-muted);
}
.cs-profile-form { display: grid; gap: .95rem; }
.cs-profile-actions { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; }
.cs-profile-said {
  font-size: .82rem;
  color: var(--cs-muted);
  min-height: 1.1em;
  margin: .1rem 0 0;
}
.cs-profile-said.is-good { color: var(--cs-sage); }
.cs-profile-said.is-bad { color: var(--cs-thread-dp); }

.cs-profile-photo-row { display: flex; gap: 1.1rem; align-items: flex-start; }
.cs-profile-photo-side { flex: 1 1 auto; min-width: 0; }

.cs-profile-danger { border-color: rgba(199, 43, 59, .3); }
/* The one place a destructive button is the button, rather than the quiet way
   out of a panel that is about something else. */
.cs-btn-danger.cs-btn-solid {
  background: var(--cs-thread);
  color: #fff;
  border-color: var(--cs-thread);
}
.cs-btn-danger.cs-btn-solid:hover:not([disabled]) { background: var(--cs-thread-dp); }

.cs-field-optional {
  font-family: var(--cs-mono);
  font-size: .58rem;
  letter-spacing: .12em;
  color: var(--cs-faint);
  margin-left: .35rem;
}

/* --- Positioning a picture inside the circle ------------------------------ */

.cs-crop-dialog { max-width: 22rem; }
.cs-crop-stage {
  position: relative;
  width: 288px;
  max-width: 100%;
  aspect-ratio: 1 / 1;
  margin: 0 auto;
  background: var(--cs-paper);
  touch-action: none;
  /* The ring dims everything outside the circle with a spread shadow, which
     would otherwise wash over the whole dialog. */
  overflow: hidden;
}
.cs-crop-stage canvas { display: block; width: 100%; height: 100%; cursor: grab; }
.cs-crop-stage canvas:active { cursor: grabbing; }
/* Everything outside the circle is dimmed rather than hidden: what is being
   chosen is legible, and so is what it was chosen out of. */
.cs-crop-ring {
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: 50%;
  box-shadow: 0 0 0 9999px rgba(27, 40, 83, .55);
  outline: 2px solid rgba(255, 255, 255, .85);
  outline-offset: -2px;
}
.cs-crop-zoom {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: 1rem 1.4rem 0;
  color: var(--cs-muted);
}
.cs-crop-zoom svg { width: 15px; height: 15px; flex: none; }
.cs-crop-zoom .cs-range { flex: 1 1 auto; }

/* --- Really deleting an account ------------------------------------------- */

.cs-delete-dialog { max-width: 25rem; }
.cs-delete-who {
  display: flex;
  gap: .7rem;
  align-items: center;
  margin: 1rem 0 .9rem;
}
.cs-delete-who-body { display: grid; gap: .1rem; min-width: 0; font-size: .9rem; }
.cs-delete-who-mail { color: var(--cs-muted); font-size: .82rem; word-break: break-all; }
.cs-delete-stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: .6rem;
  padding: .85rem;
  background: var(--cs-paper);
  border-radius: var(--cs-radius-sm);
}
.cs-delete-warning {
  margin: .9rem 0 0;
  font-size: .865rem;
  line-height: 1.5;
  color: var(--cs-thread-dp);
}

/* --- The start page ------------------------------------------------------- */

.cs-welcome-title {
  margin: 0 0 .55rem;
  font-size: clamp(1.4rem, 4vw, 1.75rem);
  font-weight: 650;
  letter-spacing: -.025em;
}
.cs-welcome-actions {
  display: flex;
  gap: .6rem;
  flex-wrap: wrap;
  margin-top: 1.3rem;
}

/* The welcome panel is also what an empty floss box shows a signed-out
   visitor, and there the buttons follow a paragraph rather than a heading. */
.cs-empty-body .cs-local-note-actions { margin-top: 1.3rem; }
.cs-admin-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:1rem}.cs-admin-grid p{padding:1rem;border:1px solid var(--cs-line,#dbe1ee);border-radius:10px}.cs-admin-grid b{font-size:1.8rem}
