/* fonts.css */
/* Helyben tárolt betűkészletek. GENERÁLT FÁJL.
 *
 * Miért nem a fonts.googleapis.com: a PWA offline is működik, a külső betű-betöltés viszont
 * hálózat nélkül rendszerbetűre esik vissza, és minden látogatás kimegy a Google felé.
 * MINDEN részhalmaz megvan, pontosan úgy, ahogy a Google is adta: a cél a self-hosting, nem a
 * betűkészlet megváltoztatása. A @font-face unicode-range-e miatt a böngésző továbbra is csak
 * azokat a részhalmazokat tölti le, amikre a lapon lévő szöveghez ténylegesen szükség van. */

/* Open Sans cyrillic-ext */
@font-face {
  font-family: 'Open Sans';
  font-style: italic;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-400-italic-cyrillic-ext.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

/* Open Sans cyrillic */
@font-face {
  font-family: 'Open Sans';
  font-style: italic;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-400-italic-cyrillic.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/* Open Sans greek-ext */
@font-face {
  font-family: 'Open Sans';
  font-style: italic;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-400-italic-greek-ext.woff2) format('woff2');
  unicode-range: U+1F00-1FFF;
}

/* Open Sans greek */
@font-face {
  font-family: 'Open Sans';
  font-style: italic;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-400-italic-greek.woff2) format('woff2');
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}

/* Open Sans hebrew */
@font-face {
  font-family: 'Open Sans';
  font-style: italic;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-400-italic-hebrew.woff2) format('woff2');
  unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}

/* Open Sans math */
@font-face {
  font-family: 'Open Sans';
  font-style: italic;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-400-italic-math.woff2) format('woff2');
  unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
}

/* Open Sans symbols */
@font-face {
  font-family: 'Open Sans';
  font-style: italic;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-400-italic-symbols.woff2) format('woff2');
  unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
}

/* Open Sans vietnamese */
@font-face {
  font-family: 'Open Sans';
  font-style: italic;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-400-italic-vietnamese.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}

/* Open Sans latin-ext */
@font-face {
  font-family: 'Open Sans';
  font-style: italic;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-400-italic-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Open Sans latin */
@font-face {
  font-family: 'Open Sans';
  font-style: italic;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-400-italic-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Open Sans cyrillic-ext */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-400-normal-cyrillic-ext.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

/* Open Sans cyrillic */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-400-normal-cyrillic.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/* Open Sans greek-ext */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-400-normal-greek-ext.woff2) format('woff2');
  unicode-range: U+1F00-1FFF;
}

/* Open Sans greek */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-400-normal-greek.woff2) format('woff2');
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}

/* Open Sans hebrew */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-400-normal-hebrew.woff2) format('woff2');
  unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}

/* Open Sans math */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-400-normal-math.woff2) format('woff2');
  unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
}

/* Open Sans symbols */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-400-normal-symbols.woff2) format('woff2');
  unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
}

/* Open Sans vietnamese */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-400-normal-vietnamese.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}

/* Open Sans latin-ext */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-400-normal-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Open Sans latin */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-400-normal-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Open Sans cyrillic-ext */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-500-normal-cyrillic-ext.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

/* Open Sans cyrillic */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-500-normal-cyrillic.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/* Open Sans greek-ext */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-500-normal-greek-ext.woff2) format('woff2');
  unicode-range: U+1F00-1FFF;
}

/* Open Sans greek */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-500-normal-greek.woff2) format('woff2');
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}

/* Open Sans hebrew */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-500-normal-hebrew.woff2) format('woff2');
  unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}

/* Open Sans math */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-500-normal-math.woff2) format('woff2');
  unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
}

/* Open Sans symbols */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-500-normal-symbols.woff2) format('woff2');
  unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
}

/* Open Sans vietnamese */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-500-normal-vietnamese.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}

/* Open Sans latin-ext */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-500-normal-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Open Sans latin */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-500-normal-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Open Sans cyrillic-ext */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-600-normal-cyrillic-ext.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

/* Open Sans cyrillic */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-600-normal-cyrillic.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/* Open Sans greek-ext */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-600-normal-greek-ext.woff2) format('woff2');
  unicode-range: U+1F00-1FFF;
}

/* Open Sans greek */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-600-normal-greek.woff2) format('woff2');
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}

/* Open Sans hebrew */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-600-normal-hebrew.woff2) format('woff2');
  unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}

/* Open Sans math */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-600-normal-math.woff2) format('woff2');
  unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
}

/* Open Sans symbols */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-600-normal-symbols.woff2) format('woff2');
  unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
}

/* Open Sans vietnamese */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-600-normal-vietnamese.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}

/* Open Sans latin-ext */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-600-normal-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Open Sans latin */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-600-normal-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Open Sans cyrillic-ext */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-700-normal-cyrillic-ext.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

/* Open Sans cyrillic */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-700-normal-cyrillic.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/* Open Sans greek-ext */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-700-normal-greek-ext.woff2) format('woff2');
  unicode-range: U+1F00-1FFF;
}

/* Open Sans greek */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-700-normal-greek.woff2) format('woff2');
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}

/* Open Sans hebrew */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-700-normal-hebrew.woff2) format('woff2');
  unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}

/* Open Sans math */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-700-normal-math.woff2) format('woff2');
  unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
}

/* Open Sans symbols */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-700-normal-symbols.woff2) format('woff2');
  unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
}

/* Open Sans vietnamese */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-700-normal-vietnamese.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}

/* Open Sans latin-ext */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-700-normal-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Open Sans latin */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/opensans-700-normal-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Roboto cyrillic-ext */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-400-normal-cyrillic-ext.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

/* Roboto cyrillic */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-400-normal-cyrillic.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/* Roboto greek-ext */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-400-normal-greek-ext.woff2) format('woff2');
  unicode-range: U+1F00-1FFF;
}

/* Roboto greek */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-400-normal-greek.woff2) format('woff2');
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}

/* Roboto math */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-400-normal-math.woff2) format('woff2');
  unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
}

/* Roboto symbols */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-400-normal-symbols.woff2) format('woff2');
  unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
}

/* Roboto vietnamese */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-400-normal-vietnamese.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}

/* Roboto latin-ext */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-400-normal-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Roboto latin */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-400-normal-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Roboto cyrillic-ext */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-500-normal-cyrillic-ext.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

/* Roboto cyrillic */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-500-normal-cyrillic.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/* Roboto greek-ext */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-500-normal-greek-ext.woff2) format('woff2');
  unicode-range: U+1F00-1FFF;
}

/* Roboto greek */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-500-normal-greek.woff2) format('woff2');
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}

/* Roboto math */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-500-normal-math.woff2) format('woff2');
  unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
}

/* Roboto symbols */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-500-normal-symbols.woff2) format('woff2');
  unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
}

/* Roboto vietnamese */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-500-normal-vietnamese.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}

/* Roboto latin-ext */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-500-normal-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Roboto latin */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-500-normal-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Roboto cyrillic-ext */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-700-normal-cyrillic-ext.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

/* Roboto cyrillic */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-700-normal-cyrillic.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/* Roboto greek-ext */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-700-normal-greek-ext.woff2) format('woff2');
  unicode-range: U+1F00-1FFF;
}

/* Roboto greek */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-700-normal-greek.woff2) format('woff2');
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}

/* Roboto math */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-700-normal-math.woff2) format('woff2');
  unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
}

/* Roboto symbols */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-700-normal-symbols.woff2) format('woff2');
  unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
}

/* Roboto vietnamese */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-700-normal-vietnamese.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}

/* Roboto latin-ext */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-700-normal-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Roboto latin */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/fonts/roboto-700-normal-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* generated_primitives.css */
/* =============================================================================
   generated_primitives.css — TIER 1 primitives (--nt-*)
   -----------------------------------------------------------------------------
   SOURCE: Nautech.Frontend.Common/Design/Palette.cs
   ============================================================================= */

:root {
    /* ── Azure (brand) — OKLCH hue ≈ 258.5° ─────────────────────────────── */
    --nt-azure-400: #4D9EFF;
    --nt-azure-500: #0073FF;
    --nt-azure-600: #0066E0;

    /* ── Crimson (night brand) — hue ≈ 27° ──────────────────────────────── */
    --nt-crimson-500: #B22222;

    /* ── Blue (notification "info" — deep indigo, distinct from brand) ────── */
    --nt-blue-800: #1E40AF;
    --nt-blue-900: #1E3A8A;

    /* ── Green (success) — hue ≈ 155° ───────────────────────────────────── */
    --nt-green-400: #47B777;
    --nt-green-600: #21834F;
    --nt-green-700: #1F6B41;
    --nt-green-800: #166534;

    /* ── Amber (warning) — hue ≈ 70° ────────────────────────────────────── */
    --nt-amber-400: #FFB147;
    --nt-amber-600: #D97706;
    --nt-amber-700: #B45309;
    --nt-amber-800: #92400E;

    /* ── Red (error / critical / delete) — hue ≈ 27° ────────────────────── */
    --nt-red-400: #F45A51;
    --nt-red-500: #EF4444;
    --nt-red-600: #DC2626;
    --nt-red-700: #B91C1C;

    /* ── Gold / Orange (fav accent) ─────────────────────────────────────── */
    --nt-gold:       #FFCF00;
    --nt-orange-400: #F97316;
    --nt-orange-600: #E06C00;

    /* ── Slate (neutral ramp) — Tailwind slate, low-chroma blue-grey ──────── */
    --nt-slate-100: #F1F5F9;
    --nt-slate-200: #E2E8F0;
    --nt-slate-300: #CBD5E1;
    --nt-slate-400: #94A3B8;
    --nt-slate-500: #64748B;
    --nt-slate-600: #475569;
    --nt-slate-700: #334155;
    --nt-slate-800: #1E293B;
    --nt-slate-900: #0F172A;

    /* ── Neutrals ───────────────────────────────────────────────────────── */
    --nt-white: #FFFFFF;
    --nt-black: #000000;

    /* ── rgb companions for alpha use (rgba(var(--nt-*-rgb), α)) ──────────── */
    --nt-azure-500-rgb:   0, 115, 255;
    --nt-azure-400-rgb:   77, 158, 255;
    --nt-crimson-500-rgb: 178, 34, 34;
    --nt-red-600-rgb:     220, 38, 38;
    --nt-red-500-rgb:     239, 68, 68;
    --nt-red-400-rgb:     244, 90, 81;
    --nt-white-rgb:       255, 255, 255;
    --nt-black-rgb:       0, 0, 0;
}

/* generated_color-tokens.css */
/* =============================================================================
   generated_color-tokens.css — TIER 2 semantics (--c-*)
   -----------------------------------------------------------------------------
   SOURCE: Common/Design/SemanticTokens.cs (Core) + Common/Design/WebSemanticTokens. */

/* ───────────────────────── LIGHT (base) ─────────────────────────────────── */
:root {
    /* Brand */
    --c-primary: var(--nt-azure-500);
    --c-primary-rgb: var(--nt-azure-500-rgb);
    --c-on-primary: var(--nt-white);
    /* Status */
    --c-success: var(--nt-green-600);
    --c-on-success: var(--nt-white);
    --c-error: var(--nt-red-600);
    --c-on-error: var(--nt-white);
    /* Accent */
    --c-favourite: var(--nt-gold);
    /* Notification level fills */
    --c-notif-info: var(--nt-blue-800);
    --c-notif-warning: var(--nt-amber-600);
    --c-notif-critical: var(--nt-red-600);
    --c-on-notif: var(--nt-white);
    /* Notification list states */
    --c-notif-ongoing: #EA580C;
    --c-notif-resolved: var(--nt-slate-500);
    --c-notif-ongoing-info: var(--nt-blue-800);
    --c-notif-ongoing-warning: var(--nt-amber-600);
    --c-notif-ongoing-critical: var(--nt-red-600);
    /* Surfaces */
    --c-surface-page: var(--nt-slate-200);
    --c-surface: var(--nt-white);
    --c-surface-elevated: #F3F3F3;
    --c-surface-hover: #DDDDDD;
    /* Borders */
    --c-border-subtle: rgba(0, 0, 0, 0.08);
    --c-divider: var(--nt-slate-100);
    /* Text */
    --c-text-primary: var(--nt-slate-900);
    --c-text-secondary: var(--nt-slate-500);
    --c-text-muted: var(--nt-slate-400);
    --c-text-faint: var(--nt-slate-300);
    --c-stale: var(--nt-slate-400);
    /* Shadows */
    --c-shadow-xs: rgba(0, 0, 0, 0.06);
    --c-shadow-sm: rgba(0, 0, 0, 0.10);
    --c-shadow-md: rgba(0, 0, 0, 0.20);
    --c-shadow-lg: rgba(0, 0, 0, 0.35);
    /* Media chrome (always dark) */
    --c-media-bg: var(--nt-black);
    --c-media-surface: #1A1A1A;
    --c-media-surface-raised: #2A2A2A;
    --c-media-surface-hover: #333333;
    /* Scrims */
    --c-scrim-weak: rgba(0, 0, 0, 0.50);
    --c-scrim-medium: rgba(0, 0, 0, 0.60);
    --c-scrim-strong: rgba(0, 0, 0, 0.80);
    /* Overlays */
    --c-overlay-light: rgba(var(--nt-white-rgb), 0.90);
    --c-overlay-loading: #BBCCDD;
    /* Map / Leaflet chrome */
    --c-map-surface: #262B36;
    --c-map-chrome: #2A2F3A;
    --c-map-chrome-border: #3A4150;
}

/* ───────────────────────────── DARK ─────────────────────────────────────── */
:root[data-theme="dark"] {
    --c-primary: var(--nt-azure-400);
    --c-primary-rgb: var(--nt-azure-400-rgb);
    --c-success: var(--nt-green-400);
    --c-error: var(--nt-red-400);
    --c-favourite: var(--nt-orange-400);
    --c-notif-info: var(--nt-blue-900);
    --c-notif-critical: var(--nt-red-700);
    --c-notif-ongoing: #FB7A3C;
    --c-notif-resolved: var(--nt-slate-400);
    --c-notif-ongoing-info: #818CF8;
    --c-notif-ongoing-warning: var(--nt-amber-400);
    --c-notif-ongoing-critical: var(--nt-red-400);
    --c-surface-page: var(--nt-slate-900);
    --c-surface: var(--nt-slate-800);
    --c-surface-elevated: #0F1D31;
    --c-surface-hover: #1F3A5F;
    --c-border-subtle: rgba(255, 255, 255, 0.08);
    --c-divider: rgba(255, 255, 255, 0.08);
    --c-text-primary: var(--nt-slate-100);
    --c-text-secondary: #8696AE;
    --c-text-faint: var(--nt-slate-700);
    --c-shadow-xs: rgba(0, 0, 0, 0.30);
    --c-shadow-sm: rgba(0, 0, 0, 0.40);
    --c-shadow-md: rgba(0, 0, 0, 0.50);
    --c-shadow-lg: rgba(0, 0, 0, 0.60);
    --c-overlay-loading: #0B1220;
}

/* ───────────────────────────── NIGHT ────────────────────────────────────── */
:root[data-theme="night"] {
    --c-primary: var(--nt-crimson-500);
    --c-primary-rgb: var(--nt-crimson-500-rgb);
    --c-on-primary: #E0CCCC;
    --c-success: var(--nt-green-700);
    --c-error: var(--nt-red-500);
    --c-favourite: var(--nt-orange-600);
    --c-notif-info: #4C1717;
    --c-notif-warning: #AB5B00;
    --c-notif-critical: #C50E0E;
    --c-on-notif: #E0CCCC;
    --c-notif-ongoing: #E0734A;
    --c-notif-resolved: #806C6C;
    --c-notif-ongoing-info: #C9A0A0;
    --c-notif-ongoing-warning: #E0954A;
    --c-notif-ongoing-critical: #F08080;
    --c-surface-page: #0D0000;
    --c-surface: #1A0A0A;
    --c-surface-elevated: #261212;
    --c-surface-hover: #2A0F0F;
    --c-border-subtle: rgba(var(--nt-red-500-rgb), 0.10);
    --c-divider: rgba(var(--nt-red-500-rgb), 0.10);
    --c-text-primary: var(--nt-red-500);
    --c-text-secondary: #A89090;
    --c-text-muted: #7A5A5A;
    --c-text-faint: #3A1F1F;
    --c-stale: #7A5A5A;
    --c-shadow-xs: rgba(0, 0, 0, 0.30);
    --c-shadow-sm: rgba(0, 0, 0, 0.40);
    --c-shadow-md: rgba(0, 0, 0, 0.50);
    --c-shadow-lg: rgba(0, 0, 0, 0.60);
    --c-overlay-loading: #0D0000;
}

/* base-tokens.css */
/* =============================================================================
   base-tokens.css — nem-szín közös tokenek (font + layout)
   -----------------------------------------------------------------------------
   Itt NINCS szín. A színek: primitives.css (--nt-*) + colors.css (--c-* / --*-clr).
   A Fluent/overlay override-ok: fluent-overrides.css. A betűtípus alkalmazása
   az app.css `html body` szabályában történik (`font-family: var(--font-family)`).
   ============================================================================= */

/* A betűkészletek helyben vannak (css/fonts.css, az index.html tölti): egy offline PWA nem
   függhet a fonts.googleapis.com-tól, és így egyetlen látogatás sem megy ki a Google felé. */

:root {
    color-scheme: light dark;
    --font-family: 'Open Sans', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;

    --main-padding: clamp(0.25rem, 2dvw, 1.25rem);
    --list-border-radius: 14px;
    --vector-opacity: 0.03;
}

:root[data-theme="light"] {
    color-scheme: light;
}

:root[data-theme="dark"] {
    color-scheme: dark;
    --vector-opacity: 0.06;
}

:root[data-theme="night"] {
    color-scheme: dark;
    --vector-opacity: 0.06;
}

/* icons.css */
/* Shared icon font — Material Symbols Outlined.
   The single source of truth for WHICH glyph each icon is lives in C#
   (Nautech.Frontend.Common/Icons/IconGlyphs.cs); this file only registers the font + base class.
   The .woff2 is produced (subsetted, FILL axis kept) by
   Nautech.Frontend.Common/Assets/Fonts/produce-fonts.ps1 and copied to wwwroot/fonts/. */
@font-face {
    font-family: 'Material Symbols Outlined';
    font-style: normal;
    font-weight: 100 700;
    font-display: block;
    src: url('../fonts/material-symbols-outlined.woff2') format('woff2');
}

/* Applied by the <Icon> component (Layout/Elements/Icon.razor). Colour inherits currentColor by
   default; size + FILL/wght/opsz are set per element via inline font-variation-settings. */
.appicon {
    font-family: 'Material Symbols Outlined';
    font-weight: normal;
    font-style: normal;
    line-height: 1;
    letter-spacing: normal;
    text-transform: none;
    white-space: nowrap;
    word-wrap: normal;
    direction: ltr;
    display: inline-block;
    vertical-align: middle;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
}

/* app.css */
/* =============================================================================
   app.css — app-shell BOOT + globális BASE (element-reset, utilities, megosztott widgetek).
   A feature-CSS külön fájlokban él: chart-grid.css, fleet-weather.css, leaflet-chrome.css,
   dialog/edit-page/cards/panels/lists/switches/fleet-map, fluent-overrides. A token-réteg:
   primitives.css (--nt-*), colors.css (--c-* + --*-clr), base-tokens.css (font/layout).
   ============================================================================= */

/* ═══════════════════════ BOOT — app-shell / betöltő-képernyő ════════════════════ */

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, var(--error-clr);
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

#blazor-error-ui {
    display: none;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 10000;
    padding: 0.75rem 3rem 0.75rem 1rem;
    background: var(--list-bg);
    color: var(--text-clr);
    border-top: 2px solid var(--error-clr);
    box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.2);
}

    #blazor-error-ui .reload {
        margin-left: 0.75rem;
        color: var(--primary-clr);
        font-weight: 600;
    }

    #blazor-error-ui .dismiss {
        position: absolute;
        top: 50%;
        right: 1rem;
        transform: translateY(-50%);
        background: none;
        border: none;
        color: var(--muted-clr);
        cursor: pointer;
        font-size: 1rem;
    }

.loading-div {
    background-color: var(--background-clr);
    height: 100vh;
    width: 100vw;
    position: relative;
    overflow: hidden;
    isolation: isolate;
}

.loading-img {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 200px;
    aspect-ratio: 126 / 27;
    background-color: var(--nt-white);
    -webkit-mask: url("../images/nautech.svg") no-repeat center / contain;
    mask: url("../images/nautech.svg") no-repeat center / contain;
    opacity: 0;
    animation: fadeIn 2s ease-out forwards;
    z-index: 10;
    filter: drop-shadow(0 3px 6px var(--c-shadow-lg));
    will-change: transform, opacity;
    backface-visibility: hidden;
    transform: translate3d(-50%, -50%, 0);
}

#ocean-canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    z-index: 1;
    will-change: transform;
    transform: translateZ(0);
}

