/* listen.css — the listening panel: identity tiles, text line, continuous-trace meters,
   filter, transmitter and RF impairments.
   Widget: web/js/listen.js */

/* The listening panel is a widget card: the box comes from `.wcard`, and only its own
   padding is stated here. Space distribution is deliberately not spread between blocks: on a
   height deficit that behaved like a top alignment and pushed the last block outside the
   frame. Every block keeps its reserved height and the free room stays at the bottom, which
   is the room the boundary above gives back to the trace. */
.wcard.w-listen{padding:6px 16px 8px;}
/* The text line closes the card, on its own padding. Its bottom margin was there to separate it
   from the widget pair that used to sit below; there is nothing under it now, so the margin only
   deepens the floor. Named rather than reached with `:last-child`, which is the corner mark: the
   slot appends it after the widget's markup. */
.wcard.w-listen > .rtfull{margin-bottom:0;}
/* Floor on the card that needs it, like the time graph's. Every block above has a reserved
   height, so their sum is exactly the point below which the panel stops being a reading: the
   identity would be cut in half and the text line would go. The card is also the slot, so this
   has to outweigh the `min-height: 0` every row item carries.
   It is the SUM, not a round number: any slack in it is room the trace and the pair never get
   back, since the floor wins over the share the operator dragged. */
[data-slot].wcard.w-listen{min-height:146px;}
/* Identity row: identity code, frequency, name and programme type on one line. The grid
   sizes by position rather than by class, so the widths follow that same order. */
