:root {
  --bg: #ffffff;
  --surface: #fbfaFd;
  --panel: #ffffff;
  --panel-2: #f4f1fa;
  --line: #eae6f2;
  --line-strong: #ddd6ea;
  --text: #241f33;
  --muted: #7b7492;
  --danger: #d1436a;

  /* Pitchman Labs: light pink into lavender. */
  --pink: #ffc0da;
  --lavender: #c3b4f2;
  --grad: linear-gradient(135deg, var(--pink) 0%, var(--lavender) 100%);
  --grad-ink: #3a2f4f;

  --radius: 16px;
  --shadow: 0 16px 44px rgba(122, 100, 168, 0.14);
  --shadow-soft: 0 4px 14px rgba(122, 100, 168, 0.10);
  --sidebar: 210px;
  --gap: 18px;
}

* { box-sizing: border-box; }

/* The browser's own [hidden] rule is in the UA stylesheet, so ANY class selector
   that sets display beats it — .photo-chip{display:flex} left the chip on screen
   permanently, broken thumbnail and all, because el.hidden = true did nothing.
   Author-level and !important so that toggling .hidden works whatever the element
   is displayed as. */
[hidden] { display: none !important; }

html, body {
  height: 100%;
  margin: 0;
}

body {
  background: var(--bg);
  color: var(--text);
  font: 15px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
}

button {
  font: inherit;
  color: inherit;
  cursor: pointer;
  border: 0;
  background: none;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
}

.brand-mark {
  width: 32px;
  height: 32px;
  border-radius: 10px;
  background: var(--grad);
  display: grid;
  place-items: center;
  flex: none;
  box-shadow: var(--shadow-soft);
}

.brand h1 {
  margin: 0;
  font-size: 17px;
  font-weight: 650;
  letter-spacing: -0.01em;
  white-space: nowrap;
}

/* ------------------------------------------------------------------ login */

.login-wrap {
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: 24px;
  background:
    radial-gradient(760px 520px at 12% -8%, rgba(255, 192, 218, 0.42), transparent 62%),
    radial-gradient(680px 520px at 96% 108%, rgba(195, 180, 242, 0.42), transparent 62%),
    var(--bg);
}

.card {
  width: 100%;
  max-width: 380px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: 32px 28px;
  box-shadow: var(--shadow);
}

.card .brand {
  justify-content: center;
  margin-bottom: 26px;
}

label {
  display: block;
  font-size: 12.5px;
  color: var(--muted);
  margin: 0 0 6px;
  letter-spacing: 0.02em;
}

.card input {
  width: 100%;
  padding: 12px 14px;
  margin-bottom: 16px;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 11px;
  color: var(--text);
  font: inherit;
  outline: none;
  transition: border-color 0.15s, box-shadow 0.15s;
}