@keyframes fadeIn {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

/* ── Karbantartás-képernyő (js/maintenance-gate.js + wwwroot/maintenance.txt) ─────
   Két helyen jelenik meg, ugyanazzal a belső markuppal:
     .loading-div.is-maintenance > .maintenance  — boot előtt, a betöltő helyén
     .maintenance-overlay > .maintenance         — már futó appra húzva, újratöltés előtt
   A kártya SAJÁT kontrasztot hoz (sötét, áttetsző lap + fehér szöveg), így témától
   függetlenül olvasható. */

/* A kapu a WebGL oceant eltávolítja (órákon át teljes képkockasebességgel futna a képernyő
   mögött), helyette ugyanaz a statikus gradient, mint az overlayé. */
.loading-div.is-maintenance {
    background: linear-gradient(to bottom, #1a3a5c 0%, #0d1f33 100%);
}

.loading-div.is-maintenance .loading-img {
    display: none;
}

.maintenance-overlay {
    position: fixed;
    inset: 0;
    z-index: 100000;
    /* Ugyanaz a gradient, mint az ocean no-WebGL fallbackje — így az újratöltés
       utáni betöltő-képernyőre nem ugrik a háttér. */
    background: linear-gradient(to bottom, #1a3a5c 0%, #0d1f33 100%);
}

.maintenance {
    position: absolute;
    inset: 0;
    z-index: 11;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1.75rem;
    padding: 2rem 1.5rem;
    text-align: center;
}

.maintenance__logo {
    width: 200px;
    max-width: 60vw;
    aspect-ratio: 126 / 27;
    flex-shrink: 0;
    background-color: var(--nt-white);
    -webkit-mask: url("../images/nautech.svg") no-repeat center / contain;
    mask: url("../images/nautech.svg") no-repeat center / contain;
    filter: drop-shadow(0 3px 6px var(--c-shadow-lg));
}

.maintenance__card {
    max-width: 30rem;
    padding: 1.5rem 1.75rem;
    border-radius: 12px;
    background: rgba(0, 0, 0, 0.45);
    border: 1px solid rgba(255, 255, 255, 0.12);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
    color: #fff;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
}

.maintenance__title {
    margin: 0 0 0.75rem;
    font-size: 1.5rem;
    font-weight: 600;
    line-height: 1.2;
}

.maintenance__text {
    margin: 0;
    font-size: 1rem;
    line-height: 1.5;
}

.maintenance__hint {
    margin: 0.75rem 0 0;
    font-size: 0.8125rem;
    line-height: 1.4;
    opacity: 0.7;
}

/* ═══════════════════════ BRAND — márka-váltás domain szerint ════════════════════
   Az index.html boot-előtti szkriptje teszi ki a :root[data-brand] attribútumot az
   aktuális domain alapján (core-cloud.io → networker).
   A Networker logó egyszínű SVG, maszkként rajzoljuk: a színt a background-color adja,
   így light / dark / night témán magától a szövegszínt veszi fel — ugyanúgy, ahogy a
   nautech logó a data-theme filtereivel. Arány: a viewBox 1000×165.14-ből. */

.brand-networker {
    display: none;
}

:root[data-brand="networker"] .brand-nautech {
    display: none;
}

:root[data-brand="networker"] .brand-networker {
    display: block;
}

.sidebar-logo-networker {
    width: 147px; /* azonos x-magasság a 125px-es nautech logóval */
    aspect-ratio: 1000 / 165.14;
    background-color: var(--text-clr);
    -webkit-mask: url("../images/networker.svg") no-repeat center / contain;
    mask: url("../images/networker.svg") no-repeat center / contain;
}

:root[data-brand="networker"] .loading-img,
:root[data-brand="networker"] .maintenance__logo {
    aspect-ratio: 1000 / 165.14;
    -webkit-mask-image: url("../images/networker.svg");
    mask-image: url("../images/networker.svg");
}

/* ═══════════════════════ BASE — globális element-alap + utilities ═══════════════ */

a, .btn-link {
    color: var(--primary-clr);
}

/* Téma-váltás finom átmenete a fő felület-elemeken. */
html,
body,
.sidebar,
.base-list__header,
.base-list__row,
.base-list-skeleton__header,
.base-list-skeleton__body {
    transition: background-color 0.25s ease, color 0.25s ease, border-color 0.25s ease;
}

/* iOS Safari pitfall: ha a system dark módban van és a page-en nincs explicit text color
   azon elemeken amelyeknek nincs saját color szabálya, a UA default CanvasText-et fehérre
   állítja → "világos háttér + fehér betűk" bug.
   A var(--text-clr) témánként már a helyes érték, ezért elég ezt témafüggetlenül kötelezővé tenni. */
body,
h1,
h2,
h3,
h4,
h5,
h6,
p,
span,
label,
div {
    color: var(--text-clr);
}

html body {
    font-family: var(--font-family);
    height: 100dvh;
    background: var(--background-clr);
    position: relative;
}

body::before {
    content: '';
    position: fixed;
    inset: 0;
    background-image: url('../images/vector.svg');
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    background-attachment: fixed;
    opacity: var(--vector-opacity);
    /*mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.05) 10%, rgba(0, 0, 0, 0.02) 60%, rgba(0, 0, 0, 0.05) 90%);*/
    z-index: 0;
}

:root[data-theme="dark"] body::before {
    filter: invert(1) hue-rotate(180deg);
}

body > * {
    position: relative;
    z-index: 2;
}

h1:focus {
    outline: none;
}

.cursor-pointer {
    cursor: pointer;
}

.validation-message {
    color: var(--error-clr);
}

.nautech-page {
    display: grid;
    gap: 2rem;
}

@media(min-width: 900px) {
    .nautech-page, .outer {
        padding-left: 20px;
    }
}

.action-btn {
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
}

    /* Billentyű-fókusz látható gyűrű (a11y) — az Edit/Delete ikon-gombok minden listán/kártyán. */
    .action-btn:focus-visible {
        outline: 2px solid var(--primary-clr);
        outline-offset: 2px;
        border-radius: 6px;
    }

.action-btn:disabled {
    opacity: 50%;
}

.fv-super {
    font-variant: super;
}

.gray {
    color: var(--muted-clr);
}

/* ── Megosztott, cross-component widgetek (globálisan kötelezőek, ezért nem scoped) ── */

/* Operátor-választó (compare-toggle): a kiválasztott gomb Primary (kitöltött brand), a többi Secondary
   (körvonalas) — a kijelölést a variáns adja, nincs külön selected-state CSS.
   EGYETLEN szabály kell: az operátor-glyph (child-content <span>) a globális `span{color:text-clr}`
   miatt különben SÖTÉT maradna a brand-kitöltésen (night módban piros-a-piroson → láthatatlan);
   `color: inherit`-tel a gomb színét veszi fel (Primary → világos on-primary, Secondary → text). */
.comparison-toggle {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
}

    .comparison-toggle .app-btn .cmp-op {
        color: inherit;
    }

/* Összehasonlító operátor szöveges jelként (a régi less_than/greater_than/equal SVG-k helyett):
   crisp, témázható (currentColor), nincs raszter img. Listában öröklött (~1rem) méret; a
   szerkesztő operátor-választó gombjaiban .cmp-op--lg (a ∅ gomb 1.1rem-jéhez igazítva). */
.cmp-op {
    font-weight: 700;
    line-height: 1;
}

.cmp-op--lg {
    font-size: 1.5rem;
}

.cmp-op--colored{
    color: var(--primary-clr);
}

/* Videó-vezérlő gombok (mute / fullscreen / play a kamera- és felvétel-lejátszókban): sötét chip
   (#383838) az AppButton-ön, ChildContent-tel rendezett feltételes ikonokkal. A `.controls .app-btn.video-ctrl-btn`
   (b=3) felülírja az AppButton scoped base-ét (b=2) — a scoped bundle a globális CSS UTÁN töltődik, ezért
   döntetlennél a base nyerne. Az ikonok a razorban explicit --primary-clr színt kapnak (jól látszanak a chipen). */
.controls .app-btn.video-ctrl-btn {
    background: var(--c-media-surface-raised);
    border-color: var(--c-media-surface-raised);
    border-radius: 8px;
    padding: 0.4rem;
    min-height: 0;
}

    .controls .app-btn.video-ctrl-btn:hover:not(:disabled) {
        background: var(--c-media-surface-hover);
        border-color: var(--c-media-surface-hover);
    }

@keyframes alarm-stripe {
    0%, 100% {
        opacity: 1;
        box-shadow: 0 0 5px 1px rgba(var(--nt-red-600-rgb), 0.55);
    }

    50% {
        opacity: 0.2;
        box-shadow: none;
    }
}

.alarm-stripe {
    display: inline-block;
    width: 4px;
    height: 1.25em;
    background: var(--critical-clr);
    border-radius: 2px;
    animation: alarm-stripe 1.4s ease-in-out infinite;
    vertical-align: middle;
    flex-shrink: 0;
}

/* Respect reduced-motion preference: disable transitions globally (a11y).
   Egyetlen kanonikus példány — korábban a cards/edit-page/lists/dialog fájlok mindegyike külön tartalmazta. */
@media (prefers-reduced-motion: reduce) {
    * {
        transition-duration: .01ms !important;
    }
}


/* =============================================================================
   TIER 3 — app aliases (--*-clr → --c-*). Static layer (not from the token model).
   ============================================================================= */
:root {
    --elev-rest: 0 2px 8px var(--c-shadow-xs);
    --elev-hover: 0 4px 16px var(--c-shadow-sm);
    --background-clr: var(--c-surface-page);
    --primary-clr: var(--c-primary);
    --delete-clr: var(--c-error);
    --error-clr: var(--c-error);
    --info-clr: var(--c-notif-info);
    --warning-clr: var(--c-notif-warning);
    --critical-clr: var(--c-notif-critical);
    --rec-clr: var(--c-notif-critical);
    --resolved-clr: var(--c-notif-resolved);
    --muted-clr: var(--c-text-secondary);
    --extra-muted-clr: var(--c-text-faint);
    --stale-clr: var(--c-stale);
    --list-bg: var(--c-surface);
    --text-clr: var(--c-text-primary);
    --border-thin: 1px solid var(--c-divider);
}
/* chart-grid.css */
/* =============================================================================
   chart-grid.css — chart-dashboard rács (.wireframe) + oszlopszám + „nincs adat" összegző.
   Korábban az app.css-ben élt. A --chart-cols-t a site.js állítja a <html data-chart-size>
   attribútumon át (nautechChartSize).
   ============================================================================= */
.wireframe {
    display: grid;
    gap: 3rem;
    container-type: inline-size;
    container-name: wireframe-container;
}

section .wrapper {
    display: grid;
    gap: 1rem;
}

.full {
    grid-column: 1 / -1;
}

/* Desktop chart-kártya oszlopszám — a UserSettings ChartCardSize állítja a <html data-chart-size>
   attribútumon át (site.js nautechChartSize). Default (Large) = 2 oszlop; Medium = 3; Small = 4.
   A --chart-cols-t KIZÁRÓLAG a @container (min-width:900px) blokkon belül használjuk, ezért mobil
   (<900px) ÉRINTETLEN: ott a section .wrapper marad egyoszlopos 100%-széles. */
:root { --chart-cols: 2; }
:root[data-chart-size="medium"] { --chart-cols: 3; }
:root[data-chart-size="small"] { --chart-cols: 4; }

/* Kisebb chart-méreteknél a full-width chartok (légnyomás, páratartalom) is a rácshoz
   igazodjanak (ne nyúljanak teljes sorra), mint a többi grafikon — csak Large-nál full-width. */
:root[data-chart-size="medium"] .full,
:root[data-chart-size="small"] .full {
    grid-column: auto;
}

@container wireframe-container (min-width: 900px) {
    section .wrapper {
        grid-template-columns: repeat(var(--chart-cols, 2), minmax(0, 1fr));
    }
}

/* N11 (user választása: KÖZÖS RÁCS, section-címek nélkül) — kis/közepes chart-méretnél minden chart
   EGYETLEN egyenletes rácsba folyik; a kategória- és eszköz-fejlécek eltűnnek (minden chart önazonosító:
   SourceName + SecondLine, pl. „Starboard / Outside temperature"). A .wireframe a @container-elem,
   ezért a rá vonatkozó szabály csak top-level @media-ban lehet (a container query önmagát nem
   stílusozza). A section + .wrapper display:contents-szel feloldódik → a chart-divek közvetlen
   grid-itemek lesznek és egyenletesen töltik a sort. Large (default) ÉS mobil (<900px) érintetlen. */
@media (min-width: 900px) {
    :root[data-chart-size="small"] .wireframe,
    :root[data-chart-size="medium"] .wireframe {
        display: grid;
        grid-template-columns: repeat(var(--chart-cols), minmax(0, 1fr));
        gap: 0.75rem 1rem;
        align-items: start;
    }

    /* Kategória- és eszköz-fejlécek elrejtése (a chartok maguk azonosítják magukat). */
    :root[data-chart-size="small"] .wireframe .section-title,
    :root[data-chart-size="medium"] .wireframe .section-title,
    :root[data-chart-size="small"] .wireframe .subsection-title,
    :root[data-chart-size="medium"] .wireframe .subsection-title {
        display: none;
    }

    /* CSAK a chart-szekciók (amelyekben .wrapper van) oldódnak fel → a chartjaik közvetlenül a
       .wireframe rácsába folynak. A NEM-chart szekciók (pl. Routes oldal: térkép, TripBook) NEM
       oldódnak fel — teljes sort kapnak (lásd lent), hogy ne préselődjenek egy keskeny rács-cellába. */
    :root[data-chart-size="small"] .wireframe > section:has(.wrapper),
    :root[data-chart-size="medium"] .wireframe > section:has(.wrapper),
    :root[data-chart-size="small"] .wireframe section .wrapper,
    :root[data-chart-size="medium"] .wireframe section .wrapper {
        display: contents;
    }

    /* Nem-chart szekciók (térkép, TripBook stb.) teljes sor — ne folyjanak a chart-rácsba,
       ne préselődjenek egy keskeny cellába (Routes oldal). */
    :root[data-chart-size="small"] .wireframe > section:not(:has(.wrapper)),
    :root[data-chart-size="medium"] .wireframe > section:not(:has(.wrapper)) {
        grid-column: 1 / -1;
    }

    /* Az időtartomány-toolbar teljes szélességű, külön sorban. */
    :root[data-chart-size="small"] .wireframe > .dateintervalpicker,
    :root[data-chart-size="medium"] .wireframe > .dateintervalpicker,
    :root[data-chart-size="small"] .wireframe > .electrical-toolbar,
    :root[data-chart-size="medium"] .wireframe > .electrical-toolbar {
        grid-column: 1 / -1;
    }
}

@media (max-width: 900px) {
    canvas {
        touch-action: pan-y pinch-zoom !important;
    }
}

/* ===== Mobil: adat nélküli chartok a lap végére, listaszerűen =====
   Mobilon (<900px) az adat nélküli chart-kártyákat kivesszük a helyükről (display:none), és a
   nevüket a lap legvégén, a NoDataChartSummary komponens listájában soroljuk fel — így elöl mindig
   az adattal rendelkező chartok látszanak. A .chart-nodata osztályt a ChartCard teszi a .card-ra,
   pontosan akkor, amikor a "nincs adat" overlay is látszana (a lista és a rejtés így szinkronban van).
   Az összegző desktopon rejtett; a chart-oldalakon kívül (nincs .wireframe) semmi nem változik. */
.nodata-summary {
    display: none;
}

.nodata-summary__list {
    list-style: none;
    margin: 0.5rem 0 0;
    padding: 0;
    display: grid;
    gap: 0.35rem;
}

    .nodata-summary__list li {
        padding: 0.5rem 0.75rem;
        background-color: var(--list-bg);
        border: var(--border-thin);
        border-radius: var(--list-border-radius);
        color: var(--muted-clr);
        font-size: 14px;
    }

@media (max-width: 900px) {
    /* Az összegző lista megjelenítése a lap legvégén. */
    .nodata-summary {
        display: block;
    }

    /* Az adat nélküli chart-kártya eltűnik a helyéről (a neve a lenti listába kerül). */
    .wireframe .card.chart-nodata {
        display: none;
    }

    /* Ha egy szekció MINDEN chartja adat nélküli, az egész szekció (a címével együtt) eltűnik —
       nem marad "árva" fejléc üres tartalommal. Betöltés/skeleton közben nincs .chart-nodata,
       ezért a szekció olyankor NEM tűnik el. */
    .wireframe > section:has(.card.chart-nodata):not(:has(.card:not(.chart-nodata))) {
        display: none;
    }

    /* Electrical: ha egy eszköz (h4 + rá következő .wrapper) minden chartja adat nélküli, az eszköz
       árva címét is elrejtjük (a csoport-szekció maga marad, ha más eszközének van adata). */
    .wireframe .electrical-device-title:has(+ .wrapper .card.chart-nodata):not(:has(+ .wrapper .card:not(.chart-nodata))) {
        display: none;
    }
}

/* dialog.css */
/* ──────────────────────────────────────────────────────────────────────────
   Shadow scale (define once, reuse) — neutral black-alpha, theme-agnostic.
   ────────────────────────────────────────────────────────────────────────── */
:root {
    --dialog-shadow-rest: 0 1px 2px var(--c-shadow-xs), 0 2px 8px var(--c-shadow-xs);
    --dialog-shadow-elevated: 0 4px 16px var(--c-shadow-sm);
}

:root[data-theme="dark"],
:root[data-theme="night"] {
    --dialog-shadow-rest: 0 1px 2px var(--c-shadow-xs), 0 2px 8px var(--c-shadow-sm);
    --dialog-shadow-elevated: 0 6px 22px var(--c-shadow-md);
}

fluent-dialog::part(control) {
    max-height: 90dvh;
    background: var(--list-bg);
    border: var(--border-thin);
    border-radius: var(--list-border-radius);
    box-shadow: var(--dialog-shadow-elevated);
}

/* Header/body divider — mirror the edit-page footer treatment. */
.fluent-dialog-header {
    border-bottom: var(--border-thin);
}

.fluent-dialog-body {
    overflow: auto;
    padding: 0.125rem;
}

    .fluent-dialog-body form {
        display: grid;
        gap: 1rem;
        width: 100%;
    }

        .fluent-dialog-body form label {
            font-style: italic;
            width: fit-content;
            color: var(--muted-clr);
        }

    .fluent-dialog-body fluent-text-field,
    .fluent-dialog-body fluent-number-field,
    .fluent-dialog-body fluent-combobox,
    .fluent-dialog-body fluent-select {
        width: 100%;
        transition: border-color .15s ease, box-shadow .15s ease;
    }

    /* Valid-state focus ring — accent token (invalid state keeps --error-clr below). */
    .fluent-dialog-body fluent-text-field:not(.invalid):focus-visible,
    .fluent-dialog-body fluent-number-field:not(.invalid):focus-visible,
    .fluent-dialog-body fluent-combobox:not(.invalid):focus-visible,
    .fluent-dialog-body fluent-select:not(.invalid):focus-visible,
    .fluent-dialog-body fluent-text-area:not(.invalid):focus-visible {
        outline: 2px solid var(--primary-clr);
        outline-offset: 2px;
        border-radius: inherit;
    }

    .fluent-dialog-body fluent-radio-group::part(positioning-region) {
        column-gap: 3rem;
        row-gap: 0.5rem;
        flex-wrap: wrap;
    }

    .fluent-dialog-body fluent-radio-group fluent-radio::part(label) {
        padding-inline-start: 0.5rem !important;
        margin: 0;
    }

    .fluent-dialog-body fluent-number-field.invalid {
        outline: none;
    }

    .fluent-dialog-body fluent-number-field.invalid::part(root),
    .fluent-dialog-body [role="checkbox"].invalid::part(control),
    .fluent-dialog-body [role="combobox"].invalid::part(control),
    .fluent-dialog-body fluent-combobox.invalid::part(control),
    .fluent-dialog-body fluent-text-area.invalid::part(control),
    .fluent-dialog-body fluent-text-field.invalid::part(root) {
        outline: calc(var(--stroke-width) * 1px) solid var(--error-clr);
    }


.alarm-threshold {
    display: grid;
    gap: 1rem;
    width: 100%;
    grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
}

    .alarm-threshold fluent-number-field {
        width: 100%;
    }

fluent-dialog[id^="id_"]::part(control) {
    padding-inline: min(3dvw, 2rem) !important;
    padding-block: min(3dvh, 2rem) !important;
}

fluent-dialog[id^="id_"] .fluent-dialog-header {
    padding-bottom: min(2dvh, 1rem) !important;
}

/* Lehetővé teszi a fluent-combobox/select listbox popupjának, hogy kilógjon a
   dialog body overflow:auto-jából. Csak a .allow-popup-overflow class-t
   tartalmazó (rövid) form-okra alkalmazzuk, hogy a hosszú dialogusoknál
   (alarm/automation) megmaradjon a scroll. */
.fluent-dialog-body:has(.allow-popup-overflow) {
    overflow: visible;
}

/* ElectricalDevice dialog DC NMEA instance mezői (Voltage / Current / Temperature / SoC).
   auto-fit minmax(14rem, 1fr): deszktopon 2 oszlop, mobilon (keskeny viewport) 1 oszlop. */
.electrical-nmea-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: 1rem;
}

/* ──────────────────────────────────────────────────────────────────────────
   Mobil layout (<= 600px viewport)
   A Fluent dialog default min-width-je (kb. 580px) nagyobb mint a mobil
   viewport — a control középre van pozícionálva translateX(-50%)-kal, így
   a tartalom mindkét oldalt egyformán kilóg. A javítás: a control max-width-jét
   a viewporthoz kötjük, min-width-jét nullázzuk, és a padding/gap értékeket
   szűkítjük a kisebb helyhez.
   ────────────────────────────────────────────────────────────────────────── */
@media (max-width: 600px) {
    fluent-dialog[id^="id_"]::part(control) {
        max-width: calc(100dvw - 0.5rem) !important;
        min-width: 0 !important;
        max-height: calc(100dvh - 0.5rem) !important;
        padding-inline: 0.75rem !important;
        padding-block: 0.75rem !important;
    }

    .fluent-dialog-body fluent-radio-group::part(positioning-region) {
        column-gap: 1rem;
        row-gap: 0.5rem;
    }

    /* A 4 NMEA combobox biztosan 1 oszlopban */
    .electrical-nmea-grid {
        grid-template-columns: 1fr;
    }
}
/* ── HMI-dialógusok közös elemei ──────────────────────────────────────────────
   Itt és nem a hmi.css-ben: a DashboardConflictDialog is ezeket használja, a hmi.css viszont csak a
   HMI-oldalakon töltődik be. */

/* Másodlagos, figyelmeztető mondat a dialógus törzsében (pl. a piszkozat-visszatöltésnél a nem
   pótolható képek). Halkabb, mint a fő kérdés. */
.hmi-dialog__warn {
    margin-top: .75rem;
    font-size: .875rem;
    color: var(--warning-clr);
}

/* Dialógus-kérdés szervertől kapott szöveggel (az ütközésnél a másik szerkesztő e-mail címe): egy
   hosszú, szóköz nélküli cím csak így nem lóg ki a dialógusból. */
.hmi-dialog__intro {
    overflow-wrap: anywhere;
}

/* Három választás a dialógus láblécében (mentési ütközés): tördelhető sor, mert a lábléc egyetlen
   nem tördelő flex-sor és az AppButton felirata nowrap, így a harmadik gomb kilógott. */
.hmi-dialog__acts {
    display: grid;
    justify-content: center;
    gap: .5rem;
    width: 100%;
}

/* Kiegészítő sor egy dialógusban (pl. „hány elem vész el a törléssel"). */
.hmi-dialog__note {
    margin: .35rem 0 0;
    font-size: .85rem;
    color: var(--muted-clr);
}

/* edit-page.css */
/* ──────────────────────────────────────────────────────────────────────────
   EditPage layout — közös stílusok a /vessel_settings/{entity}/{id|new}
   edit-page-ekhez. A Pages/EditPages/EditPageShell.razor használja.
   ────────────────────────────────────────────────────────────────────────── */

.edit-page {
    /* a nautech-page (display: grid; gap: 2rem) gap-jét finomítjuk a form-szerű
       elrendezéshez: a header és a form között kisebb hézag, a form-on belül
       az edit-page-form maga ad gap-et. */
    gap: 1rem;
}

.edit-page-header {
    display: flex;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
}

.edit-page-title {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0;
    font-size: 1.25rem;
}

.edit-page-card {
    width: 100%;
    max-width: 40rem;
    margin-inline: auto;
    background: var(--list-bg);
    border: var(--border-thin);
    border-radius: var(--list-border-radius);
    padding: 1.5rem;
    box-shadow: 0 1px 2px var(--c-shadow-xs), 0 2px 8px var(--c-shadow-xs);
}

.edit-page-form {
    display: grid;
    gap: 1rem;
    width: 100%;
}

.edit-page-form label {
    font-style: italic;
    width: fit-content;
    color: var(--muted-clr);
}

.edit-page-form fluent-text-field,
.edit-page-form fluent-number-field,
.edit-page-form fluent-combobox,
.edit-page-form fluent-select {
    width: 100%;
}

.edit-page-form fluent-text-field:focus-visible,
.edit-page-form fluent-number-field:focus-visible,
.edit-page-form fluent-combobox:focus-visible,
.edit-page-form fluent-select:focus-visible {
    outline: 2px solid var(--primary-clr);
    outline-offset: 2px;
    border-radius: inherit;
}

.edit-page-form fluent-radio-group::part(positioning-region) {
    column-gap: 3rem;
    row-gap: 0.5rem;
    flex-wrap: wrap;
}

.edit-page-form fluent-radio-group fluent-radio::part(label) {
    padding-inline-start: 0.5rem !important;
    margin: 0;
}

.edit-page-form fluent-number-field.invalid {
    outline: none;
}

.edit-page-form fluent-number-field.invalid::part(root),
.edit-page-form [role="checkbox"].invalid::part(control),
.edit-page-form [role="combobox"].invalid::part(control),
.edit-page-form fluent-combobox.invalid::part(control),
.edit-page-form fluent-select.invalid::part(control),
.edit-page-form fluent-text-area.invalid::part(control),
.edit-page-form fluent-text-field.invalid::part(root) {
    outline: calc(var(--stroke-width) * 1px) solid var(--error-clr);
}

/* Kötelező-mező csillag a select-ek LabelTemplate-jében — a text-field Required piros csillagához illesztve.
   (A select-eknél NEM használunk natív Required-et, mert a FluentSelect natív validációja Firefoxon
   „ElementInternals.setValidity: Need to provide validation message" konzol-hibát dobott.) */
.edit-page-form .req-star {
    color: var(--error-clr);
    font-weight: 600;
    margin-inline-start: 0.15rem;
}

.edit-page-footer {
    display: flex;
    gap: 0.5rem;
    padding-block-start: 1.25rem;
    margin-block-start: 1rem;
    border-block-start: var(--border-thin);
}

.edit-page-header {
    width: 100%;
    max-width: 40rem;
    margin-inline: auto;
}

@media (max-width: 600px) {
    .edit-page-card {
        padding: 1rem;
        border-radius: 0;
        margin-inline: calc(var(--main-padding) * -1);
    }
}

/* DC NMEA mezők grid az ElectricalDevice page-en (és más helyütt 4-cella autofit). */
.edit-page-form .electrical-nmea-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: 1rem;
}

@media (max-width: 600px) {
    .edit-page-header {
        gap: 0.5rem;
    }

    .edit-page-form .electrical-nmea-grid {
        grid-template-columns: 1fr;
    }

    .edit-page-form fluent-radio-group::part(positioning-region) {
        column-gap: 1rem;
        row-gap: 0.5rem;
    }
}

/* ──────────────────────────────────────────────────────────────────────────
   Kétoszlopos mezőpár — pl. engine: type + power egymás mellett.
   ────────────────────────────────────────────────────────────────────────── */
.edit-page-form .field-grid-2 {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1rem;
    align-items: start;
}

/* ──────────────────────────────────────────────────────────────────────────
   Yes/No toggle-kártyák (engine: electric / spare) — a sima rádiógombok helyett.
   Két kártya egymás mellett, mindegyik ikon + felirat + kapcsoló.
   ────────────────────────────────────────────────────────────────────────── */
.edit-page-form .toggle-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1rem;
}

