.topbar {
  position: sticky; top: 0; z-index: 200;
  background: var(--bg); border-bottom: 2.5px solid var(--ink);
  height: var(--topbar-h);
}
.topbar-inner {
  /* Matches the .app rail exactly (same max-width + margin auto) so the
     brand at the far-left and the topbar actions sit flush with the
     sidebar's left edge and the canvas's right edge below. */
  max-width: var(--content-w); margin: 0 auto;
  padding: 0.6rem 1rem;
  height: 100%;
  display: flex; justify-content: space-between; align-items: center;
  gap: 0.5rem;
}
.topbar-brand {
  font-family: 'Fredoka', sans-serif; font-weight: 700; font-size: 1.05rem;
  display: flex; align-items: center; gap: 0.5rem; letter-spacing: -0.01em;
  /* Defensive resets now that this is an <a>. Kill the default link
     color, underline, focus outline, and the mobile tap-highlight flash. */
  color: var(--ink); text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  outline: none;
}
.topbar-brand:hover { text-decoration: none; }
.brand-text {
  display: inline-flex; align-items: baseline; gap: 0.3em;
  line-height: 1;
}
.brand-prefix {
  font-weight: 600; font-size: 0.92rem;
  color: var(--ink-soft);
}
.brand-dot {
  width: 26px; height: 26px; flex-shrink: 0;
  overflow: visible;
}
.brand-dot .brand-dot-fill { fill: var(--sun); }
.brand-dot .brand-dot-grid { fill: url(#brand-dot-grid); }
.brand-dot .brand-dot-outline {
  fill: none; stroke: var(--ink); stroke-width: 2; stroke-linejoin: round;
}
.topbar-nav { display: flex; gap: 0.25rem; font-weight: 600; font-size: 0.85rem; }
.lang-picker { display: inline-flex; align-items: center; }
.lang-switcher {
  height: 34px;
  border: 1.5px solid var(--ink);
  border-radius: 999px;
  background: var(--paper);
  color: var(--ink);
  font-family: 'Fredoka', sans-serif;
  font-size: 0.78rem;
  font-weight: 600;
  padding: 0 0.85rem;
  cursor: pointer;
}
.topbar-nav a {
  color: var(--ink); text-decoration: none;
  padding: 0.3rem 0.7rem; border-radius: 10px; transition: all 0.15s ease;
}
.topbar-nav a:hover { background: var(--sun); }
.topbar-nav .topbar-help { white-space: nowrap; }
.topbar-brand, .brand-text { white-space: nowrap; }

.app {
  display: grid; grid-template-columns: var(--sidebar-w) 1fr;
  min-height: calc(100vh - var(--topbar-h));
  min-height: calc(100dvh - var(--topbar-h));
  max-width: var(--content-w); margin: 0 auto;
}

/* "More options" disclosure inside the sidebar. On desktop this is always
   expanded — the toggle is hidden and the body stays visible. On mobile
   the toggle shows and the body collapses unless the disclosure is open. */
.sb-extras { display: contents; }
.sb-extras-toggle { display: none; }
.sb-extras-body { display: contents; }

.sidebar {
  background: var(--paper); border-right: 2.5px solid var(--ink);
  padding: 1rem; overflow-y: auto; overflow-x: hidden;
  position: sticky; top: var(--topbar-h);
  max-height: calc(100vh - var(--topbar-h));
  max-height: calc(100dvh - var(--topbar-h));
  display: flex; flex-direction: column; gap: 1rem;
  min-width: 0;
}

.sb-section { display: flex; flex-direction: column; gap: 0.5rem; min-width: 0; }
.sb-heading {
  display: flex; align-items: center; gap: 0.4rem;
  font-family: 'Fredoka', sans-serif; font-weight: 600; font-size: 0.72rem;
  color: var(--ink-soft); text-transform: uppercase; letter-spacing: 0.08em;
}
.sb-heading-ico {
  font-size: 0.95rem; line-height: 1; flex-shrink: 0;
  /* Kill the full-colour glyph so emoji sit quietly next to the label
     instead of shouting. */
  filter: grayscale(0.1);
}

.main {
  padding: 1rem; display: flex; flex-direction: column;
  gap: 0.9rem; min-width: 0;
  /* Shared column width for the workshop header, canvas stage, and palette
     card so all three visual blocks align to the same rail instead of the
     palette stretching the full width of .main on wide monitors. Caps a
     bit taller than the old 720px so the canvas can grow on 1440p+. */
  --studio-col: min(100%, calc(100dvh - 240px), 880px);
}
.main > .workshop-header,
.main > .card,
.main > .ad-slot,
.main > .stage-wrap {
  width: 100%;
  max-width: var(--studio-col);
}
/* Left-align inside .main on the two-column desktop layout so the canvas
   hugs the sidebar instead of floating in the middle with dead air on
   both sides. On mobile (single column) we want the canvas centered, so
   the narrow-viewport media query in responsive.css keeps the default
   auto margins. */
@media (min-width: 841px) {
  .main > .workshop-header,
  .main > .card,
  .main > .ad-slot,
  .main > .stage-wrap {
    margin-left: 0; margin-right: auto;
  }
}

/* Stats row + recipe row, always stacked — predictable two-line layout
   regardless of brush choice. Each row keeps its chips on a single line
   and scrolls horizontally if they overflow, instead of wrapping into a
   third or fourth row that shifts the canvas down the page. */
.workshop-header {
  display: flex; flex-direction: row; flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.8rem;
  padding: 0.6rem 0.85rem;
  background: var(--paper); border: 2px solid var(--ink); border-radius: 14px;
  box-shadow: 2px 2px 0 var(--ink);
}
/* Stats hug the left; recipe pushes to the right so the header reads as
   "what it is" on one side, "how to make it" on the other. When the row
   gets too tight, flex-wrap drops recipe to its own line. */
.workshop-header > .wh-recipe { margin-left: auto; }
.wh-stats, .wh-recipe {
  display: flex; flex-wrap: nowrap; align-items: center; gap: 0.4rem;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  /* Constrain touch gestures to horizontal only. Without this, iOS and
     Android let the user "rubber-band" up/down inside the scroller even
     when everything fits, which felt like a layout bug. */
  touch-action: pan-x;
  overscroll-behavior: contain;
  /* Keep each group at its intrinsic width so the outer header wraps the
     whole group to a new line rather than shrinking either one into its
     own scrollable container. Overridden on mobile where horizontal scroll
     inside each group is the preferred fallback. */
  flex-shrink: 0;
}
.wh-stats::-webkit-scrollbar, .wh-recipe::-webkit-scrollbar { display: none; }
.wh-recipe-title {
  font-family: 'Fredoka', sans-serif; font-weight: 700; font-size: 0.78rem;
  padding: 0.18rem 0.55rem; background: var(--sun);
  border: 1.5px solid var(--ink); border-radius: 8px;
  letter-spacing: 0.01em;
}
.wh-chip {
  display: inline-flex; align-items: baseline; gap: 0.3rem;
  font-family: 'Fredoka', sans-serif; font-size: 0.78rem; color: var(--ink-soft);
  padding: 0.22rem 0.55rem;
  background: var(--cream); border: 1.5px solid var(--ink); border-radius: 100px;
  white-space: nowrap;
}
.wh-chip b {
  font-family: 'Fredoka', sans-serif; font-weight: 700; font-size: 0.86rem;
  color: var(--ink);
}
.wh-chip.accent { background: var(--paper); }
.wh-chip-label { color: var(--ink-muted); text-transform: uppercase; letter-spacing: 0.04em; font-size: 0.66rem; }
.wh-help { background: var(--paper); }

/* The wrap is the visible square — anchors the absolute-positioned zoom
   controls and download button, and defines the aspect-locked size so
   the inner .stage always fills a perfect square. */
.stage-wrap {
  position: relative;
  flex: none;
  width: var(--studio-col, min(100%, calc(100vh - 240px), 880px));
  aspect-ratio: 1 / 1;
  margin: 0 auto;
  display: flex; flex-direction: column;
}

.stage {
  background: var(--cream); border: 2.5px solid var(--ink);
  border-radius: 14px; padding: 0.75rem;
  /* Plain block + text-align on the stage, inline-block holder. This
     centers the canvas when it fits and lets overflow scroll in both
     directions when it doesn't — flex `justify-content: center` clipped
     the leading edge (Safari doesn't consistently honour `safe center`). */
  display: block; text-align: center;
  /* overflow starts hidden so single-finger swipes bubble to the page
     when the canvas fits; JS flips to `auto` once the user zooms past
     fit, turning the stage into a scroll container for panning. */
  overflow: hidden;
  box-shadow: inset 0 0 0 2.5px var(--paper);
  flex: 1; min-height: 0; width: 100%;
  touch-action: auto;
  -webkit-overflow-scrolling: touch;
}
.stage.zoomed { touch-action: pan-x pan-y; cursor: grab; }
.stage.panning { cursor: grabbing; user-select: none; }
.stage.panning .canvas-holder { pointer-events: none; }

.zoom-ctrls {
  position: absolute; top: 0.6rem; right: 0.6rem; z-index: 3;
  display: inline-flex; align-items: center; gap: 0.25rem;
  background: var(--paper); border: 2px solid var(--ink);
  border-radius: 100px; padding: 0.2rem 0.35rem;
  box-shadow: 2px 2px 0 var(--ink);
  font-family: 'Fredoka', sans-serif;
}
.zoom-ctrls button {
  height: 28px; min-width: 28px; border-radius: 100px;
  border: 1.5px solid var(--ink); background: var(--sun);
  font-family: 'Fredoka', sans-serif; font-weight: 700; font-size: 1rem;
  line-height: 1; cursor: pointer; color: var(--ink);
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0 0.35rem; transition: transform 0.1s ease;
}
.zoom-ctrls button:hover { transform: translateY(-1px); }
.zoom-ctrls button:active { transform: translateY(1px); }

/* Primary save action — lives on top of the stage, bottom-right, so users
   reach for it without hunting under the canvas. Coral-deep fill + grid
   pattern echo the brand mark. */
/* Download cluster — single pill that reveals a menu on hover/focus/click.
   Sky-blue fill with the brand dot-grid overlay, matching our logo feel. */
.download-cluster {
  position: absolute; bottom: 0.9rem; right: 0.9rem; z-index: 4;
}
.download-overlay {
  display: inline-flex; align-items: center; gap: 0.45rem;
  padding: 0.55rem 0.95rem;
  background-color: var(--sky);
  background-image:
    linear-gradient(to right, rgba(43, 30, 15, 0.18) 0.7px, transparent 0.7px),
    linear-gradient(to bottom, rgba(43, 30, 15, 0.18) 0.7px, transparent 0.7px);
  background-size: 10px 10px;
  color: var(--ink);
  border: 2.5px solid var(--ink); border-radius: 100px;
  box-shadow: 3px 3px 0 var(--ink);
  font-family: 'Fredoka', sans-serif; font-weight: 700; font-size: 0.95rem;
  cursor: pointer;
  transition: transform 0.25s var(--ease-back), box-shadow 0.25s var(--ease-back), background-color 0.15s ease;
}
.download-overlay:hover,
.download-cluster:hover .download-overlay,
.download-overlay[aria-expanded="true"] {
  transform: translate(-2px, -2px) rotate(-1.5deg); box-shadow: 5px 5px 0 var(--ink);
  background-color: var(--sun);
}
.download-overlay:active { transform: translate(1px, 1px); box-shadow: 1.5px 1.5px 0 var(--ink); }
.download-overlay .download-ico { font-size: 1.05rem; line-height: 1; }
.download-overlay .download-caret {
  font-size: 0.75rem; line-height: 1; margin-left: 0.15rem;
  transition: transform 0.15s ease;
}
.download-overlay[aria-expanded="true"] .download-caret { transform: rotate(180deg); }

/* The menu floats above the button. Hidden by default; shown on cluster
   hover (desktop) or when aria-expanded flips to true (click / keyboard /
   mobile tap). opacity + translate give a quick reveal without relying on
   display: none, which would break keyboard navigation. */
.download-menu {
  position: absolute;
  right: 0; bottom: calc(100% + 0.5rem);
  min-width: 420px;
  background: var(--paper);
  border: 2.5px solid var(--ink); border-radius: 14px;
  box-shadow: 3px 3px 0 var(--ink);
  padding: 0.35rem;
  display: flex; flex-direction: column; gap: 0.25rem;
  opacity: 0; visibility: hidden; pointer-events: none;
  transform: translateY(4px);
  transition: opacity 0.14s ease, transform 0.14s ease, visibility 0.14s;
}
.dl-menu-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.2rem;
}
.dl-menu-item--wide { grid-column: 1 / -1; }
@media (max-width: 480px) {
  .download-menu { min-width: min(280px, calc(100vw - 2rem)); }
  .dl-menu-row { grid-template-columns: 1fr; }
}
/* Invisible bridge covering the gap between menu-bottom and button-top.
   Keeps the cluster in :hover state as the mouse travels upward, so the
   menu stays open without the user having to click. */
.download-menu::after {
  content: '';
  position: absolute;
  left: 0; right: 0;
  top: 100%;
  height: calc(0.5rem + 4px);
}
.download-cluster:hover .download-menu,
.download-cluster:focus-within .download-menu,
.download-overlay[aria-expanded="true"] + .download-menu {
  opacity: 1; visibility: visible; pointer-events: auto;
  transform: translateY(0);
}
.download-menu-item {
  display: flex; flex-direction: column; align-items: flex-start; gap: 0.1rem;
  padding: 0.55rem 0.7rem;
  background: transparent;
  border: 1.5px solid transparent; border-radius: 10px;
  font-family: 'Fredoka', sans-serif; text-align: left; color: var(--ink);
  cursor: pointer;
  transition: background 0.1s ease, border-color 0.1s ease;
}
.download-menu-item:hover,
.download-menu-item:focus-visible {
  background: var(--sky); border-color: var(--ink);
  outline: none;
}
.dl-menu-title { font-weight: 700; font-size: 0.92rem; line-height: 1.1; }
.dl-menu-sub {
  font-family: 'Nunito', sans-serif; font-weight: 500;
  font-size: 0.72rem; color: var(--ink-soft);
}
.download-menu-item:hover .dl-menu-sub,
.download-menu-item:focus-visible .dl-menu-sub { color: var(--ink); }
/* Disabled menu item (currently: paint-by-numbers at 1px brush). Keeps
   the hover-tooltip flow intact — we dim the item but don't block
   pointer events so [data-tip] still fires on hover. */
.download-menu-item.is-disabled {
  opacity: 0.4; cursor: not-allowed;
}
.download-menu-item.is-disabled:hover,
.download-menu-item.is-disabled:focus-visible {
  background: transparent; border-color: transparent;
}
.download-menu-item.is-disabled:hover .dl-menu-sub,
.download-menu-item.is-disabled:focus-visible .dl-menu-sub { color: var(--ink-soft); }
/* Same pattern for toggle chips that are contextually unavailable. */
.toggle.is-disabled {
  opacity: 0.4; cursor: not-allowed;
}
.zoom-ctrls #zoom-reset {
  background: var(--cream); min-width: 46px;
  font-family: 'JetBrains Mono', monospace; font-weight: 600;
  font-size: 0.68rem;
}
.canvas-holder {
  position: relative;
  display: inline-block; border-radius: 6px; overflow: hidden;
}
canvas {
  display: block;
  image-rendering: pixelated;
  image-rendering: -moz-crisp-edges;
  image-rendering: crisp-edges;
}
/* Overlay sits exactly on top of the main canvas and shares its
   pixelated sampling so the dimmer/outline stays crisp when the stage is
   CSS-zoomed. pointer-events none so clicks/pinch still reach the stage. */
