    /* ============================================================
       Design tokens — single source of truth for colour, space,
       type, radius, shadow and motion. Promoted from values that
       were previously inlined across styles.css, app.js and admin.js
       so the whole site stays consistent from one place.
       (Phase 0 — additive only; existing rules below are unchanged.)
       ============================================================ */
    :root {
      /* surfaces + ink */
      --c-cream: #F7F4ED;
      --c-surface: #ffffff;
      --c-ink: #1A1800;
      --c-ink-strong: #1A1200;
      --c-ink-black: #1A1A1A;
      --c-muted: #8A8050;
      --c-muted-2: #6A6040;
      --c-line: #E9E2CF;
      --c-line-soft: #DDDDDD;

      /* brand accents */
      --c-yellow: #F9C33A;
      --c-red: #F47B6B;
      --c-blue: #5BBDE4;
      --c-green: #52CCA0;
      --c-green-btn: #3AAA70;
      --c-purple: #A48EF0;

      /* per-level palettes (mirror seed.js levels): col / bg / shadow1 / shadow2 */
      --lv-leik: #F9C33A;     --lv-leik-bg: #FFFAE8;   --lv-leik-s1: #D8A800;   --lv-leik-s2: #B88800;
      --lv-yngsta: #F47B6B;   --lv-yngsta-bg: #FFF0EE; --lv-yngsta-s1: #D86050; --lv-yngsta-s2: #A83020;
      --lv-midstig: #5BBDE4;  --lv-midstig-bg: #EAF6FE;--lv-midstig-s1: #3098C0;--lv-midstig-s2: #1078A0;
      --lv-efsta: #52CCA0;    --lv-efsta-bg: #E8FAF2;  --lv-efsta-s1: #28A878;  --lv-efsta-s2: #088858;
      --lv-framhald: #C77DFF; --lv-framhald-bg: #F8EAFE;--lv-framhald-s1: #9D4EDD;--lv-framhald-s2: #7B2CBF;
      --lv-isat: #FF9F45;     --lv-isat-bg: #FFF2E5;   --lv-isat-s1: #E67E22;   --lv-isat-s2: #C0590E;

      /* spacing scale (4-pt) */
      --s-1: 4px; --s-2: 8px; --s-3: 12px; --s-4: 16px; --s-5: 24px; --s-6: 32px; --s-7: 48px;

      /* radius scale */
      --r-sm: 12px; --r-md: 16px; --r-lg: 18px; --r-xl: 22px; --r-pill: 30px;

      /* shadow */
      --sh-soft: 0 2px 8px rgba(0, 0, 0, 0.06);
      --sh-card: 0 2px 10px rgba(0, 0, 0, 0.04);
      --sh-pop: 0 8px 24px rgba(0, 0, 0, 0.18);
      --sh-modal: 0 20px 60px rgba(0, 0, 0, 0.25);

      /* type scale */
      --t-xs: 11px; --t-sm: 12px; --t-base: 14px; --t-md: 15px; --t-lg: 17px;
      --t-xl: 18px; --t-2xl: 24px; --t-3xl: 28px; --t-4xl: 48px;
      --fw-bold: 700; --fw-xbold: 800; --fw-black: 900;

      /* motion */
      --ease-pop: cubic-bezier(.34, 1.56, .64, 1);
      --ease-smooth: cubic-bezier(.22, .8, .3, 1);
      --dur-fast: 0.12s; --dur-med: 0.2s; --dur-slow: 0.38s;

      /* focus ring (defined now, used from Phase 4) */
      --focus-ring: 0 0 0 3px rgba(91, 189, 228, 0.55);
    }

    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }

    body {
      font-family: 'Nunito', sans-serif;
      min-height: 100vh;
      position: relative;
      overflow-x: hidden;
      background: #F7F4ED;
    }

    .app-bg {
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      z-index: 0;
      background: #F7F4ED;
    }

    .bg-blob {
      position: absolute;
      border-radius: 50%;
      filter: blur(60px);
      opacity: 0.55;
    }

    .bb1 {
      width: 420px;
      height: 420px;
      background: #F9E87A;
      bottom: -100px;
      left: -80px;
    }

    .bb2 {
      width: 340px;
      height: 340px;
      background: #C8EAF8;
      top: -80px;
      right: -60px;
    }

    .bb3 {
      width: 220px;
      height: 220px;
      background: #B8F0D8;
      top: 40%;
      left: 20%;
      opacity: 0.35;
    }

    .bb4 {
      width: 180px;
      height: 180px;
      background: #F8C8C0;
      top: 15%;
      right: 25%;
      opacity: 0.3;
    }

    .bb5 {
      width: 140px;
      height: 140px;
      background: #D0C8F8;
      bottom: 20%;
      right: 10%;
      opacity: 0.4;
    }

    .content {
      position: relative;
      z-index: 1;
      max-width: 1100px;
      margin: 0 auto;
    }

    .page {
      display: none;
    }

    .page.active {
      display: block;
    }

    .nav {
      padding: 0.55rem 1.4rem 0.3rem;
      display: flex;
      align-items: center;
      gap: 12px;
    }

    /* Brand (logo + wordmark) vs. controls (stars/chart/auth) — two blocks so
       phones can drop the controls onto their own row instead of letting the
       shrunken wordmark text run underneath the stars pill. */
    .nav-brand {
      display: flex;
      align-items: center;
      gap: 12px;
      flex: 1;
      min-width: 0;
      cursor: pointer; /* tap logo/wordmark = go home (reliable on mobile, where the browser back button is flaky after a game opens in a new tab / inside an in-app browser) */
    }

    .nav-word {
      min-width: 0;
    }

    .nav-controls {
      display: flex;
      align-items: center;
      gap: 12px;
      flex-shrink: 0;
    }

    .logo-box {
      width: 48px;
      height: 48px;
      border-radius: 13px;
      background: white;
      border: 2px solid #E0D870;
      display: flex;
      align-items: center;
      justify-content: center;
      flex-shrink: 0;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
    }

    /* Image logo (app icon) — used in place of .logo-box */
    .logo-img {
      width: 62px;
      height: 62px;
      border-radius: 16px;
      flex-shrink: 0;
      object-fit: cover;
      display: block;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
    }

    .logo-n {
      font-size: 24px;
      font-weight: 900;
      color: #F47B6B;
      margin-right: 2px;
    }

    .logo-dots {
      display: flex;
      flex-direction: column;
      gap: 4px;
    }

    .logo-dot {
      width: 8px;
      height: 8px;
      border-radius: 50%;
    }

    .logo-name {
      font-size: 23px;
      font-weight: 900;
      color: #1A1800;
      line-height: 1.1;
    }

    .logo-tagline {
      font-size: 13px;
      font-weight: 700;
      color: #8A8050;
      line-height: 1.15;
    }

    .hero-wrap {
      /* Trim the empty band above the grid (the space around the cat) so the
         second row of level cards lifts into view on a laptop — WITHOUT
         shrinking the cards themselves. */
      padding: 0.15rem 1.4rem 0.4rem;
      display: flex;
      align-items: flex-end;
      justify-content: space-between;
      gap: 12px;

      /* How far the title and the search pill are lifted off the bottom of the
         row. The cat is the tall item, so bottom-aligning everything parked the
         search box down by the cat's FEET; the owner asked for it up at the
         cat's head. 39px puts the pill at the head exactly (head spans 96-130px
         from the top of the page, pill lands 96-131px) and carries the title up
         with it, so the two keep the bottom edge they already shared.

         Applied as margin-bottom against align-items:flex-end, so the row keeps
         its old height and nothing below it moves. Tied to .kisa-wrap's 74px
         width — resize the cat and this needs retuning. */
      --hero-lift: 39px;
    }

    .hero-big {
      /* Takes all the slack in .hero-wrap so the search pill and the cat stay
         together at the right end, instead of space-between spreading the
         three of them evenly across the row. */
      flex: 1 1 auto;
      min-width: 0;
      font-size: 24px;
      font-weight: 900;
      color: #1A1800;
      line-height: 1.1;
      margin-bottom: var(--hero-lift);
    }

    .kisa-wrap {
      flex-shrink: 0;
      width: 74px;   /* was 88 — a slightly smaller cat frees the band above the grid */
      position: relative;
      cursor: pointer;
    }

    .kisa-wrap:hover .kisa-svg {
      transform: translateY(-6px) rotate(3deg);
    }

    .kisa-wrap:hover .kisa-bubble {
      opacity: 1;
      transform: scale(1) translateY(0);
    }

    .kisa-svg {
      width: 100%;
      height: auto;
      display: block;
      transition: transform 0.3s cubic-bezier(.34, 1.56, .64, 1);
    }

    .kisa-bubble {
      position: absolute;
      top: -42px;
      right: -10px;
      background: white;
      border-radius: 14px;
      border: 2px solid #F9C33A;
      padding: 5px 9px;
      font-size: 11px;
      font-weight: 800;
      color: #8A6000;
      white-space: nowrap;
      box-shadow: 0 3px 10px rgba(0, 0, 0, 0.12);
      opacity: 0;
      transform: scale(0.7) translateY(6px);
      transition: all 0.25s cubic-bezier(.34, 1.56, .64, 1);
      pointer-events: none;
    }

    .kisa-bubble::after {
      content: '';
      position: absolute;
      bottom: -8px;
      right: 18px;
      width: 0;
      height: 0;
      border-left: 6px solid transparent;
      border-right: 6px solid transparent;
      border-top: 8px solid #F9C33A;
    }

    .grid-wrap {
      padding: 0 1rem 1.2rem;
      /* The card rows below size their ROWS from this box's width (100cqw), so
         a row can be exactly one column tall — a square. An element cannot
         query its own size, which is why the container is the wrapper and not
         the grids themselves. */
      container-type: inline-size;
    }

    /* minmax(0,1fr) — plain 1fr won't shrink below a card's min-content, so a
       long tag pill (e.g. "Íslenska sem annað tungumál") silently pushes the
       grid wider than a phone screen and the whole page scrolls sideways.

       Row height is minmax(square, auto): every card is square when the text
       fits, and when it does not — a narrow window wraps "Leikskóli og 5 ára
       bekkur" onto three lines — the whole row grows together and stays level.
       The square used to come from aspect-ratio on .ncard itself, which gave
       each card a fixed height of its own, so a card with more text simply hung
       lower than its neighbours and the row looked ragged. Sizing the ROW
       instead lets the cards stretch to match each other.
       Only the two variables change between breakpoints. */
    .top-grid,
    .bot-grid {
      --cols: 4;
      --gap: 12px;
      display: grid;
      grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
      grid-auto-rows: minmax(calc((100cqw - (var(--cols) - 1) * var(--gap)) / var(--cols)), auto);
      gap: var(--gap);
    }

    .top-grid {
      margin-bottom: 12px;
    }

    .bot-grid {
      margin-bottom: 1rem;
    }

    .ncard {
      background: white;
      border-radius: 22px;
      cursor: pointer;
      position: relative;
      padding: 1rem;
      /* No aspect-ratio here on purpose — see .top-grid. The square comes from
         the grid row now, so cards in a row always end level with each other. */
      display: flex;
      flex-direction: column;
      justify-content: flex-end;
      transition: transform 0.2s cubic-bezier(.34, 1.56, .64, 1), box-shadow 0.2s, border-color 0.2s;
    }

    .ncard.nc-leik {
      border: 2.5px solid #F9C33A;
      box-shadow: 0 2px 0 #D8A800, 0 4px 0 #B88800, 0 6px 12px rgba(180, 130, 0, 0.18);
    }

    .ncard.nc-yngsta {
      border: 2.5px solid #F47B6B;
      box-shadow: 0 2px 0 #D86050, 0 4px 0 #A83020, 0 6px 12px rgba(180, 70, 50, 0.18);
    }

    .ncard.nc-midstig {
      border: 2.5px solid #5BBDE4;
      box-shadow: 0 2px 0 #3098C0, 0 4px 0 #1078A0, 0 6px 12px rgba(20, 110, 170, 0.18);
    }

    .ncard.nc-efsta {
      border: 2.5px solid #52CCA0;
      box-shadow: 0 2px 0 #28A878, 0 4px 0 #088858, 0 6px 12px rgba(10, 130, 80, 0.18);
    }

    .ncard.nc-framhald {
      border: 2.5px solid #C77DFF;
      box-shadow: 0 2px 0 #9D4EDD, 0 4px 0 #7B2CBF, 0 6px 12px rgba(155, 79, 221, 0.18);
    }

    .ncard.nc-isat {
      border: 2.5px solid #FF9F45;
      box-shadow: 0 2px 0 #E67E22, 0 4px 0 #C0590E, 0 6px 12px rgba(230, 126, 34, 0.18);
    }

    .ncard.nc-gagna {
      border: 2.5px solid #F9C33A;
      box-shadow: 0 2px 0 #D8A800, 0 4px 0 #B88800, 0 6px 12px rgba(180, 130, 0, 0.15);
    }

    .ncard.nc-sala {
      border: 2.5px solid #A48EF0;
      box-shadow: 0 2px 0 #8870D8, 0 4px 0 #6850B8, 0 6px 12px rgba(120, 90, 200, 0.15);
    }

    .ncard.nc-leik:hover {
      transform: translateY(-8px) scale(1.02);
      box-shadow: 0 2px 0 #D8A800, 0 4px 0 #B88800, 0 6px 0 #987000, 0 12px 28px rgba(180, 130, 0, 0.28);
    }

    .ncard.nc-yngsta:hover {
      transform: translateY(-8px) scale(1.02);
      box-shadow: 0 2px 0 #D86050, 0 4px 0 #A83020, 0 6px 0 #883010, 0 12px 28px rgba(180, 70, 50, 0.28);
    }

    .ncard.nc-midstig:hover {
      transform: translateY(-8px) scale(1.02);
      box-shadow: 0 2px 0 #3098C0, 0 4px 0 #1078A0, 0 6px 0 #006890, 0 12px 28px rgba(20, 110, 170, 0.28);
    }

    .ncard.nc-efsta:hover {
      transform: translateY(-8px) scale(1.02);
      box-shadow: 0 2px 0 #28A878, 0 4px 0 #088858, 0 6px 0 #007848, 0 12px 28px rgba(10, 130, 80, 0.28);
    }

    .ncard.nc-framhald:hover {
      transform: translateY(-8px) scale(1.02);
      box-shadow: 0 2px 0 #9D4EDD, 0 4px 0 #7B2CBF, 0 6px 0 #5A189A, 0 12px 28px rgba(155, 79, 221, 0.28);
    }

    .ncard.nc-isat:hover {
      transform: translateY(-8px) scale(1.02);
      box-shadow: 0 2px 0 #E67E22, 0 4px 0 #C0590E, 0 6px 0 #8B3A00, 0 12px 28px rgba(230, 126, 34, 0.28);
    }

    .ncard.nc-gagna:hover {
      transform: translateY(-8px) scale(1.02);
      box-shadow: 0 2px 0 #D8A800, 0 4px 0 #B88800, 0 6px 0 #987000, 0 12px 28px rgba(180, 130, 0, 0.25);
    }

    .ncard.nc-sala:hover {
      transform: translateY(-8px) scale(1.02);
      box-shadow: 0 2px 0 #8870D8, 0 4px 0 #6850B8, 0 6px 0 #5840A8, 0 12px 28px rgba(120, 90, 200, 0.25);
    }

    .nc-soon-badge {
      position: absolute;
      top: 8px;
      right: 8px;
      background: #1A1A1A;
      color: white;
      padding: 3px 9px;
      border-radius: 12px;
      font-size: 11px;
      font-weight: 800;
      text-transform: uppercase;
      letter-spacing: 0.5px;
      z-index: 2;
    }

    .nc-img {
      margin-bottom: auto;
      padding-top: 0.2rem;
    }

    .nc-tag {
      display: inline-block;
      /* keep the pill content-width + left-aligned (don't stretch to fill the card) */
      align-self: flex-start;
      max-width: 100%;
      border-radius: 30px;
      padding: 4px 12px;
      font-size: 11px;
      font-weight: 800;
      margin-bottom: 6px;
      margin-top: 0.8rem;
      border: 2px solid #1A1A1A;
      background: white;
      color: #1A1A1A;
    }

    .nc-title {
      font-size: 18px;
      font-weight: 900;
      color: #1A1200;
      line-height: 1.1;
      margin-bottom: 2px;
    }

    .nc-sub {
      font-size: 11px;
      font-weight: 700;
      color: #888;
      margin-top: 2px;
    }

    .subpage {
      min-height: 100vh;
      position: relative;
      z-index: 1;
    }

    .sub-back {
      display: inline-flex;
      align-items: center;
      gap: 7px;
      font-size: 13px;
      font-weight: 800;
      cursor: pointer;
      border: 2px solid #1A1A1A;
      background: white;
      padding: 8px 16px;
      border-radius: 30px;
      font-family: 'Nunito', sans-serif;
      color: #1A1A1A;
      margin: 1rem 1.4rem;
      box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
    }

    /* Sub-page top bar: a single "Til baka" (back one step) button. It reliably
       walks back to the front page now that the history bounce is fixed. */
    .sub-nav {
      display: flex;
      align-items: center;
      gap: 10px;
      flex-wrap: wrap;
      margin: 1rem 1.4rem;
    }
    .sub-nav .sub-back {
      margin: 0;
    }

    .sub-hero {
      padding: 0 1.4rem 0.8rem;
    }

    /* Bekkur pill row: align its left edge with the heading + subject cards
       (they sit at 1.4rem; the row used to hang at 0 and looked shifted left). */
    #level-bekkur-row {
      margin: 0 1.4rem 1rem;
    }

    .sub-ey {
      font-size: 11px;
      font-weight: 800;
      letter-spacing: 1px;
      text-transform: uppercase;
      margin-bottom: 4px;
    }

    .sub-h {
      font-size: 28px;
      font-weight: 900;
      line-height: 1.05;
      margin-bottom: 5px;
    }

    .sub-p {
      font-size: 12px;
      font-weight: 700;
      color: #6A6040;
      line-height: 1.5;
    }

    .fag-scroll {
      padding: 0 1.4rem 1.4rem;
    }

    /* Subjects wrap into a responsive grid — no horizontal scrolling. */
    .fag-row {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
      gap: 12px;
    }

    .fcard {
      background: white;
      border-radius: 20px;
      cursor: pointer;
      position: relative;
      width: auto;
      padding: 1.2rem 1rem 1.1rem;
      display: flex;
      flex-direction: column;
      justify-content: flex-start;
      min-height: 150px;
      transition: transform 0.18s cubic-bezier(.34, 1.56, .64, 1), box-shadow 0.18s;
    }

    .fcard:hover {
      transform: translateY(-6px) scale(1.02);
    }

    .fc-icon {
      margin-bottom: 12px;
    }

    .fc-title {
      font-size: 17px;
      font-weight: 900;
      color: #1A1200;
      line-height: 1.2;
    }

    .gagna-list {
      padding: 0 1.4rem 1.4rem;
      display: flex;
      flex-direction: column;
      gap: 10px;
    }

    .g-item {
      background: white;
      border-radius: 16px;
      border: 2px solid #1A1A1A;
      padding: 1rem 1.2rem;
      display: flex;
      align-items: center;
      gap: 12px;
      cursor: pointer;
      transition: transform 0.15s;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
    }

    .g-item:hover {
      transform: translateY(-3px);
    }

    .g-icon {
      width: 42px;
      height: 42px;
      border-radius: 12px;
      background: #FFFAE8;
      display: flex;
      align-items: center;
      justify-content: center;
      flex-shrink: 0;
    }

    .g-text h4 {
      font-size: 14px;
      font-weight: 900;
      color: #8A6000;
    }

    .g-text p {
      font-size: 11px;
      font-weight: 700;
      color: #B09040;
    }

    /* ---- Gagnasafn folder tree (Verkefnakista) ---- */
    .g-folder-wrap {
      display: flex;
      flex-direction: column;
      gap: 10px;
    }
    .g-folder {
      cursor: pointer;
    }
    .g-folder .g-icon {
      background: #FFF3D0;
    }
    .g-caret {
      margin-left: auto;
      color: #C08000;
      font-size: 20px;
      flex-shrink: 0;
      transition: transform 0.18s ease;
    }
    .g-folder.open .g-caret {
      transform: rotate(90deg);
    }
    .g-children {
      /* JS toggles inline display none/flex; declare flex here so the
         flex-direction + gap are self-documenting and robust. */
      display: flex;
      flex-direction: column;
      gap: 10px;
      margin: 0 0 0 18px;
      padding-left: 14px;
      border-left: 2px dashed #EAD9A0;
    }
    .g-dl {
      margin-left: auto;
      color: #C08000;
      font-size: 20px;
      flex-shrink: 0;
    }
    .g-file.g-disabled {
      opacity: 0.6;
    }
    /* Arrived here from a search hit: hold the row lit long enough to find it,
       then fade back to a normal row. */
    .g-reveal {
      animation: g-reveal-flash 2.2s ease-out 1;
    }
    @keyframes g-reveal-flash {
      0%, 55% {
        background: #FFF3C9;
        border-color: #C08000;
        box-shadow: 0 0 0 4px rgba(192, 128, 0, 0.18);
      }
      100% {
        background: white;
        border-color: #1A1A1A;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
      }
    }
    @media (prefers-reduced-motion: reduce) {
      .g-reveal {
        animation: none;
        background: #FFF3C9;
        border-color: #C08000;
      }
    }
    .g-empty {
      padding: 1.2rem;
      text-align: center;
      font-size: 13px;
      font-weight: 800;
      color: #B09040;
    }

    .sala-wrap {
      padding: 0 1.4rem 1.4rem;
    }

    .ask-grid {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 12px;
      margin-bottom: 1rem;
    }

    .acrd {
      background: white;
      border-radius: 18px;
      border: 2.5px solid #1A1A1A;
      padding: 1.1rem;
      cursor: pointer;
      transition: transform 0.15s;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
    }

    .acrd:hover {
      transform: translateY(-4px);
    }

    .atag {
      display: inline-flex;
      align-items: center;
      gap: 4px;
      padding: 3px 9px;
      border-radius: 20px;
      font-size: 11px;
      font-weight: 800;
      margin-bottom: 7px;
      border: 1.8px solid #1A1A1A;
      background: white;
      color: #1A1A1A;
    }

    .acrd h3 {
      font-size: 14px;
      font-weight: 900;
      color: #1A1200;
      margin-bottom: 4px;
    }

    .acrd .pr {
      font-size: 18px;
      font-weight: 900;
      color: #2A8A50;
      margin-bottom: 2px;
    }

    .acrd .ps {
      font-size: 11px;
      font-weight: 700;
      color: #8A8060;
      margin-bottom: 7px;
    }

    .afl {
      list-style: none;
    }

    .afl li {
      font-size: 11px;
      font-weight: 700;
      color: #6A6040;
      padding: 2px 0;
      display: flex;
      align-items: center;
      gap: 5px;
    }

    .afl li i {
      font-size: 12px;
      color: #3AAA70;
    }

    .sbtns {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 10px;
      margin-top: 1rem;
    }

    .sbtn {
      padding: 12px;
      border-radius: 40px;
      border: none;
      font-size: 13px;
      font-weight: 800;
      cursor: pointer;
      font-family: 'Nunito', sans-serif;
      text-decoration: none;
      text-align: center;
      display: block;
    }

    .s1 {
      background: #1A1A1A;
      color: white;
    }

    .s2 {
      background: #3AAA70;
      color: white;
    }

    /* ============================================================
       Mosi mascot animation (round hand-built SVG)
       ------------------------------------------------------------
       .m-all carries the calm idle bob. .m-arm/.m-tail/.m-eyes are PART
       HOOKS with a transform-origin but NO default animation — they only
       move when a "Dansar" preset below targets them, so a plain cat with
       no dance equipped just bobs gently.
       ============================================================ */
    .m-all  { transform-box: fill-box; transform-origin: 50% 100%; animation: m-bob 3.2s ease-in-out infinite; }
    /* Each paw group also contains WHAT IT HOLDS (star / flower / balloon), so
       the pivot must be an absolute point in viewBox units — a percentage of
       the bounding box would drift every time the held item changes size. */
    .m-arm  { transform-box: view-box; transform-origin: 22px 72px; } /* left shoulder  */
    .m-arm2 { transform-box: view-box; transform-origin: 62px 72px; } /* right shoulder */
    .m-tail { transform-box: fill-box; transform-origin: 0% 100%; }   /* pivot = tail base */
    .m-eyes { transform-box: fill-box; transform-origin: center; }

    @keyframes m-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-3px); } }

    /* ============================================================
       Collectible characters — corner mascot as an uploaded image,
       and the picker grid on the Verðlaun page
       ============================================================ */
    /* Image-kind character in the corner: same footprint as the SVG, gentle bob. */
    .kisa-img {
      width: 88px;
      height: 110px;
      object-fit: contain;
      display: block;
      animation: m-bob 3.2s ease-in-out infinite;
      transition: transform 0.3s cubic-bezier(.34, 1.56, .64, 1);
    }
    .kisa-wrap:hover .kisa-img { transform: scale(1.06) rotate(-3deg); }

    /* Picker card figure (drawn or image), sized to the card. */
    .char-fig { height: 64px; display: flex; align-items: flex-end; justify-content: center; }
    .char-fig .char-svg { width: 52px; height: 64px; }
    .char-fig .char-img { max-width: 60px; max-height: 64px; object-fit: contain; }
    .char-fig--locked { filter: grayscale(1); }

    /* ============================================================
       Dansar — unlockable dance presets for the mascot.
       The dance class sits on the FIGURE CONTAINER (#kisa-figure, a preview
       card's .char-fig, or the floating .dance-burst-fig below); it overrides
       the calm m-bob on .m-all/.kisa-img/.char-img for as long as it's
       equipped/previewed. Locked preview cards never get a dance class in
       the first place (buildVerdlaun only adds it when unlocked), so they
       naturally keep the same grayscale-but-bobbing look as a locked
       character card — no extra rule needed. 'roleg'
       has no class — it IS the bob. Presets are code-defined (see
       DANCE_ANIMS in app.js / DANCE_PRESETS in admin.js); kept gentle —
       small distances, slow loops, ease-in-out.
       ============================================================ */
    [class*="dance-"] .kisa-img, [class*="dance-"] .char-img { transform-origin: 50% 100%; }

    /* ---- Composition modifiers (set by danceClasses() in app.js) ------------
       A dance = ONE base body move (below) + an optional ADD-ON limb move
       (.add-*), plus these modifiers. Durations are multiplied by --d-speed and
       the spin angle reads --spin-deg, so "same move, slower / twice around /
       the other way" need no new keyframes. Custom properties inherit from the
       figure container down to .m-all/.m-arm/..., which is what makes this work. */
    .dance-slow { --d-speed: 1.7; }
    .dance-fast { --d-speed: 0.6; }
    .dance-spins-2 { --spin-deg: 720deg; }
    .dance-spins-3 { --spin-deg: 1080deg; }
    .dance-spins-4 { --spin-deg: 1440deg; }

    /* hopp — a happy little bounce with a soft landing squash */
    .dance-hopp .m-all, .dance-hopp .kisa-img, .dance-hopp .char-img { animation: d-hopp calc(1.6s * var(--d-speed, 1)) ease-in-out infinite; }
    @keyframes d-hopp {
      0%, 100% { transform: translateY(0) scale(1, 1); }
      30%      { transform: translateY(-9px) scale(0.98, 1.03); }
      50%      { transform: translateY(0) scale(1.04, 0.96); }
      68%      { transform: translateY(-4px) scale(1, 1); }
      82%      { transform: translateY(0) scale(1, 1); }
    }

    /* snúningur — one graceful full turn (360°=0° keeps the loop seamless) */
    .dance-snuningur .m-all, .dance-snuningur .kisa-img, .dance-snuningur .char-img {
      animation: d-snuningur calc(3.6s * var(--d-speed, 1)) ease-in-out infinite; transform-origin: 50% 55%;
    }
    @keyframes d-snuningur {
      0%   { transform: rotate(0deg); }
      55%  { transform: rotate(var(--spin-deg, 360deg)); }
      100% { transform: rotate(var(--spin-deg, 360deg)); }
    }

    /* vagga — sways side to side on its feet, like humming a lullaby */
    .dance-vagga .m-all, .dance-vagga .kisa-img, .dance-vagga .char-img { animation: d-vagga calc(2.6s * var(--d-speed, 1)) ease-in-out infinite; transform-origin: 50% 100%; }
    @keyframes d-vagga { 0%, 100% { transform: rotate(-6deg); } 50% { transform: rotate(6deg); } }

    /* veifa — builtin cats wave the arm fast while the body keeps its calm
       bob; image characters (no separate arm) fall back to an eager rock */
    .dance-veifa .m-all { animation: m-bob calc(3.2s * var(--d-speed, 1)) ease-in-out infinite; }
    .dance-veifa .m-arm { animation: d-veifa-arm calc(0.8s * var(--d-speed, 1)) ease-in-out infinite; }
    /* -25°, not -38°: the paw now carries the star, and at 38° the star's tip
       swung ~3 units past the left edge of the 88-wide artboard and was clipped.
       Measured: 38°→3.0 clipped, 30°→0.7, 25°→0. */
    @keyframes d-veifa-arm { 0%, 100% { transform: rotate(0deg); } 50% { transform: rotate(-25deg); } }
    .dance-veifa .kisa-img, .dance-veifa .char-img { animation: d-veifa-rock calc(1.2s * var(--d-speed, 1)) ease-in-out infinite; }
    @keyframes d-veifa-rock { 0%, 100% { transform: rotate(-3deg); } 50% { transform: rotate(3deg) translateY(-2px); } }

    /* skopp — skips from foot to foot */
    .dance-skopp .m-all, .dance-skopp .kisa-img, .dance-skopp .char-img { animation: d-skopp calc(1.8s * var(--d-speed, 1)) ease-in-out infinite; transform-origin: 50% 100%; }
    @keyframes d-skopp {
      0%, 100% { transform: translate(-4px, 0) rotate(-4deg); }
      25%      { transform: translate(0, -7px) rotate(0deg); }
      50%      { transform: translate(4px, 0) rotate(4deg); }
      75%      { transform: translate(0, -7px) rotate(0deg); }
    }

    /* diskó — a gentle groove: bounce on the beat with an alternating tilt;
       builtin cats add a tail swish on the same beat */
    .dance-disko .m-all, .dance-disko .kisa-img, .dance-disko .char-img { animation: d-disko calc(1.3s * var(--d-speed, 1)) ease-in-out infinite; transform-origin: 50% 100%; }
    @keyframes d-disko {
      0%, 100% { transform: translateY(0) rotate(-5deg); }
      25%      { transform: translateY(-5px) rotate(0deg) scale(1.02); }
      50%      { transform: translateY(0) rotate(5deg); }
      75%      { transform: translateY(-5px) rotate(0deg) scale(1.02); }
    }
    .dance-disko .m-tail { animation: d-disko-tail calc(1.3s * var(--d-speed, 1)) ease-in-out infinite; }
    @keyframes d-disko-tail { 0%, 100% { transform: rotate(0deg); } 50% { transform: rotate(10deg); } }

    /* "hinn hringinn" — plays the BASE body move backwards. MUST come after
       every base move above: those use the `animation` shorthand, which resets
       animation-direction, so an earlier rule would be silently overwritten.
       Add-on limb moves keep their own direction, so a reversed spin still
       waves the right way round. */
    .dance-rev .m-all, .dance-rev .kisa-img, .dance-rev .char-img { animation-direction: reverse; }

    /* ---- ADD-ON moves (viðbót) --------------------------------------------
       These animate ONLY a limb hook (.m-arm/.m-arm2/.m-tail/.m-eyes), never
       .m-all — so they stack on top of ANY base body move without fighting it
       for the transform property. That is what makes "this one AND that one"
       combinations possible (e.g. snúningur + veifa með báðum). Builtin cats
       only: image characters have no separate limbs, so add-ons are a no-op
       for them (the base move still plays). */
    .add-veifa .m-arm  { animation: d-veifa-arm calc(0.8s * var(--d-speed, 1)) ease-in-out infinite; }
    /* veifa2 — waves with BOTH paws, mirrored so they meet in the middle */
    .add-veifa2 .m-arm  { animation: d-veifa-arm  calc(0.8s * var(--d-speed, 1)) ease-in-out infinite; }
    .add-veifa2 .m-arm2 { animation: d-veifa-arm2 calc(0.8s * var(--d-speed, 1)) ease-in-out infinite; }
    @keyframes d-veifa-arm2 { 0%, 100% { transform: rotate(0deg); } 50% { transform: rotate(25deg); } }
    /* klapp — both paws genuinely MEET in front of the chest.
       The angles are derived from the artwork, not eyeballed: the left paw
       pivots at (22,72) with a reach of ~22.4 and the right at (62,72) with a
       reach of only ~13.4, so the two arcs do NOT overlap — rotation alone can
       never make them touch (the first attempt rotated them APART, leaving a
       57px gap on an 88px-wide cat). +79° puts the left paw at ~(43,64); -80°
       puts the right at ~(51,64), and a 6px inward shift closes the last gap.
       The shift moves the right shoulder to x≈56, still buried inside the body
       (which spans x≈21–63 at that height), so the arm stays attached. */
    .add-klapp .m-arm  { animation: d-klapp-l calc(0.6s * var(--d-speed, 1)) ease-in-out infinite; }
    .add-klapp .m-arm2 { animation: d-klapp-r calc(0.6s * var(--d-speed, 1)) ease-in-out infinite; }
    @keyframes d-klapp-l { 0%, 100% { transform: rotate(0deg); }  50% { transform: rotate(79deg); } }
    @keyframes d-klapp-r { 0%, 100% { transform: rotate(0deg); }  50% { transform: translate(-6px, 0) rotate(-80deg); } }
    /* hali — tail swish on its own, usable with any body move */
    .add-hali .m-tail { animation: d-disko-tail calc(1.3s * var(--d-speed, 1)) ease-in-out infinite; }
    /* blikk — a slow blink/wink */
    .add-blikk .m-eyes { animation: d-blikk calc(3.4s * var(--d-speed, 1)) ease-in-out infinite; }
    @keyframes d-blikk { 0%, 88%, 100% { transform: scaleY(1); } 93% { transform: scaleY(0.12); } }

    /* ---- Character EFFECTS (aukahlutir → effect) ---------------------------
       Drawn inside the cat SVG by catSVG() so they follow the figure wherever
       it is used (corner mascot, reward cards, admin preview). Purely
       decorative — always aria-hidden with the rest of the SVG. */
    .m-conf-p {
      transform-box: fill-box; transform-origin: center;
      animation: m-conf-fall 2.8s linear infinite;
    }
    .m-conf-p:nth-child(2) { animation-delay: 0.5s; animation-duration: 3.2s; }
    .m-conf-p:nth-child(3) { animation-delay: 1.0s; animation-duration: 2.4s; }
    .m-conf-p:nth-child(4) { animation-delay: 1.5s; animation-duration: 3.0s; }
    .m-conf-p:nth-child(5) { animation-delay: 2.0s; animation-duration: 2.6s; }
    .m-conf-p:nth-child(6) { animation-delay: 0.8s; animation-duration: 3.4s; }
    @keyframes m-conf-fall {
      0%   { transform: translateY(-8px) rotate(0deg);   opacity: 0; }
      12%  { opacity: 1; }
      85%  { opacity: 1; }
      100% { transform: translateY(30px) rotate(240deg); opacity: 0; }
    }
    .m-spark-p {
      transform-box: fill-box; transform-origin: center;
      animation: m-spark 1.9s ease-in-out infinite;
    }
    .m-spark-p:nth-child(2) { animation-delay: 0.6s; }
    .m-spark-p:nth-child(3) { animation-delay: 1.2s; }
    .m-spark-p:nth-child(4) { animation-delay: 0.3s; }
    .m-spark-p:nth-child(5) { animation-delay: 1.5s; }
    @keyframes m-spark {
      0%, 100% { opacity: 0; transform: scale(0.3); }
      50%      { opacity: 1; transform: scale(1); }
    }

    @media (prefers-reduced-motion: reduce) {
      .kisa-img, .char-img { animation: none !important; }
    }

    /* ============================================================
       Light polish — smooth entrances & ambient motion
       (kept gentle: this is a kids' site, calm beats flashy)
       ============================================================ */
    .page.active { animation: pageIn 0.38s cubic-bezier(.22, .8, .3, 1) both; }
    @keyframes pageIn {
      from { opacity: 0; transform: translateY(10px); }
      to   { opacity: 1; transform: translateY(0); }
    }

    /* home + subject + game cards rise in with a soft stagger */
    .ncard, .fcard {
      animation: cardIn 0.45s cubic-bezier(.34, 1.56, .64, 1) both;
    }
    @keyframes cardIn {
      from { opacity: 0; transform: scale(0.92) translateY(12px); }
      to   { opacity: 1; transform: scale(1) translateY(0); }
    }
    .top-grid .ncard:nth-child(1), .bot-grid .ncard:nth-child(1), .fag-row .fcard:nth-child(1) { animation-delay: 0.02s; }
    .top-grid .ncard:nth-child(2), .bot-grid .ncard:nth-child(2), .fag-row .fcard:nth-child(2) { animation-delay: 0.08s; }
    .top-grid .ncard:nth-child(3), .bot-grid .ncard:nth-child(3), .fag-row .fcard:nth-child(3) { animation-delay: 0.14s; }
    .top-grid .ncard:nth-child(4), .bot-grid .ncard:nth-child(4), .fag-row .fcard:nth-child(4) { animation-delay: 0.20s; }
    .fag-row .fcard:nth-child(5) { animation-delay: 0.26s; }
    .fag-row .fcard:nth-child(6) { animation-delay: 0.32s; }
    .fag-row .fcard:nth-child(7) { animation-delay: 0.38s; }
    .fag-row .fcard:nth-child(8) { animation-delay: 0.44s; }

    /* tactile press feedback on the chunky buttons */
    .ncard:active, .fcard:active { transform: translateY(-2px) scale(0.99); }
    .sub-back:active, .sbtn:active { transform: scale(0.96); }
    .sub-back, .sbtn { transition: transform 0.12s ease; }

    /* ambient: the soft gradient blobs drift like slow clouds — each on a
       different path/speed so the background never looks like it's just
       sliding back and forth. Movement is large but very slow (40–70s). */
    .bb1 { animation: cloudA 46s ease-in-out infinite; }
    .bb2 { animation: cloudB 58s ease-in-out infinite; }
    .bb3 { animation: cloudC 52s ease-in-out infinite; }
    .bb4 { animation: cloudB 64s ease-in-out infinite reverse; }
    .bb5 { animation: cloudA 70s ease-in-out infinite; }
    @keyframes cloudA {
      0%   { transform: translate(0, 0) scale(1); }
      30%  { transform: translate(42px, -28px) scale(1.06); }
      62%  { transform: translate(-24px, 18px) scale(0.97); }
      100% { transform: translate(0, 0) scale(1); }
    }
    @keyframes cloudB {
      0%   { transform: translate(0, 0) scale(1); }
      34%  { transform: translate(-36px, 22px) scale(1.05); }
      68%  { transform: translate(26px, -16px) scale(0.98); }
      100% { transform: translate(0, 0) scale(1); }
    }
    @keyframes cloudC {
      0%   { transform: translate(0, 0) scale(1); }
      40%  { transform: translate(30px, 26px) scale(1.04); }
      74%  { transform: translate(-28px, -14px) scale(1.0); }
      100% { transform: translate(0, 0) scale(1); }
    }

    /* respect users who prefer less motion */
    @media (prefers-reduced-motion: reduce) {
      .m-all, .m-arm, .m-arm2, .m-eyes, .m-tail, .m-conf-p, .m-spark-p,
      .page.active, .ncard, .fcard,
      .bb1, .bb2, .bb3, .bb4, .bb5 { animation: none !important; }
    }

    /* ============================================================
       Reusable components (Phase 0)
       ------------------------------------------------------------
       Composed from JS (app.js) instead of re-inlining styles on
       every element. Per-level colour is passed in via the --lv*
       custom properties set on the element's own `style` attribute,
       so one class serves every level palette.
       ============================================================ */

    /* generic surface card */
    .u-card {
      background: var(--c-surface);
      border-radius: var(--r-lg);
      border: 2px solid var(--c-line);
      box-shadow: var(--sh-card);
    }
    /* chunky 3D card — reads --lv / --lv-s1 / --lv-s2 / --lv-soft from the element */
    .u-card--chunky {
      border-width: 2.5px;
      border-color: var(--lv, var(--c-yellow));
      box-shadow: 0 2px 0 var(--lv-s1, var(--c-yellow)),
                  0 4px 0 var(--lv-s2, var(--c-yellow)),
                  0 6px 12px var(--lv-soft, rgba(0, 0, 0, 0.12));
    }

    /* pill chip — colour comes from `color` on the element (border uses currentColor) */
    .u-chip {
      display: inline-flex;
      align-items: center;
      gap: var(--s-1);
      padding: 3px 9px;
      border-radius: var(--r-pill);
      font-size: var(--t-xs);
      font-weight: var(--fw-xbold);
      border: 1.5px solid currentColor;
      line-height: 1.2;
    }

    /* buttons */
    .u-btn {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 7px;
      padding: 12px 18px;
      border-radius: var(--r-pill);
      border: none;
      font-family: 'Nunito', sans-serif;
      font-size: 13px;
      font-weight: var(--fw-xbold);
      cursor: pointer;
      text-decoration: none;
      transition: transform var(--dur-fast) ease;
    }
    .u-btn:active { transform: scale(0.96); }
    .u-btn--primary { background: var(--c-ink-black); color: #fff; }
    .u-btn--green { background: var(--c-green-btn); color: #fff; }
    .u-btn--ghost { background: var(--c-surface); color: var(--c-ink); border: 2px solid var(--c-line); }

    /* horizontal snap rail (used by Phase 1 "continue playing") */
    .u-rail {
      display: flex;
      gap: var(--s-3);
      overflow-x: auto;
      scroll-snap-type: x mandatory;
      -webkit-overflow-scrolling: touch;
      padding-bottom: var(--s-2);
    }
    .u-rail > * { scroll-snap-align: start; flex: 0 0 auto; }

    /* progress bar */
    .u-progress {
      background: #FFFAE8;
      border-radius: var(--r-pill);
      height: 14px;
      overflow: hidden;
      border: 2px solid var(--c-yellow);
    }
    .u-progress__fill {
      height: 100%;
      background: linear-gradient(90deg, var(--c-yellow), #FFD960);
      transition: width 0.5s var(--ease-smooth);
    }

    .u-section-title {
      font-size: var(--t-base);
      font-weight: var(--fw-xbold);
      color: #5A5030;
      text-transform: uppercase;
      letter-spacing: 0.5px;
      margin-bottom: 10px;
    }

    /* ---- shared game tile (faithful port of buildGameTile's inline styles) ---- */
    .game-tile {
      background: var(--c-surface);
      border-radius: var(--r-lg);
      border: 2.5px solid var(--lv, var(--c-yellow));
      box-shadow: 0 2px 0 var(--lv-s1), 0 4px 0 var(--lv-s2), 0 6px 12px var(--lv-soft);
      cursor: pointer;
      transition: transform 0.18s var(--ease-pop);
      overflow: hidden;
      display: flex;
      flex-direction: column;
      position: relative;
    }
    .game-tile:hover { transform: translateY(-6px) scale(1.02); }
    .game-tile__thumb {
      width: 100%;
      aspect-ratio: 16 / 10;
      border-bottom: 2.5px solid var(--lv);
      background-position: center;
      background-size: cover;
      background-repeat: no-repeat;
    }
    .game-tile__thumb--placeholder { display: flex; align-items: center; justify-content: center; }
    .game-tile__body { padding: 0.9rem 1rem 1rem; }
    .game-tile__badge {
      position: absolute;
      top: 10px;
      left: 10px;
      z-index: 2;
      color: #fff;
      font-size: var(--t-xs);
      font-weight: var(--fw-xbold);
      padding: 3px 9px;
      border-radius: var(--r-pill);
    }
    .game-tile__play {
      display: inline-flex;
      align-items: center;
      gap: 5px;
      background: var(--lv-bg);
      border: 1.5px solid var(--lv);
      color: var(--lv);
      font-size: 11px;
      font-weight: var(--fw-xbold);
      padding: 3px 9px;
      border-radius: var(--r-pill);
      margin-bottom: 6px;
    }
    .game-tile__title {
      font-size: var(--t-md);
      font-weight: var(--fw-black);
      color: var(--c-ink-strong);
      line-height: 1.2;
    }

    /* ============================================================
       Site footer — contact email + legal links (terms / privacy).
       ============================================================ */
    .site-footer {
      text-align: center;
      padding: 1.6rem 1.4rem 2.6rem;
      border-top: 1px solid var(--c-line);
      margin-top: 0.4rem;
    }
    .site-footer__inner {
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      align-items: center;
      gap: 8px;
      margin-bottom: 6px;
      font-size: 13px;
      font-weight: 800;
    }
    .site-footer a {
      color: #6A6040;
      text-decoration: none;
      border-bottom: 1.5px solid transparent;
      /* 13px text alone gives a 19px-tall target; pad it past the 24px a
         finger needs. The flex gap above absorbs the extra height. */
      display: inline-block;
      padding: 4px 2px;
    }
    .site-footer a:hover {
      color: #1A1200;
      border-bottom-color: currentColor;
    }
    .site-footer__sep { color: #C9C0A0; }
    .site-footer__copy {
      font-size: 11px;
      font-weight: 700;
      color: #A89C70;
    }
    /* "Um okkur" trigger — a button styled exactly like the footer anchors. */
    .site-footer__linkbtn {
      font-family: inherit;
      font-size: inherit;
      font-weight: inherit;
      color: #6A6040;
      background: none;
      border: none;
      display: inline-block;
      padding: 4px 2px;   /* matches .site-footer a — clears the 24px tap floor */
      cursor: pointer;
      border-bottom: 1.5px solid transparent;
    }
    .site-footer__linkbtn:hover {
      color: #1A1200;
      border-bottom-color: currentColor;
    }

    /* ============================================================
       "Um okkur" popup — same visual language as the auth modal
       (blurred backdrop, ink-bordered card, rotating close ×).
       Body text is owner-editable (admin → Textar, key about_us).
       ============================================================ */
    .about-ov {
      position: fixed;
      inset: 0;
      z-index: 9000;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 18px;
      background: rgba(26, 24, 0, 0.45);
      backdrop-filter: blur(3px);
      -webkit-backdrop-filter: blur(3px);
      animation: aboutFade .18s ease both;
    }
    .about-ov[hidden] { display: none; }
    @keyframes aboutFade { from { opacity: 0; } to { opacity: 1; } }
    @keyframes aboutPop {
      from { opacity: 0; transform: translateY(14px) scale(.97); }
      to { opacity: 1; transform: none; }
    }
    .about-card {
      background: var(--c-surface);
      border-radius: 24px;
      width: 100%;
      max-width: 430px;
      border: 2.5px solid var(--c-ink-black);
      position: relative;
      box-shadow: 0 4px 0 #D8A800, 0 10px 30px rgba(40, 30, 0, 0.28);
      padding: 1.6rem 1.5rem 1.5rem;
      max-height: calc(100vh - 36px);
      overflow-y: auto;
      animation: aboutPop .22s cubic-bezier(.34, 1.56, .64, 1) both;
    }
    .about-x {
      position: absolute;
      top: 12px;
      right: 12px;
      width: 34px;
      height: 34px;
      border-radius: 50%;
      border: 2px solid var(--c-ink-black);
      background: var(--c-surface);
      cursor: pointer;
      font-size: 18px;
      line-height: 1;
      color: var(--c-ink-black);
      display: flex;
      align-items: center;
      justify-content: center;
      font-weight: 900;
      transition: transform .15s, background .15s;
    }
    .about-x:hover {
      transform: rotate(90deg);
      background: var(--c-cream);
    }
    .about-badge {
      width: 54px;
      height: 54px;
      border-radius: 16px;
      background: var(--c-cream);
      border: 2.5px solid var(--c-yellow);
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 26px;
      margin-bottom: 12px;
    }
    .about-h {
      font-size: 22px;
      font-weight: 900;
      color: var(--c-ink-black);
      line-height: 1.1;
      margin-bottom: 10px;
    }
    .about-body {
      font-size: 14px;
      font-weight: 700;
      color: var(--c-muted-2);
      line-height: 1.65;
      white-space: pre-line; /* keep Maria's line breaks from the admin textarea */
    }
    @media (max-width: 420px) {
      .about-card { padding: 1.4rem 1.2rem 1.3rem; }
    }

    /* ============================================================
       Star-earn feedback — shown after playing ANY game (built-in or
       Genially), so it's always clear that play earns stars.
       ============================================================ */
    @keyframes starsPop {
      0%, 100% { transform: scale(1); }
      35% { transform: scale(1.28); }
    }
    .stars-pop { animation: starsPop 0.5s cubic-bezier(.34, 1.56, .64, 1); }

    .star-toast {
      position: fixed;
      left: 50%;
      bottom: 28px;
      transform: translate(-50%, 20px);
      background: #1A1A1A;
      color: #fff;
      font-weight: 900;
      font-size: 15px;
      padding: 12px 20px;
      border-radius: 30px;
      box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
      z-index: 9999;
      opacity: 0;
      transition: opacity 0.25s ease, transform 0.25s cubic-bezier(.34, 1.56, .64, 1);
      pointer-events: none;
    }
    .star-toast.show { opacity: 1; transform: translate(-50%, 0); }

    /* ---- In-page game player (app.js → openGameOverlay) ---------------------
       Our own games play here instead of taking over a new browser tab, so the
       address bar keeps saying namsheimar.is (no "blob:…" URL on show) and an
       iPad doesn't collect a tab per game. z-index sits just under .star-toast
       (9999) and .milestone-overlay (10000) so a reward earned mid-game still
       lands on top of the game. ---- */
    .game-ov {
      position: fixed;
      inset: 0;
      z-index: 9998;
      display: flex;
      flex-direction: column;
      background: #17130A;
      /* Keep a game's own buttons clear of the iPhone home indicator. */
      padding-bottom: env(safe-area-inset-bottom);
    }
    .game-ov__bar {
      flex: 0 0 auto;
      display: flex;
      align-items: center;
      gap: 8px;
      padding: 8px 10px;
      background: #1A1A1A;
      color: #fff;
      padding-top: calc(8px + env(safe-area-inset-top));
    }
    .game-ov__title {
      flex: 1 1 auto;
      min-width: 0;
      font-size: 14px;
      font-weight: 900;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .game-ov__btn {
      flex: 0 0 auto;
      display: flex;
      align-items: center;
      gap: 6px;
      border: 2px solid rgba(255, 255, 255, 0.28);
      background: rgba(255, 255, 255, 0.08);
      color: #fff;
      font-family: inherit;
      font-size: 12.5px;
      font-weight: 800;
      padding: 7px 13px;
      border-radius: 24px;
      cursor: pointer;
      transition: background 0.15s ease, border-color 0.15s ease;
    }
    .game-ov__btn i { font-size: 16px; }
    .game-ov__btn:hover { background: rgba(255, 255, 255, 0.18); border-color: rgba(255, 255, 255, 0.5); }
    .game-ov__btn--x { background: #F47B6B; border-color: #D8503E; }
    .game-ov__btn--x:hover { background: #FF8E7E; border-color: #F47B6B; }
    .game-ov__stage { flex: 1 1 auto; position: relative; background: #fff; }
    .game-ov__frame { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block; }
    .game-ov__note {
      position: absolute;
      inset: 0;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: 14px;
      padding: 1.4rem;
      text-align: center;
      font-size: 14px;
      font-weight: 800;
      color: #8A8050;
      background: #FFFDF5;
    }
    .game-ov__spinner {
      width: 34px;
      height: 34px;
      border-radius: 50%;
      border: 4px solid #F0E6C8;
      border-top-color: #F9C33A;
      animation: gameOvSpin 0.9s linear infinite;
    }
    @keyframes gameOvSpin { to { transform: rotate(360deg); } }
    .game-ov__retry {
      border: 2px solid #F9C33A;
      background: #F9C33A;
      color: #1A1200;
      font-family: inherit;
      font-size: 13px;
      font-weight: 900;
      padding: 9px 20px;
      border-radius: 24px;
      box-shadow: 0 2px 0 #D8A800;
      cursor: pointer;
    }
    .game-ov__retry:active { transform: translateY(2px); box-shadow: none; }
    /* Nothing behind the player should scroll while a game is on screen. */
    body.game-ov-open { overflow: hidden; }
    /* The cookie banner is a fixed z-index:9999 element (js/cookies.js) and would
       otherwise sit across the bottom of a running game. Park it while a game is
       open; it comes straight back — still undismissed — when the game closes. */
    body.game-ov-open #nh-cookie-banner { display: none; }
    /* Same treatment for the promo strip — nothing but the game while a game runs. */
    body.game-ov-open #promo-root { display: none; }

    @media (max-width: 560px) {
      .game-ov__btn span { display: none; }
      .game-ov__btn { padding: 7px 10px; }
    }

    /* ---- Dance burst — the mascot performing its equipped dance for a
       reward moment (game finished, milestone dismissed, dance equipped),
       floating in a fixed corner so it's visible no matter which page is
       showing underneath (the big hero cat only exists on Home). ---- */
    .dance-burst {
      position: fixed;
      right: 18px;
      bottom: 18px;
      z-index: 9997;
      opacity: 0;
      transform: translateY(16px) scale(0.85);
      transition: opacity 0.3s ease, transform 0.3s cubic-bezier(.34, 1.56, .64, 1);
      pointer-events: none;
    }
    .dance-burst.show { opacity: 1; transform: translateY(0) scale(1); }
    .dance-burst-fig { filter: drop-shadow(0 6px 12px rgba(0, 0, 0, 0.22)); }

    /* ---- Milestone celebration (áfangi reached) — full-screen overlay with
       falling confetti; stays until the child taps "Áfram!" because the game
       finished in another tab and they may come back to this one later. ---- */
    .milestone-overlay {
      position: fixed;
      inset: 0;
      z-index: 10000;
      display: flex;
      align-items: center;
      justify-content: center;
      background: rgba(26, 18, 0, 0.55);
      opacity: 0;
      transition: opacity 0.3s ease;
      overflow: hidden;
      padding: 20px;
    }
    .milestone-overlay.mo-show { opacity: 1; }
    .mo-box {
      background: white;
      border-radius: 24px;
      border: 3px solid #F9C33A;
      box-shadow: 0 4px 0 #D8A800, 0 8px 0 #B88800, 0 18px 40px rgba(0, 0, 0, 0.35);
      padding: 2rem 2.2rem;
      text-align: center;
      max-width: 420px;
      width: 100%;
      max-height: 85vh;
      overflow-y: auto;
      transform: scale(0.8);
      transition: transform 0.35s cubic-bezier(.34, 1.56, .64, 1);
    }
    .milestone-overlay.mo-show .mo-box { transform: scale(1); }
    .mo-title { font-size: 26px; font-weight: 900; color: #5A3A00; margin-bottom: 12px; }
    .mo-card { background: #FFFAE8; border: 2px solid #F9C33A; border-radius: 16px; padding: 0.9rem; margin-bottom: 10px; }
    .mo-emoji { font-size: 52px; line-height: 1.1; }
    .mo-name { font-size: 17px; font-weight: 900; color: #1A1200; margin-top: 4px; }
    .mo-line { font-size: 13px; font-weight: 700; color: #8A6000; margin-top: 2px; }
    .mo-line b { color: #28A878; }
    .mo-total { font-size: 15px; font-weight: 900; color: #28A878; margin: 10px 0 4px; }
    .mo-btn {
      margin-top: 10px;
      background: linear-gradient(180deg, #FFD960, #F9C33A);
      border: none;
      border-radius: 30px;
      padding: 12px 34px;
      font-size: 16px;
      font-weight: 900;
      color: #5A3A00;
      cursor: pointer;
      box-shadow: 0 3px 0 #D8A800;
      font-family: inherit;
    }
    .mo-btn:active { transform: translateY(2px); box-shadow: 0 1px 0 #D8A800; }
    .mo-confetti {
      position: absolute;
      top: -20px;
      width: 10px;
      height: 16px;
      border-radius: 3px;
      animation: moConfettiFall 3s linear infinite;
    }
    @keyframes moConfettiFall {
      0%   { transform: translateY(0) rotate(0deg); opacity: 1; }
      100% { transform: translateY(108vh) rotate(540deg); opacity: 0.7; }
    }

    /* ---- Framvinda → "Leikirnir þínir" (app.js → fwGamesSection) -------------
       A ranked bar chart of the games this child has actually played, so a
       parent sees WHICH games and how many times — not just a total. ---- */
    .fw-card {
      background: #fff;
      border: 2.5px solid #5BBDE4;
      border-radius: 18px;
      box-shadow: 0 2px 0 #3098C0, 0 4px 0 #1078A0, 0 6px 12px rgba(20, 110, 170, 0.15);
      padding: 1.3rem 1.2rem;
      margin-top: 1rem;
    }
    .fw-title {
      display: flex;
      align-items: center;
      gap: 8px;
      font-size: 15px;
      font-weight: 900;
      color: #0A3A5A;
    }
    .fw-title i { font-size: 19px; color: #3098C0; }
    .fw-sub { font-size: 12px; font-weight: 700; color: #6A8090; margin-top: 3px; }
    .fw-summary {
      display: flex;
      flex-wrap: wrap;
      gap: 8px;
      margin: 12px 0 4px;
    }
    .fw-summary span {
      background: #EAF6FC;
      border: 1.5px solid #C8EAF8;
      border-radius: 20px;
      padding: 5px 12px;
      font-size: 12px;
      font-weight: 700;
      color: #0A3A5A;
    }
    .fw-summary b { font-weight: 900; }
    .fw-rows { margin-top: 12px; }
    .fw-row {
      display: flex;
      align-items: center;
      gap: 11px;
      padding: 9px 0;
      border-top: 1.5px solid #F0F4F6;
    }
    .fw-row:first-child { border-top: 0; }
    .fw-rank {
      flex: 0 0 auto;
      width: 26px;
      height: 26px;
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      color: #fff;
      font-size: 12px;
      font-weight: 900;
    }
    .fw-main { flex: 1 1 auto; min-width: 0; }
    .fw-name {
      font-size: 13.5px;
      font-weight: 800;
      color: #1A1A1A;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .fw-track {
      margin-top: 5px;
      height: 9px;
      border-radius: 6px;
      background: #F0F4F6;
      overflow: hidden;
    }
    .fw-track > span {
      display: block;
      height: 100%;
      border-radius: 6px;
      transition: width 0.6s cubic-bezier(.22, 1, .36, 1);
    }
    .fw-meta { font-size: 11px; font-weight: 700; color: #8898A4; margin-top: 4px; }
    .fw-count { flex: 0 0 auto; min-width: 46px; text-align: right; }
    .fw-count b { display: block; font-size: 19px; font-weight: 900; color: #1A1A1A; line-height: 1.1; }
    .fw-count span { font-size: 11px; font-weight: 800; color: #8898A4; text-transform: uppercase; letter-spacing: 0.3px; }
    .fw-more {
      display: block;
      width: 100%;
      margin-top: 12px;
      background: none;
      border: 2px solid #C8EAF8;
      color: #2090C0;
      font-family: inherit;
      font-size: 12.5px;
      font-weight: 800;
      padding: 9px 14px;
      border-radius: 22px;
      cursor: pointer;
      transition: background 0.15s ease;
    }
    .fw-more:hover { background: #EAF6FC; }
    .fw-empty {
      margin-top: 12px;
      background: #FFFDF5;
      border: 2px dashed #C8EAF8;
      border-radius: 14px;
      padding: 1.2rem 1rem;
      text-align: center;
      font-size: 13px;
      font-weight: 700;
      color: #6A8090;
    }

    @media (prefers-reduced-motion: reduce) {
      .stars-pop { animation: none; }
      .star-toast { transition: opacity 0.2s ease; }
      .dance-burst { transition: opacity 0.2s ease; }
      .mo-confetti { display: none; }
      .mo-box { transition: none; }
      .milestone-overlay { transition: opacity 0.2s ease; }
      .fw-track > span { transition: none; }
      .game-ov__spinner { animation: none; }
    }

    /* ============================================================
       Responsive — phones & small tablets.
       The home card grids are the main thing that must reflow (they
       were locked to 4 fixed columns); most subpage grids already use
       auto-fill so they adapt on their own. We step the home grids
       down 4 → 3 → 2 columns and tighten nav / hero spacing so nothing
       overflows the viewport on a phone.
       ============================================================ */

    /* NOTE: there is deliberately NO 3-across step between the 4 across above
       and the 2 across below. Both rows on the forsíða hold exactly four cards,
       so 4 and 2 columns both come out even, while 3 leaves the fourth card
       stranded on a line of its own with a two-column hole beside it — which is
       exactly what a laptop window ~700-860px wide used to look like. Four
       across still reads fine down to 600px; below that the cards would be too
       narrow for the titles, so it halves straight to 2. */

    /* Phones: 2 across, and a more compact header */
    @media (max-width: 600px) {
      .nav {
        flex-wrap: wrap;
        gap: 8px 10px;
        padding: 0.8rem 1rem 0.4rem;
      }
      /* brand takes the whole first row; stars/chart/login drop to a second
         row — never squeezed under/over the wordmark like before. Controls
         span the full row and wrap internally (the admin trio Stjórnborð +
         nafn + útskráning is wider than a phone), instead of clipping. */
      .nav-brand {
        flex: 1 1 100%;
      }
      .nav-controls {
        flex: 1 1 100%;
        flex-wrap: wrap;
        gap: 8px 10px;
      }
      .logo-img {
        width: 52px;
        height: 52px;
        border-radius: 14px;
      }
      .logo-name {
        font-size: 20px;
      }

      .hero-wrap {
        padding: 0.4rem 1rem 0.8rem;
      }
      .hero-big {
        font-size: 20px;
      }

      .grid-wrap {
        padding: 0 0.8rem 1.2rem;
      }
      .top-grid,
      .bot-grid {
        --cols: 2;
        --gap: 10px;
      }

      /* roomier tap targets + readable copy on the subpages */
      .sub-h {
        font-size: 24px;
      }
      .sub-back {
        margin: 0.8rem 1rem;
      }
      .sub-nav {
        margin: 0.8rem 1rem;
      }
      #level-bekkur-row {
        margin: 0 1rem 0.9rem;
      }
      .fag-scroll,
      .gagna-list {
        padding: 0 1rem 1.4rem;
      }
      .sub-hero {
        padding: 0 1rem 0.8rem;
      }
    }

    /* Narrow phones: keep 2 across but tighten the gutter further */
    @media (max-width: 380px) {
      .top-grid,
      .bot-grid {
        --gap: 8px;
      }
      .nc-title {
        font-size: 16px;
      }
    }



    /* ============================================================
       Leikjaleit — find a game by name from the home hero.
       One pill sitting immediately left of the cat (the owner picked
       that spot), styled like the nav pills: white, 2px border, hard
       2px drop shadow. The results panel is absolutely positioned, so
       typing never pushes the level grid down the page.
       ============================================================ */
    .gsearch {
      position: relative;
      flex: 0 1 250px;
      min-width: 0;
      margin-bottom: var(--hero-lift);
      display: flex;
      align-items: center;
      gap: 6px;
      background: white;
      border: 2px solid #5BBDE4;
      border-radius: 30px;
      /* The vertical padding lives on .gsearch__input instead, so tapping
         anywhere in the pill lands on the input rather than on dead padding.
         The 5px there replaces the 6px here — the pill's height is unchanged,
         which matters because it is aligned to the cat's head. */
      padding: 1px 12px;
      box-shadow: 0 2px 0 #3098C0;
    }

    .gsearch__icon {
      flex-shrink: 0;
      font-size: 17px;
      color: #5BBDE4;
    }

    .gsearch__input {
      flex: 1 1 auto;
      min-width: 0;
      border: 0;
      outline: none;
      background: transparent;
      font-family: inherit;
      font-size: 14px;
      font-weight: 800;
      color: #1A1800;
      padding: 5px 0;   /* see .gsearch — makes the whole pill a tap target */
    }

    .gsearch__input::placeholder {
      color: #8A8050;
      font-weight: 700;
    }

    .gsearch__clear {
      flex-shrink: 0;
      display: flex;
      align-items: center;
      border: 0;
      padding: 0;
      background: transparent;
      font-size: 15px;
      color: #8A8050;
      cursor: pointer;
    }

    /* display:flex above would otherwise beat the UA's [hidden] rule and leave
       the × showing on an empty box. */
    .gsearch__clear[hidden] {
      display: none;
    }

    .gsearch__clear:hover {
      color: #1A1800;
    }

    /* Anchored under the pill. z-index clears the level cards but stays below
       the game player (9998) and the cookie banner (9999). */
    .gsearch__panel {
      position: absolute;
      top: calc(100% + 8px);
      right: 0;
      width: min(340px, 84vw);
      max-height: 336px;
      overflow-y: auto;
      -webkit-overflow-scrolling: touch;
      padding: 6px;
      background: white;
      border: 2px solid #5BBDE4;
      border-radius: 18px;
      box-shadow: 0 10px 26px rgba(0, 0, 0, 0.16);
      z-index: 9000;
      text-align: left;
    }

    .gsearch__item {
      display: flex;
      align-items: center;
      gap: 10px;
      width: 100%;
      padding: 7px 9px;
      border: 0;
      border-radius: 14px;
      background: transparent;
      font-family: inherit;
      text-align: left;
      cursor: pointer;
    }

    .gsearch__item:hover,
    .gsearch__item.is-active {
      background: #EAF7FD;
    }

    .gsearch__thumb {
      flex-shrink: 0;
      width: 42px;
      height: 42px;
      border-radius: 12px;
      background-position: center;
      background-size: cover;
      display: flex;
      align-items: center;
      justify-content: center;
    }

    .gsearch__name {
      font-size: 13.5px;
      font-weight: 800;
      color: #1A1800;
      line-height: 1.2;
      overflow-wrap: anywhere;
    }

    .gsearch__meta {
      margin-top: 2px;
      font-size: 11.5px;
      font-weight: 700;
      color: #8A8050;
      line-height: 1.2;
    }

    .gsearch__empty {
      padding: 14px 10px;
      text-align: center;
      font-size: 12.5px;
      font-weight: 800;
      color: #8A8050;
    }

    /* "Leikir" / "Gagnasafn" divider — only drawn when the panel holds both. */
    .gsearch__section {
      padding: 8px 10px 4px;
      font-size: 10.5px;
      font-weight: 900;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      color: #A89A62;
    }

    .gsearch__section + .gsearch__item {
      margin-top: 0;
    }

    /* Phones: the title, the cat and a usable search box do not fit on one row,
       so the search gets its own full-width row underneath.

       This has to be a grid, not flex-wrap: flex breaks lines on each item's
       CONTENT width (before any shrinking), so the title alone is wide enough
       to push the cat onto a second row. Grid keeps the original title/cat
       pairing and lets the title wrap inside its own column instead. */
    @media (max-width: 600px) {
      .hero-wrap {
        display: grid;
        grid-template-columns: 1fr auto;
        grid-template-areas:
          "title cat"
          "search search";
        align-items: end;

        /* No lift down here: the search has its own row under the cat, so
           there is no "cat's head" to line up with. */
        --hero-lift: 0px;
      }
      .hero-big {
        grid-area: title;
      }
      .kisa-wrap {
        grid-area: cat;
      }
      .gsearch {
        grid-area: search;
      }
      .gsearch__panel {
        left: 0;
        right: 0;
        width: auto;
      }
    }

    /* ============================================================
       Auglýsingaborði — the owner-controlled promo strip.
       One pill, centred, sitting under the logo on Home and under the
       "Til baka" row on subpages (app.js placePromo moves the node).
       Driven entirely by admin → Textar → Borði; an empty banner_text
       leaves #promo-root [hidden], so this costs zero vertical space
       when she has nothing to advertise.
       ============================================================ */
    .promo-wrap {
      display: flex;
      justify-content: center;
      padding: 2px 1.4rem 0;
    }
    .promo {
      display: inline-flex;
      align-items: center;
      gap: 12px;
      max-width: 760px;
      background: var(--c-surface, #fff);
      border: 3px solid #F9C33A;
      border-radius: 30px;
      box-shadow: 0 3px 0 #D8A800;
      padding: 9px 16px 9px 20px;
    }
    .promo__icon {
      font-size: 18px;
      line-height: 1;
      flex: 0 0 auto;
    }
    .promo__text {
      font-weight: 900;
      font-size: 15px;
      color: #5A3A00;
    }
    .promo__cta {
      flex: 0 0 auto;
      background: #F9C33A;
      border: 2px solid #D8A800;
      border-radius: 20px;
      padding: 5px 14px;
      font-family: inherit;
      font-weight: 900;
      font-size: 13px;
      color: #5A3A00;
      box-shadow: 0 2px 0 #D8A800;
      cursor: pointer;
      white-space: nowrap;
    }
    .promo__cta:active { transform: translateY(2px); box-shadow: none; }
    .promo__x {
      flex: 0 0 auto;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      background: none;
      border: 0;
      /* The glyph is only 13x19px, well under the 24px minimum a finger needs.
         The box is padded out to 28x28 and the negative vertical margin absorbs
         the extra height, so the banner pill stays exactly as tall as before. */
      min-width: 28px;
      min-height: 28px;
      margin-top: -5px;
      margin-bottom: -5px;
      padding: 0;
      font-family: inherit;
      font-size: 19px;
      font-weight: 900;
      line-height: 1;
      color: #B89000;
      cursor: pointer;
    }
    .promo__x:hover { color: #8A6000; }

    /* Phones: let the text wrap and centre everything rather than
       squeezing the pill off-screen. */
    @media (max-width: 640px) {
      .promo-wrap { padding: 2px 1rem 0; }
      .promo {
        flex-wrap: wrap;
        justify-content: center;
        gap: 8px;
        padding: 9px 14px;
        border-radius: 22px;
      }
      .promo__text {
        font-size: 14px;
        text-align: center;
        flex: 1 1 100%;
        order: 2;
      }
      .promo__icon { order: 1; }
      .promo__x { order: 1; margin-left: auto; }
      .promo__cta { order: 3; }
    }
