Docker Build / docker (push) Successful in 41s
## Summary
- Rebuilds the master's **Services** and **Agents** pages per the "Ledger" design: sidebar shell with live nav counts, machine filter and Live indicator; dark/light theme (follows the OS, toggle overrides); embedded Schibsted Grotesk and JetBrains Mono (fontsource 5.3.0, OFL).
- Services: Swarm grouped per cluster with its machines under it, offline banner ("Agent offline since HH:MM", browser-local), Simple Icons derived from the image ref (letter circle fallback), existing badges and mode switch restyled, client-side search, machine filter and Icon / Icon + text / Text toggle.
- Agents: Online / Offline / Never connected / Revoked, master version in the header with outdated agent versions marked, revoke via popover instead of `confirm()`, "Add agent" as a three-step panel that completes when the agent says hello (SSE `agents`).
- Store: migration `0005_agent_cluster.sql` persists each agent's `swarm_cluster_id` (needed to list a cluster's managers and its offline state after a master restart).
- SSE keepalive is now a named `ping` event so the Live indicator also shows the connection is alive. `revokeAgent` now publishes `agents`.
Closes #18
## Notes
- Workers never report a cluster id (Docker only sets `Swarm.Cluster` on managers), so their Swarm role shows "—". Fixing that needs a new hello field.
- Master binary grows by about 2.16 MB, almost all Simple Icons 16.35.0 path data (gzipped JSON, regenerated with `go generate ./internal/master/web/icons`). The agent binary does not import it.
- Jobs, Job and Service detail pages only get the new shell; their own artboards are out of scope, as are Update buttons, Update all, header stats and the drift badge.
- Unchanged: CSRF on every POST, `Cache-Control: no-store` on the token response, 401 + `HX-Redirect` without a session, no `template.HTML` on agent- or user-controlled values, no Node build step.
## Test plan
- [x] `go test ./...`, `go test -race ./internal/master/...`
- [x] `go test -tags integration ./internal/master/store/` against Postgres 17
- [x] `docker build`
- [x] Browser check on fake data: both themes, 1440 px and 390 px, add-agent flow through connect
- [ ] Services grouping and icons against real stacks and a real Swarm cluster
- [ ] "Agent offline since" after stopping an agent
- [ ] Theme toggle, search and machine filter across reloads and mode changes
- [ ] Add agent end to end with a real agent, Copy buttons
- [ ] Phone at 390 px
https://claude.ai/code/session_01GAZsp3P7Qto6JnhipxWUJQ
Reviewed-on: #29
221 lines
16 KiB
CSS
221 lines
16 KiB
CSS
@font-face{font-family:'Schibsted Grotesk';font-style:normal;font-weight:400 900;font-display:swap;src:url(/static/schibsted-grotesk-latin-wght-normal.woff2) format('woff2')}
|
|
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:100 800;font-display:swap;src:url(/static/jetbrains-mono-latin-wght-normal.woff2) format('woff2')}
|
|
|
|
/* Ledger. Tokens live on <html class="app">; a script in <head> adds t-light before first paint. */
|
|
.app{color-scheme:dark;--bg:#0f1218;--side:#0b0d12;--card:#151a23;--hover:#171c26;--line:#232a37;--line2:#323a4a;--fg:#e7eaf1;--mut:#a0a9bb;--dim:#828ba0;--blue:#74a8ff;--bluebg:rgba(116,168,255,.12);--orange:#ffa262;--orangebg:rgba(255,162,98,.12);--green:#62d4a0;--red:#ff7d88;--redbg:rgba(255,125,136,.10);--violet:#c5a9ff;--violetbg:rgba(197,169,255,.12);--l0:#74a8ff;--l1:#c5a9ff;--l2:#5fd0e0;--l3:#f29bd0;--onblue:#06142e;--term:#080a0e;--shadow:0 12px 32px rgba(0,0,0,.5);--sans:'Schibsted Grotesk',system-ui,-apple-system,sans-serif;--mono:'JetBrains Mono',ui-monospace,Menlo,monospace;min-height:100vh;background:var(--bg);color:var(--fg);font:14px/1.45 var(--sans)}
|
|
.app.t-light{color-scheme:light;--bg:#f7f8fa;--side:#eceff4;--card:#ffffff;--hover:#f0f2f6;--line:#dde1e8;--line2:#c6ccd7;--fg:#121620;--mut:#4b5468;--dim:#5c6579;--blue:#2156c8;--bluebg:rgba(33,86,200,.08);--orange:#a84b0b;--orangebg:rgba(200,96,20,.09);--green:#177649;--red:#b32a3b;--redbg:rgba(179,42,59,.07);--violet:#6943c1;--violetbg:rgba(105,67,193,.08);--l0:#2156c8;--l1:#6943c1;--l2:#0b7285;--l3:#a8306f;--onblue:#ffffff;--term:#121620;--shadow:0 12px 32px rgba(20,30,50,.16)}
|
|
*{box-sizing:border-box}
|
|
body{margin:0}
|
|
[hidden]{display:none !important}
|
|
a{color:var(--blue)}a:hover{color:var(--fg)}
|
|
button,input{font-family:inherit}
|
|
:focus-visible{outline:2px solid var(--blue);outline-offset:2px}
|
|
|
|
/* Shell */
|
|
.shell{display:flex;flex-wrap:wrap;min-height:100vh}
|
|
.side{flex:0 0 232px;background:var(--side);border-right:1px solid var(--line);display:flex;flex-direction:column;gap:28px;padding:20px 14px}
|
|
.brand{display:flex;align-items:center;gap:10px;font:700 15px var(--sans);letter-spacing:-.01em;padding:0 8px;color:var(--fg);text-decoration:none}
|
|
.brand svg{color:var(--blue)}
|
|
.snav{display:flex;flex-direction:column;gap:2px}
|
|
.snav a,.mlist a{display:flex;align-items:center;gap:10px;padding:7px 8px;border-radius:6px;color:var(--mut);text-decoration:none;font-weight:500}
|
|
.snav a:hover,.mlist a:hover{background:var(--hover);color:var(--fg)}
|
|
.snav a.cur,.mlist a.cur{background:var(--card);color:var(--fg);box-shadow:inset 0 0 0 1px var(--line)}
|
|
.snav .n,.mlist .n{margin-left:auto;font:12px var(--mono);color:var(--dim)}
|
|
.slabel{font:600 11px var(--sans);text-transform:uppercase;letter-spacing:.08em;color:var(--dim);padding:0 8px 6px}
|
|
.mlist{display:flex;flex-direction:column;gap:2px}
|
|
.mlist .sub{padding-left:22px;font-size:13px}
|
|
.mdot{width:7px;height:7px;border-radius:50%;background:var(--green);flex:none}
|
|
.mdot.off{background:transparent;box-shadow:inset 0 0 0 1.5px var(--red)}
|
|
.sw-icon{width:7px;height:7px;flex:none;border-radius:2px;box-shadow:inset 0 0 0 1.5px var(--blue)}
|
|
.livebox{margin-top:auto;padding:10px 8px;border-top:1px solid var(--line);font-size:12px;color:var(--mut);display:flex;flex-direction:column;gap:2px}
|
|
.livebox b{display:flex;align-items:center;gap:8px;color:var(--fg);font-weight:600}
|
|
.pulse{width:8px;height:8px;border-radius:50%;background:var(--green);animation:breathe 2.4s ease-in-out infinite}
|
|
.livebox.down .pulse{background:var(--red);animation:none}
|
|
@keyframes breathe{0%{box-shadow:0 0 0 0 rgba(98,212,160,.45)}50%{box-shadow:0 0 0 5px rgba(98,212,160,0)}}
|
|
.sfoot{display:flex;flex-wrap:wrap;align-items:center;gap:4px 8px}
|
|
.sfoot form{margin:0}
|
|
.work{flex:999 1 640px;min-width:0;display:flex;flex-wrap:wrap}
|
|
.main{flex:999 1 520px;min-width:0;padding:28px 36px 48px;container-type:inline-size}
|
|
.sprite{position:absolute;width:0;height:0;overflow:hidden}
|
|
|
|
/* Page header, buttons */
|
|
.hdr{display:flex;flex-wrap:wrap;align-items:flex-end;gap:12px 24px;margin-bottom:18px}
|
|
.hdr h1{margin:0;font:700 34px/1 var(--sans);letter-spacing:-.025em}
|
|
.hdr .sub{font-size:14px;color:var(--mut)}
|
|
.hdr .end{margin-left:auto}
|
|
.btn,button:not([class]){height:32px;padding:0 14px;border-radius:6px;border:1px solid var(--line2);background:var(--card);color:var(--fg);font:600 13px var(--sans);cursor:pointer;display:inline-flex;align-items:center;gap:8px;white-space:nowrap;text-decoration:none}
|
|
.btn:hover,button:not([class]):hover{border-color:var(--dim);color:var(--fg)}
|
|
button:not([class]):disabled{opacity:.5;cursor:not-allowed}
|
|
.btn.pri,.btn.pri:hover{background:var(--blue);border-color:var(--blue);color:var(--onblue)}
|
|
.btn.sm{height:28px;padding:0 10px;font-size:12.5px}
|
|
.btn.quiet{border-color:transparent;background:transparent;color:var(--mut)}
|
|
.btn.quiet:hover{color:var(--fg)}
|
|
.btn.red{color:var(--red);border-color:var(--line2);background:transparent}
|
|
.btn.redfill,.btn.redfill:hover{background:var(--red);border-color:var(--red);color:#1d0408}
|
|
summary.btn{list-style:none}
|
|
summary.btn::-webkit-details-marker{display:none}
|
|
|
|
/* Overview */
|
|
.tools{display:flex;flex-wrap:wrap;align-items:center;gap:10px 18px;padding:12px 0;border-top:1px solid var(--line);border-bottom:1px solid var(--line);margin-bottom:8px}
|
|
.search{flex:0 1 340px;min-width:220px;display:flex;align-items:center;gap:8px;height:34px;padding:0 12px;border-radius:6px;background:var(--card);border:1px solid var(--line2);color:var(--dim)}
|
|
.search input{flex:1;min-width:0;border:0;outline:0;background:transparent;color:var(--fg);font-size:14px}
|
|
.search input::placeholder{color:var(--dim)}
|
|
.tools .end{margin-left:auto;display:flex;align-items:center;gap:10px}
|
|
.tools .lbl{font-size:12.5px;color:var(--dim)}
|
|
.seg{display:inline-flex;padding:2px;border-radius:7px;background:var(--card);box-shadow:inset 0 0 0 1px var(--line2)}
|
|
.seg button{display:inline-flex;align-items:center;gap:6px;height:28px;padding:0 10px;border:0;border-radius:5px;background:transparent;color:var(--mut);font:500 12.5px var(--sans);cursor:pointer}
|
|
.seg button:hover{color:var(--fg)}
|
|
.seg button[aria-pressed=true]{background:var(--hover);color:var(--fg);box-shadow:inset 0 0 0 1px var(--line2)}
|
|
.grp{margin-top:28px}
|
|
.gh{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 12px;padding-bottom:8px;border-bottom:2px solid var(--fg)}
|
|
.gh h2{margin:0;font:700 20px var(--sans);letter-spacing:-.015em}
|
|
.gh .kind{font:600 11px var(--sans);text-transform:uppercase;letter-spacing:.08em;color:var(--mut)}
|
|
.gh .meta{font-size:13px;color:var(--dim)}
|
|
.gh .warn{font-size:13px;color:var(--red);font-weight:500}
|
|
.gh .count{margin-left:auto;font:12px var(--mono);color:var(--dim)}
|
|
.grp.offline .gh{border-bottom-color:var(--line2)}
|
|
.grp.offline .row{color:var(--dim)}
|
|
.row{position:relative;display:grid;grid-template-columns:minmax(200px,1.4fr) minmax(200px,2fr) 130px minmax(160px,1.5fr) minmax(104px,150px);align-items:center;column-gap:18px;min-height:42px;padding:0 6px;border-bottom:1px solid var(--line)}
|
|
.row:hover{background:var(--hover)}
|
|
.nm{display:flex;align-items:center;gap:8px;min-width:0}
|
|
.nm a{display:inline-flex;align-items:center;gap:10px;min-width:0;color:var(--fg);font-weight:600;text-decoration:none;white-space:nowrap}
|
|
.nm a:hover .nmt{text-decoration:underline;text-underline-offset:3px}
|
|
.nm .nmt{overflow:hidden;text-overflow:ellipsis}
|
|
.nm .stk{font-size:12.5px;color:var(--dim);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
.ico{flex:none;display:grid;place-items:center;width:26px;height:26px;border-radius:7px;background:var(--card);box-shadow:inset 0 0 0 1px var(--line2);color:var(--fg)}
|
|
.ico svg{fill:currentColor}
|
|
.ltr{flex:none;display:grid;place-items:center;width:26px;height:26px;border-radius:50%;font:700 12.5px var(--sans)}
|
|
.lc-0{color:var(--l0);background:color-mix(in srgb,var(--l0) 18%,transparent)}
|
|
.lc-1{color:var(--l1);background:color-mix(in srgb,var(--l1) 18%,transparent)}
|
|
.lc-2{color:var(--l2);background:color-mix(in srgb,var(--l2) 18%,transparent)}
|
|
.lc-3{color:var(--l3);background:color-mix(in srgb,var(--l3) 18%,transparent)}
|
|
.grp.offline .ico,.grp.offline .ltr{opacity:.65}
|
|
.d-icon .nmt,.d-icon .stk{display:none}
|
|
.d-text .ico,.d-text .ltr{display:none}
|
|
.ver{display:flex;min-width:0;font:12.5px var(--mono);color:var(--dim);white-space:nowrap}
|
|
.ver .r{overflow:hidden;text-overflow:ellipsis;min-width:0}
|
|
.ver .t{flex:none}
|
|
.ver b{color:var(--fg);font-weight:500}
|
|
.st{display:inline-flex;align-items:center;gap:8px;font-size:13px;font-weight:500;white-space:nowrap}
|
|
.st svg{flex:none}
|
|
.st circle{fill:currentColor}
|
|
.s-running{color:var(--green)}.s-degraded{color:var(--orange)}.s-stopped{color:var(--dim)}.s-unknown{color:var(--dim)}
|
|
.s-stopped circle,.s-unknown circle{fill:none}
|
|
.flags{display:flex;flex-wrap:wrap;gap:6px}
|
|
.tag{font:500 12px/20px var(--sans);padding:0 8px;border-radius:999px;white-space:nowrap}
|
|
.tag em{font:500 11.5px var(--mono);font-style:normal;margin-left:6px}
|
|
.tag em:empty{display:none}
|
|
.f-up{color:var(--orange);background:var(--orangebg)}
|
|
.f-err{color:var(--red);background:var(--redbg)}
|
|
.f-notag{color:var(--mut);box-shadow:inset 0 0 0 1px var(--line2)}
|
|
.f-out{color:var(--dim);box-shadow:inset 0 0 0 1px var(--line)}
|
|
.mode{display:flex;flex-direction:column;align-items:flex-start;gap:2px}
|
|
.mode form{margin:0;display:flex;flex-direction:column;align-items:flex-start;gap:2px}
|
|
.sw{display:inline-flex;align-items:center;gap:8px;padding:0;border:0;background:none;font:inherit;font-size:13px;color:var(--mut);cursor:pointer}
|
|
.sw i{flex:none;width:30px;height:18px;border-radius:999px;background:var(--line2);position:relative;transition:background .15s}
|
|
.sw i::after{content:"";position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;background:var(--fg);transition:transform .15s}
|
|
.sw[aria-checked=true] i{background:var(--blue)}
|
|
.sw[aria-checked=true] i::after{transform:translateX(12px);background:var(--onblue)}
|
|
.sw[aria-checked=true]{color:var(--blue);font-weight:600}
|
|
.sw:disabled{opacity:.55;cursor:not-allowed}
|
|
.na{font-size:12.5px;color:var(--dim)}
|
|
.nomatch{padding:24px 6px;color:var(--dim)}
|
|
|
|
/* Agents */
|
|
.hdr.ruled{padding-bottom:16px;border-bottom:2px solid var(--fg);margin-bottom:0}
|
|
.agent{position:relative;display:grid;grid-template-columns:minmax(150px,1fr) 130px 90px 110px 120px 84px;grid-template-areas:"id st ver mgr last act";align-items:center;column-gap:18px;padding:16px 6px;border-bottom:1px solid var(--line)}
|
|
.agent .id{grid-area:id}
|
|
.agent .id b{display:block;font:700 16px var(--sans)}
|
|
.agent .id span{font:12.5px var(--mono);color:var(--dim)}
|
|
.agent .stc{grid-area:st}.agent .ver{grid-area:ver;display:block;font:13px var(--mono);color:var(--mut)}.agent .mgr{grid-area:mgr;font-size:13px;color:var(--mut)}.agent .last{grid-area:last;font-size:13px;color:var(--mut)}.agent .act{grid-area:act;display:flex;justify-content:flex-end}
|
|
.agent .k{display:block;font:600 10.5px var(--sans);text-transform:uppercase;letter-spacing:.08em;color:var(--dim);margin-bottom:2px}
|
|
.agent .ver.old{color:var(--orange)}
|
|
.agent .st{font-size:13.5px;font-weight:600}
|
|
.agent .st i{width:8px;height:8px;border-radius:50%;background:currentColor}
|
|
.a-on{color:var(--green)}.a-off{color:var(--red)}.a-never,.a-revoked{color:var(--dim)}
|
|
.a-off i,.a-never i{background:transparent !important;box-shadow:inset 0 0 0 1.5px currentColor}
|
|
/* After the .agent rules: a container query adds no specificity, so it must come later to win. */
|
|
@container (max-width:760px){
|
|
.agent{grid-template-columns:1fr auto;grid-template-areas:"id st" "ver mgr" "last act";row-gap:10px}
|
|
}
|
|
.pop{position:absolute;right:6px;top:calc(100% - 8px);z-index:5;width:360px;padding:14px 16px;border-radius:8px;background:var(--card);border:1px solid var(--line2);box-shadow:var(--shadow);font-size:13px;color:var(--mut)}
|
|
.pop::before{content:"";position:absolute;top:-6px;right:30px;width:10px;height:10px;background:var(--card);border-left:1px solid var(--line2);border-top:1px solid var(--line2);transform:rotate(45deg)}
|
|
.pop h4{margin:0 0 4px;font:700 14px var(--sans);color:var(--fg)}
|
|
.pop p{margin:0 0 12px}
|
|
.pop .acts{display:flex;gap:8px;justify-content:flex-end;margin:0}
|
|
.drawer{flex:1 1 440px;min-width:0;max-width:520px;border-left:1px solid var(--line2);background:var(--card);padding:28px 28px 40px;display:flex;flex-direction:column;gap:18px}
|
|
.dh{display:flex;align-items:center;gap:12px}
|
|
.dh h2{margin:0;font:700 20px var(--sans);letter-spacing:-.015em}
|
|
.dh .x{margin-left:auto}
|
|
.steps{display:flex;gap:8px;font-size:12.5px;color:var(--dim)}
|
|
.steps span{display:flex;align-items:center;gap:6px}
|
|
.steps b{display:inline-grid;place-items:center;width:20px;height:20px;border-radius:50%;font:600 11px var(--mono);box-shadow:inset 0 0 0 1px var(--line2)}
|
|
.steps .done{color:var(--green)}.steps .done b{box-shadow:none;background:var(--green);color:var(--bg)}
|
|
.steps .now{color:var(--fg)}.steps .now b{box-shadow:none;background:var(--blue);color:var(--onblue)}
|
|
.steps .sep{width:16px;height:1px;background:var(--line2)}
|
|
.dform{display:flex;flex-direction:column;gap:18px;margin:0}
|
|
.field{display:flex;flex-direction:column;gap:6px;font-size:13.5px;color:var(--mut)}
|
|
.field input{height:36px;padding:0 12px;border-radius:6px;border:1px solid var(--line2);background:var(--bg);color:var(--fg);font:500 14px var(--mono)}
|
|
.field small{color:var(--dim);font-size:12.5px}
|
|
.namebox{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-radius:6px;background:var(--bg);font-size:13.5px}
|
|
.namebox code{font:500 14px var(--mono);color:var(--fg)}
|
|
.notice{display:flex;gap:10px;padding:12px 14px;border-radius:6px;background:var(--orangebg);color:var(--fg);font-size:13.5px}
|
|
.notice svg{flex:none;color:var(--orange);margin-top:1px}
|
|
.code{border-radius:6px;background:var(--term);overflow:hidden}
|
|
.code-h{display:flex;align-items:center;justify-content:space-between;padding:6px 8px 6px 12px;border-bottom:1px solid #1d2230;font:12px var(--mono);color:#8d96a8}
|
|
.code pre{margin:0;padding:12px;border:0;color:#cdd3df;font:12px/1.6 var(--mono);overflow-x:auto}
|
|
.secret{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:8px 12px;font-size:13px;color:var(--mut)}
|
|
.secret code{font:12.5px var(--mono);color:var(--fg);overflow-wrap:anywhere}
|
|
.df{display:flex;gap:8px;justify-content:flex-end;align-items:center;margin-top:auto}
|
|
.df form{margin:0}
|
|
.df > span{margin-right:auto;font-size:13px;color:var(--mut);display:flex;align-items:center;gap:8px}
|
|
.df .ok{color:var(--green)}
|
|
.spin{width:12px;height:12px;border-radius:50%;box-shadow:inset 0 0 0 2px var(--line2);border-top:2px solid var(--blue);box-sizing:border-box;animation:spin 1s linear infinite}
|
|
@keyframes spin{to{transform:rotate(360deg)}}
|
|
|
|
/* Pages that keep the plain markup: service, jobs, job */
|
|
h1{font:700 34px/1.1 var(--sans);letter-spacing:-.025em;margin:0 0 18px}
|
|
h2{font:700 20px var(--sans);letter-spacing:-.015em;margin:28px 0 8px}
|
|
table{width:100%;border-collapse:collapse;margin-bottom:24px}
|
|
th,td{text-align:left;padding:6px 8px;border-bottom:1px solid var(--line)}
|
|
th{color:var(--mut);font-weight:500}
|
|
code,pre{font:13px var(--mono)}
|
|
pre{padding:12px;border:1px solid var(--line);border-radius:6px;overflow-x:auto}
|
|
.badge{font-size:12px;color:var(--mut);border:1px solid var(--line);padding:0 6px;border-radius:8px}
|
|
.status-running{color:var(--green)}
|
|
.status-degraded,.status-unknown{color:var(--orange)}
|
|
.status-stopped{color:var(--red)}
|
|
.error{color:var(--red)}
|
|
.warning{color:var(--orange)}
|
|
.muted{color:var(--dim)}
|
|
.status-job-succeeded{color:var(--green)}
|
|
.status-job-queued,.status-job-running{color:var(--orange)}
|
|
.status-job-failed{color:var(--red)}
|
|
table.details{width:auto}
|
|
table.details th{padding-right:24px}
|
|
pre.log{white-space:pre-wrap}
|
|
|
|
@media (max-width:760px){
|
|
.side{flex:1 1 100%;flex-direction:row;flex-wrap:wrap;align-items:center;gap:8px 16px;padding:12px 16px;border-right:0;border-bottom:1px solid var(--line)}
|
|
#snav{order:3;width:100%}
|
|
.snav{flex-direction:row;overflow-x:auto}
|
|
.snav a{padding:10px 12px}
|
|
.snav .n{margin-left:4px}
|
|
.mblock{display:none}
|
|
.livebox{margin:0 0 0 auto;border:0;padding:0}
|
|
.livebox #live-age{display:none}
|
|
.main{padding:20px 16px 40px}
|
|
.hdr h1{font-size:28px}
|
|
.hdr .end,.tools .end{margin-left:0}
|
|
.search{flex:1 1 100%}
|
|
.btn,button:not([class]){height:44px}
|
|
.btn.sm{height:36px}
|
|
.row{grid-template-columns:1fr auto;grid-template-areas:"nm st" "ver ver" "fl fl" "mode mode";row-gap:8px;padding:12px 4px}
|
|
.nm{grid-area:nm}.stc{grid-area:st}.row .ver{grid-area:ver}.flags{grid-area:fl}.mode{grid-area:mode}
|
|
.flags:empty{display:none}
|
|
.agent{grid-template-columns:1fr auto;grid-template-areas:"id st" "ver mgr" "last act";row-gap:10px}
|
|
.drawer{order:-1;max-width:none;border-left:0;border-bottom:1px solid var(--line2);padding:20px 16px}
|
|
.pop{left:0;right:0;width:auto}
|
|
table{display:block;overflow-x:auto}
|
|
}
|