.idrow{display:grid; grid-template-columns:0.7fr 1fr 1.3fr 1fr; gap:7px; margin-bottom:8px;}
.tile{background:#0c1218; border:1px solid var(--edge); border-radius:2px; padding:6px 10px; min-width:0;}
.tile .tk{font-size:9px; letter-spacing:.16em; text-transform:uppercase; color:var(--dim);}
/* Height reserved per tile, so an empty field does not make the row tremble. */
.tile .tv{font-family:var(--mono); color:var(--ink); margin-top:3px; line-height:1;
          white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.tile .tv.stale{opacity:.55;}   /* already seen but no longer received: dimmed, kept */
.tile.freq{background:linear-gradient(180deg,#101820,#0b1219);}
/* The readout between its two steps, centred in the tile. This is the one tile whose value
   has controls beside it, so the row is stated here and not on `.tile`. */
.tile.freq .tv{min-height:30px; display:flex; align-items:center; justify-content:center;}
/* The channel step, flanking the digits at their own height and with no box of its own: it is
   part of the reading, not a keypad set down next to it. Quiet at rest, since an instrument
   shows its measurement first and its controls second. The width is fixed so the digits sit
   at the same place whether or not the pointer is over one. */
.tile.freq .fstep{flex:0 0 auto; width:22px; text-align:center; cursor:pointer;
                  font-size:26px; line-height:30px; color:var(--dim2); user-select:none;}
.tile.freq .fstep:hover{color:var(--teal);}
.tile.freq .fstep:active{transform:translateY(1px);}
/* Identity, frequency and name share one type size: three faces of a single identity, with
   no rank between them. */
.tile.freq .bigfreq b{font-size:30px;}
.tile.freq .bigfreq i{font-size:12px; margin-left:3px;}
.tile.pi .tv{font-size:30px; color:var(--teal); min-width:4.5ch; min-height:30px;}
/* The country code reserves no space: it comes after the identity code in a left-aligned
   tile, so its arrival shifts nothing, and it is absent most of the time. Reserving would
   have dug a permanent hole for a field that only lights up on distant reception. */
/* The tuner's confidence marks share the code's type size, since they are part of what the
   tuner writes, but are dimmed: the code reads first, the reservation second. No space is
   reserved, so a confident code leaves no hole. */
.tile.pi .piq{color:var(--dim); letter-spacing:-.04em;}
.tile.ps{background:linear-gradient(180deg,#101a16,#0c1512); border-color:#1c3a30;}
.tile.ps .tv{font-size:30px; color:#eaf7f2; letter-spacing:1px; font-weight:600; min-height:30px;}
.tile.ps .tk{color:var(--teal);}
/* The level tile shares that same size and carries the decimal value. Its minimum width is
   in character units, so short and long values occupy the same space and the figure does
   not dance between measurements. */
.tile.sig .tv{font-size:30px; min-height:30px;}
.tile.sig .tv #vSig{display:inline-block; min-width:4.5ch;}
.tile.sig .tv small{font-size:11px; color:var(--dim); margin-left:4px;}
/* Banner: flags, programme type and the signal meter, on the title line. In flow rather
   than absolute, because the meter needs a real width. It starts at the panel edge: the
   panel carries no caption, so the measurements begin where the reading does. */
.lhead{display:flex; align-items:center; gap:12px; margin-bottom:8px;
       padding-bottom:13px;}   /* reserves the height of the ruler, taken out of flow below */
/* The signal meter no longer stretches across the full width: a very long track for a
   modest range gives the level a visual weight it does not have in the reading. Bounded and
   left-aligned, right after the flags, so the row reads in one stroke and the empty space
   is pushed to the end. */
.lhead .sigrow{flex:0 1 auto; min-width:0;}
/* Alignment is on the bar, not on the bar-plus-ruler block. Leaving the ruler in flow made
   the block taller, so the chips centred on that and their axis fell below the bar's. With
   the ruler absolutely positioned under the bar, everything shares one axis. */
.lhead .nbody{position:relative; width:340px; max-width:100%;}
.lhead .ruler{position:absolute; left:0; right:0; top:100%;}
/* The programme type is a labelled chip built on the flag template, so the label stays
   readable even without a value. The value has a variable length, so a fixed width plus an
   ellipsis keeps the chip from ever resizing and the meter that follows from moving. */
.flag.pty{display:inline-flex; align-items:center; gap:5px; flex:0 0 auto;}
.flag.pty b{display:inline-block; width:92px; font-weight:400; color:var(--ink);
            overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
#vPs .psc{white-space:pre;}                        /* character cells keep a stable width */
#vPs .psc.dec{color:#eaf7f2;}                      /* really decoded segment */
/* Guessed name from the registry: a rank below a decoded character, and no more. It is a
   WAITING state -- the progressive name has not arrived yet -- so it must not borrow the
   look of a station whose RDS has dropped, which is a different fact entirely. A solid
   colour rather than an opacity: opacity multiplies with the one the stale value carries,
   and the two together took the guess down to nothing. */
#vPs .psc.hint{color:#93a6b4;}
/* Activity pixel: pulses on each reception of its field, driven by the rendering. */
.apx{display:inline-block; width:4px; height:4px; border-radius:50%; background:var(--green);
     opacity:0; vertical-align:middle; box-shadow:none; will-change:opacity;}
     /* Promoted to its own compositor layer, so its blinking composites instead of
        repainting the label around it. */
.flags{display:flex; gap:6px; align-items:center;}
/* Chip groups never give up width: when space runs short the meter track compresses, not
   the flags -- a truncated flag no longer reads. */
.lhead .flags{flex:0 0 auto;}
/* The banner reads as two blocks: carrier, then RDS subcarrier. A discreet rule marks the
   boundary, and it is the only hint that the right-hand bar measures RDS and not signal. */
.lhead .rdsf{margin-left:2px; padding-left:14px; border-left:1px solid #182430;}
.flag{font-size:10px; font-family:var(--mono); color:var(--dim2);
      border:1px solid #1f2a33; border-radius:2px; padding:2px 6px; letter-spacing:.08em;}
.flag.on{color:#06231c; background:var(--teal); border-color:var(--teal); font-weight:700;}
/* When RDS is lost, the flags it carried are kept but dimmed, the same convention as the
   identity: the last known state is shown without asserting it is current. */
.flag.stale{opacity:.4;}
.flag.amber.on{background:var(--amber); border-color:var(--amber); color:#241c02;}
/* The RDS flag is an ordinary flag but keeps its own colour when active. Colour logic:
   green means the data is present, the trace colour is the default, amber is an alert. */
.flag.rds.on{color:#041a12; background:var(--green); border-color:var(--green);}

/* Text line and RDS health. One single line: label, text, rate. A separate header cost a
   whole line for two small labels while the text itself did not move.
   Aligned to the top rather than to the baseline: baseline alignment depends on content, so
   an empty text has no line box, the layout falls back to the bottom of its reserved height
   and the label dives, then rises again as soon as text arrives. Here all three start from
   the same top and share the first line's height, so the position is fixed whether the text
   is present or not, on one line or two. */
.rt{display:flex; align-items:flex-start; gap:12px;
    margin:0 0 8px; padding:5px 11px; background:#0b1218;
    border:1px solid #18222c; border-radius:2px; font-family:var(--mono);
    color:#bfe9df; line-height:1.4; letter-spacing:.02em;}
.rt .ptag,.rt .ber{flex:0 0 auto; font-size:10px; line-height:17px;}   /* the first line's height */
.rt .ptag{color:var(--dim); letter-spacing:.12em;}
.rt .ber{margin-left:auto; color:var(--green);}
/* Reserved height, so a short or long text does not make the area tremble. ONE line, which is
   the whole of what can arrive: the field is 64 characters at most and the panel is two modules
   wide, where 64 monospace characters take about 460 px of the 780 available. Reserving a second
   line dug a permanent hole for a wrap that cannot happen. It only can below roughly 570 px of
   viewport, where the layout is stacked and nothing sits under this to be pushed. */
#vRt{flex:1 1 auto; min-width:0; font-size:12px; min-height:17px; line-height:1.4; display:block;}
/* Fixed-width overmodulation indicator, so its appearance does not shift the value. */
#vOvm{display:inline-block; width:16px; text-align:center;}

/* Each slot carries the class of the widget occupying it, set at mount time, so a widget's
   own styles attach without depending on its position. The prefix avoids colliding with the
   widget's internal classes. */
/* A panel is a CONTAINER of slots, not a slot: it stacks as many cards as the markup puts in
   it, and every container has the same box, because a widget must not look different for
   having moved.

   The slots SHARE the panel's height, and the share of each is dragged on the boundary between
   two of them. The value is therefore continuous and lives in a style written by the script; the
   attribute here carries nothing but the default, and no height is ever frozen in pixels.

   A column of shares rather than a grid of equal rows, because a slot must be able to take NO
   share at all: an empty one that hands its room to a neighbour still has to exist, to be
   clickable, and equal rows would give it a full one. */
.side{display:flex; flex-direction:column; gap:7px; padding:6px; min-height:0;}
/* A ROW ITEM: what takes a share of a rows container's height. A slot, a container of slots
   stacked among them, or the whole middle row -- the page itself is such a container, which is
   why this hangs off `data-slots="rows"` and not off the panels alone.
   A share and not a content-driven height, and that is load-bearing: a card whose canvas grows
   to fill what it is given changes the height it was just measured in, so a content-sized item
   makes the resize observer fire on its own output and the page loops. The share decides the
   height first, which breaks the cycle. */
[data-slots="rows"] > [data-slot]{min-height:0; flex:1 1 0;}
/* An empty slot is still a PLACE, not a hole: it keeps an outline and its corner mark, which
   is the only way a widget can be put back into it. Nothing to see, so the mark is what there
   is to see, and it does not wait for a hover to appear. */
.wslot-empty{position:relative; background:#090d11; border:1px dashed #16212a; border-radius:2px;}
.wslot-empty .wnav{opacity:.5;}
.wslot-empty:hover .wnav{opacity:1;}
/* Folded: the script gives it no share, so only its own height is stated here. */
[data-slots="rows"] > [data-slot].wslot-folded{flex-basis:auto; height:22px;}
/* An empty CONTAINER shows nothing at all. An empty slot inside a panel is a place you can see
   and fill, and its mark is the way in; a whole region of the page with nothing in it is just a
   bar across the screen. The way back into it is the `Move` entry on the widget that belongs
   there, and the script keeps the last visible place of a family from collapsing. */
[data-slots="rows"] > [data-slots].wslot-folded{display:none;}
.wslot-folded .wnav{top:1px;}   /* the bar is the height of the mark, so it is centred in it */

/* The boundary between two stacked slots, which is where their shared height is traded. It sits
   IN the gap and takes none of it: zero height, and one of the two gaps it introduced pulled
   back, so adding a handle does not move anything. Its grab area is deliberately taller than the
   gap, since a seven-pixel target is not one. */
.wgrip{flex:0 0 auto; height:0; margin-top:-7px; position:relative; z-index:4;}
.wgrip::after{content:""; position:absolute; left:0; right:0; top:-4px; height:15px;
              cursor:row-resize;}
.wgrip i{position:absolute; top:-7px; left:50%; transform:translateX(-50%); font-style:normal;
         font-size:11px; line-height:14px; color:var(--dim2); opacity:0; pointer-events:none;
         transition:opacity .15s ease; background:var(--panel2); padding:0 6px; border-radius:3px;}
.side:hover .wgrip i{opacity:1;}
.wgrip:hover i{color:var(--teal); opacity:1;}
/* Bitten onto another line of the page: said while the gesture is still under the finger, since
   after the release there is nothing left to explain. */
.wgrip.snap i{color:var(--teal); opacity:1; box-shadow:0 0 0 1px var(--teal);}
/* Touch, where there is no hover: the handle states itself, faded. */
@media (hover:none){ .wgrip i{opacity:.45;} }
/* A PAIR: a container whose slots share a WIDTH instead of a height. Two modules cut in two,
   which is the only place in the page a one-module widget fits in the middle column. The
   columns are strictly equal, because these are interchangeable slots and not two boxes each
   tailored to its content: a widget that changes side must neither grow nor shrink, otherwise
   the swap reads as a layout jump. Same gap as the container it sits in, so its boundary reads
   like every other. */
.ez2{display:grid; grid-template-columns:1fr 1fr; gap:7px; align-items:stretch; min-height:0;}

/* A widget host, wherever it sits: a card in a lateral panel and one in the middle column are
   the same object, so a widget that moves finds the same box. */
.wcard{margin:0; display:flex; flex-direction:column; background:#0b1116; border:1px solid #141e22;
       border-radius:2px; padding:9px 11px; min-height:0;   /* the card can shrink without its canvas overflowing */
       overflow:hidden;}
       /* A card never paints outside its box. Dragged smaller than its content it CLIPS, where
          overflowing would spill it over the very neighbour whose room it just gave up -- two
          instruments printed on top of each other, which reads as a fault and not as a limit. */
.wcard.w-geo .geo{flex:1; margin-top:0;}               /* the transmitter fills its card */
.wcard.w-filter canvas{flex:1; height:auto; min-height:48px; margin-top:6px;}   /* scales instead of clipping, with a readable floor */
/* Full-width text above both cards, so it fits on one line far more often. */
.rtfull{margin:0 0 8px;}
/* Continuous-trace meters. */
.meter{margin:0 0 8px; font-family:var(--mono);}
.meter .mh{display:flex; align-items:baseline; gap:8px; margin-bottom:3px;}
.meter .mv{margin-left:auto; color:var(--ink); font-size:14px;}
.meter .mv small{color:var(--dim); font-size:10px;}
.meter .mpk{color:var(--amber); font-size:11px;}
/* The numeric reminder attached to the bar is deliberately discreet: the reading happens in
   the tile, with its decimal. Here the integer is enough to place the bar on its scale. */
.lvlbig{margin-left:auto; color:var(--dim); font-size:12px; line-height:1;}
.lvlbig small{font-size:9px; color:var(--dim2); margin-left:2px;}
.lvlbig .pk{font-size:11px; color:var(--green); font-style:normal; margin-left:8px;}
/* Signal row inside the banner: an extensible bar on the left, the reminder on the right. */
.sigrow{display:flex; align-items:center; gap:12px;}
.sigrow .nbody{flex:1; min-width:0;}
/* Transmitter: the resolved station, sole occupant of its card. */
/* Fixed-column grid: target, text, logo. Every slot has a frozen width, filled or not, so
   the group always occupies the same width and the target never moves, station resolved or
   not. Centred both ways inside the card. The flag lives on the city line, next to the
   place it qualifies.
   The TARGET LEADS and the logo closes the row, because the logo is the slot that empties
   on its own: a station without one, or nothing identified at all. Held first, its absence
   dragged the target sideways and let it grow into the freed column — the instrument
   changing size according to whether a brand image exists, which is not a measurement. */
.geo{display:grid; grid-template-columns:minmax(104px, 172px) minmax(0, 1fr) auto; gap:16px;
     align-content:center; align-items:center; margin-top:8px; min-height:56px; min-width:0;}
/* At rest the slot is kept, with the empty logo box and the rose visible and the rest
   hidden, so nothing collapses and what sits above never drops. */
.geo.empty .gtxt, .geo.empty .gdist{visibility:hidden;}
.gslot{flex:0 0 auto; width:88px; height:66px; border-radius:5px; background:#0c1218;   /* fixed logo slot */
  display:flex; align-items:center; justify-content:center; overflow:hidden;}
.glogo{max-width:100%; max-height:100%; object-fit:contain;}
.glogo[hidden]{display:none;}   /* no logo: the slot stays empty and keeps its footprint */
.gcompassbox{display:flex; flex-direction:column; align-items:center; justify-content:center;
             gap:3px; flex:0 0 auto; min-height:0; max-height:100%;
             position:relative;}   /* the hover hint is placed against this box */
/* The target follows the width its column is given, which the grid grows into the card
   rather than leaving as margin. Square by ratio, never by a frozen pair of pixel values,
   so it cannot go oval and cannot overflow the column on a narrow panel. */
.gcompass{width:100%; height:auto; aspect-ratio:1; flex:0 1 auto; min-height:0;
          max-height:100%;}                      /* a short, wide card shrinks it rather than clipping */
/* At rest, a faded placeholder. The state changes instantly: fading in over 400 ms would
   mean the tile still looks empty when the receiver already knows where it is. */
.geo.empty .gcompass{opacity:.3;}
.gring{fill:none; stroke:#1c2a38; stroke-width:1.4;}
.gringi{stroke:#16212c; stroke-width:1;}     /* inner range rings: a scale, not a frame */
.gtick{stroke:#2a3a4a; stroke-width:1.1; stroke-linecap:round;}   /* cardinal direction ticks */
.gtickn{stroke:#5a6b7c; stroke-width:1.5;}                        /* north is the reference */
.gme{stroke:var(--dim); stroke-width:1.1; stroke-linecap:round; fill:none;}   /* the receiver */
/* A site on the target. Amber is the resolved one, as everywhere else in the page; the
   others carry the measurement colour, their fill following plausibility -- the same
   language the station list uses for signal, so the page says it once. */
.gmark{fill:var(--teal); stroke:var(--teal); stroke-opacity:.55; stroke-width:.9;}
.gmark.gwin{fill:var(--amber); stroke:none;}
.gspoke{stroke:var(--amber); stroke-width:1.3; opacity:.55;}
/* Nothing was identified: the tile shows what the catalogue EXPECTS on this channel. It
   must not be mistakable for a reading, so it drops the two marks of one — the amber and
   the logo — and recedes as a whole. The head of the list is still the largest mark, so
   the tile and the target name the same site, but it is hollow: an outline, not a fill. */
.geo.possible{opacity:.82;}
/* The slot keeps its footprint, emptied: dropping it out of the layout would hand its width
   to the target and the tile would change geometry between modes. Same instrument, same
   size, whether the channel was identified or not. */
.geo.possible .gslot{visibility:hidden;}
.gmark.gtop{fill:none; stroke:var(--teal); stroke-opacity:1; stroke-width:1.6;}
.geo.possible .gname #gSite{color:var(--dim);}     /* a name proposed, not established */
.geo.possible .gmeta #gPi{color:var(--dim2); font-weight:400;}
/* The scope of the claim leads the list: six of eight and six of two hundred are not the
   same statement. */
.galtx.gscope{color:var(--dim); margin-bottom:1px;}
/* Hover target, wider than the mark and invisible. Aiming at a three-pixel dot is not a
   reading gesture. */
.ghit{fill:transparent; cursor:help;}
/* The hint appears on hover with NO delay and no transition, positioned on the mark itself
   rather than on the pointer, so it does not jitter under the hand. */
/* It wraps rather than running past the card's edge, which clips: the width is capped on the
   card by the code that places it, and a cut-off name is the one thing a hint must not have. */
.ghint{position:absolute; left:0; top:0; transform:translate(-50%,-160%); z-index:3;
  width:max-content;   /* or it would fold at the width of the narrow column it is anchored in */
  pointer-events:none; font-family:var(--mono); font-size:10px; text-align:center;
  color:var(--ink); background:#0b1218; border:1px solid var(--edge); border-radius:2px;
  padding:2px 6px; box-shadow:0 2px 8px rgba(0,0,0,.5);}
.ghint[hidden]{display:none;}
/* No transition on the marks, deliberately. A mark gliding towards its new position spends
   half a second showing a bearing that is NO LONGER TRUE -- a stale reading, not an
   animation. The tuner jumped; the target jumps with it. */
.gdist{font-family:var(--mono); font-size:12px; color:var(--dim); text-align:center; white-space:nowrap; line-height:1;}
.gdist #gBrg, .gdist #gDist{color:var(--ink);}            /* bearing and distance under the target */
.gtxt{min-width:0; line-height:1.3; overflow:hidden;}   /* fixed column width from the grid */
.gname{display:flex; align-items:center; gap:6px; min-width:0;}
/* The site is the heading of this tile. The flag sits with it, since it qualifies the place
   and not the programme. */
.gname #gSite{font:600 15px var(--sans); color:#eaf2f7; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0;}
.gname #gFlag{font-style:normal; font-size:13px; flex:0 0 auto;}
/* The country, in the list under the site. */
.gcc{font-style:normal; flex:0 0 auto;}
/* A HUE PER COUNTRY, and it is not decoration. Most systems carry no flag glyph and fall
   back to the two letters of the code, at the smallest size on the page: six of them stacked
   in the list are six grey letter pairs, and the eye separates none of them. A colour sorts
   them before they are read. Where the flag does render, the emoji is an image and ignores
   this — nothing is lost either way.
   The hues come from the flags, because a colour that has to be learnt is worth less than
   one already known. Only countries with an unmistakable colour identity are listed; the
   rest keep the neutral, which says "no claim" rather than inventing a meaning. Distant
   countries may share a family: what has to be separable is the handful that can appear on
   ONE channel, not the whole continent at once. */
.geo [data-cc="FR"]{color:#6f9dff;}   /* bleu */
.geo [data-cc="GB"]{color:#9b8ef2;}   /* Union blue */
.geo [data-cc="IE"]{color:#86cf55;}   /* green */
.geo [data-cc="NL"]{color:#ff9440;}   /* oranje */
.geo [data-cc="BE"]{color:#c9a02e;}   /* the yellow of the tricolour */
.geo [data-cc="LU"]{color:#4fc6e8;}   /* light blue */
.geo [data-cc="DE"]{color:#ebc54a;}   /* gold */
.geo [data-cc="CH"]{color:#f2695f;}   /* red */
.geo [data-cc="AT"]{color:#f0919c;}   /* red-white-red */
.geo [data-cc="IT"]{color:#3fc990;}   /* verde */
.geo [data-cc="ES"]{color:#f59a3c;}   /* rojigualda */
.geo [data-cc="AD"]{color:#d9772e;}   /* the red of the Pyrenean tricolour */
.geo [data-cc="PT"]{color:#35c2a6;}   /* verde */
.geo [data-cc="MC"]{color:#f0637a;}   /* red */
/* The programme keeps a line, quieter than the site: the logo already says it, and this is
   the fallback for a station whose logo is missing or fails to load. */
.gline #gProg{color:var(--dim);}
.gline,.gmeta{font-family:var(--mono); font-size:11px; color:var(--dim); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.gline b,.gmeta b{color:var(--ink); font-weight:600;}
/* Ambiguity marker: shown only when the location is uncertain, and nothing when it is not.
   Instrument spirit: only the exception is signalled. */
.gq{flex:0 0 auto; font-style:normal; font-size:12px; line-height:1; cursor:help;
  color:var(--amber);}
.gq[hidden]{display:none;}
/* The other plausible sites, under the resolved one. Recessed a step further than the
   metadata line: they are what the identity COULD be, not what the tile states. One per
   line, since a distance and a power run together are unreadable in a row. */
.galt{margin-top:3px; font-family:var(--mono); font-size:10px; color:var(--dim2);
      display:flex; flex-direction:column; gap:1px; min-width:0;}
.galt[hidden]{display:none;}
.galtx{white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
/* Plausibility relative to the resolved site, never an absolute figure: the score has no
   unit and means nothing outside the candidates of one frequency. */
.galtx i{font-style:normal; color:var(--dim); margin-left:6px;}
/* The programme, when a line carries one. It only does where every line is a DIFFERENT
   broadcaster, and there it is the information the line exists for, so it leads and it is
   the one thing here written in the reading colour. */
.galtx b{color:var(--ink); font-weight:600;}
/* The bearing rides with the name because it is the one figure that decides a gesture --
   where to point the antenna. Everything else about the site is one hover away: six lines
   packed to the ellipsis are six lines nobody reads. */
.galtx em{font-style:normal; color:var(--dim); margin-left:4px;}
.galtx[title]{cursor:help;}
/* Nothing identified: the logo slot is empty, so the programme has nowhere else to appear.
   It stops being the quiet echo of a logo and becomes the name the tile proposes. */
.geo.possible .gline #gProg{color:#c8d6e0;}
/* The continuous-trace meter itself lives in base.css: more than one page carries one, and
   two copies of an instrument's look would drift apart. Only the variants specific to this
   panel stay here. */
.track.dev .grad{background:linear-gradient(90deg,var(--blue) 0%,var(--teal) 55%,var(--amber) 82%,var(--red) 100%);}
.track.dev .peak{background:var(--amber); box-shadow:0 0 6px var(--amber);}
/* Header measurement block: label, bar, value, stackable. Shared idiom across the panel's
   widgets, which is what makes them look like instruments of the same family. Pushed to the
   right, so a header keeps controls on the left and measurement on the right. */
.wcard .fq{margin-left:auto; display:flex; flex-direction:column; gap:2px;}
.wcard .fqr{display:flex; align-items:center; gap:6px;
                 font-family:var(--mono); font-size:11px; color:var(--ink);}
/* Fixed-width, right-aligned label column, so both bars start at the same abscissa;
   otherwise labels of different lengths offset them and the block loses its composure. */
.wcard .fqr b{min-width:16px; text-align:right; color:var(--dim);
                   font-weight:400; font-size:9px; text-transform:uppercase; letter-spacing:.1em;}
.wcard .fqv{min-width:30px; text-align:right;}   /* fixed pitch, so the value does not shake the bar */
/* A fine bar, graduated as a watermark: the graduation is what distinguishes an instrument
   from a progress bar. The peak marker comes from the shared track. */
.track.mp{width:70px; height:5px; flex:0 0 auto;
          background-image:repeating-linear-gradient(90deg,#243442 0 1px,transparent 1px 25%);
          background-color:#0b1116;}
.track.mp .clip,.track.mp .grad,.track.mp .peak{display:block;}
.track.mp .grad{background:linear-gradient(90deg,var(--teal) 0%,var(--amber) 55%,var(--red) 100%);}
.track.mp .peak{width:2px; background:var(--amber); box-shadow:0 0 5px var(--amber);}
.meter.w-filter .mv{font-size:11px;}
/* Filter header: the tuner toggles plus the cyclic bandwidth button. */
.meter.w-filter .mh{align-items:center; flex-wrap:wrap;}   /* buttons centre rather than sit on a baseline */
.meter.w-filter .ftog{display:flex; gap:5px;}
.meter.w-filter .ftog .btn.tog{padding:3px 8px; font-size:10px; letter-spacing:.08em;
      touch-action:none;}                          /* compact toggles; touch behaviour left to the long press */
/* When the equaliser follows the measurement, the button keeps the real state and the small
   marker says only who decides. Full size, not a superscript: a marker that must be guessed
   is useless. Always in the DOM and merely transparent when the automation is off, so the
   button width never changes and the header never jumps. */
.meter.w-filter .ftog .btn.tog i{font-style:normal; font-size:inherit; letter-spacing:0;
      margin-left:5px; color:var(--teal); opacity:0;}
.meter.w-filter .ftog .btn.tog.auto i{opacity:.95;}
/* The AGC is a setting with four values rather than a state, so its marker carries the
   value and is always readable -- there is no "on" for the button itself to show. */
.meter.w-filter .ftog .btn.tog.agc i{opacity:.95; color:#eaf2f7;}
.meter.w-filter .bwbtn{font-family:var(--mono); font-size:11px; letter-spacing:.06em; color:#eaf2f7;
      background:#12202a; border:1px solid #22343e; border-radius:2px; padding:3px 9px; margin-left:2px;
      min-width:52px; text-align:center; cursor:pointer; user-select:none; touch-action:none;}
.meter.w-filter .bwbtn:hover{border-color:var(--teal);}
.meter.w-filter .bwbtn:active{transform:translateY(1px);}
.meter.w-filter .bwbtn.auto{color:var(--teal); border-color:#1f5048; background:#0d1f1c; font-weight:700;}
.meter.w-filter canvas{display:block; width:100%; height:46px; border-radius:2px; background:#0a0f14; border:1px solid #152029;}

/* RF impairments are drawn inside the filter itself; the header keeps only the value. */
.cn{display:flex; flex-direction:column; justify-content:flex-end; text-align:right; padding-bottom:1px;}
.cn .ik{font-size:9px; letter-spacing:.1em; text-transform:uppercase; color:var(--dim);}
.cn .cnv{font-family:var(--mono); font-size:16px; color:var(--ink);}
.cn .cnv small{font-size:9px; color:var(--dim);}

/* Expert RDS widget: the health and composition of the stream, not its content.
   Column layout: each row carries its label on the left at a fixed width, with its content
   on the right. No title lines, so five sections give back five lines to the only list that
   can genuinely overflow. The column of aligned labels also gives the composure of an
   instrument sheet. */
.meter.w-rds{font-family:var(--mono);}
/* Stream lost: the whole instrument fades. Everything on this card was read from a subcarrier
   that is no longer arriving, so dimming one field of it would be a half-truth -- the block
   rate would stay bright next to a group profile nobody is receiving any more. Kept rather than
   blanked, the same convention as the identity: the last known state, without the claim that it
   is current. A transition, because RDS comes and goes at the edge of reception and a card
   flicking between two opacities is unreadable. */
.meter.w-rds{transition:opacity .3s ease;}
.meter.w-rds.stale{opacity:.42;}
/* Header on the filter's template: title on the left, measurement on the right. The right
   padding leaves room for the navigation chevrons floating in the corner. */
.meter.w-rds .mh{align-items:center; gap:9px; margin-bottom:6px; padding-right:44px;}
.meter.w-rds .rcap{flex:0 0 auto; font-size:10px; letter-spacing:.14em; text-transform:uppercase;
                   color:var(--dim);}
/* The stacked bar matches the header bars of the filter -- same height, same background --
   so both widgets align their measurements exactly. */
.meter.w-rds .rstack{flex:1; min-width:70px; display:flex; height:13px; border-radius:2px;
     overflow:hidden; background:#0b1116; border:1px solid #182430;
     box-shadow:inset 0 1px 2px rgba(0,0,0,.6);}
.meter.w-rds .rstack span{height:100%; width:0; transition:width .25s ease;}
.meter.w-rds .rstack .e0{background:var(--green);}
.meter.w-rds .rstack .e1{background:var(--amber);}
.meter.w-rds .rstack .e2{background:#c98a2a;}
.meter.w-rds .rstack .e3{background:var(--red);}
/* A row is a fixed label column plus content. The label aligns to the top, so content that
   wraps over several lines does not push it to the middle. */
.meter.w-rds .rr{display:flex; gap:8px; margin-bottom:5px; min-width:0;}
.meter.w-rds .rr:last-child{margin-bottom:0;}
.meter.w-rds .rk{flex:0 0 46px; padding-top:2px; font-size:9px; letter-spacing:.1em;
                 text-transform:uppercase; color:var(--dim2); line-height:13px;}
.meter.w-rds .rk b{color:var(--dim); font-weight:400;}   /* count attached to the label */
/* Compact chips. For groups, opacity carries the share, so the profile reads by contrast. */
.meter.w-rds .rgrp{display:flex; flex-wrap:wrap; gap:3px; align-content:flex-start;
                   min-width:0; min-height:15px;}
.meter.w-rds .gchip{font-size:10px; line-height:15px; color:var(--teal); background:#0d1f1c;
     border:1px solid #1f5048; border-radius:2px; padding:0 4px; white-space:nowrap;}
.meter.w-rds .gchip b{color:var(--dim); font-weight:400; margin-left:3px; font-size:9px;}
.meter.w-rds .gnone{color:var(--dim2); font-size:11px; line-height:15px;}
/* The only elastic row: it takes all the free height and scrolls beyond it. */
.meter.w-rds .rgrow{flex:1 1 auto; min-height:0;}
.meter.w-rds .rgrow .rgrp{flex:1; min-height:0; overflow-y:auto;}
/* Frequency chips take a neutral tint, distinct from the group chips, which speak about the
   protocol. Tabular figures keep the grid regular whatever the value. */
.meter.w-rds .raf .gchip{color:var(--ink); background:#101820; border-color:#22343e;
                         font-variant-numeric:tabular-nums;}
/* A station may include its own frequency in its list: it is shown, not hidden. */
.meter.w-rds .raf .gchip.self{color:var(--teal); border-color:#1f5048; background:#0d1f1c;}
/* Application chips carry a distinct tint from the raw group chips. */
.meter.w-rds .gchip.oda{color:var(--amber); background:#1a1508; border-color:#3d3115;}
.meter.w-rds .gchip.oda b{color:var(--dim);}
/* Announced networks: the identity code, always present, plus a name that is often partial.
   They wrap, so two short entries fit side by side. */
.meter.w-rds .reon{display:flex; flex-wrap:wrap; gap:2px 10px; font-size:11px;
                   line-height:15px; min-height:15px; min-width:0;}
.meter.w-rds .eline{display:inline-flex; align-items:center; gap:5px; white-space:nowrap;}
.meter.w-rds .eline b{color:var(--teal); font-weight:400; font-variant-numeric:tabular-nums;}
.meter.w-rds .eps{color:var(--ink);}
/* The announced traffic flags and the declared flags share one small template. An active
   traffic announcement is the historical purpose of the feature, and the only thing in this
   widget that deserves a strong colour. */
.meter.w-rds .et{font-style:normal; font-size:8px; letter-spacing:.08em; color:var(--dim2);
                 border:1px solid #1f2a33; border-radius:2px; padding:0 3px; line-height:12px;}
.meter.w-rds .et.on{color:#241c02; background:var(--amber); border-color:var(--amber);}
/* Declared states in the header, between the title and the measurement. They are qualifiers
   of the station, so they border the measurement instead of taking a line of their own, and
   each disappears when not received: an instrument header is not a form and reserves no
   empty box for what the station does not emit. If space runs short, these give way, never
   the measurement. */
.meter.w-rds .rst{display:flex; align-items:center; gap:7px; min-width:0; overflow:hidden;
                  font-size:10px; color:var(--dim); white-space:nowrap;}
.meter.w-rds .rclk:not(:empty){color:var(--ink); font-variant-numeric:tabular-nums;}
.meter.w-rds .rms:not(:empty){color:var(--dim);}
/* The country code is a raw hex pair, so it is set in the monospace face the rest of the
   measured values use — it reads as a code, not as a word. */
.meter.w-rds .recc:not(:empty){color:var(--dim); font-family:var(--mono); letter-spacing:.04em;}
.meter.w-rds .di{display:inline-flex; gap:3px; flex:0 0 auto;}
.meter.w-rds .di .et.on{color:#06231c; background:var(--teal); border-color:var(--teal);}

/* Widget navigation: two chevrons in the top corner of each slot. Quiet at rest, since this
   is a layout command and not an instrument, and clear when the card is hovered. They are
   not in the widgets' markup: a widget does not carry the controls that replace it. */
.wcard{position:relative;}
.wnav{position:absolute; top:4px; right:6px; z-index:3; display:flex; gap:1px;
      opacity:0; transition:opacity .15s ease;}
.wcard:hover .wnav, .wnav:focus-within{opacity:1;}
.wnav i{font-style:normal; font-size:14px; line-height:1; color:var(--dim2); cursor:pointer;
        padding:1px 5px 3px; border-radius:3px; user-select:none;}
.wnav i:hover{color:var(--ink); background:#172530;}
.wnav i:active{transform:translateY(1px);}
/* Touch, where there is no hover: always visible but faded. */
@media (hover:none){ .wnav{opacity:.45;} }

/* Announced networks, one line each. */
.meter.w-rds .reon{display:flex; flex-wrap:wrap; gap:3px 10px; font-size:11px; min-height:15px;}
.meter.w-rds .eline{display:inline-flex; align-items:center; gap:5px; white-space:nowrap;}
.meter.w-rds .eline b{color:var(--teal); font-weight:400; font-variant-numeric:tabular-nums;}
.meter.w-rds .eps{color:var(--ink);}
/* Traffic flags of an announced network: an active one means traffic information is running
   over there right now. */
.meter.w-rds .et{font-style:normal; font-size:8px; letter-spacing:.08em; color:var(--dim2);
                 border:1px solid #1f2a33; border-radius:2px; padding:0 3px; line-height:12px;}
.meter.w-rds .et.on{color:#241c02; background:var(--amber); border-color:var(--amber);}

/* RDS quality is the same instrument as the signal meter, beside it, because RDS is the
   other received channel and its health reads like a level. A shorter bar, since a
   percentage needs no graduation, and above all a solid bar whose hue is the measurement.
   The colour is set from the script, so no background is declared here -- it would be
   overwritten -- and there is no threshold: the ramp is continuous. */
.lhead .rdsq{flex:0 0 auto;}   /* attached to its own flags, in the same block */
.lhead .rdsq .nbody{width:120px;}
/* The peak marker stays white, like the signal meter: the track colour already states the
   quality, and a tinted marker would compete with it. A marker marks a position; it does
   not judge. */
.track.rq .grad{transition:background .25s linear;}   /* the hue slides, so no colour jumps between measurements */
.lhead .rdsq .lvlbig{margin-left:8px;}

/* Application chips. */
.meter.w-rds .gchip.oda{color:var(--amber); background:#1a1508; border-color:#3d3115;}
.meter.w-rds .gchip.oda b{color:var(--dim);}
/* Flags the station declares about itself, at the end of the rare-field row, on the same
   template as the network flags. A flag never received is not rendered at all, which is not
   the same as being declared false. */
.meter.w-rds .di{margin-left:auto; display:inline-flex; gap:3px;}
.meter.w-rds .di .et.on{color:#06231c; background:var(--teal); border-color:var(--teal);}

/* Tagged text: the class name small, the value plain -- the content reads first and the
   label is only a qualifier. Wraps like the chips. */
.meter.w-rds .rtp{display:flex; flex-wrap:wrap; gap:3px 8px; min-width:0; min-height:15px;}
.meter.w-rds .tag{display:inline-flex; align-items:baseline; gap:4px;
                  font-size:11px; line-height:15px; color:var(--ink); min-width:0;}
.meter.w-rds .tag i{font-style:normal; font-size:8px; letter-spacing:.08em; text-transform:uppercase;
                    color:var(--teal); flex:0 0 auto;}
/* A finished item keeps its tags but they are stale, so they are dimmed rather than erased.
   Same convention as an identity kept when RDS is lost. */
.meter.w-rds .tag.stale{opacity:.5;}

/* A confirmed alternative frequency, meaning the same reliable identity was found on it,
   takes the amber accent. A merely announced one stays neutral: that is the difference
   between "the broadcaster claims it" and "we verified it". The frequency being listened to
   wins, being confirmed by construction. */
.meter.w-rds .raf .gchip.known{color:var(--amber); border-color:#3d3115; background:#151107;}
/* Clickable: a click tunes. The cursor and the hover say so, without a button or a label. */
.meter.w-rds .raf .gchip{cursor:pointer;}
.meter.w-rds .raf .gchip:hover{border-color:var(--teal); color:#eaf2f7;}
.meter.w-rds .raf .gchip:active{transform:translateY(1px);}