.highlight-layer {
  position: absolute; top: 0; left: 0;
  pointer-events: none;
  image-rendering: pixelated;
}

/* Dashed drop overlay, only visible while the stage has the `.dragging`
   class. Entire stage is the drop target — button is redundant once you
   can just throw an image at it. */
.drop-hint {
  position: absolute; inset: 0.75rem; z-index: 5;
  border: 3px dashed var(--coral-deep);
  border-radius: 12px;
  background: rgba(254, 215, 75, 0.7);
  backdrop-filter: blur(1px);
  display: none;
  align-items: center; justify-content: center;
  pointer-events: none;
  color: var(--ink);
}
.drop-hint-inner {
  display: inline-flex; align-items: center; gap: 0.6rem;
  padding: 0.75rem 1.1rem;
  font-family: 'Fredoka', sans-serif; font-weight: 700; font-size: 1rem;
  background: var(--paper); border: 2.5px solid var(--ink); border-radius: 100px;
  box-shadow: 3px 3px 0 var(--ink);
}
.drop-hint-emoji { font-size: 1.2rem; }
.stage.dragging .drop-hint { display: flex; }
.stage.dragging .canvas-holder { opacity: 0.35; transition: opacity 0.15s ease; }

.card {
  background: var(--paper); border: 2px solid var(--ink);
  border-radius: 14px; padding: 0.9rem 1rem;
  box-shadow: 2px 2px 0 var(--ink);
}
.card-head {
  display: flex; justify-content: space-between; align-items: center;
  margin-bottom: 0.6rem;
}
.card-title {
  font-family: 'Fredoka', sans-serif; font-weight: 600; font-size: 0.92rem;
  display: flex; align-items: center; gap: 0.35rem;
}

