/* =============================================================================
   Mensa Auslastungsanzeige — Widget Stylesheet
   Served from: GET /v1/widget/styles.css
   -----------------------------------------------------------------------------
   Self-contained. No JavaScript, no external fonts, no network dependencies.
   Safe to embed inside a maxmanager iframe.

   This file is a pure DESIGN SHELL. ALL VISIBLE TEXT comes from the
   /v1/forecast payload — the provider renders strings into the classes below
   and never authors copy. The CSS only paints what is injected; it makes no
   assumptions about slot count, time range, or wording (so the widget is
   fully localisable from the API alone). Title/subtitle/tip-wording/footnote
   are NOT part of the API contract — the provider owns those static strings
   itself (see "EXPECTED MARKUP" below).

   -----------------------------------------------------------------------------
   API PAYLOAD  (GET /v1/forecast)  —  real example, every string is content
   -----------------------------------------------------------------------------

   {
     "generated_at": "2026-06-28T12:34:39Z",
     "workspaces": [
       {
         "workspace_id": "4f60e1aa-ecee-4ccb-9f45-1a03d5890828",

         "forecast": [
           {
             "date": "2026-06-29",
             "tip": { "from": "11:30", "to": "11:45" },
             "slots": [
               { "time": "11:00", "level": "low",    "label": "entspannt geniessen" },
               { "time": "11:15", "level": "low",    "label": "entspannt geniessen" },
               { "time": "11:45", "level": "high",   "label": "Gut besucht" },
               { "time": "12:30", "level": "medium", "label": "Guter Zeitpunkt" }
             ]
           }
         ]
       }
     ]
   }

   `workspace_id`s and the workspace ↔ display-name mapping come from a
   separate call, GET /v1/workspaces (also X-API-Key auth):
   {"workspaces":[{"workspace_id":"4f60e1aa-…","name":"Mensa Bispinghof"}, …]}

   `tip` is OPTIONAL per day — omit the whole <aside class="occupancy-tip">
   block when a day's `tip` is absent. `level` is always one of
   `low` / `medium` / `high` (enforced server-side); `label` is plain text
   chosen by the API per level, never composed client-side.

   -----------------------------------------------------------------------------
   HOW THE PROVIDER ASSEMBLES THE TIP  (own copy, API only supplies from/to)
   -----------------------------------------------------------------------------
     1. range = provider's own "{from} und {to} Uhr" template with {from}/{to}
                filled from forecast[].tip, wrapped:
                <span class="occupancy-tip__time">…</span>
                (this span is what gets the blue highlight — presentation only)
     2. lead  = provider's own "Meist ist es zwischen {range} am
                entspanntesten." sentence, prefixed with <b>Tipp:</b>
     3. footnote = provider's own static text, e.g. "Die Anzeige wird
                regelmäßig aktualisiert."
   The whole tip block is rendered ONLY when forecast[].tip is present.

   -----------------------------------------------------------------------------
   EXPECTED MARKUP  (backend renders one .occupancy-slot per slots[] entry)
   -----------------------------------------------------------------------------

   <section class="occupancy-widget" role="region"
            aria-label="Aktuelle Auslastung der Mensa">

     <header class="occupancy-header">
       <h2 class="occupancy-title">Erwartete Auslastung Heute Mittag</h2>
       <p class="occupancy-subtitle">So voll wird es heute – finde die beste Zeit für deinen Besuch.</p>
     </header>

     <div class="occupancy-timeline" role="list">

       <!-- repeat for each entry in forecast[].slots[] — NO per-slot label -->
       <div class="occupancy-slot level-{slot.level}" role="listitem">
         <div class="occupancy-time">{slot.time}</div>
         <div class="occupancy-rail" aria-hidden="true"></div>
         <div class="occupancy-face" aria-hidden="true">
           {face-svg for slot.level — see reference SVGs at bottom of file}
         </div>
       </div>

     </div>

     <!-- Shared legend
          One static item per level; the provider owns this copy. -->
     <div class="occupancy-legend" aria-label="Legende">
       <div class="occupancy-legend__item level-low">
         <span class="occupancy-legend__face">{face-svg low}</span>
         <span class="occupancy-legend__text">entspannt geniessen</span>
       </div>
       <div class="occupancy-legend__item level-medium">
         <span class="occupancy-legend__face">{face-svg medium}</span>
         <span class="occupancy-legend__text">Guter Zeitpunkt</span>
       </div>
       <div class="occupancy-legend__item level-high">
         <span class="occupancy-legend__face">{face-svg high}</span>
         <span class="occupancy-legend__text">Gut besucht</span>
       </div>
     </div>

     <!-- render only if forecast[].tip is present -->
     <aside class="occupancy-tip" aria-label="Tipp">
       <div class="occupancy-tip__icon">{calendar-svg}</div>
       <div class="occupancy-tip__body">
         <!-- assembled per the rules above; {range} → styled time span -->
         <p class="occupancy-tip__lead">
           <b>Tipp:</b> Meist ist es zwischen
           <span class="occupancy-tip__time">11:30 und 11:45 Uhr</span>
           am entspanntesten.
         </p>
         <p class="occupancy-tip__sub">Die Anzeige wird regelmäßig aktualisiert.</p>
       </div>
       <div class="occupancy-tip__icon occupancy-tip__icon--right">{cutlery-svg}</div>
     </aside>

     <!-- subtle Better Bites branding, bottom-right (static, provider-owned) -->
     <div class="occupancy-brand" aria-label="Bereitgestellt von Better Bites">
       <span>powered by</span>
       <img src="https://www.better-bites.de/assets/images/bb_logo_main.svg"
            alt="Better Bites" />
     </div>

   </section>

   -----------------------------------------------------------------------------
   LEVEL → CLASS MAPPING  (set "level-{slot.level}" on each .occupancy-slot)
   -----------------------------------------------------------------------------
     The per-slot label is no longer rendered; level meaning is shown once
     in the shared .occupancy-legend (Option A). Legend copy below.

     forecast level   CSS class        colour   legend label (.occupancy-legend)
     "low"            .level-low       grün     "entspannt geniessen"
     "medium"         .level-medium    blau     "Guter Zeitpunkt"
     "high"           .level-high      orange   "Gut besucht"
     "peak"           .level-peak      rot      "Sehr voll"   (defensive only —
                       the API's Level type is currently low/medium/high and
                       never emits "peak"; kept as a non-breaking fallback)
   ============================================================================= */