.edit-page-form .toggle-card {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.75rem 0.9rem;
    border: var(--border-thin);
    border-radius: var(--list-border-radius);
    background: var(--neutral-layer-2, var(--list-bg));
}

.edit-page-form .toggle-card__icon {
    color: var(--muted-clr);
    flex: none;
}

.edit-page-form .toggle-card__label {
    flex: 1;
    /* felülírjuk az .edit-page-form label italic-muted „segédszöveg” stílusát */
    font-style: normal;
    color: var(--text-clr);
    line-height: 1.2;
}

/* ──────────────────────────────────────────────────────────────────────────
   Ikonos választó-kártyák (tank: fuel/water · geofence: enter/exit) — a rádió-
   gombok helyett. Kártyánként egy ikon + felirat; a kijelölt kártya kiemelt.
   ────────────────────────────────────────────────────────────────────────── */
.edit-page-form .choice-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1rem;
}

.edit-page-form .choice-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    padding: 1rem 0.75rem;
    border: 2px solid var(--c-divider);
    border-radius: var(--list-border-radius);
    background: var(--neutral-layer-2, var(--list-bg));
    color: var(--muted-clr);
    cursor: pointer;
    text-align: center;
    transition: border-color .15s ease, background-color .15s ease, color .15s ease;
}

.edit-page-form .choice-card:hover {
    border-color: color-mix(in srgb, var(--primary-clr) 45%, var(--c-divider));
}

.edit-page-form .choice-card:focus-visible {
    outline: 2px solid var(--primary-clr);
    outline-offset: 2px;
}

.edit-page-form .choice-card__label {
    font-weight: 600;
    font-size: 0.95rem;
}

.edit-page-form .choice-card--selected {
    border-color: var(--primary-clr);
    background: color-mix(in srgb, var(--primary-clr) 12%, var(--neutral-layer-2, var(--list-bg)));
    color: var(--text-clr);
}

/* ──────────────────────────────────────────────────────────────────────────
   Alarm-szint választó — a notifikációs chip-ekhez igazodó, kattintható chip-ek.
   A kijelölt szint teli színben, a többi elhalványítva.
   ────────────────────────────────────────────────────────────────────────── */
.edit-page-form .level-choice-group {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
}

.edit-page-form .level-choice {
    display: inline-flex;
    padding: 0;
    border: none;
    background: none;
    border-radius: 12px;
    cursor: pointer;
    opacity: 0.4;
    filter: grayscale(0.5);
    transition: opacity .15s ease, filter .15s ease, box-shadow .15s ease;
}

.edit-page-form .level-choice:hover {
    opacity: 0.8;
    filter: grayscale(0.2);
}

.edit-page-form .level-choice--active {
    opacity: 1;
    filter: none;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-clr) 55%, transparent);
}

.edit-page-form .level-choice:focus-visible {
    outline: 2px solid var(--primary-clr);
    outline-offset: 3px;
}

@media (max-width: 600px) {
    .edit-page-form .field-grid-2,
    .edit-page-form .toggle-grid,
    .edit-page-form .choice-grid {
        grid-template-columns: 1fr;
    }
}

/* cards.css */
.cards-section {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    grid-auto-rows: 152px;
    gap: 0.75rem;
    width: 100%;
    padding-bottom: 1rem;
}

/* Section labels (Data / Switches / ...) - small muted, like the mockup's "Mérőszámok". */
.section-title,
.switch-section__header h3 {
    font-size: 13px;
    font-weight: 600;
    color: var(--muted-clr);
    margin: 0 0 0.5rem;
    letter-spacing: 0.2px;
}

/* Sub-group titles (a device name, a settings sub-section) - slightly more prominent. */
.subsection-title {
    font-size: 15px;
    font-weight: 600;
    color: var(--text-clr);
    margin: 0 0 0.4rem;
}

.card {
    background-color: var(--list-bg);
    border: var(--border-thin);
    border-radius: var(--list-border-radius);
    box-shadow: var(--elev-rest);
    text-align: center;
    transition: background-color .15s ease, border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}

    /* Color modification for special usecases like placeholders. */
    .card.gray {
        background-color: var(--extra-muted-clr);
        cursor: not-allowed;
    }

    /* N3 — elavult (nincs friss adat / offline) kártya: halványítva + deszaturálva jelezzük, hogy a
       mutatott érték NEM friss (az utolsó mért adat a saját dátumával látszik). Az alarm-színű (red/yellow)
       kártyák NEM kapnak Stale-t (lásd DisplaySection), így azok színe megmarad.
       FONTOS: a halványítás/deszaturálás csak a .card-content-re (tartalom) vonatkozik, NEM az egész
       kártyára — így a history (navigációs) ikon a szürke kártyákon is megőrzi a primary színét.
       (A szülőre tett opacity/filter a gyerek ikont is szürkítené, azt a gyerek nem tudja visszacsinálni.)
       A .card-body nevet szándékosan KERÜLJÜK: az a Bootstrap saját osztálya (1rem padding-et adna). */
    .card.stale-card .card-content,
    .card.nodata-card .card-content {
        opacity: 0.5;
        filter: grayscale(0.7);
    }

    .card.ghost {
        display: grid;
        place-content: center;
        height: 100%;
        border: 2px dashed var(--extra-muted-clr);
        background-color: color-mix(in srgb, var(--extra-muted-clr) 12%, transparent);
        cursor: pointer;
    }

        .card.ghost .source-name {
            display: none;
        }

        /* A tartalom (ikon + felirat) MEGBÍZHATÓ középre igazítása — a .card-content (grid) place-content:center.
           (Enélkül magasabb cellában a bal felső sarokba csúszna.) */
        .card.ghost .card-content {
            place-content: center;
        }

        /* Slim add-csempe: Add-ikon + "Kártya hozzáadása" felirat egymás alatt, középen — a régi 52pt "+"
           helyett barátságosabb, és a szerkesztő-sáv "Kártya hozzáadása" gombjával egyértelmű a jelentése. */
        .card.ghost .ghost-add {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 0.5rem;
            padding: 0.5rem;
        }

        .card.ghost .ghost-add__label {
            color: var(--muted-clr);
            font-weight: 600;
            font-size: 0.9rem;
            text-align: center;
        }

    .card.pickableCard {
        cursor: pointer;
    }

        .card.pickableCard:hover {
            background-color: color-mix(in srgb, var(--primary-clr) 8%, var(--list-bg));
            box-shadow: var(--elev-hover);
            transform: translateY(-2px);
        }

        .card.pickableCard:focus-visible {
            outline: 2px solid var(--primary-clr);
            outline-offset: 2px;
            border-radius: inherit;
        }

    .card.non-pickable {
        opacity: 0.7;
        cursor: auto !important;
    }

        .card.non-pickable:hover {
            background-color: var(--extra-muted-clr);
        }

    /* Szerkesztő módban a kártyák húzhatók (Sortable.js) — a grab-kurzor felfedezhetővé teszi az
       átrendezést. A slim add-csempe (.ghost) NEM húzható (Sortable filter: '.card.ghost'), az marad pointer. */
    .cards-section.editing .card:not(.ghost) {
        cursor: grab;
    }

        .cards-section.editing .card:not(.ghost):active {
            cursor: grabbing;
        }

    /* ── Alarm-színű kártyák (piros = Error, sárga = Warning) ──
       A kártya a SZEMANTIKUS notif-háttérrel telik meg (--critical-clr / --warning-clr, azaz --c-notif-*),
       a rajta lévő MINDEN szöveg pedig a hozzá tartozó ON-színt (--c-on-notif) viseli — EXPLICIT módon
       minden leszármazotton. Enélkül a globális `h1..h6,p,span,div { color: text-clr }` (app.css) a
       gyerekeket KÖZVETLENÜL a téma szövegszínére festené (dark témában világosra → olvashatatlan a
       kitöltésen); a .card-re tett szín csak öröklődne, amit a közvetlen elem-szabály felülír. Az on-szín
       témánként helyes (light/dark: fehér, night: szürkés-pirosas), ezért NINCS külön téma-override. */
    .card.red {
        background-color: var(--critical-clr);
    }

    .card.yellow {
        background-color: var(--warning-clr);
    }

    /* A mini-viz (SVG fill/stroke) ÉS a history-gomb ikonja is --primary-clr-t (foreground) és
       --extra-muted-clr-t (track) használ — alarm-háttéren a primary csúnya / nem látszik. A két tokent a
       kártya SCOPE-jában felüldefiniáljuk: a foreground a szöveg on-színe lesz, a track annak halvány
       (második, megengedett) változata. A custom property öröklődik → hat az SVG presentation-attribútumokra
       ÉS a history-ikon inline Color="var(--primary-clr)" értékére is, külön szabály nélkül. */
    .card.red,
    .card.yellow {
        --primary-clr: var(--c-on-notif);
        --extra-muted-clr: color-mix(in srgb, var(--c-on-notif) 30%, transparent);
    }

    .card.red,
    .card.red h1, .card.red h2, .card.red h3, .card.red p, .card.red div,
    .card.red span, .card.red span.timestamp,
    .card.yellow,
    .card.yellow h1, .card.yellow h2, .card.yellow h3, .card.yellow p, .card.yellow div,
    .card.yellow span, .card.yellow span.timestamp {
        color: var(--c-on-notif);
    }

    /* A szürke (placeholder / nem-pickelhető) kártya világos hátterén marad a sötét időbélyeg. */
    .card.gray span.timestamp {
        color: black;
    }

    .card h2 {
        font-size: 28px;
        font-weight: 600;
        margin: 0;
    }

    /* Többsoros érték (pozíció kártya: lat + lon) — kisebb betűvel, hogy a két sor
       összmagassága az egysoros 24px-es h2-ével egyezzen, különben a
       grid-auto-rows: 1fr miatt a sor összes kártyája megnyúlna. */
    .card h2.multiline {
        font-size: 16px;
        line-height: 1.2;
        margin: 5px 0;
    }

    /* Mockup-style value typography: big number + small muted unit. A kis egységet a nagy értékkel
       FÜGGŐLEGESEN KÖZÉPRE igazítjuk (mint a panel-cellák) — enélkül a baseline-igazítás a magas glifeket
       (pl. a "°" fok-jel) középre/lejjebb csúsztatta a nagy szám mellett. Mindkét spanre vertical-align:middle:
       a középpontjaik egy vonalra esnek → egymáshoz képest középre igazítva. */
    .card h2 .metric-value,
    .card h2 .metric-unit {
        vertical-align: middle;
    }

    .card h2 .metric-unit {
        font-size: 13px;
        font-weight: 400;
        color: var(--muted-clr);
        margin-left: 4px;
    }

    /* Alarm-kártyán az érték az on-színt viseli; az egység is azt örökölje (a globális muted helyett),
       csak kicsit halványabban. */
    .card.red h2 .metric-unit,
    .card.yellow h2 .metric-unit {
        color: inherit;
        opacity: 0.85;
    }

    /* No-data placeholder ("Nincs adat") — a "--" helyett olvashatóbb, halvány címke.
       Kisebb és normál súlyú, hogy ne mérőszámnak (nagy számnak) látsszon. */
    .card h2 .metric-nodata {
        font-size: 15px;
        font-weight: 500;
        color: var(--muted-clr);
    }

    .card p {
        font-size: 1rem;
        margin: 0;
    }

        .card span.timestamp {
            font-size: 11px;
            color: var(--muted-clr);
            line-height: 1.25;
        }

        /* Mockup-style freshness indicator before the timestamp. */
        .card .card-status-line {
            display: inline-flex;
            align-items: center;
            gap: 5px;
        }

.card .status-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    flex: none;
    background: var(--c-success);
}

            .card .status-dot.stale {
                background: var(--stale-clr);
            }

.card-nav-btn {
    position: absolute;
    right: 0;
    bottom: 0;
    background: transparent;
    border: none;
    border-radius: var(--list-border-radius);
    transition: background-color .15s ease, border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}

    .card-nav-btn:focus-visible {
        outline: 2px solid var(--primary-clr);
        outline-offset: 2px;
        border-radius: inherit;
    }

/* The NoData status message and the loading ring animation is showcased on FluentUI.Overlay */
.fluent-overlay {
    border-radius: var(--list-border-radius);
    z-index: 0 !important;
}

/* Ghost element while dragging */
.sortable-ghost {
    opacity: 0.4;
    background-color: color-mix(in srgb, var(--extra-muted-clr) 30%, var(--list-bg));
}

.cards-section .card {
    padding: 11px 12px;
    text-align: left;
    /* A törlés-gomb (szerkesztő mód) abszolút pozícionálásához + a history-gomb (card-nav-btn) horgonyához. */
    position: relative;
}

/* Szerkesztő módban a törlés-gomb a kártya jobb felső sarkába LEBEG (nem foglal flow-magasságot), így a
   kártyák NEM lesznek magasabbak edit módban. Korábban a sima kártyák gombja in-flow volt → megnyúltak, és
   a grid-auto-rows:1fr miatt a paneleket is felnyújtotta. A speciális kártyák (switches/preset/panelek) már
   így csinálták; ez a szabály az ÖSSZES kártyára kiterjeszti. */
.cards-section .card > fluent-button {
    position: absolute;
    top: 6px;
    right: 6px;
    z-index: 2;
}

.card > div {
    height: 100%;
    display: grid;
}

/* min-width: 0 — grid-itemek alapból nem mehetnek a tartalmuk min-mérete alá
   (min-width: auto). A chart canvas inline pixel-szélességet kap a Chart.js-től,
   ami ablak-szűkítéskor nyitva tartaná ezt a láncot: a kártya összemegy, de a
   belső gridek a régi canvas-szélességen ragadnak és túllógnak → a Chart.js
   resize-mérése is a felfújt wrappert látja, így sosem zsugorít. */
.card > div > div {
    display: grid;
    align-items: end;
    min-width: 0;
}

.chart-content {
    min-width: 0;
    /* A "nincs adat" / betöltés FluentOverlay horgonya: az overlay-t a .chart-content-be
       tettük (ChartCard.razor), így az CSAK a chart-régiót fedi, nem az egész kártyát —
       a kártya címe (forrásnév) látható marad, hogy lássék, MELYIK jelnek hiányzik az adata. */
    position: relative;
}

/* A chart-régió overlay-ének teteje a kártya KÖZEPÉN van (a fejléc alatt), nem a kártya felső
   élén — ezért ott ne legyen lekerekítés (különben a szürke lap teteje "úszó" pirulának néz ki).
   Csak az alsó sarkok maradnak lekerekítve, a kártya alsó éléhez igazodva. */
.chart-content .fluent-overlay {
    border-top-left-radius: 0;
    border-top-right-radius: 0;
}

p.source-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    line-height: 1.4;
    font-size: 12px;
    color: var(--muted-clr);
}

.chartbuttons {
    float: right;
    top: 4px;
    right: 4px;
    position: absolute;
    display: block;
}

.marked-for-deletion {
    opacity: 0.3;
    pointer-events: none;
}

/* Chart card */
.value-card {
    display: flex;
    justify-content: space-around;
    gap: 1rem;
    padding: 0.5rem;
    margin-top: 0.5rem;
    border-bottom-left-radius: var(--list-border-radius);
    border-bottom-right-radius: var(--list-border-radius);
    background: var(--background-clr);
}

:root[data-theme="dark"] .value-container label {
    color: var(--muted-clr);
}

:root[data-theme="dark"] .value-container .value {
    color: var(--text-clr);
}

.value-card .value-container {
    display: grid;
    gap: 0.25rem;
}

.value-container label {
    font-weight: 500;
}

.value-container .value {
    font-size: 1.125rem;
}

@media (max-width: 900px) {
    .value-card {
        gap: 0.25rem;
        padding: 0.25rem 0.5rem;
        margin-top: 0.25rem;
    }

    .value-container label {
        font-size: 0.7rem;
    }

    .value-container .value {
        font-size: 0.875rem;
    }
}

@media (max-width: 900px) {
    .chart-content.chart-no-data {
        display: none;
    }
}

/* A kamera-kártyák ugyanazzal a track-mérettel folynak mint a metrika-kártya-rács
   (.cards-section), és egyenként 2 oszlopot fognak át — így pontosan 2 sima kártya szélesek,
   nem nyúlnak teljes szélességűre. */
.cameras-wrapper {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    column-gap: 0.75rem;
    row-gap: 0.75rem;
    align-items: start;
    margin-top: 20px;
}

    .cameras-wrapper > * {
        grid-column: span 2;
    }

/* ── Kamera a metrika-rácson belül ──
   A kamera-kártya a többi kártyával EGY rácsban (.cards-section) él, ugyanazzal a SortableJS
   listával húzható/rendezhető, de 2×2 cellát fog át (= "4 kártyányi" hely), így nem nyúlik teljes
   szélességűre. Megjelenítéskor élő videó, szerkesztés módban placeholder áll a helyén. */
.cards-section .card.camera-card {
    grid-column: span 2;
    grid-row: span 2;
}

/* A videó/placeholder a fejléc (forrásnév + kamera neve) alatt, a kártya teljes szélességében. */
.cards-section .card.camera-card .video-wrapper,
.cards-section .card.camera-card .camera-card__placeholder {
    margin-top: 6px;
    width: 100%;
}

/* A belső grid alapból a value-t a kártya aljára igazítja (align-items:end) — a kamera törzse
   inkább a fejléc alól, fentről induljon. */
.cards-section .card.camera-card > div > div {
    align-items: stretch;
    align-content: start;
}

/* Szerkesztés módú placeholder: a 2×2-es helyfoglalást mutatja élő HLS lejátszó nélkül. */
.camera-card__placeholder {
    display: grid;
    place-items: center;
    align-content: center;
    gap: 6px;
    aspect-ratio: 16 / 9;
    border-radius: 10px;
    background: var(--background-clr);
    color: var(--muted-clr);
    font-size: 13px;
    text-align: center;
}

/* A failed card's notice in place of its content (CardComponent's ErrorBoundary, D-F4). */
.card .card-error {
    display: grid;
    place-items: center;
    align-content: center;
    gap: 6px;
    min-height: 64px;
    padding: 8px;
    font-size: 13px;
    text-align: center;
}

    /* The global span/div text colour (app.css) would override the inherited one. */
    .card .card-error,
    .card .card-error span {
        color: var(--muted-clr);
    }

    /* Lets the content back at once; the boundary retries on its own after a while anyway. */
    .card .card-error__retry {
        border: var(--border-thin);
        border-radius: var(--list-border-radius);
        background: transparent;
        color: var(--primary-clr);
        font-size: 12px;
        padding: 2px 10px;
        cursor: pointer;
    }

    /* The favourite-switches card has no frame of its own (its content draws one), so the notice draws it. */
    .cards-section .card.switches-card .card-error {
        flex: 1 1 auto;
        border-radius: var(--list-border-radius);
        border: var(--border-thin);
        background: var(--list-bg);
    }

/* Mobil/keskeny nézetben (a dashboard ekkor egyoszlopos, de a kártya-rácsban még elfér 2-3 db
   200px-es oszlop) a kamera-kártya TELJES szélességű legyen — a fél/kétharmad széles tile-on a kis
   videó csúnyán hatott. A "1 / -1" az egész sort átfogja, az aktuális oszlopszámtól függetlenül;
   grid-row:auto, hogy teljes szélességnél ne foglaljon feleslegesen 2 sort.

   FONTOS: a teljes szélességű kamera egyetlen magas sort képez, a rács globális grid-auto-rows:1fr
   pedig fix konténer-magasság híján MINDEN sort a legmagasabbhoz (a kameráéhoz) igazítana → a sima
   kártyák is megnyúlnának. Ezért itt a sorok a saját tartalmukhoz méreteződnek (auto): csak a kamera
   lesz magas, a telemetria-kártyák tömörek maradnak. (Desktopon marad az 1fr az egységes magassághoz.) */
@media (max-width: 768px) {
    .cards-section {
        grid-auto-rows: auto;
    }

    .cards-section .card.camera-card {
        grid-column: 1 / -1;
        grid-row: auto;
    }
}

/* ── Remote access card in the metric grid (remote-access-proxy-code-plan.md 9.2) ──
   Like the camera: 2x2 cells, dragged and sorted with the others, full width on a phone. "Open" puts the device's page
   into the iframe, which fills the card under its header; in edit mode a placeholder stands there. */
.cards-section .card.remote-card {
    grid-column: span 2;
    grid-row: span 2;
}

.cards-section .card.remote-card > div > div {
    align-items: stretch;
    align-content: start;
}

.remote-card__body {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-top: 6px;
    width: 100%;
    min-height: 0;
}

.remote-card__frame {
    width: 100%;
    min-height: 220px;
    flex: 1 1 auto;
    border: 0;
    border-radius: 10px;
    background: #fff;
}

.remote-card__placeholder {
    display: grid;
    place-items: center;
    align-content: center;
    gap: 6px;
    aspect-ratio: 16 / 9;
    border-radius: 10px;
    background: var(--background-clr);
    color: var(--muted-clr);
    font-size: 13px;
    text-align: center;
}

.remote-card__bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    font-size: 13px;
    color: var(--muted-clr);
    font-variant-numeric: tabular-nums;
}

.remote-card__error {
    margin: 0;
    font-size: 13px;
    color: var(--error-clr);
}

/* A line about the point under its name in the placeholder (an RDP device's unverified certificate). */
.remote-card__note {
    padding: 0 8px;
    font-size: 12px;
}

/* The frame's session ended while the link lives on: the frame stays, the reload starts a new session. */
.remote-card__ended {
    color: var(--warning-clr);
}

.remote-card-start-form {
    margin: 0;
}

@media (max-width: 768px) {
    .cards-section .card.remote-card {
        grid-column: 1 / -1;
        grid-row: auto;
    }
}

/* A remote access link that opens in a new tab, drawn like a small secondary AppButton (whose style is scoped to it). A real
   link and not a window.open after the request: the browser lets a click on a link open the tab. Global: the card, the
   session panel and the floating window use it. */
.remote-tab-link {
    display: inline-flex;
    align-items: center;
    padding: 0.25rem 0.7rem;
    border: 1px solid currentColor;
    border-radius: 8px;
    color: var(--text-clr);
    font-size: 0.85rem;
    font-weight: 600;
    line-height: 1.2;
    text-decoration: none;
    white-space: nowrap;
}

    .remote-tab-link:hover {
        background: var(--c-surface-hover);
        color: var(--text-clr);
    }

/* ── Kedvenc kapcsolók a metrika-rácson belül ──
   A FavSwitchesComponent (korábban a dashboard jobb felső fix dobozában) most a kamerával AZONOS módon
   egy opcionális, húzható/rendezhető 2×2 kártya a metrika-rácson belül. A megjelenést a komponens SAJÁT
   kártya-kerete (.fav-switches-card) adja, ezért a külső .card keretét/hátterét/paddingjét semlegesítjük
   (nincs dupla keret) — így a kapcsoló-kártya kinézete változatlan marad. */
.cards-section .card.switches-card {
    grid-column: span 2;
    grid-row: span 2;
    position: relative;
    background: transparent;
    border: none;
    box-shadow: none;
    padding: 0;
    overflow: visible;
    /* Floor, hogy szomszéd-kártyák híján (pl. külön sorba kerülve) se essen össze a belső, abszolút
       pozícionált tartalom (lásd lent). Felfelé NEM nő a tartalomtól — a magasságot a 2×2 cella adja. */
    min-height: 160px;
}

/* A belső tartalom-wrapper a 2×2 cellát ABSZOLÚT pozícióval tölti ki. Ez a kulcs ahhoz, hogy a sok
   kapcsolóból álló hosszú lista NE nyújtsa meg a kártyát: in-flow tartalomként a grid-item a lista
   min-content magasságára nőne (a height:100% + min-height:0 + overflow:hidden ezt NEM akadályozza
   meg — mérésekkel igazolva). Abszolút pozícióval a wrapper nem járul hozzá a kártya intrinsic
   magasságához → a kártyát a 2×2 cella (szomszéd adat-kártyák) mérete adja, a .fav-switches-scroll
   pedig görget, ha a kapcsolók nem férnek ki. (A kamera-kártyánál ez nem kell.) */
.cards-section .card.switches-card > div {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
}

