:root{
    /* five spot inks on cream stock — the whole interface is printed, not lit */
    --bg:#e4d8bd; --panel:#efe6d2; --panel2:#e6dcc4; --line:#22201d;
    --txt:#22201d; --dim:#6b6355; --acc:#2b7a78; --acc2:#d2601a;
    --good:#5a7a35; --warn:#d9a521; --bad:#a8321e; --air:#2b7a78;
    --display:"Arial Black","Helvetica Neue Condensed Black",Haettenschweiler,Impact,
      "Franklin Gothic Heavy",system-ui,sans-serif;
    --body:"Inter","Helvetica Neue",Helvetica,Arial,sans-serif;
  }
  *{box-sizing:border-box}
  html,body{height:100%;margin:0;background:var(--bg);color:var(--txt);
    font:13px/1.42 var(--body);
    overflow:hidden;-webkit-user-select:none;user-select:none}
  #app{display:flex;flex-direction:column;height:100%}

  header{display:flex;align-items:center;gap:7px;padding:7px 11px;background:var(--panel);
    border-bottom:2px solid var(--line);flex:0 0 auto;flex-wrap:wrap}
  /* THE LOGO, in both places, from one SVG.
     Height is what matters in the header — it sits in a row of 11px uppercase buttons,
     so the mark is set to the cap height of that row rather than to a nominal size.
     Width follows from the artwork's own 102:33.5 ratio, so nothing is ever squashed:
     only ONE dimension is ever specified. */
  .logo{height:26px;width:auto;display:block;margin-right:10px;flex:0 0 auto;
    cursor:pointer;-webkit-user-select:none;user-select:none}
  .logo:hover{opacity:.78}
  button{background:var(--panel);color:var(--txt);border:1.5px solid var(--line);
    border-radius:0;padding:6px 10px;cursor:pointer;white-space:nowrap;
    font:600 11px/1 var(--body);letter-spacing:.9px;text-transform:uppercase}
  button:hover{background:var(--txt);color:var(--panel)}
  button.primary{background:var(--acc2);color:var(--panel);border-color:var(--line);
    box-shadow:2px 2px 0 var(--line)}
  button.primary:hover{background:var(--txt);color:var(--panel)}
  button.danger:hover{background:var(--bad);color:var(--panel)}
  button.on{background:var(--acc);color:var(--panel)}
  button:active{transform:translate(1px,1px)}
  .menuwrap{position:relative;display:inline-flex}
  #viewMenu{position:absolute;top:calc(100% + 6px);left:0;z-index:40;display:none;
    background:var(--panel);border:2px solid var(--line);border-radius:0;padding:5px;
    box-shadow:4px 4px 0 var(--line);min-width:200px}
  #viewMenu.show{display:block}
  #viewMenu button{display:flex;align-items:center;gap:9px;width:100%;text-align:left;
    border:1px solid transparent;background:transparent;margin:1px 0}
  #viewMenu button:hover{background:var(--txt);color:var(--panel);border-color:var(--line)}
  #viewMenu button.on{border-color:var(--line);background:var(--acc);color:var(--panel)}
  #viewMenu .sw{width:24px;height:16px;border-radius:0;border:1.5px solid var(--line);flex:0 0 auto}
  #viewMenu .kk{margin-left:auto;color:var(--dim);font-size:11px}
  .sp{flex:1}
  /* THE ONE PERSISTENT ASK, sitting after the spacer so it rides the right-hand end
     of the header next to the camera toggles rather than in the run of build tools.
     Styled as a header button and not as a shouting banner: it is there whenever
     you want it and never in the way of anything.
     Hidden in the markup AND display:none here — see the note in core.js about why
     this one gets two locks. */
  header .donate{display:none;align-items:center;gap:6px;margin-right:7px;
    padding:6px 11px;background:var(--warn);color:var(--line);
    border:1.5px solid var(--line);box-shadow:2px 2px 0 var(--line);
    font:800 11px/1 var(--body);letter-spacing:.9px;text-transform:uppercase;
    text-decoration:none;white-space:nowrap}
  header .donate.on{display:inline-flex}
  header .donate:hover{background:var(--line);color:var(--panel)}
  header .donate:active{transform:translate(1px,1px);box-shadow:1px 1px 0 var(--line)}
  /* In a short embed the header wraps and every pixel is contended; the ask is the
     first thing that should give up its words. */
  @media (max-width:900px){ header .donate{padding:6px 8px;font-size:0}
    header .donate::after{content:"☕";font-size:13px} }
  .vr{width:2px;align-self:stretch;background:var(--line);margin:0 3px}
  .hint{color:var(--dim);font-size:12px}

  main{flex:1;display:flex;min-height:0}
  /* The rail scrolls, and in a short embed — itch, a Reddit post — the bottom half of
     the tool list is simply below the fold with nothing to say so. A hard-edged fade
     at the bottom is the cheapest honest signal that there is more down there; it
     appears only when the rail actually overflows, because a permanent fade would be
     decoration that lies half the time. */
  #tools{width:206px;flex:0 0 auto;background:var(--panel);border-right:2px solid var(--line);
    overflow-y:auto;padding:8px;position:relative;
    scrollbar-width:thin;scrollbar-color:var(--line) var(--panel2)}
  #tools::-webkit-scrollbar{width:9px}
  #tools::-webkit-scrollbar-track{background:var(--panel2);border-left:1.5px solid var(--line)}
  #tools::-webkit-scrollbar-thumb{background:var(--line)}
  /* the fade rides the scroll container, so it sits over whatever is at the bottom */
  #tools.canscroll{
    background:
      linear-gradient(to top, var(--panel) 0%, rgba(239,230,210,0) 34px) local bottom / 100% 34px no-repeat,
      var(--panel);
  }
  #tools.atend{background:var(--panel)}
  #tools h4{margin:14px 2px 5px;font:800 10px/1 var(--body);letter-spacing:1.6px;
    color:var(--txt);text-transform:uppercase;border-bottom:1.5px solid var(--line);padding-bottom:4px;
    display:flex;align-items:baseline;gap:6px}
  #tools h4 .h4k{margin-left:auto;font-weight:700;letter-spacing:.6px;color:var(--dim);text-transform:none}
  #propList{display:none;grid-template-columns:1fr 1fr;gap:3px;margin:2px 0 4px}
  #propList.show{display:grid}
  #propList button{padding:5px 4px;font-size:9.5px;letter-spacing:.5px}
  #propList button.on{background:var(--good);color:var(--panel)}
  /* Two sections in one grid: a heading spans both columns, and the four corner toggles
     share a row of their own. */
  #propList .plh{grid-column:1/-1;font:800 9px/1.2 var(--body);letter-spacing:1.3px;
    text-transform:uppercase;color:var(--dim);margin:5px 0 1px}
  #propList .plsw{display:inline-block;width:9px;height:9px;border:1px solid var(--line);
    margin-right:5px;vertical-align:-1px}
  #propList .plh i{font-style:normal;letter-spacing:.3px;text-transform:none;font-weight:600}
  #propList .plcorn{grid-column:1/-1;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:3px}
  #propList .plcorn button{display:flex;align-items:center;gap:5px;justify-content:flex-start;
    padding:5px 5px;min-width:0;font-size:8.5px;letter-spacing:.3px}
  #propList .plcorn span{white-space:normal;line-height:1.15;text-align:left}
  #propList .plcorn svg{flex:0 0 auto}
  #lookList button{display:flex;align-items:center;gap:8px;width:100%;margin-bottom:3px;
    text-align:left;border-color:transparent;padding:4px 6px}
  #lookList button:hover{border-color:var(--line)}
  #lookList .sw{width:22px;height:14px;border:1.5px solid var(--line);flex:0 0 auto}
  #lookList .nm{font-size:10.5px}
  #lookList .tg{margin-left:auto;font-size:9px;color:var(--dim);letter-spacing:.6px}
  #lookList button.on .tg{color:var(--panel)}
  .tool{display:flex;align-items:center;gap:8px;width:100%;margin-bottom:3px;text-align:left;
    border-color:transparent;padding:5px 7px}
  .tool:hover{border-color:var(--line)}
  .tool .k{margin-left:auto;color:var(--dim);font-size:10px;letter-spacing:0}
  .tool.on .k{color:var(--panel)}
  .tool.on .ic{color:var(--panel)!important}
  .tool .ic{width:18px;height:18px;flex:0 0 auto;display:flex;align-items:center;justify-content:center}
  .tool .ic svg{width:17px;height:17px;stroke:currentColor;fill:none;
    stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
  .tool .ic svg .f{fill:currentColor;stroke:none}
  #stage{flex:1;position:relative;min-width:0}
  canvas{display:block;width:100%;height:100%;cursor:crosshair}

  /* HUD */
  #hud{position:absolute;top:10px;right:10px;width:222px;background:var(--panel);
    border:2px solid var(--line);border-radius:0;padding:9px 10px;box-shadow:3px 3px 0 var(--line)}
  /* THE TABLE FOLDS. In a short window (itch's frame is 720 tall) the full box ran half
     way down the canvas, so below 800 px of height only the top section shows: the two
     live numbers, the gauge, airtime now and height. Clicking opens the rest. A tall
     window, which is fullscreen on any ordinary monitor, opens it by default, and a click
     shuts it. Narrower than 980 the phone rules further down cut the box to two numbers
     and none of this applies. JS adds .open / .shut only once the player has chosen. */
  #hud{cursor:pointer}
  #hud .hudx{display:none;margin-top:4px;text-align:right;color:var(--dim);
    font:800 9px/1.2 var(--body);letter-spacing:1.2px;text-transform:uppercase}
  #hud .hudxc{display:none}
  #hud.shut .more{display:none}
  #hud.shut .hudx, #hud.open .hudx{display:block}
  #hud.open .hudxo{display:none}
  #hud.open .hudxc{display:inline}
  @media (max-height: 800px) and (min-width: 981px){
    #hud:not(.open) .more{display:none}
    #hud .hudx{display:block}
  }
  .row{display:flex;justify-content:space-between;align-items:baseline;margin:3px 0}
  .row b{font-variant-numeric:tabular-nums;font-weight:600}
  .lbl{color:var(--dim);font:800 10px/1.3 var(--body);text-transform:uppercase;letter-spacing:1.3px}
  .big{font:32px/0.9 var(--display);font-variant-numeric:tabular-nums;letter-spacing:-.5px}
  .unit{font:800 10px/1 var(--body);color:var(--dim);margin-left:4px;letter-spacing:1.2px;
    text-transform:uppercase}
  /* THE G GAUGE — a vertical instrument beside the numbers, not a bar under them.
     Vertical G is a vertical sensation: positive presses you down into the seat, so
     the marker sits BELOW the zero line; airtime lifts you out, so it rises above
     it. Zero is the middle, and one G reads the same distance either way.
     A marker rather than a fill, because what you want off a glance is where the
     needle IS, not how much bar has accumulated. */
  .gtop{display:flex;gap:11px;align-items:stretch}
  .gstats{flex:1;min-width:0}
  .gwrap{width:27px;flex:0 0 auto;align-self:stretch;min-height:112px;
    background:var(--panel2);border:2px solid var(--line);border-radius:0;
    position:relative;overflow:hidden}
  #gzero{position:absolute;left:0;right:0;height:1.5px;top:50%;
    background:rgba(34,32,29,.42)}
  #gbar{position:absolute;left:2px;right:2px;height:13px;top:50%;
    transform:translateY(-50%);background:var(--good);
    transition:top .05s linear,background .1s linear}
  /* THE CHAPTER THAT IS ABOUT THE NUMBERS LIGHTS THE NUMBERS.
     Telling someone to "look at the gauge beside the speed" only works if they already
     know which thing that is. While the G-forces chapter is open, the speed, the G
     figure and the gauge itself pulse red — for the whole chapter, not one beat, because
     they are the subject throughout and a flash you happened to miss is worse than none.
     Nothing moves and nothing resizes: only colour, so the readings stay readable. */
  @keyframes tutlit{
    0%,100%{color:var(--txt)}
    50%    {color:var(--bad)}
  }
  @keyframes tutlitbox{
    0%,100%{border-color:var(--line);box-shadow:none}
    50%    {border-color:var(--bad);box-shadow:0 0 0 2px rgba(168,50,30,.30)}
  }
  #hud.tutlit #mSpeed,#hud.tutlit #mG{animation:tutlit 1.15s ease-in-out infinite}
  #hud.tutlit .gwrap{animation:tutlitbox 1.15s ease-in-out infinite}
  /* POINT AT THE BUTTON, NOT AT THE IDEA OF IT.
     "Pick up the Freehand tool" is a sentence about a rail of fourteen near-identical
     buttons, and a beginner reads it, looks left, and starts comparing icons. A step
     that names one control should light that control up — so the beat can say which
     one it means in the only place the answer actually is.

     A ring and a tint, no movement and no resize: the rail must not reflow, and the
     button has to stay legible while it pulses. */
  @keyframes tutwant{
    0%,100%{box-shadow:0 0 0 0 rgba(196,105,50,0);border-color:var(--line)}
    50%    {box-shadow:0 0 0 3px rgba(196,105,50,.45);border-color:var(--acc)}
  }
  .tool.tutwant{animation:tutwant 1.05s ease-in-out infinite;position:relative}
  .tool.tutwant .ic{color:var(--acc)}
  /* Anyone who has asked not to see motion gets the emphasis as a flat state instead. */
  @media (prefers-reduced-motion:reduce){
    #hud.tutlit #mSpeed,#hud.tutlit #mG{animation:none;color:var(--bad)}
    #hud.tutlit .gwrap{animation:none;border-color:var(--bad)}
    .tool.tutwant{animation:none;border-color:var(--acc);box-shadow:0 0 0 3px rgba(196,105,50,.45)}
  }

  /* a build step's Next button, while the player has not done the thing yet: quieter,
     but never disabled — someone who already knows how to drag a line should not be
     held hostage by a tutorial */
  #tutNext.waiting{background:var(--panel2);color:var(--dim);box-shadow:none}
  #tutNext.waiting:hover{background:var(--txt);color:var(--panel)}

  /* the first-visit offer: bottom-left, out of the way of the HUD and the tool rail */
  #starter{position:absolute;left:12px;bottom:12px;max-width:330px;display:none;
    background:var(--panel);border:2px solid var(--line);padding:11px 13px;
    box-shadow:4px 4px 0 var(--line);z-index:20}
  #starter.show{display:block}
  #starter b{font:800 12.5px/1.3 var(--body);letter-spacing:.6px;text-transform:uppercase}
  #starter p{margin:5px 0 0;font-size:12px;color:var(--dim);line-height:1.45}
  #starter .btnrow{margin-top:10px;display:flex;gap:6px;flex-wrap:wrap}

  /* gallery: a shelf of drawn thumbnails. Cards are buttons, because that is what
     they do — one tap loads the coaster. */
  /* THE GALLERY IS A SCREEN, NOT A DIALOG.
     It is the browse surface — the place you go to look at other people's coasters —
     so it takes essentially the whole window rather than sitting in a box sized for a
     yes/no question. The grid scrolls; the header with the search box does not, so the
     field stays reachable however far down you are. */
  /* max-height and overflow are overridden, not just height: .card caps every dialog
     at 86vh and scrolls itself, and both of those would defeat this — the cap would
     shrink the panel back down, and the outer scroller would drag the search box up
     out of view along with the cards. Here the CARD does not scroll; #galBody does. */
  /* AND ON A PHONE IT HAS TO OWN THE WINDOW, NOT THE STAGE.
     #modal is absolute inside #stage, which is right for every other card: a dialog
     belongs over the drawing surface. It is wrong for this one. The stage is what is
     LEFT once the header, the tool strip and the timeline have taken their share —
     about 500 of a 640px portrait — while the card went on asking for 92vh of the
     WINDOW. Taller than the box centring it, it overflowed equally top and bottom,
     and the half that went up carried the search field and Close off the top of the
     screen, where html{overflow:hidden} meant nothing could scroll them back. So the
     gallery, alone among the cards, is measured against the viewport it covers. */
  #modal.galscreen{position:fixed;z-index:130}
  #modal .card.gallery{width:min(1500px,96vw);max-width:none;
    height:92vh;max-height:92vh;overflow:hidden;
    display:flex;flex-direction:column;padding:12px 14px}
  /* dvh is the viewport you can actually SEE. vh on mobile Safari is the tall one you
     only get once the address bar has scrolled away, so 92vh really does not fit on
     the screen it was measured for. Added on top of the vh rule rather than replacing
     it: vh is the fallback for browsers without dvh, not the mistake. */
  @supports (height:100dvh){
    #modal .card.gallery{height:92dvh;max-height:92dvh}
  }
  .galhead{display:flex;align-items:center;gap:9px;flex:0 0 auto;
    border-bottom:2px solid var(--line);padding-bottom:10px;margin-bottom:10px}
  .galhead h2{margin:0;flex:0 0 auto}
  .galhead input{flex:1;min-width:0;background:var(--panel2);border:2px solid var(--line);
    border-radius:0;padding:8px 10px;font:600 12px/1 var(--body);color:var(--txt);
    letter-spacing:.4px}
  .galhead input:focus{outline:none;box-shadow:2px 2px 0 var(--line)}
  /* overscroll-behavior stops the flick that runs out of cards from being handed
     onward: on itch the page AROUND the frame is what scrolls next, so without this
     the last row of a gallery scrolls itch's own footer into view under your thumb. */
  #galBody{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;
    -webkit-overflow-scrolling:touch;scrollbar-width:thin;
    scrollbar-color:var(--line) var(--panel2)}
  #galBody::-webkit-scrollbar{width:10px}
  #galBody::-webkit-scrollbar-track{background:var(--panel2)}
  #galBody::-webkit-scrollbar-thumb{background:var(--line)}

  .galgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:12px}
  /* the wrapper exists so the delete control can sit OUTSIDE the card button — nesting
     a button in a button is invalid, and it would make a mis-aimed click destructive */
  .galwrap{position:relative}
  button.galcard{display:flex;flex-direction:column;align-items:stretch;gap:4px;width:100%;
    padding:6px;text-align:left;text-transform:none;letter-spacing:0;
    background:var(--panel2);border:2px solid var(--line)}
  /* color is restated because button:hover inverts it to var(--panel) for the
     filled look, and this rule puts the background back to var(--panel) without
     it — which left .galname as panel-on-panel and made the name vanish on hover.
     .galrate and .galcode survived only because they set a colour of their own. */
  button.galcard:hover{background:var(--panel);color:var(--txt);
    box-shadow:3px 3px 0 var(--line)}
  .galthumb{width:100%;height:auto;display:block;background:var(--bg);
    border:1.5px solid var(--line)}
  .galname{font:800 12.5px/1.25 var(--body);letter-spacing:.4px;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  /* The tier as a coloured tag: the card can be read from across the grid before a
     single word of it is. Colour from --tier, set per card (TIER_COL in core.js). */
  .galrate{font:800 9.5px/1 var(--body);color:#fff;background:var(--tier,var(--dim));
    letter-spacing:.8px;align-self:flex-start;padding:4px 7px 3px;
    border:1.5px solid var(--line);text-transform:uppercase}
  .galrate b{font:800 14px/1 var(--display);color:var(--txt);letter-spacing:0}
  .galcode{font:700 9px/1 ui-monospace,Menlo,Consolas,monospace;color:var(--dim);
    letter-spacing:1px}
  button.galcard.loading{background:var(--acc);color:var(--panel)}
  button.galcard.loading .galname::after{content:' — loading…';font-weight:600}
  button.galcard.loading .galrate,button.galcard.loading .galcode{opacity:.6;color:var(--panel)}
  /* owner only: small, cornered, and it MARKS rather than acts — see galBar() */
  button.galrm{position:absolute;top:-6px;right:-6px;width:24px;height:24px;padding:0;
    font-size:12px;line-height:1;background:var(--panel);border:2px solid var(--line);
    box-shadow:2px 2px 0 var(--line);z-index:2}
  button.galrm:hover{background:var(--bad);color:var(--panel)}
  button.galrm.marked{background:var(--bad);color:var(--panel)}
  /* A marked tile has to be obvious at a glance across a full grid, because the whole
     point of batching is that you mark several and then look over what you have got
     before pulling the trigger. Struck through and faded: unmistakably "on its way
     out", and still readable enough to change your mind about. */
  .galwrap.marked .galcard{opacity:.42;filter:grayscale(1)}
  .galwrap.marked{position:relative}
  .galwrap.marked::after{content:"";position:absolute;left:0;right:0;top:50%;height:3px;
    background:var(--bad);pointer-events:none;z-index:1}
  /* THE OWNER'S KEY, EDITED IN THE CARD RATHER THAN IN A window.prompt().
     A prompt is a native modal, and a sandboxed frame may refuse those outright —
     itch serves this game inside one — so the single control that has to work for
     whoever is clearing up spam was the one control depending on the host's
     goodwill. It is also miserable on a phone, where a system dialog covers the
     screen you were working in. */
  #galKeyRow{display:none}
  #galKeyRow.on{display:flex;align-items:center;gap:7px;flex-wrap:wrap;
    margin:0 0 10px;padding:8px 10px;background:var(--panel2);
    border:2px solid var(--line)}
  #galKeyRow input{flex:1;min-width:130px;background:var(--panel);color:var(--txt);
    border:2px solid var(--line);border-radius:0;padding:8px 9px;
    font:600 12px/1 var(--body);letter-spacing:.4px}
  #galKeyRow input:focus{outline:none;box-shadow:2px 2px 0 var(--line)}
  #galKeyRow .note{flex:1 0 100%;font-size:10.5px;color:var(--dim);line-height:1.45}
  /* The confirmation bar. Only in the document when something is marked. */
  #galBar{display:none}
  #galBar.on{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:10px;
    padding:9px 12px;background:var(--panel);border:2px solid var(--line);
    box-shadow:3px 3px 0 var(--line)}
  #galBar b{font:800 11px/1 var(--body);letter-spacing:1.1px;text-transform:uppercase}
  #galBar button{margin-left:0}
  #galBar button:first-of-type{margin-left:auto}
  /* ---- PHONE, PORTRAIT: FULL BLEED ----
     The header cannot hold a title, a field and two buttons in one row, so the search
     box takes its own line rather than being squeezed to nothing.

     And the frame goes. At 360px the 2vw of margin, the 2px border and the 6px shadow
     are decoration paid for in card width, on the one screen where width buys the most
     — it is a shelf of thumbnails. The safe-area insets keep the bottom row clear of
     the home bar on a notched phone and are 0 everywhere else. */
  @media (max-width:560px){
    #modal .card.gallery{width:100vw;height:100vh;max-height:none;border:0;
      box-shadow:none;
      padding:calc(7px + env(safe-area-inset-top)) calc(8px + env(safe-area-inset-right))
              calc(7px + env(safe-area-inset-bottom)) calc(8px + env(safe-area-inset-left))}
    @supports (height:100dvh){
      #modal .card.gallery{height:100dvh}
    }
    .galhead{flex-wrap:wrap;gap:7px;padding-bottom:8px;margin-bottom:8px}
    .galhead h2{font-size:18px}
    /* the field and both buttons are what a thumb aims at, so they get thumb height */
    .galhead input{order:3;flex:1 0 100%;padding:10px;font-size:13px}
    .galhead button{padding:9px 12px}
    .galgrid{grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:8px}
    .galname{font-size:12px}
  }

  /* ---- PHONE, LANDSCAPE: HEIGHT IS THE SCARCE AXIS ----
     A phone on its side is around 740x360, which none of the width queries above
     touch — and until now nothing in this screen keyed off height at all, so a title,
     a search row and 240px columns left barely one row of cards showing. Here the
     header stays on ONE line, because width is the axis there is plenty of, and the
     columns narrow so that a card is short enough for two rows to fit.

     Gated above 560px wide so it can never combine with the portrait block: a small
     portrait post (360x500) matches on height too, and a nowrap header at 360px is
     the squeezed-to-nothing row the block above exists to avoid. */
  @media (max-height:560px) and (min-width:561px){
    #modal .card.gallery{width:100vw;height:100vh;max-height:none;border:0;
      box-shadow:none;padding:6px 9px calc(6px + env(safe-area-inset-bottom))}
    @supports (height:100dvh){
      #modal .card.gallery{height:100dvh}
    }
    .galhead{flex-wrap:nowrap;gap:7px;padding-bottom:6px;margin-bottom:7px}
    .galhead h2{font-size:15px}
    .galhead input{padding:7px 8px}
    .galgrid{grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:7px}
    .galname{font-size:11.5px}
    .galrate b{font-size:12px}
    #galBar{margin-top:7px;padding:6px 9px}
    #galKeyRow.on{margin-bottom:7px;padding:6px 9px}
  }

  /* ---- touch ----
     The mark button is the only control on this screen that stays put after being
     pressed, so it is the only one where a fingertip target and sticky :hover
     matter. Inside the card rather than hanging off the corner: at 34px a cornered
     control reaches into the gap and over the neighbouring coaster. */
  @media (pointer:coarse){
    button.galcard{padding:7px}
    button.galrm{width:34px;height:34px;top:2px;right:2px;font-size:15px}
  }
  /* Sticky :hover is a touchscreen fact — the last thing tapped keeps it until
     something else is tapped. On a card it does not matter: tapping one loads a
     coaster and closes the screen. On the mark it LIES, because unmarking leaves the
     button filled red, which is the exact signal for "still marked". */
  @media (hover:none){
    button.galrm:hover{background:var(--panel);color:var(--txt)}
    button.galrm.marked:hover{background:var(--bad);color:var(--panel)}
  }

  /* color pickers: swatches read as ink chips, the active one gets the ring */
  .paintrow{display:flex;flex-wrap:wrap;gap:3px;margin:2px 0 6px}
  button.paint{width:22px;height:18px;padding:0;border:1.5px solid var(--line);flex:0 0 auto}
  button.paint[data-paint=''],button.paint[data-sup='']{width:auto;padding:0 7px;font-size:9px}
  button.paint.on{box-shadow:0 0 0 2px var(--acc2);position:relative;z-index:1}
  hr{border:0;border-top:1.5px solid var(--line);margin:8px 0}
  #airBadge{color:var(--acc2);font-weight:800;letter-spacing:1px}
  .flash{animation:fl .6s ease-out}
  @keyframes fl{from{background:rgba(210,96,26,.30)}to{background:transparent}}

  /* timeline */
  #tl{flex:0 0 auto;display:flex;align-items:center;gap:8px;padding:7px 10px;
    background:var(--panel);border-top:2px solid var(--line)}
  #tl button{padding:6px 9px}
  #tlcv{flex:1;height:74px;min-width:0;display:block;cursor:ew-resize;
    background:var(--panel2);border:1.5px solid var(--line);border-radius:0}
  #tlTime{font:700 11px/1 ui-monospace,Menlo,Consolas,monospace;color:var(--dim);
    min-width:118px;text-align:right;font-variant-numeric:tabular-nums;letter-spacing:.5px}
  .tlgrp{display:flex;gap:5px;align-items:center}

  #selPanel{position:absolute;left:10px;top:10px;width:238px;display:none;
    background:var(--panel);border:2px solid var(--line);border-radius:0;padding:10px;
    box-shadow:3px 3px 0 var(--line)}
  #selPanel.show{display:block}
  #selPanel .sphead{border-bottom:2px solid var(--line);padding-bottom:7px;margin-bottom:2px}
  #selPanel .spwhat{font:800 12px/1.3 var(--body);letter-spacing:.8px;text-transform:uppercase}
  #selPanel .spsum{font-size:11px;color:var(--dim);line-height:1.5;margin-top:3px}
  #selPanel .spsum b{color:var(--txt)}
  #selPanel .spsec{font:800 9.5px/1 var(--body);letter-spacing:1.5px;text-transform:uppercase;
    color:var(--dim);margin:11px 0 5px;border-bottom:1.5px solid rgba(34,32,29,.2);padding-bottom:3px}
  #selPanel .sptabs{display:flex;gap:3px;margin-bottom:7px;flex-wrap:wrap}
  #selPanel .seg{display:flex;border:2px solid var(--line)}
  #selPanel .seg button{flex:1;border:0;border-radius:0;padding:6px 4px;font-size:10px;
    background:var(--panel2);color:var(--dim)}
  #selPanel .seg button+button{border-left:2px solid var(--line)}
  #selPanel .seg button.on{background:var(--acc2);color:var(--panel)}
  #selPanel .spwire{font-size:11px;line-height:1.45;color:var(--txt);background:var(--panel2);
    border-left:3px solid var(--acc2);padding:5px 7px;margin:7px 0}
  #selPanel .btnrow{margin-top:11px}
  #selPanel button{padding:5px 8px;font-size:12px}
  input[type=range]{width:100%;margin:6px 0 2px;accent-color:var(--acc2)}
  .chk{display:flex;gap:7px;align-items:center;font-size:12px;margin-top:9px;cursor:pointer;color:var(--txt)}
  .chk input{accent-color:var(--good);margin:0}
  #selPanel select{width:100%;margin-top:6px;background:var(--panel2);color:var(--txt);
    border:1.5px solid var(--line);border-radius:0;padding:5px 6px;font:inherit;font-size:12px}
  .sub{margin-left:3px;padding-left:9px;border-left:2px solid var(--line);margin-top:6px}
  /* Collapsed it is a label: the tool's name and a caret, sized to its text. Open, it is
     the paragraph it always was. A button in all but tag, so it says so to the cursor. */
  #tip{position:absolute;left:10px;bottom:10px;max-width:430px;background:var(--panel);
    border:2px solid var(--line);border-radius:0;padding:5px 9px;color:var(--dim);font-size:12px;
    cursor:pointer;-webkit-user-select:none;user-select:none}
  #tip.open{padding:8px 11px}
  #tip:hover{border-color:var(--acc)}
  #tip b{color:var(--txt);font-weight:700}
  #tip .tipx{margin-left:7px;color:var(--dim);font-size:10px}

  /* modal */
  #modal{position:absolute;inset:0;background:rgba(34,32,29,.38);display:none;
    align-items:center;justify-content:center;z-index:20}
  #modal.show{display:flex}
  /* 86vh IS THE WINDOW; 100% IS THE BOX THIS IS CENTRED IN, and on a phone they are
     nothing like each other. #modal is absolute inside #stage, and the stage is what
     is LEFT once the header, the tool rail and the timeline have taken their share —
     roughly half the window when the phone is held vertically. A card asking for 86vh
     was therefore taller than the box centring it, so it overflowed EQUALLY top and
     bottom: the half that went up carried the heading and the tabs off the top, the
     half that went down carried the Done button off the bottom, and html{overflow:
     hidden} meant nothing could scroll either back. Settings, being the tallest card,
     showed it worst.
     min() rather than replacing 86vh, because 86vh is right whenever the stage is the
     bigger of the two — which is every desktop, so nothing there moves. The gallery
     card overrides this from a more specific selector and is unaffected; it solved the
     same problem the other way, by measuring itself against the viewport it covers. */
  .card{background:var(--panel);border:2px solid var(--line);border-radius:0;padding:20px 22px;
    width:min(520px,92vw);max-height:min(86vh,100%);overflow-y:auto;overflow-x:hidden;
    box-shadow:6px 6px 0 var(--line)}
  .card h2{margin:0 0 4px;font:24px/1 var(--display);text-transform:uppercase;letter-spacing:.5px}
  .brk{display:flex;justify-content:space-between;padding:5px 0;border-bottom:1.5px solid rgba(34,32,29,.22);font-size:13px}
  .brk span:first-child{font:800 10px/1.6 var(--body);text-transform:uppercase;letter-spacing:1.2px;color:var(--dim)}
  .brk span:last-child{font-variant-numeric:tabular-nums}
  textarea{width:100%;height:110px;background:var(--panel2);color:var(--txt);border:1.5px solid var(--line);
    border-radius:0;padding:8px;font:11px ui-monospace,Menlo,Consolas,monospace;resize:vertical}
  .btnrow{display:flex;gap:8px;margin-top:14px;flex-wrap:wrap}
  .muted{color:var(--dim);font-size:12px;line-height:1.55}
  *::-webkit-scrollbar{width:11px;height:11px}
  *::-webkit-scrollbar-track{background:var(--panel2);border-left:1.5px solid var(--line)}
  *::-webkit-scrollbar-thumb{background:var(--line);border:2px solid var(--panel2)}
  *::-webkit-scrollbar-thumb:hover{background:var(--acc2)}
  *{scrollbar-width:thin;scrollbar-color:var(--line) var(--panel2)}
  .pick{margin-top:14px;display:flex;flex-direction:column;gap:7px}
  .pick button{display:block;width:100%;max-width:100%;text-align:left;padding:10px 12px;
    text-transform:none;letter-spacing:0;font-weight:400;font-size:12px;
    white-space:normal;overflow-wrap:break-word}
  .pick button b{display:block;font:800 13px/1.3 var(--body);letter-spacing:.4px;
    text-transform:uppercase;white-space:normal}
  .pick button i{display:block;font-style:normal;font-size:10px;font-weight:800;letter-spacing:1.2px;
    text-transform:uppercase;color:var(--dim);margin:1px 0 4px}
  .pick button:hover i{color:var(--panel)}
  .pick button span{display:block;line-height:1.5;color:var(--dim);white-space:normal}
  .pick button:hover span{color:var(--panel)}
  .klbl{font:800 10px/1 var(--body);letter-spacing:1.6px;text-transform:uppercase;color:var(--txt);
    margin:14px 0 6px;border-bottom:1.5px solid var(--line);padding-bottom:4px}
  .krow{display:flex;gap:10px;align-items:baseline;padding:2px 0;font-size:12px}
  .krow kbd{flex:0 0 148px;font:700 11px/1.5 ui-monospace,Menlo,Consolas,monospace;color:var(--txt)}
  .krow span{color:var(--dim)}

  /* ---------- title screen ---------- */
  /* ONE HEADER ROW AT EVERY DESKTOP WIDTH. The full labels need about 1,470 px, so in
     anything narrower (itch's 1280 frame, most laptops) the coffee link and the camera
     toggles wrapped onto a second line and took 40 px off the canvas. Between 981 and
     1500 the labels drop their long halves instead: the parts that go are wrapped in
     .lng in the markup, and Undo/Redo become arrows via .sht. Buy me a coffee keeps its
     whole label on purpose. Under 981 the existing rules below take over. */
  .sht{display:none}
  @media (max-width: 1500px) and (min-width: 981px){
    header{gap:4px;padding:7px 8px}
    header .vr{margin:0 2px}
    header button{padding:6px 6px;letter-spacing:.5px}
    header .lng, header .hint{display:none}
    header .sht{display:inline;font-size:13px;line-height:1}
  }
  /* LOADING. Over everything, the title and the gallery included, on the stock colour
     the interface is printed on. Before game.js runs the bar has no real number to show,
     so it creeps on its own towards 45% and stops there: it never claims to be done
     while the thing that would be doing is still downloading. core.js adds .live and
     sets the width from then on. */
  #boot{position:fixed;inset:0;z-index:200;display:flex;flex-direction:column;
    align-items:center;justify-content:center;gap:12px;background:var(--bg);
    transition:opacity .35s ease-out}
  #boot.gone{opacity:0;pointer-events:none}
  #boot .bootmsg{font:800 13px/1.4 var(--body);letter-spacing:.6px;color:var(--txt)}
  #boot .bootbar{width:min(240px,60vw);height:8px;border:1.5px solid var(--line);
    background:var(--panel);box-shadow:2px 2px 0 var(--line)}
  #boot .bootbar i{display:block;height:100%;width:0;background:var(--acc2);
    animation:bootcreep 2.4s cubic-bezier(.2,.7,.3,1) forwards}
  #boot .bootbar i.live{animation:none;transition:width .25s ease-out}
  @keyframes bootcreep{from{width:0}to{width:45%}}
  @media (prefers-reduced-motion:reduce){
    #boot .bootbar i{animation:none;width:45%}
    #boot,#boot .bootbar i.live{transition:none}
  }
  #title{position:fixed;inset:0;z-index:120;display:none;flex-direction:column;
    align-items:center;justify-content:center;text-align:center;
    background:rgba(239,230,210,.80)}
  #title.show{display:flex}
  /* BEHIND THE TITLE, ONLY THE RIDE. The canvas is lifted out of the layout and over
     the whole window while the title is up, just under the title's own wash, so the
     header, the tool rail, the timeline and the HUD are all simply covered rather than
     each hidden and re-shown. Nothing about the layout changes, which matters: the
     phone holds and the Reddit post are measured by phone-check exactly as they were.
     #stage has no z-index, so this one is not trapped in its stacking context.
     The canvas measures its own box, so it resizes to the window and back by itself. */
  body.titling #cv{position:fixed;inset:0;width:100%;height:100%;z-index:110}
  /* The title mark scales with the viewport the way the type it replaces did — the same
     clamp, applied to width instead of font-size, so it grows on a desktop and stays
     inside the frame on a phone. */
  #title .bigmark{width:clamp(260px,52vw,620px);height:auto;display:block;margin:0 auto}
  #title .big{font:clamp(46px,10vw,116px)/0.86 var(--display);letter-spacing:-2px;
    text-transform:uppercase;color:var(--txt);text-shadow:5px 5px 0 var(--acc2)}
  #title .big i{font-style:normal;color:var(--txt)}
  #title .tag{color:var(--txt);margin-top:16px;font:800 clamp(11px,1.5vw,13px)/1.5 var(--body);
    letter-spacing:2.6px;text-transform:uppercase;border-top:2px solid var(--line);
    border-bottom:2px solid var(--line);padding:7px 14px}
  #title .row{display:flex;gap:12px;margin-top:30px;flex-wrap:wrap;justify-content:center}
  #title button{padding:13px 24px;font-size:12px;border-radius:0;box-shadow:3px 3px 0 var(--line)}
  #title button.primary{padding:14px 32px;font-size:13px;box-shadow:4px 4px 0 var(--line)}
  /* The gallery is the other half of the game, not a third-tier option, so it gets
     the posting green it wears in the bar rather than the plain printed grey — Play
     stays orange, and the two loudest things on the screen are the two things you
     can actually do. */
  #title #tGallery{background:var(--good);color:var(--panel);font-weight:800}
  #title #tGallery:hover{background:var(--txt);color:var(--panel)}

  /* ---------- chipping in ----------
     On the title screen, which is the one place a request for money is fair: you
     see it once on the way in, before you have been given anything, and never
     again while you are using the thing. The results card was the other candidate
     and is exactly wrong — a hand out after every single ride is a toll booth.

     Printed as a stamped card rather than a button so it reads as a notice and not
     as a fourth thing competing with Play. Hidden by default in the markup; only
     the adapter turns it on, and only on our own site. */
  #title .tdonate{display:none;margin-top:26px;padding:12px 18px;text-align:center;
    border:2px solid var(--line);box-shadow:4px 4px 0 var(--line);background:var(--stock);
    max-width:420px}
  #title .tdonate.on{display:block}
  #title .tdonate b{display:block;color:var(--txt);font:800 12px/1.5 var(--body);
    letter-spacing:1.4px;text-transform:uppercase}
  #title .tdonate a{display:inline-block;margin-top:9px;padding:9px 22px;font:800 12px/1 var(--body);
    letter-spacing:1px;text-transform:uppercase;color:var(--line);background:var(--warn);
    border:2px solid var(--line);box-shadow:3px 3px 0 var(--line);text-decoration:none}
  #title .tdonate a:hover{transform:translate(1px,1px);box-shadow:2px 2px 0 var(--line)}
  #title .tdonate i{display:block;margin-top:9px;color:var(--dim);font-size:10.5px;
    font-style:normal;letter-spacing:.6px}

  /* ---------- empty state ---------- */
  #blank{position:absolute;left:50%;top:44%;transform:translate(-50%,-50%);z-index:15;display:none;
    text-align:center;width:min(430px,86%);background:var(--panel);border:2px solid var(--line);
    padding:20px 22px;box-shadow:6px 6px 0 var(--line)}
  #blank.show{display:block}
  #blank .bt{font:26px/1 var(--display);text-transform:uppercase;letter-spacing:.5px}
  #blank .bb{margin-top:9px;color:var(--dim);font-size:12px;line-height:1.6}
  #blank .brow{display:flex;gap:6px;flex-wrap:wrap;justify-content:center;margin-top:15px}
  #blank .bk{margin-top:12px;font-size:10.5px;letter-spacing:1px;color:var(--dim);text-transform:uppercase}
  #blank .bk b{color:var(--txt)}

  /* ---------- tutorial callout ---------- */
  #tut{position:absolute;left:50%;transform:translateX(-50%);bottom:16px;z-index:60;display:none;
    width:min(560px,calc(100% - 32px));background:var(--panel);border:2px solid var(--line);
    border-radius:0;padding:14px 16px;box-shadow:5px 5px 0 var(--line)}
  #tut.show{display:block}
  #tut .step{color:var(--acc2);font:800 10px/1 var(--body);letter-spacing:1.8px;text-transform:uppercase}
  #tut h3{margin:7px 0 6px;font:19px/1.05 var(--display);text-transform:uppercase;letter-spacing:.3px}
  #tut p{margin:0;color:var(--txt);font-size:12.5px;line-height:1.55}
  #tut .btnrow{margin-top:12px}
  #tut .btnrow .sp{flex:1}
  #tut .prog{height:5px;border-radius:0;background:var(--panel2);margin-top:12px;overflow:hidden;
    border:1.5px solid var(--line)}
  #tut .prog i{display:block;height:100%;background:var(--acc2);transition:width .25s}

