/* Shared stylesheet for Week 15 files. Generated from embedded page styles. */

.full-canvas-stage {
  position: absolute;
  top: 0%;
  height: 100%;
  left: 0%;
  width: 100%;
  text-align: center;
}

.mqtt-data-table {
  border-collapse: collapse;
  margin-top: 1em;
}

.mqtt-data-table th,
.mqtt-data-table td {
  border: 1px solid currentColor;
  padding: 6px;
}

.mqtt-data-key {
  text-align: left;
}

/* bird_count_bar_graph_mqtt.html */
body.week15-bird-count-bar-graph-mqtt {
      color-scheme: light;
      --bg: #f6f7f2;
      --panel: #ffffff;
      --ink: #1d2528;
      --muted: #667174;
      --line: #d8ddd5;
      --accent: #156b7a;
      --bar: #b24d26;
      --good: #247a4d;
      --warn: #a15f00;
      --bad: #a73636;
    }

    body.week15-bird-count-bar-graph-mqtt * {
      box-sizing: border-box;
    }

    body.week15-bird-count-bar-graph-mqtt {
      margin: 0;
      min-height: 100vh;
      background: var(--bg);
      color: var(--ink);
      font-family: Arial, Helvetica, sans-serif;
    }

    body.week15-bird-count-bar-graph-mqtt main {
      width: min(1180px, calc(100% - 32px));
      margin: 0 auto;
      padding: 24px 0 36px;
    }

    body.week15-bird-count-bar-graph-mqtt header {
      display: flex;
      justify-content: space-between;
      gap: 16px;
      align-items: flex-end;
      margin-bottom: 18px;
    }

    body.week15-bird-count-bar-graph-mqtt h1 {
      margin: 0 0 6px;
      font-size: 28px;
      letter-spacing: 0;
    }

    body.week15-bird-count-bar-graph-mqtt .topic {
      margin: 0;
      color: var(--muted);
      font-size: 14px;
      overflow-wrap: anywhere;
    }

    body.week15-bird-count-bar-graph-mqtt .status,
    body.week15-bird-count-bar-graph-mqtt .panel {
      background: var(--panel);
      border: 1px solid var(--line);
      border-radius: 8px;
    }

    body.week15-bird-count-bar-graph-mqtt .status {
      flex: 0 0 auto;
      padding: 10px 12px;
      font-size: 14px;
      min-width: 250px;
      text-align: right;
    }

    body.week15-bird-count-bar-graph-mqtt .status strong {
      display: block;
      color: var(--warn);
      font-size: 13px;
      margin-bottom: 2px;
    }

    body.week15-bird-count-bar-graph-mqtt .panel {
      padding: 14px;
      margin-bottom: 16px;
    }

    body.week15-bird-count-bar-graph-mqtt .summary {
      display: grid;
      grid-template-columns: repeat(3, minmax(0, 1fr));
      gap: 12px;
    }

    body.week15-bird-count-bar-graph-mqtt .metric {
      border: 1px solid var(--line);
      border-radius: 8px;
      padding: 12px;
    }

    body.week15-bird-count-bar-graph-mqtt .metric label {
      display: block;
      color: var(--muted);
      font-size: 13px;
      margin-bottom: 6px;
    }

    body.week15-bird-count-bar-graph-mqtt .metric strong {
      display: block;
      color: var(--accent);
      font-size: 26px;
      line-height: 1;
      overflow-wrap: anywhere;
    }

    body.week15-bird-count-bar-graph-mqtt .controls {
      display: flex;
      flex-wrap: wrap;
      gap: 10px;
      margin-top: 12px;
    }

    body.week15-bird-count-bar-graph-mqtt button {
      border: 1px solid var(--line);
      background: #ffffff;
      color: var(--ink);
      border-radius: 8px;
      padding: 9px 12px;
      font: inherit;
      font-weight: 700;
      cursor: pointer;
    }

    body.week15-bird-count-bar-graph-mqtt button:hover,
    body.week15-bird-count-bar-graph-mqtt button:focus-visible {
      border-color: var(--accent);
      outline: none;
    }

    body.week15-bird-count-bar-graph-mqtt .chart-shell {
      overflow-x: auto;
      padding-bottom: 6px;
    }

    body.week15-bird-count-bar-graph-mqtt .chart {
      display: flex;
      align-items: end;
      gap: 16px;
      min-height: 430px;
      min-width: 100%;
      padding: 18px 8px 8px;
    }

    body.week15-bird-count-bar-graph-mqtt .bird-column {
      display: grid;
      grid-template-rows: 28px 260px 92px auto;
      align-items: end;
      justify-items: center;
      min-width: 130px;
      max-width: 150px;
    }

    body.week15-bird-count-bar-graph-mqtt .count {
      color: var(--ink);
      font-size: 22px;
      font-weight: 700;
      line-height: 1;
    }

    body.week15-bird-count-bar-graph-mqtt .bar-wrap {
      display: flex;
      align-items: end;
      width: 48px;
      height: 250px;
      border-radius: 8px;
      background: #eef1ec;
      overflow: hidden;
      border: 1px solid var(--line);
    }

    body.week15-bird-count-bar-graph-mqtt .bar {
      width: 100%;
      min-height: 4px;
      background: var(--bar);
      border-radius: 8px 8px 0 0;
    }

    body.week15-bird-count-bar-graph-mqtt .bird-image {
      width: 82px;
      height: 72px;
      object-fit: cover;
      border-radius: 8px;
      border: 1px solid var(--line);
      background: #eef1ec;
      margin-top: 12px;
    }

    body.week15-bird-count-bar-graph-mqtt .placeholder {
      display: grid;
      place-items: center;
      width: 82px;
      height: 72px;
      border-radius: 8px;
      border: 1px solid var(--line);
      background: #eef1ec;
      color: var(--muted);
      font-size: 11px;
      text-align: center;
      padding: 6px;
      margin-top: 12px;
    }

    body.week15-bird-count-bar-graph-mqtt .bird-name {
      align-self: start;
      color: var(--ink);
      font-size: 13px;
      font-weight: 700;
      line-height: 1.2;
      text-align: center;
      overflow-wrap: anywhere;
    }

    body.week15-bird-count-bar-graph-mqtt .empty {
      color: var(--muted);
      padding: 18px;
      text-align: center;
    }

    body.week15-bird-count-bar-graph-mqtt .latest {
      margin: 0;
      white-space: pre-wrap;
      overflow-wrap: anywhere;
      color: #2f3b3f;
      font-size: 13px;
    }

    @media (max-width: 760px) {
      body.week15-bird-count-bar-graph-mqtt main {
        width: min(100% - 20px, 1180px);
        padding-top: 16px;
      }

      body.week15-bird-count-bar-graph-mqtt header {
        display: grid;
      }

      body.week15-bird-count-bar-graph-mqtt .status {
        min-width: 0;
        text-align: left;
      }

      body.week15-bird-count-bar-graph-mqtt .summary {
        grid-template-columns: 1fr;
      }
    }