/* A belső magasság-láncot FLEX-szel kötjük be (NEM height:100% százalékkal): a #switch-section
   container-type:inline-size-a (a .name cqw-alapú betűméretéhez kell) megakadályozza, hogy a
   height:100% feloldódjon → a lista a tartalom-magasságra nőtt és túllógott a 2×2 cellán. A flex
   (flex:1 + min-height:0) ezt megkerüli: minden szint a szülő definit magasságára feszül, így a
   .fav-switches-scroll helyesen görget. */
.cards-section .card.switches-card > div > div {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    /* align-items:stretch KÖTELEZŐ: a globális kártya-szabály a belső grid-re align-items:end-et tesz
       (a metrika-értéket a kártya aljára igazítja). Flex-column-ban az "end" VÍZSZINTESEN zsugorítja a
       gyerekeket a tartalom-szélességre → keskeny viewporton a kapcsoló-doboz és az edit-placeholder
       0/55px-re esett össze ("a switches kártya eltűnt"). A stretch teljes szélességre feszíti őket. */
    align-items: stretch;
}

.cards-section .card.switches-card #switch-section {
    flex: 1 1 auto;
    min-height: 0;
    /* A #switch-section container-type:inline-size-a (a .name cqw betűméretéhez) megtöri a flex
       cross-tengelyű (vízszintes) align-items:stretch-et → keskeny viewporton 0 szélességre esett
       össze a kapcsoló-doboz ("eltűnt"). Explicit width:100% kikényszeríti a teljes szélességet. */
    width: 100%;
}

/* Szerkesztés módban a törlés-gomb a kártya jobb felső sarkába lebeg (a belső tartalom fölé),
   mivel a külső .card paddingja itt 0. */
.cards-section .card.switches-card > fluent-button {
    position: absolute;
    top: 6px;
    right: 6px;
    z-index: 2;
}

/* Szerkesztés módú placeholder: a 2×2-es helyfoglalást mutatja az élő (interaktív) kapcsoló-kártya
   nélkül, hogy a húzás gördülékeny legyen és reorder közben ne lehessen véletlenül kapcsolni. */
.switches-card__placeholder {
    display: flex;
    flex-direction: column;
    gap: 6px;
    height: 100%;
    min-height: 160px;
    padding: 8px 10px;
    border-radius: var(--list-border-radius);
    border: var(--border-thin);
    background: var(--list-bg);
    color: var(--muted-clr);
    font-size: 13px;
    text-align: center;
}

/* A placeholder-ikon a fejléc alatti maradék helyet tölti, középre igazítva (a footage-panel mintája). */
.switches-card__placeholder-body {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Mobil/keskeny nézet: a kamera mintájára teljes szélességű, hogy a kapcsolók kényelmesen elférjenek. */
@media (max-width: 768px) {
    .cards-section .card.switches-card {
        grid-column: 1 / -1;
        grid-row: auto;
    }
}

/* ── Preset-kártya a metrika-rácson belül ──
   A térkép fölötti lebegő preset-pilleket leváltó, kihelyezhető kártya: a kamerával/kapcsolókkal AZONOS
   módon él a rácsban (egy SortableJS lista), 2×2 cellát fog át. A külső .card keretét MEGTARTJUK (a régi
   pilleknek nem volt saját kerete) — a "Presets" fejléc a kártya SourceName-je, a tartalom (gombrács +
   lapozó) a PresetCardComponent. */
.cards-section .card.preset-card {
    grid-column: span 2;
    grid-row: span 2;
    position: relative;
}

/* Szerkesztés módban a törlés-gomb a kártya jobb felső sarkába lebeg (a tartalom fölé), hogy ne foglaljon
   sort a magasság-rácsban (különben letolná a fejlécet/gombrácsot). A kapcsoló-kártyával egyezően. */
.cards-section .card.preset-card > fluent-button {
    position: absolute;
    top: 6px;
    right: 6px;
    z-index: 2;
}

/* Magasság-lánc: a fejléc (auto sor) alatt a tartalom (PresetCardComponent) kitölti a 2×2 cellát. */
.cards-section .card.preset-card > div {
    grid-template-rows: auto 1fr;
}

.cards-section .card.preset-card > div > div {
    align-items: stretch;
    align-content: stretch;
    height: 100%;
    min-height: 0;
}

/* Szerkesztés módú placeholder: a 2×2-es helyfoglalást mutatja az élő (interaktív) preset-kártya nélkül,
   hogy a húzás gördülékeny legyen és reorder közben ne lehessen véletlenül presetet aktiválni. */
.preset-card__placeholder {
    display: grid;
    place-items: center;
    align-content: center;
    height: 100%;
    min-height: 120px;
    color: var(--muted-clr);
}

/* Mobil/keskeny nézet: a kamera/kapcsolók mintájára teljes szélességű. */
@media (max-width: 768px) {
    .cards-section .card.preset-card {
        grid-column: 1 / -1;
        grid-row: auto;
    }
}


/* Percentage fill bar (tank level, fuel, humidity, battery SoC ...) — mockup metric "csík". */
.card .card-bar {
    width: 100%;
    height: 4px;
    margin: 5px 0 2px;
    border-radius: 3px;
    background: var(--extra-muted-clr);
    overflow: hidden;
}

.card .card-bar__fill {
    height: 100%;
    border-radius: 3px;
    background: var(--primary-clr);
    transition: width .3s ease;
}

/* N1 — per-típus mini-vizualizáció (szélirány-nyíl, hőmérő). A value MELLÉ kerül (metric-row),
   NEM alá → a kártya NEM nő magasabbra; a jobb oldali üres helyet használja. Token-színek (mind a 3 témán jó). */
.card .metric-row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
}

/* A dashboard telemetria-kártyáin (.cards-section) a value BALRA, a mini-viz a JOBB oldali üres
   helyre kerül (N1). Máshol (chart-kártyák a .wireframe-ben, kamera stb.) nincs viz, ott a value
   maradjon KÖZÉPEN — különben a space-between az egyetlen value-t balra húzná. */
.cards-section .card .metric-row {
    justify-content: space-between;
}

.card .card-viz {
    display: inline-flex;
    align-items: center;
    flex: none;
}

    .card .card-viz__dir {
        width: 24px;
        height: 24px;
        transition: transform .3s ease;
    }

    .card .card-viz__compass {
        width: 24px;
        height: 24px;
    }

        .card .card-viz__compass g {
            transition: transform .3s ease;
        }

    .card .card-viz__thermo {
        width: 13px;
        height: 26px;
    }

    /* Mélység hullám-vonalak — a dir/compass mintájára négyzetes. */
    .card .card-viz__depth {
        width: 22px;
        height: 22px;
    }

    .card .card-viz__rudder,
    .card .card-viz__gauge {
        width: 30px;
        height: 19px;
    }

    /* Áram-irány nyíl (töltés/merítés) — a dir/compass mintájára. */
    .card .card-viz__arrow {
        width: 20px;
        height: 20px;
    }

        .card .card-viz__rudder g,
        .card .card-viz__gauge g {
            transition: transform .3s ease;
        }

    /* SoC kitöltött gyűrű (a .card-bar % körkörös változata): a primary ív hossza a stroke-dashoffset-tel
       animálódik, így a töltöttség változása gördülékeny — a compass/dir mintájára 24×24. */
    .card .card-viz__ring {
        width: 24px;
        height: 24px;
    }

        .card .card-viz__ring circle {
            transition: stroke-dashoffset .3s ease;
        }

/* === Kártya függőleges ritmusa (név ↔ secondline + levegő a metrika-sor körül) ========================
   A .card-content rács KÉT `auto` sorból áll (NÉV + törzs), és az `align-content: stretch` (a normal
   alapérték) a maradék helyet EGYENLŐEN szórja szét a két sorra: ~8px a névsor ALÁ (holt hely, nem
   margó — a névsor nyúlik meg, a szöveg a tetején marad), a másik ~8px a törzsé. A törzsön belül
   ugyanez ismétlődik (lásd a következő blokkot), így a forrásnév és a secondline közé összesen ~11px
   holt hely került.
   Explicit `auto 1fr`: a névsor pontosan a szövege, MINDEN maradék a törzsé — a név és a secondline
   összezár.
   A :has() FONTOS: a CardComponent üres SourceName esetén ki sem rendeli a .source-name-et (add-csempe,
   kapcsoló-kártya), ott a törzs az EGYETLEN rácselem — fix `auto 1fr` mellett az `auto` sorba esne és
   a tartalmához zsugorodna (az add-csempe középre igazítása és a kapcsoló-kártya teljes magassága
   elveszne). Névsor nélkül tehát marad az egy-soros stretch. */
.cards-section .card > .card-content:has(> .source-name) {
    grid-template-rows: auto 1fr;
}

/* A törzs (secondline + metrika-sor + [%-csík] + dátum) alapból RÁCS `align-items: end`-del (lásd a
   `.card > div > div` szabályt fent): minden elem a saját — egyenlően felnyújtott — sorának az ALJÁN ül,
   ezért a hézag mindig az elem FÖLÉ kerül, a metrika-sor alá pedig SEMMI nem jut → a gauge-mód 56px-es
   vize gyakorlatilag nekifekszik a dátumsornak.
   Flex-oszlopban ez irányítható: a maradék helyet KÉT auto-margó osztja el egyenlően — a metrika-sor
   fölé és a dátum fölé. Következmény: a dátum marad a kártya alján (ott is volt, az `end` miatt), a
   metrika-sor ALÁ viszont ~8px levegő kerül (2.8px helyett), a sor pedig ennyivel feljebb csúszik, így
   a nagy viz nem ér hozzá a dátumhoz.
   Miért auto-margó és nem fix padding: ha a secondline két sorba törik, az auto-margók maguktól 0-ra
   fogynak → a tartalom nem csordul ki a 152px-es cellából.
   Az `align-items: stretch` KÖTELEZŐ: a fenti globális szabály `end`-je flex-oszlopban már a KERESZT-
   (vízszintes) tengelyre szólna, azaz jobbra zsugorítaná a sorokat (ugyanez a csapda a switches-card
   szabálynál is dokumentálva van).
   A :has() kizárja a panel / kamera / kapcsoló-kártyákat: azoknál HideLast → nincs .metric-row, a
   törzsük elrendezése (és a panels.css abszolút-pozíciós felülírása) érintetlen marad. */
.cards-section .card > .card-content > div:has(> .metric-row) {
    display: flex;
    flex-direction: column;
    align-items: stretch;
}

    .cards-section .card > .card-content > div > .metric-row,
    .cards-section .card > .card-content > div > p:last-child {
        margin-top: auto;
    }

/* === Metrika-sor FIX magasság + gauge mód (WS_CardGaugeModes) ========================================
   FIX SOR-MAGASSÁG: a metric-row MINDEN telemetria-kártyán fix 56px (nem csak a gauge-képeseken) →
   a card-content grid nem osztja szét eltérően a maradék helyet, így a forrásnév / secondline / dátum
   kártyáról kártyára AZONOS magasságban van, és a gauge be/ki váltás sem mozdítja őket ("keret").
   56px a legmagasabb, ami a legszűkebb kártyán (source-name + secondline + sor + zónás timestamp) is
   elfér a 152px cellában. A %-csíkos kártyák (--withbar) sora rövidebb (45px), mert alatta ott a 11px
   csík: a sor + csík együtt ≈ 56px, így a dátumsor ott is egy vonalban marad a többivel.
   (A dátum függőleges helyét MOST már a fenti "függőleges ritmus" blokk auto-margója horgonyozza a
   kártya aljára; ez a fix sor-magasság így az ÉRTÉK-blokk egységes helyét/magasságát tartja.) */
.cards-section .card .metric-row {
    height: 56px;
}

    .cards-section .card .metric-row--withbar {
        height: 45px;
    }

/* Gauge mód: UGYANAZ a markup (h2 + .card-viz), csak ez a módosító kerül a sorra; a viz (56px) pont
   kitölti a fix sort. A MŰSZER áll pontosan a sor közepén, a kis érték MELLETTE (jobbra) — ezért 3
   oszlopos rács a korábbi row-reverse flex helyett: ott a viz + érték EGYÜTT került középre, így a
   műszer kártyánként máshová esett (az érték szélességétől függően), és a rács se volt egy vonalban.
   A két szélső 1fr sáv definíció szerint egyforma → a középső (auto) viz-oszlop a sor közepére esik.
   Szándékosan `1fr` és NEM `minmax(0, 1fr)`: ha az érték szélesebb a fél maradéknál, a jobb oldali
   sáv a tartalmáig nő (a bal rovására) és a műszer arrébb csúszik — vagyis a szöveg sosem vágódik le,
   csak a tökéletes középre-igazítás enged (a mai viselkedés). */
.cards-section .card .metric-row--gauge {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 2px;
}

    /* MINDKETTŐN KELL a `grid-row: 1`! A DOM-ban a h2 (3. oszlop) áll elöl, a viz (2. oszlop) mögötte:
       ha csak az oszlopukat kötjük meg, az auto-elhelyezés "sparse" szabálya a visszafelé lépő elemet
       ÚJ SORBA teszi (a kurzor már a 3. oszlop után jár) → a viz a 2. sorba esik, az érték fölé, a sor
       tartalma 56px helyett ~73px lesz és alul belelóg a dátumba. Explicit sorral egyetlen sor marad. */
    .cards-section .card .metric-row--gauge .card-viz {
        grid-column: 2;
        grid-row: 1;
    }

    /* A justify-self:start teszi közvetlenül a műszer mellé (a sávján belül balra zárva), nem a jobb
       sáv közepére. */
    .cards-section .card .metric-row--gauge h2 {
        grid-column: 3;
        grid-row: 1;
        justify-self: start;
        font-size: 1rem;
        line-height: 1;
        margin: 0;
    }

    .cards-section .card .metric-row--gauge .metric-unit {
        font-size: 12px;
    }

    /* Négyzetes viewBox-ú vizek (irány/iránytű/SoC-gyűrű/áram-nyíl/mélység-hullám) egységes 56×56. */
    .cards-section .card .metric-row--gauge .card-viz__dir,
    .cards-section .card .metric-row--gauge .card-viz__compass,
    .cards-section .card .metric-row--gauge .card-viz__ring,
    .cards-section .card .metric-row--gauge .card-viz__arrow,
    .cards-section .card .metric-row--gauge .card-viz__depth {
        width: 56px;
        height: 56px;
    }

    .cards-section .card .metric-row--gauge .card-viz__thermo {
        width: 36px;
        height: 56px;
    }

    .cards-section .card .metric-row--gauge .card-viz__rudder,
    .cards-section .card .metric-row--gauge .card-viz__gauge {
        width: 84px;
        height: 56px;
    }

    /* Optikai igazítás — a félkör-mérő és a hőmérő RAJZA nem tölti ki függőlegesen a viewBox-át (a mérő
       rajza y=3..17.8 a 0..19-ből, a hőmérőé y=3..27 a 0..28-ból), ezért a rajz közepe a doboz közepe
       ALATT van (mérő: +2.5px, hőmérő: +2px a nagyított méreteken). A doboz közepére igazított érték
       emiatt látszik magasabbnak a műszernél — ennyivel feljebb toljuk a rajzot, így a műszer közepe és
       az érték egy vonalba kerül. A transform NEM befolyásolja a layoutot (a sor 56px marad), és ezeknek
       az SVG-knek nincs elem-szintű transformjuk: a mutató forgatása a belső <g>-n ül. (A rudder/nyíl/
       iránytű/gyűrű/hullám rajza már eleve középen van, azokat nem kell tolni.) */
    .cards-section .card .metric-row--gauge .card-viz__gauge {
        transform: translateY(-2.5px);
    }

    .cards-section .card .metric-row--gauge .card-viz__thermo {
        transform: translateY(-2px);
    }

/* No-data card: the freshness dot must NOT read as "fresh" (green) when there is no value.
   A muted grey dot keeps the row aligned without falsely signalling live data. */
.card .status-dot.nodata {
    background: var(--extra-muted-clr);
}

/* === Panel-előnézet (kártya-választó ÉS dashboard szerkesztő mód) =====================================
   A panelek élő komponensként jelennek meg. display:contents → a burkoló nem hoz létre boxot, így a
   panels.css elrendezése (a ChildContent-burkoló > .panel viszony) változatlanul érvényes; a panel az
   előnézetben is helyesen tölti a kártyát. A --static módosító letiltja a panelen belüli pointer-eventeket
   (kártya-választóban MINDIG, a dashboard szerkesztő módjában is) — így a panelen belüli gombok (pl.
   presets-mini) nem sülnek el, a húzás gördülékeny, és a kattintás a kártyára (hozzáadás) jut. */
.panel-preview {
    display: contents;
}

.panel-preview--static * {
    pointer-events: none;
}

/* === Kártya-választó (CardPickerDialog) ===============================================================
   Domain-választó gombsor: egyszerre EGY domaint látunk (a cím-fejlécek helyett kattintható "tab"-ok). */
/* A body felső paddingja (user-kérés: 20px). A dialógus MINDIG "nagyban" nyíljon (mintha görgetni lehetne),
   de a KÁRTYÁK mérete maradjon szokásos: a min-magasság a BODY-n van (NEM a rácson) → a rács a tartalmához
   méreteződik (felül marad), a maradék hely üres; sok kártyánál a body túlnő és a dialógus görget. Korábban a
   rácson volt a min-height → a grid-auto-rows:1fr felnyújtotta az egyetlen sornyi 1×1 kártyát. */
.picker-body {
    padding-top: 20px;
    /* A dialógus fix 90dvh (GhostCard DialogParameters.Height) → a body kitölti a rendelkezésre álló magasságot;
       a rács felül marad (kártyák normál méret), a maradék üres / a body görget sok kártyánál. A flex:1 + a
       min-height:100% együtt kitölti a body-t akár flex-, akár definit-magasságú a FluentDialogBody. */
    flex: 1 1 auto;
    min-height: 100%;
    display: flex;
    flex-direction: column;
}

/* Kereső: ikon + input egy pill-sorban, a tabok/rács fölött. */
.picker-search {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 1rem;
    padding: 0.4rem 0.85rem;
    border: var(--border-thin);
    border-radius: 999px;
    background: var(--list-bg);
}

.picker-search:focus-within {
    border-color: var(--primary-clr);
}

.picker-search__input {
    flex: 1 1 auto;
    min-width: 0;
    border: none;
    background: none;
    outline: none;
    color: var(--text-clr);
    font-size: 0.9rem;
}

.picker-search__input::placeholder {
    color: var(--muted-clr);
}

/* Kereső törlése (X) a sáv végén — csak nem üres keresőnél jelenik meg. */
.picker-search__clear {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    padding: 2px;
    border: none;
    border-radius: 50%;
    background: none;
    color: var(--muted-clr);
    cursor: pointer;
}

.picker-search__clear:hover {
    color: var(--text-clr);
}

.picker-search__clear:focus-visible {
    outline: 2px solid var(--primary-clr);
    outline-offset: 1px;
}

/* Üres domain-csoport: középre igazított üzenet, ami kitölti a body maradék (üres) magasságát. A pickerben
   NEM dőlt (user-kérés). A szöveg-spanre EXPLICIT muted (a globális `span{color:text-clr}` felülírná). */
.picker-empty-group {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    color: var(--muted-clr);
    font-size: 14px;
    text-align: center;
}

.picker-empty-group span {
    color: var(--muted-clr);
}

.picker-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-bottom: 1rem;
}

.picker-tab {
    padding: 0.35rem 0.8rem;
    border: var(--border-thin);
    border-radius: 999px;
    background: var(--list-bg);
    color: var(--text-clr);
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
    transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}

.picker-tab:hover {
    border-color: var(--primary-clr);
}

.picker-tab--active {
    background: var(--primary-clr);
    border-color: var(--primary-clr);
    color: var(--c-on-primary, #fff);
}

/* Már kihelyezett kártya jelzése a pickerben: HANGSÚLYOS brand-keret — primary border + 2px primary gyűrű
   (box-shadow, nincs layout-eltolás). A régi zöld pipa-badge helyett (user-kérés: legyen erősebb a keret). */
.picker-grid .card.picker-onboard {
    border-color: var(--primary-clr);
    box-shadow: 0 0 0 2px var(--primary-clr);
}

/* Törlés-gomb (CardComponent Closeable) — UGYANAZ a kör-alakú gomb a dashboard szerkesztő módjában ÉS a
   kártya-választóban. Magas z-index + saját pointer-events → az élő panel-tartalom fölött is megbízhatóan
   kattintható. A .card már position:relative (lásd .cards-section .card / panel-keretek), így a jobb felső
   sarokhoz horgonyzódik, és NEM ad flow-magasságot (a kártyák edit módban sem lesznek magasabbak). */
.cards-section .card > .card-remove {
    position: absolute;
    top: 6px;
    right: 6px;
    z-index: 5;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border-radius: 50%;
    border: var(--border-thin);
    background: var(--list-bg);
    box-shadow: 0 1px 3px var(--c-shadow-sm);
    cursor: pointer;
    pointer-events: auto;
}

.cards-section .card > .card-remove:hover {
    border-color: var(--delete-clr);
}

/* Gauge-mód kapcsoló (CardComponent ShowGaugeToggle) — a törlés-gomb tükörképe a BAL felső sarokban;
   csak szerkesztő módban, gauge-képes kártyán renderelődik. Bekapcsolt állapotban (.on) primary keret +
   halvány primary töltés jelzi, hogy a kártya műszer-nézetben áll. */
.cards-section .card > .card-gauge-toggle {
    position: absolute;
    bottom: 6px;
    right: 6px;
    z-index: 5;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border-radius: 50%;
    border: var(--border-thin);
    background: var(--list-bg);
    box-shadow: 0 1px 3px var(--c-shadow-sm);
    cursor: pointer;
    pointer-events: auto;
}

.cards-section .card > .card-gauge-toggle:hover {
    border-color: var(--primary-clr);
}

.cards-section .card > .card-gauge-toggle.on {
    border-color: var(--primary-clr);
    background: color-mix(in srgb, var(--primary-clr) 12%, var(--list-bg));
}

/* panels.css */
/* ========================================================================================================
   Dashboard PANEL-kártyák (asd.md Task 1+) — a cards.css-ből kiemelve, mert a panelek saját, közös
   "műszerfal" mintát követnek (motor / navigáció / elektromos eszköz / elektromos csoport / mini-preset).
   A panel-tartalom általános osztályai: .panel (keret/elrendezés), .panel-cell (egy readout = PanelReadoutCell).
   A generikus kártya-építőkövek (.card, .card-viz, .card-bar) MARADNAK a cards.css-ben (a DisplayCard is használja).
   ======================================================================================================== */

/* ── A panelek a metrika-rácson belül ──
   A 4 nagy panel a kamerával/preset-tel AZONOS módon él a rácsban (egy SortableJS lista), 2×2 cellát fog át.
   A külső .card keretet MEGTARTJUK (a tartalmat a *PanelComponent adja). A presets-mini 1×1 marad. */
.cards-section .card.engine-panel,
.cards-section .card.elec-panel,
.cards-section .card.elec-group-panel,
.cards-section .card.nav-panel,
.cards-section .card.footage-panel {
    grid-column: span 2;
    grid-row: span 2;
    position: relative;
}

.cards-section .card.presets-mini-card {
    position: relative;
    /* A tartalom az 1 soros cellánál magasabbra nőne → a grid-auto-rows:1fr megnyújtaná a SORT. A
       switches-kártya abszolút-pozíciós mintája: a törzs a cellát tölti, NEM járul hozzá a sor-magassághoz. */
    padding: 0;
    overflow: hidden;
    min-height: 90px;
}

.cards-section .card.presets-mini-card > div {
    position: absolute;
    inset: 0;
    padding: 8px 10px;
    display: flex;
    flex-direction: column;
}

.cards-section .card.presets-mini-card > div > div {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    align-items: stretch;
}

/* Szerkesztés módban a törlés-gomb a kártya jobb felső sarkába lebeg (nem foglal sort a magasság-rácsban). */
.cards-section .card.engine-panel > fluent-button,
.cards-section .card.elec-panel > fluent-button,
.cards-section .card.elec-group-panel > fluent-button,
.cards-section .card.nav-panel > fluent-button,
.cards-section .card.footage-panel > fluent-button,
.cards-section .card.presets-mini-card > fluent-button {
    position: absolute;
    top: 6px;
    right: 6px;
    z-index: 2;
}

/* === Műszerfal-panelek kerete (motor + navigáció + elektromos eszköz) ===============================
   A "műszerfal" tartalma (fejléc + hero + readout-rács) a 2×2 cellánál magasabbra is nőhet, ezért — a
   csoport-panel/switches mintájára — abszolút-pozíciós törzs: a cella adja a magasságot, a belső .panel
   görget, így a globális grid-auto-rows:1fr NEM nyújtja meg a sort. A .card keret marad; a paddinget 0-ra
   állítjuk, az abszolút .card-content adja a belső paddingot. */
.cards-section .card.engine-panel,
.cards-section .card.nav-panel,
.cards-section .card.elec-panel,
.cards-section .card.elec-group-panel,
.cards-section .card.footage-panel {
    padding: 0;
    overflow: hidden;
    /* A törzs ABSZOLÚT pozícionált (0 intrinsic magasság), ezért a magasságot a 2×2 cella adja. Sima
       kártyák mellett a szomszéd 1×1-ek húzzák fel a sorokat (~2× kártyamagasság); DE ha CSAK 2×2 panel
       van a dashboardon (nincs szomszéd 1×1), a sorok a min-height-re esnének → a panel összemegy és görget.
       Ezért a min-height ~2× egy sima kártya magassága: a magányos panel is elég magas, a vegyes elrendezést
       viszont nem nyújtja (a min-height/2 ≈ a sima kártya magassága, így a grid-auto-rows:1fr-t nem lépi túl).
       (Ha egy panel tartalma ennél is magasabb, a belső .panel továbbra is görget.) */
    min-height: 280px;
}

.cards-section .card.engine-panel > div,            /* .card-content: cím (.source-name) + törzs */
.cards-section .card.nav-panel > div,
.cards-section .card.elec-panel > div,
.cards-section .card.elec-group-panel > div,
.cards-section .card.footage-panel > div {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    padding: 8px 10px;
}

.cards-section .card.engine-panel > div > div,      /* a ChildContent-burkoló — a panelt tölti */
.cards-section .card.nav-panel > div > div,
.cards-section .card.elec-panel > div > div,
.cards-section .card.elec-group-panel > div > div,
.cards-section .card.footage-panel > div > div {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    align-items: stretch;
}

/* A műszerfal-panel a fejlécét (név + dátum + pötty) maga adja, ezért a CardComponent .source-name
   kártya-címét és a DisplayCard "Panel" .secondline-ját elrejtjük. Ez a .cards-section rácsra vonatkozik,
   ami MOST a kártya-választót is jelenti: a picker ÉLŐ panel-előnézetet renderel (saját fejléccel), ezért
   ott is a panel saját címét akarjuk, nem a CardComponent duplikált címét. A csoport-panelnél a .secondline
   a csoportnév, azt is mi adjuk a fejlécben → elrejtjük. */
.cards-section .card.engine-panel > div > .source-name,
.cards-section .card.engine-panel .secondline,
.cards-section .card.nav-panel > div > .source-name,
.cards-section .card.nav-panel .secondline,
.cards-section .card.elec-panel > div > .source-name,
.cards-section .card.elec-panel .secondline,
.cards-section .card.elec-group-panel > div > .source-name,
.cards-section .card.elec-group-panel .secondline,
.cards-section .card.footage-panel > div > .source-name,
.cards-section .card.footage-panel .secondline {
    display: none;
}

/* === Műszerfal-panel belső elrendezése (közös: motor, navigáció, …) ================================== */
.panel {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
    overflow-y: auto;
    gap: 8px;
    /* Iránytű két tűjének színe (navigáció): heading = brand, course = zöld. Egy helyen hangolható. */
    --panel-heading-clr: var(--primary-clr);
    --panel-course-clr: var(--c-success);
}

/* Night témán a zöld scotopikusan rossz (a night paletta vörös-eltolt, kék/zöld kerülendő) + a heading
   ott crimson → a course-t amberre váltjuk: night-barát és jól elválik a piros heading-tűtől. */
:root[data-theme="night"] .panel {
    --panel-course-clr: var(--nt-amber-600);
}

/* Fejléc: név (balra) + a minta dátuma és a státusz-pötty (jobbra). */
.panel__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

.panel__name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-clr);
    text-align: left;
}

