/* One theme, light: an off-white ground, white cards, near-black ink. There is
   no toggle and nothing to switch to. */
:root{
    /* The ground is off-white, cards are true white. That difference is what
       makes a card read as lifted; on a pure white ground every surface is the
       same colour and the layout goes flat. */
    --paper:#F6F5F2; --paper-raised:#FFFFFF; --paper-sunk:#EDECE8;
    /* Near-black rather than black: #000 on white is the highest contrast the
       screen can make and it vibrates. */
    --ink:#191A18; --ink-soft:#5D605C; --ink-faint:#8C8F8A;
    --hair:#E3E1DB; --hair-strong:#C9C7C0;
    /* Buttons are background:var(--ink) color:var(--paper) throughout, so the
       whole system inverts with these two values and nothing else. */
    --accent:#191A18; --accent-deep:#000000; --accent-tint:#E3E1DB;
    --brick:#B3261E; --brick-tint:#FBE9E7; --ochre:#5D605C;
    --gold:#8A6116; --gold-tint:#FBF1DC;
    --brand-font:var(--sans);
    --sans:'IBM Plex Sans','IBM Plex Sans Thai',-apple-system,'Segoe UI',sans-serif;
    --mono:'IBM Plex Mono',Consolas,monospace;
    /* The ground a render is judged against. Kept as its own value rather than
       reusing --ink, which names the colour of text and only happened to be
       dark while the sheet was white. */
    --canvas:#E9E7E2;
  }

  *{box-sizing:border-box;}
  html,body{margin:0;padding:0;}
  body{
    font-family:var(--sans);color:var(--ink);
    background:
      repeating-linear-gradient(0deg, transparent, transparent 27px, #0000000d 27px, #0000000d 28px),
      repeating-linear-gradient(90deg, transparent, transparent 27px, #0000000d 27px, #0000000d 28px),
      var(--paper);
  }
  .view{display:none !important;}
  .view.active{display:block !important;}
  #view-app.active{display:flex !important;flex-direction:column;}
  #view-app{height:100vh;overflow:hidden;}
  #view-login.active{display:flex !important;align-items:center;justify-content:center;}
  #view-login{min-height:100vh;padding:24px;}


  /* ===================== VIEW: HOME ===================== */
#view-home{margin:0;background:var(--paper);color:var(--ink);font-family:var(--sans);
    background-image:
      repeating-linear-gradient(0deg, transparent, transparent 27px, #0000000d 27px, #0000000d 28px),
      repeating-linear-gradient(90deg, transparent, transparent 27px, #0000000d 27px, #0000000d 28px);
  }
#view-home header{display:flex;align-items:center;justify-content:space-between;gap:20px;margin-bottom:24px;flex-wrap:wrap;padding-bottom:16px;border-bottom:1px solid var(--hair);}
#view-home .brand{display:flex;align-items:center;gap:10px;}
#view-home h1.logo{font-family:var(--brand-font);font-size:16px;font-weight:400;letter-spacing:.5px;margin:0;text-transform:uppercase;}
#view-home nav a{color:var(--ink-soft);text-decoration:none;font-size:13px;margin-left:24px;}
#view-home nav a:hover{color:var(--ink);}
#view-home footer{text-align:center;margin-top:60px;font-size:12px;color:var(--ink-faint);}
#view-home footer a,#view-gallery footer a{color:inherit;}

  /* ===================== VIEW: APP ===================== */
#view-app,#view-app{margin:0;padding:0;height:100%;overflow:hidden;}
#view-app{
    background:
      repeating-linear-gradient(0deg, transparent, transparent 27px, #0000000d 27px, #0000000d 28px),
      repeating-linear-gradient(90deg, transparent, transparent 27px, #0000000d 27px, #0000000d 28px),
      var(--paper);
    color:var(--ink);
    font-family:var(--sans);
  }
#view-app .wrap{width:100%;margin:0 auto;padding:24px 48px 16px;height:100vh;display:flex;flex-direction:column;box-sizing:border-box;}
#view-app /* ---------- header ---------- */
  header{display:flex;align-items:center;justify-content:space-between;gap:20px;margin-bottom:24px;flex-wrap:wrap;flex-shrink:0;padding-bottom:16px;border-bottom:1px solid var(--hair-strong);position:relative;z-index:20;}
#view-app .brand{display:flex;align-items:center;gap:10px;}
#view-app .brand svg{flex-shrink:0;display:block;}
#view-app h1{font-family:var(--brand-font);font-size:16px;font-weight:400;letter-spacing:.5px;margin:0;line-height:1;text-transform:uppercase;}
/* Reads its value from app.js's own ?v= query string, so what it shows is the
   file the browser actually executed — not a number typed in beside it that
   could drift. Selectable so the version can be copied into a bug report. */
#view-app .appver{font-family:var(--mono);font-size:11px;letter-spacing:.3px;color:var(--ink-faint);padding:2px 7px;border:1px solid var(--line);border-radius:999px;white-space:nowrap;user-select:all;}
#view-app .appver.stale{color:var(--brick);border-color:var(--brick);}
#view-app /* ---------- nav / account cluster ---------- */
  .nav-right{display:flex;align-items:center;gap:12px;}
#view-app .token-chip{display:flex;align-items:center;gap:6px;background:var(--paper-raised);border:1px solid var(--hair-strong);border-radius:20px;padding:6px 12px 6px 8px;font-family:var(--mono);font-size:11.5px;color:var(--ink);}
#view-app .token-chip svg{flex-shrink:0;}
#view-app .acct-wrap{position:relative;}
#view-app .tut-btn{display:flex;align-items:center;gap:7px;background:var(--paper-raised);border:1px solid var(--hair-strong);border-radius:20px;padding:6px 13px 6px 10px;cursor:pointer;font-family:var(--sans);font-size:12.5px;color:var(--ink);}
#view-app .tut-btn:hover{border-color:var(--ink-soft);}
#view-app .acct-btn{display:flex;align-items:center;gap:8px;background:var(--paper-raised);border:1px solid var(--hair-strong);border-radius:20px;padding:5px 12px 5px 5px;cursor:pointer;font-family:var(--sans);font-size:12.5px;color:var(--ink);}
#view-app .acct-btn:hover{border-color:var(--ink-soft);}
#view-app .acct-avatar{width:24px;height:24px;border-radius:50%;background:var(--accent);color:var(--paper-raised);display:flex;align-items:center;justify-content:center;font-family:var(--mono);font-size:10.5px;font-weight:600;flex-shrink:0;}
#view-app .acct-panel{
    position:absolute;top:calc(100% + 10px);right:0;width:280px;background:var(--paper-raised);
    border:1px solid var(--hair-strong);border-radius:3px;padding:16px;display:none;
    box-shadow:0 8px 24px #00000022;z-index:30;
  }
#view-app .acct-panel.open{display:block;}
#view-app .acct-panel h3{margin:0 0 10px;font-family:var(--mono);font-size:10.5px;letter-spacing:1.2px;text-transform:uppercase;color:var(--ink-soft);}
#view-app .acct-panel .btn-ghost-block{width:100%;padding:8px;border:1px solid var(--hair-strong);border-radius:2px;background:transparent;color:var(--ink-soft);font-size:12px;cursor:pointer;font-family:var(--mono);text-transform:uppercase;letter-spacing:.3px;}
#view-app .acct-panel .btn-ghost-block:hover{color:var(--ink);border-color:var(--ink-soft);}
#view-app .acct-panel .divider{height:1px;background:var(--hair);margin:12px 0;}
#view-app .acct-panel .user-line{font-size:13px;color:var(--ink);margin-bottom:2px;}
#view-app .acct-panel .plan-line{font-family:var(--mono);font-size:10.5px;color:var(--ink-soft);text-transform:uppercase;}
/* Flat sections, 2026-09-17: nothing in the settings panel folds. A fold saves
   vertical space by hiding the thing you came to change -- Output size spent
   a week two clicks down one, and peetz reported it as missing. A heading
   costs one line and never hides. */
#view-app .optsec{border-top:1px solid var(--hair);margin-top:16px;padding-top:11px;}
#view-app .optsec-head{
    margin:0 0 13px;font-family:var(--mono);font-size:10px;letter-spacing:1.2px;
    text-transform:uppercase;color:var(--ink-soft);font-weight:400;
  }
#view-app .optsec > label.field:last-child,
#view-app .optsec-body > label.field:last-child,
#view-app .optsec-body > .row2:last-child label.field{margin-bottom:0;}

#view-app .grid{display:grid;grid-template-columns:auto 330px 1fr;gap:22px;flex:1;min-height:0;}
@media (max-width:900px){#view-app .grid{grid-template-columns:1fr;overflow-y:auto;}}

/* The panel scrolls; its foot does not. Before this the whole column scrolled
   and the Run button went with it — worst on the longest parameter list, which
   is exactly when it is hardest to find. */
#view-app .panel{display:flex;flex-direction:column;min-height:0;}
#view-app .panel-scroll{overflow-y:auto;padding-right:6px;min-height:0;flex:1;}
#view-app .panel-scroll::-webkit-scrollbar{width:5px;}
#view-app .panel-scroll::-webkit-scrollbar-thumb{background:var(--hair-strong);border-radius:3px;}
#view-app .panel-foot{padding-top:12px;}
#view-app .panel-foot .run{margin:0;}

/* The rail. Icon over label, because a label alone at this width truncates and
   an icon alone is a guess. */
#view-app .rail{
    display:flex;flex-direction:column;gap:4px;
    padding-right:16px;border-right:1px solid var(--hair);
  }
#view-app .rail-b{
    display:flex;flex-direction:column;align-items:center;gap:4px;
    width:58px;padding:10px 4px;border:1px solid transparent;border-radius:6px;
    background:transparent;color:var(--ink-faint);cursor:pointer;
    font-family:var(--mono);font-size:8.5px;letter-spacing:.08em;text-transform:uppercase;
    transition:color .12s,background .12s,border-color .12s;
  }
#view-app .rail-b:hover{color:var(--ink-soft);background:var(--paper-raised);}
#view-app .rail-b.active{color:var(--ink);background:var(--paper-raised);border-color:var(--hair-strong);}
#view-app .rail-b:focus-visible{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-tint);}
@media (max-width:900px){
  /* align-items:flex-start or the lit button stretches to the row height. */
  #view-app .rail{flex-direction:row;align-items:flex-start;border-right:0;border-bottom:1px solid var(--hair);padding:0 0 12px;margin-bottom:12px;}
}

/* Work already made. */
#view-app .vlist{display:grid;gap:8px;}
#view-app .vitem{
    display:flex;gap:10px;align-items:center;width:100%;text-align:left;
    padding:7px;border:1px solid var(--hair);border-radius:6px;
    background:transparent;color:inherit;cursor:pointer;
  }
#view-app .vitem:hover{border-color:var(--hair-strong);background:var(--paper-raised);}
#view-app .vitem.on{border-color:var(--accent);}
#view-app .vitem img,#view-app .vitem video{
    width:64px;height:44px;object-fit:cover;border-radius:4px;background:var(--canvas);flex-shrink:0;
  }
#view-app .vitem .vmeta{min-width:0;}
#view-app .vitem .vwf{font-size:12px;font-weight:600;}
#view-app .vitem .vwhen{font-family:var(--mono);font-size:9.5px;color:var(--ink-faint);margin-top:2px;}
#view-app .vnote{font-size:12px;color:var(--ink-faint);padding:10px 2px 2px;}
#view-app /* ---------- plate card (registration marks) ---------- */
  .card{
    position:relative;
    background:var(--paper-raised);
    border:1px solid var(--hair);
    border-radius:3px;
    padding:16px 20px 18px;
  }
#view-app .card + .card{margin-top:12px;}
#view-app .card::before,#view-app .card::after{
    content:"";position:absolute;width:8px;height:8px;pointer-events:none;
    border-color:var(--hair-strong);
  }
#view-app .card::before{top:-1px;left:-1px;border-top:1px solid;border-left:1px solid;}
#view-app .card::after{bottom:-1px;right:-1px;border-bottom:1px solid;border-right:1px solid;}
#view-app .card h2{
    font-size:11px;text-transform:uppercase;letter-spacing:1.4px;
    color:var(--ink);margin:0;font-weight:600;font-family:var(--mono);
  }