/* ===================================================================
   POST-SIZED LAYOUT
   A Reddit web view is a short, often narrow iframe — nothing like the
   desktop window this interface was drawn for. Rather than a second
   interface, the same one degrades: labels drop off the tool rail, the
   telemetry panel keeps only the two numbers you watch while riding,
   and the header scrolls sideways instead of wrapping into three rows
   that eat the canvas.
   =================================================================== */

/* ---- narrow: the tool rail loses its words, keeps its glyphs ---- */
@media (max-width: 980px){
  #tools{width:50px;padding:4px 3px;overflow-y:auto;scrollbar-width:none}
  #tools::-webkit-scrollbar{display:none}
  /* font-size:0 collapses the bare label text nodes without needing to
     wrap every one of them in a span; the SVG glyphs are sized in px. */
  .tool{font-size:0;justify-content:center;padding:6px 0;gap:0;margin-bottom:1px}
  .tool .k{display:none}
  .tool .ic{width:20px;height:20px}
  .tool .ic svg{width:19px;height:19px}
  #tools h4{font-size:0;height:0;margin:7px 2px 5px;padding:0;
    border-bottom:2px solid var(--line)}
  #tools h4 .h4k{display:none}
  #lookList button{padding:4px 2px;gap:0}
  #lookList .nm,#lookList .tg{display:none}
  #lookList .sw{width:100%;height:15px}
  #propList{grid-template-columns:1fr;gap:2px}
  #propList button{font-size:8px;letter-spacing:0;padding:4px 1px}

  header{flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;gap:5px;
    padding:6px 8px;scrollbar-width:none}
  header::-webkit-scrollbar{display:none}
  header button{padding:6px 8px;font-size:10px}
  .logo{height:21px;margin-right:6px}
  .vr{margin:0 1px}

  /* telemetry: keep speed and G, drop the table — the ride report has it all */
  #hud{width:132px;padding:7px 8px;top:8px;right:8px}
  #hud .row,#hud hr{display:none}
  #hud .big{font-size:25px}
  #airBadge{display:block;font-size:9px;margin-top:2px}
  #tip{display:none}
  #selPanel{width:200px;left:8px;top:8px;padding:8px}
}

