/* ═══════════════════════════════════════════════════════ */
/* Mal Canvas — shell chrome                               */
/* Device frame, side panels, state chips, zoom controls.  */
/*                                                          */
/* Self-contained on purpose: every value here is namespaced */
/* --canvas-* so the design system owns everything inside    */
/* .phone and can never collide with the harness.            */
/* ═══════════════════════════════════════════════════════ */

/* Cascade layer order for the whole repo. Declared here because template.css
   loads first. Later layers win, so a prototype adjusts layout without
   !important. Harness styles below stay unlayered — they are the frame, not
   the content, and nothing should override them. */
@layer reset, tokens, components, prototype;

:root {
  /* Surfaces */
  --canvas-page:     #101010;
  --canvas-panel:    #191919;
  --canvas-elevated: #232323;
  --canvas-raised:   #302F2F;
  --canvas-hover:    #3A3A3A;
  --canvas-screen:   #191919;   /* empty phone interior — DS replaces this */

  /* Lines — every stroke in the chrome routes through these two */
  --canvas-border:   rgba(255,255,255,0.05);
  --canvas-border-2: rgba(255,255,255,0.08);

  /* Text */
  --canvas-text-1:   #FFFFFF;
  --canvas-text-2:   rgba(255,255,255,0.72);
  --canvas-text-3:   rgba(255,255,255,0.56);
  --canvas-inv:      #000000;
  /* The credit heart. A LITERAL, deliberately: this file must not read a design
     system token. It briefly used var(--icon-brand), which meant the heart was
     green on pages that load tokens.css and grey on the empty canvas, which
     loads only this file. Same value as icon/brand — kept in step by hand,
     which is the price of the harness staying self-contained. */
  --canvas-heart:    #2BD673;

  /* Side-panel content inset. The panels themselves carry no horizontal padding;
     each block inside owns its own, so a block like the credit lands in the same
     place whichever panel it is in. That is what the gallery and the prototype
     canvas disagreed about. */
  --sp-pad-x:        12px;
  --sp-pad-y:        20px;

  /* Depth + motion */
  --canvas-shadow:   0 4px 16px rgba(0,0,0,0.40);
  --canvas-ease:     cubic-bezier(0.2, 0, 0, 1);
  --canvas-focus:    #4C9FFF;   /* focus ring — never a brand colour, this is chrome */

  /* ─── Device geometry — iPhone 17 (6.3") ───
     ONE definition of the phone, derived rather than hand-totalled. Change the
     screen or a band and the outer size and every radius follow.

       screen 402x874   the iPhone 17 logical viewport, in points
       bezel  8px       black surround — the 17 Pro's bezel is notably thinner
                        than the 15's, which is most of why it reads as a newer
                        device
       rail   4px       the aluminium band. The Pro went back to aluminium in
                        2025, which is a slightly warmer, less grey metal than
                        the titanium it replaced
       radius 62pt      display corner radius on the 6.3" models

     ⚠ The design system is drawn at 393x852 (see the ambient note in
     components.css and the Figma frames). This viewport is 9pt wider and 22pt
     taller, so anything absolutely positioned against the old width — the home
     dial above all — wants checking against the gallery.

     Radii are CONCENTRIC: an enclosing radius is the inner radius plus the gap
     between them. Get this wrong and the black band visibly fattens at the
     corners — which is what 72/69/62 over 4px/9px insets was doing. */
  --device-screen-w: 402px;
  --device-screen-h: 874px;
  --device-bezel:    8px;
  --device-rail:     4px;
  --device-screen-r: 62px;

  /* Dynamic Island. Narrower than the 15/16's 125pt pill. */
  --device-island-w: 112px;
  --device-island-h: 36px;

  /* ─── Device finish ───
     Anodised aluminium in the dark colourway. Three tones, and every metal
     surface — body sweep and all four side buttons — is mixed from just these,
     so re-finishing the phone is a three-line change.

     Dark metal has to stay clearly lighter than the #000 bezel it wraps and
     darker than the highlights, or the frame flattens into a drawn outline
     against the near-black canvas. The specular stops are what sell it as
     metal rather than a grey border. */
  --device-metal-hi:  #55555C;
  --device-metal-mid: #33333A;
  --device-metal-lo:  #1F1F24;

  --device-inset:    calc(var(--device-bezel) + var(--device-rail));
  --device-w:        calc(var(--device-screen-w) + 2 * var(--device-inset));
  --device-h:        calc(var(--device-screen-h) + 2 * var(--device-inset));
  --device-bezel-r:  calc(var(--device-screen-r) + var(--device-bezel));
  --device-r:        calc(var(--device-bezel-r) + var(--device-rail));

  /* Set by fitDevice(); the stage sizes itself from it, so layout and paint
     never disagree about how big the phone is. */
  --device-scale:    1;

  /* Rail widths — every layout offset derives from these, so the
     breakpoints below only ever need to change these three numbers */
  --rail-icons: 48px;
  --rail-left:  280px;
  --rail-right: 320px;

  /* How much the centre column yields to each side. Same as the rail widths
     while the panels are docked; zeroed once they become overlay drawers. */
  --off-l: calc(var(--rail-icons) + var(--rail-left));
  --off-r: var(--rail-right);
}