#view-app .card-head{display:flex;align-items:baseline;justify-content:space-between;margin-bottom:12px;gap:10px;}
#view-app .plate-no{font-family:var(--mono);font-size:10px;color:var(--ink-faint);letter-spacing:1px;white-space:nowrap;}
#view-app label.field{display:block;margin-bottom:10px;}
#view-app label.field span{display:block;font-family:var(--mono);font-size:11px;letter-spacing:.2px;color:var(--ink-soft);margin-bottom:6px;}
/* A hint reads the same whether it is a <span> inside a field or a <div>
   standing on its own. Four of them are divs -- including the one under Output
   size that tells you what your image measures -- and they were rendering as
   unstyled body text. Nobody saw it while they sat inside a collapsed group. */
#view-app .fieldhint{
    font-family:var(--sans);font-size:11.5px;line-height:1.45;
    color:var(--ink-faint);letter-spacing:0;margin:6px 0 0;
  }
/* Sits under a control rather than above it, so it needs the margin flipped.
   Must out-specify the rule directly above, hence label.field span.fieldhint. */
#view-app label.field span.fieldhint{
    font-family:var(--sans);font-size:11.5px;line-height:1.45;
    color:var(--ink-faint);letter-spacing:0;margin:6px 0 0;
}
#view-app input[type=text],#view-app input[type=number],#view-app select,#view-app textarea{
    width:100%;background:var(--paper);border:1px solid var(--hair-strong);
    color:var(--ink);padding:9px 10px;border-radius:2px;font-size:13.5px;
    font-family:var(--sans);
  }
#view-app textarea{resize:vertical;min-height:60px;line-height:1.5;font-size:12.8px;}
#view-app input:focus,#view-app select:focus,#view-app textarea:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-tint);}
#view-app .row2{display:grid;grid-template-columns:1fr 1fr;gap:12px;}
#view-app .drop{
    border:1px dashed var(--hair-strong);border-radius:2px;padding:22px;text-align:center;
    cursor:pointer;transition:border-color .15s,background .15s;position:relative;
  }
#view-app .drop:hover,#view-app .drop.drag{border-color:var(--accent);background:var(--accent-tint);}
#view-app .drop input{display:none;}
#view-app .drop .hint{font-size:12.5px;color:var(--ink-soft);font-family:var(--sans);}
#view-app .drop .hint b{color:var(--ink);}
#view-app .preview{margin-top:12px;border-radius:2px;overflow:hidden;border:1px solid var(--hair-strong);display:none;}
#view-app .preview img{display:block;width:100%;max-height:260px;object-fit:contain;background:var(--canvas);}
/* Frame (2026-10-06): the whole upload with the kept part lit and the rest
   dimmed. The stage is exactly the picture's box, so the frame's percentages
   land on the picture. Dragging anywhere on it slides the frame. */
#view-app .preview .frame-stage{position:relative;width:fit-content;max-width:100%;margin:0 auto;overflow:hidden;cursor:grab;touch-action:none;user-select:none;}
#view-app .preview .frame-stage.dragging{cursor:grabbing;}
#view-app .preview .frame-stage img{width:auto;max-width:100%;object-fit:fill;}
#view-app .frame-box{position:absolute;outline:1.5px solid #fff;box-shadow:0 0 0 9999px rgba(12,12,12,.55);pointer-events:none;}
#view-app .frame-field{margin-top:12px;}
/* The grip in the middle of the frame says it moves, and which way (2026-10-06,
   peetz could not tell the frame could be dragged). It nudges twice on show. */
#view-app .frame-grip{position:absolute;left:50%;top:50%;width:36px;height:36px;margin:-18px 0 0 -18px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;background:rgba(12,12,12,.62);color:#fff;box-shadow:0 0 0 1px rgba(255,255,255,.5);}
#view-app .frame-stage[data-axis="y"] .frame-grip svg{transform:rotate(90deg);}
#view-app .frame-stage[data-axis=""] .frame-grip{display:none;}
@media (prefers-reduced-motion:no-preference){
  #view-app .frame-grip .nudge{animation:frameNudge 1.4s ease-in-out 3;}
}
@keyframes frameNudge{0%,100%{transform:translateX(0);}25%{transform:translateX(-3px);}75%{transform:translateX(3px);}}
#view-app .frame-field.port .chip[data-v="16:9"] svg,#view-app .frame-field.port .chip[data-v="4:3"] svg{transform:rotate(90deg);}
#view-app button.run{
    width:100%;padding:13px;border:1px solid var(--accent-deep);border-radius:2px;
    background:var(--accent);color:var(--paper-raised);font-weight:600;font-size:13.5px;
    cursor:pointer;letter-spacing:.6px;text-transform:uppercase;font-family:var(--mono);
  }
#view-app button.run:disabled{background:var(--paper-sunk);color:var(--ink-faint);border-color:var(--hair);cursor:not-allowed;}
#view-app button.run:hover:not(:disabled){background:var(--accent-deep);}
#view-app button.ghost{
    background:transparent;border:1px solid var(--hair-strong);color:var(--ink-soft);
    padding:7px 10px;border-radius:2px;font-size:11.5px;cursor:pointer;font-family:var(--mono);letter-spacing:.3px;text-transform:uppercase;
  }
#view-app button.ghost:hover{color:var(--ink);border-color:var(--ink-soft);}
#view-app .status{
    font-family:var(--mono);font-size:12px;color:var(--ink-soft);
    background:var(--paper);border:1px solid var(--hair-strong);border-radius:2px;
    padding:12px 14px;height:120px;overflow-y:auto;white-space:pre-wrap;line-height:1.6;
  }
#view-app .status .ok{color:var(--accent-deep);font-weight:500;}
#view-app .status .err{color:var(--brick);font-weight:500;}
#view-app .result-area{min-height:0;height:100%;display:flex;flex-direction:column;}
#view-app .card.result-area{padding:16px 18px;}
#view-app .result-head{display:flex;align-items:baseline;justify-content:space-between;margin-bottom:12px;}
#view-app .result-head .hint{font-family:var(--mono);font-size:10.5px;color:var(--ink-faint);text-transform:uppercase;letter-spacing:.8px;}
#view-app .cmp{
    position:relative;width:100%;flex:1;min-height:200px;
    background:var(--canvas);border:1px solid var(--hair-strong);border-radius:2px;overflow:hidden;
    user-select:none;
  }
#view-app .cmp img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;background:var(--canvas);}
#view-app .cmp .cmp-empty{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:var(--ink-faint);font-size:12.5px;font-family:var(--mono);text-align:center;padding:20px;letter-spacing:.3px;}
#view-app .cmp .cmp-before-wrap{position:absolute;inset:0;width:100%;overflow:hidden;pointer-events:none;}
#view-app .cmp .cmp-before-wrap img{width:100%;height:100%;}
#view-app .cmp-label{position:absolute;top:10px;font-size:9.5px;text-transform:uppercase;letter-spacing:1.4px;background:#00000099;padding:4px 9px;border-radius:2px;color:#FFFFFF;font-family:var(--mono);z-index:3;pointer-events:none;}
#view-app .cmp-label.before{left:10px;}
#view-app .cmp-label.after{right:10px;}
#view-app .cmp-line{position:absolute;top:0;bottom:0;width:1px;background:var(--accent);pointer-events:none;z-index:2;left:50%;
    background-image:repeating-linear-gradient(180deg, var(--accent) 0 5px, transparent 5px 9px);}
#view-app .cmp-dot{position:absolute;top:50%;width:38px;height:22px;border-radius:3px;background:var(--accent);left:50%;
    transform:translate(-50%,-50%);display:flex;align-items:center;justify-content:center;pointer-events:none;z-index:3;box-shadow:0 2px 8px #00000055;}
#view-app .cmp-dot svg{width:20px;height:11px;}
#view-app .cmp-range{position:absolute;inset:0;width:100%;height:100%;margin:0;opacity:0;cursor:ew-resize;z-index:4;}
#view-app .actions-bottom{display:flex;gap:10px;margin-top:14px;align-items:center;}
/* The two continue-the-chain buttons sit together on the right, opposite the
   downloads, in the order the work actually flows: sketch on it, then upscale it. */
#view-app button.dl:disabled{opacity:.45;cursor:default;}
#view-app a.dl,
#view-app button.dl{
    display:inline-flex;align-items:center;gap:7px;background:var(--accent);color:var(--paper-raised);
    text-decoration:none;padding:9px 16px;border-radius:2px;font-size:12px;font-weight:600;font-family:var(--mono);letter-spacing:.3px;text-transform:uppercase;
  }
#view-app a.dl:hover,
#view-app button.dl:hover{background:var(--accent-deep);}
#view-app a.dl.secondary,
#view-app button.dl.secondary{background:transparent;color:var(--ink);border:1px solid var(--hair-strong);}
#view-app a.dl.secondary:hover,
#view-app button.dl.secondary:hover{border-color:var(--ink-soft);}
#view-app .seedrow{display:flex;gap:8px;}
#view-app .seedrow input{flex:1;}

  /* ---------- SSS Sketch to Add ----------
     The stage tabs and the drawing surface live in the right-hand column
     because the left one is a fixed 330px and a mask drawn at that width is
     too coarse to place anything accurately. */
#view-app .stage-tabs{display:flex;border:1px solid var(--hair-strong);border-radius:2px;overflow:hidden;}
#view-app .stage-tabs button{
    background:transparent;border:0;padding:5px 13px;cursor:pointer;
    font-family:var(--mono);font-size:10.5px;text-transform:uppercase;letter-spacing:.8px;color:var(--ink-soft);
  }
#view-app .stage-tabs button + button{border-left:1px solid var(--hair-strong);}
#view-app .stage-tabs button:hover{color:var(--ink);}
#view-app .stage-tabs button.active{background:var(--accent);color:var(--paper-raised);}
#view-app .sketch-stage{flex:1;min-height:0;display:flex;flex-direction:column;gap:10px;}
#view-app .sk-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:6px;}
#view-app .sk-chip{
    display:inline-flex;align-items:center;gap:7px;
    background:var(--paper);border:1px solid var(--hair-strong);border-radius:2px;
    padding:5px 10px;cursor:pointer;font-family:var(--sans);font-size:12px;color:var(--ink-soft);
  }
#view-app .sk-chip .sw{width:9px;height:9px;border-radius:50%;flex-shrink:0;}
#view-app .sk-chip .n{font-family:var(--mono);font-size:10px;color:var(--paper-raised);background:var(--ink-faint);
    border-radius:20px;padding:0 5px;line-height:14px;min-width:14px;text-align:center;}
#view-app .sk-chip:hover{border-color:var(--ink-soft);}
#view-app .sk-chip.on{border-color:var(--accent);background:var(--accent-tint);color:var(--ink);font-weight:600;}
#view-app .sk-chip.on .n{background:var(--accent);}
#view-app .sk-tools{display:flex;align-items:center;gap:12px;margin-left:auto;}
/* The number is the point of the control, so it is set in a fixed-width slot
   and does not shuffle the row as it changes. */
/* Both readouts are fixed-width slots. They are inside a wrapping toolbar, and
   the toolbar sits above a stage that refits its canvas whenever its height
   changes — so a label that grows by a few characters can rewrap the row,
   shorten the stage, refit the canvas and flash the whole picture. Dragging
   the brush did exactly that, once per step. Nothing here may change width. */
#view-app .sk-stagewrap{
    position:relative;flex:1;min-height:220px;
    background:var(--canvas);border:1px solid var(--hair-strong);border-radius:2px;overflow:hidden;
    display:flex;align-items:center;justify-content:center;
  }
#view-app .sk-stagewrap img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;}
#view-app .sk-stagewrap canvas{position:absolute;cursor:none;touch-action:none;}
#view-app .sk-empty{color:var(--ink-faint);font-size:12.5px;font-family:var(--mono);text-align:center;padding:20px;letter-spacing:.3px;}
#view-app .sk-note{font-size:11.5px;color:var(--ink-soft);line-height:1.65;
    border-left:2px solid var(--hair-strong);padding-left:10px;margin-bottom:14px;}

  /* ===================== VIEW: LOGIN ===================== */