/* ---- very narrow: phones in portrait ---- */
@media (max-width: 620px){
  #tools{width:46px}
  .tool{padding:7px 0}
  .tool .ic svg{width:17px;height:17px}
  #hud{width:104px;padding:5px 6px}
  #hud .big{font-size:21px}
  #hud .lbl{font-size:8.5px;letter-spacing:.9px}
  #blank{width:calc(100% - 20px);padding:14px}
  #blank .bt{font-size:19px}
  #blank .bb{font-size:11px}
  #tut{padding:11px 12px;bottom:8px}
  #tut h3{font-size:15px}
  #tut p{font-size:11.5px}
  .card{padding:15px 16px}
  .card h2{font-size:19px}
}

/* ---- short: the vertical budget is the scarce one in a post ---- */
@media (max-height: 700px){
  #tlcv{height:52px}
  #tl{padding:5px 8px;gap:6px}
  #tl button{padding:5px 7px;font-size:10px}
  #tlTime{min-width:96px;font-size:10px}
}
@media (max-height: 560px){
  #tlcv{height:40px}
  header{padding:4px 8px}
  header button{padding:5px 7px}
  #tut{bottom:6px;padding:9px 11px}
  #tut .prog{margin-top:8px}
  #tut .btnrow{margin-top:8px}
}