/* ─── Reset & base ───
   The `*` reset MUST live in @layer reset. Unlayered, it outranks every layered
   rule — which silently killed padding on .btn and margins inside components,
   because unlayered styles beat anything in @layer regardless of specificity. */
@layer reset {
  *, *::before, *::after { margin:0; padding:0; box-sizing:border-box; }
}
html { overflow-y:scroll; }
body {
  font-family:'Inter', -apple-system, sans-serif;
  background:var(--canvas-page);
  color:var(--canvas-text-1);
  letter-spacing:-0.25px;
  -webkit-font-smoothing:antialiased;
}
input, button, select, textarea, a { font-family:inherit; }

/* ─── Focus ───
   One ring for every interactive thing in the chrome. :focus-visible, so it
   appears for keyboard and assistive tech but never as a click artefact. The
   harness had no focus styling at all, which made it unusable without a mouse
   once the tabs became real buttons. Scoped to chrome only — anything inside
   .phone is the design system's business. */
.sp-icon-btn:focus-visible,
.sp-nav-item:focus-visible,
.canvas-tab:focus-visible,
.drawer-btn:focus-visible,
.zoom-btn:focus-visible,
.zoom-restart:focus-visible,
.state-chip:focus-visible,
.state-section-title:focus-visible {
  outline:2px solid var(--canvas-focus);
  outline-offset:2px;
}

/* Dark scrollbars for the panels. The default light ones cut a bright stripe
   down a near-black panel. */
.side-panel-left, .side-panel-right, .sp-tab-panel {
  scrollbar-width:thin;
  scrollbar-color:rgba(255,255,255,0.16) transparent;
}
.side-panel-left::-webkit-scrollbar,
.side-panel-right::-webkit-scrollbar,
.sp-tab-panel::-webkit-scrollbar { width:8px; height:8px; }
.side-panel-left::-webkit-scrollbar-thumb,
.side-panel-right::-webkit-scrollbar-thumb,
.sp-tab-panel::-webkit-scrollbar-thumb {
  background:rgba(255,255,255,0.16);
  border-radius:99px;
  border:2px solid transparent;
  background-clip:content-box;
}
.side-panel-left::-webkit-scrollbar-thumb:hover,
.side-panel-right::-webkit-scrollbar-thumb:hover,
.sp-tab-panel::-webkit-scrollbar-thumb:hover { background:rgba(255,255,255,0.28); background-clip:content-box; }
.side-panel-left::-webkit-scrollbar-track,
.side-panel-right::-webkit-scrollbar-track,
.sp-tab-panel::-webkit-scrollbar-track { background:transparent; }

/* ═══════════════════════════════════════ */
/* iPhone Device Frame                     */
/*                                          */
/* Three nested boxes: .device is the metal, */
/* .bezel the black surround, .phone the     */
/* screen. Only .phone belongs to the        */
/* prototype — the other two are furniture.  */
/* ═══════════════════════════════════════ */

/* Sized to the SCALED device so the centring flexbox has real numbers to work
   with. The old approach scaled .device and clawed the leftover layout box back
   with negative margins on three sides; any change to origin or padding broke
   the centring. Here layout and paint are the same size by construction. */