#view-login{margin:0;min-height:100vh;background:
      repeating-linear-gradient(0deg, transparent, transparent 27px, #0000000d 27px, #0000000d 28px),
      repeating-linear-gradient(90deg, transparent, transparent 27px, #0000000d 27px, #0000000d 28px),
      var(--paper);
    color:var(--ink);font-family:var(--sans);display:flex;align-items:center;justify-content:center;padding:24px;}
#view-login .login-card{width:100%;max-width:380px;background:var(--paper-raised);border:1px solid var(--hair-strong);border-radius:4px;padding:34px 30px 28px;position:relative;}
#view-login .login-card::before,#view-login .login-card::after{content:"";position:absolute;width:8px;height:8px;border-color:var(--hair-strong);}
#view-login .login-card::before{top:-1px;left:-1px;border-top:1px solid;border-left:1px solid;}
#view-login .login-card::after{bottom:-1px;right:-1px;border-bottom:1px solid;border-right:1px solid;}
#view-login .login-card .brand{display:flex;flex-direction:column;align-items:center;gap:10px;margin-bottom:26px;}
#view-login .login-card .brand svg{display:block;}
#view-login .login-name{font-family:var(--brand-font);font-size:18px;font-weight:400;letter-spacing:.5px;margin:0;text-transform:uppercase;}
#view-login .sub{font-family:var(--mono);font-size:11px;color:var(--ink-soft);letter-spacing:.5px;margin-top:2px;}
#view-login h2{font-size:16px;font-weight:600;text-align:center;margin:0 0 22px;}
#view-login .g-btn{width:100%;display:flex;align-items:center;justify-content:center;gap:10px;background:#FFFFFF;border:1px solid var(--hair-strong);border-radius:3px;padding:11px;font-size:13.5px;font-weight:500;color:#171717;cursor:pointer;font-family:var(--sans);}
#view-login .g-btn:hover{border-color:var(--ink-soft);background:var(--paper-sunk);}
#view-login .g-btn:disabled{opacity:.6;cursor:default;}
#view-login .divider{display:flex;align-items:center;gap:10px;margin:20px 0;color:var(--ink-faint);font-size:11px;font-family:var(--mono);text-transform:uppercase;}
#view-login .divider::before,#view-login .divider::after{content:"";flex:1;height:1px;background:var(--hair);}
#view-login .lfield{display:block;margin-bottom:12px;}
#view-login .lfield span{display:block;font-family:var(--mono);font-size:11px;color:var(--ink-soft);margin-bottom:6px;}
#view-login .lfield input{width:100%;background:var(--paper);border:1px solid var(--hair-strong);color:var(--ink);padding:10px 11px;border-radius:2px;font-size:13.5px;font-family:var(--sans);}
#view-login .lfield input:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-tint);}
#view-login .primary-btn{width:100%;padding:11px;border:1px solid var(--accent-deep);border-radius:2px;background:var(--accent);color:var(--paper);font-weight:600;font-size:13px;cursor:pointer;letter-spacing:.4px;text-transform:uppercase;font-family:var(--mono);margin-top:4px;}
#view-login .primary-btn:hover{background:var(--accent-deep);}
#view-login .foot{text-align:center;margin-top:20px;font-size:12px;color:var(--ink-soft);}
#view-login .legal-foot{font-size:11px;color:var(--ink-faint);line-height:1.5;}
#view-login .legal-foot a{color:var(--ink-soft);}
#view-login .foot a{color:var(--ink);text-decoration:none;border-bottom:1px solid var(--hair-strong);}
#view-login .status{display:none;text-align:center;font-family:var(--mono);font-size:12px;color:var(--ink-soft);margin-top:16px;}
#view-login .status.show{display:block;}
#view-login .spin{display:inline-block;width:12px;height:12px;border:2px solid var(--hair-strong);border-top-color:var(--ink);border-radius:50%;animation:login_sp .7s linear infinite;vertical-align:-2px;margin-right:6px;}
@keyframes login_sp{to{transform:rotate(360deg);}}
#view-login .success{display:none;text-align:center;}
#view-login .success.show{display:block;}
#view-login .success .check{width:44px;height:44px;border-radius:50%;background:var(--accent);display:flex;align-items:center;justify-content:center;margin:0 auto 14px;}
#view-login .success p{font-size:13px;color:var(--ink-soft);margin:6px 0 18px;}
#view-login .success a.btn{display:inline-block;background:var(--accent);color:var(--paper);padding:10px 20px;border-radius:2px;text-decoration:none;font-size:13px;font-weight:600;font-family:var(--mono);letter-spacing:.4px;text-transform:uppercase;}
#view-login #loginForm.hide,#view-login .g-btn.hide,#view-login .divider.hide{display:none;}

  /* ===================== VIEW: UPGRADE ===================== */
#view-upgrade{margin:0;background:var(--paper);color:var(--ink);font-family:var(--sans);
    background-image:
      repeating-linear-gradient(0deg, transparent, transparent 27px, #0000000d 27px, #0000000d 28px),
      repeating-linear-gradient(90deg, transparent, transparent 27px, #0000000d 27px, #0000000d 28px);
  }
#view-upgrade .wrap{width:100%;margin:0 auto;padding:24px 48px 80px;}
#view-upgrade header{display:flex;align-items:center;justify-content:space-between;gap:20px;margin-bottom:24px;flex-wrap:wrap;padding-bottom:16px;border-bottom:1px solid var(--hair);}
#view-upgrade .brand{display:flex;align-items:center;gap:10px;}
#view-upgrade h1.logo{font-family:var(--brand-font);font-size:16px;font-weight:400;letter-spacing:.5px;margin:0;text-transform:uppercase;}
#view-upgrade nav a{color:var(--ink-soft);text-decoration:none;font-size:13px;margin-left:20px;}
#view-upgrade nav a:hover{color:var(--ink);}
#view-upgrade nav a.close{color:var(--ink);font-size:20px;line-height:1;}
#view-upgrade .hero{text-align:center;margin-bottom:8px;}
#view-upgrade .hero h2{font-size:26px;font-weight:700;margin:0 0 8px;}
#view-upgrade .hero p{color:var(--ink-soft);font-size:13.5px;margin:0;}
#view-upgrade .plans{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-top:34px;}
@media (max-width:820px){#view-upgrade .plans{grid-template-columns:1fr;}}
#view-upgrade .plan{background:var(--paper-raised);border:1px solid var(--hair-strong);border-radius:8px;padding:26px 24px;display:flex;flex-direction:column;}
#view-upgrade .plan.reco{border-color:var(--ink);box-shadow:0 0 0 1px var(--ink);position:relative;}
#view-upgrade .reco-tag{position:absolute;top:-12px;left:50%;transform:translateX(-50%);background:var(--ink);color:var(--paper);font-family:var(--mono);font-size:10px;letter-spacing:1px;padding:4px 12px;border-radius:20px;text-transform:uppercase;white-space:nowrap;}
#view-upgrade .plan .pname{font-size:15px;font-weight:700;margin-bottom:4px;}
#view-upgrade .plan .pdesc{font-size:12.5px;color:var(--ink-soft);margin-bottom:18px;min-height:32px;}
#view-upgrade .plan .pprice{font-size:30px;font-weight:700;letter-spacing:-.5px;}
#view-upgrade .plan .pprice span{font-size:13px;font-weight:400;color:var(--ink-soft);}
#view-upgrade .plan .pcredits{font-family:var(--mono);font-size:11.5px;color:var(--ink-soft);margin:4px 0 20px;}
#view-upgrade .plan ul{list-style:none;margin:0 0 22px;padding:0;flex:1;}
#view-upgrade .plan li{display:flex;gap:8px;align-items:flex-start;font-size:13px;margin-bottom:11px;}
#view-upgrade .plan li svg{flex-shrink:0;margin-top:2px;}
#view-upgrade .plan li.pnote{color:var(--ink-faint);}
#view-upgrade .plan button{width:100%;padding:11px;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;font-family:var(--sans);}
#view-upgrade .plan .btn-primary{background:var(--ink);color:var(--paper);border:1px solid var(--ink);}
#view-upgrade .plan .btn-primary:hover{background:var(--accent-deep);}
#view-upgrade .plan .btn-current{background:var(--paper-sunk);color:var(--ink-faint);border:1px solid var(--hair);cursor:default;}
#view-upgrade .plan .btn-outline{background:transparent;color:var(--ink);border:1px solid var(--hair-strong);}
#view-upgrade .plan .btn-outline:hover{border-color:var(--ink-soft);}
#view-upgrade .packs{margin-top:44px;padding-top:34px;border-top:1px solid var(--hair);}
#view-upgrade .packs h3{font-size:17px;font-weight:700;margin:0 0 6px;text-align:center;}
#view-upgrade .packs-sub{text-align:center;color:var(--ink-soft);font-size:13px;margin:0 0 22px;}
#view-upgrade .pack-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;}
@media (max-width:820px){#view-upgrade .pack-grid{grid-template-columns:repeat(2,1fr);}}
@media (max-width:460px){#view-upgrade .pack-grid{grid-template-columns:1fr;}}
/* Lighter than a plan card on purpose: a top-up is the secondary thing on
   this page, and four cards with a plan's weight would out-shout the plans. */
#view-upgrade .pack{background:var(--paper-raised);border:1px solid var(--hair);border-radius:8px;padding:18px 16px;display:flex;flex-direction:column;gap:3px;}
#view-upgrade .pack .pk-credits{font-size:20px;font-weight:700;letter-spacing:-.3px;}
#view-upgrade .pack .pk-renders{font-family:var(--mono);font-size:11px;color:var(--ink-faint);}
#view-upgrade .pack .pk-price{font-size:14px;font-weight:600;margin-top:10px;}
#view-upgrade .pack button{margin-top:14px;width:100%;padding:9px;border-radius:6px;font-size:12.5px;font-weight:600;font-family:var(--sans);background:var(--paper-sunk);color:var(--ink-faint);border:1px solid var(--hair);cursor:default;}
#view-upgrade .foot-note{text-align:center;margin-top:32px;font-size:12px;color:var(--ink-faint);}

  /* ===================== VIEW: GALLERY ===================== */
#view-gallery{margin:0;background:var(--paper);color:var(--ink);font-family:var(--sans);
    background-image:
      repeating-linear-gradient(0deg, transparent, transparent 27px, #0000000d 27px, #0000000d 28px),
      repeating-linear-gradient(90deg, transparent, transparent 27px, #0000000d 27px, #0000000d 28px);
  }
#view-gallery .wrap{width:100%;margin:0 auto;padding:24px 48px 80px;}
#view-gallery header{display:flex;align-items:center;justify-content:space-between;gap:20px;margin-bottom:24px;flex-wrap:wrap;padding-bottom:16px;border-bottom:1px solid var(--hair);}
#view-gallery .brand{display:flex;align-items:center;gap:10px;}
#view-gallery h1.logo{font-family:var(--brand-font);font-size:16px;font-weight:400;letter-spacing:.5px;margin:0;text-transform:uppercase;}
#view-gallery nav a{color:var(--ink-soft);text-decoration:none;font-size:13px;margin-left:22px;}
#view-gallery nav a:hover{color:var(--ink);}
#view-gallery .hero{margin-bottom:28px;}
#view-gallery .hero h2{font-family:var(--brand-font);font-size:32px;font-weight:400;text-transform:lowercase;margin:0 0 10px;letter-spacing:.5px;}
#view-gallery .hero p{color:var(--ink-soft);font-size:13.5px;margin:0;max-width:560px;}
#view-gallery .tabs{display:flex;gap:8px;margin-bottom:26px;}
#view-gallery .tab{background:var(--paper-raised);border:1px solid var(--hair-strong);border-radius:20px;padding:8px 16px;font-size:12.5px;font-family:var(--mono);letter-spacing:.4px;text-transform:uppercase;cursor:pointer;color:var(--ink-soft);}
#view-gallery .tab.active{background:var(--ink);color:var(--paper);border-color:var(--ink);}
#view-gallery .grid{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;}
@media (max-width:900px){#view-gallery .grid{grid-template-columns:repeat(2,1fr);}}
@media (max-width:600px){#view-gallery .grid{grid-template-columns:1fr;}}
#view-gallery .gcmp{position:relative;width:100%;aspect-ratio:4/3;background:#e8e8e8;overflow:hidden;cursor:ew-resize;user-select:none;}
#view-gallery .gcmp .after{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:#e2e2e2;}
#view-gallery .gcmp .before-wrap{position:absolute;inset:0;width:100%;overflow:hidden;pointer-events:none;display:flex;align-items:center;justify-content:center;background:#f4f4f4;}
#view-gallery .gcmp-line{position:absolute;top:0;bottom:0;width:1px;background:var(--ink);left:50%;pointer-events:none;z-index:2;}
#view-gallery .gcmp-dot{position:absolute;top:50%;left:50%;width:32px;height:20px;border-radius:3px;background:var(--ink);transform:translate(-50%,-50%);display:flex;align-items:center;justify-content:center;pointer-events:none;z-index:3;box-shadow:0 2px 6px #00000044;}
#view-gallery .gcmp-label{position:absolute;top:8px;font-size:9px;text-transform:uppercase;letter-spacing:1px;background:#00000099;padding:3px 8px;border-radius:2px;color:#fff;font-family:var(--mono);z-index:3;pointer-events:none;}
#view-gallery .gcmp-label.b{left:8px;}
#view-gallery .gcmp-label.a{right:8px;}
#view-gallery footer{text-align:center;margin-top:40px;font-size:12px;color:var(--ink-faint);}


/* ===================== DARK MODE ICON FIXES ===================== */
/* icons drawn with currentColor inherit the theme ink color automatically;
   the rules below handle glyphs that sit on an accent/ink background and
   therefore need the opposite (paper) color instead */
.token-chip svg{color:var(--ink-soft);}
#view-app .cmp-dot svg path{stroke:var(--paper-raised);}
#view-gallery .gcmp-dot svg path{stroke:var(--paper);}
#view-upgrade .plan li svg path{stroke:var(--paper-raised);}
#view-login .success .check svg path{stroke:var(--paper-raised);}

/* ---------------------------------------------------------------------------
   Chip choice groups.

   Every settings control here used to be a <select>. A dropdown hides its own
   options until you open it, so picking "Midday" meant one click to open, five
   lines of Thai to read, and a second click to choose. These show all the
   options at once and take one click. The <select> is still in the DOM and is
   still the source of truth -- it is only hidden.
   --------------------------------------------------------------------------- */
/* Square tiles since 2026-09-17, at peetz's request: "ออกแบบปุ่มตัวเลือกให้
   เป็น 1:1 เน้น icon แต่ก็ยังมีตัวหนังสือ". The row of text chips read as a
   dense settings form; a grid of equal squares with a drawing on each reads
   as a thing you pick from, which is what these are. The label stays — an
   icon alone is a guessing game on choices as close as Front and Back.

   A grid rather than a flex row, so the tiles line up in columns down the
   card instead of ragging, and auto-fit so a five- and a three-option control
   both fill the width they are given. */
#view-app select.chipped{display:none;}
/* The column has a ceiling as well as a floor. With a bare 1fr a two-option
   control (Focus: Deep / Shallow) stretched into two tiles twice the size of
   the six-option control above it, and the card stopped looking like one set
   of buttons. Capped, every tile in the card is the same square whatever
   control it belongs to, and short rows simply end early. */
#view-app .chips{display:grid;grid-template-columns:repeat(auto-fit,minmax(44px,52px));gap:4px;justify-content:start;}
#view-app .chip{
    display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;
    /* aspect-ratio alone is a suggestion: content taller than the square wins
       and the tile grows. min-height:0 with overflow hidden makes it a rule,
       so one long label can never break the grid's rhythm. */
    aspect-ratio:1;min-height:0;overflow:hidden;padding:3px;
    border:1px solid var(--hair-strong);background:var(--paper);color:var(--ink-soft);
    border-radius:3px;font-size:10px;font-family:var(--sans);
    line-height:1.15;cursor:pointer;text-align:center;
    transition:border-color .12s,background .12s,color .12s,box-shadow .12s;
  }
#view-app .chips .chip>span{
    /* The label needs its own size and margin: "#view-app label.field span"
       matches it directly, so it was winning over .chip's own font and adding
       a 6px bottom margin -- which at 96px merely wasted space, and at 52px
       squeezed every two-line label into one clipped line. It takes .chips as
       well as .chip to out-specify it: that rule carries two type selectors
       to this one's one, and types are only compared after classes. Sans, not the
       label rule's mono, fits about two more characters per line. */
    /* color:inherit for the same reason: without it the label kept the field
       rule's grey, so a SELECTED tile showed dim grey text on its inverted
       dark ground instead of following .chip[aria-pressed="true"]. */
    margin:0;font-family:var(--sans);font-size:10px;line-height:1.15;letter-spacing:0;color:inherit;
    /* Two lines, then ellipsis. "Golden Hour" must wrap rather than shrink
       the tile, and a label long enough to need three lines has a title
       attribute carrying the whole of it. */
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
    overflow:hidden;word-break:break-word;
  }
#view-app .chip:hover{border-color:var(--ink-soft);color:var(--ink);box-shadow:0 1px 4px rgba(0,0,0,.05);}
/* Sized against the 52px tile, not the 96px one it replaced: icon, a 3px
   gap and two 10px lines come to 50px, so a two-line label still fits the
   square instead of being clipped by the overflow rule above. */
#view-app .chip svg{flex-shrink:0;width:18px;height:18px;opacity:.75;}
/* The selected chip inverts to the ink colour, the same device the primary
   button and the nav CTA already use, so it reads as chosen in both themes. */
#view-app .chip[aria-pressed="true"]{
    background:var(--ink);border-color:var(--ink);color:var(--paper);
  }
#view-app .chip[aria-pressed="true"] svg{opacity:1;}
#view-app .chip:focus-visible{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-tint);}
/* The recommended setting (<option data-reco>): a small dot in the tile's
   corner, and the same dot leads the hint that says what it means. Muted
   accent, not a tick — see the Recommended tag on the pricing page. */
#view-app .chip.reco{position:relative;}
#view-app .chip.reco::after{content:'';position:absolute;top:4px;right:4px;width:5px;height:5px;border-radius:50%;background:var(--accent);}
#view-app .chip.reco[aria-pressed="true"]::after{background:var(--paper);}
/* label.field span.reco-dot, to out-specify "#view-app label.field span". */
#view-app label.field span.reco-dot{display:inline-block;width:5px;height:5px;margin:0 6px 0 0;border-radius:50%;background:var(--accent);vertical-align:1px;}
#view-app .aer-note{margin:0 0 10px;}
/* The process picker, as cards. peetz asked for them back on 2026-09-18 —
   "เรียงเป็นการ์ดเหมือนตัวเลือกแต่ให้ใหญ่กว่า พร้อม icon" — after one day as a
   dropdown, and the day before that six illustrated cards that made this the
   tallest thing on the page. So: the chip's own borders, ink and pressed
   state, but laid on its side.

   Sideways rather than a bigger square, because the panel card is only ~277px
   wide and the square leaves no good size. Four squares across fit at 65px,
   barely larger than the 52px option tile; two across are 135px each and two
   rows of those stand 276px tall, which is the illustrated cards again. A
   135x56 card is plainly bigger than an option tile, takes the whole of a
   name like "Sketchup to Render" on two lines beside its icon, and the four
   live modes come to ~118px of height in total. */
#view-app .chips.chips-lg{grid-template-columns:repeat(auto-fit,minmax(118px,1fr));gap:6px;}
#view-app .chips-lg .chip{
    /* aspect-ratio:1 is the square tile's rule and has to be undone by name,
       or the card is as tall as it is wide. */
    aspect-ratio:auto;min-height:56px;
    flex-direction:row;justify-content:flex-start;gap:8px;
    padding:9px 10px;border-radius:4px;text-align:left;
  }
#view-app .chips-lg .chip>span{font-size:11.5px;line-height:1.25;text-align:left;}
#view-app .chips-lg .chip svg{width:22px;height:22px;}
/* data-badge (2026-10-08): Beta on a mode, in the logo tag's quiet outline,
   pinned to the tile's top corner so the name keeps its width; it takes the
   tile's own colour, so it reads on the dark pressed tile too. */
#view-app .chips-lg .chip:has(.chip-badge){position:relative;}
#view-app .chips-lg .chip .chip-badge{position:absolute;top:6px;right:7px;padding:2px 5px 1px;font-family:var(--mono);font-size:8.5px;font-weight:500;
  letter-spacing:.9px;text-transform:uppercase;line-height:1.3;border:1px solid currentColor;border-radius:3px;opacity:.5;}
#view-app .chips-lg .chip.locked,#view-app .chips-wide .chip.locked{opacity:.55;cursor:not-allowed;}

/* Wide rows (2026-10-04): Output size, Quality and Variation -- two or three
   options, each with something to say under its name (the size it makes,
   what it costs). Equal columns across the card instead of 52px squares,
   which left "x4" as the whole of the explanation. Hidden sizes take no
   column, so a row of two still spans the card. */
#view-app .chips.chips-wide{grid-template-columns:none;grid-auto-flow:column;grid-auto-columns:minmax(0,1fr);gap:4px;}
#view-app .chips-wide .chip{aspect-ratio:auto;min-height:42px;padding:6px 8px;gap:2px;}
#view-app .chips-wide .chip>span{font-size:12px;font-weight:500;-webkit-line-clamp:1;}
#view-app .chips-wide .chip .chip-sub{font-family:var(--mono);font-size:9.5px;letter-spacing:.2px;line-height:1.2;opacity:.7;white-space:nowrap;}
#view-app .chips-wide .chip[aria-pressed="true"] .chip-sub{opacity:.8;}
/* Variation's two buttons read as words beside a picture, not a tile. */
#view-app .seedchips .chip{flex-direction:row;gap:7px;min-height:38px;}
#view-app .seedchips .chip svg{width:15px;height:15px;}
#view-app .seedchips .chip>span{font-size:11.5px;}
/* A <div>, not a <label>: a label hands any click on its text to the first
   button inside it, which here would be "New each Run". */
#view-app .field.seedfield{display:block;margin-bottom:10px;}
#view-app .seedfield .seedlabel{display:block;font-family:var(--mono);font-size:11px;letter-spacing:.2px;color:var(--ink-soft);margin-bottom:6px;}
#view-app .optsec > .field:last-child{margin-bottom:0;}
#view-app .seedfield .seedrow{margin-top:6px;}
#view-app .seedfield .seedrow input{font-family:var(--mono);font-size:12px;padding:7px 9px;}

/* The settings card is ~320px, so a .row2 half-column is ~133px -- narrower
   than two tiles. Paired that way a five-option control stacked into five
   rows and stood taller than the dropdown it replaced, which is the opposite
   of the point. A row that contains tiles therefore drops to one column and
   lets them flow across the full width instead. */
#view-app .row2:has(.chips){grid-template-columns:1fr;}
#view-app .row2 .chips{min-width:0;}
/* There is deliberately NO narrow-screen override any more. The 96px tile
   needed one; 52px is already a phone-sized target and six fit a 375px card.
   The override that used to live here shrank them again to 42px with a 1fr
   max, which cost a line of every two-line label AND let a two-option control
   stretch to 111px beside a five-option one at 42px -- the exact unevenness
   the capped column above exists to prevent. */

/* ---------------------------------------------------------------------------
   SSS Video
   The camera-move picker is its own grid rather than a chipControls group: it
   carries eleven options, past that helper's six-option ceiling, and the moves
   read far better as a fixed grid of arrows than as a wrapping row where
   "Dolly in" and "Pan left" land on different lines each time the card
   resizes. It writes to a hidden <select>, exactly as the chips do, so every
   reader stays pointed at the same source of truth.
   --------------------------------------------------------------------------- */
#view-app .vid-moves{display:grid;grid-template-columns:repeat(3,1fr);gap:4px;}
#view-app .vid-move{
    display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;
    border:1px solid var(--hair-strong);background:var(--paper);color:var(--ink-soft);
    border-radius:2px;padding:8px 4px;font-size:10.5px;font-family:var(--sans);
    line-height:1.25;cursor:pointer;text-align:center;
    transition:border-color .12s,background .12s,color .12s;
  }
#view-app .vid-move:hover{border-color:var(--ink-soft);color:var(--ink);}
#view-app .vid-move svg{flex-shrink:0;opacity:.7;}
#view-app .vid-move[aria-pressed="true"]{background:var(--ink);border-color:var(--ink);color:var(--paper);}
#view-app .vid-move[aria-pressed="true"] svg{opacity:1;}
#view-app .vid-move:focus-visible{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-tint);}

/* What this run will cost, in the same place every time, because the price
   moves with two different controls and a per-second charge is easy to
   misjudge from the dropdowns alone. */
#view-app .vid-cost{
    font-family:var(--mono);font-size:11px;color:var(--ink-soft);
    border:1px solid var(--hair);border-radius:2px;padding:7px 9px;margin-bottom:12px;
  }
#view-app .vid-cost b{color:var(--ink);font-weight:600;}

/* Sits in the same frame as the compare slider's images, so a clip fills the
   panel the way a still does. */
#view-app .cmp video{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;background:var(--canvas);}

/* ---------------------------------------------------------------------------
   Login: the parts an ordinary site has and this one was missing — a reveal
   toggle, a way back in when the password is gone, and somewhere to say
   something that is not an error.
   --------------------------------------------------------------------------- */
.pw-wrap{position:relative;display:block;}
.pw-wrap input{width:100%;padding-right:56px;}
.pw-toggle{
  position:absolute;right:8px;top:50%;transform:translateY(-50%);
  border:0;background:none;cursor:pointer;padding:4px 6px;
  font-family:var(--mono);font-size:10.5px;letter-spacing:.04em;text-transform:uppercase;
  color:var(--ink-soft);
}
.pw-toggle:hover{color:var(--ink);}
.pw-toggle:focus-visible{outline:none;color:var(--ink);text-decoration:underline;}

.lrow{margin-top:10px;font-size:12.5px;text-align:center;}
.lrow a{color:var(--ink-soft);text-decoration:none;border-bottom:1px solid var(--hair-strong);}
.lrow a:hover{color:var(--ink);border-bottom-color:var(--ink);}

/* Not an error — a confirmation, so it must not be the error colour. */
#loginNote{color:var(--ink-soft);line-height:1.5;}


/* ---------------------------------------------------------------------------
   Folded hints.

   Every explanation on this page used to stand open, so a settings card read as
   a page of prose and the controls got lost in it. Anything long now folds
   behind a "?" attached to its own label — one glyph instead of four lines, and
   the answer is one click away at the exact control it describes.

   Folding is applied by script to anything past a length threshold, so a hint
   written later is picked up with no registration step.
   --------------------------------------------------------------------------- */
#view-app .fieldhint.folded{display:none;}
#view-app .hint-q{
    display:inline-flex;align-items:center;justify-content:center;
    width:15px;height:15px;margin-left:6px;padding:0;vertical-align:-2px;
    border:1px solid var(--hair-strong);border-radius:50%;background:transparent;
    color:var(--ink-faint);font-family:var(--mono);font-size:9.5px;line-height:1;
    cursor:pointer;transition:color .12s,border-color .12s;
  }