/* ===================================================================
   PHONE: REARRANGED, NOT SHRUNK
   The queries above shrink the desktop layout. That is right down to about a
   tablet and wrong on a phone: at 340x500 — a Reddit post in a feed — roughly
   half the box went to chrome and the canvas, the only part that matters, got
   the leftovers. A 190px rail at 46px wide is still a rail.

   So below 500px the arrangement changes. The tool rail stops being a rail and
   becomes a thumb-height strip under the canvas; the telemetry panel stops being
   a panel and becomes one line; the selection panel stops floating over the
   drawing area and becomes a sheet at the bottom of it.

   WHY 500px, and why width only. Every desktop Reddit post panel is wider than
   this — Reddit's own layout puts a custom post at roughly 640-750px, and the
   existing queries already treat anything under 980px as narrow — so a desktop
   post keeps the editor it has today and cannot fall into this branch. Height is
   deliberately not tested: a phone held in landscape is short and wide and wants
   the roomier arrangement, not this one.

   Nothing here modifies an existing rule; it is all inside this query, so at any
   width above 500px none of it applies at all.
   =================================================================== */
@media (max-width: 500px){
  /* canvas on top and dominant, tools beneath it */
  main{flex-direction:column}
  #stage{order:1; flex:1 1 auto; min-height:0}
  #tools{order:2; width:auto; height:auto; flex:0 0 auto;
    display:flex; flex-direction:row; align-items:center; gap:4px;
    overflow-x:auto; overflow-y:hidden; padding:5px 6px;
    border-right:0; border-top:2px solid var(--line); scrollbar-width:none}
  #tools::-webkit-scrollbar{display:none}
  /* the group headings are wayfinding for a vertical list; in a strip they are noise */
  #tools h4, #tools #propList{display:none}
  /* 44px minimum in both directions: a fingertip is about 8mm */
  .tool{width:auto; flex:0 0 auto; min-width:44px; min-height:44px;
    margin:0; padding:0 7px; justify-content:center}
  .tool .ic{width:22px; height:22px}
  .tool .ic svg{width:21px; height:21px}

  /* telemetry: the two numbers you watch while riding, on one line, top-left so
     it sits over sky rather than over the track you are drawing */
  #hud{width:auto; left:6px; right:auto; top:6px; padding:4px 8px;
    display:flex; gap:10px; align-items:baseline}
  #hud .lbl, #hud hr, #hud .row{display:none}
  /* the gauge STAYS on a phone. It was hidden when it was a wide horizontal bar
     competing for space; as a slim vertical strip it costs 18px and it is the most
     glanceable thing in the HUD while riding. */
  #hud .gtop{gap:7px}
  #hud .gwrap{width:18px;min-height:46px}
  #hud .big{font-size:18px}
  #hud .unit{font-size:9px}

  /* a floating panel over a 340px canvas covers the thing it describes */
  #selPanel{left:6px; right:6px; width:auto; top:auto; bottom:6px;
    max-height:46%; overflow-y:auto; padding:8px}
}

