/* Modernist — the design system the fashion-generator handoff is built on
   (docs/design/design_handoff_fashion_generator, _ds/modernist-<id>/styles.css),
   ported as-is: zero radii, 2px section rules, 1px hairlines on inputs and
   tiles, 16px section padding, Archivo with 800-weight headings, near-black
   ink instead of a grey ramp.

   The one substitution is the accent: the prototype's red becomes sellestic
   violet, and the page ground is the 30% tint of it. Retune the app by
   changing --ws-accent alone. */
:root {
  --ws-accent:     #4C1D95;
  --ws-accent-600: #3B1679;
  --ws-accent-700: #2E1065;
  --ws-ink:        #201e1d;
  /* The page ground: plain white for now. */
  --ws-bg:         #ffffff;
  --ws-surface:    #ffffff;
  /* The left form column — a hair off white, nothing more. */
  --ws-panel-bg:   #fafafa;
  --ws-divider:    color-mix(in srgb, var(--ws-ink) 14%, transparent);
  /* The system's type tints, collapsed to solid ink: labels, captions and
     body copy all read black, only the hierarchy of size and weight
     separates them. */
  --ws-ink-70:     var(--ws-ink);
  --ws-ink-60:     var(--ws-ink);
  --ws-ink-55:     var(--ws-ink);
  --ws-neutral-300: #d7d3d3;
  --ws-neutral-400: #bab6b6;
  --ws-neutral-500: #9b9797;
  --ws-space-1: 4px;
  --ws-space-2: 8px;
  --ws-space-3: 12px;
  --ws-space-4: 16px;
  --ws-space-6: 24px;
}

/* The workspace surface. Everything inside it is flat and square — no radius
   leaks in from the app's rounded utilities. */
.ws {
  background: var(--ws-bg);
  color: var(--ws-ink);
  font-size: 15px;
  line-height: 1.55;
}
.ws *,
.ws *::before,
.ws *::after { border-radius: 0; }

.ws :focus { outline: none; }
.ws :focus-visible { outline: 2px solid var(--ws-accent); outline-offset: 2px; }
.ws ::selection { background: color-mix(in srgb, var(--ws-accent) 30%, transparent); }

/* — type ———————————————————————————————————————————————————————————————— */
/* Section kicker: the handoff's h6 — 11px / 800 / uppercase / .08em. */
.ws-kicker {
  margin: 0;
  font-size: 11px;
  font-weight: 800;
  line-height: 1.12;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ws-ink-60);
}
/* The panel is a sequence, so its sections are numbered. A CSS counter rather
   than hard-coded digits: when Frame layout is hidden (nano renders a single
   frame) the sections after it renumber instead of leaving a gap. Scoped to
   the panel so the feed's own kickers stay unnumbered. */
/* The form column sits on a faint grey so the white feed reads as the
   content and the panel as the tool next to it. */
.ws-panel { counter-reset: ws-step; background: var(--ws-panel-bg); }
.ws-panel .ws-kicker::before {
  counter-increment: ws-step;
  content: counter(ws-step, decimal-leading-zero) " ";
  color: var(--ws-accent);
}

.ws-h3 {
  margin: 0;
  font-size: 25px;
  font-weight: 800;
  line-height: 1.12;
  letter-spacing: -0.015em;
}
/* Small captions: counters, cost, timestamps, slot numbers. */
.ws-mono {
  font-family: 'Archivo Mono', ui-monospace, SFMono-Regular, monospace;
  font-size: 11px;
  color: var(--ws-ink-55);
}
.ws-body { font-size: 13px; color: var(--ws-ink-70); }

/* — structure ——————————————————————————————————————————————————————————— */
.ws-section { padding: var(--ws-space-4); }
/* Only the sidebar/content split is ruled; section rules are off for now. */
.ws-rule-t { border-top: 0; }
.ws-rule-b { border-bottom: 0; }
.ws-rule-r { border-right: 1px solid var(--ws-divider); }
.ws-hairline-b { border-bottom: 1px solid var(--ws-divider); }
.ws-dashed { border: 2px dashed var(--ws-neutral-400); background: var(--ws-surface); }