.device-stage {
  flex:none;
  width:calc(var(--device-w) * var(--device-scale));
  height:calc(var(--device-h) * var(--device-scale));
  /* Centres the device in the column, both axes. Auto margins rather than
     align-items:center because auto margins only consume POSITIVE free space —
     zoom in past the fit and they collapse to 0, so the phone anchors at the top
     and overflows downward where the page can scroll to it, instead of having
     its island clipped off above the column. */
  margin:auto;
}

.device {
  position:relative;
  width:var(--device-w); height:var(--device-h);
  padding:var(--device-rail);
  border-radius:var(--device-r);
  /* The 17 Pro's aluminium unibody. A shallow sweep with the light catching two
     edges; the stops are asymmetric on purpose, because a symmetrical gradient
     reads as plastic. Tones come from --device-metal-*. */
  background:
    linear-gradient(145deg,
      var(--device-metal-hi)  0%,
      var(--device-metal-mid) 22%,
      var(--device-metal-lo)  44%,
      var(--device-metal-mid) 60%,
      var(--device-metal-hi)  82%,
      var(--device-metal-lo)  100%);
  /* Both INSET, so neither can change the box: a 1px border here would eat 2px
     off the bezel and break the concentric maths above. The hairline is the lit
     outer edge, the darker ring is where the metal turns under toward the glass.
     No cast shadow — the device sits flat on the canvas by choice. */
  box-shadow:
    inset 0 0 0 1px rgba(255,255,255,0.16),
    inset 0 0 0 var(--device-rail) rgba(0,0,0,0.38);
  transform:scale(var(--device-scale));
  transform-origin:top left;
}

/* ─── Side buttons ───
   Left rail top-to-bottom: Action, Volume up, Volume down. Right: Power, set
   lower so it sits between the two volume keys, as on the real device. Both
   pseudo-elements are spoken for, so the extra keys are box-shadow clones —
   cheap, and they cannot drift out of alignment with their source. */
.device::before {
  content:'';
  position:absolute; right:calc(-1 * var(--device-rail)); top:214px;
  width:var(--device-rail); height:84px;
  background:linear-gradient(90deg,
    var(--device-metal-lo), var(--device-metal-hi) 60%, var(--device-metal-mid));
  border-radius:0 2px 2px 0;
}
.device::after {
  content:'';
  position:absolute; left:calc(-1 * var(--device-rail)); top:162px;
  width:var(--device-rail); height:32px;
  background:linear-gradient(270deg,
    var(--device-metal-lo), var(--device-metal-hi) 60%, var(--device-metal-mid));
  border-radius:2px 0 0 2px;
  /* Volume up / down. Flat mid-tone: box-shadow takes a colour, not a gradient,
     so the clones cannot carry the sweep their source has. */
  box-shadow:
    0 56px 0 0 var(--device-metal-mid),
    0 106px 0 0 var(--device-metal-mid);
}

.bezel {
  position:relative;       /* the Dynamic Island anchors here */
  width:100%; height:100%;
  background:#000;
  border-radius:var(--device-bezel-r);
  overflow:hidden;
  display:flex; align-items:center; justify-content:center;
}

.phone {
  width:var(--device-screen-w); height:var(--device-screen-h);
  flex-shrink:0;
  background:var(--canvas-screen);
  border-radius:var(--device-screen-r);
  overflow:hidden;
  position:relative;
  z-index:0;
  /* Both colours are fully OPAQUE, so as an alpha mask this paints nothing — it
     is not a vignette. The point is the mask layer itself: it forces the corner
     clip to antialias against a transformed ancestor, which overflow:hidden
     alone does not. It also makes .phone a backdrop root, so the design
     system's backdrop-filter glass samples the screen rather than the page
     behind it — which is what you want inside a phone. Do not "clean up". */
  -webkit-mask-image:-webkit-radial-gradient(white, black);
  mask-image:radial-gradient(white, black);
}

/* ─── Dynamic Island ───
   On .bezel, not .phone: it is hardware, and parking it here leaves
   .phone::before / ::after free for prototypes to use. Sits 11pt into the
   display; the pill is fully rounded, so the radius is just half the height. */