/* ---- design tokens -------------------------------------------------------- */
.occupancy-widget {
  --o-text:        #0b1730;
  --o-text-mute:   #6b7790;
  --o-rule:        #c8cdd8;
  --o-dot:         #98a0b3;
  --o-bg:          #ffffff;

  --o-low:         #2f8a3e;   /* grün */
  --o-low-bg:      #e6f3e7;

  --o-amber:       #e89a13;   /* amber — used by level-high */
  --o-amber-bg:    #fdf1d8;

  --o-blue:        #2563d9;   /* blau — used by level-medium */
  --o-blue-bg:     #e5edfb;

  --o-peak:        #c8362e;   /* rot — defensive fallback */
  --o-peak-bg:     #fbe4e2;

  --o-tip-bg:      #eef2fb;
  --o-tip-fg:      var(--o-blue);

  --o-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue",
            Helvetica, Arial, "Liberation Sans", sans-serif;
}


/* ---- shell ---------------------------------------------------------------- */
.occupancy-widget,
.occupancy-widget * { box-sizing: border-box; }

.occupancy-widget {
  font-family: var(--o-font);
  color: var(--o-text);
  background: var(--o-bg);
  width: 100%;
  max-width: 1100px;
  margin: 0 auto;
  padding: clamp(20px, 3vw, 36px) clamp(16px, 3vw, 40px);
  line-height: 1.3;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}


