1@font-face { font-family: "Shoulders"; src: url(fonts/big-shoulders-display-600.woff2) format("woff2"); font-weight: 600; }2@font-face { font-family: "Shoulders"; src: url(fonts/big-shoulders-display-800.woff2) format("woff2"); font-weight: 800; }3@font-face { font-family: "Schibsted"; src: url(fonts/schibsted-grotesk-400.woff2) format("woff2"); font-weight: 400; }4@font-face { font-family: "Schibsted"; src: url(fonts/schibsted-grotesk-400-italic.woff2) format("woff2"); font-weight: 400; font-style: italic; }5@font-face { font-family: "Schibsted"; src: url(fonts/schibsted-grotesk-500.woff2) format("woff2"); font-weight: 500; }6@font-face { font-family: "Schibsted"; src: url(fonts/schibsted-grotesk-700.woff2) format("woff2"); font-weight: 700; }7@font-face { font-family: "Plex Mono"; src: url(fonts/ibm-plex-mono-400.woff2) format("woff2"); font-weight: 400; }8@font-face { font-family: "Plex Mono"; src: url(fonts/ibm-plex-mono-500.woff2) format("woff2"); font-weight: 500; }910:root {11 --ink: #07080a;12 --ink-2: #0d0f12;13 --ink-3: #15181d;14 --ink-4: #1d2128;15 --line: rgba(237, 230, 214, .08);16 --line-2: rgba(237, 230, 214, .16);17 --paper: #ede6d6;18 --muted: #908a7c;19 --faint: #5d5850;20 --amber: #ffb224;21 --amber-2: #c98714;22 --teal: #3fe0c5;23 --red: #ff5d4a;24 --f-display: "Shoulders", "Impact", sans-serif;25 --f-body: "Schibsted", "Helvetica Neue", sans-serif;26 --f-mono: "Plex Mono", "Menlo", monospace;27 /* Interface size: js/settings.js zooms the root by --zoom. Viewport units go28 through --vw and --vh, which undo the zoom so full screen stays full;29 applyScale sets them in pixels, and these stand in until it does. */30 --zoom: 1;31 --vw: calc(1vw / var(--zoom));32 --vh: calc(1vh / var(--zoom));33 --gtop: calc(42 * var(--vh));34 --pv-h: calc(var(--gtop) - 44px - 20px);35 --pv-w: calc(var(--pv-h) * 16 / 9);36 --chw: 260px;37 --ease: cubic-bezier(.2, .8, .2, 1);38}3940* { box-sizing: border-box; }41html, body { height: 100%; }42body {43 margin: 0;44 background: var(--ink);45 color: var(--paper);46 font: 400 15px/1.4 var(--f-body);47 overflow: hidden;48 -webkit-user-select: none;49 user-select: none;50 -webkit-font-smoothing: antialiased;51 cursor: default;52}53/* Only content worth copying is selectable: program text, messages, inputs. */54.gd-title, .gd-ep, .gd-desc, .ns-msg, .boot-log li.err, .s-row input {55 -webkit-user-select: text;56 user-select: text;57 cursor: text;58}59/* Film grain and faint scanlines over everything but kept off the video. */60body::after {61 content: "";62 position: fixed; inset: 0; z-index: 90; pointer-events: none;63 background:64 repeating-linear-gradient(0deg, rgba(255,255,255,.018) 0 1px, transparent 1px 3px),65 url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%' height='100%' filter='url(%23n)' opacity='.55'/></svg>");66 opacity: .07;67 mix-blend-mode: overlay;68}69body.mode-tv::after { opacity: 0; }70button, input { font: inherit; color: inherit; }71kbd { font: 500 10px var(--f-mono); }72[hidden] { display: none ; }7374#titlebar { position: fixed; inset: 0 0 auto 0; height: 34px; z-index: 60; --wails-draggable: drag; }7576.eyebrow { font: 500 11px var(--f-mono); letter-spacing: .18em; text-transform: uppercase; color: var(--amber); }7778/* ---------- Stage ---------- */79#stage {80 position: fixed; z-index: 1;81 top: 0; left: 0; width: calc(100 * var(--vw)); height: calc(100 * var(--vh));82 background: #000; overflow: hidden;83 transition: top .5s var(--ease), left .5s var(--ease), width .5s var(--ease), height .5s var(--ease), border-radius .5s var(--ease), box-shadow .5s;84}85#video { width: 100%; height: 100%; object-fit: contain; display: block; background: #000; }86body.mode-guide #stage {87 z-index: 25; top: 44px; left: 28px; width: var(--pv-w); height: var(--pv-h);88 border-radius: 4px; box-shadow: 0 0 0 1px var(--line-2), 0 24px 60px rgba(0,0,0,.6); cursor: pointer;89}90/* Every other view keeps the TV in the same top-left corner as the guide. */91:root { --pip-w: 224px; --pip-h: 126px; --pip-gap: 252px; }92body.mode-recordings #stage, body.mode-podcasts #stage, body.mode-settings #stage, body.mode-weather #stage {93 z-index: 25; top: 46px; left: 28px; width: var(--pip-w); height: var(--pip-h);94 border-radius: 4px; box-shadow: 0 0 0 1px var(--line-2), 0 18px 40px rgba(0,0,0,.7); cursor: pointer;95}9697#static {98 position: absolute; inset: -20%; pointer-events: none; opacity: 0; transition: opacity .25s;99 background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='300' height='300'><filter id='s'><feTurbulence type='fractalNoise' baseFrequency='1.4' numOctaves='1'/><feColorMatrix values='0 0 0 0 .8 0 0 0 0 .8 0 0 0 0 .8 0 0 0 1.6 -.3'/></filter><rect width='100%' height='100%' filter='url(%23s)'/></svg>") #111;100}101#stage.tuning #static { opacity: .9; animation: static .18s steps(3) infinite; }102@keyframes static { 0% { transform: translate(0,0) } 33% { transform: translate(-7%, 4%) } 66% { transform: translate(5%, -6%) } 100% { transform: translate(-3%, 7%) } }103104#nosignal { position: absolute; inset: 0; display: grid; place-items: center; background: #050506; }105.ns-bars { position: absolute; inset: 0 0 38% 0; display: grid; grid-template-columns: repeat(7, 1fr); opacity: .22; filter: saturate(.6); }106.ns-bars i:nth-child(1) { background: #c0c0c0 } .ns-bars i:nth-child(2) { background: #c0c000 } .ns-bars i:nth-child(3) { background: #00c0c0 }107.ns-bars i:nth-child(4) { background: #00c000 } .ns-bars i:nth-child(5) { background: #c000c0 } .ns-bars i:nth-child(6) { background: #c00000 } .ns-bars i:nth-child(7) { background: #0000c0 }108.ns-card { position: relative; text-align: center; max-width: 70%; padding: 28px 36px; background: rgba(7,8,10,.86); border-top: 2px solid var(--amber); }109.ns-num { font: 800 clamp(48px, calc(10 * var(--vw)), 120px)/.85 var(--f-display); color: var(--amber); }110.ns-call { font: 500 12px var(--f-mono); letter-spacing: .16em; text-transform: uppercase; margin-top: 10px; }111.ns-msg { color: var(--muted); font-size: clamp(11px, calc(1.2 * var(--vw)), 15px); margin: 14px 0 0; }112body:not(.mode-tv) .ns-card { padding: 10px 14px; max-width: 92%; }113body:not(.mode-tv) .ns-num { font-size: 40px; }114body:not(.mode-tv) .ns-msg { display: none; }115116/* ---------- Number entry, banner ---------- */117#entry {118 position: fixed; z-index: 30; top: 40px; right: 34px; text-align: right; pointer-events: none;119 opacity: 0; transform: translateY(-8px); transition: opacity .35s, transform .35s var(--ease);120 text-shadow: 0 2px 18px rgba(0,0,0,.8);121}122body.mode-tv #entry.show { opacity: 1; transform: none; }123#entry { font: 800 96px/.85 var(--f-display); color: var(--paper); }124#entry::after { content: "_"; color: var(--amber); animation: blink 1s steps(2) infinite; }125@keyframes blink { 50% { opacity: 0 } }126127#banner {128 position: fixed; z-index: 30; left: 28px; right: 28px; bottom: 26px;129 display: grid; grid-template-columns: 210px minmax(0, 1fr); gap: 14px 30px;130 padding: 24px 28px 22px;131 background: linear-gradient(180deg, rgba(13,15,18,.86), rgba(7,8,10,.94));132 backdrop-filter: blur(20px) saturate(1.2); -webkit-backdrop-filter: blur(20px) saturate(1.2);133 border-top: 2px solid var(--amber); border-radius: 3px;134 box-shadow: 0 30px 80px rgba(0,0,0,.6);135 transform: translateY(calc(100% + 40px)); opacity: 0;136 transition: transform .5s var(--ease), opacity .4s;137}138body.mode-tv #banner.show { transform: none; opacity: 1; }139.bn-num { font: 800 104px/.8 var(--f-display); color: var(--amber); letter-spacing: -.01em; }140.bn-call { font: 500 12px var(--f-mono); letter-spacing: .16em; text-transform: uppercase; margin-top: 14px; }141.bn-net { color: var(--muted); margin-top: 2px; }142/* The server's own channels: numbers like 104.0 fit, and the logo set on its admin page shows under the name. */143.bn-num.long { font-size: 76px; }144.bn-logo { width: 96px; height: 48px; margin-top: 14px; background: left center/contain no-repeat; }145.bn-kicker { display: flex; align-items: center; gap: 12px; font: 400 12px var(--f-mono); color: var(--muted); letter-spacing: .06em; }146/* How far behind live, or where in a recording (showTimeshift). */147.bn-shift { color: var(--amber); }148.bn-shift:empty { display: none; }149.bn-label { font: 500 10px var(--f-mono); letter-spacing: .16em; text-transform: uppercase; background: var(--amber); color: var(--ink); padding: 2px 6px; border-radius: 2px; }150.bn-next .bn-label { background: transparent; color: var(--muted); box-shadow: inset 0 0 0 1px var(--line-2); }151.bn-title { font: 800 42px/1 var(--f-display); margin: 10px 0 4px; letter-spacing: .005em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }152.bn-ep { color: var(--paper); opacity: .85; font-style: italic; min-height: 1.4em; }153.bn-progress { height: 3px; background: var(--line-2); margin: 12px 0 10px; border-radius: 2px; overflow: hidden; }154.bn-progress i { display: block; height: 100%; background: var(--amber); width: 0; transition: width .6s; }155.bn-desc { margin: 0; color: var(--muted); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; max-width: 72ch; }156.bn-cast { color: var(--muted); font-size: .92em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 72ch; margin-top: 2px; }157.bn-cast:empty, #banner.osd .bn-cast { display: none; }158/* The details (I) have room for the whole description. */159#banner.detail .bn-desc { -webkit-line-clamp: 6; }160.bn-next { display: flex; gap: 10px; align-items: center; margin-top: 12px; font-size: 14px; }161.bn-signal { border-left: 1px solid var(--line); padding-left: 24px; display: none; flex-direction: column; gap: 8px; justify-content: center; }162#banner.detail, #banner.warn { grid-template-columns: 210px minmax(0, 1fr) 270px; }163#banner.detail .bn-signal, #banner.warn .bn-signal { display: flex; }164#banner.warn:not(.detail) .bn-tx, #banner.warn:not(.detail) .bn-atsc3 { display: none; }165.bn-tier { font: 600 26px/1 var(--f-display); letter-spacing: .02em; }166.bn-tx { font: 400 12px/1.5 var(--f-mono); color: var(--muted); }167.bn-atsc3:empty, .bn-note:empty { display: none; }168.bn-atsc3 { font: 500 11px/1.5 var(--f-mono); color: var(--teal); }169.bn-note { font-size: 12px; color: var(--amber); margin-top: 8px; }170171.chips { display: inline-flex; gap: 5px; flex-wrap: wrap; }172.chip { font: 500 10px/1 var(--f-mono); letter-spacing: .08em; text-transform: uppercase; padding: 4px 6px; border-radius: 2px; box-shadow: inset 0 0 0 1px var(--line-2); color: var(--muted); }173.chip.live { background: var(--red); color: #fff; box-shadow: none; }174.chip.new { color: var(--amber); box-shadow: inset 0 0 0 1px var(--amber-2); }175.chip.ng { color: var(--teal); box-shadow: inset 0 0 0 1px rgba(63,224,197,.5); }176177.meter { display: inline-flex; gap: 3px; align-items: flex-end; height: 16px; }178.meter i { width: 4px; background: var(--ink-4); border-radius: 1px; }179.meter i:nth-child(1) { height: 20% } .meter i:nth-child(2) { height: 40% } .meter i:nth-child(3) { height: 60% } .meter i:nth-child(4) { height: 80% } .meter i:nth-child(5) { height: 100% }180.meter.big { height: 30px; gap: 4px; } .meter.big i { width: 8px; }181.meter i.on { background: var(--c, var(--amber)); }182183/* ---------- Views ---------- */184.view {185 position: fixed; inset: 0; z-index: 20; opacity: 0; pointer-events: none; visibility: hidden;186 background: radial-gradient(120% 80% at 70% 0%, #11141a 0%, var(--ink) 60%);187 transition: opacity .35s, visibility 0s .35s;188}189body.mode-guide #guide, body.mode-settings #settings, body.mode-recordings #recordings, body.mode-podcasts #podcasts, body.mode-weather #weather {190 opacity: 1; pointer-events: auto; visibility: visible; transition: opacity .35s;191}192193/* ---------- Guide ---------- */194.g-top {195 height: var(--gtop); padding: 44px 28px 20px;196 display: grid; grid-template-columns: var(--pv-w) minmax(0, 1fr) minmax(0, 26%); gap: 34px; align-items: end;197}198.g-detail { min-width: 0; padding-bottom: 2px; }199.gd-kicker { font: 500 12px var(--f-mono); letter-spacing: .12em; text-transform: uppercase; color: var(--amber); display: flex; gap: 12px; align-items: center; }200.gd-title { font: 800 clamp(34px, calc(4.4 * var(--vw)), 60px)/.95 var(--f-display); margin: 10px 0 6px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }201.gd-ep { font-style: italic; min-height: 1.4em; }202.gd-meta { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin: 10px 0; font: 400 12px var(--f-mono); color: var(--muted); }203.gd-cast { margin: 0 0 6px; color: var(--muted); font-size: .92em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }204.gd-cast:empty { display: none; }205.gd-desc { margin: 0; color: var(--muted); max-width: 70ch; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }206.gd-hint { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 12px; font: 400 11px var(--f-mono); color: var(--faint); letter-spacing: .06em; }207.g-detail .gd-gone { display: none; }208.gd-art { align-self: stretch; margin-top: -2px; border-radius: 3px; background: var(--ink-2) center/cover no-repeat; opacity: 0; transition: opacity .4s;209 -webkit-mask-image: linear-gradient(90deg, transparent, #000 35%); mask-image: linear-gradient(90deg, transparent, #000 35%); }210.gd-art.has { opacity: .85; }211/* A larger interface size leaves less room above the guide's grid. */212.tight .gd-title { -webkit-line-clamp: 1; font-size: clamp(30px, calc(5.2 * var(--vh)), 48px); }213.tight .gd-meta { margin: 6px 0; }214.tight .gd-desc { -webkit-line-clamp: 2; }215/* A narrow window gives the program's picture's room to its details216 (js/guide.js decides, by width). */217.g-top.no-art { grid-template-columns: var(--pv-w) minmax(0, 1fr); }218.g-top.no-art .gd-art { display: none; }219.g-top.no-art .gd-desc, .g-top.no-art .gd-hint { max-width: none; }220221.g-bar { height: 44px; padding: 0 28px; display: flex; align-items: center; justify-content: space-between; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }222.g-filters { display: flex; gap: 2px; height: 100%; }223.g-filters button {224 background: none; border: 0; padding: 0 14px; height: 100%; cursor: pointer;225 font: 500 11px var(--f-mono); letter-spacing: .14em; text-transform: uppercase; color: var(--muted);226 box-shadow: inset 0 -2px 0 transparent; transition: color .2s, box-shadow .2s;227}228.g-filters button:hover { color: var(--paper); }229.g-filters button.on { color: var(--amber); box-shadow: inset 0 -2px 0 var(--amber); }230.g-filters button .n { color: var(--faint); margin-left: 6px; }231.g-status { font: 400 12px var(--f-mono); color: var(--muted); min-width: 0; margin-left: 18px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }232233.g-grid { position: absolute; top: calc(var(--gtop) + 44px); left: 0; right: 0; bottom: 0; }234.g-head { height: 34px; display: grid; grid-template-columns: var(--chw) 1fr; border-bottom: 1px solid var(--line); }235.g-day { padding: 0 28px; display: flex; align-items: center; font: 600 18px var(--f-display); letter-spacing: .06em; text-transform: uppercase; color: var(--amber); }236.g-ticks { position: relative; }237.g-ticks span { position: absolute; top: 0; bottom: 0; display: flex; align-items: center; padding-left: 10px; font: 400 12px var(--f-mono); color: var(--muted); border-left: 1px solid var(--line); }238.g-rows { position: absolute; top: 34px; bottom: 0; left: 0; right: 0; overflow-y: auto; overflow-x: hidden; scrollbar-width: none; outline: none; }239.g-rows::-webkit-scrollbar { display: none; }240.g-row { height: 58px; display: grid; grid-template-columns: var(--chw) 1fr; border-bottom: 1px solid var(--line); }241.g-ch {242 display: grid; grid-template-columns: 62px minmax(0, 1fr) auto; align-items: center; gap: 10px;243 padding: 0 14px 0 28px; background: var(--ink-2); cursor: pointer; min-width: 0;244}245.g-ch .num { font: 600 28px/1 var(--f-display); position: relative; }246.g-ch .num .ng { position: absolute; left: 0; top: 100%; margin-top: 2px; font: 500 9px var(--f-mono); letter-spacing: .1em; color: var(--teal); }247.g-ch .call span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }248.g-ch .who { min-width: 0; }249.g-ch .call { font: 400 11px var(--f-mono); letter-spacing: .06em; color: var(--muted); display: flex; gap: 6px; align-items: center; }250.g-ch .net { font-size: 13px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }251/* A station's logo drawn for a light background sits on a light chip; one252 drawn for the dark (white, from Schedules Direct) and the server's own253 channels' sit on the guide itself. */254.g-ch .logo { width: 44px; height: 26px; border-radius: 2px; background: #e9e3d4 center/80% no-repeat; }255.g-ch .logo.own { background: center/contain no-repeat; }256.g-ch .num.long { font-size: 22px; }257.g-row.cur .g-ch .num { color: var(--amber); }258.g-row.sel .g-ch { background: var(--ink-3); box-shadow: inset 3px 0 0 var(--amber); }259.g-progs { position: relative; overflow: hidden; }260.g-cell {261 position: absolute; top: 5px; bottom: 5px; padding: 8px 11px; overflow: hidden; cursor: pointer;262 background: var(--ink-3); border-radius: 2px; box-shadow: inset 2px 0 0 var(--line-2);263 transition: background .15s;264}265.g-cell:hover { background: var(--ink-4); }266.g-cell .t { font-weight: 500; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }267.g-cell .s { font: 400 11px var(--f-mono); color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 2px; }268.g-cell.now { background: #1b1e24; box-shadow: inset 2px 0 0 var(--amber-2); }269.g-cell.past { opacity: .45; }270.g-cell.empty { background: transparent; box-shadow: inset 0 0 0 1px var(--line); }271.g-cell.empty .t { color: var(--faint); font-weight: 400; font-style: italic; }272.g-cell.sel { background: var(--focus-fill); box-shadow: inset 0 0 0 1px var(--amber-2); opacity: 1; }273.g-cell .tag { color: var(--teal); }274.g-now { position: absolute; top: 0; bottom: 0; width: 2px; background: var(--amber); pointer-events: none; box-shadow: 0 0 14px rgba(255,178,36,.45); }275.g-now span { position: absolute; top: 0; left: -5px; border: 6px solid transparent; border-top: 8px solid var(--amber); }276277/* ---------- Rail views ---------- */278/* Rail layout shared by recordings, settings and weather: the TV preview279 sits at the top of a left rail, with that view's menu beneath it. */280.view.railed { display: grid; grid-template-columns: var(--pip-w) minmax(0, 1fr); gap: 34px; padding: 46px 28px 22px; }281.rail { padding-top: calc(var(--pip-h) + 24px); display: flex; flex-direction: column; gap: 30px; min-height: 0; }282.rail-menu { display: grid; grid-auto-rows: max-content; gap: 2px; } /* rows whole, however many */283.rm-item {284 border: 0; background: none; text-align: left; cursor: pointer; color: var(--muted);285 display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: baseline; gap: 3px 10px;286 padding: 11px 14px; border-radius: 3px; transition: background .15s, color .15s;287}288.rm-item .rm-label { font: 600 23px/1 var(--f-display); letter-spacing: .03em; }289.rm-item .rm-count { font: 500 12px var(--f-mono); color: var(--faint); }290.rm-item small { grid-column: 1 / -1; font-size: 12px; color: var(--faint); }291.rm-item:hover { color: var(--paper); background: var(--ink-2); }292.rm-item.on { color: var(--paper); background: var(--ink-3); box-shadow: inset 3px 0 0 var(--amber); }293.rm-item.on .rm-count { color: var(--amber); }294.rail-stats { margin: 0; display: grid; gap: 18px; padding: 0 14px; }295.rail-stats dt { font: 500 10px var(--f-mono); letter-spacing: .16em; text-transform: uppercase; color: var(--faint); }296.rail-stats dd { margin: 4px 0 0; font: 800 36px/1 var(--f-display); color: var(--paper); }297.rail-stats dd small { display: block; margin-top: 5px; font: 400 12px/1.35 var(--f-body); color: var(--muted); }298.main { min-height: 0; }299/* ---------- Reception (a tab of Settings) ---------- */300/* Above the map and the table: the tuner, Measure now with its progress,301 Scan, and how many channels there are. */302#antenna { display: grid; grid-template-rows: auto minmax(0, 1fr); gap: 22px; min-height: 0; }303#antenna[hidden] { display: none; }304.a-head { display: grid; grid-template-columns: minmax(220px, 1.2fr) minmax(190px, 1fr) minmax(190px, 1fr) auto; gap: 18px; align-items: start; }305.a-head .a-measure { margin-top: 0; }306.a-stats { grid-auto-flow: column; gap: 26px; padding: 4px 6px 0; }307.a-stats dd { font-size: 30px; }308.a-tuner { display: grid; gap: 3px; padding: 12px 14px; border-radius: 3px; background: var(--ink-2); box-shadow: inset 3px 0 0 var(--line-2); }309.at-label { font: 500 10px var(--f-mono); letter-spacing: .16em; text-transform: uppercase; color: var(--faint); }310.at-name { font: 800 30px/1 var(--f-display); color: var(--paper); margin: 3px 0 2px; }311.at-line { font-size: 13px; color: var(--muted); }312.at-warn { color: var(--amber); }313.at-note { font-size: 12px; color: var(--teal); margin-top: 4px; }314.at-model { font: 400 10px var(--f-mono); color: var(--faint); margin-top: 4px; }315.a-measure { display: grid; gap: 8px; margin-top: -14px; }316.a-measure .btn { justify-self: stretch; text-align: center; }317.a-measure .btn.busy:not(:focus) { background: var(--ink-3); color: var(--amber); box-shadow: inset 0 0 0 1px var(--amber-2); }318.a-mprog { height: 4px; border-radius: 2px; background: var(--ink-4); overflow: hidden; }319.a-mprog[hidden] { display: none; }320.a-mprog i { display: block; height: 100%; width: 0; background: var(--amber); transition: width .4s var(--ease); }321.a-mnote { font-size: 12px; line-height: 1.35; color: var(--muted); padding: 0 2px; }322323.a-body { display: grid; grid-template-columns: minmax(280px, 34%) minmax(0, 1fr); gap: 28px; }324.a-left { display: grid; grid-template-rows: minmax(0, 1fr) auto; min-height: 0; gap: 10px; }325.radar-box { position: relative; min-height: 0; }326.radar { position: absolute; inset: 0; width: 100%; height: 100%; overflow: hidden; }327.radar .ring { fill: none; stroke: var(--line-2); stroke-dasharray: 2 4; }328.radar .ring.edge { stroke-dasharray: none; stroke: var(--line); }329.radar .axis { stroke: var(--line); }330.radar text { font: 400 11px var(--f-mono); fill: var(--faint); letter-spacing: .08em; }331.radar text.card { font: 600 17px var(--f-display); fill: var(--muted); letter-spacing: .1em; }332.radar text.site { font: 500 13px var(--f-mono); fill: var(--paper); paint-order: stroke; stroke: var(--ink); stroke-width: 3px; }333.radar .home { fill: var(--paper); }334.radar .site-dot { cursor: pointer; stroke: var(--ink); stroke-width: 1.5px; transition: r .2s; fill: var(--ink-4); }335.radar .site-dot.s-lock { fill: var(--amber); }336.radar .site-dot.s-part { fill: var(--amber-2); }337.radar .site-dot.s-off { fill: var(--red); }338.radar .site-dot.sel { stroke: var(--paper); stroke-width: 2.5px; }339.radar .ray { stroke: var(--amber); stroke-width: 1; stroke-dasharray: 3 3; opacity: .7; }340341/* The picked RF channel: its last reading, its readings over time (a bar342 a stretch: the mean quality, the low as a notch), its channels and the343 stations licensed on it. */344.a-detail { display: grid; gap: 8px; min-height: 0; }345.a-detail:empty { display: none; }346.ad-head { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; font: 400 12px var(--f-mono); color: var(--muted); }347.ad-head b { font: 800 26px/1 var(--f-display); color: var(--paper); }348.ad-scan { margin-left: auto; }349.ad-sig { display: flex; gap: 14px; align-items: center; }350.ad-label { font: 600 22px/1 var(--f-display); letter-spacing: .02em; }351.ad-sub { font-size: 12px; color: var(--muted); margin-top: 3px; }352.ad-hist { display: flex; align-items: flex-end; gap: 3px; height: 46px; padding-bottom: 1px; border-bottom: 1px solid var(--line-2); }353.ad-hist i { position: relative; flex: 1 1 0; max-width: 14px; height: var(--h); border-radius: 2px 2px 0 0; background: var(--amber); }354.ad-hist i::after { content: ""; position: absolute; left: 0; right: 0; bottom: var(--lo); height: 1px; background: var(--ink); opacity: .55; }355.ad-hist i.h-part { background: var(--amber-2); }356.ad-hist i.h-off { background: var(--red); height: 4px; }357.ad-hist i.h-off::after { display: none; }358.ad-axis { display: flex; justify-content: space-between; font: 400 10px var(--f-mono); color: var(--faint); }359.ad-chans { display: flex; flex-wrap: wrap; gap: 3px 14px; font-size: 12px; color: var(--muted); }360.ad-chans b { font: 500 11px var(--f-mono); color: var(--paper); }361.ad-stations { margin: 0; padding: 0; list-style: none; display: grid; gap: 2px; font-size: 12px; color: var(--muted); max-height: calc(14 * var(--vh)); overflow: hidden; }362.ad-stations b { color: var(--paper); font-weight: 500; }363.ad-hint { font: 400 11px var(--f-mono); color: var(--faint); }364365.a-right { display: grid; grid-template-rows: minmax(0, 1fr) auto; gap: 16px; min-height: 0; }366.a-tablewrap { overflow: auto; min-height: 0; scrollbar-width: thin; scroll-padding: 40px 0 12px; } /* rows scroll clear of the sticky header */367.a-table { width: 100%; border-collapse: collapse; font-size: 13px; }368.a-table th { position: sticky; top: 0; z-index: 1; background: var(--ink); text-align: left; font: 500 10px var(--f-mono); letter-spacing: .14em; text-transform: uppercase; color: var(--muted); padding: 8px; border-bottom: 1px solid var(--line-2); }369.a-table td { padding: 7px 8px; border-bottom: 1px solid var(--line); vertical-align: middle; white-space: nowrap; }370.a-table .r { text-align: right; } .a-table .c { text-align: center; }371.a-table tr[data-rf] { cursor: pointer; transition: background .15s; }372.a-table tbody tr[data-rf]:hover { background: var(--ink-2); }373.a-table tr.sel { background: var(--ink-3); box-shadow: inset 3px 0 0 var(--amber-2); }374.a-table tr.nf td { color: var(--muted); }375.a-table tr.nf .call, .a-table tr.nf .rf b { color: var(--muted); }376.a-table .rf b { font: 800 24px/1 var(--f-display); }377.a-table .call { font: 600 18px var(--f-display); letter-spacing: .03em; }378.a-table .sub { font-size: 11px; color: var(--muted); }379.a-table .mono { font-family: var(--f-mono); font-size: 12px; }380.a-table .carries { font: 400 11px var(--f-mono); color: var(--paper); white-space: normal; max-width: 150px; }381.a-table .carries span { white-space: nowrap; }382.a-table .errs { color: var(--red); }383.a-table tr.a-sect td { padding: 18px 8px 6px; font: 500 10px var(--f-mono); letter-spacing: .14em; text-transform: uppercase; color: var(--faint); border-bottom: 1px solid var(--line-2); }384.meter.m-none i { background: var(--ink-4); }385.meter.m-off i:first-child { background: var(--red); }386387.a-nextgen { max-height: calc(24 * var(--vh)); overflow: hidden; padding: 12px 18px; background: var(--ink-2); border-top: 2px solid var(--teal); border-radius: 3px; }388.a-nextgen:empty { display: none; }389.ng-label { font: 500 11px var(--f-mono); letter-spacing: .16em; text-transform: uppercase; color: var(--teal); margin-bottom: 10px; display: flex; gap: 14px; align-items: baseline; flex-wrap: wrap; }390.ng-label small { font: 400 11px var(--f-body); letter-spacing: 0; text-transform: none; color: var(--muted); }391.ng-hosts { display: grid; gap: 4px; }392.ng-host { font-size: 13px; color: var(--muted); line-height: 1.5; }393.ng-host b { font: 600 17px var(--f-display); letter-spacing: .03em; color: var(--paper); margin-right: 4px; }394.ng-host em { font: 400 11px var(--f-mono); font-style: normal; margin-right: 10px; }395.ng-host span { white-space: nowrap; margin-right: 10px; }396.ng-host span i { font: 400 11px var(--f-mono); font-style: normal; color: var(--teal); }397398/* ---------- Settings ---------- */399/* One list of rows (settingsRows in js/settings.js), moved through with a remote or400 a controller: the focused row is filled and outlined in amber. */401#settings { grid-template-rows: minmax(0, 1fr); }402.s-main { display: grid; grid-template-rows: minmax(0, 1fr) auto; min-height: 0; max-width: 940px; }403#settings[data-tab="reception"] .s-main { max-width: none; }404.s-list { overflow: auto; min-height: 0; padding: 4px 2px 48px; scrollbar-width: none; }405.s-list::-webkit-scrollbar { display: none; }406.s-head { margin: 28px 0 10px 18px; font: 500 11px var(--f-mono); letter-spacing: .16em; text-transform: uppercase; color: var(--amber); }407.s-head:first-child { margin-top: 2px; }408.s-group { display: grid; gap: 2px; }409.s-row {410 display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 28px;411 min-height: 64px; padding: 10px 20px; border-radius: 3px; background: var(--ink-2); cursor: pointer;412 scroll-margin: 48px 0 24px;413}414body.mouse-active .s-row:hover { background: var(--ink-3); } /* not under a resting pointer */415.s-row.focus { background: var(--ink-3); box-shadow: inset 0 0 0 1px var(--line-2); }416.s-label { min-width: 0; }417.s-label b { display: block; font: 500 17px/1.25 var(--f-body); color: var(--paper); }418.s-label small { display: block; margin-top: 3px; font-size: 13px; color: var(--muted); }419.s-value { display: flex; align-items: center; gap: 12px; font: 500 13px var(--f-mono); letter-spacing: .06em; text-transform: uppercase; color: var(--muted); white-space: nowrap; }420.s-row.focus .s-value { color: var(--paper); }421.s-choice { min-width: 9.5em; text-align: center; color: var(--paper); }422.s-step { font: 400 26px/1 var(--f-body); color: var(--faint); font-style: normal; width: 18px; text-align: center; }423.s-row.focus .s-step { color: var(--amber); }424.s-step.end, .s-row.focus .s-step.end { opacity: .25; }425.s-more { font: 400 26px/1 var(--f-body); font-style: normal; color: var(--faint); }426.s-row.focus .s-more { color: var(--amber); }427.s-switch { position: relative; width: 46px; height: 26px; border-radius: 13px; background: var(--ink-4); box-shadow: inset 0 0 0 1px var(--line-2); }428.s-switch i { position: absolute; top: 4px; left: 4px; width: 18px; height: 18px; border-radius: 50%; background: var(--muted); transition: left .15s var(--ease); }429.s-switch.on { background: var(--amber); box-shadow: none; }430.s-switch.on i { left: 24px; background: var(--ink); }431.s-row input {432 width: 300px; padding: 9px 12px; border: 1px solid var(--line-2); border-radius: 3px; outline: none;433 background: var(--ink); font: 400 14px var(--f-mono); color: var(--paper); text-transform: none; letter-spacing: 0;434}435.s-row input:focus { border-color: var(--amber); }436.s-glyphs { display: inline-flex; gap: 4px; }437.s-glyphs .glyph { height: 24px; margin: 0; opacity: 1; }438.s-value kbd { min-width: 11em; text-align: right; font: 500 12px var(--f-mono); color: var(--paper); text-transform: none; }439.s-row.k-info { cursor: default; }440.s-note { margin: 16px 20px 0; max-width: 80ch; font-size: 13px; color: var(--muted); }441.s-hint { padding: 12px 20px 0; border-top: 1px solid var(--line); font: 400 12px var(--f-mono); letter-spacing: .04em; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }442/* The tabs, in the rail. */443.s-tabs .rm-item { padding: 9px 14px; }444.s-tabs .rm-item .rm-label { font-size: 21px; }445.btn { border: 0; cursor: pointer; padding: 12px 18px; border-radius: 3px; background: var(--amber); color: var(--ink); font: 500 11px var(--f-mono); letter-spacing: .14em; text-transform: uppercase; justify-self: start; }446.btn.ghost { background: none; color: var(--paper); box-shadow: inset 0 0 0 1px var(--line-2); }447.btn:hover { filter: brightness(1.1); }448449/* ---------- Dock, boot, toast ---------- */450#dock {451 position: fixed; z-index: 70; top: 7px; left: 50%; transform: translate(-50%, -6px);452 display: flex; gap: 2px; padding: 3px; border-radius: 999px;453 background: rgba(13,15,18,.78); backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);454 box-shadow: inset 0 0 0 1px var(--line-2); --wails-draggable: no-drag;455 opacity: 0; transition: opacity .3s, transform .3s var(--ease);456}457body:not(.mode-tv) #dock, body.mouse-active #dock { opacity: 1; transform: translate(-50%, 0); }458body:not(.has-dvr) #dock [data-view="recordings"] { display: none; }459body:not(.has-podcasts) #dock [data-view="podcasts"] { display: none; }460#dock button { border: 0; background: none; cursor: pointer; padding: 5px 12px 5px 8px; border-radius: 999px; font: 500 11px var(--f-mono); letter-spacing: .1em; text-transform: uppercase; color: var(--muted); display: flex; gap: 7px; align-items: center; }461#dock button kbd { padding: 1px 5px; border-radius: 3px; box-shadow: inset 0 0 0 1px var(--line-2); color: var(--faint); }462#dock button.on { background: var(--ink-4); color: var(--paper); }463#dock button.on kbd { color: var(--amber); }464/* Controller buttons in place of keys (renderHints in js/input.js). */465.glyph { height: 1.4em; width: auto; vertical-align: middle; margin: -.25em 0; opacity: .9; }466body.pad-input #dock kbd { padding: 0; box-shadow: none; }467#dock .glyph { height: 17px; margin: -3px 0; }468/* Picked with the arrow keys, from a remote or a controller. */469body.dock-nav #dock { opacity: 1; transform: translate(-50%, 0); }470#dock button.pick kbd { color: var(--amber); }471body.osd-open #dock { opacity: 1; transform: translate(-50%, 0); }472/* Recordings that failed, until Recordings is opened. */473#dock .badge { min-width: 17px; padding: 1px 5px; border-radius: 999px; background: var(--red); color: var(--paper); font: 600 10px/15px var(--f-mono); letter-spacing: 0; text-align: center; }474/* Larger across a room, where it's the way to every view. */475body.pf-tv #dock button, body.pf-console #dock button { padding: 7px 15px 7px 11px; font-size: 13px; }476477#boot { position: fixed; inset: 0; z-index: 100; display: grid; place-items: center; background: radial-gradient(90% 70% at 50% 40%, #12151b, var(--ink) 70%); transition: opacity .9s .2s, visibility 0s 1.1s; }478body:not(.booting) #boot { opacity: 0; visibility: hidden; }479.boot-inner { width: min(720px, calc(80 * var(--vw))); }480.boot-mark {481 font: 800 clamp(80px, calc(13 * var(--vw)), 168px)/.8 var(--f-display); text-transform: uppercase; letter-spacing: .01em;482 background: linear-gradient(90deg, var(--paper) 0%, var(--paper) 40%, var(--amber) 50%, var(--paper) 60%, var(--paper) 100%);483 background-size: 300% 100%; -webkit-background-clip: text; background-clip: text; color: transparent;484 animation: sweep 2.6s var(--ease) infinite;485}486@keyframes sweep { from { background-position: 100% 0 } to { background-position: 0 0 } }487.boot-sub { font: 500 12px var(--f-mono); letter-spacing: .2em; text-transform: uppercase; color: var(--amber); margin: 18px 0 26px; }488.boot-log { margin: 0; padding: 0; list-style: none; font: 400 13px/1.9 var(--f-mono); color: var(--muted); min-height: 9em; }489.boot-log li { animation: rise .4s var(--ease) both; }490.boot-log li::before { content: "> "; color: var(--amber); }491.boot-log li.err { color: var(--red); }492@keyframes rise { from { opacity: 0; transform: translateY(6px) } }493.boot-connect { display: grid; grid-template-columns: 2fr 1fr auto; gap: 10px; margin-top: 22px; }494.boot-connect[hidden] { display: none; }495.boot-field { display: grid; border-radius: 3px; }496.boot-connect input {497 min-width: 0; background: var(--ink); border: 1px solid var(--line-2); border-radius: 3px; padding: 12px 14px;498 font: 400 15px var(--f-mono); color: var(--paper); outline: none; -webkit-user-select: text; user-select: text;499}500.boot-connect input:focus { border-color: var(--amber); }501.boot-hint { margin-top: 14px; font: 400 12px var(--f-mono); color: var(--faint); letter-spacing: .04em; min-height: 1.4em; }502/* In a browser the address is the page's own (web.js). */503.boot-connect input[readonly] { background: transparent; border-style: dashed; color: var(--muted); }504505#toast {506 position: fixed; z-index: 80; top: 48px; left: 50%; transform: translate(-50%, -10px); opacity: 0; pointer-events: none;507 max-width: calc(70 * var(--vw)); padding: 10px 16px; border-radius: 3px; background: var(--ink-3); box-shadow: inset 3px 0 0 var(--amber), 0 12px 30px rgba(0,0,0,.5);508 font: 400 13px var(--f-mono); transition: opacity .3s, transform .3s var(--ease);509}510#toast.show { opacity: 1; transform: translate(-50%, 0); }511512@media (prefers-reduced-motion: reduce) {513 *, *::before, *::after { animation: none ; transition: none ; }514}515516/* ---------- Recording ---------- */517.gd-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 12px; }518.gd-actions:empty { display: none; }519/* No room for a second row (a small window at a large size): one row that520 scrolls sideways, with room for the focus ring. */521.g-detail.one-row .gd-actions { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; padding: 4px; margin: 8px -4px -4px; }522.g-detail.one-row .gd-actions::-webkit-scrollbar { display: none; }523.g-detail.one-row .gd-actions > * { flex-shrink: 0; }524.act {525 border: 0; cursor: pointer; padding: 8px 13px; border-radius: 3px; background: none;526 box-shadow: inset 0 0 0 1px var(--line-2); color: var(--paper);527 font: 500 12px var(--f-mono); letter-spacing: .1em; text-transform: uppercase;528 display: inline-flex; align-items: center; gap: 7px;529}530.act:hover { box-shadow: inset 0 0 0 1px var(--paper); }531.act.rec i { width: 8px; height: 8px; border-radius: 50%; background: var(--red); }532.g-cell.rec::after {533 content: ""; position: absolute; top: 9px; right: 9px; width: 8px; height: 8px; border-radius: 50%;534 background: var(--red); box-shadow: 0 0 0 2px var(--ink-3);535}536.g-cell.rec { padding-right: 24px; }537.g-cell.rec-on::after { animation: pulse 1.4s ease-in-out infinite; }538body:not(.mode-guide) .g-cell.rec-on::after { animation: none; } /* hidden, so still */539.g-cell.rec-x::after { background: transparent; box-shadow: inset 0 0 0 1.5px var(--red); }540.g-cell.sel.rec::after { box-shadow: 0 0 0 2px var(--focus-fill); }541@keyframes pulse { 50% { opacity: .35; } }542543.r-main { display: grid; grid-template-columns: minmax(0, 1fr); min-height: 0; }544.r-panel { display: none ; min-height: 0; }545.r-panel.on { display: grid ; }546.r-lib.on { grid-template-rows: auto minmax(0, 1fr); grid-template-columns: minmax(0, 1fr); gap: 24px; }547.r-detail { --rd-title: clamp(34px, calc(4 * var(--vw)), 54px); display: grid; grid-template-columns: minmax(0, 1fr) min(34%, 420px); gap: 30px; min-height: var(--pip-h); align-items: end; }548.r-detail .rd-title { font: 800 var(--rd-title)/.95 var(--f-display); margin: 6px 0 6px; }549.r-detail .rd-kicker { font: 500 12px var(--f-mono); letter-spacing: .12em; text-transform: uppercase; color: var(--amber); }550.r-detail .rd-ep { font-style: italic; min-height: 1.4em; }551.r-detail .rd-desc { margin: 10px 0 0; color: var(--muted); max-width: 70ch; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }552.r-detail .rd-hint { margin-top: 12px; font: 400 11px var(--f-mono); color: var(--faint); letter-spacing: .06em; }553.r-detail .rd-art { align-self: stretch; aspect-ratio: 16 / 9; border-radius: 3px; background: var(--ink-2) center/cover no-repeat;554 -webkit-mask-image: linear-gradient(90deg, transparent, #000 35%); mask-image: linear-gradient(90deg, transparent, #000 35%); }555.r-detail .rd-empty { font: 600 28px/1.15 var(--f-display); color: var(--muted); max-width: 30ch; }556/* Rows as tall as their cards: a card clips what it holds, which would557 otherwise let a long list squeeze it to a sliver to fit. */558.r-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); grid-auto-rows: max-content; gap: 18px; align-content: start; overflow: auto; min-height: 0; padding: 7px; scrollbar-width: thin; }559.r-card { background: var(--ink-2); border-radius: 3px; overflow: hidden; cursor: pointer; box-shadow: 0 0 0 1px var(--line); transition: box-shadow .15s, transform .15s; }560.r-card:hover { box-shadow: 0 0 0 1px var(--line-2); }561.r-card.sel { box-shadow: 0 0 0 1px var(--amber-2); }562.r-card.failed { opacity: .55; }563.r-art { aspect-ratio: 16 / 9; background: linear-gradient(135deg, #2a2219, #0d0f12) center/cover no-repeat; display: grid; place-items: end start; }564.r-art span { padding: 12px 14px; font: 800 26px/.95 var(--f-display); color: var(--amber); }565.r-meta { padding: 12px 14px 14px; }566.r-name { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }567.r-sub { font-size: 13px; color: var(--muted); font-style: italic; min-height: 1.4em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }568.r-when { font: 400 11px var(--f-mono); color: var(--faint); margin-top: 6px; }569.r-fail { font: 400 11px var(--f-mono); color: var(--red); margin-top: 4px; }570.r-empty { color: var(--muted); font-size: 14px; list-style: none; }571.r-grid .r-empty { grid-column: 1 / -1; max-width: 60ch; margin: 0; }572.r-list { list-style: none; margin: 0; padding: 0; gap: 2px; align-content: start; overflow: auto; max-width: 900px; scrollbar-width: thin; }573.r-item { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 18px; align-items: center; padding: 14px 16px; border-radius: 3px; cursor: pointer; }574.r-item:hover { background: var(--ink-2); }575.r-item.sel { background: var(--ink-3); box-shadow: inset 3px 0 0 var(--amber-2); }576.r-item .r-name { font-size: 16px; }577.r-item .r-time { font: 400 11px var(--f-mono); color: var(--muted); text-align: right; min-width: 64px; }578.r-item .r-time b { display: block; font: 600 18px var(--f-display); color: var(--paper); letter-spacing: .02em; }579.r-item.unavailable .r-name, .r-item.conflict .r-name { color: var(--muted); }580.r-item .r-why { color: var(--muted); font-size: 0.85em; margin-top: 4px; }581.r-detail .rd-note { margin: 6px 0 0; color: var(--amber-2); max-width: 70ch; }582.chip.warn { background: var(--amber-2); color: #000; box-shadow: none; }583.r-item .chip { display: inline-block; margin-top: 5px; }584.r-list ul .r-item, .r-list .r-item:not(:has(.r-time)) { grid-template-columns: minmax(0, 1fr) auto; }585.x { border: 0; background: none; color: var(--faint); cursor: pointer; font-size: 14px; padding: 6px; border-radius: 3px; }586.x:hover { color: var(--red); background: var(--ink-3); }587588/* ---------- Timeshift, favorites, resume ---------- */589.g-ch .net .fav { margin-right: 5px; font-size: 11px; color: var(--amber); vertical-align: 1px; }590.r-art { position: relative; }591.r-prog { position: absolute; left: 0; right: 0; bottom: 0; height: 4px; background: rgba(0,0,0,.55); }592.r-prog i { display: block; height: 100%; background: var(--amber); }593.r-badge { position: absolute; top: 8px; right: 8px; font: 500 10px var(--f-mono); letter-spacing: .12em; text-transform: uppercase; padding: 3px 6px; border-radius: 2px; background: rgba(7,8,10,.8); color: var(--muted); }594.r-card.watched .r-art { filter: saturate(.4) brightness(.7); }595.r-hint { list-style: none; padding: 14px 16px; font: 400 11px var(--f-mono); color: var(--faint); letter-spacing: .06em; }596597/* ---------- Podcasts ---------- */598/* Recordings' library, for episodes: the one picked above a grid. */599.p-main { display: grid; grid-template-rows: auto minmax(0, 1fr); grid-template-columns: minmax(0, 1fr); gap: 24px; min-height: 0; }600/* The episode picked takes the same room whichever it is, so the grid601 under it stays put: room for the kicker (16px), two lines of title and602 its margins (12px), three lines of description and its margin (10px),603 and the actions (32px) and the hint (15px) with theirs (24px). The words604 sit at the bottom, by the actions; the picture is its column's width. */605#podcasts .r-detail { height: calc(16px + 1.9 * var(--rd-title) + 12px + 4.2em + 10px + 32px + 15px + 24px); }606#podcasts .rd-kicker { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }607#podcasts .rd-title { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }608#podcasts .rd-art { align-self: end; width: 100%; }609.p-menu { overflow: auto; min-height: 0; scrollbar-width: none; }610/* Refresh, under the lists: every show listed again now. */611.p-refresh { display: grid; gap: 8px; padding: 0 14px; }612.p-refresh[hidden] { display: none; }613.p-refresh .btn { justify-self: stretch; text-align: center; }614.p-refresh .btn.busy:not(:focus) { color: var(--amber); box-shadow: inset 0 0 0 1px var(--amber-2); }615.p-rnote { font-size: 12px; line-height: 1.35; color: var(--faint); }616.p-name { white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; min-height: 2.6em; line-height: 1.3; }617.r-badge.p-len { color: var(--paper); }618619/* ---------- Weather ---------- */620/* The frame never takes focus, so channel keys keep working on WX. */621/* Always 854x480, the display's own size, scaled by fitWX in js/weather.js to622 fill the stage. */623#wx { position: absolute; left: 0; top: 0; width: 854px; height: 480px; transform-origin: 0 0; border: 0; background: #000; pointer-events: none; }624#crawl {625 position: fixed; z-index: 29; left: 0; right: 0; bottom: 0; height: 40px; overflow: hidden; display: none;626 background: var(--amber-2); color: #fff; font: 600 18px/40px var(--f-display); letter-spacing: .04em; white-space: nowrap;627}628#crawl.severe { background: #b3261e; }629body.has-crawl.mode-tv #crawl { display: block; }630body.has-crawl #banner { bottom: 56px; }631#crawl span { display: inline-block; padding-left: 100%; animation: crawl 40s linear infinite; }632@keyframes crawl { to { transform: translateX(-100%); } }633634.wx-now { display: flex; flex-direction: column; gap: 6px; padding: 0 14px; }635.wx-temp { font: 800 96px/.85 var(--f-display); color: var(--paper); }636.wx-desc { font: 600 22px var(--f-display); letter-spacing: .03em; color: var(--muted); margin-bottom: 18px; }637.wx-facts { padding: 0; }638.wx-facts dd { font-size: 28px; }639.wx-updated { margin-top: 14px; font: 400 11px var(--f-mono); color: var(--faint); letter-spacing: .06em; }640/* A definite height, so the maps take whatever is left below the forecast. */641#weather { grid-template-rows: minmax(0, 1fr); }642.wx-main { display: flex; flex-direction: column; gap: 26px; overflow: auto; scrollbar-width: thin; padding-right: 8px; }643.wx-label { font: 500 11px var(--f-mono); letter-spacing: .16em; text-transform: uppercase; color: var(--amber); margin-bottom: 10px; }644.wx-alerts:empty { display: none; }645.wx-alerts { display: grid; gap: 10px; }646.wx-alert { padding: 14px 18px; border-radius: 3px; background: rgba(201,135,20,.12); box-shadow: inset 3px 0 0 var(--amber); }647.wx-alert.severe { background: rgba(179,38,30,.16); box-shadow: inset 3px 0 0 var(--red); }648.wx-alert-event { font: 800 26px/1 var(--f-display); letter-spacing: .02em; }649.wx-alert-when { font: 400 12px var(--f-mono); color: var(--muted); margin: 6px 0; }650.wx-alert p { margin: 0; color: var(--muted); max-width: 90ch; }651.wx-block { position: relative; }652.wx-chart { width: 100%; height: 170px; display: block; }653.wx-line { fill: none; stroke: var(--amber); stroke-width: 2.5; vector-effect: non-scaling-stroke; }654.wx-pop { fill: rgba(63,224,197,.28); }655.wx-night { fill: rgba(255,255,255,.025); }656.wx-hours { position: relative; height: 46px; margin-top: 6px; }657.wx-hours div { position: absolute; transform: translateX(-50%); text-align: center; font: 400 11px/1.3 var(--f-mono); color: var(--muted); white-space: nowrap; }658.wx-hours div:first-child { transform: none; }659.wx-hours b { display: block; font: 600 18px var(--f-display); color: var(--paper); }660.wx-hours em { display: block; font-style: normal; color: var(--teal); }661.wx-days { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 10px; }662.wx-day { padding: 14px 14px 16px; border-radius: 3px; background: var(--ink-2); }663.wx-dname { font: 600 20px var(--f-display); letter-spacing: .04em; text-transform: uppercase; color: var(--amber); }664.wx-hilo { margin: 8px 0 6px; display: flex; gap: 10px; align-items: baseline; }665.wx-hilo b { font: 800 34px/1 var(--f-display); }666.wx-hilo span { font: 600 22px var(--f-display); color: var(--muted); }667.wx-short { font-size: 13px; color: var(--muted); }668.wx-precip { margin-top: 6px; font: 500 12px var(--f-mono); color: var(--teal); }669.wx-maps { flex: 1 0 auto; display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }670/* The caption sits above the picture: the radar and satellite images carry671 their own legends along the bottom edge. */672.wx-maps figure { margin: 0; min-height: 260px; display: flex; flex-direction: column; gap: 8px; }673.wx-maps img { flex: 1 1 0; min-height: 0; width: 100%; object-fit: contain; background: #000; border-radius: 3px; }674.wx-maps figcaption { order: -1; font: 500 11px var(--f-mono); letter-spacing: .16em; text-transform: uppercase; color: var(--faint); }675676/* ---------- Captions ---------- */677/* Drawn by js/captions.js, which sets the size and the lines: like broadcast TV,678 white text on a black bar per line, low on the picture. A timed line's679 bar grows behind its words as they're said (--r). */680#captions {681 position: absolute; z-index: 2; left: 0; right: 0; bottom: 6%;682 display: flex; flex-direction: column; align-items: center; gap: .08em;683 font: 500 32px/1.25 "Schibsted", "Helvetica Neue", sans-serif; color: #fff;684 pointer-events: none; transition: bottom .35s var(--ease);685}686body:not(.mode-tv) #captions { display: none; }687.cap-line { white-space: pre; }688.cap-bar { display: inline-block; padding: .04em .3em .08em; background: rgba(0, 0, 0, .8); border-radius: .1em; }689.cap-bar.timed { background: linear-gradient(90deg, rgba(0, 0, 0, .8) var(--r, 0px), transparent var(--r, 0px)); }690.cap-bar .wait { visibility: hidden; }691692/* ---------- Playback state ---------- */693/* The badge at the picture's bottom right (renderPlayState in js/playback.js):694 paused and buffering stay; a skip, how far behind live and "Live" flash.695 placePlayState places it. */696#pstate {697 position: absolute; z-index: 3; right: 3%; bottom: 5%;698 display: flex; align-items: center; gap: 9px; padding: 7px 11px; border-radius: 3px;699 background: rgba(7, 8, 10, .74); color: var(--paper);700 font: 500 14px/1 var(--f-mono); letter-spacing: .06em; white-space: nowrap;701 opacity: 0; pointer-events: none; transition: opacity .3s, bottom .35s var(--ease);702}703#pstate.show { opacity: 1; }704body:not(.mode-tv) #pstate { display: none; }705#pstate svg { width: 15px; height: 15px; fill: currentColor; flex: none; }706#pstate.ps-pause svg, #pstate.ps-back svg, #pstate.ps-ahead svg { color: var(--amber); }707#pstate.ps-place { color: var(--paper); }708.ps-dot { display: block; width: 8px; height: 8px; border-radius: 50%; background: var(--red); }709#pstate.ps-live span { text-transform: uppercase; letter-spacing: .14em; }710.ps-spin { display: block; width: 15px; height: 15px; border-radius: 50%; border: 2px solid rgba(237, 230, 214, .22); border-top-color: var(--amber); animation: ps-spin .9s linear infinite; }711@keyframes ps-spin { to { transform: rotate(1turn); } }712713/* ---------- Lite ---------- */714/* Linux (WebKitGTK on a TV box, see setLite in js/core.js): the same look, flat.715 No backdrop blurs, film grain, big soft shadows or transitions, and716 nothing animates over the video but a weather alert's crawl. */717body.lite::after { display: none; }718body.lite *, body.lite *::before, body.lite *::after { transition: none ; }719body.lite #banner { backdrop-filter: none; -webkit-backdrop-filter: none; background: linear-gradient(180deg, rgba(13,15,18,.95), rgba(7,8,10,.97)); box-shadow: none; }720body.lite #dock { backdrop-filter: none; -webkit-backdrop-filter: none; background: rgba(13,15,18,.95); }721body.lite #stage { box-shadow: 0 0 0 1px var(--line-2); }722body.lite .g-now, body.lite #toast { box-shadow: none; }723body.lite #toast { border-left: 3px solid var(--amber); }724body.lite #stage.tuning #static, body.lite #entry::after, body.lite .boot-mark, body.lite .boot-log li,725body.lite .g-cell.rec-on::after, body.lite .ob svg { animation: none ; }726body.lite .boot-mark { background: none; color: var(--paper); }727/* The spinner turns in eight steps: eight redraws a second, not sixty. */728body.lite .ps-spin { animation-timing-function: steps(8); }729730/* ---------- On-screen controls ---------- */731/* OK on live TV (js/player.js, on-screen controls): the banner grows a timeline732 and a row of buttons, and the dock shows above as the way to the views. */733#banner .tl, #banner .osd-bar { grid-column: 1 / -1; }734#banner:not(.osd) .tl, #banner:not(.osd) .osd-bar { display: none; }735#banner.osd.tl-on .bn-progress { display: none; }736#banner.osd .bn-desc { -webkit-line-clamp: 1; }737#banner.osd .bn-next { margin-top: 8px; }738/* The timeline: the program from its start to its end, what's aired of it,739 what can be rewound (amber), the live edge (red) and where the picture740 is. Left and Right skip while it has the focus. */741.tl { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 18px; margin: 0 -12px; padding: 12px; border-radius: 3px; cursor: pointer; }742.tl-t { font: 500 13px var(--f-mono); color: var(--muted); letter-spacing: .04em; min-width: 4.6em; white-space: nowrap; }743.tl-t:last-child { text-align: right; }744.tl-bar { position: relative; height: 6px; border-radius: 3px; background: var(--line-2); }745.tl-bar i { position: absolute; top: 0; bottom: 0; left: 0; display: block; border-radius: 3px; }746.tl-past { background: rgba(237, 230, 214, .2); }747.tl-buf { background: var(--amber-2); }748.tl-edge { width: 3px; margin-left: -1px; top: -5px ; bottom: -5px ; background: var(--red); border-radius: 1px ; }749.tl-head { width: 14px; height: 14px; top: 50% ; margin: -7px 0 0 -7px; border-radius: 50% ; background: var(--paper); box-shadow: 0 0 0 3px rgba(7, 8, 10, .75); }750.tl-head b { display: none; position: absolute; bottom: 22px; left: 50%; transform: translateX(-50%); padding: 4px 7px; border-radius: 2px; background: var(--ink); box-shadow: inset 0 0 0 1px var(--amber-2); font: 500 13px/1 var(--f-mono); color: var(--paper); white-space: nowrap; }751.tl:focus .tl-head { width: 20px; height: 20px; margin: -10px 0 0 -10px; background: var(--amber); }752.tl:focus .tl-head b:not(:empty) { display: block; }753.tl:focus .tl-bar { height: 8px; }754/* The buttons: an icon over a label each. */755.osd-bar { display: flex; gap: 6px; padding-top: 14px; border-top: 1px solid var(--line); }756.ob {757 flex: 0 1 118px; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 8px;758 padding: 11px 6px 10px; border: 0; border-radius: 3px; background: none; color: var(--muted); cursor: pointer;759}760.ob svg { width: 28px; height: 28px; fill: currentColor; flex: none; }761.ob span { max-width: 100%; font: 500 12px var(--f-mono); letter-spacing: .08em; text-transform: uppercase; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }762body.mouse-active .ob:hover { color: var(--paper); background: var(--ink-3); }763.ob.on svg { color: var(--amber); }764.ob[data-act="record"] svg, .ob[data-act="live"].at svg { color: var(--red); }765.ob[data-act="record"].on svg { animation: pulse 1.4s ease-in-out infinite; }766767/* ---------- Touch ---------- */768/* A phone or a tablet (body.pf-touch, js/touch.js): fingers tap, swipe and769 hold, so nothing selects as text or zooms on a double tap, the picture's770 swipes are the app's, lists scroll under a finger, what's tapped is big771 enough to hit, and the notch and the home bar are left clear. */772body.pf-touch { -webkit-tap-highlight-color: transparent; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; touch-action: manipulation; overscroll-behavior: none; }773body.pf-touch input { -webkit-user-select: text; user-select: text; }774body.pf-touch #stage, body.pf-touch #banner { touch-action: none; }775body.pf-touch .g-rows { touch-action: pan-y; }776body.pf-touch .r-grid, body.pf-touch .s-list, body.pf-touch .a-tablewrap, body.pf-touch .p-menu, body.pf-touch .r-list { overflow-y: auto; touch-action: pan-y; }777body.pf-touch .wx-main { overflow-y: auto; touch-action: pan-y; }778body.pf-touch #dock { top: max(7px, env(safe-area-inset-top)); }779body.pf-touch #dock button { min-height: 40px; padding: 8px 16px; font-size: 13px; }780body.pf-touch #dock kbd { display: none; }781body.pf-touch .act, body.pf-touch .btn { min-height: 44px; }782body.pf-touch .rm-item { min-height: 48px; }783body.pf-touch .view { padding-left: max(28px, env(safe-area-inset-left)); padding-right: max(28px, env(safe-area-inset-right)); }784body.pf-touch #banner { left: max(28px, env(safe-area-inset-left)); right: max(28px, env(safe-area-inset-right)); bottom: max(26px, env(safe-area-inset-bottom)); }785/* Nothing to press, so no hints of keys. */786body.pf-touch .s-hint, body.pf-touch .rd-hint, body.pf-touch .gd-hint, body.pf-touch .r-hint { display: none; }787body.pf-touch .s-main { grid-template-rows: minmax(0, 1fr); }788789/* A phone on its side is short: the picture-in-picture in the rail goes790 (it plays on, heard), the guide's top and the banner keep to the791 essentials, and a picked item's details take less of the height. */792@media (max-height: 520px) {793 body.pf-touch:not(.mode-tv):not(.mode-guide) #stage { visibility: hidden; }794 body.pf-touch .rail { padding-top: 0; }795 body.pf-touch .view.railed { padding-top: 64px; grid-template-columns: 190px minmax(0, 1fr); gap: 20px; }796 body.pf-touch .rm-item .rm-label { font-size: 19px; }797 body.pf-touch .p-menu .rm-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }798 body.pf-touch .r-detail { --rd-title: 30px; min-height: 0; grid-template-columns: minmax(0, 1fr) min(30%, 300px); gap: 20px; }799 body.pf-touch .r-detail .rd-title { -webkit-line-clamp: 2; display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden; }800 body.pf-touch .r-detail .rd-desc { -webkit-line-clamp: 2; }801 /* Two lines of description, no hint, and actions a finger can hit, in802 one row that scrolls sideways, as the guide's do. */803 body.pf-touch #podcasts .r-detail { height: calc(16px + 1.9 * var(--rd-title) + 12px + 2.8em + 10px + 44px + 12px); }804 body.pf-touch #podcasts .rd-actions { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; padding: 4px; margin: 8px -4px -4px; }805 body.pf-touch #podcasts .rd-actions::-webkit-scrollbar { display: none; }806 body.pf-touch #podcasts .rd-actions > * { flex-shrink: 0; }807 body.pf-touch .p-main, body.pf-touch .r-lib.on { gap: 14px; }808 body.pf-touch #banner { padding: 16px 20px 14px; grid-template-columns: 130px minmax(0, 1fr); gap: 8px 20px; }809 body.pf-touch .bn-num { font-size: 64px; }810 body.pf-touch .bn-title { font-size: 30px; margin-top: 6px; }811 body.pf-touch .bn-desc, body.pf-touch .bn-cast, body.pf-touch .bn-logo { display: none; }812 body.pf-touch .bn-next { margin-top: 6px; }813 /* The controls leave most of the picture showing, there to tap: what's814 next is in the guide, and the timeline and the buttons draw tighter. */815 body.pf-touch #banner.osd { padding: 12px 20px 8px; gap: 4px 20px; }816 body.pf-touch #banner.osd .bn-num { font-size: 48px; }817 body.pf-touch #banner.osd .bn-call { margin-top: 8px; }818 body.pf-touch #banner.osd .bn-next { display: none; }819 body.pf-touch .bn-ep { min-height: 0; }820 body.pf-touch #banner .tl { padding: 6px 12px; }821 body.pf-touch .osd-bar { padding-top: 4px; }822 body.pf-touch .ob { padding: 8px 4px 6px; gap: 5px; }823 body.pf-touch .ob svg { width: 24px; height: 24px; }824 /* The guide's top clears the dock, and leaves the grid more rows. */825 body.pf-touch { --gtop: calc(40 * var(--vh)); --pv-h: calc(var(--gtop) - 62px - 14px); --pv-w: calc(var(--pv-h) * 16 / 9); }826 body.pf-touch .g-top { padding-top: 62px; padding-bottom: 14px; }827 body.pf-touch.mode-guide #stage { top: 62px; }828}829830/* Held upright, a phone is asked to turn sideways: the TV and its views831 are laid out for a wide screen. */832#rotate { display: none; }833@media (orientation: portrait) and (max-width: 700px) {834 body.pf-touch #rotate {835 position: fixed; inset: 0; z-index: 200; display: grid; place-items: center; text-align: center;836 background: var(--ink); color: var(--paper); letter-spacing: .03em; text-transform: uppercase;837 /* The screen's own size, whatever the page's zoom. */838 font: 600 calc(22px / var(--zoom, 1))/1.3 var(--f-display);839 }840}841.rotate-mark { width: 2.4em; height: 3.9em; margin: 0 auto 1em; border: calc(3px / var(--zoom, 1)) solid var(--amber); border-radius: .45em; animation: rotate-hint 2.4s var(--ease) infinite; }842@keyframes rotate-hint { 0%, 30% { transform: rotate(0); } 60%, 100% { transform: rotate(-90deg); } }843844/* ---------- Focus ---------- */845/* One mark for wherever the remote, a controller or the arrow keys are, in846 every view: an amber ring, a warm fill and a slight lift (Android TV's847 focus system: an outline and a raise). Buttons, fields and blocks take848 the page's focus (.fx, only ever from keys); the guide's grid, the lists849 and the dock mark their pick with a class, only while their part of the850 view has the focus (data-zone, set in js/). The ring is drawn inside where851 a scrolling box would clip it. */852:root {853 --ring: 0 0 0 3px var(--amber);854 --ring-in: inset 0 0 0 3px var(--amber);855 --focus-fill: #2a2216;856 --lift: translateY(-2px);857 --glow: 0 12px 28px rgba(0, 0, 0, .55);858}859body.lite { --glow: 0 0 0 0 transparent; }860.fx { outline: none; }861.fx:focus, #dock button.pick {862 box-shadow: var(--ring), var(--glow); background-color: var(--focus-fill); color: var(--paper); transform: var(--lift);863}864/* A filled button keeps its fill, ringed apart from it. */865.btn.fx:not(.ghost):focus { background-color: var(--amber); color: var(--ink); box-shadow: 0 0 0 2px var(--ink), 0 0 0 5px var(--amber), var(--glow); }866.wx-main .fx:focus, .a-tuner.fx:focus { box-shadow: var(--ring-in), var(--glow); }867body:not(.dock-nav) #guide[data-zone="grid"] .g-cell.sel,868body:not(.dock-nav) #settings[data-zone="list"] .s-row.focus,869body:not(.dock-nav) #recordings[data-zone="list"] .r-item.sel,870body:not(.dock-nav) #settings[data-zone="list"] #antenna[data-zone="table"] .a-table tr.sel {871 box-shadow: var(--ring-in), var(--glow); background: var(--focus-fill); transform: var(--lift);872}873body:not(.dock-nav) #recordings[data-zone="list"] .r-card.sel,874body:not(.dock-nav) #podcasts[data-zone="grid"] .r-card.sel { box-shadow: var(--ring), var(--glow); transform: var(--lift); }875.g-cell.sel { z-index: 1; }876.s-row.focus .s-value { color: var(--paper); }877/* Where the focus goes back to when it's elsewhere in the view. */878.rm-item.fx:focus { box-shadow: var(--ring-in), var(--glow); }879.g-filters button.fx:focus { box-shadow: var(--ring-in); color: var(--amber); }880.wx-main > .fx { border-radius: 3px; }881.wx-main .wx-block.fx, .wx-main .wx-days.fx, .wx-main .wx-maps.fx { padding: 10px 12px; margin: -10px -12px; }882.wx-main { padding: 12px 20px 12px 14px; }883.wx-watch { margin: 0 14px; justify-self: stretch; text-align: center; }884885/* ---------- Actions on a selected item ---------- */886/* Recordings: the selected recording's buttons, and a list item's own. */887.rd-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }888.rd-actions:empty { display: none; }889.r-acts { display: flex; gap: 6px; justify-content: flex-end; flex-wrap: wrap; }890.act.armed { box-shadow: inset 0 0 0 1px var(--red); color: var(--red); }891.act.fx.armed:focus { box-shadow: var(--ring), var(--glow); color: var(--red); }892/* A field being typed in has the ring, not its row. */893.boot-connect input:focus, .s-row input:focus { box-shadow: var(--ring); border-color: var(--amber); }894body:not(.dock-nav) #settings[data-zone="list"] .s-row.focus:focus-within { box-shadow: inset 0 0 0 1px var(--line-2); transform: none; }