.bezel::before {
  content:'';
  position:absolute; z-index:2;
  top:calc(var(--device-bezel) + 11px); left:50%;
  transform:translateX(-50%);
  width:var(--device-island-w); height:var(--device-island-h);
  background:#000;
  border-radius:calc(var(--device-island-h) / 2);
}
/* No lens element. Any disc drawn on top of a pure-black pill reads as a grey
   circle rather than as camera glass — the rim highlight that was meant to hint
   at it was the thing making it visible. On real hardware at this size the lens
   is indistinguishable from the island, so the truest rendering is nothing at
   all. .bezel::after is free if something else ever needs it. */

/* ═══════════════════════════════════════ */
/* Canvas Layout (left + center + right)   */
/* ═══════════════════════════════════════ */
.canvas-wrap { width:100%; min-height:100vh; position:relative; }

/* ─── Canvas pages only ───
   template.js sets .is-canvas on <html> when it renders a device canvas. The DS
   gallery shares this stylesheet but lays itself out with its own fixed panes,
   so it must not get the column treatment below.

   A column: top bar at its natural height, then the view taking the rest. It is
   an exact HEIGHT, not min-height — the phone has to be able to make the column
   too small for it, otherwise a short window can never scale the device down. It
   grew to fit its content instead, and the device overflowed the viewport at
   full size. All scrolling therefore happens inside the view, not on the page. */
html.is-canvas, html.is-canvas body { height:100%; overflow:hidden; }
html.is-canvas .canvas-wrap {
  height:100vh; min-height:0;
  display:flex; flex-direction:column;
}

/* Icon sidebar (narrow, far left) */
.side-panel-icons {
  position:fixed; top:0; bottom:0; left:0;
  width:var(--rail-icons); z-index:101;
  background:var(--canvas-panel);
  border-right:1px solid var(--canvas-border);
  display:flex; flex-direction:column;
  align-items:center;
  padding:16px 0;
  gap:4px;
  box-sizing:border-box;
}
.sp-icon-btn {
  width:36px; height:36px; border-radius:8px;
  display:flex; align-items:center; justify-content:center;
  color:var(--canvas-text-3); cursor:pointer;
  text-decoration:none;
  transition:background .15s, color .15s;
}
.sp-icon-btn:hover,
.sp-icon-btn.on { background:var(--canvas-elevated); color:var(--canvas-text-1); }
/* Mal's gradient mark at the top of the rail. No radius and no hairline: those
   framed the old app tile, and a ring around a transparent mark just draws a
   box. The mark is 56x56.7, so contain keeps that half-pixel from stretching. */
.sp-app-icon {
  width:26px; height:26px; margin-bottom:10px;
  object-fit:contain;
}

.side-panel-left, .side-panel-right {
  position:fixed; top:0; bottom:0;
  width:var(--rail-left); z-index:100;
  background:var(--canvas-panel);
  display:flex; flex-direction:column;
  overflow-y:auto;
  box-sizing:border-box;
}
/* No horizontal or bottom padding — the blocks inside supply their own, so the
   credit sits identically here and in the gallery, whose sidebar is full-bleed
   for its nav. Only the top inset belongs to the panel. */
.side-panel-left {
  left:var(--rail-icons); border-right:1px solid var(--canvas-border);
  padding:var(--sp-pad-y) 0 0;
}
.side-panel-right {
  right:0; width:var(--rail-right);
  border-left:1px solid var(--canvas-border);
  padding:0; overflow-y:visible;
}

.sp-section-label {
  padding:0 var(--sp-pad-x);
  font-size:10px; font-weight:600; color:var(--canvas-text-3);
  text-transform:uppercase; letter-spacing:1px;
  margin-top:18px; margin-bottom:8px;
}
.sp-section-label:first-child { margin-top:0; }

.sp-nav { display:flex; flex-direction:column; gap:4px; padding:0 var(--sp-pad-x); }
.sp-nav-item {
  display:flex; align-items:center; gap:12px;
  padding:10px; text-decoration:none;
  border-radius:10px;
  border:1px solid transparent;
  transition:background .15s, border-color .15s, transform .1s;
}
.sp-nav-item:hover { background:var(--canvas-elevated); }
.sp-nav-item:active { transform:scale(.99); }
.sp-nav-item.on {
  background:var(--canvas-elevated);
  border-color:var(--canvas-border);
}
/* A saved AI prototype carries a delete control, so its row is a container with
   two siblings rather than a button inside a link. */