.panel__meta {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.panel__date {
    font-size: 11px;
    color: var(--muted-clr);
    white-space: nowrap;
}

.panel__status {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex: none;
    background: var(--c-success);
}

    .panel__status.stale { background: var(--stale-clr); }
    .panel__status.alarm-warning { background: var(--warning-clr); }
    .panel__status.alarm-error { background: var(--critical-clr); }

/* Az EGY-dátumos 2×2 panelek (motor / navigáció / egy-eszközös elektromos) fejléc-metája — a STÁTUSZ-PÖTTY
   ÉS a dátum együtt (mint az 1×1 kártyák .card-status-line-ja: [pötty] dátum) — a kártya BAL ALSÓ sarkába
   kerül. A dátum+pötty közös konténerét (engine/nav: .panel__meta, egy-eszközös elec: .elec-block__meta)
   pozícionáljuk abszolút a nem-görgő .card-content-hez képest (a .panel static + görget, így NEM az ő
   containing block-ja) → görgetéskor is a sarokban marad. A markup [dátum][pötty] sorrendjét row-reverse
   fordítja [pötty][dátum]-ra (1×1-gyel egyező). A .panel alsó paddingja helyet tart, hogy ne fedje a
   legalsó tartalom-sort. A csoport-panel (.elec-group-panel) per-eszköz dátum+pöttyei SZÁNDÉKOSAN maradnak
   a blokk-fejlécekben, a felső aggregált pötty is a csoport-fejlécben. */
.cards-section .card.engine-panel .panel,
.cards-section .card.nav-panel .panel,
.cards-section .card.elec-panel .panel {
    padding-bottom: 18px;
}

.cards-section .card.engine-panel .panel__meta,
.cards-section .card.nav-panel .panel__meta,
.cards-section .card.elec-panel .elec-block__meta {
    position: absolute;
    left: 6px;
    bottom: 6px;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    flex-direction: row-reverse;
    gap: 5px;
    margin-left: 0;
    /* Átlátszatlan chip a kártya hátterével (--list-bg): kis képernyőn, amikor a .panel GÖRGET, a tartalom
       a rögzített dátum MÖGÉ kerül (nem folyik át rajta). Görgetés nélkül a chip beleolvad a háttérbe → csak
       a pötty+dátum látszik. A panelek sosem alarm-színűek (a panel-piece AlarmLevel=Normal), ezért a
       --list-bg mindig a helyes kártya-háttér. A .panel padding-bottom-ja (lásd fent) gondoskodik róla, hogy
       a legaljára görgetve az utolsó sor a chip FÖLÖTT, teljesen látszódjon. */
    background: var(--list-bg);
    padding: 2px 6px;
    border-radius: 8px;
}

/* Hero: középen a nagy mérő (RPM / iránytű …), két oldalán readout-oszlopok (a szélesség kihasználása). */
.panel__hero {
    display: flex;
    align-items: center;
    height: 100%;
    gap: 1rem;
}

.panel__hero-side {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 1rem;
}

.panel__hero-center {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    border-radius: 6px;
    padding: 0 4px;
}

.panel__hero-gauge {
    width: 84px;
    height: 53px;
}

    .panel__hero-gauge g {
        transition: transform .3s ease;
    }

/* Iránytű-hero (navigáció): négyzetes tárcsa — a közös hero-viz nagyobb (a motor mérőjének ~kétszerese). */
.panel__hero-compass {
    width: 116px;
    height: 116px;
}

    .panel__hero-compass g {
        transition: transform .3s ease;
    }

/* Iránytű-legend: a heading és course értéke külön sorban, mindkettő a tűje színét viseli
   (pötty + felirat + érték), hogy egyértelmű legyen, melyik-melyik. */
.panel__compass-legend {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-top: 3px;
    width: 100%;
    max-width: 130px;
}

.panel__compass-row {
    display: flex;
    align-items: center;
    gap: 5px;
    line-height: 1.15;
}

.panel__compass-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex: none;
}

.panel__compass-lbl {
    font-size: 11px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.panel__compass-val {
    font-size: 14px;
    font-weight: 600;
    margin-left: auto;
    white-space: nowrap;
}

.panel__compass-row--heading .panel__compass-dot { background: var(--panel-heading-clr); }
.panel__compass-row--heading .panel__compass-lbl,
.panel__compass-row--heading .panel__compass-val { color: var(--panel-heading-clr); }

.panel__compass-row--course .panel__compass-dot { background: var(--panel-course-clr); }
.panel__compass-row--course .panel__compass-lbl,
.panel__compass-row--course .panel__compass-val { color: var(--panel-course-clr); }

/* Kiemelt pozíció-sor (navigáció): koordináták ikonnal, teljes szélességben. */
.panel__pos {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
}

.panel__pos-val {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-clr);
    line-height: 1.25;
}

.panel__pos.stale {
    opacity: 0.5;
    filter: grayscale(0.7);
}

.panel__hero-num {
    display: flex;
    align-items: baseline;
    gap: 3px;
    line-height: 1;
}

.panel__hero-val {
    font-size: 24px;
    font-weight: 600;
    color: var(--text-clr);
}

.panel__hero-unit {
    font-size: 12px;
    color: var(--muted-clr);
}

.panel__hero-nodata {
    font-size: 14px;
    font-weight: 500;
    color: var(--muted-clr);
}

/* A hero közepének (mérő-cella) alarm-szín / elavult-szürke állapota. */
.panel__hero-center.stale {
    opacity: 0.5;
    filter: grayscale(0.7);
}

.panel__hero-center.alarm-warning {
    background: color-mix(in srgb, var(--warning-clr) 22%, transparent);
}

.panel__hero-center.alarm-error {
    background: color-mix(in srgb, var(--critical-clr) 18%, transparent);
}

    .panel__hero-center.alarm-error .panel__hero-val {
        color: var(--critical-clr);
    }

/* Readout-rács: 3 oszlop, kompakt cellák. */
.panel__grid {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    justify-items: center;
    gap: 4px 8px;
}

/* === Egy readout-cella (PanelReadoutCell.razor) ===================================================== */
.panel-cell {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: left;
    padding: 4px;
    border-radius: 6px;
    min-width: 0;
}

.panel-cell__label {
    font-size: 12px;
    color: var(--muted-clr);
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* A mini-viz mindig BALRA (a valrow elején), az érték+egység utána (jobbra). */
.panel-cell__valrow {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 4px;
    min-width: 0;
}

.panel-cell__val {
    font-size: 16px;
    font-weight: 600;
    color: var(--text-clr);
}

.panel-cell__unit {
    font-size: 12px;
    color: var(--muted-clr);
    margin-left: 2px;
}

.panel-cell__nodata {
    font-size: 12px;
    color: var(--muted-clr);
}

/* Alarm-szín / elavult-szürke a kis kártyák (DisplayCard) logikájával egyezően. */
.panel-cell.stale {
    opacity: 0.5;
    filter: grayscale(0.7);
}

.panel-cell.alarm-warning {
    background: color-mix(in srgb, var(--warning-clr) 22%, transparent);
}

.panel-cell.alarm-error {
    background: color-mix(in srgb, var(--critical-clr) 18%, transparent);
}

    .panel-cell.alarm-error .panel-cell__val,
    .panel-cell.alarm-error .panel-cell__label {
        color: var(--critical-clr);
    }

/* === Elektromos eszköz-blokk (ElectricalDeviceBlock — per-eszköz + csoport-panel közös) ============= */
.elec-block {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

/* A per-eszköz panelnél a blokk az EGYETLEN tartalom → töltse ki a cellát (a battery-rombusz height:100%-ához).
   A csoport-panelnél több blokk van (NEM only-child), ezek tartalom-magasságúak, egymás alatt görgethetők. */
.panel > .elec-block:only-child {
    height: 100%;
}

/* Csoport-panelben elválasztó a (felső) csoportnév-fejléc ÉS az első blokk közé, valamint a blokkok közé.
   (A .panel gap adja a külső térközt; itt csak a vonal + kis belső térköz.) */
.panel__head + .elec-block,
.elec-block + .elec-block {
    padding-top: 8px;
    border-top: var(--border-thin);
}

.elec-block__head {
    display: flex;
    align-items: center;
    gap: 6px;
}

.elec-block__name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-clr);
}

.elec-block__chip {
    flex: 0 1 auto;
    min-width: 0;
    max-width: 45%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 10px;
    font-weight: 600;
    padding: 1px 6px;
    border-radius: 9px;
    background: color-mix(in srgb, var(--primary-clr) 14%, transparent);
    color: var(--primary-clr);
}