/* ---- header --------------------------------------------------------------- */
.occupancy-header {
  text-align: center;
  margin-bottom: clamp(20px, 3vw, 32px);
}
.occupancy-title {
  margin: 0 0 .35em;
  font-size: clamp(26px, 4.2vw, 50px);
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--o-text);
  text-wrap: balance;
}
.occupancy-subtitle {
  margin: 0;
  color: var(--o-text-mute);
  font-size: clamp(14px, 1.5vw, 19px);
  font-weight: 500;
}


/* ---- timeline ------------------------------------------------------------- */
.occupancy-timeline {
  --slot-min: 55px;
  position: relative;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(var(--slot-min), 1fr); /* Slots teilen sich die Breite
                                   gleichmäßig und schrumpfen bis --slot-min; erst
                                   darunter (Anzahl x --slot-min > Breite) wird
                                   horizontal gescrollt. */
  justify-content: safe center; /* nur wirksam, wenn die Slots (bei Überlauf) nicht
                                   mehr die volle Breite füllen */
  gap: 0;
  padding: 0 0 8px;
  overflow-x: auto;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
}
.occupancy-timeline::-webkit-scrollbar { height: 6px; }
.occupancy-timeline::-webkit-scrollbar-thumb {
  background: var(--o-rule);
  border-radius: 3px;
}

/* Each slot is its own 4-row grid. Because the time / rail / face rows hold
   identical-height content across every slot, they line up automatically in
   ALL browsers — no subgrid required. The only variable-height row is the
   label, which sits LAST (below the faces) and reserves two lines (see
   .occupancy-label min-height) so a 1- vs 2-line label never shifts the face.
   Browsers that support subgrid get an even tighter alignment via the
   @supports block further down. */
.occupancy-slot {
  display: grid;
  grid-template-rows: auto auto auto;
  row-gap: 12px;
  align-items: center;
  justify-items: center;
  text-align: center;
  padding: 0 6px;
  position: relative;
}

/* time label */
.occupancy-time {
  font-size: clamp(14px, 1.4vw, 18px);
  font-weight: 700;
  color: var(--o-text);
  letter-spacing: -0.01em;
  white-space: nowrap;
}

/* connector rail + dot — adapts to any number of slots */
.occupancy-rail {
  width: 100%;
  height: 14px;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}
.occupancy-rail::before {
  content: "";
  position: absolute;
  inset: 50% -6px auto -6px;   /* je 6px über den Slot-Rand, um die Lücke
                                  der Slot-Innenabstände zu überbrücken ->
                                  durchgehende Linie zwischen allen Slots */
  height: 2px;
  background: var(--o-rule);
  transform: translateY(-50%);
}
.occupancy-slot:first-child .occupancy-rail::before { left: 50%; }
.occupancy-slot:last-child  .occupancy-rail::before { right: 50%; }
.occupancy-rail::after {
  content: "";
  position: relative;
  width: 10px;
  height: 10px;
  border-radius: 999px;
  background: var(--o-dot);
  z-index: 1;
}

/* face bubble — smaller than before (was clamp(56px, 7vw, 88px)) */
.occupancy-face {
  width: 44px; /* feste Icon-Größe: nur der Abstand (Spaltenbreite via 1fr) variiert */
  aspect-ratio: 1;
  border-radius: 999px;
  display: grid;
  place-items: center;
}
/* Fallback for browsers without aspect-ratio (pre-2021): mirror the width. */
@supports not (aspect-ratio: 1) {
  .occupancy-face { height: 44px; }
}
.occupancy-face svg {
  width: 78%;
  height: 78%;
  display: block;
}

/* ---- progressive enhancement: subgrid -----------------------------------
   Chrome 117+ / Safari 16+ / Firefox 71+. Where available, hang each slot's
   rows off the timeline's shared tracks for pixel-exact alignment. */
@supports (grid-template-rows: subgrid) {
  .occupancy-timeline { grid-template-rows: auto auto auto; }
  .occupancy-slot {
    grid-row: 1 / -1;
    grid-template-rows: subgrid;
  }
}


/* ---- level → colour mapping ---------------------------------------------- */
.level-low    .occupancy-face  { background: var(--o-low-bg);   color: var(--o-low); }
.level-medium .occupancy-face  { background: var(--o-blue-bg);  color: var(--o-blue); }
.level-high   .occupancy-face  { background: var(--o-amber-bg); color: var(--o-amber); }