#view-app .hint-q:hover{color:var(--ink);border-color:var(--ink-soft);}
#view-app .hint-q[aria-expanded="true"]{color:var(--paper);background:var(--ink);border-color:var(--ink);}
#view-app .hint-q:focus-visible{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-tint);}

/* ---------------------------------------------------------------------------
   Admin — editing the Gallery and How-to pages.

   Same sheet as everywhere else: hairline rules, mono labels, no colour beyond
   what the state needs. A draft entry is dimmed rather than badged, because the
   list is read at a glance and a row that is not live should look that way.
   --------------------------------------------------------------------------- */
#view-admin{background:var(--paper);color:var(--ink);font-family:var(--sans);}
#view-admin .wrap{max-width:900px;margin:0 auto;padding:24px 32px 80px;}
#view-admin header{display:flex;align-items:center;justify-content:space-between;gap:20px;margin-bottom:32px;padding-bottom:16px;border-bottom:1px solid var(--hair);}
#view-admin .brand{display:flex;align-items:center;gap:10px;}
#view-admin h1.logo{font-family:var(--brand-font);font-size:16px;font-weight:400;letter-spacing:.5px;margin:0;text-transform:uppercase;}
#view-admin nav a{color:var(--ink-soft);text-decoration:none;font-size:13px;margin-left:24px;}
#view-admin nav a:hover{color:var(--ink);}