/* — buttons ————————————————————————————————————————————————————————————— */
.ws-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  cursor: pointer;
  font-family: inherit;
  font-size: 14px;
  font-weight: 800;
  line-height: 1.2;
  color: var(--ws-ink);
  background: transparent;
  border: 1px solid transparent;
  padding: var(--ws-space-2) 14px;
}
.ws-btn:disabled { opacity: 0.45; cursor: not-allowed; }
.ws-btn-primary { background: var(--ws-accent); color: #fff; }
.ws-btn-primary:hover:not(:disabled) { background: var(--ws-accent-600); }
.ws-btn-primary:active:not(:disabled) { background: var(--ws-accent-700); }
.ws-btn-secondary { border-color: var(--ws-divider); }
.ws-btn-secondary:hover { background: color-mix(in srgb, var(--ws-ink) 7%, transparent); }
.ws-btn-secondary:active { background: color-mix(in srgb, var(--ws-ink) 14%, transparent); }
.ws-btn-ghost { color: var(--ws-accent); padding-inline: var(--ws-space-1); }
.ws-btn-ghost:hover { background: color-mix(in srgb, var(--ws-accent) 10%, transparent); }

/* Thumb-up / thumb-down button. Square, only the icon inside, and once the
   user has clicked it the border and icon pick up the brand accent so the
   choice reads back at a glance. Kept tight so all four action buttons
   (Download · Improve · 👍 · 👎) fit on a single line of a narrow card;
   flex-wrap on the parent drops the pair to their own line only when they
   truly can't share one. */
.ws-rate-btn { padding-inline: 5px; flex: 0 0 auto; }
.ws-rate-btn.is-active { border-color: var(--ws-accent); background: color-mix(in srgb, var(--ws-accent) 8%, transparent); }
.ws-rate-btn.is-active img { filter: brightness(0) saturate(100%) invert(15%) sepia(90%) saturate(3000%) hue-rotate(255deg); }
/* Full-width action. The handoff sets its label flush left; here the label and
   its icon sit together in the middle, which is what reads as the one button
   that starts everything. */
.ws-btn-block {
  width: 100%;
  justify-content: center;
  padding: var(--ws-space-3) var(--ws-space-4);
}

/* — forms ——————————————————————————————————————————————————————————————— */
.ws-input {
  width: 100%;
  min-height: 36px;
  padding: 6px 10px;
  font: inherit;
  font-size: 14px;
  color: var(--ws-ink);
  caret-color: var(--ws-accent);
  background: var(--ws-surface);
  border: 1px solid var(--ws-divider);
}
.ws-input:hover { border-color: color-mix(in srgb, var(--ws-ink) 45%, transparent); }
.ws-input:focus-visible { border-color: var(--ws-accent); outline-offset: 0; }
textarea.ws-input { min-height: 90px; resize: vertical; }
select.ws-input { width: auto; font-weight: 800; cursor: pointer; }

/* — segmented control ——————————————————————————————————————————————————— */
.ws-seg { display: flex; width: 100%; border: 1px solid var(--ws-divider); }
.ws-seg-opt {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  gap: 6px;
  padding: 7px 12px;
  font-size: 13px;
  font-weight: 800;
  cursor: pointer;
  background: var(--ws-surface);
  color: var(--ws-ink);
  border: 0;
}
.ws-seg-opt + .ws-seg-opt { border-left: 1px solid var(--ws-divider); }
.ws-seg-opt:not(.is-on):hover { background: color-mix(in srgb, var(--ws-ink) 7%, transparent); }
.ws-seg-opt.is-on { background: var(--ws-accent); color: #fff; }

/* Outline variant — same segmented shell, quieter cells: no accent fill on the
   active option, just accent-tinted text. Used where two heavy buttons in a
   row (e.g. Model) drown the rest of the section. */
.ws-seg--outline .ws-seg-opt { font-weight: 500; }
.ws-seg--outline .ws-seg-opt.is-on { background: transparent; color: var(--ws-accent); font-weight: 700; }

/* — tags ———————————————————————————————————————————————————————————————— */
.ws-tag {
  display: inline-flex;
  align-items: center;
  font-size: 11px;
  letter-spacing: 0.02em;
  padding: 3px 10px;
  white-space: nowrap;
}
/* .tag-accent in the system is a tint of the accent, not a fill. */
.ws-tag-accent {
  background: color-mix(in srgb, var(--ws-accent) 12%, #fff);
  color: var(--ws-accent-700);
}
.ws-tag-outline { border: 1px solid var(--ws-accent); color: var(--ws-accent); }

/* — frame-layout picker ————————————————————————————————————————————————— */
.ws-grid-opt {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 6px;
  cursor: pointer;
  background: var(--ws-surface);
  border: 2px solid var(--ws-neutral-400);
}
.ws-grid-opt.is-on { border-color: var(--ws-accent); }
.ws-grid-diagram { display: grid; gap: 2px; aspect-ratio: 3/4; width: 100%; }
.ws-grid-cell { background: var(--ws-neutral-500); }
.ws-grid-opt.is-on .ws-grid-cell { background: var(--ws-accent); }
/* "auto": an open frame, since the layout is the generator's call. */
.ws-grid-auto { border: 1px dashed var(--ws-neutral-500); }
.ws-grid-opt.is-on .ws-grid-auto { border-color: var(--ws-accent); }
.ws-grid-label { font-size: 9px; color: var(--ws-ink); }
.ws-grid-opt.is-on .ws-grid-label { color: var(--ws-accent); }

/* — photo slots ————————————————————————————————————————————————————————— */
.ws-slot-plus {
  font-size: 16px;
  font-weight: 800;
  line-height: 1;
  color: color-mix(in srgb, var(--ws-ink) 45%, transparent);
}
.ws-slot-more {
  font-family: var(--ws-mono, ui-monospace, monospace);
  font-size: 9px;
  letter-spacing: .04em;
  color: color-mix(in srgb, var(--ws-ink) 45%, transparent);
}
.ws-slot {
  position: relative;
  aspect-ratio: 3/4;
  min-width: 0;
  background: var(--ws-bg);
  border: 1px solid var(--ws-neutral-400);
  overflow: hidden;
}

/* — the two columns ————————————————————————————————————————————————————
   The handoff's sizing: the panel rests at 360px and never exceeds 420px, the
   feed takes the rest, and below the wrap point each one owns the full width. */
.ws-panel { flex: 1 1 360px; min-width: min(300px, 100%); max-width: 420px; }
/* The feed is its own sizing context: the column sits next to the panel, so
   how many cards fit depends on the column's width, not the window's. */
.ws-feed-col { flex: 3 1 520px; min-width: min(320px, 100%); container-type: inline-size; }
@media (max-width: 767px) {
  .ws-panel { flex-basis: 100%; max-width: none; }
  .ws-feed-col { flex-basis: 100%; }
}

/* — the feed ————————————————————————————————————————————————————————————
   Four generations per row on a wide screen, stepping down to one on a phone.
   minmax(0, 1fr) rather than auto-fill: the count is fixed, and the cards
   share the width evenly however narrow they get. */
.ws-feed {
  display: grid;
  /* Fallback for browsers without container queries: pack at 300px a card. */
  grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
  gap: var(--ws-space-3);
  padding: var(--ws-space-3);
}
@supports (container-type: inline-size) {
  .ws-feed { grid-template-columns: minmax(0, 1fr); }
}
/* A card never goes below ~280px, so the result stays big enough to judge:
   one column on a phone, and four only once the column can really hold them. */
@container (min-width: 600px)  { .ws-feed { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@container (min-width: 900px)  { .ws-feed { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@container (min-width: 1200px) { .ws-feed { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.ws-feed-card {
  min-width: 0;
  padding: var(--ws-space-4);
  border-radius: 12px;
  /* No rules any more, so the card reads by its own ground: a faint tint,
     since the page behind it is white. */
  background: color-mix(in srgb, var(--ws-ink) 4%, #fff);
}
/* The card holds the delete button, which only shows on hover of the card. */
.ws-feed-card-hoverable { position: relative; }

/* — delete, top-right of the card ——————————————————————————————————————— */
/* Sits over the image on its own plate so it reads on any photo. Hidden until
   the pointer is on the card: destructive, and the card is mostly a picture. */
.ws-card-delete {
  position: absolute;
  top: calc(var(--ws-space-4) + 8px);
  right: calc(var(--ws-space-4) + 8px);
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 6px;
  border: 0;
  border-radius: 8px;
  color: var(--ws-ink);
  background: rgba(255, 255, 255, .85);
  backdrop-filter: blur(4px);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .18);
  cursor: pointer;
  opacity: 0;
  transition: opacity .15s ease, background .15s ease, color .15s ease;
}
.ws-feed-card-hoverable:hover .ws-card-delete,
.ws-card-delete:focus-visible { opacity: 1; }
.ws-card-delete:hover { color: #fff; background: #dc2626; }
.ws-card-delete:disabled { opacity: .5; cursor: default; }
/* Touch: there is no hover to reveal it, so it is always on the card. */
@media (hover: none) {
  .ws-card-delete { opacity: 1; }
}

/* Banners and empty states run the whole width of the grid. */
.ws-feed-full { grid-column: 1 / -1; }

/* — guest sign-up banner ————————————————————————————————————————————————— */
/* Same card shape as a result, tinted with the accent and carrying the gift in
   a badge, so it reads as an offer without shouting over the feed below. */
.ws-promo {
  display: flex;
  align-items: center;
  gap: var(--ws-space-3);
  width: 100%;
  padding: 14px var(--ws-space-4);
  border-radius: 12px;
  text-align: left;
  border: 1px solid color-mix(in srgb, var(--ws-accent) 20%, transparent);
  background: linear-gradient(135deg,
              color-mix(in srgb, var(--ws-accent) 11%, #fff),
              color-mix(in srgb, var(--ws-accent) 4%,  #fff));
  transition: border-color .15s ease, box-shadow .15s ease;
}
.ws-promo:hover {
  border-color: color-mix(in srgb, var(--ws-accent) 42%, transparent);
  box-shadow: 0 6px 18px color-mix(in srgb, var(--ws-accent) 14%, transparent);
}
.ws-promo-badge {
  display: grid;
  place-items: center;
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 10px;
  background: color-mix(in srgb, var(--ws-accent) 14%, #fff);
}
.ws-promo-title { font-size: 13px; font-weight: 800; letter-spacing: -.01em; }
/* The number is the offer — it carries the accent, the rest stays ink. */
.ws-promo-title em { font-style: normal; color: var(--ws-accent); }
.ws-promo-cta {
  display: flex;
  align-items: center;
  flex: none;
  gap: var(--ws-space-1);
  font-size: 12px;
  font-weight: 700;
  white-space: nowrap;
  color: var(--ws-accent);
}
.ws-promo-cta svg { transition: transform .15s ease; }
.ws-promo:hover .ws-promo-cta svg { transform: translateX(2px); }

/* — result tiles ———————————————————————————————————————————————————————— */
/* The card holds one image and it fills the card's width; its height follows
   from the result's own aspect ratio, set inline on the tile. */
.ws-tiles { position: relative; display: block; }
.ws-tile {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  background: var(--ws-surface);
  border: 1px solid var(--ws-neutral-400);
  overflow: hidden;
}
/* — caption on the image ———————————————————————————————————————————————— */
/* Time and wishes sit on the result itself, over a shadow that fades upward
   — the photo keeps its full height and the card loses a line of chrome. */
.ws-tile-caption {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 20px 10px 8px;
  text-align: left;
  pointer-events: none;
  background: linear-gradient(to top, rgba(0, 0, 0, .62), rgba(0, 0, 0, 0));
}
.ws-tile-caption-time {
  display: block;
  font-family: 'Archivo Mono', ui-monospace, SFMono-Regular, monospace;
  font-size: 11px;
  color: rgba(255, 255, 255, .8);
}
.ws-tile-caption-text {
  margin: 2px 0 0;
  font-size: 12px;
  line-height: 1.35;
  color: rgba(255, 255, 255, .92);
  /* Two lines at most: a long wish must not cover the result. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* — the dots that switch the card's image ——————————————————————————————— */
/* One dot per image the row holds. Round — the one exception to the square
   rule, since a 8px square reads as a speck rather than a control. */
.ws-dots { gap: 6px; }
.ws-dot {
  width: 8px;
  height: 8px;
  padding: 0;
  border-radius: 999px;
  border: 1px solid var(--ws-neutral-400);
  background: transparent;
  cursor: pointer;
}
.ws-dot:hover { border-color: var(--ws-accent); }
.ws-dot.is-active { background: var(--ws-accent); border-color: var(--ws-accent); }

/* On the image itself: centred at the top, over a soft plate so the dots read
   on a light photo as well as a dark one. */
.ws-dots-on-image {
  position: absolute;
  top: 8px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 2;
  padding: 5px 7px;
  border-radius: 999px;
  background: rgba(0, 0, 0, .35);
  backdrop-filter: blur(4px);
}
.ws-dots-on-image .ws-dot { border-color: rgba(255, 255, 255, .75); }
.ws-dots-on-image .ws-dot:hover { border-color: #fff; }
.ws-dots-on-image .ws-dot.is-active { background: #fff; border-color: #fff; }

/* Opens the full-size view. */
.ws-tile.is-clickable { cursor: zoom-in; }
.ws-tile.is-clickable:hover { border-color: var(--ws-accent); }
/* Still running: the tile holds the shape its result will have. */
.ws-tile-skeleton {
  width: 100%;
  position: relative;
  overflow: hidden;
  background: color-mix(in srgb, var(--ws-ink) 9%, #fff);
}
/* Content-loading shimmer: a light band sweeping across the placeholder,
   the way a card fills in — not the whole tile blinking. */
.ws-skel {
  position: relative;
  overflow: hidden;
  background: color-mix(in srgb, var(--ws-ink) 9%, #fff);
}
.ws-skel::after,
.ws-tile-skeleton::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    transparent 20%,
    color-mix(in srgb, #fff 85%, transparent) 50%,
    transparent 80%
  );
  transform: translateX(-100%);
  animation: ws-shimmer 1.4s ease-in-out infinite;
  animation-delay: inherit;
}
@keyframes ws-shimmer {
  to { transform: translateX(100%); }
}
@media (prefers-reduced-motion: reduce) {
  .ws-skel {
  position: relative;
  overflow: hidden;
  background: color-mix(in srgb, var(--ws-ink) 9%, #fff);
}
.ws-skel::after,
.ws-tile-skeleton::after { animation: none; }
}

/* — header —————————————————————————————————————————————————————————————
   The app bar, on the system's terms: 16px gutter so it lines up with the
   panel below it, a 2px rule against the body, links flush right. */
.ws-nav {
  display: flex;
  align-items: center;
  gap: var(--ws-space-4);
  padding: var(--ws-space-3) var(--ws-space-4);
  border-bottom: 1px solid var(--ws-divider);
  background: var(--ws-surface);
  color: var(--ws-ink);
}
.ws-nav *, .ws-nav *::before, .ws-nav *::after { border-radius: 0; }
.ws-nav-brand { font-size: 18px; font-weight: 800; margin-right: auto; }
.ws-nav-link {
  font-size: 14px;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
  padding: 0;
}
.ws-nav-link:hover, .ws-nav-link.is-on { color: var(--ws-accent); }
.ws-nav-link.is-on { font-weight: 800; }
/* Initials badge — the design's 28x28 accent square. */
.ws-avatar {
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  background: var(--ws-accent);
  color: #fff;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.04em;
}
.ws-menu {
  background: var(--ws-surface);
  border: 1px solid var(--ws-divider);
  box-shadow: 0 3px 10px color-mix(in srgb, var(--ws-ink) 16%, transparent);
}
.ws-menu-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 8px 16px;
  font-size: 14px;
  background: none;
  border: 0;
  cursor: pointer;
  color: var(--ws-ink);
}
.ws-menu-item:hover { background: color-mix(in srgb, var(--ws-ink) 7%, transparent); }

/* — phones ——————————————————————————————————————————————————————————————
   The two columns stack, so the panel's divider moves from its right edge to
   its bottom and the header tightens up. The result keeps the card's full
   width — a phone shows one card per row, and shrinking it there would leave
   the image too small to judge. */
@media (max-width: 767px) {
  .ws-rule-r {
    border-right: 0;
    border-bottom: 1px solid var(--ws-divider);
  }
  .ws-nav { gap: var(--ws-space-3); padding: var(--ws-space-3); }
  .ws-nav-brand { font-size: 16px; }
}

/* Touch screens have no hover, so anything that only appears on hover — the
   × on an uploaded photo — has to stay visible. */
@media (hover: none) {
  .ws-slot-remove { opacity: 1 !important; }
}