/* ===================================================================
   SNUG: ONE TRANSPORT, AND THE HEIGHT GOES TO THE CANVAS

   Reddit's mobile web-view embed renders short, and the layout above filled that
   short box with two play buttons, four transport controls, a speed selector and a
   running clock — so the canvas, the only part anybody came for, got a letterbox.
   Everything here is subtraction.

   WHY THIS QUERY AND NOT THE ONE THE JS USES. Hit-testing keys off COMPACT_Q,
   which includes `pointer: coarse` — correct there, because a touchscreen laptop
   draws with a finger and needs finger-sized targets at any size. It is wrong
   here: that same laptop at 1920x1080 is roomy and should keep every control. So
   this gate is about the BOX, not the input device.

   Nothing in here modifies an existing rule. Above 620px wide and 560px tall none
   of it applies, which is what keeps desktop and roomy tablets untouched.
   =================================================================== */
@media (max-width: 620px), (max-height: 560px){
  /* ONE play control, and it lives with the scrubber it controls.
     The scrubber IS the transport: dragging it is faster and more precise than
     stepping a frame at a time, so ◀| |▶ were paying rent on a 40px-tall strip to
     do a worse job. togglePlay() falls through to startRide() when there is no
     simulation yet, so #tlPlay alone is a complete transport and hiding the header
     button costs nothing. Hidden with CSS rather than removed from the DOM on
     purpose: startRide() writes '1x' into #tlRate and the timeline writes the
     clock into #tlTime every frame, and both keep working on an unseen element. */
  #btnRide{display:none}
  #tlPrev,#tlNext,#tlRate,#tlTime{display:none}
  #tlPlay{min-width:46px;min-height:34px;font-size:14px}
  #tl{gap:9px;padding:5px 7px}

  /* The HUD is two numbers and the gauge. Anything else is a table, and the ride
     report already has the table. Capped in height as well as trimmed, because on
     a short embed it is vertical space the HUD steals, not horizontal. */
  #hud .row,#hud hr{display:none}
  #hud{padding:5px 7px}
  #hud .gwrap{min-height:42px}
  #tip{display:none}
}