.sp-nav-row { display:flex; align-items:center; gap:4px; }
.sp-nav-row .sp-nav-item { flex:1; min-width:0; }
.sp-nav-del {
  flex:0 0 auto; width:26px; height:26px;
  display:flex; align-items:center; justify-content:center;
  border:0; border-radius:8px; background:transparent; cursor:pointer;
  color:var(--canvas-text-3); font-size:15px; line-height:1;
  opacity:0; transition:opacity .15s, background .15s, color .15s;
}
.sp-nav-row:hover .sp-nav-del, .sp-nav-del:focus-visible { opacity:1; }
.sp-nav-del:hover { background:var(--canvas-elevated); color:var(--canvas-text-1); }

.sp-nav-text { flex:1; min-width:0; }
.sp-nav-title {
  font-size:13px; font-weight:600; color:var(--canvas-text-1);
  letter-spacing:-.25px; margin-bottom:1px;
}
.sp-nav-sub {
  font-size:11px; color:var(--canvas-text-3);
  letter-spacing:-.1px;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}

/* Empty-state block inside a panel */
.sp-empty {
  text-align:center; padding:40px 16px;
  color:var(--canvas-text-3);
}
.sp-empty-title { font-size:13px; font-weight:600; color:var(--canvas-text-2); margin-bottom:6px; letter-spacing:-.25px; }
.sp-empty-sub { font-size:12px; color:var(--canvas-text-3); line-height:1.5; max-width:180px; margin:0 auto; }

/* ─── Sidebar credit footer ───
   THE definition, for the prototype canvas and the DS gallery both. The gallery
   used to restate all of this with a different heart, so the two drifted; it now
   uses the identical markup from creditHtml() in template.js and inherits from
   here. Change it once. */
/* Its own padding on all four sides, inheriting nothing from whichever panel it
   is in — that is what makes the position identical. It used to pick up the
   panel's padding too, so it sat 38px above the bottom on the canvas and flush
   at 0px in the gallery. */
.sp-credit {
  margin-top:auto;
  padding:var(--sp-pad-y) calc(var(--sp-pad-x) + 4px);
  display:flex; flex-direction:column; gap:3px;
  font-size:11px; line-height:1.5;
  color:var(--canvas-text-3); letter-spacing:-.1px;
  text-align:center;
}
.sp-credit-by { opacity:.72; }
/* A text heart, not the .icon-filled-heart glyph: the credit also appears on the
   empty canvas, which loads no icons.css. --canvas-heart is a harness literal,
   so the colour is the same on every page — see the note on the token. */
.sp-credit-heart {
  color:var(--canvas-heart);
  font-style:normal;
  vertical-align:-0.5px;
}

/* Right panel tabs (Notes / States) */
.sp-tabs-wrap { padding:16px 12px 12px; }
.sp-tabs-wrap .seg-control { width:100%; }
.sp-tabs-wrap .canvas-tab { flex:1; width:auto; }
.sp-tab-panel {
  flex:1; overflow-y:auto;
  padding:16px 12px;
  display:flex; flex-direction:column; gap:8px;
}
/* REQUIRED alongside the display:flex above. `hidden` only works through the UA
   sheet's `[hidden]{display:none}`, and any author display rule outranks it — so
   without this line a hidden panel stays on screen. */
.sp-tab-panel[hidden] { display:none; }

/* Notes body */
.sp-notes-body { gap:0; font-size:13px; color:var(--canvas-text-2); line-height:1.5; letter-spacing:-.1px; }
.sp-notes-body h2 { font-size:14px; font-weight:600; color:var(--canvas-text-1); letter-spacing:-.25px; margin:20px 0 8px; }
.sp-notes-body h2:first-child { margin-top:0; }
.sp-notes-body h3 { font-size:12px; font-weight:600; color:var(--canvas-text-1); letter-spacing:-.15px; margin:14px 0 6px; }
.sp-notes-body p { margin:0 0 8px; }
.sp-notes-body ul { margin:0 0 10px; padding-left:18px; }
.sp-notes-body li { margin-bottom:3px; }