/* bird_record_mqtt_gallery.html */
body.week15-bird-record-mqtt-gallery {
      color-scheme: light;
      --bg: #f6f7f2;
      --panel: #ffffff;
      --ink: #1d2528;
      --muted: #667174;
      --line: #d8ddd5;
      --accent: #156b7a;
      --good: #247a4d;
      --warn: #a15f00;
      --bad: #a73636;
    }

    body.week15-bird-record-mqtt-gallery * {
      box-sizing: border-box;
    }

    body.week15-bird-record-mqtt-gallery {
      margin: 0;
      min-height: 100vh;
      background: var(--bg);
      color: var(--ink);
      font-family: Arial, Helvetica, sans-serif;
    }

    body.week15-bird-record-mqtt-gallery main {
      width: min(1180px, calc(100% - 32px));
      margin: 0 auto;
      padding: 24px 0 36px;
    }

    body.week15-bird-record-mqtt-gallery header {
      display: flex;
      justify-content: space-between;
      gap: 16px;
      align-items: flex-end;
      margin-bottom: 18px;
    }

    body.week15-bird-record-mqtt-gallery h1 {
      margin: 0 0 6px;
      font-size: 28px;
      letter-spacing: 0;
    }

    body.week15-bird-record-mqtt-gallery .topic {
      margin: 0;
      color: var(--muted);
      font-size: 14px;
      overflow-wrap: anywhere;
    }

    body.week15-bird-record-mqtt-gallery .status,
    body.week15-bird-record-mqtt-gallery .panel,
    body.week15-bird-record-mqtt-gallery .bird-card {
      background: var(--panel);
      border: 1px solid var(--line);
      border-radius: 8px;
    }

    body.week15-bird-record-mqtt-gallery .status {
      flex: 0 0 auto;
      padding: 10px 12px;
      font-size: 14px;
      min-width: 250px;
      text-align: right;
    }

    body.week15-bird-record-mqtt-gallery .status strong {
      display: block;
      color: var(--warn);
      font-size: 13px;
      margin-bottom: 2px;
    }

    body.week15-bird-record-mqtt-gallery .panel {
      padding: 14px;
      margin-bottom: 16px;
    }

    body.week15-bird-record-mqtt-gallery .summary {
      display: grid;
      grid-template-columns: repeat(3, minmax(0, 1fr));
      gap: 12px;
    }

    body.week15-bird-record-mqtt-gallery .metric {
      border: 1px solid var(--line);
      border-radius: 8px;
      padding: 12px;
    }

    body.week15-bird-record-mqtt-gallery .metric label {
      display: block;
      color: var(--muted);
      font-size: 13px;
      margin-bottom: 6px;
    }

    body.week15-bird-record-mqtt-gallery .metric strong {
      display: block;
      color: var(--accent);
      font-size: 26px;
      line-height: 1;
      overflow-wrap: anywhere;
    }

    body.week15-bird-record-mqtt-gallery .controls {
      display: flex;
      flex-wrap: wrap;
      gap: 10px;
      margin-top: 12px;
    }

    body.week15-bird-record-mqtt-gallery button {
      border: 1px solid var(--line);
      background: #ffffff;
      color: var(--ink);
      border-radius: 8px;
      padding: 9px 12px;
      font: inherit;
      font-weight: 700;
      cursor: pointer;
    }

    body.week15-bird-record-mqtt-gallery button:hover,
    body.week15-bird-record-mqtt-gallery button:focus-visible {
      border-color: var(--accent);
      outline: none;
    }

    body.week15-bird-record-mqtt-gallery .gallery {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
      gap: 14px;
    }

    body.week15-bird-record-mqtt-gallery .bird-card {
      overflow: hidden;
    }

    body.week15-bird-record-mqtt-gallery .bird-card img {
      display: block;
      width: 100%;
      aspect-ratio: 4 / 3;
      object-fit: cover;
      background: #eef1ec;
      border-bottom: 1px solid var(--line);
    }

    body.week15-bird-record-mqtt-gallery .image-placeholder {
      display: grid;
      place-items: center;
      width: 100%;
      aspect-ratio: 4 / 3;
      background: #eef1ec;
      color: var(--muted);
      border-bottom: 1px solid var(--line);
      padding: 16px;
      text-align: center;
    }

    body.week15-bird-record-mqtt-gallery .card-body {
      padding: 12px;
    }

    body.week15-bird-record-mqtt-gallery .card-body h2 {
      margin: 0 0 4px;
      font-size: 18px;
      letter-spacing: 0;
    }

    body.week15-bird-record-mqtt-gallery .scientific {
      margin: 0 0 10px;
      color: var(--muted);
      font-size: 14px;
      font-style: italic;
      overflow-wrap: anywhere;
    }

    body.week15-bird-record-mqtt-gallery dl {
      display: grid;
      grid-template-columns: 94px minmax(0, 1fr);
      gap: 7px 10px;
      margin: 0;
      font-size: 13px;
    }

    body.week15-bird-record-mqtt-gallery dt {
      color: var(--muted);
      font-weight: 700;
    }

    body.week15-bird-record-mqtt-gallery dd {
      margin: 0;
      overflow-wrap: anywhere;
    }

    body.week15-bird-record-mqtt-gallery details {
      margin-top: 12px;
      border-top: 1px solid var(--line);
      padding-top: 10px;
    }

    body.week15-bird-record-mqtt-gallery summary {
      cursor: pointer;
      color: var(--accent);
      font-size: 13px;
      font-weight: 700;
    }

    body.week15-bird-record-mqtt-gallery pre {
      margin: 10px 0 0;
      white-space: pre-wrap;
      overflow-wrap: anywhere;
      color: #2f3b3f;
      font-size: 12px;
    }

    body.week15-bird-record-mqtt-gallery .empty {
      color: var(--muted);
      padding: 18px;
      text-align: center;
    }

    @media (max-width: 760px) {
      body.week15-bird-record-mqtt-gallery main {
        width: min(100% - 20px, 1180px);
        padding-top: 16px;
      }

      body.week15-bird-record-mqtt-gallery header {
        display: grid;
      }

      body.week15-bird-record-mqtt-gallery .status {
        min-width: 0;
        text-align: left;
      }

      body.week15-bird-record-mqtt-gallery .summary {
        grid-template-columns: 1fr;
      }
    }