.card input::placeholder { color: #b3adc4; }

.card input:focus {
  border-color: var(--lavender);
  box-shadow: 0 0 0 3px rgba(195, 180, 242, 0.34);
}

.btn-primary {
  width: 100%;
  padding: 12px 16px;
  border-radius: 11px;
  font-weight: 650;
  color: var(--grad-ink);
  background: var(--grad);
  box-shadow: var(--shadow-soft);
  transition: filter 0.15s, transform 0.06s;
}

.btn-primary:hover { filter: brightness(1.04) saturate(1.06); }
.btn-primary:active { transform: translateY(1px); }
.btn-primary[disabled] { opacity: 0.55; cursor: default; }

.error {
  min-height: 20px;
  margin: 0 0 10px;
  font-size: 13px;
  color: var(--danger);
}

/* -------------------------------------------------------------------- app */

.app {
  height: 100dvh;
  display: flex;
  align-items: stretch;
  background: var(--bg);
}

/* ----------------------------------------------------------------- sidebar */

.sidebar {
  width: var(--sidebar);
  flex: none;
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: 16px 14px calc(16px + env(safe-area-inset-bottom));
  border-right: 1px solid var(--line);
  background: var(--surface);
}

.rail {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 20px;
  scrollbar-width: thin;
}

.block { display: grid; gap: 9px; }

.block h2 {
  margin: 0;
  font-size: 11px;
  font-weight: 650;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
}

.swatches {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}

.swatch {
  aspect-ratio: 1 / 1;
  width: 100%;
  border-radius: 50%;
  border: 2px solid transparent;
  box-shadow: inset 0 0 0 1px rgba(36, 31, 51, 0.12);
  transition: transform 0.1s;
}

.swatch:hover { transform: scale(1.08); }

.swatch[aria-pressed="true"] {
  border-color: var(--lavender);
  box-shadow: 0 0 0 2px rgba(195, 180, 242, 0.45);
}

.sizes {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
}

.size {
  aspect-ratio: 1 / 1;
  border-radius: 9px;
  display: grid;
  place-items: center;
  background: var(--panel);
  border: 1px solid var(--line);
}

.size i {
  display: block;
  background: var(--muted);
  border-radius: 50%;
  /* Backstop only. The ramp above is sized to fit the smallest cell the layout
     uses (16px of content in the rotated strip), so this should never bite; it is
     here so a future brush size cannot spill out and get sliced by the rail. */
  max-width: 90%;
  max-height: 90%;
}

.size[aria-pressed="true"] {
  border-color: transparent;
  background: var(--grad);
}

.size[aria-pressed="true"] i { background: var(--grad-ink); }

.tool {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 11px;
  border-radius: 10px;
  font-size: 13.5px;
  color: var(--muted);
  text-align: left;
}

.tool svg {
  width: 17px;
  height: 17px;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.tool:hover:not([disabled]) { color: var(--text); background: var(--panel-2); }

.tool[aria-pressed="true"] {
  color: var(--grad-ink);
  background: var(--grad);
}

.tool[disabled] { opacity: 0.38; cursor: default; }

/* ----------------------------------------------------------------- photo */
/* The chip replaces the Add photo button once something is picked, so the rail
   height does not jump and the thumbnail is proof the right file landed. */

.photo-chip {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px;
  border-radius: 10px;
  background: var(--panel-2);
  border: 1px solid var(--line);
}

.photo-chip img {
  width: 34px;
  height: 34px;
  flex: none;
  border-radius: 7px;
  object-fit: cover;
  background: var(--line);
}

.photo-name {
  flex: 1;
  min-width: 0;
  font-size: 12px;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.photo-remove {
  flex: none;
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  border-radius: 6px;
  color: var(--muted);
}

.photo-remove svg {
  width: 13px;
  height: 13px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.1;
  stroke-linecap: round;
}

.photo-remove:hover { color: var(--danger); background: var(--panel); }

/* Compact rails have no room for a row, so the remove button rides the corner of
   the thumbnail instead of sitting beside it. */
@media (max-width: 900px) {
  .photo-chip { position: relative; }
  .photo-remove {
    position: absolute;
    top: -5px;
    right: -5px;
    background: var(--panel);
    border: 1px solid var(--line-strong);
    box-shadow: var(--shadow-soft);
  }
}

.sidebar-foot {
  flex: none;
  display: grid;
  gap: 8px;
}

.generate {
  width: 100%;
  padding: 13px 16px;
  border-radius: 12px;
  font-weight: 650;
  color: var(--grad-ink);
  background: var(--grad);
  box-shadow: 0 6px 18px rgba(195, 180, 242, 0.45);
  transition: filter 0.15s, transform 0.06s;
}

.generate:hover { filter: brightness(1.04) saturate(1.06); }
.generate:active { transform: translateY(1px); }
.generate[disabled] { opacity: 0.45; cursor: default; box-shadow: none; }

.signout {
  width: 100%;
  padding: 8px;
  border-radius: 9px;
  font-size: 12.5px;
  color: var(--muted);
}

.signout:hover { color: var(--text); background: var(--panel-2); }

/* ------------------------------------------------------------------ canvas */

.stage {
  flex: 1;
  min-width: 0;
  display: grid;
  place-items: center;
  padding: var(--gap);
}

.paper {
  position: relative;
  aspect-ratio: 16 / 9;
  width: min(100%, calc((100dvh - var(--gap) * 2) * 16 / 9));
  background: #fff;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
}

canvas {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;
  cursor: crosshair;
}

/* --------------------------------------------------------------- text bar */
/* Typing a label beats fingerpainting one, and the reader only ever needs the word
   legible. The bar lives inside .paper, so it shares the canvas coordinate space and
   rides the quarter-turn rotation with everything else. Its position is held in
   normalised 0..1 coords by JS, exactly like the strokes. */

.textbox {
  position: absolute;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 3px;
  padding: 5px;
  max-width: 88%;
  border-radius: 13px;
  background: rgba(255, 255, 255, 0.97);
  border: 1.5px solid var(--lavender);
  box-shadow: 0 8px 26px rgba(122, 100, 168, 0.28);
  touch-action: none;   /* the grip drags; the browser must not pan instead */
}

.textbox.dragging { box-shadow: 0 14px 34px rgba(122, 100, 168, 0.4); }

/* The input is the preview: same font, size and colour as the committed text, so
   what is typed sits exactly where it will land. JS sets font-size and color. */
.tb-input {
  flex: none;
  min-width: 0;
  margin: 0;
  border: 0;
  outline: 0;
  box-shadow: none;
  appearance: none;
  -webkit-appearance: none;
  padding: 0 6px;
  background: none;
  font-family: inherit;
  font-weight: 600;
  /* Must clear the font's full ascender-to-descender span: an input clips its
     content rather than overflowing it, so anything tighter slices descenders. */
  line-height: 1.42;
}

.tb-input:focus { outline: 0; box-shadow: none; }

/* Held well below the text size on purpose: at the full preview size the
   placeholder alone set the bar's width and swamped the canvas. */
.tb-input::placeholder {
  color: #bdb6cf;
  font-weight: 500;
  font-size: 13px;
}

.textbox button {
  flex: none;
  display: grid;
  place-items: center;
  width: 27px;
  height: 27px;
  border-radius: 7px;
  color: var(--muted);
}

.textbox button svg {
  width: 14px;
  height: 14px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.1;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.tb-grip { cursor: grab; }
.tb-grip { width: 22px; }
.tb-grip svg { fill: currentColor; stroke: none; width: 15px; height: 15px; }
.textbox.dragging .tb-grip { cursor: grabbing; }

.tb-arrow[aria-pressed="true"] { color: var(--grad-ink); background: var(--panel-2); }
.tb-ok { color: var(--grad-ink); background: var(--grad); }
.tb-ok[disabled] { opacity: 0.4; cursor: default; }
.tb-x:hover { color: var(--danger); background: var(--panel-2); }

/* The arrow's target. Dragged around the canvas to aim at whatever the label
   names; the curve itself is painted onto the canvas, not drawn in the DOM, so
   what is previewed is exactly what gets committed and exported. */
.arrow-tip {
  position: absolute;
  z-index: 3;
  width: 30px;
  height: 30px;
  margin: -15px 0 0 -15px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.82);
  border: 2px solid var(--lavender);
  box-shadow: 0 3px 10px rgba(122, 100, 168, 0.3);
  cursor: grab;
  touch-action: none;
}

.arrow-tip::after {
  content: "";
  position: absolute;
  inset: 8px;
  border-radius: 50%;
  background: var(--lavender);
}

.arrow-tip.dragging { cursor: grabbing; transform: scale(1.12); }

.hint {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  gap: 4px;
  padding: 20px;
  text-align: center;
  color: #a9a2bd;
  pointer-events: none;
  transition: opacity 0.25s;
}

.hint strong { color: #6e6786; font-weight: 650; font-size: 18px; }
.hint span { font-size: 13.5px; }
.hint.gone { opacity: 0; }

/* ------------------------------------------------------------------ result */

.result {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: none;
  place-items: center;
  padding: 20px;
  background: rgba(250, 248, 253, 0.88);
  backdrop-filter: blur(12px);
}

.result.open { display: grid; }

.result-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  max-width: 100%;
}

.frame {
  position: relative;
  aspect-ratio: 16 / 9;
  width: min(86vw, calc((100dvh - 136px) * 16 / 9));
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--panel);
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
}

.frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.loading {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  gap: 14px;
  justify-items: center;
  background: var(--panel);
}

.loading.hidden { display: none; }

.spinner {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 3px solid var(--line);
  border-top-color: var(--lavender);
  border-right-color: var(--pink);
  animation: spin 0.85s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

.loading p {
  margin: 0;
  color: var(--muted);
  font-size: 13.5px;
}

.result-actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: center;
}

.pill {
  padding: 10px 18px;
  border-radius: 11px;
  font-size: 14px;
  border: 1px solid var(--line-strong);
  background: var(--panel);
  color: var(--text);
}

.pill:hover { background: var(--panel-2); }

.pill.solid {
  border-color: transparent;
  color: var(--grad-ink);
  background: var(--grad);
  font-weight: 650;
  box-shadow: var(--shadow-soft);
}

.note {
  margin: 0;
  font-size: 12px;
  color: var(--muted);
}

.toast {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translate(-50%, 20px);
  z-index: 40;
  padding: 11px 18px;
  border-radius: 11px;
  background: var(--panel);
  border: 1px solid var(--line-strong);
  box-shadow: var(--shadow);
  font-size: 13.5px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s, transform 0.2s;
}

.toast.show { opacity: 1; transform: translate(-50%, 0); }

/* ------------------------------------------------- narrow: icon-only rail */

/* Tablets and small laptops: keep the sidebar, shrink it to icons. Applies in
   both orientations, since an iPad has room for a real 16:9 canvas either way. */
@media (max-width: 900px) {
  :root { --sidebar: 68px; --gap: 10px; }

  .sidebar { padding: 12px 8px calc(12px + env(safe-area-inset-bottom)); gap: 14px; }
  .sidebar .brand { justify-content: center; }
  .sidebar .brand h1 { display: none; }
  .block h2 { display: none; }
  .swatches { grid-template-columns: repeat(2, 1fr); gap: 6px; }
  .sizes { grid-template-columns: repeat(2, 1fr); }
  .tool { justify-content: center; padding: 9px 0; }
  .tool span { display: none; }
  .photo-name { display: none; }
  .textbox button { width: 25px; height: 25px; }
  .textbox { max-width: 92%; }
  .photo-chip { padding: 0; border: 0; background: none; justify-content: center; }
  .photo-chip img { width: 100%; height: auto; aspect-ratio: 1; border-radius: 9px; }
  .generate { padding: 12px 4px; font-size: 13px; }
  .signout { font-size: 11px; }
  .hint strong { font-size: 16px; }
  .hint span { font-size: 12px; }
  .frame { width: min(92vw, calc((100dvh - 146px) * 16 / 9)); }
  .pill { padding: 9px 14px; font-size: 13px; }
  .note { font-size: 11.5px; }
}

/* Short landscape phones: the rail has to give up more height. */
@media (max-height: 480px) {
  :root { --sidebar: 58px; --gap: 8px; }
  .rail { gap: 12px; }
  .sizes { grid-template-columns: repeat(2, 1fr); gap: 4px; }
  .swatches { gap: 5px; }
  .tool { padding: 7px 0; }
  .tool svg { width: 15px; height: 15px; }
  .photo-chip img { border-radius: 7px; }
  .generate { padding: 10px 4px; font-size: 12px; }
  .signout { display: none; }
  .result-actions { gap: 7px; }
  .note { display: none; }
}

/* ---------------------------------------------------------------- rotation */
/* A 16:9 canvas on a portrait touch screen collapses to a letterbox strip, so the
   whole app is turned on its side and drawn landscape instead. The .rot class is
   set from JS off one media query, so the pointer maths and the layout can never
   disagree about whether the app is rotated.

   The turn is anti-clockwise (-90deg) on purpose: it puts the toolbar along the
   physical bottom of the phone and runs the controls left to right. Turning the
   other way lands the toolbar at the top in reverse order. */
html.rot, html.rot body { overflow: hidden; }

html.rot .app {
  position: fixed;
  top: 100dvh;
  left: 0;
  width: 100dvh;   /* swapped: the app's width runs across the screen's height */
  height: 100dvw;
  transform-origin: 0 0;
  transform: rotate(-90deg);
}

/* Anything sized against the viewport has to swap axes too. */
html.rot .paper { width: min(100%, calc((100dvw - var(--gap) * 2) * 16 / 9)); }
html.rot .frame { width: min(86dvh, calc((100dvw - 118px) * 16 / 9)); }

/* Rotated, the sidebar becomes a strip whose LENGTH is the screen's width - about
   400px on a phone. The upright rail does not fit in that, which is what clipped the
   swatches and forced a scrollbar. So it gets thicker and lays the controls out in
   more columns, trading strip thickness (of which there is plenty) for length. */
html.rot { --sidebar: 106px; --gap: 8px; }

html.rot .sidebar {
  padding: 8px 9px;
  padding-left: calc(9px + env(safe-area-inset-bottom));
  gap: 9px;
}

html.rot .rail {
  gap: 11px;
  overflow: hidden;          /* it fits now; never show a scrollbar */
  justify-content: center;
}

/* Fixed track sizes, not 1fr: with aspect-ratio in the mix the flexible tracks
   resolve wider than the strip and spill out of it. A 106px strip minus 18px of
   padding leaves 88px; both rows below are sized to leave a few px of slack inside
   that, because an exact fit leaves the largest brush dot on the clip boundary. */
html.rot .swatches {
  grid-template-columns: repeat(3, 24px);   /* 3*24 + 2*5 = 82, in 88 */
  gap: 5px;
  justify-content: center;
}
html.rot .swatch { width: 24px; height: 24px; aspect-ratio: auto; }

html.rot .sizes {
  grid-template-columns: repeat(4, 19px);   /* 4*19 + 3*3 = 85, in 88 */
  gap: 3px;
  justify-content: center;
}
html.rot .size { width: 19px; height: 19px; aspect-ratio: auto; }

html.rot .block h2,
html.rot .brand h1,
html.rot .tool span { display: none; }

html.rot .brand { justify-content: center; }
html.rot .tool { justify-content: center; padding: 7px 0; }
html.rot .tool svg { width: 16px; height: 16px; }
html.rot .generate { padding: 10px 4px; font-size: 12.5px; }
html.rot .signout { padding: 5px; font-size: 10.5px; }

/* The rail fits by the pixel, so the photo block buys its height back from the
   surrounding padding rather than pushing the rest off the strip. */
html.rot .rail { gap: 9px; }
html.rot .tool { padding: 6px 0; }
html.rot .photo-chip { position: relative; padding: 0; border: 0; background: none; }
html.rot .photo-chip img { width: 100%; height: auto; aspect-ratio: 1; border-radius: 8px; }
html.rot .photo-name { display: none; }
html.rot .photo-remove {
  position: absolute;
  top: -5px;
  right: -5px;
  background: var(--panel);
  border: 1px solid var(--line-strong);
  box-shadow: var(--shadow-soft);
}