/* Defensive: the /v1/forecast contract can emit level "peak". If you are
   certain "peak" is never returned, you may delete this block and the two
   --o-peak* tokens above. Leaving it in keeps the widget from rendering an
   unstyled (white) bubble should "peak" ever appear. */
.level-peak   .occupancy-face  { background: var(--o-peak-bg);   color: var(--o-peak); }


/* ---- legend (Option A: chips below the timeline) -------------------------
   Replaces the repeated per-slot labels with one static chip per level.
   Markup: .occupancy-legend > .occupancy-legend__item.level-{level}
             > .occupancy-legend__face (face-svg) + .occupancy-legend__text */
.occupancy-legend {
  margin-top: clamp(16px, 2.4vw, 24px);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px 14px;
}
.occupancy-legend__item {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 6px 14px 6px 6px;
  border: 1px solid var(--o-rule);
  border-radius: 999px;
  background: #fff;
}
.occupancy-legend__face {
  width: 30px;
  height: 30px;
  border-radius: 999px;
  display: grid;
  place-items: center;
  flex: none;
}
.occupancy-legend__face svg { width: 78%; height: 78%; display: block; }
.occupancy-legend__text {
  font-weight: 700;
  font-size: clamp(13px, 1.35vw, 16px);
}

.occupancy-legend__item.level-low    .occupancy-legend__face { background: var(--o-low-bg);   color: var(--o-low); }
.occupancy-legend__item.level-low    .occupancy-legend__text { color: var(--o-low); }
.occupancy-legend__item.level-medium .occupancy-legend__face { background: var(--o-blue-bg);  color: var(--o-blue); }
.occupancy-legend__item.level-medium .occupancy-legend__text { color: var(--o-blue); }
.occupancy-legend__item.level-high   .occupancy-legend__face { background: var(--o-amber-bg); color: var(--o-amber); }
.occupancy-legend__item.level-high   .occupancy-legend__text { color: var(--o-amber); }


/* ---- tip banner ----------------------------------------------------------- */
.occupancy-tip {
  margin-top: clamp(20px, 3vw, 32px);
  background: var(--o-tip-bg);
  border-radius: 14px;
  padding: clamp(16px, 2vw, 22px) clamp(18px, 2.4vw, 28px);
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: clamp(14px, 2vw, 24px);
}
.occupancy-tip__icon {
  width: clamp(34px, 3.5vw, 44px);
  height: clamp(34px, 3.5vw, 44px);
  color: var(--o-tip-fg);
  flex: none;
}
.occupancy-tip__icon svg { width: 100%; height: 100%; display: block; }

.occupancy-tip__body { min-width: 0; }
.occupancy-tip__lead {
  margin: 0;
  font-size: clamp(15px, 1.55vw, 20px);
  color: var(--o-text);
  font-weight: 500;
  line-height: 1.35;
}
.occupancy-tip__lead b { font-weight: 700; }
.occupancy-tip__time {
  color: var(--o-tip-fg);
  font-weight: 700;
  white-space: nowrap;
}
.occupancy-tip__sub {
  margin: .35em 0 0;
  font-size: clamp(13px, 1.25vw, 16px);
  color: var(--o-text-mute);
  font-weight: 500;
}


/* ---- branding (subtle, bottom-right) ------------------------------------- */
.occupancy-brand {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 7px;
  margin-top: 14px;
  opacity: .5;
}
.occupancy-brand span {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .02em;
  color: var(--o-text-mute);
  text-transform: lowercase;
}
.occupancy-brand img {
  height: 18px;
  width: auto;
  display: block;
}