/* tempandhumiditytest_v2.html */
body.week15-tempandhumiditytest-v2 {
      color-scheme: light;
      --bg: #f6f7f2;
      --panel: #ffffff;
      --ink: #1d2528;
      --muted: #667174;
      --line: #d8ddd5;
      --temperature: #b24d26;
      --humidity: #156b7a;
      --good: #247a4d;
      --warn: #a15f00;
      --bad: #a73636;
    }

    body.week15-tempandhumiditytest-v2 * {
      box-sizing: border-box;
    }

    body.week15-tempandhumiditytest-v2 {
      margin: 0;
      min-height: 100vh;
      background: var(--bg);
      color: var(--ink);
      font-family: Arial, Helvetica, sans-serif;
    }

    body.week15-tempandhumiditytest-v2 main {
      width: min(1120px, calc(100% - 32px));
      margin: 0 auto;
      padding: 24px 0 36px;
    }

    body.week15-tempandhumiditytest-v2 header {
      display: flex;
      justify-content: space-between;
      gap: 16px;
      align-items: flex-end;
      margin-bottom: 18px;
    }

    body.week15-tempandhumiditytest-v2 h1 {
      margin: 0 0 6px;
      font-size: 28px;
      font-weight: 700;
      letter-spacing: 0;
    }

    body.week15-tempandhumiditytest-v2 .topic {
      margin: 0;
      color: var(--muted);
      font-size: 14px;
      overflow-wrap: anywhere;
    }

    body.week15-tempandhumiditytest-v2 .status {
      flex: 0 0 auto;
      border: 1px solid var(--line);
      background: var(--panel);
      border-radius: 8px;
      padding: 10px 12px;
      font-size: 14px;
      min-width: 230px;
      text-align: right;
    }

    body.week15-tempandhumiditytest-v2 .status strong {
      display: block;
      color: var(--warn);
      font-size: 13px;
      margin-bottom: 2px;
    }

    body.week15-tempandhumiditytest-v2 .dashboard {
      display: grid;
      grid-template-columns: minmax(0, 1fr) 300px;
      gap: 16px;
      align-items: start;
    }

    body.week15-tempandhumiditytest-v2 .chart-panel,
    body.week15-tempandhumiditytest-v2 .side-panel,
    body.week15-tempandhumiditytest-v2 .raw-panel {
      background: var(--panel);
      border: 1px solid var(--line);
      border-radius: 8px;
    }

    body.week15-tempandhumiditytest-v2 .chart-panel,
    body.week15-tempandhumiditytest-v2 .side-panel,
    body.week15-tempandhumiditytest-v2 .raw-panel {
      padding: 14px;
    }

    body.week15-tempandhumiditytest-v2 canvas {
      display: block;
      width: 100%;
      height: min(58vh, 520px);
      min-height: 360px;
    }

    body.week15-tempandhumiditytest-v2 .chart-toolbar {
      display: flex;
      justify-content: flex-end;
      align-items: center;
      gap: 10px;
      margin-bottom: 10px;
      color: var(--muted);
      font-size: 13px;
    }

    body.week15-tempandhumiditytest-v2 .unit-toggle {
      border: 1px solid var(--line);
      background: #ffffff;
      color: var(--ink);
      border-radius: 8px;
      padding: 7px 11px;
      font: inherit;
      cursor: pointer;
    }

    body.week15-tempandhumiditytest-v2 .unit-toggle:hover,
    body.week15-tempandhumiditytest-v2 .unit-toggle:focus-visible {
      border-color: var(--temperature);
      outline: none;
    }

    body.week15-tempandhumiditytest-v3 .reconnect-button {
      display: inline-block;
      margin-top: 8px;
      border: 1px solid var(--line);
      background: #ffffff;
      color: var(--ink);
      border-radius: 8px;
      padding: 7px 10px;
      font: inherit;
      cursor: pointer;
    }

    body.week15-tempandhumiditytest-v3 .reconnect-button:hover,
    body.week15-tempandhumiditytest-v3 .reconnect-button:focus-visible {
      border-color: var(--temperature);
      outline: none;
    }

    body.week15-tempandhumiditytest-v2 .reading-grid {
      display: grid;
      gap: 10px;
      margin-bottom: 14px;
    }

    body.week15-tempandhumiditytest-v2 .reading {
      border: 1px solid var(--line);
      border-radius: 8px;
      padding: 12px;
    }

    body.week15-tempandhumiditytest-v2 .reading label {
      display: block;
      color: var(--muted);
      font-size: 13px;
      margin-bottom: 5px;
    }

    body.week15-tempandhumiditytest-v2 .reading .value {
      font-size: 30px;
      line-height: 1;
      font-weight: 700;
      overflow-wrap: anywhere;
    }

    body.week15-tempandhumiditytest-v2 .reading.temperature .value {
      color: var(--temperature);
    }

    body.week15-tempandhumiditytest-v2 .reading.humidity .value {
      color: var(--humidity);
    }

    body.week15-tempandhumiditytest-v2 table {
      width: 100%;
      border-collapse: collapse;
      font-size: 13px;
    }

    body.week15-tempandhumiditytest-v2 th,
    body.week15-tempandhumiditytest-v2 td {
      border-top: 1px solid var(--line);
      padding: 8px 0;
      text-align: left;
      vertical-align: top;
      overflow-wrap: anywhere;
    }

    body.week15-tempandhumiditytest-v2 th {
      width: 34%;
      color: var(--muted);
      font-weight: 600;
      padding-right: 10px;
    }

    body.week15-tempandhumiditytest-v2 .raw-panel {
      margin-top: 16px;
    }

    body.week15-tempandhumiditytest-v2 .raw-panel h2,
    body.week15-tempandhumiditytest-v2 .side-panel h2 {
      margin: 0 0 10px;
      font-size: 16px;
    }

    body.week15-tempandhumiditytest-v2 pre {
      margin: 0;
      white-space: pre-wrap;
      overflow-wrap: anywhere;
      color: #2f3b3f;
      font-size: 13px;
    }

    body.week15-tempandhumiditytest-v2 .legend {
      display: flex;
      flex-wrap: wrap;
      gap: 12px;
      margin-top: 10px;
      color: var(--muted);
      font-size: 13px;
    }

    body.week15-tempandhumiditytest-v2 .legend span {
      display: inline-flex;
      align-items: center;
      gap: 6px;
    }

    body.week15-tempandhumiditytest-v2 .swatch {
      display: inline-block;
      width: 22px;
      height: 3px;
      border-radius: 999px;
      background: currentColor;
    }

    body.week15-tempandhumiditytest-v2 .temperature-key {
      color: var(--temperature);
    }

    body.week15-tempandhumiditytest-v2 .humidity-key {
      color: var(--humidity);
    }

    @media (max-width: 820px) {
      body.week15-tempandhumiditytest-v2 main {
        width: min(100% - 20px, 1120px);
        padding-top: 16px;
      }

      body.week15-tempandhumiditytest-v2 header,
      body.week15-tempandhumiditytest-v2 .dashboard {
        display: grid;
        grid-template-columns: 1fr;
      }

      body.week15-tempandhumiditytest-v2 .status {
        min-width: 0;
        text-align: left;
      }

      body.week15-tempandhumiditytest-v2 canvas {
        min-height: 310px;
        height: 430px;
      }

      body.week15-tempandhumiditytest-v2 .chart-toolbar {
        justify-content: flex-start;
      }
    }