/* ═══════════════════════════════════════ */
/* State chips (right panel)               */
/* ═══════════════════════════════════════ */
.state-section { display:flex; flex-direction:column; }
.state-section-title {
  display:flex; align-items:center; justify-content:space-between;
  font-size:13px; font-weight:600; color:var(--canvas-text-1);
  letter-spacing:-.25px;
  cursor:pointer;
  user-select:none;
  padding:14px 12px 12px;
  border:1px solid var(--canvas-border);
  border-radius:12px;
  background:var(--canvas-elevated);
  transition:background .15s, color .15s, border-color .15s;
}
.state-section .state-section-title:hover { color:var(--canvas-text-2); }
.state-section.open .state-section-title {
  border-bottom:none;
  border-bottom-left-radius:0;
  border-bottom-right-radius:0;
}
.state-section-chevron {
  flex-shrink:0;
  color:var(--canvas-text-3);
  transition:transform .2s var(--canvas-ease);
}
.state-section.open .state-section-chevron { transform:rotate(180deg); }
.state-section-body { display:none; }
.state-section.open .state-section-body {
  display:flex; flex-direction:column; gap:6px;
  background:var(--canvas-elevated);
  border:1px solid var(--canvas-border);
  border-top:none;
  border-bottom-left-radius:12px;
  border-bottom-right-radius:12px;
  padding:6px 10px 12px;
}

/* Restart, at the top of the States panel. Full width and quieter than a chip:
   a chip is a state you are choosing, this is the way back to the start. */
.sp-restart {
  display:flex; align-items:center; justify-content:center; gap:6px;
  width:100%; margin:0 0 14px;
  padding:9px 14px;
  background:transparent;
  border:1px solid var(--canvas-border-2);
  border-radius:12px;
  font:inherit; font-size:12px; font-weight:500; color:var(--canvas-text-2);
  letter-spacing:-.25px;
  cursor:pointer;
  transition:background .15s, border-color .15s, color .15s;
}
.sp-restart:hover { background:var(--canvas-elevated); color:var(--canvas-text-1); }
.sp-restart:active { transform:scale(.99); }

.state-chip {
  display:flex; align-items:center;
  padding:10px 14px;
  background:var(--canvas-raised);
  border:1px solid var(--canvas-border);
  border-radius:12px;
  font-size:13px; font-weight:500; color:var(--canvas-text-1);
  letter-spacing:-.25px;
  cursor:pointer;
  transition:background .15s, border-color .15s, transform .1s;
}
.state-chip:hover { background:var(--canvas-hover); }
.state-chip:active { transform:scale(.98); }
.state-chip.active {
  background:var(--canvas-text-1);
  color:var(--canvas-inv);
  border-color:var(--canvas-text-1);
}

/* ═══════════════════════════════════════ */
/* Center column                           */
/* ═══════════════════════════════════════ */
.center-top-bar {
  display:flex; justify-content:center; align-items:center;
  padding:20px 20px 8px;
  margin-left:var(--off-l); margin-right:var(--off-r);
}
/* flex:1, NOT min-height. With min-height the column grew to fit the phone, so
   its content height and the phone's height were the same number and there was
   never any free space for margin:auto to centre within — measured as exactly
   0px. Taking the leftover height of .canvas-wrap instead makes the box a fixed
   size the device can sit in the middle of.

   Overflow scrolls here rather than on the page, so zooming in never pushes the
   phone somewhere unreachable. scrollbar-gutter:stable reserves the scrollbar's
   width whether or not it is showing — without it, the bar appearing would
   narrow the column, which would shrink the fitted scale, which would hide the
   bar again: a visible oscillation. */
.view-proto {
  flex:1; min-height:0;   /* min-height:0 or a flex item refuses to shrink below its content */
  display:flex; justify-content:center; align-items:flex-start;
  overflow:auto;
  scrollbar-gutter:stable;
  padding:24px 20px 80px;
  margin-left:var(--off-l); margin-right:var(--off-r);
}
.view-flows {
  display:none;
  flex:1; min-height:0;
  overflow:auto;
  margin-left:var(--off-l); margin-right:var(--off-r);
  padding:40px 32px 80px;
}