/* ---- the drawing surface belongs to the game, not the browser ----
   UNGATED on purpose, and the only rule here that is not inside a media query.

   Without this, a finger dragged across the canvas is claimed by the browser as
   a scroll or a pinch-zoom of the page, so drawing either does nothing or drags
   the whole interface around. It cannot be gated behind a size query: a
   touchscreen laptop at a roomy size draws with a finger too, and would keep
   zooming the page out from under itself.

   It is safe to leave unconditional because touch-action governs touch and pen
   only — it has no effect on a mouse, so the desktop mouse path is unchanged by
   it. Input handling relies on this: the pointerdown handler in core.js
   deliberately does NOT call preventDefault for a mouse, which is what keeps
   focus and text-selection behaving as they always have, and this rule is what
   covers touch instead. */
#cv{touch-action:none;transition:opacity .38s ease-out}
/* Held back while the title screen's coaster is still arriving, so the frame before
   it lands does not flash the track that was already loaded. Opacity rather than
   display, because the canvas must keep its size — the game measures it. */
#cv.veil{opacity:0;transition:none}

/* ---- touch: fingers need bigger targets than a mouse ---- */
@media (pointer: coarse){
  .camgrp .cam{width:38px;height:36px}
  .tool{padding:10px 0}
  button{padding:8px 11px}
  #tl button{padding:8px 10px}
  input[type=range]{height:26px}
  .chk{margin-top:12px}
}

/* ---------- settings panel rows ---------- */
.setrow{display:flex;align-items:center;gap:9px;width:100%;text-align:left;
  padding:7px 9px;margin-bottom:3px;text-transform:none;letter-spacing:0;font-weight:400}
.setrow .sw2{width:15px;height:15px;flex:0 0 auto;border:2px solid var(--line);background:var(--panel2)}
.setrow.on .sw2{background:var(--acc2)}
.setrow.on .sw2::after{content:"";display:block;width:5px;height:9px;margin:-1px 0 0 3px;
  border:2px solid var(--panel);border-top:0;border-left:0;transform:rotate(40deg)}
.setrow .txt{display:flex;flex-direction:column;gap:1px;min-width:0}
.setrow .txt b{font:800 11.5px/1.3 var(--body);letter-spacing:.5px;text-transform:uppercase}
.setrow .txt i{font-style:normal;font-size:10.5px;color:var(--dim);line-height:1.4}
.setrow:hover .txt i{color:var(--panel)}
.setrow kbd{margin-left:auto;font:700 10px/1 ui-monospace,Menlo,monospace;color:var(--dim);
  border:1.5px solid var(--line);padding:3px 5px;flex:0 0 auto}
.setrow:hover kbd{color:var(--panel);border-color:var(--panel)}
.setrow.static{cursor:default;border-color:transparent;background:transparent}
.setrow.static:hover{background:transparent;color:var(--txt)}
.setrow.static .txt i{color:var(--dim)}
.nudge{margin-left:auto;display:flex;align-items:center;gap:5px;flex:0 0 auto}
.nudge b{font:800 13px/1 var(--body);min-width:16px;text-align:center;
  font-variant-numeric:tabular-nums}
.nudge button{padding:4px 9px;font-size:12px}
#card #lookList{margin-bottom:2px}
#card #lookList button{display:flex;align-items:center;gap:9px;width:100%;text-align:left;
  padding:6px 9px;margin-bottom:3px;text-transform:none;letter-spacing:0}
#card #lookList .sw{width:26px;height:16px;border:1.5px solid var(--line);flex:0 0 auto}
#card #lookList .nm{font:800 11.5px/1 var(--body);letter-spacing:.5px;text-transform:uppercase}
#card #lookList .tg{margin-left:auto;font-size:9px;letter-spacing:1px;color:var(--dim)}
#card #lookList button.on .tg{color:var(--panel)}

/* actions inside Settings, two up */
.pick2{display:grid;grid-template-columns:1fr 1fr;gap:5px;margin-bottom:2px}
.pick2 button{padding:8px 7px;font-size:10px}

/* ---- narrow: the header keeps only what you need constantly; everything else is
        reachable from Settings, which is always the second button ---- */
@media (max-width: 760px){
  #btnPresets,#btnSave,#btnTutorial,#btnReset,#btnKeys,#btnClear{display:none}
  header{overflow-x:visible}
}


/* ---------- a genuinely destructive button should look it ---------- */
button.wreck{background:var(--bad);color:var(--panel);border-color:var(--line);
  box-shadow:2px 2px 0 var(--line)}
button.wreck:hover{background:var(--txt);color:var(--bad)}

/* ---------- camera cluster, right end of the top bar ---------- */
.camgrp{display:inline-flex;border:2px solid var(--line);flex:0 0 auto}
/* icon only: a square target, glyph centred, no text to wrap or truncate */
.camgrp .cam{border:0;border-radius:0;padding:0;width:32px;height:30px;
  display:inline-flex;align-items:center;justify-content:center;gap:0;font-size:0}
.camgrp .cam + .cam{border-left:2px solid var(--line)}
.camgrp .cam .ic{width:18px;height:18px;display:flex;align-items:center;justify-content:center}
.camgrp .cam .ic svg{width:18px;height:18px;display:block;stroke:currentColor;fill:none;
  stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.camgrp .cam .ic svg .f{fill:currentColor;stroke:none}
.camgrp .cam.on{background:var(--acc);color:var(--panel)}

/* ---------- rail glyphs centred on a common axis ---------- */
#tools .tool{align-items:center}
#tools .tool .ic{width:20px;height:20px;flex:0 0 auto;display:flex;
  align-items:center;justify-content:center}
#tools .tool .ic svg{display:block;margin:0 auto}

/* ---------- tabbed settings ---------- */
.tabs{display:flex;gap:0;border:2px solid var(--line);margin:12px 0 14px}
.tabs button{flex:1;border:0;border-radius:0;padding:8px 4px;background:var(--panel2);color:var(--dim)}
.tabs button+button{border-left:2px solid var(--line)}
.tabs button.on{background:var(--txt);color:var(--panel)}
.slider{display:grid;grid-template-columns:1fr auto;align-items:baseline;
  gap:0 8px;margin:2px 0 10px;padding:0 2px}
.slider input{grid-column:1/3;margin-top:4px}
.slider b{font-variant-numeric:tabular-nums;font:800 11px/1 var(--body)}
.nowplay{border-left:3px solid var(--acc2);background:var(--panel2);padding:7px 9px;margin-bottom:8px}
.nowplay .lbl{display:block}
.nowplay b{display:block;font:800 13px/1.3 var(--body);letter-spacing:.4px;text-transform:uppercase}
.nowplay i{font-style:normal;font-size:11px;color:var(--dim)}
.setlist{display:flex;flex-direction:column;gap:3px;margin-top:8px}
.setlist button{text-align:left;text-transform:none;letter-spacing:0;padding:7px 9px}
.setlist b{display:block;font:800 11.5px/1.3 var(--body);letter-spacing:.4px;text-transform:uppercase}
.setlist i{font-style:normal;font-size:10px;color:var(--dim)}
.setlist button.on i,.setlist button:hover i{color:var(--panel)}

/* look tab: a swatch preview per press run */
.looks{display:flex;flex-direction:column;gap:4px}
.looks button{display:flex;align-items:center;gap:10px;text-align:left;
  text-transform:none;letter-spacing:0;padding:6px 8px}