/* vcnl4010_electron/index.html */
body.week15-vcnl4010-electron-index {
      color-scheme: light;
      --bg: #f6f7f2;
      --panel: #ffffff;
      --ink: #1d2528;
      --muted: #667174;
      --line: #d8ddd5;
      --ambient: #156b7a;
      --proximity: #b24d26;
      --good: #247a4d;
      --warn: #a15f00;
      --bad: #a73636;
    }

    body.week15-vcnl4010-electron-index * {
      box-sizing: border-box;
    }

    body.week15-vcnl4010-electron-index {
      margin: 0;
      min-height: 100vh;
      background: var(--bg);
      color: var(--ink);
      font-family: Arial, Helvetica, sans-serif;
    }

    body.week15-vcnl4010-electron-index main {
      width: min(1120px, calc(100% - 32px));
      margin: 0 auto;
      padding: 24px 0 36px;
    }

    body.week15-vcnl4010-electron-index header {
      display: flex;
      justify-content: space-between;
      gap: 16px;
      align-items: flex-end;
      margin-bottom: 18px;
    }

    body.week15-vcnl4010-electron-index h1 {
      margin: 0 0 6px;
      font-size: 28px;
      letter-spacing: 0;
    }

    body.week15-vcnl4010-electron-index .topic {
      margin: 0;
      color: var(--muted);
      font-size: 14px;
      overflow-wrap: anywhere;
    }

    body.week15-vcnl4010-electron-index .status,
    body.week15-vcnl4010-electron-index .chart-panel,
    body.week15-vcnl4010-electron-index .side-panel,
    body.week15-vcnl4010-electron-index .raw-panel,
    body.week15-vcnl4010-electron-index .reading {
      background: var(--panel);
      border: 1px solid var(--line);
      border-radius: 8px;
    }

    body.week15-vcnl4010-electron-index .status {
      flex: 0 0 auto;
      padding: 10px 12px;
      font-size: 14px;
      min-width: 230px;
      text-align: right;
    }

    body.week15-vcnl4010-electron-index .status strong {
      display: block;
      color: var(--warn);
      font-size: 13px;
      margin-bottom: 2px;
    }

    body.week15-vcnl4010-electron-index .dashboard {
      display: grid;
      grid-template-columns: minmax(0, 1fr) 300px;
      gap: 16px;
      align-items: start;
    }

    body.week15-vcnl4010-electron-index .chart-panel,
    body.week15-vcnl4010-electron-index .side-panel,
    body.week15-vcnl4010-electron-index .raw-panel {
      padding: 14px;
    }

    body.week15-vcnl4010-electron-index canvas {
      display: block;
      width: 100%;
      height: min(58vh, 520px);
      min-height: 360px;
    }

    body.week15-vcnl4010-electron-index .legend {
      display: flex;
      flex-wrap: wrap;
      gap: 12px;
      margin-top: 10px;
      color: var(--muted);
      font-size: 13px;
    }

    body.week15-vcnl4010-electron-index .legend span {
      display: inline-flex;
      align-items: center;
      gap: 6px;
    }

    body.week15-vcnl4010-electron-index .swatch {
      display: inline-block;
      width: 22px;
      height: 3px;
      border-radius: 999px;
      background: currentColor;
    }

    body.week15-vcnl4010-electron-index .ambient-key,
    body.week15-vcnl4010-electron-index .reading.ambient .value {
      color: var(--ambient);
    }

    body.week15-vcnl4010-electron-index .proximity-key,
    body.week15-vcnl4010-electron-index .reading.proximity .value {
      color: var(--proximity);
    }

    body.week15-vcnl4010-electron-index .reading-grid {
      display: grid;
      gap: 10px;
      margin-bottom: 14px;
    }

    body.week15-vcnl4010-electron-index .reading {
      padding: 12px;
    }

    body.week15-vcnl4010-electron-index .reading label {
      display: block;
      color: var(--muted);
      font-size: 13px;
      margin-bottom: 5px;
    }

    body.week15-vcnl4010-electron-index .value {
      font-size: 30px;
      line-height: 1;
      font-weight: 700;
      overflow-wrap: anywhere;
    }

    body.week15-vcnl4010-electron-index table {
      width: 100%;
      border-collapse: collapse;
      font-size: 13px;
    }

    body.week15-vcnl4010-electron-index th,
    body.week15-vcnl4010-electron-index td {
      border-top: 1px solid var(--line);
      padding: 8px 0;
      text-align: left;
      vertical-align: top;
      overflow-wrap: anywhere;
    }

    body.week15-vcnl4010-electron-index th {
      width: 34%;
      color: var(--muted);
      font-weight: 600;
      padding-right: 10px;
    }

    body.week15-vcnl4010-electron-index .raw-panel {
      margin-top: 16px;
    }

    body.week15-vcnl4010-electron-index .raw-panel h2,
    body.week15-vcnl4010-electron-index .side-panel h2 {
      margin: 0 0 10px;
      font-size: 16px;
    }

    body.week15-vcnl4010-electron-index pre {
      margin: 0;
      white-space: pre-wrap;
      overflow-wrap: anywhere;
      color: #2f3b3f;
      font-size: 13px;
    }

/* vcnl4010_ionic_visualization.html */
body.week15-vcnl4010-ionic-visualization {
      --ion-font-family: Arial, Helvetica, sans-serif;
      --ion-background-color: #f6f7f2;
      --ambient: #156b7a;
      --proximity: #b24d26;
      --line: #d8ddd5;
      --muted: #667174;
      --ink: #1d2528;
      --good: #247a4d;
      --warn: #a15f00;
      --bad: #a73636;
    }

    body.week15-vcnl4010-ionic-visualization ion-content::part(background) {
      background: #f6f7f2;
    }

    body.week15-vcnl4010-ionic-visualization .page-wrap {
      width: min(1120px, calc(100% - 32px));
      margin: 0 auto;
      padding: 24px 0 36px;
    }

    body.week15-vcnl4010-ionic-visualization .topic {
      margin: 4px 0 0;
      color: var(--muted);
      font-size: 14px;
      overflow-wrap: anywhere;
    }

    body.week15-vcnl4010-ionic-visualization .dashboard {
      display: grid;
      grid-template-columns: minmax(0, 1fr) 300px;
      gap: 16px;
      align-items: start;
    }

    body.week15-vcnl4010-ionic-visualization ion-card {
      border: 1px solid var(--line);
      border-radius: 8px;
      box-shadow: none;
      margin: 0;
    }

    body.week15-vcnl4010-ionic-visualization .status-card {
      margin-bottom: 16px;
    }

    body.week15-vcnl4010-ionic-visualization .status-line {
      display: flex;
      justify-content: space-between;
      gap: 16px;
      align-items: center;
      color: var(--muted);
      font-size: 14px;
    }

    body.week15-vcnl4010-ionic-visualization .status-line strong {
      color: var(--warn);
      font-size: 13px;
    }

    body.week15-vcnl4010-ionic-visualization .reconnect-button {
      border: 1px solid var(--line);
      background: #ffffff;
      color: var(--ink);
      border-radius: 8px;
      padding: 7px 10px;
      font: inherit;
      cursor: pointer;
    }

    body.week15-vcnl4010-ionic-visualization .reconnect-button:hover,
    body.week15-vcnl4010-ionic-visualization .reconnect-button:focus-visible {
      border-color: var(--ambient);
      outline: none;
    }

    body.week15-vcnl4010-ionic-visualization canvas {
      display: block;
      width: 100%;
      height: min(58vh, 520px);
      min-height: 360px;
    }

    body.week15-vcnl4010-ionic-visualization .legend {
      display: flex;
      flex-wrap: wrap;
      gap: 12px;
      margin-top: 10px;
      color: var(--muted);
      font-size: 13px;
    }

    body.week15-vcnl4010-ionic-visualization .legend span {
      display: inline-flex;
      align-items: center;
      gap: 6px;
    }

    body.week15-vcnl4010-ionic-visualization .swatch {
      display: inline-block;
      width: 22px;
      height: 3px;
      border-radius: 999px;
      background: currentColor;
    }

    body.week15-vcnl4010-ionic-visualization .ambient-key,
    body.week15-vcnl4010-ionic-visualization #ambientValue {
      color: var(--ambient);
    }

    body.week15-vcnl4010-ionic-visualization .proximity-key,
    body.week15-vcnl4010-ionic-visualization #proximityValue {
      color: var(--proximity);
    }

    body.week15-vcnl4010-ionic-visualization .reading-grid {
      display: grid;
      gap: 10px;
      margin-bottom: 14px;
    }

    body.week15-vcnl4010-ionic-visualization .reading {
      border: 1px solid var(--line);
      border-radius: 8px;
      padding: 12px;
    }

    body.week15-vcnl4010-ionic-visualization .reading label {
      display: block;
      color: var(--muted);
      font-size: 13px;
      margin-bottom: 5px;
    }

    body.week15-vcnl4010-ionic-visualization .value {
      font-size: 30px;
      line-height: 1;
      font-weight: 700;
      overflow-wrap: anywhere;
    }

    body.week15-vcnl4010-ionic-visualization table {
      width: 100%;
      border-collapse: collapse;
      font-size: 13px;
    }

    body.week15-vcnl4010-ionic-visualization th,
    body.week15-vcnl4010-ionic-visualization td {
      border-top: 1px solid var(--line);
      padding: 8px 0;
      text-align: left;
      vertical-align: top;
      overflow-wrap: anywhere;
    }

    body.week15-vcnl4010-ionic-visualization th {
      width: 34%;
      color: var(--muted);
      font-weight: 600;
      padding-right: 10px;
    }

    body.week15-vcnl4010-ionic-visualization pre {
      margin: 0;
      white-space: pre-wrap;
      overflow-wrap: anywhere;
      color: #2f3b3f;
      font-size: 13px;
    }

    body.week15-vcnl4010-ionic-visualization .raw-card {
      margin-top: 16px;
    }

    @media (max-width: 820px) {
      body.week15-vcnl4010-ionic-visualization .page-wrap {
        width: min(100% - 20px, 1120px);
        padding-top: 16px;
      }

      body.week15-vcnl4010-ionic-visualization .dashboard {
        grid-template-columns: 1fr;
      }

      body.week15-vcnl4010-ionic-visualization .status-line {
        align-items: flex-start;
        flex-direction: column;
      }
    }