.elec-block__meta {
    flex: none;
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

/* DC akku — ROMBUSZ elrendezés: felül a nagyobb SoC-gyűrű, balra feszültség, jobbra áram, alul hőmérséklet.
   3×3 grid: SoC a felső középen, V bal-közép, I jobb-közép, T alsó-közép. */
.elec-batt {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    justify-items: center;
    gap: 4px 8px;
    height: 100%;
}

/* A SoC a felső-középen; a feszültség/áram VELE EGY SORBAN (a SoC vonalába érve, align-items:center →
   a gyűrű függőleges közepéhez igazítva); a hőmérséklet a SoC alatt, középen. */
.elec-batt__soc {
    grid-column: 2;
    grid-row: 1 / span 2;
    border-radius: 6px;
}
.elec-batt__v   { grid-column: 1; grid-row: 2; }
.elec-batt__i   { grid-column: 3; grid-row: 2; }
.elec-batt__t   { grid-column: 2; grid-row: 3; }

/* SoC-gyűrű középre írt %-kal (nagyobb mérő — a rombusz központi eleme). */
.elec-batt__ring {
    position: relative;
    width: 116px;
    height: 116px;
}

    .elec-batt__ring svg {
        width: 100%;
        height: 100%;
    }

        .elec-batt__ring svg circle {
            transition: stroke-dashoffset .3s ease;
        }

.elec-batt__ring-val {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    font-size: 24px;
    font-weight: 700;
    color: var(--text-clr);
}

.elec-batt__soc.stale { opacity: 0.5; filter: grayscale(0.7); }

    .elec-batt__soc.alarm-error .elec-batt__ring-val { color: var(--critical-clr); }

/* A V/I/T akku-cellák a közös PanelReadoutCell-t használják (felirat + balra viz + jobbra érték; a viz a
   MiniViz.Classify-ból: V→gauge, I→áram-nyíl, T→hőmérő). NAGYOBB érték a jó olvashatóságért (kevés adat,
   sok hely a 2×2 panelen — user-kérés). A solar (DC nem-akku, .elec-dc) ugyanígy nagyobb (lásd lent). */
.elec-batt .panel-cell__val { font-size: 22px; }
.elec-batt .panel-cell__unit { font-size: 14px; }

/* Kompakt összefoglaló (csoport-panel egy eszközéhez): kulcs-metrikák egy (törhető) sorban. */
.elec-summary {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 14px;
}

    .elec-summary > .panel-cell {
        align-items: flex-start;
        padding: 0;
    }

.elec-summary__power {
    display: flex;
    flex-direction: column;
}

.elec-summary__power-lbl {
    font-size: 10px;
    color: var(--muted-clr);
}

.elec-summary__power-val {
    font-size: 16px;
    font-weight: 700;
    color: var(--text-clr);
}

/* DC nem-akku (pl. solar): feszültség + áram egymás mellett, középre. */
.elec-dc {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 1.5rem;
    height: 100%;
}

/* Solar/DC-nem-akku: nagyobb érték (2 érték, sok hely — user-kérés, az akku-rombusszal egyezően). */
.elec-dc .panel-cell__val { font-size: 22px; }
.elec-dc .panel-cell__unit { font-size: 14px; }

/* AC táblázat: V/A/Hz/W/PF sorok × fázis-oszlopok. */
.elec-ac {
    display: grid;
    gap: 0.5rem;
    align-items: center;
    font-size: 12px;
}

.elec-ac__corner {
    /* üres bal-felső sarok a fázis-fejléc sorban */
}

.elec-ac__phase {
    font-size: 10px;
    font-weight: 600;
    color: var(--muted-clr);
    text-align: right;
}

.elec-ac__rowlbl {
    font-size: 10px;
    font-weight: 600;
    color: var(--muted-clr);
    white-space: nowrap;
}

.elec-ac__cell {
    text-align: right;
    font-size: 1rem;
    font-variant-numeric: tabular-nums;
    color: var(--text-clr);
    border-radius: 4px;
    padding: 0 2px;
}

    .elec-ac__cell.stale { opacity: 0.5; filter: grayscale(0.7); }
    .elec-ac__cell.alarm-warning { background: color-mix(in srgb, var(--warning-clr) 22%, transparent); }
    .elec-ac__cell.alarm-error { background: color-mix(in srgb, var(--critical-clr) 18%, transparent); color: var(--critical-clr); }

/* Kiemelt összteljesítmény az AC táblázat alatt. */
.elec-ac__total {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    margin-top: 4px;
    padding-top: 4px;
    border-top: var(--border-thin);
}

.elec-ac__total-lbl {
    font-size: 11px;
    color: var(--muted-clr);
}

.elec-ac__total-val {
    font-size: 16px;
    font-weight: 700;
    color: var(--text-clr);
}

/* (Kompakt módban — csoport-panel — a teljes táblázat/rombusz HELYETT az .elec-summary kulcs-metrika-sor
   jelenik meg, ezért külön .compact méret-override-okra nincs szükség.) */

/* (Az elektromos-CSOPORT panel kerete a fenti közös műszerfal-frame-be olvadt: abszolút törzs +
   görgethető .panel, hogy sok eszköznél se nyúljon meg a 2×2 cella.) */

/* Szerkesztés/stub placeholder a 4 nagy panelhez: a helyfoglalást mutatja a (még üres) tartalom helyett. */
.panel-card__placeholder {
    display: grid;
    place-items: center;
    align-content: center;
    gap: 6px;
    height: 100%;
    min-height: 120px;
    color: var(--muted-clr);
    font-size: 13px;
    text-align: center;
}

/* A presets-mini (1×1) placeholdere: csak ikon, középre. */
.presets-mini-card__placeholder {
    display: grid;
    place-items: center;
    align-content: center;
    height: 100%;
    min-height: 60px;
    color: var(--muted-clr);
}

/* Mobil/keskeny nézet: a nagy panelek teljes szélességűek (a presets-mini 1×1 marad). */
@media (max-width: 768px) {
    .cards-section .card.engine-panel,
    .cards-section .card.elec-panel,
    .cards-section .card.elec-group-panel,
    .cards-section .card.nav-panel,
    .cards-section .card.footage-panel {
        grid-column: 1 / -1;
        grid-row: auto;
    }
}

/* === Footage-panel (a legutóbbi 4 felvétel thumbnailje) ============================================
   A .panel (flex column: fejléc + rács) mintáját követi. A rács 2×2 (max 4 elem), a cellák a rendelkezésre
   álló magasságot töltik (grid-auto-rows:1fr) — a thumbnail 16:9 helyett flex-kitöltés, hogy a 2×2 cellán
   belül maradjon. A footage-lista mintája: thumbnail + hossz-badge + dátum. */
.footage-panel__grid {
    flex: 1 1 auto;
    min-height: 0;
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-auto-rows: 1fr;
    gap: 6px;
}

.footage-panel__item {
    display: flex;
    flex-direction: column;
    min-height: 0;
    gap: 3px;
    padding: 0;
    border: none;
    background: none;
    font: inherit;
    color: inherit;
    text-align: left;
    cursor: pointer;
    overflow: hidden;
}

.footage-panel__thumb-wrap {
    position: relative;
    flex: 1 1 auto;
    min-height: 0;
    border-radius: 6px;
    overflow: hidden;
    background: var(--background-clr);
}

.footage-panel__item:hover .footage-panel__thumb-wrap {
    outline: 2px solid var(--primary-clr);
    outline-offset: -2px;
}

.footage-panel__thumb {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.footage-panel__duration {
    position: absolute;
    bottom: 4px;
    right: 4px;
    font-size: 10px;
    line-height: 1;
    color: #fff;
    background: var(--c-scrim-medium);
    padding: 2px 4px;
    border-radius: 4px;
}

.footage-panel__date {
    flex: none;
    font-size: 11px;
    color: var(--muted-clr);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Üres állapot: középre igazított üzenet a panel közepén. */
.footage-panel__empty {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    color: var(--muted-clr);
    font-size: 13px;
    text-align: center;
}

/* A globális `span { color: text-clr }` (app.css) a szöveg-spanre KÖZVETLENÜL illeszkedik → felülírná a
   szülő öröklött muted színét. Ezért a spanre EXPLICIT muted (view módban ez a "nincs footage" üzenet). */
.footage-panel__empty span {
    color: var(--muted-clr);
}

/* Dőlt CSAK megjelenítéskor (view); a pickerben/edit módban NEM (a font-style öröklődik a spanre). */
.footage-panel__empty--view {
    font-style: italic;
}

/* lists.css */
/* List header */
.split-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
}

    .split-header > h3,
    .simple-header > h3 {
        margin: 0;
        font-size: 1.25rem;
        font-weight: 600;
        color: var(--muted-clr);
        letter-spacing: 0.2px;
    }


/* BASE LIST */
.base-list-component {
    display: grid;
    gap: 0.75rem;
}

.base-list {
    display: flex;
    flex-direction: column;
    gap: 0;
    background-color: var(--list-bg);
    border: var(--border-thin);
    border-radius: var(--list-border-radius);
    box-shadow: var(--elev-rest);
    overflow: hidden;
}

/* Sorban lévő legördülő (fluent-select) esetén a slab NEM vághat. A Fluent select popupja a
   komponens shadow DOM-jában, absolute pozícionálva nyílik (nincs portál / body-re emelés), tehát
   a fenti `overflow: hidden` LEVÁGJA: ha a lista aljához közel nyílik, a popup FELFELÉ fordul, és
   a fejléc-sáv magasságában egyszerűen eltűnik (source-bindings: az első opció félbevágva látszott).

   A clip helyett a szélső sorokon kerekítünk, így a slab kinézete változatlan. A szegély a
   .base-list-en van, a gyerekek 1px-rel beljebb ülnek — 14px sugárnál ez nem látszik. */
.base-list--dropdowns {
    overflow: visible;
}

.base-list--dropdowns > .base-list__header {
    border-top-left-radius: var(--list-border-radius);
    border-top-right-radius: var(--list-border-radius);
}

/* Fejléc nélküli lista (HeaderVisible=false): ilyenkor a tartalom az első gyerek, tehát a felső
   sarkokat az első sor adja. */
.base-list--dropdowns > .base-list__content:first-child > *:first-child {
    border-top-left-radius: var(--list-border-radius);
    border-top-right-radius: var(--list-border-radius);
}

.base-list--dropdowns > .base-list__content > *:last-child {
    border-bottom-left-radius: var(--list-border-radius);
    border-bottom-right-radius: var(--list-border-radius);
}

.base-list__header, .base-list__row {
    display: grid;
    align-items: center;
    padding-block: 0.75rem;
    padding-inline: 1rem;
    background-color: var(--list-bg);
    transition: background-color .15s ease, border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
/* #region Header */
.base-list__header {
    font-size: clamp(0.6rem, calc(0.6rem + 1dvw), 1rem);
    border-bottom: var(--border-thin);
    background-color: var(--c-surface-elevated);
}

/* Night módban a fejléc a hover-felület (az elevated túl közel van a sor-háttérhez night-ban). */
:root[data-theme="night"] .base-list__header {
    background-color: var(--c-surface-hover);
}
/* #endregion */

/* #region Content */
.base-list__content {
    display: grid;
    gap: 0;
}

.base-list__content > * + * {
    border-top: var(--border-thin);
}

.base-list__row {
    font-size: clamp(0.75rem, calc(0.75rem + 1dvw), 1rem);
}

    .base-list__row:not(.empty-row):hover {
        background-color: color-mix(in srgb, var(--primary-clr) 8%, var(--list-bg));
    }

    /* Letiltott sor (IsDisabled): a tartalom halvány + szürke, de a .center cella (enable-toggle)
       és a .base-list__actions (Edit/Delete) élesek maradnak → a sor kezelhető. A dashboard
       .stale-card mintáját követi. */
    .base-list__row.is-disabled .base-list_cell:not(.center) {
        opacity: 0.5;
        filter: grayscale(0.7);
    }

    .base-list__row.empty-row {
        text-align: center;
        color: var(--muted-clr);
        font-style: italic;
        padding-block: 1.25rem;
    }

        /* A globális `span { color: var(--text-clr) }` (app.css) különben felülírná az öröklött
           muted színt — ezért a span-t közvetlenül célozzuk. */
        .base-list__row.empty-row span {
            color: var(--muted-clr);
        }

.base-list__details {
    display: none;
    padding: 0.75rem 1rem;
    background-color: var(--background-clr);
    grid-column: 1 / -1;
}

    .base-list__details .card {
        overflow: hidden;
    }

.base-list_cell.center{
    justify-self: center;
}

.base-list__cell-header {
    display: none;
}

.base-list--conditions .base-list__cell-content {
    display: grid;
    gap: 0.5rem;
}

.base-list--conditions .name-cell {
    grid-column: 1 / -1;
    margin-bottom: 0.75rem;
}

/* A span-t is célozzuk: a globális `span { color: var(--text-clr) }` (app.css) különben
   felülírná az öröklött muted színt a név-cellában. */
.base-list--conditions .name-cell .base-list__cell-content,
.base-list--conditions .name-cell .base-list__cell-content span {
    font-style: italic;
    color: var(--muted-clr);
}

/* Units lista: aránytartó oszlopok — Név / Egység / Tizedesjegyek / Műveletek. A generikus
   BaseListComponent inline repeat(N,1fr)-t adna (nem felülírható), ezért a UnitsComponent kézzel
   írt .base-list--units markupot használ, és az oszlopokat itt adjuk (ld. AlarmsComponent minta).
   Mobilon a globális @media reflow (1fr 1fr / 1fr) marad érvényben. */
.base-list--units .base-list__header,
.base-list--units .base-list__row:not(.empty-row) {
    grid-template-columns: 2fr 1fr 2fr 1fr;
}

/* #endregion */

/* #region Actions */
.base-list__actions {
    display: flex;
    gap: 0.5rem;
    margin-left: auto;
}

.base-list .expand-toggle {
    width: 20px;
    height: auto;
    transition: transform 0.2s ease;
    cursor: pointer;
}

    .base-list .expand-toggle:focus-visible {
        outline: 2px solid var(--primary-clr);
        outline-offset: 2px;
        border-radius: inherit;
    }
/* #endregion */

.base-list-skeleton__header,
.base-list-skeleton__body {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding-block: 0.75rem;
    padding-inline: 1rem;
    background: var(--list-bg);
}

.base-list-skeleton__header {
    border-top-left-radius: var(--list-border-radius);
    border-top-right-radius: var(--list-border-radius);
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
}

.base-list-skeleton__body {
    border-radius: 0;
}

.base-list-skeleton__body:last-of-type {
    border-bottom-left-radius: var(--list-border-radius);
    border-bottom-right-radius: var(--list-border-radius);
}


@media (max-width: 600px) {
    /* Mobilon a slab-keretet feloldjuk, és a sorok külön dashboard-csempeként jelennek meg
       (keret + árnyék), a meglévő 2-oszlopos reflow-val. */
    .base-list {
        border: none;
        box-shadow: none;
        overflow: visible;
        border-radius: 0;
        background: transparent;
        gap: 0.5rem;
    }

    .base-list__content {
        gap: 0.5rem;
    }

    .base-list__content > * + * {
        border-top: none;
    }

    .base-list__row:not(.empty-row),
    .base-list__row.empty-row {
        border: var(--border-thin);
        box-shadow: var(--elev-rest);
    }

    .base-list__header {
        display: none;
    }

    .base-list__row:not(.empty-row) {
        grid-template-columns: 1fr 1fr !important;
        row-gap: 0.75rem;
        align-items: start;
        border-radius: var(--list-border-radius);
    }

    /* CSAK az alarm/automation (feltétel-listás) kártyáknál kerül minden cella külön sorba
       (egy oszlop); a többi lista marad 2-oszlopos. */
    .base-list--conditions .base-list__row:not(.empty-row) {
        grid-template-columns: 1fr !important;
    }

    .base-list__row.empty-row {
        border-radius: var(--list-border-radius);
    }

    .base-list_cell.center {
        justify-self: start;
    }

    .base-list__cell-header {
        display: block;
        font-weight: bold;
        font-size: 0.85rem;
        color: var(--muted-clr);
    }

    .base-list__actions {
        grid-column: 1 / -1;
    }

    .base-list--conditions .name-cell {
        margin-bottom: 0;
    }

/* Dark-téma felületi felülírások (korábban app.css) — az emelt részlet-sor és a skeleton
   háttere a sötét felület-tokenekhez igazodik. */
:root[data-theme="dark"] .base-list__details {
    background-color: var(--c-surface-elevated);
}

:root[data-theme="dark"] .base-list-skeleton__header,
:root[data-theme="dark"] .base-list-skeleton__body {
    background: var(--list-bg);
}

    .base-list--conditions .name-cell .base-list__cell-content,
    .base-list--conditions .name-cell .base-list__cell-content span {
        font-style: inherit;
        color: inherit;
    }

    .base-list--conditions .base-list__cell-content {
        gap: 0.25rem;
    }

    .base-list--conditions .conditions-cell {
        grid-column: 1 / -1;
    }
}

@media (max-width: 400px) {
    .base-list__row:not(.empty-row) {
        grid-template-columns: 1fr !important;
    }
}

/* entity-cards.css */
/* ── Entity list cards ──────────────────────────────────────────────────────
   KÖZÖS csempe-elrendezés a beállítások-oldali listákhoz:
     - OwnersComponent (tulajdonosok)
     - VesselsComponent (hajók)
   A két lista MINDIG egységesen változik: az elrendezést itt egy helyen definiáljuk
   (.entity-cards konténer + .entity-card csempe), így ha pl. flex helyett grid kell,
   elég itt átírni és mindkét lista követi.
   Minden szabály .entity-card(s) alá van szűkítve, hogy a generikus belső osztály-
   nevek (.card-header/.card-content/.label/.actions) ne ütközzenek a dashboard
   kártyáival (.card / .cards-section, lásd cards.css). */

.entity-cards {
    display: flex;
    flex-wrap: wrap;
    align-items: start;
    gap: 1rem;
}

.entity-card {
    width: 17rem;
    background: var(--c-surface);
    border: var(--border-thin);
    border-radius: 1.25rem;
    overflow: hidden;
    transition: .2s;
}

    .entity-card:hover {
        box-shadow: 0 10px 25px rgba(0, 0, 0, .08);
        transform: translateY(-2px);
    }

    /* Betöltő skeleton-kártya: nincs hover-kiemelés (a pointer-events:none miatt a :hover sem aktiválódik). */
    .entity-card--skeleton {
        pointer-events: none;
    }

.entity-card .card-header {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.5rem 1rem;
    background: var(--c-surface-elevated);
}

    /* Cím egy sorban; hosszú név esetén ellipsis. A min-width:0 kell, hogy a flex-item
       a tartalma mérete alá zsugorodhasson (alapból min-width:auto). */
    .entity-card .card-header h3 {
        margin: 0;
        min-width: 0;
        font-size: 1rem;
        font-weight: 600;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

.entity-card .card-content {
    display: grid;
    gap: 0.5rem;
    padding: 0.625rem 0.75rem;
}

/* Sor: ikon/státusz-pötty + szöveg egy sorban, a szöveg ellipsis-szel */
.entity-card .label {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    min-width: 0;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: .05em;
}

    /* Az ikon/pötty (első elem) sosem zsugorodik — mindig a szöveg rövidül. */
    .entity-card .label > :first-child {
        flex-shrink: 0;
    }

    .entity-card .label > span:last-child {
        min-width: 0;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

.entity-card .actions {
    display: flex;
    justify-content: end;
    gap: 0.25rem;
    padding: 0.25rem 0.5rem;
}

/* ── Kép (csak a hajó-listában van) ────────────────────────────────────────── */
.entity-card .card-image-wrap {
    width: 100%;
    height: 7rem;
    overflow: hidden;
}

.entity-card .card-image {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.entity-card .card-image--placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--c-surface-elevated);
    color: var(--c-text-muted);
}

/* ── Online/offline státusz-pötty ──────────────────────────────────────────── */
.entity-card .status-dot {
    display: inline-block;
    width: 0.625rem;
    height: 0.625rem;
    border-radius: 50%;
    flex-shrink: 0;
}

    .entity-card .status-dot--online {
        background: var(--c-success);
    }

    .entity-card .status-dot--offline {
        background: var(--c-text-muted);
    }

/* switches.css */
/* Digital switches kártya-rács — a Nautech.App.Remote (MAUI) SwitchesView vizuális nyelvét
   követi: 2+ oszlopos reszponzív kártyarács a korábbi tábla-szerű lista helyett. GLOBÁLIS
   (index.html-ben regisztrálva) — a "dsw-" prefix elkerüli az ütközést a FavSwitchesComponent
   scoped .switch-card osztályával. Minden szín token-alapú (light/dark/night automatikus). */

.dsw-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
    gap: 0.75rem;
    margin-bottom: 1.5rem;
}

/* Mobilon (keskeny kijelző) a 210px-es min csak 1 oszlopot engedne — itt fix 2 oszlop, hogy
   2 kapcsoló elférjen egymás mellett. */
@media (max-width: 560px) {
    .dsw-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

.dsw-card {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    min-height: 120px; /* MAUI HeightRequest 120 */
    padding: 12px;
    background: var(--list-bg);
    border: 2px solid var(--c-divider); /* MAUI StrokeThickness 2 */
    border-radius: var(--list-border-radius);
    box-shadow: 0 2px 8px var(--c-shadow-xs);
    transition: background-color .15s ease, border-color .15s ease, box-shadow .15s ease;
}

:root[data-theme="dark"] .dsw-card,
:root[data-theme="night"] .dsw-card {
    box-shadow: 0 2px 8px var(--c-shadow-sm);
}

/* Állapot kizárólag a status-dot + a border finom árnyalata jelzi (mint a MAUI-ban); a kártya
   háttere NEM vált állapot szerint, hogy ne ütközzön a hover-tinttel. */
.dsw-card.is-on {
    border-color: color-mix(in srgb, var(--c-success) 45%, var(--c-divider));
}

/* §11.11 unavailable: halott Modbus switch — a kártya elhalványul, NEM on/off-ként jelzünk. */
.dsw-card.is-unavailable {
    border-color: color-mix(in srgb, var(--muted-clr) 40%, var(--subtle-divider-clr));
    opacity: 0.6;
}

.dsw-card:hover {
    background: color-mix(in srgb, var(--primary-clr) 6%, var(--list-bg));
}

.dsw-card.alarm {
    border-color: var(--error-clr);
    animation: dswAlarmPulse 1.4s ease-in-out infinite;
}

/* Error állapotú NMEA switch kártya: piros, pulzáló keret (kiemelt jelzés). */
.dsw-card.is-error {
    border-color: var(--error-clr);
    animation: dswAlarmPulse 1.4s ease-in-out infinite;
}

/* Unknown állapotú kártya: visszafogott, szaggatott keret (nincs értelmezhető adat). */
.dsw-card.is-unknown {
    border-style: dashed;
}

.dsw-card__top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.5rem;
}

.dsw-card__name {
    font-size: 15px;
    font-weight: 600;
    color: var(--text-clr);
    line-height: 1.25;
    display: flex;
    align-items: center;
    gap: 0.4rem;
    word-break: break-word;
}

.dsw-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    flex: none;
    margin-top: 4px;
}

/* Kontakt-bemenet állapot-ikon (csengő/hold) a kártya jobb felső sarkában (a pötty helyén). */
.dsw-card__state-icon {
    flex: none;
    display: inline-flex;
    line-height: 1;
}

.dsw-dot.on {
    background: var(--c-success);
}

    .dsw-dot.off {
        background: var(--extra-muted-clr);
    }

    .dsw-dot.error {
        background: var(--error-clr);
    }

    /* §11.11 unavailable: dashed szürke, mint az unknown — nincs friss adat a Modbus switchről. */
    .dsw-dot.unavailable {
        background: transparent;
        border: 2px dashed var(--muted-clr);
        box-sizing: border-box;
    }

    .dsw-dot.unknown {
        background: transparent;
        border: 2px dashed var(--muted-clr);
        box-sizing: border-box;
    }

/* NMEA switch state-oszlop badge-ek a 4. állapotokhoz (Error / Unknown). */
.dsw-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.72rem;
    font-weight: 600;
    padding: 2px 8px;
    border-radius: 999px;
    line-height: 1.4;
    white-space: nowrap;
}

.dsw-badge--error {
    color: var(--error-clr);
    background: color-mix(in srgb, var(--error-clr) 14%, transparent);
}

.dsw-badge--unknown {
    color: var(--muted-clr);
    background: color-mix(in srgb, var(--muted-clr) 12%, transparent);
}

/* Kis pulzáló piros jelző az error badge-ben. */
.error-stripe {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--error-clr);
    flex: none;
    animation: dswErrorPulse 1.4s ease-in-out infinite;
}

@keyframes dswErrorPulse {
    0%, 100% {
        opacity: 1;
    }

    50% {
        opacity: 0.35;
    }
}

.dsw-card__meta {
    display: flex;
    flex-direction: column;
    gap: 2px;
    font-size: 0.72rem;
    color: var(--muted-clr);
    line-height: 1.3;
}

.dsw-card__control {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    min-height: 40px;
}

/* §11.11 unavailable felirat a toggle helyén (szürke, áthúzott) — „no data". */
.dsw-unavailable {
    font-size: 0.72rem;
    font-style: italic;
    color: var(--muted-clr);
    text-decoration: line-through;
    text-align: center;
}

/* Extend analog input: the live value in its own unit, where the toggle would be. */
.dsw-value {
    font-size: 1.35rem;
    font-weight: 600;
    color: var(--text-clr);
    font-variant-numeric: tabular-nums;
    text-align: center;
}

.dsw-card__actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.25rem;
    border-top: var(--border-thin);
    padding-top: 0.5rem;
}

.dsw-group-title {
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--muted-clr);
    margin: 0 0 0.5rem 0;
}

.dsw-empty {
    color: var(--muted-clr);
    font-style: italic;
    text-align: center;
    padding: 1rem;
}

/* Betöltési hiba (offline / elérhetetlen régió) — a semleges "nincs kapcsoló"-tól megkülönböztetve. */
.dsw-error {
    color: var(--error-clr);
}

@keyframes dswAlarmPulse {
    0%, 100% {
        border-color: color-mix(in srgb, var(--error-clr) 25%, transparent);
    }

    50% {
        border-color: var(--error-clr);
    }
}

@media (prefers-reduced-motion: reduce) {
    .dsw-card.alarm,
    .dsw-card.is-error,
    .error-stripe {
        animation: none;
    }
}

/* modbus.css */
/* ──────────────────────────────────────────────────────────────────────────
   Modbus editor + vessel_settings list + health badge
   (modbus-integration-plan.md §5.1 / §5.2). Uses the shared design tokens.
   ────────────────────────────────────────────────────────────────────────── */

/* The register table needs far more than the 40rem edit-card default. */
.modbus-edit-page .edit-page-card {
    max-width: 72rem;
}

.modbus-conn-row {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: flex-end;
}

.modbus-slave-card {
    border: 1px solid var(--subtle-divider-clr);
    border-radius: var(--list-border-radius, 8px);
    padding: 1rem;
    display: grid;
    gap: 0.75rem;
    background: var(--list-bg);
}

.modbus-slave-header {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    align-items: flex-end;
}

.modbus-empty {
    color: var(--muted-clr);
    font-style: italic;
}

/* Register editor: kártyánként EGY register. A mezők flex-wrappel törnek több sorba, így a szekció
   SOHA nem görget oldalra (a régi 13-oszlopos, min-width:72rem rács helyett). A per-oszlop súgó-„?"
   mostantól minden mező saját mini-feliratában él. */
.modbus-register-cards {
    display: grid;
    gap: 0.6rem;
}

.modbus-register-card {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 0.75rem;
    align-items: flex-end;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--subtle-divider-clr);
    border-radius: 6px;
    background: var(--list-bg);
}

.modbus-register-fields {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 0.75rem;
    align-items: flex-end;
    flex: 1 1 auto;
    min-width: 0;
}

.mrf-cell {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    flex: 1 1 7rem;
    min-width: 0;
}

.mrf-cell--wide   { flex: 2 1 11rem; }
.mrf-cell--num    { flex: 0 1 5.5rem; }
/* max-width: a switch-cella egy beágyazott konténer (SwitchKind + Is reversed + Bit) → önálló sorra
   törve ne nyúljon a teljes kártyaszélességre. */
.mrf-cell--switch { flex: 1 1 10rem; max-width: 20rem; }

/* Checkbox-cellák: a checkbox hozza a saját feliratát → csak az input-alapvonalra igazítjuk. */
.mrf-cell--check {
    flex: 0 0 auto;
    flex-direction: row;
    align-items: center;
    align-self: flex-end;
    padding-bottom: 0.35rem;
}

.modbus-register-fields .fluent-input-label {
    display: inline-flex;
    align-items: center;
}

/* A Fluent mezők töltsék ki a cellájukat. A fluent-select FluentUI-default min-width:250px-e
   túlnyúlt a keskeny cellán és rátakart a szomszédra → min-width:0 !important (a text/number-field
   min-width:auto, azok eleve fértek). Ugyanez a fogás a dialog.css-ben is. */
.mrf-cell fluent-text-field,
.mrf-cell fluent-number-field,
.mrf-cell fluent-select {
    width: 100%;
    min-width: 0 !important;
}

.modbus-register-actions {
    display: flex;
    gap: 0.25rem;
    align-items: flex-end;
    align-self: flex-end;
    padding-bottom: 0.35rem;
}

.modbus-register-card .modbus-register-error,
.modbus-register-card .modbus-register-test {
    flex-basis: 100%;
}

/* ── health badge (§3.7) — a Modbus busz-lista most a közös BaseListComponent-et
   használja (a többi vessel_settings-szekcióval egységesen); a régi .modbus-bus-*
   flex-lista törölve. A health-badge a lista Health-oszlopának cellájában él. ── */
.modbus-health-badge {
    font-size: 0.75rem;
    font-weight: 600;
    padding: 0.15rem 0.5rem;
    border-radius: 999px;
    white-space: nowrap;
}

