  :root{
    --bg:#f3f0ea; --panel:#fffdf9; --panel-2:#f8f5ee;
    --ink:#1c1814; --ink-2:#5b554b; --muted:#8a8378;
    --grid:#e7e2d6; --axis:#c8c2b4; --hair:rgba(28,24,20,.12);
    --accent:#d1490f; --accent-soft:rgba(209,73,15,.09);
    --silent:#9a9284; --tooltip:#fffdf9;
  }
  @media (prefers-color-scheme: dark){
    :root{
      --bg:#0f0e0c; --panel:#1c1a17; --panel-2:#211f1b;
      --ink:#f2ede4; --ink-2:#b5aea1; --muted:#7d766a;
      --grid:#2a2722; --axis:#403b33; --hair:rgba(242,237,228,.14);
      --accent:#e2571a; --accent-soft:rgba(226,87,26,.14);
      --silent:#6f6759; --tooltip:#26231f;
    }
  }
  :root[data-theme="dark"]{
    --bg:#0f0e0c; --panel:#1c1a17; --panel-2:#211f1b;
    --ink:#f2ede4; --ink-2:#b5aea1; --muted:#7d766a;
    --grid:#2a2722; --axis:#403b33; --hair:rgba(242,237,228,.14);
    --accent:#e2571a; --accent-soft:rgba(226,87,26,.14);
    --silent:#6f6759; --tooltip:#26231f;
  }
  :root[data-theme="light"]{
    --bg:#f3f0ea; --panel:#fffdf9; --panel-2:#f8f5ee;
    --ink:#1c1814; --ink-2:#5b554b; --muted:#8a8378;
    --grid:#e7e2d6; --axis:#c8c2b4; --hair:rgba(28,24,20,.12);
    --accent:#d1490f; --accent-soft:rgba(209,73,15,.09);
    --silent:#9a9284; --tooltip:#fffdf9;
  }

  *{box-sizing:border-box}
  body{
    margin:0; background:var(--bg); color:var(--ink);
    font:14px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
    -webkit-font-smoothing:antialiased;
  }
  .display{font-family:"Futura","Avenir Next","Century Gothic","Segoe UI",system-ui,sans-serif}
  .mono{font-family:ui-monospace,"SF Mono",Menlo,Consolas,monospace}

  /* Graffiti face (Permanent Marker, OFL/Apache-2.0) — self-hosted in fonts/,
     used ONLY for the scratch name and the six record-hand words.
     js/30-editor.js applies the family (and each word's direction hue and
     contour slant) inline in styleToken; the scratch title uses it via
     .stage-head h2 below. */
  @font-face{
    font-family:"Graffiti";
    src:url("fonts/permanent-marker.woff2") format("woff2");
    font-weight:400; font-style:normal; font-display:swap;
  }
  .graffiti{font-family:"Graffiti","Futura",system-ui,sans-serif}

  .app{max-width:1180px; margin:0 auto; padding:28px 24px 48px}

  header.masthead{
    position:relative;
    padding-bottom:0; border-bottom:1px solid var(--hair); margin-bottom:20px;
    /* single source of truth for the banner's size: width scales continuously
       with viewport (no breakpoint jump), height follows from the 6:1 aspect
       ratio, and .bar's min-height locks to the same formula so the row —
       and so the whole header — grows to match the banner instead of the
       banner being squeezed into a fixed-height row. padding-bottom is 0 so
       the banner sits snug against the top and bottom edges of the header. */
    --hdr-w:clamp(390px,51vw,858px);
  }
  .masthead .bar{
    display:flex; align-items:center; justify-content:space-between; gap:12px;
    position:relative; min-height:calc(var(--hdr-w) / 6);
  }
  .masthead h1.hdr-banner-h1{
    margin:0; position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
  }
  /* header.jpg is 4 equal horizontal banners stacked (aspect 3:2 overall,
     6:1 per banner). The crop window matches one banner's aspect ratio;
     the img is 4x that height, and app.js ("headerBanner" — grep app.js)
     drives it with a continuously-updating translateY between 0% and
     -75% — the full range that still keeps the image covering the
     window — so it drifts slowly up and down rather than snapping
     between fixed slices. */
  .hdr-banner{
    width:var(--hdr-w); aspect-ratio:6/1; overflow:hidden;
    border-radius:4px; flex:none;
  }
  .hdr-banner-img{
    display:block; width:100%; height:auto;
    /* no zoom: at scale 1 each stacked slice is 6:1 and fills the 6:1 window
       exactly (width:100%, image is 3:2 => each of the 4 slices is W×W/6), so
       the full banner width shows with nothing cropped off the sides. A zoom
       here would magnify the image and spill its left/right edges out of the
       frame. `scale` composes with the translateY drift app.js writes. */
    scale:1; transform-origin:center 15%;
    transition:transform 1s ease-in-out;
  }

  /* the two header buttons keep their 44px width but grow in height only to
     match the strip (the banner-driven .bar height), and their graphic is
     stretched to fill that taller box (background-size 100% 100%) so the icon
     gains the height rather than floating at natural size. transform-origin
     centres the intermittent horizontal "thinning" pulse (see btn-thin). */
  .menu-btn{
    width:30px; height:calc(var(--hdr-w) / 6); padding:0; flex:none; cursor:pointer;
    border:0; border-radius:8px; background:transparent; opacity:.8;
    background-image:url(buttons/menu.png); background-size:100% 100%;
    background-position:center; background-repeat:no-repeat;
    transform-origin:center center;
  }
  .menu-btn .burger{display:none}
  .menu-btn:hover,.help-btn:hover{filter:brightness(1.12)}
  .help-btn{
    width:30px; height:calc(var(--hdr-w) / 6); padding:0; flex:none; cursor:pointer;
    border:0; font-size:0; background:transparent; opacity:.8;
    background-image:url(buttons/help.png); background-size:100% 100%;
    background-position:center; background-repeat:no-repeat;
    transform-origin:center center;
  }
  .dropdown{
    margin-top:12px; background:var(--panel); border:1px solid var(--hair);
    border-radius:9px; padding:8px; box-shadow:0 6px 24px rgba(0,0,0,.10);
  }
  .dropdown[hidden]{display:none}
  .lib-menu{
    max-height:min(72vh,620px);
    display:grid; grid-template-columns:minmax(150px,190px) 1fr;
    gap:10px; align-items:start;
  }
  .lib-list{
    display:flex; flex-direction:column; gap:2px;
    border-right:1px solid var(--hair); padding-right:8px;
  }
  nav.menu{max-height:min(72vh,620px); overflow-y:auto; padding-right:4px}
  .lib-list button{
    display:block; width:100%; text-align:left; font:inherit; font-weight:600;
    font-size:14px; letter-spacing:.12em; text-transform:uppercase; color:var(--ink-2);
    background:transparent; border:1px solid transparent; border-radius:6px;
    padding:10px 12px; cursor:pointer;
  }
  .lib-list button:hover{background:var(--panel-2)}
  .lib-list button[aria-pressed="true"]{background:var(--accent-soft); border-color:var(--accent); color:var(--accent)}
  .lib-list button .cnt{float:right; font-size:11px; letter-spacing:.02em; color:var(--muted); text-transform:none; font-weight:500}

  @media (max-width:820px){
    .app{padding:20px 16px 40px}
    .stage{padding:18px 16px 16px}
    .stage-head h2{font-size:22px}
  }

  /* ---------- technique menu (lives inside the burger dropdown) ---------- */
  nav.menu{
    display:flex; flex-direction:column; gap:2px;
    margin-top:8px; padding-top:8px; border-top:1px solid var(--hair);
  }
  .menu .group{
    font-size:10.5px; letter-spacing:.16em; text-transform:uppercase; color:var(--muted);
    margin:12px 0 6px 12px; font-weight:600;
  }
  .menu .group:first-child{margin-top:2px}
  .menu button{
    display:block; width:100%; text-align:left; padding:9px 12px;
    border:1px solid transparent; border-radius:6px;
    background:transparent; cursor:pointer; font:inherit;
    font-family:"Futura","Avenir Next","Century Gothic",system-ui,sans-serif;
    font-weight:600; font-size:14.5px; letter-spacing:.03em; color:var(--ink);
  }
  .menu button:hover{background:var(--panel-2)}
  .menu button:focus-visible{outline:2px solid var(--accent); outline-offset:1px}
  .menu button[aria-pressed="true"]{background:var(--accent-soft); border-color:var(--accent); color:var(--accent)}

  /* each scratch is a row: its select button + a trash button. A trailing
     "＋ New scratch" button adds to the current library. */
  .menu-item{display:flex; align-items:stretch; gap:4px}
  /* a hold on the label renames it, so the browser must not select the text or
     raise an iOS callout while the finger is down — and manipulation kills the
     double-tap zoom that would otherwise eat the double-click. The rename field
     that opens inside it stays selectable. */
  .menu-item .menu-select{
    flex:1 1 auto; width:auto; min-width:0;
    -webkit-user-select:none; user-select:none;
    -webkit-touch-callout:none; touch-action:manipulation;
  }
  .menu-item .menu-select .name-edit{-webkit-user-select:text; user-select:text}
  .menu .trash{
    flex:0 0 auto; width:auto; padding:0 10px; font-size:14px; line-height:1;
    color:var(--muted); font-family:inherit; opacity:.7;
  }
  .menu .trash:hover{background:rgba(209,73,15,.10); color:var(--accent); opacity:1}
  .menu .menu-add{
    margin-top:8px; text-align:center; color:var(--accent);
    border:1px dashed var(--accent); background:transparent;
    font-weight:700; letter-spacing:.06em; text-transform:uppercase; font-size:12.5px;
  }
  .menu .menu-add:hover{background:var(--accent-soft)}
  /* inline rename fields inside the menus reuse .name-edit at menu/list scale */
  .name-edit.menu-rename{
    font-size:14.5px; font-weight:600; letter-spacing:.03em;
    padding:2px 6px; width:100%; border-radius:6px;
  }
  .name-edit.lib-rename{
    font-size:14px; font-weight:600; letter-spacing:.10em; text-transform:uppercase;
    padding:6px 8px; width:100%; border-radius:6px;
  }

  /* ---------- stage ---------- */
  .stage{
    background:var(--panel); border:1px solid var(--hair); border-radius:10px;
    padding:22px 24px 18px; min-width:0;
  }
  .stage-head{display:flex; align-items:baseline; gap:14px; flex-wrap:wrap; margin-bottom:4px}
  .stage-actions{margin-left:auto; display:flex; align-items:center; gap:8px}
  .stage-head .play{align-self:center}
  .name-edit{
    font-family:"Futura","Avenir Next","Century Gothic",system-ui,sans-serif;
    font-size:27px; font-weight:700; letter-spacing:.02em; color:var(--ink);
    background:var(--panel-2); border:1px solid var(--accent); border-radius:8px;
    padding:2px 8px; width:min(100%,22ch); box-sizing:border-box;
  }
  .name-edit:focus{outline:2px solid var(--accent); outline-offset:1px}
  /* the title IS the rename control — click it (no separate ✎ button) */
  .stage-head h2{
    font-family:"Graffiti","Futura","Avenir Next","Century Gothic",system-ui,sans-serif;
    font-size:34px; font-weight:400; letter-spacing:.01em; margin:0; text-wrap:balance;
    cursor:text; border-radius:8px; padding:2px 6px; margin-left:-6px;
    border:1px solid transparent;
  }
  .stage-head h2:hover{border-color:var(--axis); background:var(--panel-2)}
  .stage-head h2:focus-visible{outline:2px solid var(--accent); outline-offset:1px}
  .chip{
    font-size:10.5px; letter-spacing:.12em; text-transform:uppercase; font-weight:600;
    color:var(--ink-2); border:1px solid var(--hair); border-radius:999px; padding:3px 10px 2px;
    position:relative; top:-3px;
  }
  .desc{color:var(--ink-2); max-width:62ch; margin:2px 0 14px}

  /* The svg takes the full width and its box height follows from its own
     920:368 aspect ratio (height:auto), so the box is always the exact size
     of the drawn graph — the viewBox is cropped snug to the drawing's bottom
     (see app.js H/MB), and nothing beside it can force the box taller. */
  .graph-wrap{position:relative}
  .graph-wrap svg{display:block; width:100%; height:auto}

  /* ---------- pace control: horizontal slider below the editor menus ---------- */
  .pace-ctl{
    display:flex; align-items:center; gap:14px; margin-top:16px;
  }
  .pace-slider{
    flex:1 1 auto; min-width:0;
    display:flex; align-items:center;
  }
  .pace-slider input[type=range]{
    width:100%; height:24px; margin:0;
    accent-color:var(--accent); cursor:pointer;
  }
  .pace-readout{
    flex:none; display:flex; align-items:baseline; gap:6px; line-height:1.2;
  }
  .pace-unit{
    font-size:9.5px; letter-spacing:.14em; text-transform:uppercase; color:var(--muted); font-weight:600;
  }
  .pace-num{font-size:13.5px; color:var(--ink); font-weight:600}

  .legend{
    display:flex; flex-direction:column; gap:12px; align-items:flex-start;
    font-size:12.5px; color:var(--ink-2); padding:4px;
  }
  .legend .item{display:flex; align-items:center; gap:8px}
  .legend svg{display:block}

  /* flash-in: whole finished graph fades in at once, no tracing */
  .seg-live,.seg-dead,.mk{opacity:0; transition:opacity .25s ease}
  .drawn .seg-live,.drawn .seg-dead,.drawn .mk{opacity:1}
  @media (prefers-reduced-motion: reduce){
    .seg-live,.seg-dead,.mk{opacity:1; transition:none}
  }

  /* ---------- transport ---------- */
  /* Sample now lives in .ed-foot beside Onsets; this is just the status line. */
  .transport{
    border-top:1px solid var(--hair); margin-top:16px; padding-top:16px;
  }
  .play{
    width:46px; height:46px; padding:0; border:0; cursor:pointer;
    font-size:0; color:transparent; background:transparent;
    background-image:url(buttons/play.png); background-size:contain;
    background-position:center; background-repeat:no-repeat;
  }
  .play:hover{filter:brightness(1.08)}
  .play:focus-visible{outline:2px solid #27ae38; outline-offset:2px}
  .play[aria-pressed="true"]{background-image:url(buttons/stop.png)}
  .loop-btn{
    width:46px; height:46px; padding:0; flex:none; cursor:pointer;
    border:0; background:transparent;
    background-image:url(buttons/loop.png); background-size:contain;
    background-position:center; background-repeat:no-repeat;
  }
  .loop-btn:hover{filter:brightness(1.15)}
  .loop-btn:focus-visible{outline:2px solid #e6bf00; outline-offset:2px}
  .ctl{display:flex; flex-direction:column; gap:5px}
  .ctl label{
    font-size:10px; letter-spacing:.16em; text-transform:uppercase; color:var(--muted); font-weight:600;
    display:flex; gap:8px; align-items:baseline;
  }
  .ctl label .val{color:var(--ink-2); letter-spacing:.02em; text-transform:none; font-weight:500}
  .transport .ghost{
    font:inherit; font-size:12.5px; color:var(--ink-2); background:transparent;
    border:1px solid var(--axis); border-radius:6px; padding:8px 12px; cursor:pointer;
  }
  .transport .ghost:hover{background:var(--panel-2)}
  .transport .status{font-size:11.5px; color:var(--muted)}
  .transport .status.err{color:var(--accent)}

  /* editor: build-your-own scratch (the dropdowns ARE the column labels, so
     they sit snug under the graph — no rule, no gap between the two). The
     graph now spans the full width, so the onset-row does too: both the svg
     and this row are the full container width, and the onset cells (sized as
     percentages of W in app.js) land on the graph's vertical bars. */
  .editor{margin-top:0}
  /* Onsets and Sample sit as one flat row: label, menu, label, menu — each
     group lays its label beside its select rather than stacked above it. */
  .ed-foot{display:flex; align-items:center; gap:12px 20px; flex-wrap:wrap; margin-top:16px}
  .ed-foot .ed-count,
  .ed-foot .ctl{display:flex; flex-direction:row; align-items:center; gap:8px; min-width:0}
  .ed-foot .ctl select{width:auto}
  .ed-count label{
    font-size:10px; letter-spacing:.16em; text-transform:uppercase; color:var(--muted);
    font-weight:600; display:block; margin-bottom:0; white-space:nowrap;
  }
  .editor select{
    font:inherit; font-size:13.5px; color:var(--ink); background:var(--panel);
    border:1px solid var(--axis); border-radius:6px; padding:7px 10px; cursor:pointer;
  }
  .ed-count select{width:auto}
  /* three column-aligned menu rows: on-prefix, the record hand, off-suffix.
     Each menu's left edge sits on its onset's vertical bar in the graph —
     widths/offset are set inline from the same ML/PW/W geometry render()
     uses, so every row lines up with the columns. */
  .onset-menus{display:flex; flex-direction:column; gap:5px}
  .onset-row{display:flex; align-items:flex-start}
  /* the record hand is the primary row; the two click rows read a touch lighter */
  .prefix-row .onset-cell select,
  .suffix-row .onset-cell select{opacity:.85}
  .onset-cell{flex:0 0 auto; position:relative; box-sizing:border-box; padding-right:4px; min-height:34px}
  .onset-cell:last-child{padding-right:0}
  /* no dropdown arrow — the whole cell width is the word, centred */
  .onset-cell select{
    width:100%; height:34px; text-align:center; text-align-last:center;
    -webkit-appearance:none; -moz-appearance:none; appearance:none;
    background-image:none; padding:0 4px;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  /* pop wider on open so the whole word always shows, overlapping neighbours
     instead of reflowing the row — a mobile column is far too narrow to read
     a word in place */
  .onset-cell select:focus{
    position:absolute; left:0; top:0; z-index:20;
    width:auto; min-width:100%;
    box-shadow:0 3px 12px rgba(0,0,0,.3);
  }
  /* Open popup (customizable-select picker): the native dropdown can't be
     restyled, so opt the onset menus into base-select to control the open
     list. Frame is centred over its button, a touch wider on both sides, and
     sits on top so the button underneath is hidden; the native tick and the
     hover/selected background highlight are removed. Progressive — browsers
     without base-select keep the appearance:none native popup above. */
  @supports (appearance: base-select){
    .onset-cell select,
    .onset-cell select::picker(select){ appearance:base-select }
    .onset-cell select::picker-icon{ display:none }          /* no arrow */
    .onset-cell select::picker(select){
      position-area:center;                                  /* on top of the button */
      min-width:calc(anchor-size(width) + 28px);             /* wider both sides */
      margin:0; padding:4px;
      background:var(--panel); border:1px solid var(--axis); border-radius:8px;
      box-shadow:0 6px 20px rgba(0,0,0,.35);
    }
    .onset-cell select option{
      text-align:center; text-align-last:center;
      padding:6px 10px; border-radius:6px;
    }
    .onset-cell select option::checkmark{ display:none }     /* remove the tick */
    .onset-cell select option:hover,
    .onset-cell select option:focus,
    .onset-cell select option:active,
    .onset-cell select option:checked,
    .onset-cell select option:checked:hover{ background:transparent }  /* no highlight */
  }
  .ed-err{flex-basis:100%; min-height:16px; font-size:11.5px; color:var(--accent)}

  .sr-only{position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap}
  footer.note{margin-top:16px; font-size:12px; color:var(--muted); max-width:72ch}
  footer.note .rec{color:var(--accent); font-weight:600; font-family:ui-monospace,Menlo,monospace}
  footer.note .fad{color:var(--ink-2); font-family:ui-monospace,Menlo,monospace}

  /* intermittent shake — the jitter fires only in the last sliver of each
     cycle, so the rest of the loop is a quiet rest; staggered per-element
     delays keep them from all twitching in unison. */
  @keyframes btn-shake{
    0%,90%,100%{ transform:translate(0,0) rotate(0) }
    91%{ transform:translate(-1px,1px) rotate(-2.5deg) }
    92.5%{ transform:translate(1px,-1px) rotate(2.5deg) }
    94%{ transform:translate(-1px,-1px) rotate(-1.5deg) }
    95.5%{ transform:translate(1px,1px) rotate(1.5deg) }
    97%{ transform:translate(-1px,0) rotate(-1deg) }
    98.5%{ transform:translate(1px,0) rotate(1deg) }
  }
  /* the title seesaws — a slow rock rather than a jitter — and pivots on its
     centre so a long name's ends swing evenly instead of one edge sweeping
     out past the frame. */
  @keyframes title-seesaw{
    0%,86%,100%{ transform:rotate(0) }
    89%{ transform:rotate(-1.6deg) }
    92%{ transform:rotate(1.6deg) }
    95%{ transform:rotate(-.9deg) }
    98%{ transform:rotate(.4deg) }
  }
  .play,.loop-btn,.lib-list button,.menu button{
    animation:btn-shake 5.5s ease-in-out infinite;
  }
  /* now-tall header buttons: instead of the shake, they intermittently thin —
     a brief horizontal squeeze back out to full width, once per cycle. Both
     animate transform, so they can't also carry btn-shake. */
  @keyframes btn-thin{
    0%,64%,100%{ transform:scaleX(1) }
    76%{ transform:scaleX(.5) }
    88%{ transform:scaleX(1.04) }
  }
  .menu-btn{ animation:btn-thin 5.5s ease-in-out infinite }
  .help-btn{ animation:btn-thin 6.3s ease-in-out .7s infinite }
  .play{ animation-duration:4.9s; animation-delay:1.4s }
  .loop-btn{ animation-duration:6.7s; animation-delay:2.1s }
  #s-name{
    animation:title-seesaw 7.2s ease-in-out 2.8s infinite;
    transform-origin:center center;
  }
  .lib-list button{ animation-duration:6.1s }
  .menu button{ animation-duration:5.9s }
  /* don't shake while the user is interacting with it */
  .menu-btn:hover,.help-btn:hover,.play:hover,.loop-btn:hover,
  .lib-list button:hover,.menu button:hover,#s-name:hover,
  .menu-btn:focus-visible,.help-btn:focus-visible,.play:focus-visible,
  .loop-btn:focus-visible,#s-name:focus-visible{ animation-play-state:paused }
  @media (prefers-reduced-motion:reduce){
    .menu-btn,.help-btn,.play,.loop-btn,.lib-list button,.menu button,#s-name{
      animation:none;
    }
  }

  /* ---------- membership panel (js/80-account.js) ----------
     There is no entry gate any more: the library is free to read and this panel
     is only about WRITING. It holds the member-code card and the activity feed. */
  .acct-btn{
    width:30px; height:calc(var(--hdr-w) / 6); padding:0; flex:none; cursor:pointer;
    border:0; background:transparent; opacity:.7;
    color:var(--muted); font-size:17px; line-height:1;
    transform-origin:center center;
  }
  .acct-btn:hover{opacity:1}
  .acct-btn.in{color:var(--accent); opacity:1}          /* signed in */
  .acct-menu{max-width:420px}
  .acct-body{padding:6px 8px 10px}
  .acct-sub,.acct-who{margin:0 0 10px; color:var(--muted); font-size:13px; line-height:1.5}
  .acct-who{color:var(--ink)}
  .acct-user{color:var(--muted); font-size:12px}
  .acct-form{display:flex; flex-wrap:wrap; align-items:center; gap:8px}
  .acct-fields{flex:1 1 200px; display:flex; flex-wrap:wrap; gap:8px; min-width:0}
  .acct-inp{
    flex:1 1 140px; font:inherit; font-size:14px; color:var(--ink);
    background:var(--panel-2); border:1px solid var(--axis); border-radius:8px;
    padding:9px 10px; min-width:0;
  }
  .acct-inp.acct-code{letter-spacing:.10em; text-transform:uppercase; flex-basis:100%}
  .acct-inp:focus{outline:2px solid var(--accent); outline-offset:1px; border-color:var(--accent)}
  .acct-go,.acct-out{
    font:inherit; font-weight:700; letter-spacing:.06em; text-transform:uppercase;
    font-size:12px; color:#fff; background:var(--accent);
    border:1px solid var(--accent); border-radius:8px; padding:9px 14px; cursor:pointer;
  }
  .acct-out{background:transparent; color:var(--muted); border-color:var(--axis)}
  .acct-go:hover{filter:brightness(1.06)}
  .acct-out:hover{color:var(--ink); border-color:var(--ink)}
  .acct-go[disabled]{opacity:.55; cursor:default}
  .acct-err{flex:1 0 100%; margin:0; min-height:1.2em; color:var(--accent); font-size:12px}
  /* the quiet third option: swap credential, or read what membership is for */
  .acct-alt{
    flex:1 0 100%; margin-top:2px; padding:0; text-align:left; cursor:pointer;
    font:inherit; font-size:12px; color:var(--muted);
    background:none; border:0; border-bottom:1px dotted var(--axis); align-self:flex-start;
  }
  .acct-alt:hover{color:var(--accent); border-bottom-color:var(--accent)}

  /* ---------- the sign-in gate (js/80-account.js) ----------
     What a GUEST meets the moment they try to change something. It is the only
     modal in the app, and it exists because the alternative — letting the edit
     appear and refusing it later — is a lie that survives a reload. */
  .gate{
    position:fixed; inset:0; z-index:200; display:flex; align-items:center; justify-content:center;
    padding:20px; background:rgba(12,10,8,.55); backdrop-filter:blur(2px);
    animation:gate-in .16s ease-out;
  }
  .gate[hidden]{display:none}
  body.gated{overflow:hidden}
  @keyframes gate-in{from{opacity:0} to{opacity:1}}
  .gate-card{
    position:relative; width:min(460px,100%); max-height:88vh; overflow-y:auto;
    padding:22px 22px 20px; background:var(--panel); color:var(--ink);
    border:1px solid var(--hair); border-radius:14px;
    box-shadow:0 24px 60px rgba(0,0,0,.35);
  }
  .gate-x{
    position:absolute; top:8px; right:10px; width:30px; height:30px; padding:0; cursor:pointer;
    font:inherit; font-size:22px; line-height:1; color:var(--muted);
    background:none; border:0;
  }
  .gate-x:hover{color:var(--ink)}
  .gate-title{
    margin:0 0 6px; font-size:17px; letter-spacing:.01em;
    font-family:"Futura","Avenir Next","Century Gothic","Segoe UI",system-ui,sans-serif;
  }
  .gate-why{margin:0 0 16px; font-size:13px; line-height:1.5; color:var(--muted)}
  .gate-tabs{display:flex; gap:4px; margin-bottom:14px; border-bottom:1px solid var(--hair)}
  .gate-tab{
    padding:8px 12px; cursor:pointer; font:inherit; font-size:12px; font-weight:700;
    letter-spacing:.08em; text-transform:uppercase; color:var(--muted);
    background:none; border:0; border-bottom:2px solid transparent; margin-bottom:-1px;
  }
  .gate-tab:hover{color:var(--ink)}
  .gate-tab.on{color:var(--accent); border-bottom-color:var(--accent)}
  .gate-copy{margin:0 0 14px; font-size:13px; line-height:1.55; color:var(--ink-2)}
  .gate-soon{
    margin:14px 0 0; padding:9px 11px; font-size:12.5px; line-height:1.5; color:var(--muted);
    background:var(--panel-2); border:1px solid var(--hair); border-radius:8px;
  }
  .gate-buy{display:inline-block; margin-top:14px; text-decoration:none}
  .gate-can{margin:0 0 12px}
  .gate-can h3{
    margin:0 0 5px; font-size:11px; font-weight:700; letter-spacing:.10em;
    text-transform:uppercase; color:var(--muted);
  }
  .gate-can ul{margin:0; padding-left:18px; font-size:12.5px; line-height:1.6; color:var(--ink-2)}
  .gate-can.member h3{color:var(--accent)}
  .gate-can.member li{color:var(--ink)}

  /* ---------- guest: every write affordance says so ----------
     Marking only. The refusal itself is Account.gate() in js/80-account.js and
     the members check in server.js — a stylesheet is not a permission system.
     These stay clickable on purpose: the click is what opens the gate. */
  body[data-role="guest"] .menu .trash,
  body[data-role="guest"] .menu-add{opacity:.45}
  body[data-role="guest"] .menu .trash:hover,
  body[data-role="guest"] .menu-add:hover{opacity:.8}
  body[data-role="guest"] .menu-add::after{
    content:" · members"; font-size:10px; letter-spacing:.08em; color:var(--muted);
  }
  body[data-role="guest"] #s-name{cursor:default}
  body[data-role="guest"] .onset-menus{opacity:.86}

  /* ---------- activity feed ----------
     Author, time, and an editable note: already the shape of a forum post, which
     is what this becomes when the CYMBALFORENSICS forum feeds it. */
  .feed-title{
    margin:4px 8px 6px; font-size:11px; font-weight:700; letter-spacing:.10em;
    text-transform:uppercase; color:var(--muted);
    border-top:1px solid var(--hair); padding-top:10px;
  }
  .acct-feed{list-style:none; margin:0; padding:0 8px 4px; max-height:44vh; overflow-y:auto}
  .feed-row{padding:7px 0; border-bottom:1px solid var(--hair)}
  .feed-row:last-child{border-bottom:0}
  .feed-head{display:flex; align-items:baseline; gap:8px; font-size:11px}
  .feed-who{font-weight:700; color:var(--ink)}
  .feed-when,.feed-edited{color:var(--muted)}
  .feed-edited{font-style:italic}
  .feed-note{margin:2px 0 0; font-size:13px; color:var(--ink); line-height:1.45}
  .feed-note.editable{cursor:text; border-bottom:1px dotted var(--axis)}
  .feed-note.editable:hover{border-bottom-color:var(--accent)}
  .feed-edit{width:100%; font-size:13px}
  .feed-empty{padding:8px 0; color:var(--muted); font-size:13px}

  /* ---------- attribution + notices ---------- */
  .menu-by{display:block; font-size:10px; font-weight:600; color:var(--muted); letter-spacing:.04em}
  .stage-by{margin:-2px 0 6px; font-size:11px; color:var(--muted); letter-spacing:.04em}
  .stage-by:empty{display:none}
  .notice{
    margin:8px 0 0; padding:7px 10px; font-size:12.5px; line-height:1.45;
    color:var(--ink); background:var(--panel-2);
    border:1px solid var(--hair); border-left:3px solid var(--accent); border-radius:6px;
  }
  .notice[hidden]{display:none}