/* vcnl4010_mqtt_visualization.html */
body.week15-vcnl4010-mqtt-visualization /*
      CSS variables used throughout the page.
      Keeping the colors here makes it easy to adjust the whole visualization.
    */
    :root {
      color-scheme: light;
      --bg: #f6f7f2;
      --panel: #ffffff;
      --ink: #1d2528;
      --muted: #667174;
      --line: #d8ddd5;
      --ambient: #156b7a;
      --proximity: #b24d26;
      --good: #247a4d;
      --warn: #a15f00;
      --bad: #a73636;
    }

    body.week15-vcnl4010-mqtt-visualization /*
      Include border and padding inside an element's declared width.
      This makes layout sizing more predictable.
    */
    * {
      box-sizing: border-box;
    }

    body.week15-vcnl4010-mqtt-visualization /* Overall page background, body.week15-vcnl4010-mqtt-visualization text color, body.week15-vcnl4010-mqtt-visualization and default font. */
    body {
      margin: 0;
      min-height: 100vh;
      background: var(--bg);
      color: var(--ink);
      font-family: Arial, Helvetica, sans-serif;
    }

    body.week15-vcnl4010-mqtt-visualization /*
      Main page wrapper.
      The width keeps the dashboard readable on large screens while still
      shrinking on smaller screens.
    */
    main {
      width: min(1120px, calc(100% - 32px));
      margin: 0 auto;
      padding: 24px 0 36px;
    }

    body.week15-vcnl4010-mqtt-visualization /*
      Header area.
      Left side shows the title/topic, body.week15-vcnl4010-mqtt-visualization right side shows live MQTT status.
    */
    header {
      display: flex;
      justify-content: space-between;
      gap: 16px;
      align-items: flex-end;
      margin-bottom: 18px;
    }

    body.week15-vcnl4010-mqtt-visualization /* Page title. */
    h1 {
      margin: 0 0 6px;
      font-size: 28px;
      font-weight: 700;
      letter-spacing: 0;
    }

    body.week15-vcnl4010-mqtt-visualization /* MQTT topic text under the title. */
    .topic {
      margin: 0;
      color: var(--muted);
      font-size: 14px;
      overflow-wrap: anywhere;
    }

    body.week15-vcnl4010-mqtt-visualization /* Small status panel that changes as MQTT connects and messages arrive. */
    .status {
      flex: 0 0 auto;
      border: 1px solid var(--line);
      background: var(--panel);
      border-radius: 8px;
      padding: 10px 12px;
      font-size: 14px;
      min-width: 230px;
      text-align: right;
    }

    body.week15-vcnl4010-mqtt-visualization /* Status label such as Connecting, body.week15-vcnl4010-mqtt-visualization Subscribed, body.week15-vcnl4010-mqtt-visualization or Message Received. */
    .status strong {
      display: block;
      color: var(--warn);
      font-size: 13px;
      margin-bottom: 2px;
    }

    body.week15-vcnl4010-mqtt-visualization .reconnect-button {
      border: 1px solid var(--line);
      background: #ffffff;
      color: var(--ink);
      border-radius: 8px;
      padding: 7px 10px;
      margin-top: 8px;
      font: inherit;
      cursor: pointer;
    }

    body.week15-vcnl4010-mqtt-visualization .reconnect-button:hover,
    body.week15-vcnl4010-mqtt-visualization .reconnect-button:focus-visible {
      border-color: var(--ambient);
      outline: none;
    }

    body.week15-vcnl4010-mqtt-visualization /*
      Two-column dashboard layout.
      The canvas takes most of the page; the latest readings sit on the right.
    */
    .dashboard {
      display: grid;
      grid-template-columns: minmax(0, 1fr) 300px;
      gap: 16px;
      align-items: start;
    }

    body.week15-vcnl4010-mqtt-visualization /* Shared panel styling for graph, body.week15-vcnl4010-mqtt-visualization side panel, body.week15-vcnl4010-mqtt-visualization and raw payload area. */
    .chart-panel,
    body.week15-vcnl4010-mqtt-visualization .side-panel,
    body.week15-vcnl4010-mqtt-visualization .raw-panel {
      background: var(--panel);
      border: 1px solid var(--line);
      border-radius: 8px;
    }

    body.week15-vcnl4010-mqtt-visualization /* Padding around the graph and legend. */
    .chart-panel {
      padding: 14px;
    }

    body.week15-vcnl4010-mqtt-visualization /*
      The canvas is the drawing surface for the live line graph.
      JavaScript later resizes its internal pixel resolution for crisp rendering.
    */
    canvas {
      display: block;
      width: 100%;
      height: min(58vh, 520px);
      min-height: 360px;
    }

    body.week15-vcnl4010-mqtt-visualization /* Right-side panel containing the current ambient/proximity readings. */
    .side-panel {
      padding: 14px;
    }

    body.week15-vcnl4010-mqtt-visualization /* Grid that stacks the latest numeric readings. */
    .reading-grid {
      display: grid;
      gap: 10px;
      margin-bottom: 14px;
    }

    body.week15-vcnl4010-mqtt-visualization /* Individual latest-value card. */
    .reading {
      border: 1px solid var(--line);
      border-radius: 8px;
      padding: 12px;
    }

    body.week15-vcnl4010-mqtt-visualization /* Label above each latest-value number. */
    .reading label {
      display: block;
      color: var(--muted);
      font-size: 13px;
      margin-bottom: 5px;
    }

    body.week15-vcnl4010-mqtt-visualization /* Large numeric value for each sensor reading. */
    .reading .value {
      font-size: 30px;
      line-height: 1;
      font-weight: 700;
      overflow-wrap: anywhere;
    }

    body.week15-vcnl4010-mqtt-visualization /* Ambient value uses the same color as the ambient graph line. */
    .reading.ambient .value {
      color: var(--ambient);
    }

    body.week15-vcnl4010-mqtt-visualization /* Proximity value uses the same color as the proximity graph line. */
    .reading.proximity .value {
      color: var(--proximity);
    }

    body.week15-vcnl4010-mqtt-visualization /* Parsed MQTT message table. */
    table {
      width: 100%;
      border-collapse: collapse;
      font-size: 13px;
    }

    body.week15-vcnl4010-mqtt-visualization /* Table cells for key/value MQTT fields. */
    th,
    body.week15-vcnl4010-mqtt-visualization td {
      border-top: 1px solid var(--line);
      padding: 8px 0;
      text-align: left;
      vertical-align: top;
      overflow-wrap: anywhere;
    }

    body.week15-vcnl4010-mqtt-visualization /* Table key column. */
    th {
      width: 34%;
      color: var(--muted);
      font-weight: 600;
      padding-right: 10px;
    }

    body.week15-vcnl4010-mqtt-visualization /* Panel underneath the dashboard that shows the untouched MQTT payload. */
    .raw-panel {
      margin-top: 16px;
      padding: 14px;
    }

    body.week15-vcnl4010-mqtt-visualization /* Section headings inside the side and raw panels. */
    .raw-panel h2,
    body.week15-vcnl4010-mqtt-visualization .side-panel h2 {
      margin: 0 0 10px;
      font-size: 16px;
    }

    body.week15-vcnl4010-mqtt-visualization /* Raw payload display. */
    pre {
      margin: 0;
      white-space: pre-wrap;
      overflow-wrap: anywhere;
      color: #2f3b3f;
      font-size: 13px;
    }

    body.week15-vcnl4010-mqtt-visualization /* Legend under the chart. */
    .legend {
      display: flex;
      flex-wrap: wrap;
      gap: 12px;
      margin-top: 10px;
      color: var(--muted);
      font-size: 13px;
    }

    body.week15-vcnl4010-mqtt-visualization /* Each legend item aligns a color swatch and label. */
    .legend span {
      display: inline-flex;
      align-items: center;
      gap: 6px;
    }

    body.week15-vcnl4010-mqtt-visualization /* Small colored line used in the legend. */
    .swatch {
      display: inline-block;
      width: 22px;
      height: 3px;
      border-radius: 999px;
      background: currentColor;
    }

    body.week15-vcnl4010-mqtt-visualization /* Legend color for ambient light. */
    .ambient-key {
      color: var(--ambient);
    }

    body.week15-vcnl4010-mqtt-visualization /* Legend color for proximity. */
    .proximity-key {
      color: var(--proximity);
    }

    body.week15-vcnl4010-mqtt-visualization /*
      Responsive layout for tablets and phones.
      The dashboard changes from two columns to a single stacked column.
    */
    @media (max-width: 820px) {
      main {
        width: min(100% - 20px, 1120px);
        padding-top: 16px;
      }

      header,
      .dashboard {
        grid-template-columns: 1fr;
        display: grid;
      }

      .status {
        text-align: left;
        min-width: 0;
      }

      canvas {
        min-height: 310px;
        height: 430px;
      }
    }