/* Which view is showing is one class on <body>, so the flex/block distinction
   lives here rather than in inline styles set from JS. */
body.is-flows .view-proto,
body.is-flows .zoom-controls { display:none; }
body.is-flows .view-flows    { display:block; }

/* Seg control (Prototype/Flows tabs + panel tabs) */
.seg-control {
  display:flex; background:rgba(255,255,255,0.06); border-radius:8px; padding:2px;
}
/* These are <button>s now — strip the UA chrome the div version never had. */
.canvas-tab {
  width:100px; text-align:center;
  padding:6px 0; font-size:13px; font-weight:500; color:var(--canvas-text-3);
  font-family:inherit; letter-spacing:inherit;
  background:none; border:none;
  cursor:pointer; border-radius:6px;
  transition:background .2s var(--canvas-ease), color .2s var(--canvas-ease);
  user-select:none;
}
.canvas-tab:hover { color:var(--canvas-text-2); }
.canvas-tab.on {
  background:var(--canvas-raised); color:var(--canvas-text-1);
  box-shadow:0 1px 3px rgba(0,0,0,0.3);
}

/* ═══════════════════════════════════════ */
/* Zoom controls (fixed bottom-center)     */
/* ═══════════════════════════════════════ */
.zoom-controls {
  position:fixed; bottom:20px;
  left:calc(50% + (var(--off-l) - var(--off-r)) / 2);
  transform:translateX(-50%);
  display:flex; align-items:center; gap:6px;
  background:var(--canvas-elevated);
  border:1px solid var(--canvas-border-2);
  border-radius:999px;
  padding:6px 8px;
  box-shadow:var(--canvas-shadow);
  z-index:90;
}
.zoom-btn {
  width:28px; height:28px; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  background:transparent; border:none;
  color:var(--canvas-text-1); cursor:pointer;
  transition:background .15s;
}
.zoom-btn:hover { background:var(--canvas-raised); }
.zoom-level {
  min-width:44px; text-align:center;
  font-size:12px; font-weight:500; color:var(--canvas-text-2);
  letter-spacing:-.2px;
}
.zoom-divider { width:1px; height:16px; background:var(--canvas-border-2); }
.zoom-restart {
  display:inline-flex; align-items:center; gap:6px;
  height:28px; padding:0 12px;
  background:var(--canvas-text-1); color:var(--canvas-inv);
  border:none; border-radius:999px;
  font-size:12px; font-weight:500; letter-spacing:-.2px;
  cursor:pointer;
  transition:opacity .15s, transform .1s;
}
.zoom-restart:hover { opacity:.88; }
.zoom-restart:active { transform:scale(.97); }

/* ═══════════════════════════════════════ */
/* Canvas empty state (no prototype yet)   */
/* ═══════════════════════════════════════ */
.canvas-empty {
  height:100%;
  display:flex; flex-direction:column;
  align-items:center; justify-content:center;
  gap:12px; padding:40px;
  text-align:center;
}
.canvas-empty-title {
  font-size:16px; font-weight:600; color:var(--canvas-text-2);
  letter-spacing:-.25px;
}
.canvas-empty-sub {
  font-size:14px; color:var(--canvas-text-3);
  line-height:1.5; max-width:240px;
}

/* ═══════════════════════════════════════ */
/* Drawer chrome (small screens only)      */
/* ═══════════════════════════════════════ */
.drawer-btn {
  display:none;
  align-items:center; justify-content:center; gap:6px;
  height:32px; padding:0 12px;
  background:var(--canvas-elevated); color:var(--canvas-text-2);
  border:1px solid var(--canvas-border-2); border-radius:999px;
  font-size:12px; font-weight:500; letter-spacing:-.2px;
  cursor:pointer; white-space:nowrap;
  transition:background .15s, color .15s;
}
.drawer-btn:hover { background:var(--canvas-raised); color:var(--canvas-text-1); }
.drawer-btn:active { transform:scale(.97); }

.canvas-scrim {
  position:fixed; inset:0; z-index:110;
  background:rgba(0,0,0,0.60);
  opacity:0; pointer-events:none;
  transition:opacity .3s var(--canvas-ease);
}
.canvas-scrim.on { opacity:1; pointer-events:auto; }