/* Reserved space for ad units. Default to collapsed — flip the
   [data-ads="on"] attribute on <body> once an ad provider is wired up. */
.ad-slot {
  display: none;
  min-height: 90px;
  border: 2px dashed var(--ink-muted);
  border-radius: 12px;
  background: var(--paper);
  color: var(--ink-muted);
  font-family: 'Fredoka', sans-serif; font-size: 0.75rem;
  align-items: center; justify-content: center;
  text-align: center;
}
body[data-ads="on"] .ad-slot { display: flex; }

.footer-info { padding: 2rem 1rem 3rem; max-width: 720px; margin: 0 auto; }
.view-studio .footer-info { display: none; }
.footer-info h2 {
  font-family: 'Fredoka', sans-serif; font-weight: 700; font-size: 1.35rem;
  text-align: center; margin-bottom: 0.5rem;
}
.footer-info > p {
  text-align: center; font-family: 'Fredoka', sans-serif; font-weight: 500;
  color: var(--ink-soft); margin-bottom: 1.25rem; font-size: 0.85rem;
}
.faq {
  background: var(--paper); border: 2px solid var(--ink);
  border-radius: 12px; margin-bottom: 0.5rem;
  box-shadow: 2px 2px 0 var(--ink); overflow: hidden;
}
.faq summary {
  padding: 0.6rem 0.9rem; font-family: 'Fredoka', sans-serif;
  font-weight: 600; font-size: 0.88rem; cursor: pointer; list-style: none;
  display: flex; justify-content: space-between; align-items: center;
}
.faq summary h3 {
  margin: 0;
  font: inherit;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: '+'; font-size: 1.2rem; font-weight: 600; color: var(--coral-deep);
  transition: transform 0.2s ease;
}
.faq[open] summary::after { transform: rotate(45deg); }
.faq-body {
  padding: 0 0.9rem 0.7rem; font-size: 0.82rem; color: var(--ink-soft); line-height: 1.5;
}
.contact-card {
  margin: 2rem auto 0;
  max-width: 480px;
  padding: 1.1rem 1.4rem 1.3rem;
  text-align: center;
  background: color-mix(in srgb, var(--teal, #78DEF4) 30%, var(--paper, #FFF7E0));
  border: 2.5px solid var(--ink); border-radius: 16px;
  box-shadow: var(--shadow-pop);
  transform: rotate(-0.6deg);
  transition: transform 0.35s var(--ease-back);
}
.contact-card:hover { transform: rotate(0.4deg) translateY(-2px); }
.contact-title {
  margin: 0 0 0.35rem;
  font-family: 'Fredoka', sans-serif; font-weight: 700;
  font-size: 1.05rem; color: var(--ink);
}
.contact-body {
  margin: 0 0 0.9rem;
  font-family: 'Nunito', sans-serif; font-size: 0.9rem;
  color: var(--ink-soft, var(--ink)); line-height: 1.5;
}
.contact-btn {
  display: inline-flex; align-items: center; gap: 0.5rem;
  padding: 0.55rem 1.1rem;
  background: var(--sun, #FFDD00); color: var(--ink);
  border: 2.5px solid var(--ink); border-radius: 999px;
  box-shadow: 3px 3px 0 var(--ink);
  font-family: 'Fredoka', sans-serif; font-weight: 600;
  text-decoration: none;
  transition: transform 0.25s var(--ease-back), box-shadow 0.25s var(--ease-back);
}
.contact-btn:hover {
  transform: translate(-2px, -2px) rotate(-2deg);
  box-shadow: 5px 5px 0 var(--ink);
}

.foot-credit {
  text-align: center; margin: 1.5rem auto 0;
  max-width: var(--content-w);
  padding: 0 1rem calc(2rem + env(safe-area-inset-bottom));
  font-family: 'Fredoka', sans-serif; font-size: 0.72rem; color: var(--ink-muted);
}
.foot-version {
  display: inline-block; margin-left: 0.5rem; opacity: 0.6;
  font-family: 'JetBrains Mono', monospace; font-size: 0.68rem;
}
.foot-legal {
  display: block; margin-top: 0.6rem;
  font-size: 0.72rem;
}
.foot-legal a {
  color: var(--ink-muted); text-decoration: underline;
  text-underline-offset: 2px;
}
.foot-legal a:hover { color: var(--ink); }
.foot-legal span { margin: 0 0.4rem; opacity: 0.5; }

.toast {
  position: fixed; bottom: 1rem; left: 50%;
  transform: translate(-50%, 20px);
  background: var(--ink); color: var(--cream);
  padding: 0.55rem 1rem; border-radius: 100px;
  font-family: 'Fredoka', sans-serif; font-size: 0.82rem; font-weight: 500;
  box-shadow: 3px 3px 0 rgba(43, 30, 15, 0.3);
  opacity: 0; pointer-events: none;
  transition: opacity 0.25s ease, transform 0.25s ease;
  z-index: 400;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