/* vcnl4010_react_visualization.html */
body.week15-vcnl4010-react-visualization {
      color-scheme: light;
      --bg: #f6f7f2;
      --panel: #ffffff;
      --ink: #1d2528;
      --muted: #667174;
      --line: #d8ddd5;
      --ambient: #156b7a;
      --proximity: #b24d26;
      --good: #247a4d;
      --warn: #a15f00;
      --bad: #a73636;
    }

    body.week15-vcnl4010-react-visualization * {
      box-sizing: border-box;
    }

    body.week15-vcnl4010-react-visualization {
      margin: 0;
      min-height: 100vh;
      background: var(--bg);
      color: var(--ink);
      font-family: Arial, Helvetica, sans-serif;
    }

    body.week15-vcnl4010-react-visualization main {
      width: min(1120px, calc(100% - 32px));
      margin: 0 auto;
      padding: 24px 0 36px;
    }

    body.week15-vcnl4010-react-visualization header {
      display: flex;
      justify-content: space-between;
      gap: 16px;
      align-items: flex-end;
      margin-bottom: 18px;
    }

    body.week15-vcnl4010-react-visualization h1 {
      margin: 0 0 6px;
      font-size: 28px;
      letter-spacing: 0;
    }

    body.week15-vcnl4010-react-visualization .topic {
      margin: 0;
      color: var(--muted);
      font-size: 14px;
      overflow-wrap: anywhere;
    }

    body.week15-vcnl4010-react-visualization .status,
    body.week15-vcnl4010-react-visualization .chart-panel,
    body.week15-vcnl4010-react-visualization .side-panel,
    body.week15-vcnl4010-react-visualization .raw-panel,
    body.week15-vcnl4010-react-visualization .reading {
      background: var(--panel);
      border: 1px solid var(--line);
      border-radius: 8px;
    }

    body.week15-vcnl4010-react-visualization .status {
      flex: 0 0 auto;
      padding: 10px 12px;
      font-size: 14px;
      min-width: 230px;
      text-align: right;
    }

    body.week15-vcnl4010-react-visualization .status strong {
      display: block;
      color: var(--warn);
      font-size: 13px;
      margin-bottom: 2px;
    }

    body.week15-vcnl4010-react-visualization .reconnect-button {
      border: 1px solid var(--line);
      background: #ffffff;
      color: var(--ink);
      border-radius: 8px;
      padding: 7px 10px;
      margin-top: 8px;
      font: inherit;
      cursor: pointer;
    }

    body.week15-vcnl4010-react-visualization .reconnect-button:hover,
    body.week15-vcnl4010-react-visualization .reconnect-button:focus-visible {
      border-color: var(--ambient);
      outline: none;
    }

    body.week15-vcnl4010-react-visualization .dashboard {
      display: grid;
      grid-template-columns: minmax(0, 1fr) 300px;
      gap: 16px;
      align-items: start;
    }

    body.week15-vcnl4010-react-visualization .chart-panel,
    body.week15-vcnl4010-react-visualization .side-panel,
    body.week15-vcnl4010-react-visualization .raw-panel {
      padding: 14px;
    }

    body.week15-vcnl4010-react-visualization canvas {
      display: block;
      width: 100%;
      height: min(58vh, 520px);
      min-height: 360px;
    }

    body.week15-vcnl4010-react-visualization .legend {
      display: flex;
      flex-wrap: wrap;
      gap: 12px;
      margin-top: 10px;
      color: var(--muted);
      font-size: 13px;
    }

    body.week15-vcnl4010-react-visualization .legend span {
      display: inline-flex;
      align-items: center;
      gap: 6px;
    }

    body.week15-vcnl4010-react-visualization .swatch {
      display: inline-block;
      width: 22px;
      height: 3px;
      border-radius: 999px;
      background: currentColor;
    }

    body.week15-vcnl4010-react-visualization .ambient-key,
    body.week15-vcnl4010-react-visualization .reading.ambient .value {
      color: var(--ambient);
    }

    body.week15-vcnl4010-react-visualization .proximity-key,
    body.week15-vcnl4010-react-visualization .reading.proximity .value {
      color: var(--proximity);
    }

    body.week15-vcnl4010-react-visualization .reading-grid {
      display: grid;
      gap: 10px;
      margin-bottom: 14px;
    }

    body.week15-vcnl4010-react-visualization .reading {
      padding: 12px;
    }

    body.week15-vcnl4010-react-visualization .reading label {
      display: block;
      color: var(--muted);
      font-size: 13px;
      margin-bottom: 5px;
    }

    body.week15-vcnl4010-react-visualization .value {
      font-size: 30px;
      line-height: 1;
      font-weight: 700;
      overflow-wrap: anywhere;
    }

    body.week15-vcnl4010-react-visualization table {
      width: 100%;
      border-collapse: collapse;
      font-size: 13px;
    }

    body.week15-vcnl4010-react-visualization th,
    body.week15-vcnl4010-react-visualization td {
      border-top: 1px solid var(--line);
      padding: 8px 0;
      text-align: left;
      vertical-align: top;
      overflow-wrap: anywhere;
    }

    body.week15-vcnl4010-react-visualization th {
      width: 34%;
      color: var(--muted);
      font-weight: 600;
      padding-right: 10px;
    }

    body.week15-vcnl4010-react-visualization .raw-panel {
      margin-top: 16px;
    }

    body.week15-vcnl4010-react-visualization .raw-panel h2,
    body.week15-vcnl4010-react-visualization .side-panel h2 {
      margin: 0 0 10px;
      font-size: 16px;
    }

    body.week15-vcnl4010-react-visualization pre {
      margin: 0;
      white-space: pre-wrap;
      overflow-wrap: anywhere;
      color: #2f3b3f;
      font-size: 13px;
    }

    @media (max-width: 820px) {
      body.week15-vcnl4010-react-visualization main {
        width: min(100% - 20px, 1120px);
        padding-top: 16px;
      }

      body.week15-vcnl4010-react-visualization header,
      body.week15-vcnl4010-react-visualization .dashboard {
        display: grid;
        grid-template-columns: 1fr;
      }

      body.week15-vcnl4010-react-visualization .status {
        min-width: 0;
        text-align: left;
      }
    }