/* ═══════════════════════════════════════ */
/* Breakpoints                             */
/* Only the rail/offset vars change — every */
/* layout rule above already derives from them */
/* ═══════════════════════════════════════ */

/* Tighten the rails before anything has to collapse */
@media (max-width:1440px) {
  :root { --rail-right:280px; }
}
@media (max-width:1280px) {
  :root { --rail-left:240px; --rail-right:248px; }
}

/* Panels become overlay drawers — the centre column gets the full width */
@media (max-width:1080px) {
  :root {
    --rail-left:280px;
    --rail-right:300px;
    --off-l:var(--rail-icons);
    --off-r:0px;
  }
  .side-panel-left, .side-panel-right {
    z-index:120;
    max-width:calc(100vw - var(--rail-icons));
    box-shadow:var(--canvas-shadow);
    transition:transform .3s var(--canvas-ease);
  }
  .side-panel-left  { transform:translateX(calc(-100% - var(--rail-icons))); }
  .side-panel-right { transform:translateX(100%); }
  .side-panel-left.open, .side-panel-right.open { transform:translateX(0); }

  .drawer-btn { display:inline-flex; }
  .center-top-bar { justify-content:space-between; gap:12px; }
}

/* Phone-width browsers: trim padding, let the device use what's left */
@media (max-width:640px) {
  :root { --rail-left:min(280px, 84vw); --rail-right:min(300px, 84vw); }
  .center-top-bar { padding:12px 12px 4px; }
  .view-proto     { padding:12px 12px 76px; min-height:calc(100vh - 60px); }
  .view-flows     { padding:24px 16px 76px; }
  .canvas-tab     { width:auto; padding:6px 14px; }
  .zoom-controls  { bottom:12px; }
}

/* Narrowest: drawer buttons go icon-only so the top bar never overflows */
@media (max-width:560px) {
  .drawer-btn-txt { display:none; }
  .drawer-btn { padding:0; width:32px; }
}

/* ═══════════════════════════════════════ */
/* Reduced motion                          */
/*                                          */
/* Harness transitions are feedback, not     */
/* decoration, so they shorten rather than   */
/* vanish — an instant drawer reads as a     */
/* glitch. The device transform is exempt:   */
/* it is layout, not animation.              */
/* ═══════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  .sp-icon-btn, .sp-nav-item, .canvas-tab, .drawer-btn,
  .zoom-btn, .zoom-restart, .state-chip, .state-section-title,
  .state-section-chevron, .canvas-scrim,
  .side-panel-left, .side-panel-right {
    transition-duration:.01ms;
  }
  .sp-nav-item:active, .state-chip:active,
  .zoom-restart:active, .drawer-btn:active { transform:none; }
}

/* ── Account & sharing ──
   The canvas is private to mal.ai, so the signed-in address and the way out
   sit in the sidebar, and a saved flow can be opened to a public link. */
.sp-account {
  margin-top: auto;
  padding: 12px 14px;
  border-top: 1px solid var(--border-subtle, rgba(255, 255, 255, .08));
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.sp-account-mail {
  font-size: 12px;
  opacity: .7;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.sp-account-out {
  align-self: flex-start;
  font: inherit;
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid var(--border-subtle, rgba(255, 255, 255, .14));
  background: transparent;
  color: inherit;
  cursor: pointer;
}
.sp-account-out:hover { background: rgba(255, 255, 255, .06); }

.ai-share { display: flex; flex-direction: column; gap: 8px; margin-top: 14px; }
.ai-share-btn {
  align-self: flex-start;
  font: inherit;
  font-size: 12px;
  padding: 6px 14px;
  border-radius: 999px;
  border: 1px solid var(--border-subtle, rgba(255, 255, 255, .14));
  background: transparent;
  color: inherit;
  cursor: pointer;
}
.ai-share-btn:hover { background: rgba(255, 255, 255, .06); }
.ai-share-url {
  font: inherit;
  font-size: 11px;
  padding: 6px 10px;
  border-radius: 8px;
  border: 1px solid var(--border-subtle, rgba(255, 255, 255, .14));
  background: rgba(255, 255, 255, .04);
  color: inherit;
}
