/* The plan editor's chrome, in the product's own visual language.

   It used to be a dark CAD tool sitting inside a light product, and that was
   the whole complaint: somebody who traced their flat here and then went to
   3D crossed what looked like a boundary between two applications. Nothing
   about the editor changed in this pass -- same tools, same shortcuts, same
   validation, same «План готов» -- only what it looks like.

   TWO ACCENTS, and keeping them apart is the one rule in here that is not
   taste. `--accent` is the SELECTION COLOUR: js/theme.js writes it onto the
   root element every time somebody picks a swatch, because a pink highlight
   is invisible on a plan whose walls are printed pink. So it belongs to the
   drawing and to the things that mark the drawing -- the length badge, the
   cursor prompt, the swatch that is currently chosen -- and to nothing else.
   Everything that is CHROME uses `--brand`, the mark's own blue, sampled off
   docs/design/logo.png and the same value app/web/index.html and the login
   page use. Before this split, choosing a green highlight turned the whole
   interface green.

   The palette is the viewer's, name for name where the names agree, so that
   a person moving between the two screens is looking at one product. */
  :root{
    /* Surfaces, lightest first. `--ink` is the WELL a control sits in -- an
       input, a keycap, a figure's background -- and `--panel` is the chrome
       around it; they were the other way up in the dark theme, and the names
       kept their jobs rather than their brightness. */
    --ink:#FFFFFF; --panel:#FFFFFF; --panel-2:#F1F3F7;
    --line:#E1E5EB; --line-2:#EFF1F5;
    --text:#171B22; --dim:#6B747D;
    /* The workspace. It matches what render.js fills the canvas with, so the
       stage never flashes a different grey before the first paint. */
    --paper:#E9ECEF;

    /* The product's blue, and the three tints the rest of the site uses it in. */
    --brand:#3B5BF6; --brand-ink:#2B45D4; --brand-soft:#EEF2FF; --brand-line:#C9D5FF;

    /* The SELECTION colour. Overwritten at runtime -- see the note above. */
    --accent:#FF2D78;

    --teal:#0E9F8A; --teal-soft:#E7F6F3;
    --warn:#B8801B; --warn-soft:#FDF3E2; --warn-line:#EBD9B4;
    --blue:#2E86FF;

    --mono:ui-monospace,"SF Mono",Menlo,Consolas,monospace;
    --sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;

    /* One radius vocabulary and one shadow vocabulary, both the viewer's.
       Three arbitrary radii is how an interface stops looking like one. */
    --r-card:14px; --r-ctl:8px;
    --shadow-soft:0 2px 10px rgba(19,30,54,.06);
    --shadow-card:0 8px 28px rgba(19,30,54,.10);
    --shadow-pop:0 14px 40px rgba(19,30,54,.16);

    /* The two rows of chrome above the editor. Written once because the rail,
       the panel and the stage are all sized against what is left. */
    --head-h:56px;
  }
  *{box-sizing:border-box}
  html,body{height:100%;margin:0}
  /* THREE ROWS, not one. The product header is above the editor's own bar
     rather than merged into it, for the reason the viewer gives for the same
     split: they answer different questions -- who this is and how to get an
     account, against what am I doing to this plan -- and one row that mixed
     them would put «Войти» next to «Отменить». */
  body{
    font-family:var(--sans);color:var(--text);background:var(--panel);
    -webkit-font-smoothing:antialiased;
    display:grid;overflow:hidden;user-select:none;
    grid-template-columns:60px 272px 1fr;
    grid-template-rows:auto auto 1fr;
    grid-template-areas:
      "head head head"
      "ebar ebar ebar"
      "rail side stage";
  }

  /* ============================================================ product header
     The one row that is about the PRODUCT rather than about this plan. It is
     the viewer's header, rebuilt in this page's own stylesheet rather than
     imported: the editor has no build step and shares no CSS with the bundle,
     so what is shared here is the vocabulary -- the same tokens, the same
     control shapes, the same blue. */
  #head{
    grid-area:head;background:var(--panel);border-bottom:1px solid var(--line);
    display:flex;align-items:center;gap:12px;padding:9px 18px;min-height:var(--head-h);
  }
  #brand{
    display:flex;align-items:center;gap:11px;text-decoration:none;color:inherit;
    min-width:0;border-radius:var(--r-ctl);
  }
  #brand img{display:block;width:28px;height:28px;flex:none}
  #brandname{font-size:15px;font-weight:650;letter-spacing:-.01em;white-space:nowrap}
  #headactions{margin-left:auto;display:flex;align-items:center;gap:9px}
  /* The header's popover hangs off the RIGHT edge: anchored left it runs off
     the window, which is the whole difference between a menu and a scrollbar. */
  #headactions details.pop > .popbody{left:auto;right:0}
  #langpop > summary{font-weight:600;letter-spacing:.02em}
  #langpop > .popbody{min-width:200px}
  .langrow{
    display:flex;align-items:center;gap:10px;justify-content:space-between;
    width:100%;padding:5px 2px;color:var(--text);font-size:13px;
  }
  .langrow .tag{color:var(--dim);font-size:11px}
  .langrow[aria-disabled=true]{opacity:.55}
  .langrow .mark{color:var(--brand);font-weight:600}
  /* Signed in: the picture, the name and the way out, at the weight «Войти»
     had. An account is optional here, so the header must not start
     announcing one. */
  #authuser{display:flex;align-items:center;gap:8px;font-size:13px}
  #authuser img{border-radius:50%;display:block}
  #authuser > a{color:var(--text);text-decoration:none;font-weight:600}
  #authuser > a:hover{text-decoration:underline}
  #authout{margin:0}

  /* ---- the shared control shapes, borrowed name for name from the viewer --- */
  a.cta,a.ghost,a.plain{display:inline-flex;text-decoration:none;line-height:normal}
  .cta{
    display:inline-flex;align-items:center;justify-content:center;gap:8px;
    background:var(--brand);color:#fff;border:1px solid var(--brand);border-radius:var(--r-ctl);
    padding:9px 16px;font:inherit;font-weight:600;font-size:13px;cursor:pointer;white-space:nowrap;
  }
  .cta:hover{background:var(--brand-ink);border-color:var(--brand-ink);color:#fff}
  .cta svg{width:17px;height:17px;flex:none;fill:none;stroke:currentColor;
    stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}
  button.plain,a.plain,details.pop > summary{
    display:inline-flex;align-items:center;gap:7px;
    border:1px solid var(--line);background:var(--panel);border-radius:var(--r-ctl);
    padding:6px 12px;font:inherit;font-size:13px;cursor:pointer;color:var(--text);
  }
  button.plain:hover,a.plain:hover,details.pop > summary:hover{background:var(--line-2);border-color:#CFD6DF}
  button.plain svg,a.plain svg{width:15px;height:15px;flex:none;fill:none;stroke:currentColor;
    stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round;color:var(--dim)}
  /* Disabled, not hidden: a control that vanishes when it has nothing to do
     makes the bar move under the pointer, and it hides the fact that undo
     exists from somebody who has not yet done anything to undo. */
  button.plain:disabled{opacity:.4;cursor:default}
  button.plain:disabled:hover{background:var(--panel);border-color:var(--line)}
  details.pop{position:relative}
  details.pop > summary{list-style:none;user-select:none}
  details.pop > summary::-webkit-details-marker{display:none}
  details.pop > summary::after{content:'▾';color:var(--dim);font-size:11px}
  details.pop[open] > summary{background:var(--line-2)}
  details.pop > .popbody{
    position:absolute;z-index:30;top:calc(100% + 6px);left:0;
    background:var(--panel);border:1px solid var(--line);border-radius:var(--r-card);
    padding:11px 13px;display:flex;flex-direction:column;gap:4px;
    box-shadow:var(--shadow-card);white-space:nowrap;align-items:flex-start;
  }

  /* ============================================================ editor bar
     What page this is, and the one action that leaves it. «План готов» sits
     here because it is the primary action of the whole screen and it used to
     be a floating button in the bottom-right corner of the canvas, where it
     covered the plan and read as a widget rather than as the way on.

     It is the SAME button -- same id, same handler, same disabled rule -- and
     there is deliberately no second control beside it that goes to 3D. The
     way into 3D from a finished plan is validation first: see showReadyModal()
     in js/ui/modals.js. «Расстановка (3D)» in the panel is the other,
     pre-existing door and belongs to a project that already has one. */
  #ebar{
    grid-area:ebar;background:var(--panel);border-bottom:1px solid var(--line);
    display:flex;align-items:center;gap:10px;padding:8px 18px;min-height:52px;
  }
  #etitle{
    font-size:15px;font-weight:650;letter-spacing:-.01em;margin:0;
    color:var(--text);white-space:nowrap;
  }
  /* The project's name in the bar: the same weight as the title it replaces,
     so the bar does not change shape when a project appears, and a pencil that
     only turns solid under the cursor -- the name is a label first and a
     control second. `max-width` because a name is 120 characters if somebody
     wants it to be, and this bar has «План готов» on the other end of it. */
  .ename{
    display:inline-flex;align-items:center;gap:7px;max-width:min(38vw,340px);
    font:650 15px/1.2 var(--sans);letter-spacing:-.01em;color:var(--text);
    background:none;border:1px solid transparent;border-radius:8px;
    padding:4px 8px;margin:0 -8px;cursor:pointer;
  }
  .ename[hidden]{display:none}
  .ename > span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .ename svg{
    width:14px;height:14px;flex:none;fill:none;stroke:currentColor;stroke-width:1.8;
    stroke-linecap:round;stroke-linejoin:round;color:var(--dim);
  }
  .ename:hover{background:var(--panel-2);border-color:var(--line)}
  .ename:hover svg{color:var(--brand)}
  .ename:focus-visible{outline:2px solid var(--brand);outline-offset:1px}
  /* Unnamed reads as unnamed. It is a placeholder and not a name, and it must
     not look like one somebody chose. */
  .ename.unnamed > span{color:var(--dim);font-weight:500}
  #ebar .egap{flex:1}
  #ebar .edash{width:1px;align-self:stretch;background:var(--line);margin:0 4px}
  #bHome{color:var(--text);font-weight:500}
  #bHome svg{width:15px;height:15px}

  /* ============================================================ tool rail */
  #rail{
    grid-area:rail;background:var(--panel);border-right:1px solid var(--line);
    display:flex;flex-direction:column;align-items:center;padding:10px 0;gap:2px;
  }
  .tool{
    width:42px;height:42px;border-radius:10px;border:1px solid transparent;background:transparent;
    color:var(--dim);cursor:pointer;display:grid;place-items:center;position:relative;
    transition:background .12s ease,color .12s ease;
  }
  .tool:hover{background:var(--panel-2);color:var(--text)}
  .tool[aria-pressed="true"]{background:var(--brand);color:#fff;box-shadow:var(--shadow-soft)}
  .tool[aria-pressed="true"]:hover{background:var(--brand-ink);color:#fff}
  .tool:disabled{opacity:.3;cursor:default}
  .tool:disabled:hover{background:transparent;color:var(--dim)}
  .tool svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.6;
    stroke-linecap:round;stroke-linejoin:round}
  /* The shortcut, on the key itself. Small, in the corner, and legible --
     it is the only place the letter is written where somebody is not
     already hovering. */
  .tool .k{position:absolute;right:3px;bottom:1px;font:9px/1 var(--mono);opacity:.65;font-weight:600}
  .tool[aria-pressed="true"] .k{opacity:.85}
  .sep{width:26px;height:1px;background:var(--line);margin:6px 0}

  /* ============================================================ side panel */
  #side{
    grid-area:side;background:var(--panel);border-right:1px solid var(--line);
    overflow-y:auto;padding:0 16px 40px;
  }
  .sec{border-top:1px solid var(--line-2);padding:14px 0}
  .sec:first-of-type{border-top:none}
  .sec h2{
    font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--dim);
    margin:0 0 10px;font-weight:700;display:flex;justify-content:space-between;align-items:center;
  }
  .row{display:flex;align-items:center;gap:8px;margin-bottom:8px}
  .row label{font-size:12.5px;color:var(--dim);flex:0 0 82px}
  input[type=number],input[type=text],select{
    flex:1;min-width:0;background:var(--ink);border:1px solid var(--line);
    color:var(--text);border-radius:var(--r-ctl);padding:7px 9px;font:12.5px/1.2 var(--mono);
  }
  input:focus,select:focus{outline:none;border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-soft)}
  input[type=range]{flex:1;accent-color:var(--brand);min-width:0}
  .btn{
    background:var(--panel);border:1px solid var(--line);color:var(--text);
    border-radius:var(--r-ctl);padding:8px 11px;font-size:12.5px;cursor:pointer;
    font-family:var(--sans);transition:background .12s ease,border-color .12s ease;
  }
  .btn:hover{background:var(--line-2);border-color:#CFD6DF;color:var(--text)}
  .btn.wide{width:100%;margin-bottom:7px}
  .btn.on{background:var(--brand);border-color:var(--brand);color:#fff}
  .btn.on:hover{background:var(--brand-ink);border-color:var(--brand-ink);color:#fff}
  .bg{display:flex;gap:6px;margin-bottom:8px}
  .bg .btn{flex:1;padding:7px 4px;text-align:center}
  .empty{font-size:12px;color:var(--dim);line-height:1.55}
  .kbd{
    font:10px/1 var(--mono);background:var(--panel-2);border:1px solid var(--line);
    border-bottom-width:2px;border-radius:4px;padding:2px 5px;color:var(--text);
  }
  .stat{display:flex;justify-content:space-between;font:12px/1.7 var(--mono);color:var(--dim)}
  .stat b{color:var(--text);font-weight:600}
  .hintbox{
    font-size:11.5px;line-height:1.6;color:var(--dim);background:var(--panel-2);
    border-radius:10px;padding:10px 11px;
  }
  .hintbox b{color:var(--text)}
  /* The primary action of the «Подложка» section, and the one thing on this
     panel a first-time visitor is looking for. Dashed, so it reads as a place
     to drop something rather than as one more grey button. */
  .btn.pick{
    display:flex;align-items:center;justify-content:center;gap:9px;width:100%;
    background:var(--brand-soft);border:1px dashed var(--brand-line);color:var(--brand-ink);
    font-weight:600;padding:14px 12px;margin-bottom:8px;line-height:1.3;text-align:left;
  }
  .btn.pick:hover{background:#E4EAFF;border-color:var(--brand);color:var(--brand-ink)}
  .btn.pick svg{width:18px;height:18px;flex:none;fill:none;stroke:currentColor;
    stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
  .btn.pick small{display:block;font-weight:400;font-size:11px;color:var(--dim)}
  /* «Дополнительно»: JSON in and out, and wiping the geometry. Folded away
     rather than removed -- they are real controls with real uses, and neither
     is anything a person tracing their flat reaches for. */
  details.sec > summary{
    list-style:none;cursor:pointer;font-size:10.5px;letter-spacing:.12em;
    text-transform:uppercase;color:var(--dim);font-weight:700;
    display:flex;align-items:center;justify-content:space-between;
  }
  details.sec > summary::-webkit-details-marker{display:none}
  details.sec > summary::after{content:'▾';letter-spacing:0;font-size:11px}
  details.sec[open] > summary{margin-bottom:10px}
  details.sec[open] > summary::after{content:'▴'}
  details.sec > summary:hover{color:var(--text)}

  /* wall list */
  #list{max-height:210px;overflow-y:auto;margin:-2px -4px 0}
  .li{
    display:flex;align-items:center;gap:6px;padding:5px 6px;border-radius:6px;
    font:11.5px/1 var(--mono);color:var(--dim);cursor:pointer;
  }
  .li:hover{background:var(--panel-2);color:var(--text)}
  .li.sel{background:var(--brand);color:#fff}
  .li .nm{flex:0 0 34px;opacity:.7}
  .li .ln{flex:1}
  .li .th{opacity:.6}
  .li .x{width:18px;height:18px;border-radius:4px;display:grid;place-items:center;opacity:.5}
  .li .x:hover{opacity:1;background:#FCE8EF;color:#D63864}
  .li.bad .ln{color:var(--warn)}
  .li.sel .x:hover{background:rgba(255,255,255,.22);color:#fff}

  /* ============================================================ the workspace
     Distinct from the chrome around it on purpose: the panels are white, the
     drawing sits on light grey, and the eye lands on the grey. */
  #stage{grid-area:stage;position:relative;background:var(--paper);overflow:hidden}
  canvas{display:block;position:absolute;inset:0;touch-action:none}

  /* ---- the canvas controls, in one strip over the top of the drawing ----
     Still absolutely positioned rather than a fourth grid row: #fab, #ctx,
     #bartip and #totalEdit are all placed against #stage's own box, and
     taking 40 px out of it would move every one of them by 40 px. */
  /* It WRAPS rather than squashes, and that is the only thing about it that is
     not obvious. Every child was a shrinkable flex item, so on a 1050 px stage
     the bar silently crushed «сумма 21.95 м²» onto two lines inside a 40 px
     strip and flattened the six step buttons to ten pixels of nothing. Nothing
     overflowed and nothing said anything.

     So: nothing shrinks, and a bar with more in it than fits takes a second
     row. It only ever has to on a plan with problems to report -- «сшить
     разрывы» and «углы ≈90°» appear only when their counts are above zero, and
     without them the row fits a 1280 px laptop with room to spare. */
  #topbar{
    position:absolute;top:0;left:0;right:0;min-height:40px;display:flex;align-items:center;
    align-content:center;flex-wrap:wrap;gap:5px 8px;padding:5px 9px;
    background:rgba(255,255,255,.92);backdrop-filter:blur(8px);
    font:11px/1 var(--sans);color:var(--dim);z-index:5;border-bottom:1px solid var(--line);
  }
  #topbar > *{flex:0 0 auto;white-space:nowrap}
  #topbar b{color:var(--text);font-weight:600;font-family:var(--mono)}
  #topbar .sp{flex:1 1 0}
  #topbar .tdiv{width:1px;height:16px;background:var(--line);flex:none}
  #topbar .tlabel{color:var(--dim);font-weight:700;font-size:10.5px;white-space:nowrap}
  /* A label and the control it names wrap TOGETHER. Without this the bar
     breaks between «Шаг сетки» and its six buttons, and a label at the end
     of one row naming something at the start of the next is worse than no
     label. */
  #topbar .tgroup{display:inline-flex;align-items:center;gap:8px}
  /* The step buttons are one control, so they are one shape with divisions
     in it rather than six loose buttons -- the reference's segmented switch,
     and the same `.seg` idea the viewer's toolbar uses. */
  #topbar .seg{display:inline-flex;border:1px solid var(--line);border-radius:var(--r-ctl);
    overflow:hidden;background:var(--panel)}
  #topbar .seg .snapbtn{border:0;border-radius:0;border-left:1px solid var(--line);padding:5px 7px}
  #topbar .seg .snapbtn:first-child{border-left:0}
  .snapbtn{
    background:var(--panel);border:1px solid var(--line);color:var(--text);
    border-radius:var(--r-ctl);padding:5px 8px;font:11px/1 var(--sans);cursor:pointer;
    display:inline-flex;align-items:center;gap:5px;white-space:nowrap;
  }
  .snapbtn:hover{background:var(--line-2);border-color:#CFD6DF}
  .snapbtn.on{background:var(--brand);border-color:var(--brand);color:#fff}
  .snapbtn.on:hover{background:var(--brand-ink);border-color:var(--brand-ink)}
  #topbar .seg .snapbtn.on{border-left-color:var(--brand)}
  /* «сшить разрывы» and «углы ≈90°» wear `.on` to mean THERE IS SOMETHING
     WRONG, not "this mode is enabled" -- they appear only when their count is
     above zero. Blue would read as a toggle somebody had switched on, so they
     are amber, which is what the same numbers are on the canvas. */
  #bWeld.on,#bSkew.on{background:var(--warn-soft);border-color:var(--warn-line);color:var(--warn)}
  #bWeld.on:hover,#bSkew.on:hover{background:#FAEBCE;border-color:var(--warn)}
  #bWeld.on b,#bSkew.on b{color:var(--warn)}

  /* floating action bar */
  #fab{
    position:absolute;z-index:6;display:none;align-items:center;gap:4px;background:var(--panel);
    border:1px solid var(--line);border-radius:11px;padding:5px;box-shadow:var(--shadow-card);
  }
  #fab button{
    background:transparent;border:none;color:var(--text);border-radius:7px;height:30px;min-width:30px;
    padding:0 8px;cursor:pointer;font:11.5px/1 var(--sans);display:grid;place-items:center;
  }
  #fab button:hover{background:var(--panel-2)}
  #fab button.on{background:var(--brand);color:#fff}
  #fab button.on:hover{background:var(--brand-ink)}
  #fab button.del:hover{background:#FCE8EF;color:#D63864}
  #fab svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.7;
    stroke-linecap:round;stroke-linejoin:round}
  #fab .fsep{width:1px;height:18px;background:var(--line);margin:0 2px}
  #fab input{
    width:66px;background:var(--ink);border:1px solid var(--line);color:var(--text);
    border-radius:6px;padding:5px 6px;font:11.5px/1 var(--mono);text-align:right;
  }
  #fab .lbl{font:10.5px/1 var(--mono);color:var(--dim);padding:0 2px}
  #fab .row{margin-bottom:6px}
  #fab .btn{padding:6px 10px}
  #fab .btn.primary{background:var(--brand);border-color:var(--brand);color:#fff;font-weight:600}
  #fab .btn.primary:hover{background:var(--brand-ink);border-color:var(--brand-ink);color:#fff}
  #fab .eyebrow{font:10px/1 var(--mono);letter-spacing:.14em;text-transform:uppercase;
    color:var(--dim);margin-bottom:8px}
  #fab .sub{color:var(--dim);font-size:11.5px;line-height:1.5}

  /* context menu */
  #ctx{
    position:absolute;z-index:7;display:none;background:var(--panel);border:1px solid var(--line);
    border-radius:11px;padding:5px;min-width:190px;box-shadow:var(--shadow-pop);
  }
  #ctx div.mi{padding:7px 10px;border-radius:6px;font-size:12.5px;cursor:pointer;
    display:flex;justify-content:space-between;gap:14px}
  #ctx div.mi:hover{background:var(--panel-2)}
  #ctx div.mi.del:hover{background:#FCE8EF;color:#D63864}
  #ctx div.mi span{font:10px/1 var(--mono);color:var(--dim);align-self:center}
  #ctx .msep{height:1px;background:var(--line-2);margin:4px 2px}
  #ctx .mh{padding:5px 10px 7px;font:10px/1 var(--mono);letter-spacing:.1em;
    text-transform:uppercase;color:var(--dim)}

  /* The running instruction for the current tool. Bottom-left, out of the way
     of the drawing and of the primary action, which is now in the bar above. */
  #hint{
    position:absolute;left:14px;bottom:14px;z-index:4;background:rgba(255,255,255,.96);
    border:1px solid var(--line);border-left:3px solid var(--brand);border-radius:10px;
    padding:10px 14px;font:12.5px/1.55 var(--sans);color:var(--dim);
    max-width:min(620px,64vw);box-shadow:var(--shadow-soft);backdrop-filter:blur(4px);
  }
  #hint b{color:var(--text)}
  /* The length being typed while a wall is drawn: this one IS about the
     drawing, so it wears the selection colour. */
  #lenentry{
    position:absolute;z-index:6;background:var(--accent);color:#fff;border-radius:7px;
    padding:5px 9px;font:13px/1 var(--mono);display:none;pointer-events:none;
    box-shadow:0 4px 14px rgba(19,30,54,.28);
  }
  #warn{
    position:absolute;right:14px;top:52px;z-index:6;background:var(--warn-soft);
    border:1px solid var(--warn-line);color:#7A5410;border-radius:10px;padding:10px 13px;
    font:12px/1.5 var(--sans);max-width:300px;display:none;box-shadow:var(--shadow-card);
  }

  /* selection colour palette */
  .sw{
    width:22px;height:22px;border-radius:6px;border:2px solid var(--panel);cursor:pointer;
    padding:0;box-shadow:0 0 0 1px var(--line);
  }
  .sw.on{box-shadow:0 0 0 2px var(--brand)}
  .swrow{display:flex;gap:5px;align-items:center;flex-wrap:wrap}
  #palette{
    position:absolute;z-index:9;display:none;background:var(--panel);border:1px solid var(--line);
    border-radius:var(--r-card);padding:11px;box-shadow:var(--shadow-pop);
  }
  #palette .t{font:10px/1 var(--mono);letter-spacing:.1em;text-transform:uppercase;
    color:var(--dim);margin-bottom:9px}
  #palette input[type=color]{width:30px;height:26px;padding:0;border:1px solid var(--line);
    border-radius:6px;background:var(--ink);cursor:pointer}

  /* a number in the top bar that explains itself on hover */
  .hint-src{cursor:help;border-radius:6px;padding:4px 4px;margin:0 -3px}
  .hint-src:hover{background:var(--panel-2);color:var(--text)}
  .hint-src.act{cursor:pointer}

  /* ---- «План готов» ------------------------------------------------------
     The primary action of the screen, and the ONLY door to 3D that a plan
     goes through: it opens the checklist in js/ui/modals.js, which is what
     decides whether «Перейти в 3D» is offered at all. Disabled until there is
     at least one wall, by updateBar() in js/ui/panels.js. */
  #bReady{
    display:inline-flex;align-items:center;gap:9px;background:var(--brand);color:#fff;
    border:1px solid var(--brand);border-radius:var(--r-ctl);padding:10px 18px;
    font:650 13.5px/1 var(--sans);cursor:pointer;white-space:nowrap;
    box-shadow:0 2px 10px rgba(59,91,246,.28);transition:background .12s ease,box-shadow .12s ease;
  }
  #bReady:hover{background:var(--brand-ink);border-color:var(--brand-ink);
    box-shadow:0 4px 16px rgba(59,91,246,.34)}
  #bReady svg{width:17px;height:17px;fill:none;stroke:currentColor;stroke-width:2.6;
    stroke-linecap:round;stroke-linejoin:round}
  #bReady:disabled{
    background:var(--panel-2);color:var(--dim);border-color:var(--line);
    box-shadow:none;cursor:default;
  }
  .rdy{display:flex;gap:9px;align-items:flex-start;font-size:13px;line-height:1.5;margin-bottom:10px}
  .rdy .m{flex:0 0 16px;font:13px/1.3 var(--mono);font-weight:700}
  .rdy.ok .m{color:var(--teal)} .rdy.no .m{color:var(--warn)}
  .rdy b{color:var(--text);font-weight:650}

  /* The instruction rides the cursor: a line in the corner is exactly where
     nobody is looking while placing something. Selection-coloured, because it
     is a mark on the drawing. */
  #cursorTip{
    position:absolute;z-index:9;display:none;pointer-events:none;white-space:nowrap;
    background:var(--accent);color:#fff;border-radius:8px;padding:7px 11px;
    font:600 12px/1 var(--sans);box-shadow:0 4px 14px rgba(19,30,54,.25);
  }
  #cursorTip.warn{background:var(--warn);color:#fff}
  #cursorTip small{display:block;font:400 10.5px/1.3 var(--mono);opacity:.88;margin-top:4px}

  #totalEdit{
    position:absolute;z-index:11;display:none;background:var(--panel);border:1px solid var(--line);
    border-radius:var(--r-card);padding:12px 13px;box-shadow:var(--shadow-pop);
  }
  #totalEdit .t{font:10px/1 var(--mono);letter-spacing:.1em;text-transform:uppercase;
    color:var(--dim);margin-bottom:9px}
  #totalEdit .note{font:11.5px/1.45 var(--sans);color:var(--dim);margin-top:10px;max-width:230px}
  #totalEdit input{background:var(--ink);border:1px solid var(--line);color:var(--text);
    border-radius:6px;padding:7px 8px;font:13px/1 var(--mono);text-align:right}
  #totalEdit input:focus{outline:none;border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-soft)}

  #bartip{
    position:absolute;z-index:10;display:none;max-width:300px;background:var(--panel);
    border:1px solid var(--line);border-left:2px solid var(--teal);border-radius:10px;
    padding:10px 12px;font:12px/1.5 var(--sans);color:var(--dim);
    box-shadow:var(--shadow-card);pointer-events:none;
  }

  /* ---- coach tips --------------------------------------------------------
     All three share one rule set: keeping two copies is how the second one
     ended up with unstyled buttons and a cramped last line. */
  #coachTool{
    position:fixed;z-index:60;display:none;width:320px;max-width:calc(100vw - 120px);
    background:var(--panel);border:1px solid var(--warn-line);border-radius:var(--r-card);
    padding:14px 14px 12px;box-shadow:var(--shadow-pop);
  }
  #coachTool:before{
    content:"";position:absolute;left:-7px;top:26px;width:12px;height:12px;background:var(--panel);
    border-left:1px solid var(--warn-line);border-bottom:1px solid var(--warn-line);
    transform:rotate(45deg);
  }
  #coachTool p{margin:0 0 12px;font-size:12.5px;line-height:1.6;color:var(--dim)}
  #coachTool p b{color:var(--text)}
  #coachTool .cact{display:flex;justify-content:space-between;align-items:center;gap:12px}
  #coachTool button{background:none;border:none;cursor:pointer;font-family:var(--sans);white-space:nowrap}
  #coachTool .never{color:var(--dim);font-size:11.5px;padding:6px 0;
    text-decoration:underline;text-underline-offset:2px}
  #coachTool .never:hover{color:var(--text)}
  #coachTool .got{color:#fff;background:var(--warn);border-radius:var(--r-ctl);padding:8px 15px;
    font-size:12px;font-weight:600}
  #coachTool .got:hover{background:#9C6C13}
  /* The rail button the tip points at, while it points at it. */
  .tool.pulse{background:var(--warn) !important;color:#fff !important}
  .tool.pulse:after{content:"";position:absolute;inset:-5px;border-radius:13px;
    border:2px solid var(--warn);animation:ring 1.4s ease-out infinite}
  .tool.pulse:before{content:"";position:absolute;inset:-5px;border-radius:13px;
    box-shadow:0 0 0 3px rgba(184,128,27,.24)}
  @keyframes ring{0%{transform:scale(1);opacity:.9}100%{transform:scale(1.35);opacity:0}}
  @media (prefers-reduced-motion:reduce){ .tool.pulse:after{animation:none;opacity:.9} }

  #coach, #coachX{
    position:absolute;z-index:8;display:none;width:300px;max-width:calc(100vw - 100px);
    max-height:calc(100% - 70px);overflow-y:auto;background:var(--panel);
    border:1px solid var(--brand-line);border-radius:var(--r-card);padding:14px 14px 12px;
    box-shadow:var(--shadow-pop);
  }
  #coach:before{
    content:"";position:absolute;top:-6px;left:calc(50% - 6px);width:10px;height:10px;
    background:var(--panel);border-left:1px solid var(--brand-line);
    border-top:1px solid var(--brand-line);transform:rotate(45deg);
  }
  #coach p, #coachX p{margin:0 0 14px;font-size:12.5px;line-height:1.6;color:var(--dim)}
  #coach p b, #coachX p b{color:var(--text)}
  #coach .cact, #coachX .cact{display:flex;justify-content:space-between;align-items:center;
    gap:12px;padding-top:2px}
  #coach button, #coachX button{background:none;border:none;cursor:pointer;
    font-family:var(--sans);white-space:nowrap}
  #coach .never, #coachX .never{color:var(--dim);font-size:11.5px;padding:6px 0;
    text-decoration:underline;text-underline-offset:2px}
  #coach .never:hover, #coachX .never:hover{color:var(--text)}
  #coach .got, #coachX .got{color:#fff;background:var(--brand);border-radius:var(--r-ctl);
    padding:8px 15px;font-size:12px;font-weight:600}
  #coach .got:hover, #coachX .got:hover{background:var(--brand-ink)}

  /* ============================================================ modals */
  #modal{
    position:absolute;inset:0;z-index:20;display:none;place-items:center;
    background:rgba(23,27,34,.42);backdrop-filter:blur(3px);
  }
  #modal.on{display:grid}
  .mcard{
    width:min(460px,calc(100% - 40px));max-height:calc(100% - 40px);overflow-y:auto;
    background:var(--panel);border:1px solid var(--line);border-radius:18px;padding:24px;
    box-shadow:0 24px 60px rgba(19,30,54,.22);animation:pop .18s cubic-bezier(.2,.8,.3,1);
  }
  @keyframes pop{from{opacity:0;transform:translateY(8px) scale(.98)}to{opacity:1;transform:none}}
  @media (prefers-reduced-motion:reduce){ .mcard{animation:none} }
  .mcard .eyebrow{font:10px/1 var(--mono);letter-spacing:.16em;text-transform:uppercase;
    color:var(--brand);margin-bottom:10px;font-weight:600}
  .mcard h3{margin:0 0 10px;font-size:20px;font-weight:650;color:var(--text);letter-spacing:-.015em}
  .mcard p{margin:0 0 12px;font-size:13.5px;line-height:1.6;color:var(--text)}
  .mcard p.sub{color:var(--dim);font-size:12.5px}
  .mcard label{font-size:12.5px}
  .mfig{background:var(--panel-2);border:1px solid var(--line);border-radius:10px;
    padding:12px;margin:0 0 16px}
  .mfig svg{display:block;width:100%;height:auto}
  .mact{display:flex;gap:8px;margin-top:4px}
  .mact .btn{flex:1;padding:11px;font-size:13px}
  .mact .btn.primary{background:var(--brand);border-color:var(--brand);color:#fff;font-weight:600}
  .mact .btn.primary:hover{background:var(--brand-ink);border-color:var(--brand-ink);color:#fff}

  /* ============================================================ the project
     Which version is open, and what else exists. Rows are two lines -- a name
     and its details -- with the controls on the right of the first, so the
     list stays readable in a 272 px column. */
  #projsec h2 span{font-weight:600;letter-spacing:0;text-transform:none;color:var(--brand-ink);
    background:var(--brand-soft);border-radius:999px;padding:3px 9px;font-size:10.5px}
  #verList{margin-top:10px}
  .verhead{font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--dim);
    margin:12px 0 6px;font-weight:700}
  .verhead:first-child{margin-top:0}
  .verrow,.projrow{display:grid;grid-template-columns:1fr auto auto;gap:3px 6px;align-items:center;
    padding:7px 0;border-top:1px solid var(--line-2)}
  .vername{grid-column:1;font-size:12.5px;color:var(--text)}
  .vermeta{grid-column:1;grid-row:2;font-size:10.5px;color:var(--dim)}
  .btn.tiny{padding:4px 8px;font-size:11px;text-decoration:none;display:inline-block}
  .btn.tiny[disabled]{opacity:.45;cursor:not-allowed}
  .btn.tiny[disabled]:hover{border-color:var(--line);background:var(--panel);color:var(--text)}
  #verList .hintbox{margin-top:10px}

  /* What dismisses the phone drawer, and nothing at all anywhere else: it is
     `display:none` until `body.panel-open`, and the only control that sets
     that class is `display:none` outside the phone block at the bottom. */
  #scrim{
    position:fixed;inset:0;z-index:39;display:none;
    background:rgba(23,27,34,.42);backdrop-filter:blur(2px);
  }
  /* The drawer's head and the drawer's door. Both belong to a panel that is a
     COLUMN at this width, so both are off until the phone block turns them on
     -- `#bPanel` inherits `display:inline-flex` from `button.plain` and was
     sitting in the desktop bar beside «Отменить», offering to open a panel
     that was already open. */
  #sidehead,#bPanel{display:none}

  /* The editor never came up. A classic script in the head collects the error
     and appends this; with the page a grid, an unstyled child would land in a
     column somewhere. */
  #bootfail{
    position:fixed;z-index:99;left:50%;top:24px;transform:translateX(-50%);
    max-width:min(560px,calc(100vw - 32px));background:var(--warn-soft);
    border:1px solid var(--warn-line);color:#7A5410;border-radius:var(--r-card);
    padding:14px 18px;font:13px/1.5 var(--sans);box-shadow:var(--shadow-card);
  }

  /* ============================================================ narrow
     The editor is a desktop tool and stays one. These give the canvas back
     the pixels the chrome would otherwise take on a small laptop; nothing is
     hidden that cannot be reached another way. */
  @media (max-width:1180px){
    body{grid-template-columns:60px 248px 1fr}
    #head{padding:8px 12px;min-height:52px;gap:9px}
    #ebar{padding:8px 12px}
  }
  @media (max-width:900px){
    #brandname{display:none}
    #headactions{gap:6px}
    #etitle{font-size:14px}
    #bHome span{display:none}
  }

  /* ============================================================ phone
     The editor is a desktop tool and stays one. It is also, on a phone, three
     columns of 60 + 272 + 1fr inside a body that says `overflow:hidden` -- so
     on a 390 px screen the drawing was off the right-hand edge with no way to
     scroll to it, and what was on screen was a settings panel for a plan
     nobody could see. That is not "cramped", it is the product missing.

     So the same three pieces are REARRANGED rather than removed. The rail
     lies along the bottom where a thumb is; the side panel leaves the flow and
     becomes a drawer over the drawing; the stage takes everything in between.
     Every control the desktop has is still on the page and still reachable,
     every id the modules look up is where it was, and nothing below this
     comment changes a single pixel above the breakpoint.

     The gestures are the other half and are not here: pinch to zoom, two
     fingers to pan and press-and-hold for the actions menu are in
     js/pointer.js, because a phone has no wheel and no right button.

     TWO queries, one block. The second is a phone in LANDSCAPE -- 844×390 is
     wide enough to keep the desktop columns and far too short for two rows of
     chrome plus a canvas, and it is exactly the shape somebody turns their
     phone into to trace a wide plan. Capped at 1024 so a small laptop window
     keeps its columns. */
  @media (max-width:760px), (max-height:560px) and (max-width:1024px){
    /* Four rows, one column. `100dvh` and not `100%`: a phone browser's
       toolbar slides away, and a 100vh app shell keeps the rail under it. */
    body{
      grid-template-columns:1fr;
      grid-template-rows:auto auto 1fr auto;
      grid-template-areas:
        "head"
        "ebar"
        "stage"
        "rail";
      height:100dvh;
    }

    /* ---- the two bars, down to what fits across 390 px ---------------- */
    #head{padding:7px 10px;min-height:46px;gap:8px}
    #head .cta{padding:8px 13px;font-size:12.5px}
    #ebar{padding:6px 8px;gap:6px;min-height:46px}
    #ebar .edash{display:none}
    /* The page's title. The tab says it, the header above says whose product
       it is, and the screen is a canvas with tools on it -- this is the line
       a phone can spare.

       The project's NAME is not that line and stays: it is the one thing in
       this bar that says which flat is on screen, and «Переименовать» in the
       panel is a drawer away. It gets a phone's share of the width. */
    #etitle{display:none}
    .ename{max-width:42vw;font-size:13.5px;padding:4px 6px;margin:0 -6px;gap:5px}
    #bUndo span,#bRedo span,#bPanel span{display:none}
    #bUndo,#bRedo,#bPanel{padding:8px 10px}
    #bReady{padding:9px 14px;font-size:13px;gap:7px}
    /* The drawer's own door. A button and not an edge swipe: swiping in from
       the edge is the browser's own gesture on both platforms, and a control
       that fights the back gesture is a control that loses. */
    #bPanel{display:inline-flex}

    /* ---- the rail, along the bottom ------------------------------------
       Twelve tools do not fit across a phone at a size a thumb can hit, so it
       SCROLLS sideways rather than shrinking them: a 26 px target is one
       nobody can press, and hiding four tools would take the door, the window
       and the opening off a plan-tracing tool. */
    #rail{
      flex-direction:row;justify-content:flex-start;
      border-right:none;border-top:1px solid var(--line);
      padding:5px 8px calc(5px + env(safe-area-inset-bottom));
      gap:3px;overflow-x:auto;overflow-y:hidden;
      scrollbar-width:none;-webkit-overflow-scrolling:touch;
    }
    #rail::-webkit-scrollbar{display:none}
    #rail .tool{flex:none;width:44px;height:44px}
    #rail .sep{width:1px;height:26px;margin:0 5px;flex:none;align-self:center}

    /* ---- the side panel, as a drawer -----------------------------------
       Out of the grid and over the drawing, shut by default. Same panel, same
       sections, same controls, same ids -- `position:fixed` means the grid
       area it names is simply never used at this width. */
    #side{
      position:fixed;z-index:40;top:0;right:0;bottom:0;
      width:min(330px,88vw);padding:0 14px 40px;
      border-right:none;border-left:1px solid var(--line);
      box-shadow:var(--shadow-pop);overscroll-behavior:contain;
      transform:translateX(101%);transition:transform .2s ease;
    }
    body.panel-open #side{transform:none}
    body.panel-open #scrim{display:block}
    /* Sticky, so the way out is on screen however far down the panel you are
       -- «Хранение» is a long scroll from the top. */
    #sidehead{
      display:flex;align-items:center;justify-content:space-between;gap:10px;
      position:sticky;top:0;z-index:1;background:var(--panel);
      padding:13px 0 11px;border-bottom:1px solid var(--line);
      font-size:13px;font-weight:650;
    }

    /* A modal is raised OVER the drawer rather than the drawer being shut for
       it. «Показать инструкцию», «Очистить геометрию» and the picture chooser
       all live in the panel and all answer with a modal, and a modal that
       opens BEHIND the thing that opened it reads as a dead button. #modal
       sits inside #stage and #stage makes no stacking context, so one number
       is the whole fix. */
    #modal{z-index:50}

    /* ---- the strip over the drawing ------------------------------------
       It wraps on a desktop, which on a phone means four rows of chrome lying
       across the plan. Here it is ONE row that scrolls: every readout and
       every toggle is still reachable and the drawing keeps its pixels. */
    #topbar{
      flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;
      min-height:34px;padding:4px 8px;gap:6px;
      scrollbar-width:none;-webkit-overflow-scrolling:touch;
    }
    #topbar::-webkit-scrollbar{display:none}
    #topbar .sp{display:none}
    /* Where the pointer is, in millimetres. There is no pointer. */
    #tCur{display:none}

    /* The running instruction for the current tool. Two lines by default and
       all of it on a tap -- it was covering a third of the drawing, and it is
       the one thing on screen that says what tapping does, so cutting the
       words was not the answer. */
    #hint{
      left:6px;right:6px;bottom:6px;max-width:none;padding:9px 28px 9px 11px;
      font:11.5px/1.45 var(--sans);max-height:34vh;overflow-y:auto;
    }
    /* Two lines exactly: 2 × 1.45em of text plus the padding, since
       `box-sizing:border-box` is on everything here.

       `clip` with `overflow-clip-margin:content-box`, and that pair is the
       whole of what took three attempts. `overflow:hidden` clips at the
       PADDING box, not the content box -- so the top nine pixels of the third
       line landed inside this card's own bottom padding and were painted,
       leaving half a line of text hanging under a card that had visibly
       ended. `-webkit-line-clamp` does the same thing and adds an ellipsis to
       it. A browser without `overflow-clip-margin` falls back to the previous
       declaration and gets that sliver back, which is cosmetic; nothing about
       the fold depends on it. */
    #hint.mini{max-height:calc(2.9em + 18px);overflow:hidden;
      overflow:clip;overflow-clip-margin:content-box}
    /* Which way the tap goes. Always there, because the box toggles whether
       or not there is anything hidden, and a control that only announces
       itself when it has news is one nobody learns. */
    #hint:after{
      content:'▾';position:absolute;right:10px;bottom:7px;
      color:var(--brand);font-size:12px;line-height:1;
    }
    #hint:not(.mini):after{content:'▴'}

    /* The action bar over a selection, which is built from that selection's
       own controls and comes out three times a phone's width. */
    #fab{max-width:calc(100vw - 12px);flex-wrap:wrap;align-content:flex-start;gap:4px 5px}
    #ctx{max-width:calc(100vw - 24px)}

    /* The blocking x-ray tip points at a rail button that is now BELOW it, so
       its left-edge arrow would aim at nothing. js/ui/coach.js puts the card
       above a horizontal rail and leaves the horizontal placement to this. */
    #coachTool{left:8px;right:8px;width:auto;max-width:none}
    #coachTool:before{display:none}
  }
  @media (prefers-reduced-motion:reduce){ #side{transition:none} }

  /* A PORTRAIT phone only, and the one rule that is about width alone rather
     than about the phone layout: the action bar wraps at any of these sizes,
     but only here is it so much narrower than the bar that the wrap lands
     between a label and the field it names. So the DIVIDERS become the breaks.
     `.fsep` already separates the groups the author of each bar meant --
     «масштаб по», thickness, the openings, the delete -- so one group per line
     is the bar's own structure rather than a new one invented for it.

     Left out of the block above deliberately: a phone on its side is 844 px
     wide, where forcing a line per group would make a three-row bar out of one
     that fits across the top in two. */
  @media (max-width:560px){
    #fab .fsep{flex:0 0 100%;width:auto;height:1px;background:var(--line-2);margin:3px 0}
    /* And the fields stop growing into the space the breaks just made. Every
       number field in here inherits `flex:1` from the panel's own input rule,
       which is invisible while the bar is one packed row and turns a 52 px
       thickness box into half the screen the moment that row holds three
       things. */
    #fab input{flex:0 0 auto}
  }