/* vcnl4010_visualization_experiments.html */
body.week15-vcnl4010-visualization-experiments {
      color-scheme: light;
      --bg: #f6f7f2;
      --panel: #ffffff;
      --ink: #1d2528;
      --muted: #667174;
      --line: #d8ddd5;
      --ambient: #156b7a;
      --proximity: #b24d26;
      --good: #247a4d;
      --warn: #a15f00;
      --bad: #a73636;
    }

    body.week15-vcnl4010-visualization-experiments * {
      box-sizing: border-box;
    }

    body.week15-vcnl4010-visualization-experiments {
      margin: 0;
      min-height: 100vh;
      background: var(--bg);
      color: var(--ink);
      font-family: Arial, Helvetica, sans-serif;
    }

    body.week15-vcnl4010-visualization-experiments main {
      width: min(1120px, calc(100% - 32px));
      margin: 0 auto;
      padding: 24px 0 36px;
    }

    body.week15-vcnl4010-visualization-experiments header {
      display: flex;
      justify-content: space-between;
      gap: 16px;
      align-items: flex-end;
      margin-bottom: 18px;
    }

    body.week15-vcnl4010-visualization-experiments h1 {
      margin: 0 0 6px;
      font-size: 28px;
      letter-spacing: 0;
    }

    body.week15-vcnl4010-visualization-experiments .topic {
      margin: 0;
      color: var(--muted);
      font-size: 14px;
      overflow-wrap: anywhere;
    }

    body.week15-vcnl4010-visualization-experiments .status,
    body.week15-vcnl4010-visualization-experiments .panel,
    body.week15-vcnl4010-visualization-experiments .reading {
      background: var(--panel);
      border: 1px solid var(--line);
      border-radius: 8px;
    }

    body.week15-vcnl4010-visualization-experiments .status {
      flex: 0 0 auto;
      padding: 10px 12px;
      font-size: 14px;
      min-width: 250px;
      text-align: right;
    }

    body.week15-vcnl4010-visualization-experiments .status strong {
      display: block;
      color: var(--warn);
      font-size: 13px;
      margin-bottom: 2px;
    }

    body.week15-vcnl4010-visualization-experiments button {
      border: 1px solid var(--line);
      background: #ffffff;
      color: var(--ink);
      border-radius: 8px;
      padding: 8px 11px;
      font: inherit;
      cursor: pointer;
    }

    body.week15-vcnl4010-visualization-experiments button:hover,
    body.week15-vcnl4010-visualization-experiments button:focus-visible {
      border-color: var(--ambient);
      outline: none;
    }

    body.week15-vcnl4010-visualization-experiments button.active {
      background: var(--ink);
      border-color: var(--ink);
      color: #ffffff;
    }

    body.week15-vcnl4010-visualization-experiments .reconnect-button {
      display: inline-block;
      margin-top: 8px;
    }

    body.week15-vcnl4010-visualization-experiments .toolbar {
      display: flex;
      flex-wrap: wrap;
      gap: 8px;
      margin-bottom: 16px;
    }

    body.week15-vcnl4010-visualization-experiments .layout {
      display: grid;
      grid-template-columns: minmax(0, 1fr) 300px;
      gap: 16px;
      align-items: start;
    }

    body.week15-vcnl4010-visualization-experiments .panel {
      padding: 14px;
    }

    body.week15-vcnl4010-visualization-experiments canvas {
      display: block;
      width: 100%;
      height: min(58vh, 540px);
      min-height: 380px;
    }

    body.week15-vcnl4010-visualization-experiments .legend {
      display: flex;
      flex-wrap: wrap;
      gap: 12px;
      margin-top: 10px;
      color: var(--muted);
      font-size: 13px;
    }

    body.week15-vcnl4010-visualization-experiments .legend span {
      display: inline-flex;
      align-items: center;
      gap: 6px;
    }

    body.week15-vcnl4010-visualization-experiments .swatch {
      display: inline-block;
      width: 22px;
      height: 3px;
      border-radius: 999px;
      background: currentColor;
    }

    body.week15-vcnl4010-visualization-experiments .ambient-key,
    body.week15-vcnl4010-visualization-experiments .reading.ambient .value {
      color: var(--ambient);
    }

    body.week15-vcnl4010-visualization-experiments .proximity-key,
    body.week15-vcnl4010-visualization-experiments .reading.proximity .value {
      color: var(--proximity);
    }

    body.week15-vcnl4010-visualization-experiments .reading-grid {
      display: grid;
      gap: 10px;
      margin-bottom: 14px;
    }

    body.week15-vcnl4010-visualization-experiments .reading {
      padding: 12px;
    }

    body.week15-vcnl4010-visualization-experiments .reading label {
      display: block;
      color: var(--muted);
      font-size: 13px;
      margin-bottom: 5px;
    }

    body.week15-vcnl4010-visualization-experiments .value {
      font-size: 30px;
      line-height: 1;
      font-weight: 700;
      overflow-wrap: anywhere;
    }

    body.week15-vcnl4010-visualization-experiments table {
      width: 100%;
      border-collapse: collapse;
      font-size: 13px;
    }

    body.week15-vcnl4010-visualization-experiments th,
    body.week15-vcnl4010-visualization-experiments td {
      border-top: 1px solid var(--line);
      padding: 8px 0;
      text-align: left;
      vertical-align: top;
      overflow-wrap: anywhere;
    }

    body.week15-vcnl4010-visualization-experiments th {
      width: 34%;
      color: var(--muted);
      font-weight: 600;
      padding-right: 10px;
    }

    body.week15-vcnl4010-visualization-experiments .raw-panel {
      margin-top: 16px;
    }

    body.week15-vcnl4010-visualization-experiments .panel h2 {
      margin: 0 0 10px;
      font-size: 16px;
    }

    body.week15-vcnl4010-visualization-experiments pre {
      margin: 0;
      white-space: pre-wrap;
      overflow-wrap: anywhere;
      color: #2f3b3f;
      font-size: 13px;
    }

    body.week15-vcnl4010-visualization-led-control .led-control {
      border-top: 1px solid var(--line);
      margin-top: 14px;
      padding-top: 14px;
    }

    body.week15-vcnl4010-visualization-led-control .led-display {
      display: grid;
      justify-items: center;
      gap: 8px;
      margin-bottom: 12px;
    }

    body.week15-vcnl4010-visualization-led-control .led {
      width: 72px;
      height: 72px;
      border-radius: 50%;
      background: #cfd6d1;
      border: 8px solid #eef1ec;
      box-shadow: inset 0 0 18px rgba(0,0,0,0.12);
    }

    body.week15-vcnl4010-visualization-led-control .led.on {
      background: #78d889;
      border-color: #d9f5df;
      box-shadow: 0 0 28px rgba(36,122,77,0.45), inset 0 0 14px rgba(255,255,255,0.75);
    }

    body.week15-vcnl4010-visualization-led-control .led-label {
      color: var(--muted);
      font-size: 13px;
    }

    body.week15-vcnl4010-visualization-led-control .toggle-button {
      width: 100%;
      min-height: 48px;
      margin-bottom: 12px;
      color: var(--good);
    }

    body.week15-vcnl4010-visualization-led-control .toggle-button.on {
      color: var(--bad);
      border-color: rgba(36,122,77,0.45);
      background: #f3fbf5;
    }

    body.week15-vcnl4010-visualization-led-control button:disabled {
      cursor: not-allowed;
      opacity: 0.55;
    }

    body.week15-vcnl4010-visualization-led-control .log {
      min-height: 78px;
      max-height: 160px;
      overflow: auto;
      border-top: 1px solid var(--line);
      padding-top: 10px;
    }

    @media (max-width: 820px) {
      body.week15-vcnl4010-visualization-experiments main {
        width: min(100% - 20px, 1120px);
        padding-top: 16px;
      }

      body.week15-vcnl4010-visualization-experiments header,
      body.week15-vcnl4010-visualization-experiments .layout {
        display: grid;
        grid-template-columns: 1fr;
      }

      body.week15-vcnl4010-visualization-experiments .status {
        min-width: 0;
        text-align: left;
      }

      body.week15-vcnl4010-visualization-experiments canvas {
        min-height: 330px;
        height: 430px;
      }
    }