/* ---- responsive ----------------------------------------------------------- */
@media (max-width: 720px) {
  .occupancy-timeline { --slot-min: 55px; }
  .occupancy-slot { row-gap: 10px; }
  .occupancy-tip { grid-template-columns: auto 1fr; }
  .occupancy-tip__icon--right { display: none; }
}
@media (max-width: 480px) {
  .occupancy-time  { font-size: 14px; }

  /* Vertikale Liste – "farbige Zeilen":
     jeder Slot wird eine eingefärbte Zeile  ->  Zeit links | Smiley rechts.
     Keine Verbindungslinie; die Level-Farbe der Zeile trägt die Bedeutung.
     Markup bleibt unverändert. */
  .occupancy-timeline {
    display: flex;
    flex-direction: column;
    gap: 7px;
    overflow-x: visible;          /* kein horizontales Scrollen mehr */
    padding-bottom: 0;
  }
  .occupancy-slot {
    grid-template-rows: none;
    grid-template-columns: auto 1fr auto;   /* Zeit | Abstand | Smiley */
    column-gap: 10px;
    row-gap: 0;
    align-items: center;
    padding: 4px 12px;        /* flachere Zeile (~2/3 Höhe) */
    border-radius: 9px;
  }
  .occupancy-time { justify-self: start; text-align: left; }
  .occupancy-rail { display: none; }        /* keine Verbindungslinie */
  .occupancy-face {
    justify-self: end;
    width: 28px;              /* kleinerer Smiley für flachere Zeile */
  }

  /* Zeilen-Hintergrund + Zeit-Farbe pro Level (Smiley-Tönung kommt schon
     aus den globalen .level-* Regeln und verschmilzt mit der Zeile). */
  .occupancy-slot.level-low    { background: var(--o-low-bg); }
  .occupancy-slot.level-low    .occupancy-time { color: var(--o-low); }
  .occupancy-slot.level-medium { background: var(--o-blue-bg); }
  .occupancy-slot.level-medium .occupancy-time { color: var(--o-blue); }
  .occupancy-slot.level-high   { background: var(--o-amber-bg); }
  .occupancy-slot.level-high   .occupancy-time { color: var(--o-amber); }
  .occupancy-slot.level-peak   { background: var(--o-peak-bg); }
  .occupancy-slot.level-peak   .occupancy-time { color: var(--o-peak); }

  /* Legende links bündig statt zentriert */
  .occupancy-legend { justify-content: flex-start; }
}


/* =============================================================================
   REFERENCE FACE SVGs  (the backend embeds one per slot, chosen by level)
   Use stroke="currentColor" so the face inherits the level colour set above.
   -----------------------------------------------------------------------------

   low  — wink + smile:
     <svg viewBox="0 0 64 64" fill="none" stroke="currentColor" stroke-width="4"
          stroke-linecap="round" stroke-linejoin="round">
       <path d="M20 26 q4 -4 8 0"/>
       <circle cx="42" cy="26" r="2.4" fill="currentColor" stroke="none"/>
       <path d="M20 40 q12 12 24 0"/>
     </svg>

   medium — friendly smile:
     <svg viewBox="0 0 64 64" fill="none" stroke="currentColor" stroke-width="4"
          stroke-linecap="round" stroke-linejoin="round">
       <circle cx="22" cy="26" r="2.4" fill="currentColor" stroke="none"/>
       <circle cx="42" cy="26" r="2.4" fill="currentColor" stroke="none"/>
       <path d="M20 38 q12 12 24 0"/>
     </svg>

   high — neutral:
     <svg viewBox="0 0 64 64" fill="none" stroke="currentColor" stroke-width="4"
          stroke-linecap="round">
       <circle cx="22" cy="26" r="2.4" fill="currentColor" stroke="none"/>
       <circle cx="42" cy="26" r="2.4" fill="currentColor" stroke="none"/>
       <line x1="22" y1="42" x2="42" y2="42"/>
     </svg>

   peak — concerned / frown (defensive, see note above):
     <svg viewBox="0 0 64 64" fill="none" stroke="currentColor" stroke-width="4"
          stroke-linecap="round" stroke-linejoin="round">
       <circle cx="22" cy="26" r="2.4" fill="currentColor" stroke="none"/>
       <circle cx="42" cy="26" r="2.4" fill="currentColor" stroke="none"/>
       <path d="M20 44 q12 -12 24 0"/>
     </svg>
   ============================================================================= */