.modbus-health-ok         { color: #0a7a2f; background: rgba(10, 122, 47, 0.12); }
.modbus-health-connecting { color: #8a6d00; background: rgba(138, 109, 0, 0.12); }
/* Kapcsolódik, de van per-register olvasási hiba (pl. IllegalDataAddress egy címen) → borostyán figyelmeztetés. */
.modbus-health-warn       { color: #9a5b00; background: rgba(214, 128, 0, 0.16); }
.modbus-health-fail       { color: #b3261e; background: rgba(179, 38, 30, 0.12); }
.modbus-health-unknown    { color: var(--muted-clr); background: rgba(0, 0, 0, 0.06); }

/* Per-register ÉLŐ olvasási hiba a busz-szerkesztőben (health-ből, a register-kártya alján) —
   megkülönböztetve a validációs .modbus-register-error-tól. */
.modbus-register-liveerror {
    flex: 1 1 100%;
    display: flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.8rem;
    font-weight: 600;
    color: #b3261e;
    background: rgba(179, 38, 30, 0.10);
    border-radius: 6px;
    padding: 0.2rem 0.5rem;
    margin-top: 0.15rem;
}
.modbus-liveerror-time {
    font-weight: 400;
    opacity: 0.7;
    font-size: 0.72rem;
}

/* ── Modbus-sourced appliance composer (modbus-appliance-datasource-plan.md §8) ── */
.modbus-appliance-panel {
    display: grid;
    gap: 0.75rem;
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid var(--subtle-divider-clr);
}

.modbus-appliance-list {
    display: grid;
    gap: 0.5rem;
}

.modbus-appliance-item {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--subtle-divider-clr);
    border-radius: 6px;
    background: var(--list-bg);
}

.modbus-appliance-info {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-width: 0;
}

.modbus-appliance-name {
    font-weight: 600;
}

.modbus-appliance-meta {
    font-size: 0.8rem;
    color: var(--muted-clr);
}

.modbus-appliance-actions {
    display: flex;
    gap: 0.25rem;
}

.modbus-appliance-form {
    display: grid;
    gap: 0.75rem;
    padding: 1rem;
    border: 1px solid var(--subtle-divider-clr);
    border-radius: var(--list-border-radius, 8px);
    background: var(--list-bg);
}

/* Field-map: role → register párok kompakt reszponzív rácsban. */
.modbus-appliance-map {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
    gap: 0.75rem;
}

.modbus-appliance-map-row {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.modbus-appliance-form-footer {
    display: flex;
    gap: 0.5rem;
}

/* ── HelpTip: kattintható „?" súgó-jelvény (Layout/Elements/HelpTip.razor) ──
   Jelenleg csak a Modbus-editor használja; ha máshol is kell, emelhető közös css-be. */
.help-tip {
    position: relative;
    display: inline-flex;
    vertical-align: middle;
    margin-left: 0.3rem;
}

.help-tip__btn {
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 1.05rem;
    height: 1.05rem;
    padding: 0;
    font-family: inherit;
    font-size: 0.72rem;
    font-weight: 700;
    line-height: 1;
    color: var(--muted-clr);
    background: transparent;
    border: 1px solid var(--muted-clr);
    border-radius: 50%;
    transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}

.help-tip__btn:hover,
.help-tip__btn--open {
    color: var(--primary-clr);
    border-color: var(--primary-clr);
    /* Téma-agnosztikus szürke tint (a --subtle-fill-clr token nem létezik; a fekete fallback
       sötét/éjszakai témán láthatatlan volt). */
    background: rgba(128, 128, 128, 0.18);
}

/* Nyitott állapotban a „?" a backdrop (z-index:4000) FÖLÉ kerül, hogy a gombra kattintás maga is
   zárja a buborékot (különben a backdrop nyelné el a kattintást). */
.help-tip__btn--open {
    position: relative;
    z-index: 4002;
}

/* Átlátszó teljes-képernyős fogó a click-outside záráshoz. */
.help-tip__backdrop {
    position: fixed;
    inset: 0;
    z-index: 4000;
    background: transparent;
}

/* A buborék position:fixed (a kurzornál) → kívül esik a scroll-konténerek overflow-clipjén. */
.help-tip__pop {
    position: fixed;
    z-index: 4001;
    width: max-content;
    max-width: 22rem;
    /* A tényleges max-height-et a HelpTip.razor inline állítja (a clampelt top-tól a viewport aljáig),
       hogy hosszú szöveg belül görögjön és a doboz alja soha ne lógjon le. Itt csak az overflow kell. */
    overflow-y: auto;
    padding: 0.6rem 0.75rem;
    font-size: 0.8rem;
    font-weight: 400;
    line-height: 1.45;
    text-align: left;
    white-space: normal;
    /* ⚠️ A buborék `position: fixed`, de a DOM-ban a HORGONY gyereke marad — tehát MINDENT örököl,
       amit az anyaelem tipográfiája ráír. Egy nagybetűsített, ritkított szakaszcím mellé téve (HMI
       inspektor) a magyarázat is csupa nagybetűvel jelent volna meg. Ezért a szedést itt lezárjuk:
       a súgó-szöveg mindig sima, olvasható mondat, akárhova is tesszük a „?"-et. */
    text-transform: none;
    letter-spacing: normal;
    font-style: normal;
    color: var(--text-clr);
    background: var(--list-bg, #ffffff);
    border: 1px solid var(--subtle-divider-clr);
    border-radius: 6px;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.22);
}

/* Label + „?" egy sorban, alatta az input (a Fluent Label helyett, hogy a súgó a felirat mellé kerüljön). */
.field-with-help {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.field-with-help > .fluent-input-label {
    display: inline-flex;
    align-items: center;
}

.extend-settings {
    margin-bottom: 1.5rem;
}

.extend-relay-table {
    width: 100%;
    border-collapse: collapse;
}

.extend-relay-table th,
.extend-relay-table td {
    padding: 0.35rem 0.5rem;
    text-align: left;
    border-bottom: var(--border-thin);
}

.extend-relay-missing td {
    color: var(--muted-clr);
}

/* "Missing": the channel is still configured, only the device no longer reports it, so the label is not struck through. */
.extend-missing-label {
    font-size: 0.72rem;
    font-style: italic;
    color: var(--muted-clr);
}

.extend-relay-table .extend-missing-label {
    margin-left: 0.4rem;
}

.extend-live-unavailable {
    font-style: italic;
    color: var(--muted-clr);
}

/* The analog input cards need more than the 40rem edit-card default, like the Modbus register cards. */
.extend-edit-page .edit-page-card {
    max-width: 72rem;
}

/* .nautech-page is a grid: without min-width 0 a wide table stretches the page instead of scrolling. */
.extend-edit-page .edit-page-card,
.extend-edit-page .edit-page-form {
    min-width: 0;
}

.extend-table-scroll {
    max-width: 100%;
    overflow-x: auto;
}

.extend-section-title {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    margin: 0.5rem 0 0;
    font-size: 1rem;
    font-weight: 600;
    color: var(--text-clr);
}

.extend-input-number {
    font-weight: 600;
}

.extend-input-module {
    display: block;
    font-size: 0.72rem;
    color: var(--muted-clr);
}

.extend-input-live {
    white-space: nowrap;
    color: var(--muted-clr);
}

/* Narrower than the register card's cells, so a card stays on one row at the usual content width. */
.extend-analog-card .modbus-register-fields {
    column-gap: 0.5rem;
}

.extend-analog-number {
    flex: 0 0 4.5rem;
}

.extend-analog-card .mrf-cell--wide {
    flex-basis: 9rem;
}

.extend-analog-card .extend-mode-cell {
    flex: 1 0 8.5rem;
}

/* On a phone the table scrolls in its own frame instead of squeezing the name field to nothing. */
.extend-input-table fluent-text-field {
    min-width: 9rem;
}

.extend-analog-card .fluent-input-label {
    white-space: nowrap;
}

/* On a phone the digital input rows become cards like the analog inputs: number and name on top, then the two
   checkboxes and the state, each under its column header (data-label). Last in the file: it overrides the
   desktop rules above (the name field's min-width). */
@media (max-width: 600px) {
    .extend-input-table thead {
        display: none;
    }

    .extend-input-table,
    .extend-input-table tbody {
        display: block;
    }

    .extend-input-table tr {
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: 0.5rem 0.75rem;
        align-items: start;
        padding: 0.6rem 0.75rem;
        margin-bottom: 0.5rem;
        border: 1px solid var(--subtle-divider-clr);
        border-radius: 6px;
        background: var(--list-bg);
    }

    .extend-input-table td {
        display: block;
        padding: 0;
        border-bottom: 0;
    }

    .extend-input-table td:first-child {
        align-self: center;
    }

    .extend-input-table td:nth-child(2) {
        grid-column: 2 / -1;
    }

    /* "No data" in French does not fit a third of a phone's width on one line. */
    .extend-input-table td.extend-input-live {
        white-space: normal;
    }

    .extend-input-table td[data-label]::before {
        content: attr(data-label);
        display: block;
        margin-bottom: 0.2rem;
        font-size: var(--type-ramp-base-font-size, 14px);
        font-weight: 600;
        color: var(--text-clr);
    }

    .extend-input-table fluent-text-field {
        min-width: 0;
        width: 100%;
    }
}

/* nmea-discovery.css */
/* ── NMEA discovery liveness badge (nmea-discovery-liveness-plan.md) ──────────
   A modbus.css .modbus-health-badge pill-jének alakja, de TOKEN-színekkel:
   azok a badge-ek hardcode-olt hexet használnak, ezért nem követik a dark/night
   témát. Itt a Tier-2/Tier-3 tokenekre hivatkozunk (--c-success, --error-clr,
   --muted-clr), így a téma-váltás ingyen jön.
   Megjegyzés: --success-clr Tier-3 alias NINCS, a --c-success a helyes forrás.  */
.nmea-live-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.72rem;
    font-weight: 600;
    padding: 0.15rem 0.5rem;
    border-radius: 999px;
    white-space: nowrap;
}

/* A pont a badge színét veszi fel. A `color: inherit` KELL: az app.css globális
   `span, label, div { color: var(--text-clr) }` szabálya (app.css:97) különben a pontra is
   ráhúzza a szöveg-színt, és akkor a `currentColor` nem a zöld/piros badge-színt adja, hanem a
   sima szövegszínt (night témában épp vörösest) — azaz a státusz-pont mindhárom állapotban
   egyforma lenne. Az osztály-szelektor (0,1,0) veri a `span` elem-szelektort (0,0,1). */
.nmea-live-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    flex: none;
    color: inherit;
    background: currentColor;
}

.nmea-live-online {
    color: var(--c-success);
    background: color-mix(in srgb, var(--c-success) 14%, transparent);
}

.nmea-live-offline {
    color: var(--error-clr);
    background: color-mix(in srgb, var(--error-clr) 14%, transparent);
}

.nmea-live-unknown {
    color: var(--muted-clr);
    background: color-mix(in srgb, var(--muted-clr) 12%, transparent);
}

/* Elavult snapshot: az állapot lehet, hogy már nem aktuális → halványítjuk. */
.nmea-live-faded {
    opacity: 0.55;
}

/* Státusz-pötty a dropdown-opciókban (edit-oldalak NMEA-instance selectje, OptionTemplate). A badge
   .nmea-live-dot-ját tükrözi, de pill-HÁTTÉR NÉLKÜL: itt a szöveg maga az opció-címke, csak a színes pont
   kell elé. Dedikált modifier-osztályok (nem a badge .nmea-live-online-ja), hogy ne ütközzön annak
   háttér-tint szabályával. A `background` a színforrás — az app.css `span{color}` globálja NEM érinti. */
.nmea-opt-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex: none;
    margin-right: 0.45rem;
    vertical-align: baseline;
}

.nmea-opt-dot--online  { background: var(--c-success); }
.nmea-opt-dot--offline { background: var(--error-clr); }
.nmea-opt-dot--unknown { background: var(--muted-clr); }

/* A szekció fejlécének jobb oldala: a snapshot-kor / állapotsor. Rescan gomb és spinner NINCS —
   a frissítés automatikus, amíg a szekció nyitva van (NmeaDiscoveryService.BeginAutoScan). */
.nmea-discovery-header-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
}

/* A snapshot-kor felirata: másodlagos információ, ne vonja el a figyelmet a listáról. */
.nmea-snapshot-age {
    font-size: 0.75rem;
    color: var(--muted-clr);
    white-space: nowrap;
}

.nmea-snapshot-age--stale {
    color: var(--warning-clr);
    font-weight: 600;
}

/* Az instance-azonosító monospace: a technikus a fizikai eszközzel veti össze. */
.nmea-instance-id {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.82rem;
}

.nmea-delete-hint {
    font-size: 0.75rem;
    color: var(--muted-clr);
}

/* fleet-weather.css */
/* =============================================================================
   fleet-weather.css — a flotta-időjárás térkép marker/popup stílusai (.fw-*).
   A markereket a leafletfleetweathermap.js JS injektálja (nem Razor-markup), ezért
   GLOBÁLIS (scoped .razor.css nem érné el). Korábban az app.css-ben élt szétszórva.
   ============================================================================= */

/* ===== Fleet Weather map marker ===== */
.fw-marker-icon {
    background: transparent !important;
    border: none !important;
    overflow: visible !important;
}

.fw-marker {
    position: relative;
    width: 40px;
    height: 40px;
    overflow: visible;
    pointer-events: auto;
}

.fw-vessel-icon {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    object-fit: cover;
    box-shadow: 0 0 0 2px var(--nt-white), 0 1px 4px var(--c-shadow-lg);
    background: #ddd;
    display: block;
}

.fw-vessel-icon-fallback {
    background: #999;
}

.fw-name {
    position: absolute;
    top: 42px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 12px;
    font-weight: 600;
    background: var(--c-overlay-light);
    padding: 1px 6px;
    border-radius: 8px;
    white-space: nowrap;
    box-shadow: 0 1px 2px var(--c-shadow-md);
    pointer-events: none;
}

.fw-chip {
    position: absolute;
    top: 62px;
    left: 50%;
    transform: translateX(-50%);
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background: var(--c-overlay-light);
    font-size: 12px;
    font-weight: 500;
    padding: 2px 8px;
    border-radius: 10px;
    box-shadow: 0 1px 3px var(--c-shadow-md);
    white-space: nowrap;
    pointer-events: none;
}

.fw-chip-item {
    display: inline-flex;
    align-items: center;
    gap: 2px;
}

    .fw-chip-item small {
        font-size: 9px;
        opacity: 0.7;
        margin-left: 1px;
    }

.fw-sep {
    color: var(--extra-muted-clr); /* faint (slate-300 light / slate-700 dark) */
}

.fw-arrow {
    display: inline-block;
    margin-left: 2px;
    font-size: 13px;
    line-height: 1;
    transform-origin: center;
}

/* ===== Fleet Weather popup ===== */
.leaflet-popup.fw-popup .leaflet-popup-content {
    margin: 10px 14px;
    min-width: 220px;
}

.fw-popup-body {
    font-size: 12px;
}

.fw-popup-title {
    font-size: 14px;
    font-weight: 700;
    margin-bottom: 6px;
    color: var(--primary-clr);
}

.fw-popup-row {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    padding: 2px 0;
    border-bottom: var(--border-thin);
}

    .fw-popup-row:last-of-type {
        border-bottom: none;
    }

.fw-popup-label {
    color: var(--muted-clr);
}

.fw-popup-value {
    font-weight: 600;
}

.fw-popup-updated {
    margin-top: 6px;
    font-size: 10px;
    color: var(--stale-clr); /* „frissítve" időbélyeg = stale-szerep */
    text-align: right;
}

/* ===== Téma-felülírások (dark / night) ===== */
:root[data-theme="dark"] .fw-name,
:root[data-theme="dark"] .fw-chip {
    background: rgba(22, 36, 59, 0.95);
    color: var(--text-clr);
}

/* Night mirror — különben a fleet-térkép marker-feliratok fehér háttérrel + sötét szöveggel
   maradnának a piros night-térkép fölött (a base rule white/#1d2939). */
:root[data-theme="night"] .fw-name,
:root[data-theme="night"] .fw-chip {
    background: rgba(26, 10, 10, 0.95);
    color: var(--text-clr);
}

:root[data-theme="dark"] .fw-popup-body,
:root[data-theme="dark"] .fw-popup-title {
    color: var(--text-clr);
}

:root[data-theme="dark"] .fw-popup-label {
    color: var(--muted-clr);
}

:root[data-theme="dark"] .fw-popup-row {
    border-bottom-color: var(--c-divider);
}

:root[data-theme="dark"] .fw-sep {
    color: #2d3f5a;
}

/* Fleet weather popup overrides for dark mode (defaults use light colors) */
html[data-theme="dark"] .fw-popup-body,
html[data-theme="dark"] .fw-popup-title {
    color: #e5ebf5;
}

/* .fw-popup-label / .fw-popup-updated / .fw-sep: a szín-token (muted/stale/faint) már témázott,
   külön dark-override nem kell. */
html[data-theme="dark"] .fw-popup-row {
    border-bottom-color: var(--c-divider);
}

/* leaflet-chrome.css */
/* =============================================================================
   leaflet-chrome.css — generikus Leaflet UI-chrome témázása (attribution, zoom-bar,
   popup, távolság-skála, tile-pane) dark ÉS night témán. A Leaflet a DOM-ot maga
   rendereli (nem Razor-markup) → GLOBÁLIS. A világos kinézet a Leaflet saját alap-
   stílusából jön; itt csak a sötét/éjszakai kontrasztot állítjuk. Korábban app.css.
   NEM ide tartozik: a fleet-specifikus marker/popup (fleet-weather.css) és a
   .map-controls/.fleet-* (fleet-map.css).
   ============================================================================= */

/* ===== Leaflet attribution: discreet by default, full opacity on hover ===== */
.leaflet-control-attribution {
    font-size: 9px;
    line-height: 1.4;
    padding: 1px 5px;
    background: rgba(var(--nt-white-rgb), 0.55);
    color: rgba(29, 41, 57, 0.7);
    opacity: 0.55;
    transition: opacity 0.15s ease, background-color 0.15s ease;
}

    .leaflet-control-attribution:hover,
    .leaflet-control-attribution:focus-within {
        opacity: 1;
        background: var(--c-overlay-light);
    }

    .leaflet-control-attribution a {
        color: rgba(29, 41, 57, 0.85);
        text-decoration: none;
    }

        .leaflet-control-attribution a:hover {
            text-decoration: underline;
        }

html[data-theme="dark"] .leaflet-control-attribution {
    background: rgba(20, 24, 33, 0.5);
    color: rgba(229, 235, 245, 0.75);
}

    html[data-theme="dark"] .leaflet-control-attribution:hover,
    html[data-theme="dark"] .leaflet-control-attribution:focus-within {
        background: rgba(20, 24, 33, 0.88);
    }

    html[data-theme="dark"] .leaflet-control-attribution a {
        color: rgba(229, 235, 245, 0.9);
    }

/* Dark map container — prevents white flash before Dark Matter tiles load */
html[data-theme="dark"] .leaflet-container {
    background: #1a1a24;
}

/* ===== Leaflet zoom / bar controls in dark mode ===== */
html[data-theme="dark"] .leaflet-bar a,
html[data-theme="dark"] .leaflet-bar a:hover {
    background-color: var(--c-map-chrome);
    color: #e5ebf5;
    border-bottom-color: var(--c-map-chrome-border);
}

    html[data-theme="dark"] .leaflet-bar a:hover {
        background-color: #353c49;
    }

    html[data-theme="dark"] .leaflet-bar a.leaflet-disabled {
        background-color: #1f242e;
        color: #6b7280;
    }

html[data-theme="dark"] .leaflet-bar {
    border-color: rgba(0, 0, 0, 0.5);
    box-shadow: 0 1px 5px rgba(0, 0, 0, 0.55);
}

/* ===== Leaflet popups in dark mode ===== */
html[data-theme="dark"] .leaflet-popup-content-wrapper,
html[data-theme="dark"] .leaflet-popup-tip {
    background: var(--c-map-surface);
    color: #e5ebf5;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.6);
}

html[data-theme="dark"] .leaflet-popup-content {
    color: #e5ebf5;
}

    html[data-theme="dark"] .leaflet-popup-content a {
        color: var(--primary-clr);
    }

html[data-theme="dark"] .leaflet-popup-close-button,
html[data-theme="dark"] .leaflet-popup-close-button:hover {
    color: #cbd5e1;
}

/* Scale bar drawn as a ruler, matching the MapLibre map (.maplibregl-ctrl-scale in
   nautech-map.css): the label sits above a segment closed at both ends, with no plate behind it.
   Leaflet sets the width inline, so the element keeps no horizontal padding and the ruler is an
   ::after spanning it exactly. */
.nautech-scale .leaflet-control-scale-line {
    background: none;
    border: none;
    color: var(--text-clr);
    font-variant-numeric: tabular-nums;
    line-height: 1.3;
    padding: 0 0 7px;
    position: relative;
    text-align: center;
    text-shadow: 0 0 3px var(--list-bg), 0 1px 2px var(--list-bg);
}

.nautech-scale .leaflet-control-scale-line::after {
    border: 2px solid var(--text-clr);
    border-top: none;
    bottom: 0;
    content: "";
    filter: drop-shadow(0 0 2px var(--list-bg));
    height: 5px;
    left: 0;
    position: absolute;
    right: 0;
}

/* ===== Dark / night alaptérkép (CSS-sötétítés) ===== */
/* Egyetlen csempe-forrás van, az OSM standard (site.js -> nautechMapTiles.basemap); a sötét
   megjelenést itt állítjuk elő filterrel. Így nem kell API kulcsos szolgáltató: a korábbi
   CARTO Dark Matter kulcs nélkül "API KEY REQUIRED" vízjelet éget a csempékbe.
   A filter a .nautech-basemap konténeren ül, NEM a teljes .leaflet-tile-pane-en, ezért a marine
   WMS overlay-ek (GEBCO mélység, OpenSeaMap izobát) eredeti színükben maradnak, és a
   marker-/overlay-pane (hajóikon, útvonal, geofence) is érintetlen. */
:root[data-theme="dark"] .nautech-basemap,
:root[data-theme="night"] .nautech-basemap {
    filter: invert(1) hue-rotate(180deg) grayscale(0.8) brightness(0.8) contrast(1.05);
}

/* ===== NIGHT theme map (red-shift palette) ===== */
/* Ugyanaz a sötétítés, a lánc végén meleg vörös-eltolással a night identitáshoz. Egy filter-
   láncban kell megadni: két külön szabály nem adódik össze, az azonos specificitású későbbi
   egyszerűen felülírná az előzőt. */
:root[data-theme="night"] .nautech-basemap {
    filter: invert(1) hue-rotate(180deg) grayscale(0.8) brightness(0.8) contrast(1.05)
            sepia(0.45) hue-rotate(-18deg) saturate(1.15);
}

/* Prevent white flash before the basemap tiles load */
html[data-theme="night"] .leaflet-container {
    background: var(--list-bg);
}

html[data-theme="night"] .leaflet-bar a,
html[data-theme="night"] .leaflet-bar a:hover {
    background-color: var(--list-bg);
    color: var(--text-clr);
    border-bottom-color: var(--c-divider);
}

    html[data-theme="night"] .leaflet-bar a:hover {
        background-color: color-mix(in srgb, var(--primary-clr) 20%, var(--list-bg));
    }

    html[data-theme="night"] .leaflet-bar a.leaflet-disabled {
        background-color: var(--list-bg);
        color: var(--muted-clr);
    }

html[data-theme="night"] .leaflet-bar {
    border-color: rgba(0, 0, 0, 0.5);
    box-shadow: 0 1px 5px rgba(0, 0, 0, 0.55);
}

html[data-theme="night"] .leaflet-popup-content-wrapper,
html[data-theme="night"] .leaflet-popup-tip {
    background: var(--list-bg);
    color: var(--text-clr);
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.6);
}

html[data-theme="night"] .leaflet-popup-content,
html[data-theme="night"] .leaflet-popup-content a {
    color: var(--text-clr);
}

html[data-theme="night"] .leaflet-popup-close-button,
html[data-theme="night"] .leaflet-popup-close-button:hover {
    color: var(--muted-clr);
}

html[data-theme="night"] .leaflet-control-attribution {
    background: rgba(20, 0, 0, 0.85);
}

    html[data-theme="night"] .leaflet-control-attribution a {
        color: var(--text-clr);
    }


/* nautech-map.css */
/* A vektoros (MapLibre) térképek megjelenése.

   A MapLibre saját CSS-e (maplibre-gl.css) adja a működést; ez a fájl a Nautech-arculatot teszi rá,
   ugyanazokból a design-tokenekből, amikből a kártyák és a listák élnek. A cél az, hogy a térkép
   popupja és kontrolljai NE váljanak el a felület többi részétől.

   A `leaflet-chrome.css` érintetlen marad: az a raszteres fallback térképé, ami WebGL2 hiányában
   továbbra is használatban van. */

/* ===== Popup ===== */

.maplibregl-popup-content {
    padding: 0;
    background: var(--list-bg);
    border: var(--border-thin);
    border-radius: var(--list-border-radius);
    box-shadow: var(--elev-hover);
    font-family: var(--font-family);
    color: var(--text-clr);
    overflow: hidden;
}

/* A nyilat is a kártya háttere és kerete adja, különben fehér csőrrel lógna a sötét popup alatt. */
.maplibregl-popup-anchor-top .maplibregl-popup-tip,
.maplibregl-popup-anchor-top-left .maplibregl-popup-tip,
.maplibregl-popup-anchor-top-right .maplibregl-popup-tip {
    border-bottom-color: var(--list-bg);
}

.maplibregl-popup-anchor-bottom .maplibregl-popup-tip,
.maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip,
.maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip {
    border-top-color: var(--list-bg);
}

.maplibregl-popup-anchor-left .maplibregl-popup-tip {
    border-right-color: var(--list-bg);
}

.maplibregl-popup-anchor-right .maplibregl-popup-tip {
    border-left-color: var(--list-bg);
}

.maplibregl-popup-close-button {
    width: 28px;
    height: 28px;
    font-size: 1.1rem;
    line-height: 1;
    color: var(--muted-clr);
    background: transparent;
    border-radius: 8px;
}

.maplibregl-popup-close-button:hover,
.maplibregl-popup-close-button:focus-visible {
    color: var(--text-clr);
    background: color-mix(in srgb, var(--primary-clr) 12%, transparent);
}

.nautech-map-popup {
    display: grid;
    gap: 0.15rem;
    /* A jobb oldali térköz a bezáró gombot kerüli ki: az abszolút pozicionált ×
       egyébként ráült az első sor végére (mérve: a "(UTC)" alá csúszott). */
    padding: 0.7rem 2rem 0.7rem 0.9rem;
    font-size: 0.82rem;

    /* Hosszú lista (sok mező) ne nőjön ki a képernyőből. */
    max-height: 60dvh;
    overflow-y: auto;
}

/* A flotta-popup címsora: a hajó neve. A mezők FÖLÉ kerül, mert a flottában ez az elsődleges
   azonosító, nem egy a mérések közül. */
.nautech-map-popup-title {
    margin-bottom: 0.35rem;
    padding-bottom: 0.3rem;
    border-bottom: var(--border-thin);
    font-weight: 600;
}

.nautech-map-popup-row {
    display: grid;
    grid-template-columns: minmax(0, auto) minmax(0, 1fr);
    gap: 0.1rem 0.6rem;
    align-items: baseline;
}

.nautech-map-popup-key {
    color: var(--muted-clr);
    white-space: nowrap;
}

.nautech-map-popup-key::after {
    content: ':';
}

.nautech-map-popup-value {
    /* A koordináta és az időbélyeg számjegyei így nem ugrálnak soronként. */
    font-variant-numeric: tabular-nums;
    overflow-wrap: anywhere;
}

/* ===== Kontrollok ===== */

.maplibregl-ctrl-group {
    background: var(--list-bg);
    border: var(--border-thin);
    border-radius: 10px;
    box-shadow: var(--elev-rest);
    overflow: hidden;
}

.maplibregl-ctrl-group button + button {
    border-top: var(--border-thin);
}

.maplibregl-ctrl-group button:hover {
    background: color-mix(in srgb, var(--primary-clr) 10%, var(--list-bg));
}

/* A MapLibre a gomb-ikonokat sötét SVG-vel rajzolja; sötét témán ki kell fordítani, különben
   fekete ikon ül fekete gombon. Ugyanaz a megfontolás, mint a Leaflet-es kontrolloknál. */
:root[data-theme="dark"] .maplibregl-ctrl-group button .maplibregl-ctrl-icon,
:root[data-theme="night"] .maplibregl-ctrl-group button .maplibregl-ctrl-icon {
    filter: invert(1) brightness(1.6);
}

/* Scale bar drawn as a ruler, not a box: the label sits above a segment closed at both ends, and
   the segment spans exactly the width MapLibre measures. No plate behind it, so the contrast comes
   from a shadow instead -- the bar has to stay readable over sea, land and hillshade alike. */
.maplibregl-ctrl-scale {
    background: none;
    border: none;
    color: var(--text-clr);
    font-family: var(--font-family);
    font-size: 0.72rem;
    font-variant-numeric: tabular-nums;
    line-height: 1.3;
    /* No horizontal padding: MapLibre sets the width inline, and the ruler must match it. */
    padding: 0 0 7px;
    position: relative;
    text-align: center;
    text-shadow: 0 0 3px var(--list-bg), 0 1px 2px var(--list-bg);
}

.maplibregl-ctrl-scale::after {
    border: 2px solid var(--text-clr);
    border-top: none;
    bottom: 0;
    content: "";
    filter: drop-shadow(0 0 2px var(--list-bg));
    height: 5px;
    left: 0;
    position: absolute;
    right: 0;
}

/* A tömör állapotra a könyvtár FEHÉR pirulát rajzol (`background-color:#fff; color:#000`), és a
   szabálya két osztályt köt — az egy osztályos sajátunk gyengébb volt nála, ezért maradt világító
   folt a sötét térkép sarkában. A forrásmegjelölést jogilag KELL látni, tehát a szöveg színe sem
   esztétikai kérdés. */
.maplibregl-ctrl.maplibregl-ctrl-attrib,
.maplibregl-ctrl-attrib.maplibregl-compact {
    background: color-mix(in srgb, var(--list-bg) 82%, transparent);
    color: var(--muted-clr);
    font-size: 0.68rem;
}

.maplibregl-ctrl-attrib.maplibregl-compact {
    border: var(--border-thin);
    border-radius: 13px;
    padding: 2px 26px 2px 2px;
}

.maplibregl-ctrl-attrib.maplibregl-compact-show {
    padding: 2px 28px 2px 9px;
}

.maplibregl-ctrl-attrib a {
    color: var(--muted-clr);
}