/* xiao_esp32c3_led_mqtt_control.html */
body.week15-xiao-esp32c3-led-mqtt-control {
      color-scheme: light;
      --bg: #f6f7f2;
      --panel: #ffffff;
      --ink: #1d2528;
      --muted: #667174;
      --line: #d8ddd5;
      --on: #247a4d;
      --off: #a73636;
      --accent: #156b7a;
      --warn: #a15f00;
    }

    body.week15-xiao-esp32c3-led-mqtt-control * {
      box-sizing: border-box;
    }

    body.week15-xiao-esp32c3-led-mqtt-control {
      margin: 0;
      min-height: 100vh;
      background: var(--bg);
      color: var(--ink);
      font-family: Arial, Helvetica, sans-serif;
    }

    body.week15-xiao-esp32c3-led-mqtt-control main {
      width: min(760px, calc(100% - 32px));
      margin: 0 auto;
      padding: 24px 0 36px;
    }

    body.week15-xiao-esp32c3-led-mqtt-control header {
      display: flex;
      justify-content: space-between;
      gap: 16px;
      align-items: flex-end;
      margin-bottom: 18px;
    }

    body.week15-xiao-esp32c3-led-mqtt-control h1 {
      margin: 0 0 6px;
      font-size: 28px;
      letter-spacing: 0;
    }

    body.week15-xiao-esp32c3-led-mqtt-control .topic {
      margin: 0;
      color: var(--muted);
      font-size: 14px;
      overflow-wrap: anywhere;
    }

    body.week15-xiao-esp32c3-led-mqtt-control .panel,
    body.week15-xiao-esp32c3-led-mqtt-control .status {
      background: var(--panel);
      border: 1px solid var(--line);
      border-radius: 8px;
    }

    body.week15-xiao-esp32c3-led-mqtt-control .status {
      flex: 0 0 auto;
      padding: 10px 12px;
      font-size: 14px;
      min-width: 230px;
      text-align: right;
    }

    body.week15-xiao-esp32c3-led-mqtt-control .status strong {
      display: block;
      color: var(--warn);
      font-size: 13px;
      margin-bottom: 2px;
    }

    body.week15-xiao-esp32c3-led-mqtt-control .panel {
      padding: 16px;
    }

    body.week15-xiao-esp32c3-led-mqtt-control .led-display {
      display: grid;
      place-items: center;
      gap: 12px;
      min-height: 230px;
      border: 1px solid var(--line);
      border-radius: 8px;
      margin-bottom: 16px;
      background: #fbfcf8;
    }

    body.week15-xiao-esp32c3-led-mqtt-control .led {
      width: 110px;
      height: 110px;
      border-radius: 50%;
      background: #cfd6d1;
      border: 10px solid #e9ece7;
      box-shadow: inset 0 0 18px rgba(0,0,0,0.12);
      transition: background 160ms ease, box-shadow 160ms ease, border-color 160ms ease;
    }

    body.week15-xiao-esp32c3-led-mqtt-control .led.on {
      background: #78d889;
      border-color: #d9f5df;
      box-shadow: 0 0 34px rgba(36,122,77,0.45), inset 0 0 16px rgba(255,255,255,0.75);
    }

    body.week15-xiao-esp32c3-led-mqtt-control .led-label {
      color: var(--muted);
      font-size: 14px;
    }

    body.week15-xiao-esp32c3-led-mqtt-control .controls {
      display: grid;
      gap: 10px;
      margin-bottom: 16px;
    }

    body.week15-xiao-esp32c3-led-mqtt-control button {
      border: 1px solid var(--line);
      background: #ffffff;
      color: var(--ink);
      border-radius: 8px;
      padding: 13px 14px;
      font: inherit;
      font-weight: 700;
      cursor: pointer;
    }

    body.week15-xiao-esp32c3-led-mqtt-control button:hover,
    body.week15-xiao-esp32c3-led-mqtt-control button:focus-visible {
      border-color: var(--accent);
      outline: none;
    }

    body.week15-xiao-esp32c3-led-mqtt-control button:disabled {
      cursor: not-allowed;
      opacity: 0.55;
    }

    body.week15-xiao-esp32c3-led-mqtt-control .toggle-button {
      min-height: 58px;
      color: var(--on);
    }

    body.week15-xiao-esp32c3-led-mqtt-control .toggle-button.on {
      color: var(--off);
      border-color: rgba(36,122,77,0.45);
      background: #f3fbf5;
    }

    body.week15-xiao-esp32c3-led-mqtt-control .secondary-controls {
      display: flex;
      flex-wrap: wrap;
      gap: 10px;
      margin-bottom: 16px;
    }

    body.week15-xiao-esp32c3-led-mqtt-control .log {
      margin: 0;
      min-height: 96px;
      max-height: 220px;
      overflow: auto;
      white-space: pre-wrap;
      overflow-wrap: anywhere;
      color: #2f3b3f;
      font-size: 13px;
      background: #fbfcf8;
      border: 1px solid var(--line);
      border-radius: 8px;
      padding: 12px;
    }

    @media (max-width: 680px) {
      body.week15-xiao-esp32c3-led-mqtt-control header {
        display: grid;
      }

      body.week15-xiao-esp32c3-led-mqtt-control .status {
        min-width: 0;
        text-align: left;
      }
    }