.looks .prev{display:flex;width:74px;height:30px;border:1.5px solid var(--line);flex:0 0 auto;overflow:hidden}
.looks .prev i{flex:1;display:block}
.looks .prev i.h{flex:.7}
.looks .prev i.r{flex:0 0 5px}
.looks .nm2 b{display:block;font:800 11.5px/1.3 var(--body);letter-spacing:.4px;text-transform:uppercase}
.looks .nm2 i{font-style:normal;font-size:10px;color:var(--dim)}
.looks button.on .nm2 i,.looks button:hover .nm2 i{color:var(--panel)}

/* livery swatches */
.livery{display:grid;grid-template-columns:repeat(8,1fr);gap:4px;margin-bottom:6px}
.livery button{padding:0;height:30px;display:flex;overflow:hidden}
.livery button span{flex:1}
.livery button.on{outline:3px solid var(--acc2);outline-offset:-3px}

/* ---------- ride result: a notification, not a road block ----------
   The old full-screen card stopped play dead every single time the train came home. */
#rideNote{position:absolute;right:12px;bottom:12px;z-index:40;display:none;
  align-items:center;gap:10px;max-width:min(330px,calc(100% - 24px));
  background:var(--panel);border:2px solid var(--line);padding:9px 10px;
  box-shadow:4px 4px 0 var(--line);cursor:pointer}
#rideNote.show{display:flex}
#rideNote:hover{background:var(--panel2)}
#rideNote .mk{width:24px;height:24px;flex:0 0 auto;display:flex;align-items:center;
  justify-content:center;background:var(--good);color:var(--panel);font-weight:800}
#rideNote.warn .mk{background:var(--warn)}
#rideNote.bad .mk{background:var(--bad)}
#rideNote .mk svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:2.6;
  stroke-linecap:round;stroke-linejoin:round}
#rideNote .body{display:flex;flex-direction:column;gap:1px;min-width:0}
#rideNote b{font:800 11.5px/1.3 var(--body);letter-spacing:.6px;text-transform:uppercase}
#rideNote i{font-style:normal;font-size:11px;color:var(--dim);line-height:1.4;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#rideNote #noteX{margin-left:auto;flex:0 0 auto;padding:3px 6px;font-size:11px;
  border-color:transparent;background:transparent;color:var(--dim)}
#rideNote #noteX:hover{border-color:var(--line);background:var(--txt);color:var(--panel)}
@media (max-width: 620px){
  #rideNote{right:8px;bottom:8px;padding:7px 8px;max-width:calc(100% - 16px)}
  #rideNote i{font-size:10px}
}

/* The ride chip sits at z-index 40 so it clears the canvas, which also put it above
   #modal at 20 — so it floated over any card opened while it was still up, including
   over the Settings panel. Hidden by sibling rather than restacked: the chip SHOULD be
   above the canvas, and #modal precedes it in the markup, so this says the real thing
   — while a card is open there is no chip — rather than juggling two z-indexes. */
#modal.show ~ #rideNote{display:none}

/* ---------- posting: the loudest thing in the bar after Ride ---------- */
button.ship{background:var(--good);color:var(--panel);border-color:var(--line);
  box-shadow:2px 2px 0 var(--line);font-weight:800}
button.ship:hover{background:var(--txt);color:var(--panel)}
button.ship:disabled{opacity:.5;cursor:default}
.shipbox{margin-top:14px;border:2px solid var(--line);background:var(--panel2);padding:12px}
.shipbox .score{display:flex;gap:14px;margin-bottom:10px}
.shipbox .score div{flex:1;text-align:center;border:1.5px solid var(--line);
  background:var(--panel);padding:7px 4px}
.shipbox .score b{display:block;font:26px/1 var(--display);font-variant-numeric:tabular-nums}
.shipbox .score i{display:block;font-style:normal;font:800 9px/1.4 var(--body);
  letter-spacing:1.2px;text-transform:uppercase;color:var(--dim);margin-top:3px}
.shipbox input[type=text]{width:100%;background:var(--panel);color:var(--txt);
  border:1.5px solid var(--line);padding:7px 8px;font:inherit;font-size:12px}
.verdictline{font:800 12px/1.4 var(--body);letter-spacing:1.4px;text-transform:uppercase;
  color:var(--tier,var(--acc2));margin-bottom:8px}

/* THE WRAPPER MUST NOT EXIST AS A BOX. #hdrRight groups the three right-hand header
   controls so the portrait block can move them as one, and display:contents is what
   makes that free: its children stay direct flex items of the header, so the desktop
   and Reddit-post headers lay out exactly as they did before the wrapper was added.
   Without this the wrapper becomes a flex item of its own and the .vr inside it stops
   stretching, which is a visible change at every size — see the portrait block, which
   overrides this to `flex` precisely because that is where the box IS wanted. */
#hdrRight{display:contents}

/* Present in the markup at every size, shown only by the portrait-phone block below.
   Kept out of the way rather than removed because core.js has always wired it, and
   because adding a Settings button to the desktop header is a different decision from
   this one — the 760px block's promise that "everything else is reachable from
   Settings" is still unkept above 500px, and still deliberately out of scope here. */
#btnSettings{display:none}
/* TWO LOCKS, exactly like the donate link: the "hidden" attribute in both shells and
   this rule. Only core.js opens both, and only when PLATFORM.canReport. One failed
   stylesheet should not be all that stands between a Reddit player and a button that
   silently eats what they typed. */
#btnBug{display:none}
#btnBug.on{display:inline-flex; align-items:center; justify-content:center}

/* ===================================================================
   PHONE, HELD VERTICALLY

   The 500px block above rearranges the phone layout: canvas on top, tool rail
   turned into a strip beneath it. That is right, and this does not undo any of it.
   What it adds is the part that only makes sense when the screen is TALLER than it
   is wide — the shape a phone actually gets held in.

   Two things change. The rail stops being one strip you scroll sideways and becomes
   three fixed rows on one grid, because a strip that scrolls hides half the tools
   behind a gesture nobody makes; there is width enough here for nine across, so
   nothing needs hiding. And the right-hand header controls come off the header and
   sit on the canvas, so the header stays one line.

   WHY orientation AND width, when the block above is width-only. That one is
   width-only on purpose: a phone in landscape is short and wide and wants the roomier
   arrangement. This one wants the opposite — it is specifically the vertical hold —
   so it tests both. A landscape phone is under 500px on neither axis it cares about
   and never reaches here.

   Nothing above is modified except where the comments below say so, and every rule is
   inside the query, so at any other size or orientation none of it applies.
   =================================================================== */
@media (max-width: 500px) and (orientation: portrait){

  /* ---- the header: one line, and the right-hand controls leave it ---- */

  /* overflow goes back to visible so the cluster below can hang out of the header and
     onto the canvas — the 980px block had made it a sideways-scrolling strip, which
     would have clipped anything hanging out of it.

     WRAP, NOT NOWRAP, and that is the whole reason this is safe. At the 481px this was
     drawn for the header is one line with room to spare, so nowrap would have looked
     identical — but overflow is visible now, so on a 320px phone nowrap would push the
     Gallery button off the side of a header that can no longer scroll to reach it.
     Wrapping instead costs a second header line only on the phones that need one, and
     the cluster below is positioned against the header's own height, so it follows.

     The header's padding is deliberately NOT touched. `right:0` on the cluster
     resolves against the header's PADDING EDGE, not its content box, so the 8px the
     980px block sets never held the bar off the corner in the first place. */
  header{position:relative; flex-wrap:wrap; overflow:visible}
  /* The rule that separated "Clear all" from the Gallery. Clear all is hidden at this
     size, so it was dividing the logo from a button — a line about nothing. Child
     selector, so the one inside #hdrRight is untouched. */
  header > .vr{display:none}

  /* Onto the canvas and flush into the corner, the way the HUD is flush into the
     other one. Absolute rather than a wrapped second header line, because a second
     line pushes the canvas down by its full height and the canvas is the only part
     of a phone screen worth spending pixels on. */
  /* z-index BELOW #modal's 20. At 30 this bar painted on top of every open card —
     Settings worst, because it is the card this button opens — so the first thing the
     new button did was cover the panel it had just brought up. Anything above 0 is
     enough to clear the canvas and the HUD, both of which sit at auto. */
  #hdrRight{position:absolute; top:100%; right:0; z-index:12;
    display:flex; align-items:stretch; gap:6px;
    padding:5px 6px; background:var(--panel);
    border:2px solid var(--line); border-top:0; border-right:0}
  #btnSettings{display:inline-flex; align-items:center; justify-content:center;
    order:1; padding:9px 14px}
  #hdrRight .vr{order:2}
  /* Camera toggles before the ask, which inverts the desktop order: at the end of the
     bar the ask is where a thumb reaching for the grid toggle cannot catch it. */
  #hdrRight .camgrp{order:3}
  #hdrRight .donate{order:4; margin-right:0}

  /* ---- the rail: three rows, one pitch ---- */

  /* THE ORDER BELOW IS THE DESIGN, and it is deliberately not the DOM order: ride
     pieces first, track types second, drawing tools last — on a phone the thing you
     reach for after placing a drop is another piece of ride, not another pencil.
     `order` re-sequences the grid's auto-placement without touching the markup,
     which is what keeps desktop and the Reddit post byte-identical.

     NINE COLUMNS AT ONE PITCH. Measured off the mockup, its glyphs ran at a 52px
     pitch but with a 99px hole in the first row, a 149px hole in the second, and the
     two active buttons rendering half again as wide as the rest. repeat(9,1fr) is
     what fixes that: one cell width, every row, so the columns line up down the whole
     strip and no glyph needs nudging. 481/9 lands within a pixel of the pitch the
     mockup was already using, so this is the spacing it was reaching for.

     grid-auto-rows stays `auto` and the 44px lives on the item, so the two zero-height
     break rows below can actually be zero. */
  #tools{display:grid; grid-template-columns:repeat(9,1fr); grid-auto-rows:auto;
    align-content:start; gap:0; padding:0; overflow:visible}
  /* 44px in both directions still, but the width comes from the column now, so the
     min-width and side padding that used to set it would only fight it. The
     pointer:coarse block also adds 10px of vertical padding to every .tool — fine in
     a row that scrolls, and the thing that made these rows uneven. */
  #tools .tool{min-width:0; min-height:44px; margin:0; padding:0}

  /* ROW BREAKS MADE FROM THE GROUP HEADINGS ALREADY IN THE MARKUP. A grid item that
     spans every column forces the next item onto a new row, so these two both draw
     the rules between the groups and guarantee the 8/7/9 split. A wrapped flex row
     would have re-split itself the first time a glyph changed width. The other five
     headings stay hidden. Which two is arbitrary: `order` does not move them for a
     screen reader, which reads the markup, so this announces nothing anywhere new. */
  #tools h4:nth-of-type(1), #tools h4:nth-of-type(2){
    display:block; grid-column:1/-1; height:0; margin:0; padding:0; font-size:0;
    border-top:2px solid var(--line); border-bottom:0}

  /* row 1 — the ride: elements, then the turnarounds */
  #tools [data-tool="drop"]{order:1}
  #tools [data-tool="loop"]{order:2}
  #tools [data-tool="hill"]{order:3}
  #tools [data-tool="bunny"]{order:4}
  #tools [data-tool="valley"]{order:5}
  #tools [data-tool="halfloop"]{order:6}
  #tools [data-tool="immel"]{order:7}
  #tools [data-tool="dive"]{order:8}
  #tools h4:nth-of-type(1){order:9}

  /* row 2 — what the rail does, then the camera, then the one look shortcut */
  #tools [data-tool="plain"]{order:10}
  #tools [data-tool="chain"]{order:11}
  #tools [data-tool="launch"]{order:12}
  #tools [data-tool="brake"]{order:13}
  #tools [data-tool="trigger"]{order:14}
  #tools #btnFit{order:15}
  #tools #btnCustLook{order:16}
  #tools h4:nth-of-type(2){order:17}

  /* row 3 — drawing, the barrel roll, and scenery */
  #tools [data-tool="select"]{order:18}
  #tools [data-tool="pencil"]{order:19}
  #tools [data-tool="line"]{order:20}
  #tools [data-tool="curve"]{order:21}
  #tools [data-tool="erase"]{order:22}
  #tools [data-tool="start"]{order:23}
  #tools [data-tool="pan"]{order:24}
  #tools [data-tool="roll"]{order:25}
  #tools [data-tool="scenery"]{order:26}

  /* Music and Ride move under Settings — that is what the Settings button is for on
     this layout. Visual style keeps its rail button because the mockup keeps it: it is
     the one of the three you change mid-build. It is still in Settings as well, so
     nothing became unreachable by taking the other two out of the rail. */
  #btnCustMusic, #btnCustRide{display:none}
}