/* Az „i” gomb beégetett, TÖMÖR fehér korongot hoz magával, sötét témán pedig ugyanaz az ikon
   fekete korongként tűnne el. Ezért nem képként, hanem MASZKKÉNT rajzoljuk: a festék a
   `--muted-clr`, tehát a gomb ugyanúgy követi a témát, mint a többi kontroll, és nem kell két
   külön ikon a világos és a sötét változathoz. */
.maplibregl-ctrl-attrib-button {
    background-image: none;
    background-color: var(--muted-clr);
    -webkit-mask: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath fill-rule='evenodd' d='M10 3a7 7 0 1 0 0 14 7 7 0 0 0 0-14m0 1.4a5.6 5.6 0 1 1 0 11.2 5.6 5.6 0 0 1 0-11.2'/%3E%3Ccircle cx='10' cy='6.9' r='1.05'/%3E%3Crect x='9.1' y='8.9' width='1.8' height='5.2' rx='.9'/%3E%3C/svg%3E") center / 17px 17px no-repeat;
    mask: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath fill-rule='evenodd' d='M10 3a7 7 0 1 0 0 14 7 7 0 0 0 0-14m0 1.4a5.6 5.6 0 1 1 0 11.2 5.6 5.6 0 0 1 0-11.2'/%3E%3Ccircle cx='10' cy='6.9' r='1.05'/%3E%3Crect x='9.1' y='8.9' width='1.8' height='5.2' rx='.9'/%3E%3C/svg%3E") center / 17px 17px no-repeat;
    opacity: 0.85;
}

.maplibregl-ctrl-attrib-button:hover {
    background-color: var(--text-clr);
    opacity: 1;
}

/* A vízjel a csempéken ül, ezért nem kap hátteret; a `pointer-events: none` azért kell, hogy a
   logó ne fogja el a panolást a jobb alsó sarokban. */
.nautech-map-watermark {
    margin: 0 8px 6px 0;
    opacity: 0.82;
    pointer-events: none;
}

/* Nyitott réteg-panel mellett a vízjel eltűnik — DE CSAK HA A PANEL TÉNYLEG RÁLÓG. Az osztályt
   a LayerControl méréssel teszi fel (lásd #syncWatermark): kártya-méretű térképen a panel a jobb
   alsó sarokig ér (mérve egy 290x385-ösön), egy flotta-térképen viszont meg sem közelíti. */
.nautech-map--layers-open .nautech-map-watermark {
    display: none;
}

/* ===== Réteg-kapcsoló ===== */

.nautech-map-layers {
    background: var(--list-bg);
    border: var(--border-thin);
    border-radius: 10px;
    box-shadow: var(--elev-rest);
    font-family: var(--font-family);
    font-size: 0.78rem;
    color: var(--text-clr);
    overflow: hidden;
}

.nautech-map-layers > button {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 29px;
    height: 29px;
    background: transparent;
    border: 0;
    cursor: pointer;
    color: inherit;
}

.nautech-map-layers > button:hover {
    background: color-mix(in srgb, var(--primary-clr) 10%, var(--list-bg));
}

.nautech-map-layers-panel {
    display: grid;
    gap: 0.35rem;
    padding: 0.6rem 0.75rem;
    min-width: 12rem;

    /* A felső korlátot a kontroll méri be a térkép aljához; ez csak a görgetést engedélyezi
       hozzá, hogy a panel a kis kártyán se lógjon rá a vízjelre és a forrásmegjelölésre.
       A `border-box` nem részletkérdés: nélküle a belső térköz a mért korláton FELÜL jön, és a
       panel megint kilóg (mérve: 300 px-es kártyán 306 px-ig ért). */
    box-sizing: border-box;
    overflow-y: auto;
    overscroll-behavior: contain;
}

.nautech-map-layers-panel label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    cursor: pointer;
    user-select: none;
}

.nautech-map-layers-panel input[type="checkbox"] {
    accent-color: var(--primary-clr);
    margin: 0;
}

/* A `[hidden]` a böngésző alap-stíluslapjában `display: none`, de az OSZTÁLY-szelektorunk
   erősebb nála, tehát a becsukott panel nyitva maradt. Mérve: `panel.hidden === true`, a computed
   display mégis `grid`. */
.nautech-map-layers-panel[hidden] {
    display: none;
}

.nautech-map-layers-panel hr {
    margin: 0.15rem 0;
    border: 0;
    border-top: var(--border-thin);
}

/* A térkép-vászon a konténer háttérszínét mutatja ott, ahol a döntött nézetben a terep-háló széle
   nem ér el a horizontig. Enélkül sötét témán fekete csík jelenik meg a horizont alatt. */
.maplibregl-map {
    font-family: var(--font-family);
    background-color: var(--list-bg);
}

/* fleet-map.css */
/* Fleet térkép marker + klaszter blip-ek.
   A Leaflet divIcon-ok JS-ből injektálódnak (leafletfleetmap.js), ezért NEM működne
   rajtuk a Blazor scoped CSS — ezeket globálisan kell definiálni.
   A severity-szín a hajó (vagy klaszter) legmagasabb severity-jét tükrözi:
   offline > critical > warning > info > clear. */

.fleet-marker-wrapper,
.fleet-cluster-wrapper {
    background: transparent;
    border: none;
}

/* Egyedi (nem összevont) hajó-marker: orrirányba forgatott nyíl (irány nélkül korong). A KITÖLTÉS
   a súlyosság színe, a SZEGÉLY az online-állapot — a két információ nem nyomja el egymást. A
   forgatást a markup adja (inline transform), itt csak a méret és a színek élnek. */
.fleet-marker {
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
}

    .fleet-marker svg {
        filter: drop-shadow(0 1px 2px var(--c-shadow-lg));
        overflow: visible;
    }

    /* Körvonal = online-állapot, mag = súlyosság. A mag fehér pereme választja el a kettőt akkor is,
       ha a két szín egyezik (kritikus + offline: mindkettő piros). */
    .fleet-marker .outline {
        fill: none;
        stroke: var(--fleet-online-clr, var(--c-success));
        stroke-width: 3;
        stroke-linejoin: round;
    }

    .fleet-marker .core {
        fill: var(--fleet-sev-clr, var(--primary-clr));
        stroke: #fff;
        stroke-width: 1.6;
        stroke-linejoin: round;
    }

.fleet-marker.is-online  { --fleet-online-clr: var(--c-success); }
.fleet-marker.is-offline { --fleet-online-clr: var(--critical-clr); }

/* Klaszter blip: körben a darabszám, severity-színnel. */
.fleet-cluster {
    width: 42px;
    height: 42px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-weight: 600;
    font-size: 0.9rem;
    border: 2px solid rgba(var(--nt-white-rgb), 0.85);
    box-shadow: 0 1px 5px var(--c-shadow-lg);
    background: var(--fleet-sev-clr, var(--primary-clr));
}

/* Severity → szín. A --fleet-sev-clr lokális változót állítjuk az adott blipre. */
/* A nyíl kitöltése riasztás nélkül a hajó-térkép kékje: zöld kitöltés zöld (online) szegéllyel
   egybeolvadna. A klaszter ugyanennél a jelnél zöld marad, annak nincs külön szegély-információja. */
.fleet-marker--clear                              { --fleet-sev-clr: var(--primary-clr); }
.fleet-cluster--clear                             { --fleet-sev-clr: var(--c-success); }
.fleet-marker--info,     .fleet-cluster--info     { --fleet-sev-clr: var(--info-clr); }
.fleet-marker--warning,  .fleet-cluster--warning  { --fleet-sev-clr: var(--warning-clr); }
.fleet-marker--critical, .fleet-cluster--critical { --fleet-sev-clr: var(--critical-clr); }
.fleet-marker--offline,  .fleet-cluster--offline  { --fleet-sev-clr: var(--muted-clr); }


/* ── Szűrő-panel (FleetMap.razor) ───────────────────────────────────────────
   Globálisan (nem scoped) definiálva, hogy a @<...> RenderFragment-tel rendelt
   chip-eket is biztosan elérje. Minden szabály a .fleet-filter névtérbe zárva. */
/* BAL-FELSŐ sarokba, a .map-controls (reset/rétegek, left:10px, 40px széles) MELLÉ JOBBRA.
   Ez a szabály a Leaflethez készült, ahol a nagyítás a bal felső sarokban ült, ezért a szűrő
   jobbra került. A vektoros térképpel a MapLibre a JOBB felső sarokba teszi a saját vezérlőit
   (nagyítás, iránytű, rétegkapcsoló), tehát a régi elhelyezés pont oda esett: mérve a
   flotta-áttekintőn 2418 képpontnyi átfedés, az időjárás-oldalon 651. A lap saját gombjai ezért
   átkerültek balra — ugyanaz a megoldás, mint az egyhajós térképen (`.map-controls--vector`).
   A 10 px-es eltolás ehhez KEVÉS volt: a Flotta-térképen a szűrő ÉS a reset/rétegek gombcsoport
   IS ott van, és egymásra csúsztak (1444 képpont). A szűrő ezért a gombcsoport mellé megy.
   A panel BALRA igazítva nyílik, tehát jobbra terjed a térkép fölé. */
.fleet-filter {
    position: absolute;
    top: 10px;
    left: 56px;
    z-index: 1000;
    display: flex;
    flex-direction: column;
    gap: 6px;
    align-items: flex-start;
}

.fleet-filter__toggle {
    width: 38px;
    height: 38px;
    border-radius: 10px;
    border: var(--border-thin);
    background: var(--list-bg, #fff);
    color: var(--text-clr);
    box-shadow: 0 2px 8px var(--c-shadow-md);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s ease;
}

    /* Nyitott szűrő → aktív állapot: brand-háttér (a fehér ikont a razor CustomColor adja). */
    .fleet-filter__toggle.active {
        background: var(--primary-clr);
        border-color: var(--primary-clr);
    }

    .fleet-filter__toggle:focus-visible {
        outline: 2px solid var(--primary-clr);
        outline-offset: 2px;
    }

.fleet-filter__body {
    background: var(--list-bg, #fff);
    border-radius: 10px;
    padding: 10px;
    box-shadow: 0 2px 10px var(--c-shadow-md);
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-width: min(78vw, 320px);
}

.fleet-filter__pills,
.fleet-filter__chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.fleet-filter .pill {
    border: 1px solid var(--extra-muted-clr, #ccc);
    background: transparent;
    color: var(--text-clr);
    border-radius: 999px;
    padding: 4px 12px;
    font-size: 0.8rem;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

    .fleet-filter .pill.active {
        background: var(--primary-clr);
        border-color: var(--primary-clr);
        color: #fff;
    }

.fleet-filter .chip {
    border: none;
    border-radius: 999px;
    padding: 4px 10px;
    font-size: 0.78rem;
    color: #fff;
    cursor: pointer;
    opacity: 0.55; /* nem kiválasztott = halvány */
    transition: opacity 0.15s ease, box-shadow 0.15s ease;
}

    .fleet-filter .chip.active {
        opacity: 1;
        box-shadow: 0 0 0 2px rgba(var(--nt-white-rgb), 0.65) inset;
    }

    .fleet-filter .chip:disabled {
        opacity: 0.4; /* kölcsönös kizárás (clear ↔ info/warning/critical) */
        cursor: default;
    }

    .fleet-filter .chip--clear { background: var(--c-success); }
    .fleet-filter .chip--info { background: var(--info-clr); }
    .fleet-filter .chip--warning { background: var(--warning-clr); }
    .fleet-filter .chip--critical { background: var(--critical-clr); }
    .fleet-filter .chip--offline { background: var(--muted-clr); }

.fleet-filter__search {
    border: 1px solid var(--extra-muted-clr, #ccc);
    border-radius: 8px;
    padding: 6px 10px;
    font-size: 0.85rem;
    background: var(--list-bg, #fff);
    color: var(--text-clr);
    outline: none;
}

    .fleet-filter__search:focus {
        border-color: var(--primary-clr);
    }

/* ── Térkép-vezérlők (Reset zoom + Depth layers) ──────────────────────────────
   A sima egyhajós térkép (.map-controls / .map-icon-btn) stílusa, GLOBÁLISAN, hogy a fleet-térkép
   gombjai is pont ugyanúgy nézzenek ki. A MapComponent.razor.css scoped másolata specifikusabb
   (class + [b-...]), ezért ott felülírja ezt → az egyhajós térkép változatlan. */
.map-controls {
    position: absolute;
    top: 10px;
    right: 10px;
    z-index: 1000;
    display: flex;
    flex-direction: column;
    background: var(--list-bg);
    border: var(--border-thin);
    border-radius: var(--list-border-radius);
    box-shadow: 0 2px 8px var(--c-shadow-md);
    overflow: hidden;
    opacity: 0.55;
    transition: opacity 0.2s ease;
}

/* A FLOTTA-OLDALAKON BALRA. Ott a vektoros térkép mindig fut (nincs raszteres tartalék-ág),
   tehát a jobb felső sarok fixen a MapLibre vezérlőié. Az egyhajós térképen ezt a feltételes
   `.map-controls--vector` modosító intézi, mert ott van raszteres tartalék is. */
.fleet-overview .map-controls,
.fleet-map-page .map-controls,
.fleet-weather-page .map-controls {
    right: auto;
    left: 10px;
}

/* Nyugalomban halvány (felfedezhetőség + touch-tappable), a térképre/vezérlőre húzva teljes. */
.fleet-map-page:hover .map-controls,
.fleet-weather-page:hover .map-controls,
.fleet-overview:hover .map-controls,
.map-controls:hover {
    opacity: 1;
}

/* A gombok AppButton-ök → natív <button class="app-btn ... map-icon-btn">. A `.app-btn.map-icon-btn`
   (2 osztály) felülírja az AppButton base/icon/plain méretét, keretét és színét; a hover a
   `:not(:disabled)`-del veri az AppButton variáns-hoverét. Nincs többé ::part(control) (shadow-DOM). */
/* A `.map-controls` prefix b=3 specificitást ad, ami MEGVERI az AppButton scoped base-ét (b=2) —
   a scoped bundle a globális CSS UTÁN töltődik (index.html), így b=2 döntetlennél a base nyerne
   (kerek sarok / padding / szín). A .map-controls minden térkép-oldalon a gombok konténere. */
.map-controls .app-btn.map-icon-btn {
    min-width: 38px;
    width: 38px;
    height: 38px;
    padding: 0;
    border: none;
    border-radius: 0;
    background: transparent;
    color: var(--muted-clr);
}

.map-controls .app-btn.map-icon-btn + .app-btn.map-icon-btn {
    border-top: var(--border-thin);
}

.map-controls .app-btn.map-icon-btn:hover:not(:disabled) {
    background: var(--c-divider);
    color: var(--text-clr);
}

.map-controls .app-btn.map-icon-btn.is-active {
    color: var(--primary-clr);
    background: color-mix(in srgb, var(--primary-clr) 12%, transparent);
}

.map-controls .app-btn.map-icon-btn:focus-visible {
    outline: 2px solid var(--primary-clr);
    outline-offset: -2px;
}

:root[data-theme="night"] .map-controls .app-btn.map-icon-btn {
    color: var(--text-clr);
}

    :root[data-theme="night"] .map-controls .app-btn.map-icon-btn:hover:not(:disabled) {
        background: color-mix(in srgb, var(--primary-clr) 25%, transparent);
        color: var(--text-clr);
    }

    :root[data-theme="night"] .map-controls .app-btn.map-icon-btn.is-active {
        color: var(--primary-clr);
        background: color-mix(in srgb, var(--primary-clr) 25%, transparent);
    }

/* fluent-overrides.css */
/* =============================================================================
   fluent-overrides.css — FluentUI komponens-override-ok (legutolsó token-fájl)
   -----------------------------------------------------------------------------
   A Fluent saját JS-vezérelt design-token rendszert + shadow-DOM-ot használ, amit a
   `--*-clr` aliasok nem érnek el közvetlenül. Itt a `::part`-okat színezzük a téma-
   tokenekkel (colors.css `--primary-clr`/`--text-clr`/`--background-clr`, `--c-overlay-loading`),
   hogy a Fluent elemek a brandet és a témát kövessék. Az index.html-ben LEGUTOLSÓNAK
   töltjük, hogy felülírja a Fluent default stílusait.
   ============================================================================= */

/* ---- Fluent accent gombok brand-színre (Save / Reset / Submit / dialógus-akciók) ----
   A <fluent-button appearance="accent"> a Fluent SAJÁT --accent-fill-* tokenjeit használja
   (NEM a --primary-clr-t) — ezért night módban kéken maradna a piros téma közepén. A
   shadow-DOM ::part(control) háttér felülírása megkerüli a JS-tokeneket, így minden accent
   gomb mindhárom témán a brand --primary-clr-t kapja. A fehér felirat mindhárom shade-en AA-felett. */
:is(fluent-button, fluent-anchor)[appearance="accent"]::part(control) {
    background: var(--primary-clr);
    border-color: var(--primary-clr);
    color: #fff;
}

:is(fluent-button, fluent-anchor)[appearance="accent"]:hover::part(control) {
    background: color-mix(in srgb, var(--primary-clr) 88%, #000);
    border-color: color-mix(in srgb, var(--primary-clr) 88%, #000);
}

:is(fluent-button, fluent-anchor)[appearance="accent"]:active::part(control) {
    background: color-mix(in srgb, var(--primary-clr) 78%, #000);
}

/* Fluent hyperlink (nem-accent anchor) színe dark / night témán a brand --primary-clr-re. A Fluent
   sötétkék hyperlink (#023b8f) a sötét kártyán alig látszik és kirí a piros night-témából. */
:root[data-theme="dark"] fluent-anchor:not([appearance="accent"])::part(control),
:root[data-theme="night"] fluent-anchor:not([appearance="accent"])::part(control) {
    color: var(--primary-clr);
}

/* ---- Fluent input-mezők dark / night témán ----
   A <FluentDesignTheme> ellenére a Fluent design-tokenek itt light módban ragadnak: a mezők fehér
   háttérrel, a label + érték + border pedig --neutral-foreground-rest ≈ #1a1a1a-val rajzolódnak.
   A sötét kártyán így a label és a border láthatatlan, a fehér mező pedig kirí a sötét témából.
   A ::part-okat közvetlenül színezzük (téma-tokenekkel), hogy a mezők a sötét témába illeszkedjenek. */
/* Label szín — minden field-szerű Fluent komponensnél (csak color, layoutot nem érint). */
:root[data-theme="dark"] :is(fluent-text-field, fluent-number-field, fluent-search, fluent-text-area, fluent-select, fluent-combobox, fluent-date-picker)::part(label),
:root[data-theme="night"] :is(fluent-text-field, fluent-number-field, fluent-search, fluent-text-area, fluent-select, fluent-combobox, fluent-date-picker)::part(label) {
    color: var(--text-clr);
}

/* Mező-háttér + border. A text-field/number/search a ::part(root)-on hordozza a dobozt; a
   text-area/select/combobox/date-picker a ::part(control)-on. */
:root[data-theme="dark"] :is(fluent-text-field, fluent-number-field, fluent-search)::part(root),
:root[data-theme="night"] :is(fluent-text-field, fluent-number-field, fluent-search)::part(root),
:root[data-theme="dark"] :is(fluent-text-area, fluent-select, fluent-combobox, fluent-date-picker)::part(control),
:root[data-theme="night"] :is(fluent-text-area, fluent-select, fluent-combobox, fluent-date-picker)::part(control) {
    background: var(--background-clr);
    border-color: var(--extra-muted-clr);
}

/* Érték/szöveg szín a mezőkben. */
:root[data-theme="dark"] :is(fluent-text-field, fluent-number-field, fluent-search, fluent-text-area, fluent-select, fluent-combobox, fluent-date-picker)::part(control),
:root[data-theme="night"] :is(fluent-text-field, fluent-number-field, fluent-search, fluent-text-area, fluent-select, fluent-combobox, fluent-date-picker)::part(control) {
    color: var(--text-clr);
}

/* Legördülő opciók (select/combobox popup) — különben fehér popup marad a sötét témán. */
:root[data-theme="dark"] fluent-option,
:root[data-theme="night"] fluent-option {
    background: var(--list-bg);
    color: var(--text-clr);
}

/* .fluent-input-label — egyedi label-osztály az edit / automation / alarm / geofence / tank form-okban.
   Több komponens scoped CSS-e color: var(--neutral-foreground-rest)-tel színezi (Fluent #1a1a1a),
   ami night-ban láthatatlan a sötét kártyán. Témakövető --text-clr-re állítjuk (a scoped rule
   class+attribútum specificitása miatt !important kell). */
:root[data-theme="dark"] .fluent-input-label,
:root[data-theme="night"] .fluent-input-label {
    color: var(--text-clr) !important;
}

/* Radio / checkbox / switch felirat. A .label part SAJÁT #1a1a1a színt hordoz (a host color nem
   örökli felül), ezért közvetlenül a ::part(label)-t színezzük. */
:root[data-theme="dark"] :is(fluent-radio, fluent-checkbox, fluent-switch, fluent-radio-group),
:root[data-theme="night"] :is(fluent-radio, fluent-checkbox, fluent-switch, fluent-radio-group),
:root[data-theme="dark"] :is(fluent-radio, fluent-checkbox, fluent-switch)::part(label),
:root[data-theme="night"] :is(fluent-radio, fluent-checkbox, fluent-switch)::part(label) {
    color: var(--text-clr);
}

/* Kiválasztott radio-pötty / checkbox-pipa a brand-színre (Fluent kék #036ac4 accent helyett). */
:root[data-theme="dark"] :is(fluent-radio, fluent-checkbox)[aria-checked="true"]::part(control),
:root[data-theme="night"] :is(fluent-radio, fluent-checkbox)[aria-checked="true"]::part(control) {
    background: var(--primary-clr);
    border-color: var(--primary-clr);
}

/* ---- FluentOverlay loading / "nincs adat" scrim — témafüggő háttér ----
   A FluentUI FluentOverlay BackgroundColor paramétere CSAK literál hexet fogad (CSS var()-t
   nem — ezért a komponensekben #bbccdd fallback van). Itt tesszük témafüggővé: a komponens
   inline background-color-t rak a .fluent-overlay-re, ezért az override-hoz !important kell.

   FONTOS — csak a SAJÁT scrim-overlay-einkre (Transparent=false) szabad ráhúzni a színt.
   A FluentUI popup-komponensek (FluentDatePicker, FluentSelect, FluentCombobox, FluentMenu,
   FluentAutocomplete) egy Transparent=true, full-screen, "kattintás-elkapó" .fluent-overlay-t
   renderelnek a popup bezárásához — ez inline background-color NÉLKÜL jön. Egy szűrés nélküli
   `.fluent-overlay { background-color: ... !important }` ezekre is ráfeküdne, így a #bbccdd
   átlátszatlan lappal letakarná az EGÉSZ viewportot (csak a popup látszana), és az oldal
   eltűnne — pl. a Date Interval dialog naptárának megnyitásakor (DateIntervalPickerDialog).
   A [style*="background-color"] szűrő csak azokat fogja meg, amik maguk is színt kérnek. */
.fluent-overlay[style*="background-color"] {
    background-color: var(--c-overlay-loading) !important;
}

/* ---- Fluent slider / number-field ::part méretezés (korábban app.css) ----
   A ::part a Fluent komponensek shadow-DOM-ját célozza, ezért GLOBÁLIS (scoped nem érné el). */
/* ::part only works here, not in isolation */
.volume-slider::part(positioning-region) {
    top: 10px;
}

.seek-slider::part(positioning-region) {
    margin: 0 2px;
}

.threshold-input::part(root),
.threshold-input::part(control) {
    height: 3rem;
}

.threshold-input::part(control) {
    width: 5rem;
    font-size: 1.5rem;
}

.threshold-input::part(controls) {
    top: 10px;
}

/* ---- Update banner ütközések (Layout/UpdateBanner.razor) ----
   A toast-provider bottom:24px-en áll, a banner is ott van: amíg a banner kint van, a toastok
   feljebb kerülnek, hogy egy riasztás ne tűnjön el a banner alatt. Mobilon a becsúszó menü
   alsó menüpontjait fedné a banner, ezért nyitott menü mellett elrejtjük. */
body:has(.update-banner) .fluent-toast-provider.position-bottomright {
    bottom: 96px;
}

@media (max-width: 560px) {
    body:has(.update-banner) .fluent-toast-provider.position-bottomright {
        bottom: 148px;
    }
}

@media (max-width: 900px) {
    body:has(#sidebar.show) .update-banner {
        display: none;
    }
}