#view-admin .ad-head{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;margin-bottom:18px;}
#view-admin .ad-head h2{font-size:20px;font-weight:700;margin:0;}
#view-admin .ad-tabs{display:flex;gap:4px;}
#view-admin .ad-tab{
    border:1px solid var(--hair-strong);background:var(--paper);color:var(--ink-soft);
    border-radius:2px;padding:6px 12px;font-family:var(--mono);font-size:11px;letter-spacing:.4px;
    cursor:pointer;text-transform:uppercase;
  }
#view-admin .ad-tab:hover{color:var(--ink);border-color:var(--ink-soft);}
#view-admin .ad-tab.active{background:var(--ink);border-color:var(--ink);color:var(--paper);}

#view-admin .ad-bar{display:flex;align-items:center;gap:14px;margin-bottom:16px;}
#view-admin .ad-add{width:auto;padding:9px 16px;}
#view-admin .ad-status{font-family:var(--mono);font-size:11px;color:var(--ink-soft);}

#view-admin .ad-list{display:flex;flex-direction:column;gap:8px;}
#view-admin .ad-row{
    display:flex;align-items:center;gap:14px;
    border:1px solid var(--hair-strong);border-radius:2px;background:var(--paper-raised);padding:10px 12px;
  }
#view-admin .ad-row.unpublished{opacity:.55;}
#view-admin .ad-row-thumb{width:74px;height:52px;flex-shrink:0;background:var(--canvas);border:1px solid var(--hair);
    display:flex;align-items:center;justify-content:center;overflow:hidden;}
#view-admin .ad-row-thumb img{width:100%;height:100%;object-fit:cover;display:block;}
#view-admin .ad-row-thumb span{font-family:var(--mono);font-size:9px;color:var(--ink-faint);}
#view-admin .ad-row-main{flex:1;min-width:0;}
#view-admin .ad-row-title{font-size:13.5px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
#view-admin .ad-row-sub{font-family:var(--mono);font-size:10.5px;color:var(--ink-soft);margin-top:3px;text-transform:uppercase;letter-spacing:.4px;}
#view-admin .ad-row-acts{display:flex;gap:4px;flex-shrink:0;}
#view-admin .ad-mini{
    border:1px solid var(--hair-strong);background:var(--paper);color:var(--ink-soft);
    border-radius:2px;padding:5px 9px;font-family:var(--mono);font-size:10.5px;cursor:pointer;
  }
#view-admin .ad-mini:hover{color:var(--ink);border-color:var(--ink-soft);}

#view-admin .ad-modal{position:fixed;inset:0;z-index:300;background:#000000aa;display:flex;align-items:center;justify-content:center;padding:20px;}
#view-admin .ad-dialog{width:100%;max-width:520px;max-height:90vh;display:flex;flex-direction:column;
    background:var(--paper-raised);border:1px solid var(--hair-strong);border-radius:2px;overflow:hidden;box-shadow:0 20px 50px #00000066;}
#view-admin .ad-dialog-head{display:flex;align-items:center;justify-content:space-between;padding:14px 16px;border-bottom:1px solid var(--hair);}
#view-admin .ad-dialog-head h3{margin:0;font-size:14px;font-weight:700;}
#view-admin .ad-x{border:0;background:none;color:var(--ink-soft);font-size:20px;line-height:1;cursor:pointer;padding:0 4px;}
#view-admin .ad-x:hover{color:var(--ink);}
#view-admin .ad-dialog-body{padding:16px;overflow-y:auto;}
#view-admin .ad-dialog-foot{display:flex;align-items:center;gap:8px;padding:12px 16px;border-top:1px solid var(--hair);}
#view-admin .ad-dialog-foot .primary-btn{width:auto;padding:9px 20px;}
#view-admin .ad-dialog-foot .ghost{border:1px solid var(--hair-strong);background:var(--paper);color:var(--ink-soft);
    border-radius:2px;padding:8px 14px;font-size:12.5px;cursor:pointer;font-family:var(--sans);}
#view-admin .ad-dialog-foot .ghost:hover{color:var(--ink);border-color:var(--ink-soft);}

#view-admin .ad-drops{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:14px;}
#view-admin .ad-drop{
    position:relative;border:1px dashed var(--hair-strong);border-radius:2px;background:var(--paper);
    min-height:120px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;
    cursor:pointer;padding:10px;text-align:center;overflow:hidden;
  }
#view-admin .ad-drop:hover,#view-admin .ad-drop.over{border-color:var(--ink-soft);}
#view-admin .ad-drop.busy{opacity:.5;pointer-events:none;}
#view-admin .ad-drop-label{font-family:var(--mono);font-size:10px;letter-spacing:.6px;text-transform:uppercase;color:var(--ink-faint);}
#view-admin .ad-drop-hint{font-size:11.5px;color:var(--ink-soft);}
#view-admin .ad-drop.filled .ad-drop-hint{display:none;}
#view-admin .ad-thumb{width:100%;max-height:120px;object-fit:contain;background:var(--canvas);display:block;}
#view-admin .ad-clear{display:none;position:absolute;top:6px;right:6px;border:1px solid var(--hair-strong);
    background:var(--paper);color:var(--ink-soft);border-radius:2px;font-family:var(--mono);font-size:9.5px;
    padding:3px 6px;cursor:pointer;}
#view-admin .ad-drop.filled .ad-clear{display:block;}
#view-admin .ad-clear:hover{color:var(--ink);border-color:var(--ink-soft);}

#view-admin .ad-error{color:var(--brick);font-size:12.5px;margin-top:10px;line-height:1.5;}

/* The form primitives are scoped to #view-app, so the Admin view would render
   browser-default inputs on a dark sheet — white boxes, unreadable. Same rules,
   same values, one more scope. */
#view-admin label.field{display:block;margin-bottom:12px;}
#view-admin label.field > span{display:block;font-family:var(--mono);font-size:11px;letter-spacing:.2px;color:var(--ink-soft);margin-bottom:6px;}
#view-admin label.field span.fieldhint{
    font-family:var(--sans);font-size:11.5px;line-height:1.45;
    color:var(--ink-faint);letter-spacing:0;margin:6px 0 0;
  }
#view-admin input[type=text],#view-admin select,#view-admin textarea{
    width:100%;background:var(--paper);border:1px solid var(--hair-strong);
    color:var(--ink);padding:9px 10px;border-radius:2px;font-size:13.5px;font-family:var(--sans);
  }
#view-admin textarea{resize:vertical;line-height:1.5;font-size:12.8px;}
#view-admin input:focus,#view-admin select:focus,#view-admin textarea:focus{
    outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-tint);
  }
#view-admin .primary-btn{
    border:1px solid var(--accent-deep);border-radius:2px;background:var(--accent);color:var(--paper);
    font-weight:600;font-size:13px;cursor:pointer;letter-spacing:.4px;text-transform:uppercase;
    font-family:var(--mono);padding:9px 16px;
  }
#view-admin .primary-btn:disabled{opacity:.5;cursor:default;}


/* Plans: a deliberately dead button, and the beta marker in a plan list. */
/* A dead button must not keep the weight of a live one — at half opacity a
   filled primary was still the loudest thing on the page. */
#view-upgrade .plan button[disabled]{
    cursor:default;
    background:var(--paper-sunk);color:var(--ink-faint);border:1px solid var(--hair);
  }
#view-upgrade .plan button[disabled]:hover{background:var(--paper-sunk);border-color:var(--hair);}
#view-upgrade .plan .pill-beta{
    font-family:var(--mono);font-size:9px;letter-spacing:.09em;text-transform:uppercase;
    color:var(--ink-faint);border:1px solid var(--hair);border-radius:2px;
    padding:1px 4px;margin-left:5px;vertical-align:1px;
  }