/* ===================================================================
   PHONE, HELD SIDEWAYS

   The other hold, and it wants the opposite of the vertical one. Held sideways a
   phone has width to spare and almost no height: the 429px of a 934x429 landscape
   phone is less than half what the vertical hold gives, and the header and the
   timeline take the same bite out of it either way. So the rail cannot go under the
   canvas here — a strip beneath it costs 44 of the ~320px the stage has left.

   It goes BESIDE the canvas instead, as three columns rather than the one 50px
   column the 980px block gives it. Three columns of a 44px target cost 136px of a
   width that has hundreds to spare, and they show three times as many tools without
   scrolling as the single column did.

   THE COLUMNS ARE THE VERTICAL HOLD'S ROWS, TURNED. Same three groups, same
   sequence inside each, so the two holds are the same layout seen twice and nothing
   has to be re-learned when a phone is turned over. The column ORDER is the one
   thing that differs, and it differs because the mockup says so: drawing tools
   nearest the canvas edge you draw from, ride pieces furthest.

   WHY ALL THREE AXES. Width alone would catch a small tablet in landscape, which
   has the height to keep the roomier arrangement. Height alone would catch a short
   desktop window. It is the combination that is a phone on its side, and
   orientation is named as well so the intent is readable rather than inferred.

   Nothing above is modified except where the comments say so.
   =================================================================== */
@media (max-width: 980px) and (max-height: 560px) and (orientation: landscape){

  /* One number for the column, because the rail's width and the position of the
     floating cluster both derive from it and they must not drift apart. */
  :root{--railcol:44px}

  /* ---- the header gives up its right-hand controls, as it does in portrait ---- */
  /* wrap for the same reason as portrait: overflow is visible now, so the 980px
     block's nowrap would spill buttons off a header that can no longer scroll to
     reach them. It fits on one line at every width this query allows — below 760px
     the hide-block takes Load, Tutorial, Reset and Clear out — so the wrap is
     insurance that costs a line of the scarce height only if it is ever needed. */
  header{position:relative; overflow:visible; flex-wrap:wrap}

  /* Top-LEFT here, not top-right: the HUD is in the right-hand corner at this size
     and keeps its labels, so the right corner is taken. left is the rail's full
     width, which puts the bar flush against the rail's edge — `left` resolves
     against the header's padding edge, so no padding arithmetic is involved.
     No Settings button in this hold: the header is wide enough here to keep Load,
     Tutorial and Reset, so nothing had to be moved out of reach to make room. */
  #hdrRight{position:absolute; top:100%; left:calc(3 * var(--railcol));
    z-index:12; display:flex; align-items:stretch; gap:6px;
    padding:5px 6px; background:var(--panel);
    border:2px solid var(--line); border-top:0; border-left:0}
  #hdrRight .camgrp{order:1}
  #hdrRight .donate{order:2; margin-right:0}

  /* ---- the rail: three columns, one pitch ---- */

  /* grid-auto-flow:column fills a column top to bottom before starting the next, so
     the `order` values below read down each column rather than across.

     THE NINE ROWS ARE WHAT SPLITS THE GROUPS, and there are deliberately no break
     items here. Portrait can use them because its rows are grid-auto-rows:auto and a
     zero-height item makes a zero-height row. Columns here are a FIXED 44px, so a
     zero-WIDTH break cannot collapse: it eats a whole 44px column, pushes the group
     after it into the next one, and pushes the last group into an implicit column 5
     that width + overflow-x:hidden then clips away entirely. That shipped — the
     middle column was blank and every ride piece was gone. The groups are 9/9/8, so
     nine rows split them on their own, and phone-check holds that shape. */
  #tools{flex:0 0 auto; width:calc(3 * var(--railcol));
    display:grid; grid-template-columns:repeat(3, var(--railcol));
    grid-template-rows:repeat(9, auto); grid-auto-flow:column;
    justify-content:start; align-content:start;
    gap:0; padding:0; overflow-y:auto; overflow-x:hidden;
    /* The two column rules, painted rather than laid out, because anything with a box
       would have to come out of the 44px the touch target needs. A scroll container's
       background does not scroll with its content, so these stay put down the
       boundaries at 44 and 88px while the tools scroll past them. */
    background-image:linear-gradient(var(--line),var(--line)),
                     linear-gradient(var(--line),var(--line));
    background-size:2px 100%, 2px 100%;
    background-position:var(--railcol) 0, calc(2 * var(--railcol)) 0;
    background-repeat:no-repeat}
  #tools .tool{min-width:0; min-height:44px; margin:0; padding:0}
  /* propList is a grid of prop swatches and there is no column to spare for it; the
     vertical hold drops it at 500px for the same reason. The scenery tool still
     places whatever prop is selected. */
  #tools #propList{display:none}

  /* Every heading is hidden here — see the note on #tools above for why none of them
     is reused as a column break the way two are reused as row breaks in portrait. */
  #tools h4{display:none}

  /* column 1 — drawing, nearest the canvas you draw on */
  #tools [data-tool="select"]{order:1}
  #tools [data-tool="pencil"]{order:2}
  #tools [data-tool="line"]{order:3}
  #tools [data-tool="curve"]{order:4}
  #tools [data-tool="erase"]{order:5}
  #tools [data-tool="start"]{order:6}
  #tools [data-tool="pan"]{order:7}
  #tools [data-tool="roll"]{order:8}
  #tools [data-tool="scenery"]{order:9}

  /* column 2 — what the rail does, the camera, and the three customization shortcuts.
     Music and Ride keep their buttons in THIS hold. They only lose them in portrait
     because portrait gains a Settings button to reach them by; there is no Settings
     button here, so taking them off the rail would strand them. */
  #tools [data-tool="plain"]{order:10}
  #tools [data-tool="chain"]{order:11}
  #tools [data-tool="launch"]{order:12}
  #tools [data-tool="brake"]{order:13}
  #tools [data-tool="trigger"]{order:14}
  #tools #btnFit{order:15}
  #tools #btnCustLook{order:16}
  #tools #btnCustMusic{order:17}
  #tools #btnCustRide{order:18}

  /* column 3 — the ride: elements, then the turnarounds */
  #tools [data-tool="drop"]{order:19}
  #tools [data-tool="loop"]{order:20}
  #tools [data-tool="hill"]{order:21}
  #tools [data-tool="bunny"]{order:22}
  #tools [data-tool="valley"]{order:23}
  #tools [data-tool="halfloop"]{order:24}
  #tools [data-tool="immel"]{order:25}
  #tools [data-tool="dive"]{order:26}
}

/* [REC]: the card's progress bar and the finished video, played in place. */
.recbar{height:8px;border:1.5px solid var(--line);background:var(--panel);margin:10px 0 6px}
.recbar i{display:block;height:100%;width:0;background:var(--acc2);transition:width .2s}
.recvid{display:block;width:100%;margin-top:10px;border:2px solid var(--line);background:#000}
