:root { --bg: #0f1216; --panel: #171c23; --border: #262d38; --text: #e6e9ef; --muted: #8b95a5; --accent: #4f9dff; --accent-hover: #3b86e6; --danger: #ff5c5c; --ok: #37c871; } * { box-sizing: border-box; } /* The hidden attribute wins over any display set by a class. */ [hidden] { display: none !important; } body { margin: 0; background: var(--bg); color: var(--text); font: 15px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; } a { color: var(--accent); text-decoration: none; } a:hover { text-decoration: underline; } header.topbar { display: flex; align-items: center; justify-content: space-between; padding: 14px 24px; border-bottom: 1px solid var(--border); background: var(--panel); } header.topbar .brand { font-weight: 600; } header.topbar .brand a.home { color: inherit; text-decoration: none; transition: color .15s; } header.topbar .brand a.home:hover { color: var(--accent); } /* A long machine name gives way: the links and the status stay in sight. */ @media (min-width: 641px) { header.topbar .brand { flex: 1 1 auto; min-width: 0; margin-right: 16px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } header.topbar nav { flex: none; white-space: nowrap; } } header.topbar nav { display: flex; align-items: center; } header.topbar nav a { margin-left: 18px; color: var(--muted); } header.topbar nav a:hover { color: var(--text); } .wrap { max-width: 960px; margin: 0 auto; padding: 24px 16px; } .center { max-width: 420px; margin: 8vh auto; padding: 0 16px; } .card { background: var(--panel); border: 1px solid var(--border); border-radius: 10px; padding: 24px; margin-bottom: 20px; } h1 { font-size: 22px; margin: 0 0 18px; } h2 { font-size: 17px; margin: 0 0 14px; } label { display: block; margin: 12px 0 4px; color: var(--muted); font-size: 13px; } input[type=text], input[type=password], input[type=number], input[type=url], input[type=email] { width: 100%; padding: 10px 12px; border-radius: 8px; border: 1px solid var(--border); background: #0c0f13; color: var(--text); font-size: 14px; } /* No native spinner: it is drawn light and breaks the dark field. */ input[type=number] { -moz-appearance: textfield; appearance: textfield; } input[type=number]::-webkit-inner-spin-button, input[type=number]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; } /* Sized and shaped like .btn.ghost so it lines up with the buttons next to it. */ select { -webkit-appearance: none; appearance: none; cursor: pointer; min-width: 180px; padding: 10px 38px 10px 14px; border-radius: 8px; border: 1px solid var(--border); color: var(--text); font: inherit; font-size: 14px; font-weight: 500; line-height: 1.5; background: #0c0f13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238b95a5' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat right 14px center; } select:hover, select:focus { border-color: var(--accent); outline: none; } select:disabled, button:disabled { opacity: .5; cursor: not-allowed; } button, .btn { display: inline-block; cursor: pointer; border: 0; border-radius: 8px; padding: 10px 16px; font-size: 14px; font-weight: 500; background: var(--accent); color: #fff; } button:hover, .btn:hover { background: var(--accent-hover); text-decoration: none; } .btn.ghost { background: transparent; border: 1px solid var(--border); color: var(--text); } .btn.danger { background: var(--danger); } /* Close buttons: red text and border on the ghost button. */ .btn.close, .btn.ghost.close { background: transparent; border: 1px solid rgba(255, 92, 92, .6); color: var(--danger); } .btn.close:hover, .btn.ghost.close:hover { background: #3a1d1d; border-color: var(--danger); } /* A close button that is just a cross: square, the height of the buttons by it. */ .btn.close.x { width: 32px; height: 32px; padding: 0; display: inline-flex; align-items: center; justify-content: center; } .btn.small.close.x { width: 28px; height: 28px; } .btn.small { padding: 6px 10px; font-size: 13px; } .btn.icon { width: 30px; height: 30px; padding: 0; display: inline-flex; align-items: center; justify-content: center; font-size: 14px; line-height: 1; text-decoration: none; } /* Name of an icon button, shown at once on hover or keyboard focus. */ [data-tip] { position: relative; } [data-tip]::after { content: attr(data-tip); position: absolute; bottom: calc(100% + 6px); left: 50%; transform: translateX(-50%); padding: 4px 8px; border-radius: 6px; white-space: nowrap; background: #0c0f13; border: 1px solid var(--border); color: var(--text); font-size: 12px; font-weight: 400; line-height: 1.4; opacity: 0; visibility: hidden; pointer-events: none; z-index: 10; } [data-tip]:hover::after, [data-tip]:focus-visible::after { opacity: 1; visibility: visible; } .err { background: #3a1d1d; border: 1px solid #6b2b2b; color: #ffb3b3; padding: 10px 12px; border-radius: 8px; margin-bottom: 14px; font-size: 14px; } table { width: 100%; border-collapse: collapse; } th, td { text-align: left; padding: 10px 8px; border-bottom: 1px solid var(--border); font-size: 14px; } th { color: var(--muted); font-weight: 500; } .nowrap { white-space: nowrap; } /* Row buttons stay on one line; the column is only as wide as they need. */ td.actions { width: 1%; white-space: nowrap; } td.actions .row { flex-wrap: nowrap; justify-content: flex-end; } .dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: 6px; } .dot.on { background: var(--ok); } .dot.off { background: #555d6a; } .muted { color: var(--muted); } code, pre { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; background: #0c0f13; border: 1px solid var(--border); border-radius: 8px; } code { padding: 2px 6px; font-size: 13px; } pre { padding: 12px; overflow-x: auto; font-size: 13px; white-space: pre-wrap; word-break: break-all; } .row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; } .spacer { flex: 1; } #terminal { width: 100%; height: calc(100vh - 150px); } /* The PIN lock of a new tab: the page stays hidden until it is unlocked. */ html.pin-wait body > :not(.pinlock) { visibility: hidden; } .pinlock { position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center; justify-content: center; padding: 16px; background: rgba(10, 13, 17, .92); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); opacity: 0; transition: opacity .3s ease; } .pinlock.on { opacity: 1; } .pinlock.done { opacity: 0; pointer-events: none; } .pin-card { width: 100%; max-width: 300px; display: flex; flex-direction: column; align-items: center; text-align: center; transform: translateY(8px) scale(.98); transition: transform .3s ease; } .pinlock.on .pin-card { transform: none; } .pinlock.done .pin-card { transform: scale(1.03); } .pin-ico { width: 54px; height: 54px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: #12202f; border: 1px solid var(--border); color: var(--accent); } .pin-title { margin: 14px 0 18px; font-size: 18px; font-weight: 600; } .pin-dots { display: flex; gap: 16px; } .pin-dots i { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--muted); transition: background .15s, border-color .15s, transform .15s; } .pin-dots i.on { background: var(--accent); border-color: var(--accent); transform: scale(1.1); } .pin-card.bad .pin-dots i.on { background: var(--danger); border-color: var(--danger); } .pin-card.bad .pin-dots { animation: pin-shake .38s ease; } @keyframes pin-shake { 20% { transform: translateX(-9px); } 40% { transform: translateX(8px); } 60% { transform: translateX(-5px); } 80% { transform: translateX(3px); } } .pin-msg { min-height: 20px; margin: 14px 0 6px; font-size: 13px; color: var(--danger); } .pin-pad { display: grid; grid-template-columns: repeat(3, 72px); gap: 14px; justify-content: center; } .pin-pad button { width: 72px; height: 72px; border-radius: 50%; padding: 0; display: inline-flex; align-items: center; justify-content: center; background: #151a21; border: 1px solid var(--border); color: var(--text); font-size: 26px; font-weight: 400; -webkit-tap-highlight-color: transparent; touch-action: manipulation; transition: background .12s, transform .08s; } .pin-pad button:hover { background: #1b222c; } .pin-pad button:active { background: #12202f; transform: scale(.94); } .pin-pad button[data-k="back"], .pin-pad button[data-k="out"] { background: transparent; border-color: transparent; color: #c26a6a; } .pin-pad button[data-k="out"] { font-size: 15px; font-weight: 500; } .pin-pad button[data-k="back"]:hover, .pin-pad button[data-k="out"]:hover { background: rgba(194, 106, 106, .1); } .pin-pad button[data-k="back"]:active, .pin-pad button[data-k="out"]:active { background: rgba(194, 106, 106, .18); } @media (max-height: 560px) { .pin-pad { grid-template-columns: repeat(3, 58px); gap: 10px; } .pin-pad button { width: 58px; height: 58px; font-size: 22px; } .pin-pad button[data-k="out"] { font-size: 13px; } } /* Dialogs shared by the pages. */ dialog { background: var(--panel); color: var(--text); border: 1px solid var(--border); border-radius: 12px; padding: 0; max-width: 900px; width: 92vw; } dialog::backdrop { background: rgba(0, 0, 0, .6); } dialog:focus { outline: none; } /* Dialogs come and go smoothly: rise and fade in, sink and fade out, the backdrop with them. Browsers without the exit transition just close. */ dialog { opacity: 0; transform: translateY(10px) scale(.97); transition: opacity .2s ease, transform .2s ease, overlay .2s ease allow-discrete, display .2s ease allow-discrete; } dialog[open] { opacity: 1; transform: none; } dialog::backdrop { opacity: 0; transition: opacity .2s ease, overlay .2s ease allow-discrete, display .2s ease allow-discrete; } dialog[open]::backdrop { opacity: 1; } @starting-style { dialog[open] { opacity: 0; transform: translateY(10px) scale(.97); } dialog[open]::backdrop { opacity: 0; } } @media (prefers-reduced-motion: reduce) { dialog, dialog::backdrop { transition: none; } } .dlg-h { padding: 14px 18px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 10px; } .dlg-b { padding: 16px 18px; } /* The account button in the top bar, and its dialog. */ header.topbar nav .acct-btn { margin-left: 22px; width: 34px; height: 34px; padding: 0; vertical-align: middle; } #acctdlg { max-width: 480px; } .acct-who { display: flex; align-items: center; gap: 12px; } .acct-ava { width: 40px; height: 40px; border-radius: 50%; background: #12202f; border: 1px solid var(--border); display: inline-flex; align-items: center; justify-content: center; font-weight: 600; color: var(--accent); flex: none; } .acct-name { font-weight: 600; } .acct-mode { font-size: 13px; } .acct h3 { font-size: 15px; margin: 18px 0 4px; } .acct-hint { font-size: 13px; margin: 0 0 10px; } .acct-keys { list-style: none; margin: 0; padding: 0; } .acct-keys li { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--border); font-size: 14px; } .acct-keys li .muted { font-size: 12px; } .acct-kicon { color: var(--accent); display: inline-flex; flex: none; } .acct-kinfo { flex: 1; min-width: 0; overflow-wrap: anywhere; } .acct-add { display: flex; gap: 8px; margin-top: 12px; } .acct-add input { flex: 1; min-width: 0; } .acct-add button { flex: none; } .acct-msg { min-height: 20px; margin: 8px 0 0; font-size: 13px; color: var(--ok); } .acct-msg.bad { color: var(--danger); } .acct-pw { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 14px 0 0; padding-top: 12px; border-top: 1px solid var(--border); color: var(--text); font-size: 14px; cursor: pointer; } .acct-pw b { display: block; font-weight: 500; } .acct-pw small { display: block; font-size: 12px; margin-top: 2px; } /* A switch: a checkbox drawn as a sliding knob. */ input.sw { -webkit-appearance: none; appearance: none; flex: none; width: 40px; height: 22px; margin: 0; border-radius: 11px; background: var(--border); position: relative; cursor: pointer; transition: background .2s; } input.sw::after { content: ""; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%; background: #fff; transition: transform .2s; } input.sw:checked { background: var(--accent); } input.sw:checked::after { transform: translateX(18px); } input.sw:disabled { opacity: .45; cursor: not-allowed; } .acct-chpw { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); } .acct-chpw summary { cursor: pointer; font-size: 14px; color: var(--text); } .acct-pwform { display: grid; gap: 8px; margin-top: 10px; } .acct-pwform button { justify-self: end; } .acct-pin { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); } .acct-pin summary { cursor: pointer; font-size: 14px; color: var(--text); } .acct-pin small { color: var(--muted); font-size: 12px; } .acct-pinform { display: grid; gap: 8px; margin-top: 10px; } .acct-pinform input:not(:placeholder-shown) { letter-spacing: .4em; font-family: ui-monospace, Menlo, monospace; } .acct-pinform .row { justify-content: flex-end; } .acct-pingrace { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 12px; font-size: 13px; color: var(--muted); } .acct-pingrace[hidden] { display: none; } .acct-pingrace select { width: auto; flex: none; } .acct-pinmsg { margin: 8px 0 0; font-size: 13px; color: var(--ok, #3fb950); min-height: 0; } .acct-pinmsg.bad { color: var(--danger); } .acct-out { margin: 0 0 0 auto; flex: none; } .acct-whotext { min-width: 0; } /* Ключи доступа below the rest, after a line. */ .acct-keysec { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border); } .acct-keysec h3 { margin-top: 0; } .acct-add input, .acct-add button { height: 40px; box-sizing: border-box; } .acct-add button { padding-top: 0; padding-bottom: 0; } /* Sign-in page. */ .or { display: flex; align-items: center; gap: 10px; color: var(--muted); font-size: 13px; margin: 18px 0 4px; } .or::before, .or::after { content: ""; flex: 1; border-top: 1px solid var(--border); } .or + .keybtn { margin-top: 10px; } .keybtn { width: 100%; display: inline-flex; align-items: center; justify-content: center; gap: 8px; } /* Phones. A table marked .stack turns each row into a block of "label: value" lines, the labels coming from data-l on the cells; a cell marked .lead goes first, as the row's title. */ @media (max-width: 640px) { header.topbar { flex-wrap: wrap; gap: 6px 12px; padding: 10px 12px; } header.topbar .brand { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; } header.topbar nav { display: flex; align-items: center; gap: 16px; max-width: 100%; overflow-x: auto; white-space: nowrap; scrollbar-width: none; } header.topbar nav::-webkit-scrollbar { display: none; } header.topbar nav > * { margin-left: 0 !important; flex: none; } /* One line: no logo, the links and the account on the right; they scroll sideways when they do not fit. */ header.topbar .brand { display: none; } header.topbar nav { flex: 1 1 auto; min-width: 0; } header.topbar nav > :first-child { margin-left: auto !important; } header.topbar nav #status { flex: 0 1 auto; min-width: 0; max-width: 30vw; } /* The links scroll; the profile button stays put at the right. */ header.topbar nav #acct { position: sticky; right: 0; z-index: 1; background: var(--panel); box-shadow: -14px 0 12px -4px var(--panel); } .acct-add { flex-wrap: wrap; } .acct-add button { width: 100%; } .acct-keys li { flex-wrap: wrap; } .wrap { padding: 16px 12px; } .card { padding: 16px; border-radius: 8px; } h1 { font-size: 20px; } code { word-break: break-all; } /* 16px and up, or iOS zooms the page into a field on focus. */ input[type=text], input[type=password], input[type=number], input[type=url], input[type=email], select, textarea { font-size: 16px; } table.stack thead { display: none; } table.stack, table.stack tbody { display: block; } table.stack tr { display: flex; flex-direction: column; gap: 2px; padding: 12px 0; border-bottom: 1px solid var(--border); } table.stack tr:first-child { padding-top: 0; } table.stack tr:last-child { border-bottom: 0; padding-bottom: 0; } table.stack td { display: flex; justify-content: flex-start; align-items: center; gap: 0; width: auto; padding: 2px 0; border: 0; text-align: right !important; overflow-wrap: anywhere; } table.stack td[data-l]::before { content: attr(data-l); flex: none; margin-right: auto; padding-right: 12px; color: var(--muted); font-size: 13px; text-align: left; } table.stack td.lead { order: -1; justify-content: flex-start; text-align: left !important; font-size: 16px; font-weight: 600; } table.stack td.actions { width: auto; padding-top: 8px; justify-content: flex-start; } table.stack td:empty { display: none; } } /* Scrollbars as on a Mac: thin, no track, seen only while scrolling or with the pointer at the edge, then fading out (scroll.js sets --sb from 0 to 1). Nothing to scroll, nothing shown. */ * { --sb: 0; } ::-webkit-scrollbar { width: 8px; height: 8px; background: transparent; } ::-webkit-scrollbar-track, ::-webkit-scrollbar-corner { background: transparent; } ::-webkit-scrollbar-thumb { border-radius: 4px; border: 2px solid transparent; background-clip: padding-box; background-color: rgba(62, 68, 78, calc(var(--sb) * .9)); } ::-webkit-scrollbar-thumb:hover { background-color: rgba(80, 87, 98, calc(var(--sb) * .95)); } @supports not selector(::-webkit-scrollbar) { * { scrollbar-width: thin; scrollbar-color: rgba(62, 68, 78, calc(var(--sb) * .9)) transparent; } } /* The one-page shell: the header's current view. */ header.topbar nav a.on { color: var(--text); } /* ---- The workspace (Терминал view) ---- */ #status.st-ok { color:#37c871; } #status.st-err { color:#ff5c5c; } #status { display:inline-block; vertical-align:middle; margin-left:18px; min-width:0; max-width:32vw; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } /* SSH: the badge on a pane, and the dialog to connect. */ .badge-ssh { flex:none; font-size:10px; font-weight:600; letter-spacing:.04em; padding:1px 5px; border-radius:4px; background:#12202f; color:var(--accent); border:1px solid #1f3a57; } dialog.sshdlg { max-width:460px; } .sshdlg .dlg-h .ico { color:var(--accent); display:inline-flex; } .sshdlg .dlg-b { display:grid; gap:12px; } .sshdlg .f { display:grid; gap:5px; margin:0; } .sshdlg .f > span { font-size:12px; color:var(--muted); } .sshdlg .frow { display:flex; gap:10px; align-items:end; } .sshdlg .frow .grow { flex:1; min-width:0; } .sshdlg .frow .port { width:92px; flex:none; } .sshdlg input, .sshdlg select, .sshdlg textarea { width:100%; box-sizing:border-box; } .sshdlg textarea { font:12px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; resize:vertical; min-height:90px; background:var(--bg, #0c0f13); color:var(--text); border:1px solid var(--border); border-radius:8px; padding:8px 10px; } .sshdlg textarea:focus { border-color:var(--accent); outline:none; } .sshdlg .auth { display:grid; gap:8px; animation:auth-in .18s ease; } .sshdlg .auth[hidden] { display:none; } @keyframes auth-in { from { opacity:0; transform:translateY(-4px); } } .sshdlg p.auth { margin:0; font-size:13px; } .sshdlg .keyname { font-size:12px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; align-self:center; } .sshdlg .seg { display:flex; padding:3px; gap:3px; background:#0c0f13; border:1px solid var(--border); border-radius:9px; } .sshdlg .seg button { flex:1; padding:6px 8px; font-size:13px; background:transparent; color:var(--muted); border:0; border-radius:6px; transition:background .18s, color .18s; } .sshdlg .seg button:hover { color:var(--text); } .sshdlg .seg button.on { background:#12202f; color:var(--text); box-shadow:inset 0 0 0 1px #1f3a57; } .sshdlg .sshmsg { margin:0; min-height:18px; font-size:13px; color:var(--danger); } .sshdlg .sshmsg.going { color:var(--muted); } .sshdlg .row.end { display:flex; justify-content:flex-end; gap:8px; } @media (max-width:520px) { .sshdlg .seg button { font-size:12px; padding:6px 4px; } } /* The page is the terminal: bar, tabs, then the terminals to the bottom. */ /* The shell: the header, then the view to the bottom. Машины and Настройки scroll inside it; the terminal view fills it. */ body.app { height:100vh; display:flex; flex-direction:column; overflow:hidden; } body.app main#app { flex:1; min-height:0; overflow:auto; } .view-term { height:100%; display:flex; flex-direction:column; overflow:hidden; } header.topbar { flex:none; } /* Tabs: folded away while there is one, sliding in with the second. */ .tabs { flex:none; display:flex; align-items:flex-end; gap:4px; padding:0 16px; max-height:0; opacity:0; overflow:hidden; transition:max-height .28s ease, opacity .22s ease, padding .28s ease; } body.multi .tabs, body.multi-hint .tabs { max-height:48px; opacity:1; padding-top:10px; } .tablist { position:relative; display:flex; gap:4px; min-width:0; overflow-x:auto; scrollbar-width:none; } .tablist::-webkit-scrollbar { display:none; } .tab { display:flex; align-items:center; gap:8px; flex:none; min-width:0; height:34px; padding:0 6px 0 12px; box-sizing:border-box; border:1px solid var(--border); border-bottom:0; border-radius:9px 9px 0 0; background:#11151b; color:var(--muted); font-size:13px; user-select:none; cursor:default; overflow:hidden; white-space:nowrap; transition:background .18s, color .18s, width .22s ease, max-width .22s ease, opacity .2s, padding .22s ease; } .tab:hover { color:var(--text); background:#151a21; } .tab.on { background:var(--panel); color:var(--text); } .tab.enter, .tab.leave { max-width:0; opacity:0; padding:0; } .tab.dragging { opacity:.3; } .tab-title { flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0; } /* What a tab holds, just before its name. */ .tab-kinds { display:inline-flex; gap:3px; flex:none; color:var(--muted); opacity:.75; } .tab.on .tab-kinds { color:var(--text); opacity:.6; } .tab-kinds svg { display:block; } .tab-edit { width:auto; min-width:60px; max-width:150px; height:24px; padding:2px 6px; font-size:13px; } .tab-x, .pane-x { flex:none; width:20px; height:20px; padding:0; display:inline-flex; align-items:center; justify-content:center; background:transparent; border:0; border-radius:5px; color:var(--muted); opacity:.6; transition:opacity .15s, background .15s, color .15s; } .tab-x:hover, .pane-x:hover { opacity:1; background:#3a1d1d; color:var(--danger); } .tab-add { flex:none; width:34px; height:34px; padding:0; display:inline-flex; align-items:center; justify-content:center; border:1px solid var(--border); border-bottom:0; border-radius:9px 9px 0 0; background:transparent; color:var(--muted); font-size:18px; line-height:1; transition:background .18s, color .18s; } .tab-add:hover { background:#151a21; color:var(--text); } .tabmark { position:absolute; top:6px; bottom:0; width:3px; margin-left:-1px; border-radius:2px; background:var(--accent); pointer-events:none; transition:left .12s ease; } .tabmark[hidden] { display:none; } .dot { width:7px; height:7px; border-radius:50%; background:#555d6a; flex:none; transition:background .3s; } .dot.ok { background:var(--ok); } .dot.err { background:var(--danger); padding:0; border:0; margin:0; } .dot.wait { background:#e6a23c; } /* The terminals' area. */ .area { position:relative; flex:1; min-height:0; margin:12px 16px 16px; border:1px solid var(--border); border-radius:10px; background:#0c0f13; overflow:hidden; } body.multi .area { margin-top:0; border-top-left-radius:0; } .float-add { position:absolute; top:8px; right:10px; z-index:6; width:28px; height:28px; padding:0; display:inline-flex; align-items:center; justify-content:center; font-size:17px; line-height:1; color:var(--text); background:transparent; border:1px solid var(--border); opacity:.28; transition:opacity .25s ease, background .25s ease, top .25s ease, right .25s ease; } /* One tab split in panes: below the panes' title bars, clear of their close buttons. */ body.split1 .float-add { top:42px; right:16px; } .float-add:hover, .float-add:focus-visible { opacity:1; background:var(--panel); } body.multi .float-add { opacity:0; pointer-events:none; } .float-add { transition:opacity .25s ease, background .25s ease, top .3s ease, right .3s ease, width .3s ease, height .3s ease, border-radius .3s ease; } /* In a file pane's top bar: a button like its neighbours. */ .float-add.in-files { position:static; z-index:auto; width:34px; height:34px; font-size:19px; opacity:1; border-radius:8px; } .float-add.in-files:hover { background:#151a21; } .tabview { position:absolute; inset:8px; opacity:0; visibility:hidden; transform:translateY(4px) scale(.995); transition:opacity .22s ease, transform .22s ease, visibility 0s .22s; } /* A tab fading out takes no pointer: a drop goes to the tab coming in. */ .tabview:not(.on) { pointer-events:none; } .tabview.on { opacity:1; visibility:visible; transform:none; transition:opacity .22s ease, transform .22s ease, visibility 0s; } .pane { position:absolute; display:flex; flex-direction:column; border-radius:7px; overflow:hidden; transition:left .26s ease, top .26s ease, width .26s ease, height .26s ease, border-color .2s; } .tabview.instant .pane, .pane.instant { transition:none; } .pane-head { display:none; } .tabview.split .pane { border:1px solid var(--border); } .tabview.split .pane.active { border-color:rgba(79,157,255,.7); } .tabview.split .pane-head { display:flex; align-items:center; gap:7px; flex:none; height:26px; padding:0 4px 0 9px; background:#11151b; border-bottom:1px solid var(--border); font-size:12px; color:var(--muted); cursor:grab; user-select:none; } .tabview.split .pane.active .pane-head { color:var(--text); } .pane-title { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } .pane-body { flex:1; min-height:0; padding:4px 0 0 4px; } /* A lone terminal's scrollbar sits in the margin by the frame, right of the corner "+", not over the text. */ body:not(.touch) .tabview:not(.split) .pane { overflow:visible; } body:not(.touch) .tabview:not(.split) .xterm .xterm-viewport { right:-7px; } .pane.dragging { opacity:.45; } .gutter { position:absolute; z-index:3; } .gutter.c { cursor:col-resize; } .gutter.r { cursor:row-resize; } .gutter::after { content:""; position:absolute; inset:0; border-radius:3px; background:var(--accent); opacity:0; transition:opacity .15s; } .gutter:hover::after, .gutter.drag::after { opacity:.55; } .hint { position:absolute; z-index:5; display:flex; align-items:center; justify-content:center; text-align:center; padding:8px; border:2px solid var(--accent); border-radius:9px; background:rgba(79,157,255,.14); color:var(--text); font-size:13px; pointer-events:none; transition:left .14s ease, top .14s ease, width .14s ease, height .14s ease; } .hint.bad { border-color:var(--danger); background:rgba(255,92,92,.12); } .hint[hidden] { display:none; } .drag-ghost { position:fixed; left:0; top:0; z-index:60; pointer-events:none; padding:7px 12px; border:1px solid var(--accent); border-radius:8px; background:var(--panel); color:var(--text); font-size:13px; white-space:nowrap; box-shadow:0 12px 30px rgba(0,0,0,.55); transition:transform .04s linear; } .drag-ghost[data-hint]:not([data-hint=""])::after { content:attr(data-hint); display:block; margin-top:3px; font-size:11px; color:var(--muted); } body.dnd, body.dnd * { cursor:grabbing !important; } .tab, .tab *, .pane-head, .pane-head *, .ai-head, .ai-head *, .ai-fab, .fp-list tr, .fp-list tr * { -webkit-user-drag:none; } /* Files dragged out of a pane: the target's outline, and the small window where they were dropped. */ .hint.drophint { position:fixed; z-index:55; transition:none; } .dropdlg { position:fixed; z-index:56; width:300px; max-width:calc(100vw - 16px); padding:12px; display:flex; flex-direction:column; gap:8px; background:var(--panel); border:1px solid var(--border); border-radius:10px; box-shadow:0 14px 36px rgba(0,0,0,.55); font-size:13px; } .dropdlg[hidden] { display:none; } .dropdlg .dd-what { font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } .dropdlg .dd-to { overflow-wrap:anywhere; } .dropdlg .dd-c { display:flex; align-items:center; gap:6px; } .dropdlg .mv-prog { margin-top:0; } .dropdlg .dd-btns { display:flex; gap:6px; justify-content:flex-end; } /* Dragging rows: no text gets selected meanwhile (it can be selected otherwise). */ body.dnd, body.dnd * { user-select:none !important; -webkit-user-select:none !important; } .empty { position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:12px; color:var(--muted); } .empty[hidden] { display:none; } /* The "+" menu: a terminal here, or on another machine. */ .addmenu { position:fixed; z-index:50; min-width:220px; max-width:340px; max-height:70vh; overflow:auto; padding:6px; background:var(--panel); border:1px solid var(--border); border-radius:10px; box-shadow:0 14px 36px rgba(0,0,0,.55); animation:menu-in .14s ease; } .addmenu[hidden] { display:none; } @keyframes menu-in { from { opacity:0; transform:translateY(-4px); } } .addmenu .item { display:flex; align-items:center; gap:10px; width:100%; padding:8px 10px; border-radius:7px; text-align:left; background:transparent; color:var(--text); font-weight:400; } .addmenu .item:hover:not(:disabled) { background:#12202f; } .addmenu .item:disabled { opacity:.45; cursor:not-allowed; } .addmenu .item b { display:block; font-weight:500; font-size:14px; } .addmenu .item small { display:block; color:var(--muted); font-size:12px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:240px; } .addmenu .ico { width:22px; text-align:center; color:var(--accent); font-size:16px; flex:none; } .addmenu .chev { margin-left:auto; color:var(--muted); transition:transform .2s ease; } .addmenu .item[aria-expanded="true"] .chev { transform:rotate(90deg); } .addmenu .mlist { display:grid; grid-template-rows:0fr; margin-top:0; padding-top:0; border-top:0 solid transparent; opacity:0; transition:grid-template-rows .22s ease, margin-top .22s ease, padding-top .22s ease, border-width .22s ease, opacity .18s ease; } .addmenu .mlist.open { grid-template-rows:1fr; margin-top:4px; padding-top:4px; border-top:1px solid var(--border); opacity:1; } .addmenu .mlist-inner { min-height:0; overflow:hidden; } .addmenu .mlist-content { min-height:0; } /* Touch screens: the page is as tall as what the on-screen keyboard leaves, with a row of extra keys at the bottom, the way Termux does it. Tabs are smaller, and there is no splitting. */ .keys { display:none; } body.touch { position:fixed; left:0; right:0; top:0; height:100%; } body.touch .area { margin:6px; } body.touch.multi .area { margin-top:0; } body.touch .tabs { padding-left:6px; padding-right:6px; } body.touch.multi .tabs { padding-top:6px; max-height:38px; } body.touch .tab { height:28px; font-size:12px; padding:0 2px 0 8px; gap:6px; } body.touch .tab-add { width:30px; height:28px; } body.touch .tabview { inset:4px; } body.touch .tabview:not(.split) .fpane-body { padding-left:10px; padding-right:10px; } body.touch:not(.term-active) .keys { display:none; } body.touch.term-active .keys { display:grid; grid-template-columns:repeat(7, 1fr); gap:4px; flex:none; padding:4px 4px calc(4px + env(safe-area-inset-bottom)); background:var(--panel); border-top:1px solid var(--border); } .keys button { height:38px; padding:0; border:1px solid var(--border); border-radius:6px; background:#0c0f13; color:var(--text); font:500 13px ui-monospace, SFMono-Regular, Menlo, monospace; touch-action:manipulation; user-select:none; -webkit-user-select:none; -webkit-tap-highlight-color:transparent; } .keys button:active { background:var(--border); } .keys button.on { background:var(--accent); border-color:var(--accent); color:#fff; } .keys button.lock { background:var(--accent); border-color:#fff; color:#fff; } /* Copy and paste on touch screens: a long press selects, this menu acts. */ .selmenu { position:fixed; left:50%; transform:translateX(-50%); z-index:20; display:flex; gap:7px; padding:5px; background:var(--panel); border:1px solid var(--border); border-radius:8px; box-shadow:0 6px 24px rgba(0,0,0,.5); } .selmenu[hidden] { display:none; } .selmenu button { width:42px; height:42px; padding:0; display:inline-flex; align-items:center; justify-content:center; touch-action:manipulation; -webkit-tap-highlight-color:transparent; } body.touch .area { -webkit-user-select:none; user-select:none; -webkit-touch-callout:none; } #status:empty { display:none; } /* ---- File panes and their dialogs ---- */ .fm-bar { display:flex; gap:8px; align-items:center; flex-wrap:wrap; margin-bottom:12px; } /* Normalize buttons and the file