/* ---------------------------------------------------------------------------
   Landing sections: use cases, steps, voices.

   All three are card grids on the same measure as .features, so the page reads
   as one system rather than three imported blocks. Each hides itself when it
   has no published content, so the spacing has to come from the grid rather
   than from margins on the headings around it.
   --------------------------------------------------------------------------- */
#view-home .usecases,
#view-home .steps,
#view-home .voices{
    display:grid;gap:14px;margin-bottom:56px;
  }
#view-home .usecases{grid-template-columns:repeat(auto-fit,minmax(240px,1fr));}
#view-home .steps{grid-template-columns:repeat(auto-fit,minmax(260px,1fr));}
#view-home .voices{grid-template-columns:repeat(auto-fit,minmax(280px,1fr));}

#view-home .uc,
#view-home .step,
#view-home .voice{
    border:1px solid var(--hair);border-radius:10px;background:var(--paper-raised);
    padding:18px 20px;
  }

/* A use case leads with what the reader recognises — the job, not the feature. */
#view-home .uc h4{font-size:14px;font-weight:600;margin:0 0 6px;}
#view-home .uc p{font-size:13px;color:var(--ink-soft);margin:0;line-height:1.55;}
#view-home .uc img{
    width:100%;aspect-ratio:16/10;object-fit:cover;border-radius:6px;
    margin-bottom:14px;background:var(--canvas);
  }

/* Steps are numbered because they are genuinely a sequence — you cannot render
   before you upload. The number is information, not decoration. */
#view-home .step{display:flex;flex-direction:column;}
#view-home .step img{
    width:100%;aspect-ratio:16/10;object-fit:cover;border-radius:6px;
    margin-bottom:14px;background:var(--canvas);
  }
#view-home .step-n{
    font-family:var(--mono);font-size:10px;letter-spacing:.12em;
    color:var(--ink-faint);margin-bottom:6px;
  }
#view-home .step h4{font-size:14px;font-weight:600;margin:0 0 6px;}
#view-home .step p{font-size:13px;color:var(--ink-soft);margin:0;line-height:1.55;}

/* A quote earns its space only with a name and a firm attached. */
#view-home .voice blockquote{
    margin:0 0 14px;font-size:14px;line-height:1.6;color:var(--ink);
  }
#view-home .voice-by{display:flex;align-items:center;gap:10px;}
#view-home .voice-by img{
    width:34px;height:34px;border-radius:50%;object-fit:cover;
    background:var(--canvas);flex-shrink:0;
  }
#view-home .voice-name{font-size:13px;font-weight:600;}
#view-home .voice-firm{font-size:12px;color:var(--ink-faint);}


/* The stand-in for a picture not uploaded yet. Same frame as the <img> it will
   be replaced by, so nothing on the card moves when the real one arrives. */
#view-home .uc .ph,
#view-home .step .ph{
    width:100%;aspect-ratio:16/10;border-radius:6px;margin-bottom:14px;
    background:var(--canvas);display:flex;align-items:center;justify-content:center;
    overflow:hidden;
  }
#view-home .uc .ph svg,
#view-home .step .ph svg{width:100%;height:100%;}


/* ---------------------------------------------------------------------------
   The drifting row: Trusted by, on the home page.

   The movement is a transform on a track that holds its contents twice, so the
   50% mark is pixel-identical to the start and the loop has no seam. It is not
   a scroll: scrolling would fight the reader's own wheel and could not run at
   a constant speed.
   --------------------------------------------------------------------------- */
.marquee{
    display:grid;gap:14px;margin-bottom:44px;
  }

/* Full-bleed: the rows run past the page measure to the window edges, which is
   what makes them read as a continuing band rather than a wide card. */
.mrow{
    overflow:hidden;
    /* 100vw counts the vertical scrollbar, so a plain 100vw bleed hangs past
       the viewport by exactly its width. --sbw is measured in app.js. */
    width:calc(100vw - var(--sbw, 0px));
    margin-left:calc(50% - (100vw - var(--sbw, 0px)) / 2);
    -webkit-mask-image:linear-gradient(to right,transparent,#000 6%,#000 94%,transparent);
    mask-image:linear-gradient(to right,transparent,#000 6%,#000 94%,transparent);
  }
.mtrack{
    display:flex;gap:14px;width:max-content;height:100%;
    animation:gmarquee var(--dur,60s) linear infinite;
    will-change:transform;
  }
.mrow.rev .mtrack{animation-direction:reverse;}
/* Stop it under the pointer so a picture can actually be looked at and clicked. */
.mrow:hover .mtrack{animation-play-state:paused;}

@keyframes gmarquee{from{transform:translateX(0);}to{transform:translateX(-50%);}}

.mrow{height:100%;}
.mitem{
    position:relative;flex:0 0 auto;height:100%;border-radius:10px;overflow:hidden;
    background:var(--canvas);border:1px solid var(--hair);cursor:pointer;
  }
.mitem{aspect-ratio:3/2;}
.mitem img{display:block;width:100%;height:100%;max-width:none;object-fit:cover;}

#view-gallery .gempty{
    text-align:center;color:var(--ink-faint);font-size:13px;padding:40px 0 60px;
  }

/* The lightbox. */
#view-gallery .lb{
    position:fixed;inset:0;z-index:60;background:rgba(0,0,0,.88);
    display:flex;align-items:center;justify-content:center;padding:40px 24px;
  }
/* display:flex outranks the hidden attribute on its own, which left the
   lightbox standing open on load. */
#view-gallery .lb[hidden]{display:none;}
#view-gallery .lb-inner{width:min(1100px,100%);}
#view-gallery .lb .gcmp{position:relative;aspect-ratio:16/10;border-radius:10px;overflow:hidden;background:var(--canvas);}
#view-gallery .lb .gcmp .after,
#view-gallery .lb .gcmp .before-wrap{position:absolute;inset:0;}
/* Only the picture itself — not the chevron inside the drag handle, which
   this rule used to inflate to the full width of the frame. */
#view-gallery .lb .gcmp > .after > *,
#view-gallery .lb .gcmp > .before-wrap > *{width:100%;height:100%;object-fit:cover;display:block;}
#view-gallery .lb-meta{
    display:flex;gap:12px;align-items:baseline;margin-top:12px;color:#fff;
  }
#view-gallery .lb-meta span:first-child{
    font-family:var(--mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase;opacity:.6;
  }
#view-gallery .lb-meta span:last-child{font-size:14px;font-weight:600;}
#view-gallery .lb-x{
    position:absolute;top:18px;right:22px;width:36px;height:36px;
    border:1px solid rgba(255,255,255,.25);background:transparent;color:#fff;
    border-radius:50%;font-size:20px;line-height:1;cursor:pointer;
  }
#view-gallery .lb-x:hover{border-color:#fff;}

/* Someone who has asked for less movement gets a row they push themselves. */
@media (prefers-reduced-motion: reduce){
  .mtrack{animation:none;}
  .mrow{overflow-x:auto;}
}

/* The drifting rows are full-bleed at 100vw, and 100vw counts the vertical
   scrollbar — so every page carrying one gained a horizontal scrollbar exactly
   that wide. clip rather than hidden: hidden on body also kills position:sticky
   for anything inside it. */
body{overflow-x:hidden;}
@supports (overflow:clip){body{overflow-x:clip;}}


/* The logo is a link in every header now. It has to keep looking like a logo:
   no underline, no link colour, just a hand cursor and a little life on hover. */
a.brand{text-decoration:none;color:inherit;cursor:pointer;}
a.brand:hover{opacity:.78;}
a.brand:focus-visible{outline:2px solid var(--accent);outline-offset:4px;border-radius:4px;}

/* The sign-in page's own header. That page centres a card in the viewport, so
   this sits over it rather than in the normal flow. */
#view-login .login-top{
    position:absolute;top:0;left:0;right:0;z-index:2;
    display:flex;align-items:center;justify-content:space-between;
    padding:20px 28px;
  }
/* .brand is laid out per view header; this one is outside them all, so it
   needs its own row or the mark stacks above the name. */
#view-login .login-top .brand{display:flex;align-items:center;gap:9px;}
#view-login .login-top .logo{font-size:15px;margin:0;}
#view-login .login-back{
    font-family:var(--sans);font-size:12.5px;color:var(--ink-soft);text-decoration:none;
    border:1px solid var(--hair);border-radius:18px;padding:7px 14px;
  }
#view-login .login-back:hover{color:var(--ink);border-color:var(--hair-strong);}
@media (max-width:560px){
  #view-login .login-top{padding:14px 16px;}
  #view-login .login-top .logo{font-size:13px;}
}


/* Trusted by: the same drifting row, kept small. Logos are shown at a size
   where they read as marks rather than as pictures, and they sit quiet until
   pointed at — a wall of full-strength logos competes with the work above it. */
#view-home .logos{margin-bottom:56px;}
#view-home .logos .mrow{height:auto;}
#view-home .logos .mtrack{height:auto;}
#view-home .logos .mitem{
    height:84px;aspect-ratio:auto;width:auto;overflow:visible;
    border:0;background:transparent;border-radius:0;
    cursor:default;padding:0 34px;display:flex;align-items:center;
  }
#view-home .logos .mitem img{
    height:56px;width:auto;object-fit:contain;
    opacity:.75;filter:grayscale(1);transition:opacity .15s,filter .15s;
  }
#view-home .logos .mitem:hover img{opacity:1;filter:none;}

/* The "Trusted by" label: quiet, because the logos underneath are the content. */
#view-home .logos-head{
    font-family:var(--mono);font-size:10px;letter-spacing:.16em;text-transform:uppercase;
    color:var(--ink-faint);margin:0 0 14px;
  }

/* Detail Shots: one row per drawn box, number swatch, two angle chips, remove. */
#view-app .dt-list{display:flex;flex-direction:column;gap:8px;margin:10px 0;}
#view-app .dt-row{display:flex;align-items:center;gap:6px;flex-wrap:wrap;}
#view-app .dt-n{width:20px;height:20px;border-radius:2px;color:#fff;font:600 11px/20px var(--mono);text-align:center;flex-shrink:0;}
#view-app .dt-row .dt-del{margin-left:auto;}
#view-app .dt-foot{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px;}
#view-app #dtWrap canvas{cursor:crosshair;}

/* ---------------------------------------------------------------------------
   Back office. Four panes behind one tab bar: Overview, Profit & loss,
   Members, Site content. Numbers are monospaced and right-aligned wherever
   they line up in a column, because the reason to look at this page is to
   compare one row against another. */
#view-admin .bo-pane{margin-top:4px;}
#view-admin .bo-h3{font-size:13px;font-weight:700;margin:26px 0 10px;text-transform:uppercase;letter-spacing:.6px;color:var(--ink-soft);}
#view-admin .bo-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(178px,1fr));gap:10px;}
#view-admin .bo-card{border:1px solid var(--hair);background:var(--paper);padding:13px 15px;}
#view-admin .bo-card .k{font-family:var(--mono);font-size:10px;letter-spacing:.5px;text-transform:uppercase;color:var(--ink-soft);}
#view-admin .bo-card .v{font-size:23px;font-weight:700;margin-top:6px;font-variant-numeric:tabular-nums;line-height:1.15;}
#view-admin .bo-card .s{font-family:var(--mono);font-size:10.5px;color:var(--ink-faint);margin-top:4px;}
#view-admin .bo-card.warn{border-color:#b8860b;background:#fffaf0;}
#view-admin .bo-card.bad{border-color:#b33;background:#fff5f5;}
#view-admin .bo-card.bad .v,#view-admin .bo-card.warn .v{color:#8a1c1c;}
#view-admin .bo-note{font-size:12px;color:var(--ink-soft);line-height:1.55;margin-top:14px;max-width:70ch;}
#view-admin .bo-note b{color:var(--ink);}
#view-admin .bo-tablewrap{overflow-x:auto;border:1px solid var(--hair);background:var(--paper);}
#view-admin .bo-table{width:100%;border-collapse:collapse;font-size:12.5px;}
#view-admin .bo-table th,#view-admin .bo-table td{padding:8px 12px;text-align:left;border-bottom:1px solid var(--hair);white-space:nowrap;}
#view-admin .bo-table th{font-family:var(--mono);font-size:10px;letter-spacing:.5px;text-transform:uppercase;color:var(--ink-soft);font-weight:600;}
#view-admin .bo-table tr:last-child td{border-bottom:0;}
#view-admin .bo-table td.n,#view-admin .bo-table th.n{text-align:right;font-variant-numeric:tabular-nums;font-family:var(--mono);font-size:11.5px;}
#view-admin .bo-table td.neg{color:#8a1c1c;}
/* The baht beside every dollar. Quieter than the figure it follows, because
   the dollar is the number the business is quoted and run in now and this is
   the reading in the currency it banks in — a check, not the headline. It sits
   on its own line in a table cell, where two figures side by side would have
   set the column width by the longest pair. */
#view-admin .bo-alt{display:block;color:var(--ink-faint);font-size:10px;font-weight:400;}
#view-admin .bo-card .s .bo-alt{display:inline;font-size:inherit;}
#view-admin .bo-table td.neg .bo-alt{color:inherit;opacity:.65;}
#view-admin .bo-table .bo-act{padding:3px 8px;font-size:11px;}
#view-admin .bo-keep{font-family:var(--mono);font-size:11px;color:var(--ink-soft);margin:10px 0 4px;line-height:1.6;}
#view-admin .bo-keep b{color:var(--ink);}
#view-admin .bo-stmt tr.bo-sub td{color:var(--ink-soft);}
#view-admin .bo-stmt tr.bo-sub td:first-child{padding-left:26px;}
#view-admin .bo-stmt tr.bo-total td{font-weight:700;border-top:1px solid var(--ink-faint);}
#view-admin .bo-stmt tr.bo-yours td{font-size:13px;}
#view-admin .bo-plan{font-family:var(--mono);font-size:10px;text-transform:uppercase;letter-spacing:.4px;border:1px solid var(--hair);padding:2px 6px;}
#view-admin .bo-empty{padding:18px;font-size:12.5px;color:var(--ink-soft);}

/* ---- Add People clean-up brush (public/cleanup.js), 2026-09-25 ---- */
#view-app button.dl#btnCleanPeople{border:0;cursor:pointer;font-family:var(--mono);}
#view-app .cmp .pc-overlay{position:absolute;inset:0;width:100%;height:100%;z-index:6;cursor:none;touch-action:none;background:var(--canvas);}
#view-app .pc-tools{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-top:10px;padding:8px 10px;border:1px solid var(--hair);background:var(--paper-raised);animation:rise .22s ease-out;}
#view-app .pc-b{font-family:var(--mono);font-size:11px;letter-spacing:.3px;padding:6px 11px;border:1px solid var(--hair-strong);background:transparent;color:var(--ink);cursor:pointer;transition:background .15s,color .15s,border-color .15s;}
#view-app .pc-b:hover{border-color:var(--ink-soft);}
#view-app .pc-b.on{background:var(--ink);color:var(--paper-raised);border-color:var(--ink);}
#view-app .pc-b.pc-save{background:var(--accent);color:var(--paper-raised);border-color:var(--accent);}
#view-app .pc-b:disabled{opacity:.5;cursor:default;}
#view-app .pc-size{display:flex;align-items:center;gap:6px;font-family:var(--mono);font-size:11px;color:var(--ink-soft);}
#view-app .pc-size input{width:110px;}
#view-app .pc-gap{flex:1;}

/* ---- Views: per-row download/delete and the 1 GB bar, 2026-09-25 ---- */
#view-app .vrow{position:relative;transition:opacity .2s,transform .2s;}
#view-app .vrow.gone{opacity:0;transform:translateX(-8px);}
#view-app .vrow .vitem{width:100%;padding-right:74px;}
#view-app .vact{position:absolute;right:8px;top:50%;transform:translateY(-50%);display:flex;gap:4px;opacity:.55;transition:opacity .15s;}
#view-app .vrow:hover .vact,#view-app .vact:focus-within{opacity:1;}
#view-app .vbtn{width:28px;height:28px;display:grid;place-items:center;border:1px solid var(--hair);background:var(--paper-raised);color:var(--ink-soft);cursor:pointer;transition:color .15s,border-color .15s,transform .15s;}
#view-app .vbtn:hover{color:var(--ink);border-color:var(--ink-soft);transform:translateY(-1px);}
#view-app .vbtn[data-act="rm"]:hover{color:#a3261c;border-color:#a3261c;}
#view-app .vkeep{margin:0 0 10px;font-size:12px;color:var(--ink-soft);}
#view-app .vquota{display:grid;gap:5px;margin:0 0 12px;font-family:var(--mono);font-size:10.5px;color:var(--ink-soft);}
#view-app .vquota-bar{height:4px;background:var(--paper-sunk);overflow:hidden;}
#view-app .vquota-bar i{display:block;height:100%;width:0;background:var(--ink);transition:width .6s cubic-bezier(.2,.7,.2,1);}
#view-app .vquota.full .vquota-bar i{background:#a3261c;}
#view-app .vquota.full{color:#a3261c;}

/* ===========================================================================
   Loading screen, page changes and small motion — 2026-09-25.
   peetz: "ดูมีลูกเล่นแต่ไม่เยอะจนตาลาย". The rule held to here: every movement
   is short (≤ 450 ms), small (a few px), eased, and answers something the
   person did. Nothing loops except while work is actually running.
   =========================================================================== */
@keyframes rise{from{opacity:0;transform:translateY(6px);}to{opacity:1;transform:none;}}
@keyframes fadeIn{from{opacity:0;}to{opacity:1;}}
@keyframes sweep{from{transform:scaleX(0);}to{transform:scaleX(1);}}
@keyframes curtainOut{0%{opacity:1;}100%{opacity:0;visibility:hidden;}}
@keyframes markIn{0%{opacity:0;transform:scale(.92);}40%{opacity:1;transform:scale(1);}100%{opacity:0;}}
@keyframes barRun{0%{transform:scaleX(0);opacity:1;}70%{transform:scaleX(1);opacity:1;}100%{transform:scaleX(1);opacity:0;}}
@keyframes shimmer{0%{background-position:-160% 0;}100%{background-position:260% 0;}}
@keyframes nudge{0%,100%{transform:translateY(0);}50%{transform:translateY(-2px);}}

/* the loading screen */
#boot{position:fixed;inset:0;z-index:9999;display:grid;place-items:center;background:#F6F5F2;color:#191A18;transition:opacity .5s ease,visibility .5s;}
#boot.out{opacity:0;visibility:hidden;}
#boot .boot-in{display:grid;justify-items:center;gap:22px;animation:rise .6s cubic-bezier(.2,.7,.2,1) both;}
/* the logo intro (logo/logo-intro.html), on brand/sss-lockup-intro.svg's viewBox
   (0 -3.975 216.854 39.755): every % below is x / 216.854 — the mark ends at 24.67%,
   the divider sits at 32.74% and runs the full height, the mark's centre is 12.34%,
   so it starts shifted 37.66% to sit alone in the middle; the mark is the middle 80%
   of the height, so the scan line at 1.36 × mark height overhangs the box by 4.4% */
#boot .bl{position:relative;width:min(360px,80vw);aspect-ratio:216.854/39.755;color:#191A18;
  transform:translateX(37.66%);animation:blSettle .6s cubic-bezier(.77,0,.18,1) 1.15s forwards;}
#boot .bl > *{position:absolute;inset:0;width:100%;height:100%;display:block;}
#boot .bl-out path{fill:none;stroke:currentColor;stroke-width:1.2;stroke-linejoin:miter;stroke-dasharray:1;stroke-dashoffset:1;
  animation:blDraw .6s cubic-bezier(.65,0,.35,1) forwards;}
#boot .bl-out path:nth-child(2){animation-delay:.1s;}
#boot .bl-out path:nth-child(3){animation-delay:.2s;}
#boot .bl-out{animation:blFade .35s ease 1.05s forwards;}
#boot .bl-fill{fill:currentColor;clip-path:inset(0 100% 0 0);animation:blFill .45s cubic-bezier(.77,0,.18,1) .7s forwards;}
/* one line: it scans across the esses, then travels on and stays as the divider */
#boot .bl-line{inset:auto;left:0;top:-4.4%;bottom:-4.4%;height:auto;width:2px;margin-left:-1px;background:currentColor;opacity:0;
  animation:blScan .45s cubic-bezier(.77,0,.18,1) .7s forwards,blToRule .45s cubic-bezier(.65,0,.35,1) 1.15s forwards;}
#boot .bl-words{clip-path:inset(0 100% 0 33%);transform:translateX(-1.5%);animation:blWords .55s cubic-bezier(.2,.8,.2,1) 1.45s forwards;}
#boot.seen .bl,#boot.seen .bl *{animation-delay:0s !important;animation-duration:1ms !important;}
@keyframes blDraw{to{stroke-dashoffset:0;}}
@keyframes blFade{to{opacity:0;}}
@keyframes blFill{to{clip-path:inset(0 75.3% 0 0);}}
@keyframes blScan{0%{opacity:0;left:0;}15%{opacity:.9;}100%{opacity:.9;left:24.67%;}}
@keyframes blToRule{from{left:24.67%;opacity:.9;top:-4.4%;bottom:-4.4%;width:2px;margin-left:-1px;}
                    to{left:32.74%;opacity:.45;top:0;bottom:0;width:1px;margin-left:0;}}
@keyframes blSettle{to{transform:translateX(0);}}
@keyframes blWords{to{clip-path:inset(0 0 0 33%);transform:translateX(0);}}

/* a page change: a curtain that fades off the new view, a hairline across the top */
.view-curtain{position:fixed;inset:0;z-index:9000;pointer-events:none;display:grid;place-items:center;background:var(--paper,#F6F5F2);color:var(--ink,#191A18);opacity:0;visibility:hidden;}
.view-curtain svg{width:46px;height:auto;opacity:0;}
.view-curtain.run{visibility:visible;animation:curtainOut .42s cubic-bezier(.4,0,.2,1) forwards;}
.view-curtain.run svg{animation:markIn .42s ease-out forwards;}
.view-bar{position:fixed;left:0;right:0;top:0;height:2px;z-index:9001;pointer-events:none;background:var(--ink,#191A18);transform-origin:0 50%;transform:scaleX(0);opacity:0;}
.view-bar.run{animation:barRun .6s cubic-bezier(.2,.7,.2,1) forwards;}
.view.view-in .card{animation:rise .38s cubic-bezier(.2,.7,.2,1) both;}
.view.view-in .card:nth-child(2){animation-delay:.04s;}
.view.view-in .card:nth-child(3){animation-delay:.08s;}
.view.view-in .card:nth-child(4){animation-delay:.12s;}
.view.view-in .card:nth-child(n+5){animation-delay:.16s;}

/* option tiles: the icon lifts a hair on hover and settles when chosen */
#view-app .chip{transition:border-color .15s,color .15s,box-shadow .2s,transform .18s cubic-bezier(.2,.7,.2,1),background .15s;}
#view-app .chip:hover{transform:translateY(-1px);}
#view-app .chip svg{transition:transform .25s cubic-bezier(.2,.7,.2,1),opacity .15s;}
#view-app .chip:hover svg{transform:translateY(-1px) scale(1.06);}
#view-app .chip[aria-pressed="true"] svg{animation:nudge .35s ease-out 1;}
#view-app .chip:active{transform:translateY(0) scale(.98);}

/* the rail */
#view-app .rail-b svg{transition:transform .2s cubic-bezier(.2,.7,.2,1);}
#view-app .rail-b:hover svg{transform:translateY(-1px) scale(1.08);}

/* Run: a slow light passes over the button only while a job is running */
#view-app button.run:disabled.busy,
#view-app button.run.busy{
  background:linear-gradient(100deg,var(--paper-sunk) 30%,#fff 50%,var(--paper-sunk) 70%);
  background-size:220% 100%;animation:shimmer 1.6s linear infinite;color:var(--ink-soft);
}

/* results fade in rather than snapping */
#view-app .cmp img,#view-app .cmp video{animation:fadeIn .35s ease-out;}
#view-app .actions-bottom{animation:rise .3s ease-out;}
#view-app .vitem{transition:border-color .15s,background .15s,transform .15s;}
#view-app .vitem:hover{transform:translateX(2px);}
#view-app .vlist .vrow{animation:rise .3s cubic-bezier(.2,.7,.2,1) both;}
#view-app .vlist .vrow:nth-child(2){animation-delay:.03s;}
#view-app .vlist .vrow:nth-child(3){animation-delay:.06s;}
#view-app .vlist .vrow:nth-child(n+4){animation-delay:.09s;}

@media (prefers-reduced-motion: reduce){
  #boot .boot-in,.view-curtain.run,.view-curtain.run svg,.view-bar.run,
  .view.view-in .card,#view-app .chip svg,#view-app .cmp img,#view-app .cmp video,
  #view-app .actions-bottom,#view-app .vlist .vrow,#view-app button.run.busy,#view-app .pc-tools{animation:none !important;}
  .view-curtain.run{visibility:hidden;}
  #boot .bl,#boot .bl *{animation-delay:0s !important;animation-duration:1ms !important;}
  #view-app .chip:hover,#view-app .chip:hover svg,#view-app .rail-b:hover svg,#view-app .vitem:hover{transform:none;}
}

/* the back office's banner (System tab), 2026-09-25 */
.sys-banner{padding:9px 16px;font-size:12.5px;text-align:center;background:var(--ink,#191A18);color:#fff;animation:fadeIn .3s ease-out;}
.sys-banner.warn{background:#8a1c1c;}

/* back office: jobs, storage, system (2026-09-25) */
#view-admin .bo-filters{display:flex;flex-wrap:wrap;gap:8px;align-items:center;font-size:12.5px;}
#view-admin .bo-filters input,#view-admin .bo-filters select{padding:6px 8px;font-size:12.5px;border:1px solid var(--hair-strong);background:var(--paper-raised);width:auto;}
#view-admin .bo-thumb{width:56px;height:36px;object-fit:cover;display:block;border:1px solid var(--hair);}
#view-admin .bo-wrap{white-space:normal !important;max-width:280px;font-size:11.5px;color:var(--ink-soft);}
#view-admin .bo-bad{border-color:#8a1c1c !important;color:#8a1c1c;}
#view-admin .bo-act.bo-on{border-color:#8a1c1c;color:#8a1c1c;}
#view-admin .bo-switches{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:8px;max-width:900px;}
#view-admin .bo-switch{display:flex;align-items:center;gap:9px;padding:10px 12px;border:1px solid var(--hair);background:var(--paper);font-size:13px;cursor:pointer;}
#view-admin .bo-switch input{width:auto;margin:0;}

/* The top-left logo is the official horizontal lockup (logo/PDF Asset 4),
   2026-09-25 — peetz: "logo ซ้ายบนใช้ logo Asset 4 ขอใหญ่ขึ้นอีกหน่อย".
   The name stays in the heading for screen readers and search, unseen. */
.brand-lockup{display:block;height:42px;width:auto;aspect-ratio:216.854/39.755;flex-shrink:0;}
@media (max-width:760px){.brand-lockup{height:32px;}}
.sr-only{position:absolute !important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;}

/* Under the upload box: the preparation guide (2026-09-26). */
.drop-guide{margin:10px 0 0;font-size:12px;color:var(--ink-soft);line-height:1.5;}
.drop-guide a{color:var(--ink);font-weight:500;}

/* Checkout notices (billing() in app.js): one quiet line at the foot of the screen. */
.pay-note{position:fixed;left:50%;bottom:24px;transform:translate(-50%,12px);max-width:calc(100vw - 32px);
  padding:10px 16px;background:var(--ink);color:var(--paper, #fff);font-family:var(--sans);font-size:13px;
  border-radius:4px;opacity:0;pointer-events:none;transition:opacity .25s,transform .25s;z-index:1000;}
.pay-note.show{opacity:1;transform:translate(-50%,0);}
#accountView #btnManagePlan{margin-top:10px;}
@media (prefers-reduced-motion: reduce){.pay-note{transition:none;}}

/* ---- Beta tag and Feedback popup (2026-10-02, feedback.js) ---- */
.beta-tag{display:inline-flex;align-items:center;align-self:center;margin-left:9px;padding:2px 6px 1px;
  font-family:var(--mono);font-size:9.5px;font-weight:500;letter-spacing:.9px;text-transform:uppercase;line-height:1.4;
  color:var(--ink-soft);border:1px solid var(--hair-strong);border-radius:3px;white-space:nowrap;}
#view-login .login-card .brand .beta-tag{margin-left:0;}
.fb-link{font-family:var(--sans);font-size:12.5px;color:var(--ink-soft);text-decoration:none;margin-right:16px;}
.fb-link:hover{color:var(--ink);}
.fb-overlay{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center;padding:16px;
  background:rgba(25,26,24,.32);backdrop-filter:blur(2px);}
.fb-overlay[hidden]{display:none;}
.fb-card{position:relative;width:100%;max-width:420px;background:var(--paper-raised);border:1px solid var(--hair);
  border-radius:6px;padding:22px 22px 18px;box-shadow:0 18px 50px rgba(0,0,0,.14);font-family:var(--sans);color:var(--ink);}
.fb-card h3{margin:0 0 6px;font-size:16px;font-weight:600;letter-spacing:-.1px;}
.fb-sub{margin:0 0 16px;font-size:12.5px;line-height:1.5;color:var(--ink-soft);}
.fb-x{position:absolute;top:10px;right:12px;border:0;background:none;font-size:20px;line-height:1;color:var(--ink-faint);cursor:pointer;padding:4px;}
.fb-x:hover{color:var(--ink);}
.fb-rate{display:grid;grid-template-columns:repeat(3,1fr);gap:6px;margin-bottom:12px;}
.fb-rate button{padding:8px 6px;border:1px solid var(--hair-strong);border-radius:4px;background:var(--paper);
  font-family:var(--sans);font-size:12.5px;color:var(--ink-soft);cursor:pointer;transition:border-color .15s,color .15s,background .15s;}
.fb-rate button:hover{color:var(--ink);border-color:var(--ink-faint);}
.fb-rate button[aria-checked="true"]{background:var(--ink);border-color:var(--ink);color:var(--paper-raised);}
.fb-card textarea{width:100%;box-sizing:border-box;resize:vertical;min-height:84px;padding:9px 10px;border:1px solid var(--hair-strong);
  border-radius:4px;background:var(--paper);font-family:var(--sans);font-size:13px;line-height:1.5;color:var(--ink);}
.fb-card textarea:focus{outline:none;border-color:var(--ink-soft);}
.fb-attach{display:flex;align-items:center;gap:7px;margin-top:10px;font-size:12px;color:var(--ink-soft);cursor:pointer;}
.fb-attach[hidden]{display:none;}
.fb-foot{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:14px;}
.fb-status{font-size:12px;color:var(--ink-soft);}
.fb-send{padding:8px 22px;border:0;border-radius:4px;background:var(--ink);color:var(--paper-raised);
  font-family:var(--sans);font-size:13px;font-weight:500;cursor:pointer;}
.fb-send:disabled{opacity:.5;cursor:default;}
.fb-rate button:focus-visible,.fb-send:focus-visible,.fb-x:focus-visible{outline:2px solid var(--ink-soft);outline-offset:2px;}
#view-admin .bo-table td.bo-fb-msg{white-space:normal;min-width:260px;max-width:520px;line-height:1.5;}

/* The live logo (2026-10-03): every header shows the intro lockup from the
   loading screen, its three esses <use>d one by one (#sss-e1..3 in index.html)
   so they can move: they drop in when the header appears, then a small wave
   runs through them every 9 s, and again on hover. Words and divider stay still. */
.sss-live .ess{--d:0s;transform-box:fill-box;transform-origin:50% 50%;
  animation:essIn .6s cubic-bezier(.2,.7,.2,1) var(--d) both,essWave 9s ease-in-out calc(1.4s + var(--d)) infinite;}
.sss-live .ess:nth-of-type(2){--d:.09s;}
.sss-live .ess:nth-of-type(3){--d:.18s;}
:is(a.brand,.hm-brand,.mu-brand):hover .ess{animation:essHop .7s ease-in-out var(--d) 1;}
@keyframes essIn{from{opacity:0;transform:translateY(-22%);}to{opacity:1;transform:none;}}
@keyframes essWave{0%,12%,100%{transform:none;}4%{transform:translateY(-9%);}8%{transform:translateY(3%);}}
@keyframes essHop{0%,100%{transform:none;}35%{transform:translateY(-12%);}70%{transform:translateY(3%);}}
@media (prefers-reduced-motion: reduce){ .sss-live .ess{animation:none !important;} }

/* Loading on the picture (2026-10-03), replacing the Status card: the image
   under it stays faintly visible, the logo's esses draw, fill and wipe away on
   a 2.4 s loop, with one short line of what is happening and the seconds. */
#view-app .cmp-load{position:absolute;inset:0;z-index:6;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;
  background:color-mix(in srgb,var(--canvas) 80%,transparent);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);color:var(--ink);}
#view-app .cmp-load[hidden]{display:none;}
#view-app .cmp-load .ld-mark{width:92px;height:auto;overflow:visible;}
#view-app .ld-out path{fill:none;stroke:currentColor;stroke-width:.7;stroke-linejoin:miter;stroke-dasharray:1;stroke-dashoffset:1;
  animation:ldDraw 2.4s cubic-bezier(.6,0,.3,1) infinite;}
#view-app .ld-out path:nth-child(2){animation-delay:.1s;}
#view-app .ld-out path:nth-child(3){animation-delay:.2s;}
#view-app .ld-fill{fill:currentColor;animation:ldFill 2.4s cubic-bezier(.77,0,.18,1) infinite;}
@keyframes ldDraw{0%{stroke-dashoffset:1;opacity:1;}38%,82%{stroke-dashoffset:0;opacity:1;}100%{stroke-dashoffset:0;opacity:0;}}
@keyframes ldFill{0%,32%{clip-path:inset(0 100% 0 0);}56%,78%{clip-path:inset(0 0 0 0);}100%{clip-path:inset(0 0 0 100%);}}
#view-app .cmp-load .ld-msg{font-family:var(--mono);font-size:11px;letter-spacing:1.2px;text-transform:uppercase;color:var(--ink-soft);text-align:center;max-width:80%;}
#view-app .cmp-load .ld-time{font-family:var(--mono);font-size:10.5px;color:var(--ink-faint);font-variant-numeric:tabular-nums;margin-top:-8px;}
#view-app .cmp-note{position:absolute;left:12px;right:12px;bottom:12px;z-index:7;padding:10px 14px;cursor:pointer;
  background:var(--paper-raised);border:1px solid var(--hair-strong);border-radius:2px;box-shadow:0 4px 18px #0000001a;
  font-family:var(--mono);font-size:12px;line-height:1.5;color:var(--brick);}
#view-app .cmp-note[hidden]{display:none;}
@media (prefers-reduced-motion: reduce){
  #view-app .ld-out path{animation:none;stroke-dashoffset:0;}
  #view-app .ld-fill{animation:ldPulse 2s ease-in-out infinite;}
}
@keyframes ldPulse{0%,100%{opacity:.25;}50%{opacity:1;}}
/* SketchUp plugins in the account panel (2026-10-09). */
#view-app .acct-plug-list{display:flex;flex-direction:column;gap:10px;}
#view-app .acct-plug{display:flex;align-items:center;justify-content:space-between;gap:12px;}
#view-app .acct-plug span{display:flex;flex-direction:column;gap:2px;min-width:0;}
#view-app .acct-plug b{font-size:12.5px;font-weight:600;color:var(--ink);}
#view-app .acct-plug i{font-style:normal;font-size:11.5px;line-height:1.4;color:var(--ink-faint);}
#view-app .acct-plug.locked{opacity:.6;}
#view-app .acct-plug-dl{flex:none;padding:6px 10px;border:1px solid var(--ink);border-radius:2px;background:var(--ink);color:var(--paper,#fff);font-family:var(--mono);font-size:10.5px;text-transform:uppercase;letter-spacing:.3px;cursor:pointer;}
#view-app .acct-plug-dl:disabled{opacity:.5;cursor:default;}
#view-app .acct-plug-note{display:block;margin-top:10px;font-family:var(--mono);font-size:10.5px;color:var(--ink-soft);text-transform:uppercase;letter-spacing:.3px;}
