Dette er kildekoden til et Android Studio-prosjekt.
Hver fil er separert med overskrifter.


============================================================
FILSTI: build.gradle.kts
============================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    alias(libs.plugins.android.application) apply false
    // NY LINJE: Legg til Google Services plugin her
    id("com.google.gms.google-services") version "4.4.2" apply false
}


============================================================
FILSTI: settings.gradle.kts
============================================================
pluginManagement {
    repositories {
        google {
            content {
                includeGroupByRegex("com\\.android.*")
                includeGroupByRegex("com\\.google.*")
                includeGroupByRegex("androidx.*")
            }
        }
        mavenCentral()
        gradlePluginPortal()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

rootProject.name = "KBS Intranett"
include(":app")
 

============================================================
FILSTI: app\build.gradle.kts
============================================================
import java.util.Properties
import java.io.FileInputStream

plugins {
    alias(libs.plugins.android.application)
    // NY LINJE: Aktiver Google Services plugin her
    id("com.google.gms.google-services")
}

// --- NY KODE: Last inn local.properties ---
// Vi bruker "Properties()" direkte siden vi har importert den på toppen
val localProperties = Properties()
val localPropertiesFile = rootProject.file("local.properties")
if (localPropertiesFile.exists()) {
    localProperties.load(FileInputStream(localPropertiesFile))
}
// ------------------------------------------

android {
    namespace = "com.kbs.kbsintranett"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.kbs.kbsintranett"
        minSdk = 28
        targetSdk = 34
        versionCode = 4
        versionName = "1.5.1"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

        // Hent verdien vi lastet inn på toppen av filen
        val webClientId = localProperties.getProperty("WEB_CLIENT_ID") ?: ""

        // Opprett BuildConfig-feltet.
        // Vi legger på ekstra hermetegn (\") for at det skal bli en String i Java-koden.
        buildConfigField("String", "WEB_CLIENT_ID", "\"$webClientId\"")
    }

    // NYTT: Dette må til for å kunne bruke BuildConfig.DEBUG i koden
    buildFeatures {
        buildConfig = true
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
}

dependencies {
    implementation(libs.appcompat)
    implementation(libs.material)
    implementation(libs.activity)
    implementation(libs.constraintlayout)
    testImplementation(libs.junit)
    androidTestImplementation(libs.ext.junit)
    androidTestImplementation(libs.espresso.core)

    // Nettverk og JSON-håndtering
    implementation("com.squareup.retrofit2:retrofit:2.9.0")
    implementation("com.squareup.retrofit2:converter-gson:2.9.0")
    implementation("com.google.code.gson:gson:2.10.1")

    // Navigation Component
    val navVersion = "2.8.5"
    implementation("androidx.navigation:navigation-fragment:$navVersion")
    implementation("androidx.navigation:navigation-ui:$navVersion")

    implementation("com.google.android.gms:play-services-auth:20.7.0")
    implementation("com.github.bumptech.glide:glide:4.16.0")
    implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")

    implementation("androidx.work:work-runtime:2.9.0")

    // Swipe Refresh Layout
    implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")

    // NY LINJE: Firebase BOM (Bill of Materials) styrer versjoner
    implementation(platform("com.google.firebase:firebase-bom:33.1.2"))

    // NY LINJE: (Valgfritt, men lurt for statistikk)
    implementation("com.google.firebase:firebase-analytics")

    // NYTT: Firebase Cloud Messaging lagt til her
    implementation("com.google.firebase:firebase-messaging")
}

============================================================
FILSTI: app\proguard-rules.pro
============================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

============================================================
FILSTI: app\src\androidTest\java\com\kbs\kbsintranett\ExampleInstrumentedTest.java
============================================================
package com.kbs.kbsintranett;

import android.content.Context;

import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.*;

/**
 * Instrumented test, which will execute on an Android device.
 *
 * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
 */
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
    @Test
    public void useAppContext() {
        // Context of the app under test.
        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
        assertEquals("com.kbs.kbsintranett", appContext.getPackageName());
    }
}

============================================================
FILSTI: app\src\main\AndroidManifest.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" android:maxSdkVersion="32" />
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" android:required="false" />
    <uses-permission android:name="android.permission.READ_CALENDAR" />

    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
    <uses-permission android:name="android.permission.USE_EXACT_ALARM" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <application
        android:name=".KbsApplication"
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.KBSIntranett"
        android:usesCleartextTraffic="true"
        tools:targetApi="31">

        <activity
            android:name=".MainActivity"
            android:configChanges="orientation|screenSize|keyboardHidden"
            android:exported="true"
            android:theme="@style/Theme.KBSIntranett">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".WebViewActivity" />

        <receiver
            android:name=".AlarmReceiver"
            android:enabled="true"
            android:exported="false" />

        <!-- NYTT: Registrering av Firebase Messaging Service -->
        <service
            android:name=".MyFirebaseMessagingService"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>

        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.kbs.kbsintranett.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

        <!-- Standard ikon for Firebase varsler -->
        <meta-data
            android:name="com.google.firebase.messaging.default_notification_icon"
            android:resource="@drawable/ic_stat_kbs" />

        <!-- Standard farge for Firebase varsler (KBS Blå) -->
        <meta-data
            android:name="com.google.firebase.messaging.default_notification_color"
            android:resource="@color/kbs_logo_blue" />

    </application>

</manifest>

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\AddTaskBottomSheet.java
============================================================
package com.kbs.kbsintranett;

import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class AddTaskBottomSheet extends BottomSheetDialogFragment {

    private EditText etTitle, etDesc;
    private Button btnDate, btnUsers, btnSave, btnClearDate; // NYTT
    private TextView txtSheetTitle, txtDatePreview, txtUsersPreview;

    private Calendar dueDate = Calendar.getInstance();
    private boolean hasDate = true; // NYTT

    private List<User> filteredUsers = new ArrayList<>();
    private List<User> selectedUsers = new ArrayList<>();
    private TaskItem taskToEdit = null;

    public interface OnTaskAddedListener {
        void onTaskAdded(TaskItem task);
        void onTaskUpdated(TaskItem task);
    }

    private OnTaskAddedListener listener;

    public void setOnTaskAddedListener(OnTaskAddedListener listener) {
        this.listener = listener;
    }

    public void setTaskToEdit(TaskItem task) {
        this.taskToEdit = task;
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        BottomSheetDialog dialog = (BottomSheetDialog) super.onCreateDialog(savedInstanceState);
        dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
        dialog.setOnShowListener(dialogInterface -> {
            BottomSheetDialog d = (BottomSheetDialog) dialogInterface;
            View bottomSheet = d.findViewById(com.google.android.material.R.id.design_bottom_sheet);
            if (bottomSheet != null) {
                BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
            }
        });
        return dialog;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.bottom_sheet_add_task, container, false);

        txtSheetTitle = v.findViewById(R.id.txt_sheet_title);
        etTitle = v.findViewById(R.id.et_task_title);
        etDesc = v.findViewById(R.id.et_task_desc);
        btnDate = v.findViewById(R.id.btn_task_date);
        btnClearDate = v.findViewById(R.id.btn_clear_date); // NYTT
        btnUsers = v.findViewById(R.id.btn_task_users);
        btnSave = v.findViewById(R.id.btn_save_task);
        txtDatePreview = v.findViewById(R.id.txt_date_preview);
        txtUsersPreview = v.findViewById(R.id.txt_users_preview);

        if (taskToEdit != null) {
            txtSheetTitle.setText("Rediger Oppgave");
            etTitle.setText(taskToEdit.getTitle());
            etDesc.setText(taskToEdit.getDescription());

            if (taskToEdit.getDueDate() > 0) {
                dueDate.setTimeInMillis(taskToEdit.getDueDate());
                hasDate = true;
            } else {
                hasDate = false;
            }
            btnSave.setText("Oppdater Oppgave");
        } else {
            dueDate.add(Calendar.DAY_OF_MONTH, 1);
            hasDate = true;
        }

        updateDatePreview();

        btnDate.setOnClickListener(view -> {
            new DatePickerDialog(getContext(), (d, y, m, day) -> {
                dueDate.set(y, m, day);
                hasDate = true;
                updateDatePreview();
            }, dueDate.get(Calendar.YEAR), dueDate.get(Calendar.MONTH), dueDate.get(Calendar.DAY_OF_MONTH)).show();
        });

        // NYTT: Knapp for å fjerne frist
        btnClearDate.setOnClickListener(v1 -> {
            hasDate = false;
            updateDatePreview();
        });

        btnUsers.setOnClickListener(view -> showUserSelectionDialog());
        btnSave.setOnClickListener(view -> saveTask());

        fetchAndFilterUsers();

        return v;
    }

    private void fetchAndFilterUsers() {
        RetrofitClient.getApiService().getUsersList().enqueue(new Callback<List<User>>() {
            @Override
            public void onResponse(Call<List<User>> call, Response<List<User>> response) {
                if (response.isSuccessful() && response.body() != null) {
                    filteredUsers = UserFilterHelper.getFilteredUsers(response.body());

                    if (taskToEdit != null) {
                        selectedUsers.clear();
                        Map<String, Boolean> currentAssignees = taskToEdit.getAssigneeStatus();
                        for (User u : filteredUsers) {
                            if (currentAssignees.containsKey(u.getEmail())) {
                                selectedUsers.add(u);
                            }
                        }
                        updateUsersPreview();
                    }
                }
            }
            @Override
            public void onFailure(Call<List<User>> call, Throwable t) {}
        });
    }

    private void showUserSelectionDialog() {
        if (filteredUsers.isEmpty()) {
            Toast.makeText(getContext(), "Henter tilgjengelige personer...", Toast.LENGTH_SHORT).show();
            return;
        }
        String[] names = new String[filteredUsers.size()];
        boolean[] checked = new boolean[filteredUsers.size()];
        for (int i = 0; i < filteredUsers.size(); i++) {
            names[i] = filteredUsers.get(i).getName();
            boolean isSelected = false;
            for(User su : selectedUsers) {
                if(su.getEmail().equalsIgnoreCase(filteredUsers.get(i).getEmail())) {
                    isSelected = true;
                    break;
                }
            }
            checked[i] = isSelected;
        }
        new AlertDialog.Builder(getContext())
                .setTitle("Tildel til...")
                .setMultiChoiceItems(names, checked, (dialog, which, isChecked) -> {
                    User user = filteredUsers.get(which);
                    if (isChecked) {
                        selectedUsers.add(user);
                    } else {
                        selectedUsers.removeIf(u -> u.getEmail().equalsIgnoreCase(user.getEmail()));
                    }
                })
                .setPositiveButton("OK", (dialog, which) -> updateUsersPreview())
                .show();
    }

    private void updateUsersPreview() {
        if (selectedUsers.isEmpty()) txtUsersPreview.setText("Kun meg");
        else if (selectedUsers.size() == 1) txtUsersPreview.setText(selectedUsers.get(0).getName());
        else txtUsersPreview.setText(selectedUsers.size() + " personer valgt");
    }

    private void updateDatePreview() {
        if (hasDate) {
            SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault());
            txtDatePreview.setText("Frist: " + sdf.format(dueDate.getTime()));
            btnClearDate.setVisibility(View.VISIBLE);
        } else {
            txtDatePreview.setText("Ingen frist");
            btnClearDate.setVisibility(View.GONE);
        }
    }

    private void saveTask() {
        String title = etTitle.getText().toString().trim();
        if (title.isEmpty()) {
            etTitle.setError("Mangler tittel");
            return;
        }

        long finalDueDate = hasDate ? dueDate.getTimeInMillis() : 0;

        if (taskToEdit != null) {
            Map<String, Boolean> oldStatus = taskToEdit.getAssigneeStatus();
            taskToEdit.getAssigneeStatus().clear();

            if (selectedUsers.isEmpty()) {
                String myEmail = UserManager.getInstance().getUserEmail();
                taskToEdit.addAssignee(myEmail);
                if (oldStatus.containsKey(myEmail)) taskToEdit.setParticipantStatus(myEmail, oldStatus.get(myEmail));
            } else {
                for (User u : selectedUsers) {
                    taskToEdit.addAssignee(u.getEmail());
                    if (oldStatus.containsKey(u.getEmail())) {
                        taskToEdit.setParticipantStatus(u.getEmail(), oldStatus.get(u.getEmail()));
                    }
                }
            }
            taskToEdit.setTitle(title);
            taskToEdit.setDescription(etDesc.getText().toString());
            taskToEdit.setDueDate(finalDueDate);
            if (listener != null) listener.onTaskUpdated(taskToEdit);
        } else {
            TaskItem newTask = new TaskItem(title, etDesc.getText().toString(), finalDueDate);
            if (selectedUsers.isEmpty()) {
                newTask.addAssignee(UserManager.getInstance().getUserEmail());
            } else {
                for (User u : selectedUsers) newTask.addAssignee(u.getEmail());
            }
            if (listener != null) listener.onTaskAdded(newTask);
        }
        dismiss();
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\AlarmReceiver.java
============================================================
package com.kbs.kbsintranett;

import android.Manifest;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat; // <-- Denne manglet

public class AlarmReceiver extends BroadcastReceiver {

    private static final String CHANNEL_ID = "kbs_calendar_channel";
    private static final String CHANNEL_NAME = "KBS Kalendervarsler";

    @Override
    public void onReceive(Context context, Intent intent) {
        String title = intent.getStringExtra("TITLE");
        String message = intent.getStringExtra("MESSAGE");
        int notificationId = intent.getIntExtra("ID", 0);

        createNotificationChannel(context);
        showNotification(context, title, message, notificationId);
    }

    private void showNotification(Context context, String title, String message, int notificationId) {
        // Sjekk rettigheter for Android 13+
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            if (ActivityCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
                // Vi kan ikke vise varsel uten rettighet.
                return;
            }
        }

        // Intent for hva som skjer når man trykker på varselet (åpne appen)
        Intent tapIntent = new Intent(context, MainActivity.class);
        tapIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        PendingIntent pendingIntent = PendingIntent.getActivity(
                context,
                0,
                tapIntent,
                PendingIntent.FLAG_IMMUTABLE
        );

        NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_stat_kbs)
                .setColor(ContextCompat.getColor(context, R.color.kbs_logo_blue)) // Setter KBS-blå farge på ikonet
                .setContentTitle(title)
                .setContentText(message)
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setContentIntent(pendingIntent)
                .setAutoCancel(true);

        NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
        notificationManager.notify(notificationId, builder.build());
    }

    private void createNotificationChannel(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            int importance = NotificationManager.IMPORTANCE_HIGH;
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, importance);
            channel.setDescription("Varsler for kalenderhendelser i KBS Intranett");

            NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
            if (notificationManager != null) {
                notificationManager.createNotificationChannel(channel);
            }
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\AlarmScheduler.java
============================================================
package com.kbs.kbsintranett;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.util.Log;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;

public class AlarmScheduler {

    private static final String TAG = "AlarmScheduler";
    private static final String PREFS_NAME = "kbs_alarm_history";

    /**
     * Denne metoden går gjennom en liste hendelser og setter alarmer for dem.
     */
    public static void scheduleAlarmsForEvents(Context context, List<CalendarEvent> events) {
        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);

        // Sjekk rettigheter for Android 12+ (Exact Alarm)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !alarmManager.canScheduleExactAlarms()) {
            Log.w(TAG, "Mangler rettighet til å sette nøyaktige alarmer.");
            return;
        }

        SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
        long now = System.currentTimeMillis();
        SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());

        // Vi ser etter hendelser 30 dager frem i tid
        long futureWindow = now + (30L * 24 * 60 * 60 * 1000L);

        for (CalendarEvent event : events) {
            try {
                // Hopp over hvis ingen dato eller heldags (uten tidspunkt)
                if (event.getRawDate() == null || event.getRawDate().length() == 10) continue;

                Date eventDate = null;
                if (event.getRawDate().contains("T")) {
                    String raw = event.getRawDate();
                    if (raw.length() > 19) raw = raw.substring(0, 19);
                    eventDate = isoFormat.parse(raw);
                }

                if (eventDate == null) continue;

                // Loop gjennom alle varsler (f.eks. 15 min før, 60 min før)
                for (int minutesBefore : event.getReminders()) {
                    if (minutesBefore < 0) continue;

                    long triggerTime = eventDate.getTime() - (minutesBefore * 60 * 1000L);
                    String alarmKey = "alarm_" + event.getId() + "_" + triggerTime;

                    // Hvis tidspunktet er i fremtiden (og innenfor vinduet)
                    if (triggerTime > now && triggerTime < futureWindow) {

                        // Sjekk om vi allerede har satt denne alarmen for å unngå dobbeltarbeid
                        if (prefs.getBoolean(alarmKey, false)) {
                            continue;
                        }

                        int alarmId = alarmKey.hashCode(); // Unik ID basert på hendelse+tid

                        Intent intent = new Intent(context, AlarmReceiver.class);
                        intent.putExtra("TITLE", event.getTitle());
                        String timeStr = new SimpleDateFormat("HH:mm", Locale.getDefault()).format(eventDate);
                        intent.putExtra("MESSAGE", "Starter kl " + timeStr);
                        intent.putExtra("ID", alarmId);

                        PendingIntent pendingIntent = PendingIntent.getBroadcast(
                                context,
                                alarmId,
                                intent,
                                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
                        );

                        // Sett alarmen
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                            alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
                        } else {
                            alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
                        }

                        // Marker som satt
                        prefs.edit().putBoolean(alarmKey, true).apply();
                        Log.d(TAG, "Alarm satt for " + event.getTitle() + " om " + minutesBefore + " min.");
                    }
                }
            } catch (Exception e) {
                Log.e(TAG, "Feil ved setting av alarm", e);
            }
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\AuthRepository.java
============================================================
// FILSTI: app\src\main\java\com\kbs\kbsintranett\AuthRepository.java
package com.kbs.kbsintranett;

import android.util.Log;
import com.google.gson.JsonElement;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class AuthRepository {

    private static final String TAG = "AuthRepository";

    // Interface for å gi beskjed tilbake til Activity/Fragment
    public interface AuthCallback {
        void onSuccess(String role);
        void onError(String message);
    }

    /**
     * Utfører selve API-kallet mot WordPress.
     * Denne brukes nå av både MainActivity (Silent Sign-In) og LoginFragment (Manuell).
     */
    public static void loginToWordPress(String googleIdToken, String displayName, String email, String photoUrl, AuthCallback callback) {

        // 1. Lagre Google-info midlertidig
        UserManager.getInstance().setUserData(displayName, email, googleIdToken, photoUrl);

        // 2. Gjør klar request
        LoginRequest request = new LoginRequest(googleIdToken);

        // 3. Send til WordPress
        RetrofitClient.getApiService().googleLogin(request).enqueue(new Callback<LoginResponse>() {
            @Override
            public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
                if (response.isSuccessful() && response.body() != null && response.body().success) {
                    // SUKSESS!
                    String cookie = response.body().fullCookie;
                    String role = response.body().role;

                    // NYTT: Hent utvidet info fra responsen
                    int userId = response.body().userId;
                    String fName = response.body().firstName;
                    String lName = response.body().lastName;
                    String stilling = response.body().stilling;
                    String mobil = response.body().mobiltelefon;

                    Log.d(TAG, "WordPress Login suksess! Rolle: " + role + ", UserID: " + userId);

                    // Lagre cookie, rolle og ID
                    UserManager.getInstance().setCookie(cookie);
                    UserManager.getInstance().setUserRole(role);
                    UserManager.getInstance().setUserId(userId);

                    // Lagre utvidet info i UserManager
                    UserManager.getInstance().setExtendedUserInfo(fName, lName, stilling, mobil);

                    // Lagre listen over skrivbare kalendere
                    UserManager.getInstance().setWriteableCalendars(response.body().writeableCalendars);

                    // NYTT: Hvis vi har en ventende FCM-token, send den nå som vi er logget inn
                    String pendingToken = UserManager.getInstance().getFcmToken();
                    if (pendingToken != null && !pendingToken.isEmpty()) {
                        updateDeviceToken(pendingToken);
                    }

                    callback.onSuccess(role);

                } else {
                    Log.e(TAG, "WordPress Login nektet. Kode: " + response.code());
                    callback.onError("Kunne ikke logge inn på Intranettet (Kode: " + response.code() + ")");
                }
            }

            @Override
            public void onFailure(Call<LoginResponse> call, Throwable t) {
                Log.e(TAG, "Nettverksfeil mot WP", t);
                callback.onError("Nettverksfeil: " + t.getMessage());
            }
        });
    }

    /**
     * Sender FCM-token til WordPress for å registrere enheten for push-varsler.
     */
    public static void updateDeviceToken(String token) {
        if (!UserManager.getInstance().isLoggedIn()) {
            // Hvis ikke logget inn, bare lagre den til senere
            UserManager.getInstance().setFcmToken(token);
            return;
        }

        // Send til server
        RegisterDeviceRequest request = new RegisterDeviceRequest(token);
        RetrofitClient.getApiService().registerDevice(request).enqueue(new Callback<JsonElement>() {
            @Override
            public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {
                if (response.isSuccessful()) {
                    Log.d(TAG, "FCM Token registrert på server OK.");
                } else {
                    Log.e(TAG, "Feil ved registrering av FCM Token. Kode: " + response.code());
                }
            }

            @Override
            public void onFailure(Call<JsonElement> call, Throwable t) {
                Log.e(TAG, "Nettverksfeil ved sending av FCM token", t);
            }
        });
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\CacheManager.java
============================================================
package com.kbs.kbsintranett;

import android.content.Context;
import android.content.SharedPreferences; // NYTT
import android.util.Log;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

public class CacheManager {
    private static final String FILE_CALENDAR = "cache_calendar.json";
    private static final String FILE_NEWS = "cache_news.json";
    private static final String FILE_HANDBOOK = "cache_handbook.json";
    private static final String FILE_TASKS = "cache_tasks.json";

    private static final String TAG = "CacheManager";
    private static final Gson gson = new Gson();

    // NYTT: SharedPreferences for tidsstempler
    private static final String PREFS_CACHE = "kbs_cache_prefs";

    // --- KALENDER ---
    public static void saveCalendarEvents(Context context, List<CalendarEvent> events) {
        saveList(context, FILE_CALENDAR, events);
        setLastFetchTime(context, "calendar"); // NYTT
    }

    public static List<CalendarEvent> getCachedCalendarEvents(Context context) {
        Type type = new TypeToken<List<CalendarEvent>>() {}.getType();
        List<CalendarEvent> list = loadList(context, FILE_CALENDAR, type);
        return list != null ? list : new ArrayList<>();
    }

    // --- NYHETER ---
    public static void saveNewsPosts(Context context, List<WpPost> posts) {
        saveList(context, FILE_NEWS, posts);
        setLastFetchTime(context, "news"); // NYTT
    }

    public static List<WpPost> getCachedNewsPosts(Context context) {
        Type type = new TypeToken<List<WpPost>>() {}.getType();
        List<WpPost> list = loadList(context, FILE_NEWS, type);
        return list != null ? list : new ArrayList<>();
    }

    // --- HÅNDBOK ---
    public static void saveHandbookItems(Context context, List<HandbookItem> items) {
        saveList(context, FILE_HANDBOOK, items);
        // Håndboken endres sjelden, trenger kanskje ikke tidssjekk, men greit å ha
    }

    public static List<HandbookItem> getCachedHandbookItems(Context context) {
        Type type = new TypeToken<List<HandbookItem>>() {}.getType();
        List<HandbookItem> list = loadList(context, FILE_HANDBOOK, type);
        return list != null ? list : new ArrayList<>();
    }

    // --- OPPGAVER ---
    public static void saveTasks(Context context, List<TaskItem> tasks) {
        saveList(context, FILE_TASKS, tasks);
        setLastFetchTime(context, "tasks"); // NYTT
    }

    public static List<TaskItem> getTasks(Context context) {
        Type type = new TypeToken<List<TaskItem>>() {}.getType();
        List<TaskItem> list = loadList(context, FILE_TASKS, type);
        return list != null ? list : new ArrayList<>();
    }

    // --- LOGIKK FOR TID ---

    private static void setLastFetchTime(Context context, String key) {
        SharedPreferences prefs = context.getSharedPreferences(PREFS_CACHE, Context.MODE_PRIVATE);
        prefs.edit().putLong(key + "_timestamp", System.currentTimeMillis()).apply();
    }

    /**
     * Sjekker om cachen er gyldig.
     * @param maxAgeMinutes Hvor gammel cachen kan være før vi henter på nytt (f.eks. 60 minutter).
     *                      Hvis push fungerer, kan vi sette denne høyt!
     */
    public static boolean isCacheValid(Context context, String key, int maxAgeMinutes) {
        SharedPreferences prefs = context.getSharedPreferences(PREFS_CACHE, Context.MODE_PRIVATE);
        long lastTime = prefs.getLong(key + "_timestamp", 0);
        long diff = System.currentTimeMillis() - lastTime;
        // Konverter minutter til millisekunder
        long maxDiff = maxAgeMinutes * 60 * 1000L;

        return diff < maxDiff;
    }

    // Metode for å tvinge oppdatering neste gang (brukes av FCM)
    public static void invalidateCache(Context context, String key) {
        SharedPreferences prefs = context.getSharedPreferences(PREFS_CACHE, Context.MODE_PRIVATE);
        prefs.edit().remove(key + "_timestamp").apply();
    }

    // --- GENERISKE HJELPEMETODER (Uendret) ---

    private static <T> void saveList(Context context, String filename, List<T> list) {
        if (context == null || list == null) return;
        new Thread(() -> {
            try {
                String json = gson.toJson(list);
                FileOutputStream fos = context.openFileOutput(filename, Context.MODE_PRIVATE);
                fos.write(json.getBytes());
                fos.close();
            } catch (Exception e) {
                Log.e(TAG, "Feil ved lagring av cache: " + filename, e);
            }
        }).start();
    }

    private static <T> List<T> loadList(Context context, String filename, Type type) {
        if (context == null) return null;
        File file = new File(context.getFilesDir(), filename);
        if (!file.exists()) return null;

        try {
            FileInputStream fis = context.openFileInput(filename);
            InputStreamReader isr = new InputStreamReader(fis);
            BufferedReader bufferedReader = new BufferedReader(isr);
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                sb.append(line);
            }
            fis.close();
            return gson.fromJson(sb.toString(), type);
        } catch (Exception e) {
            Log.e(TAG, "Feil ved lesing av cache: " + filename, e);
            return null;
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\CalendarAdapter.java
============================================================
package com.kbs.kbsintranett;

import android.content.res.ColorStateList;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

public class CalendarAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    public static final int TYPE_EVENT = 0;
    public static final int TYPE_YEAR_HEADER = 1;

    private List<Object> items;
    private final OnItemClickListener listener;

    public interface OnItemClickListener {
        void onItemClick(CalendarEvent event);
    }

    public CalendarAdapter(List<Object> items, OnItemClickListener listener) {
        this.items = items;
        this.listener = listener;
    }

    @Override
    public int getItemViewType(int position) {
        return (items.get(position) instanceof String) ? TYPE_YEAR_HEADER : TYPE_EVENT;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        if (viewType == TYPE_YEAR_HEADER) {
            return new YearViewHolder(inflater.inflate(R.layout.item_calendar_year_header, parent, false));
        } else {
            return new EventViewHolder(inflater.inflate(R.layout.item_calendar, parent, false));
        }
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        Object item = items.get(position);

        if (holder instanceof YearViewHolder) {
            ((YearViewHolder) holder).yearText.setText((String) item);
        }
        else if (holder instanceof EventViewHolder) {
            CalendarEvent event = (CalendarEvent) item;
            EventViewHolder vh = (EventViewHolder) holder;

            vh.day.setText(event.getDay());
            vh.month.setText(event.getMonth());
            vh.time.setText(event.getTime());
            vh.title.setText(event.getTitle());

            // --- ÅRSTALL LOGIKK: BAKGRUNNSFARGE ---
            // Vi henter årstall fra datoen (yyyy-MM-dd)
            String year = "2025";
            if (event.getRawDate() != null && event.getRawDate().length() >= 4) {
                year = event.getRawDate().substring(0, 4);
            }

            // Alternerende bakgrunn basert på år (partall vs oddetall)
            int yearInt = Integer.parseInt(year);
            if (yearInt % 2 == 0) {
                vh.itemView.setBackgroundColor(Color.parseColor("#F5F7FA")); // KBS Very Light Blue
            } else {
                vh.itemView.setBackgroundColor(Color.WHITE);
            }

            // Privat-markering og farge på datoboks
            boolean isPrivate = event.getDescription() != null && event.getDescription().contains("#deltakere:");
            try {
                int color = Color.parseColor(isPrivate ? "#673AB7" : event.getCalendarColor());
                vh.dateBox.setBackgroundTintList(ColorStateList.valueOf(color));
            } catch (Exception e) {
                vh.dateBox.setBackgroundTintList(ColorStateList.valueOf(Color.parseColor("#0069B3")));
            }

            vh.itemView.setOnClickListener(v -> listener.onItemClick(event));
        }
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    static class EventViewHolder extends RecyclerView.ViewHolder {
        TextView day, month, title, time;
        LinearLayout dateBox;
        EventViewHolder(View view) {
            super(view);
            day = view.findViewById(R.id.cal_day);
            month = view.findViewById(R.id.cal_month);
            title = view.findViewById(R.id.cal_title);
            time = view.findViewById(R.id.cal_time);
            dateBox = view.findViewById(R.id.date_box_background);
        }
    }

    static class YearViewHolder extends RecyclerView.ViewHolder {
        TextView yearText;
        YearViewHolder(View view) {
            super(view);
            yearText = view.findViewById(R.id.txt_calendar_year);
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\CalendarDetailsBottomSheet.java
============================================================
package com.kbs.kbsintranett;

import android.app.AlertDialog;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.navigation.fragment.NavHostFragment;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.gson.JsonElement;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class CalendarDetailsBottomSheet extends BottomSheetDialogFragment {
    private CalendarEvent event;
    private OnEventChangeListener changeListener;

    private static final String PRIVATE_EVENT_COLOR = "#673AB7";

    public interface OnEventChangeListener {
        void onEventChanged();
    }

    public CalendarDetailsBottomSheet(CalendarEvent event) {
        this.event = event;
    }

    public void setOnEventChangeListener(OnEventChangeListener listener) {
        this.changeListener = listener;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.bottom_sheet_calendar_details, container, false);

        TextView title = view.findViewById(R.id.sheet_title);
        TextView time = view.findViewById(R.id.sheet_time);
        TextView desc = view.findViewById(R.id.sheet_desc);
        TextView loc = view.findViewById(R.id.sheet_location);
        TextView calName = view.findViewById(R.id.sheet_calendar_name);
        TextView participantsView = view.findViewById(R.id.sheet_participants);
        TextView organizerView = view.findViewById(R.id.sheet_organizer); // NYTT

        LinearLayout adminLayout = view.findViewById(R.id.layout_admin_buttons);
        Button btnDelete = view.findViewById(R.id.btn_delete);
        Button btnEdit = view.findViewById(R.id.btn_edit);

        title.setText(event.getTitle());
        time.setText(event.getTime() + " (" + event.getDay() + ". " + event.getMonth() + ")");

        // Sjekk om privat
        boolean isPrivate = event.getDescription() != null && event.getDescription().contains("#deltakere:");

        if (isPrivate) {
            calName.setText(event.getCalendarName().toUpperCase() + " (BEGRENSET INNSYN)");
            try {
                calName.setBackgroundTintList(ColorStateList.valueOf(Color.parseColor(PRIVATE_EVENT_COLOR)));
            } catch (Exception e) {}

            showParticipants(event.getDescription(), participantsView);

        } else {
            calName.setText(event.getCalendarName().toUpperCase());
            try {
                int color = Color.parseColor(event.getCalendarColor());
                calName.setBackgroundTintList(ColorStateList.valueOf(color));
            } catch (Exception e) {}

            participantsView.setVisibility(View.GONE);
        }

        // VIS ARRANGØR ALLTID
        showOrganizer(event.getDescription(), organizerView);

        // --- BESKRIVELSE OG LENKER ---
        if (!event.getDescription().isEmpty()) {
            String cleanDesc = event.getDescription()
                    .replaceAll("#varsel:[\\d,]+", "")
                    .replaceAll("#deltakere:[^\\s]+", "")
                    .replaceAll("#arrangor:.+", "") // Fjern arrangør fra brødtekst
                    .trim();

            if (!cleanDesc.isEmpty()) {
                desc.setText(Html.fromHtml(cleanDesc, Html.FROM_HTML_MODE_COMPACT));
                Linkify.addLinks(desc, Linkify.WEB_URLS | Linkify.EMAIL_ADDRESSES | Linkify.PHONE_NUMBERS);
                desc.setMovementMethod(LinkMovementMethod.getInstance());
                desc.setVisibility(View.VISIBLE);
            } else {
                desc.setVisibility(View.GONE);
            }
        } else {
            desc.setVisibility(View.GONE);
        }

        // --- ADRESSE OG KART ---
        if (!event.getLocation().isEmpty()) {
            loc.setText(event.getLocation());
            loc.setVisibility(View.VISIBLE);
            loc.setOnClickListener(v -> {
                String location = event.getLocation();
                Uri gmmIntentUri = Uri.parse("geo:0,0?q=" + Uri.encode(location));
                Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri);
                mapIntent.setPackage("com.google.android.apps.maps");
                try {
                    startActivity(mapIntent);
                } catch (Exception e) {
                    mapIntent.setPackage(null);
                    try {
                        startActivity(mapIntent);
                    } catch (Exception ex) {
                        Toast.makeText(getContext(), "Fant ingen kart-app", Toast.LENGTH_SHORT).show();
                    }
                }
            });
        } else {
            loc.setVisibility(View.GONE);
        }

        // Sjekk admin-rettigheter
        if (UserManager.getInstance().isEditorOrAbove()) {
            boolean canEdit = UserManager.getInstance().getWriteableCalendars().contains(event.getCalendarName())
                    || UserManager.getInstance().isAdmin();

            if (canEdit) {
                adminLayout.setVisibility(View.VISIBLE);
                btnDelete.setOnClickListener(v -> confirmDelete());
                btnEdit.setOnClickListener(v -> {
                    Bundle bundle = new Bundle();
                    bundle.putSerializable("edit_event", event);
                    NavHostFragment.findNavController(this).navigate(R.id.navigation_create_event, bundle);
                    dismiss();
                });
            } else {
                adminLayout.setVisibility(View.GONE);
            }
        }

        return view;
    }

    private void showParticipants(String rawDescription, TextView view) {
        if (rawDescription == null) return;
        Matcher m = Pattern.compile("#deltakere:([^\\s]+)").matcher(rawDescription);
        if (m.find()) {
            String allEmails = m.group(1);
            String[] emails = allEmails.split(",");
            StringBuilder sb = new StringBuilder();
            sb.append("<b>Synlig for:</b><br/>");
            for (String email : emails) {
                sb.append("• ").append(email.trim()).append("<br/>");
            }
            view.setText(Html.fromHtml(sb.toString(), Html.FROM_HTML_MODE_COMPACT));
            view.setVisibility(View.VISIBLE);
        }
    }

    // NYTT: Vis arrangør
    private void showOrganizer(String rawDescription, TextView view) {
        if (rawDescription == null) return;
        Matcher m = Pattern.compile("#arrangor:(.+)").matcher(rawDescription);
        if (m.find()) {
            String organizer = m.group(1).trim();
            view.setText("Invitert av: " + organizer);
            view.setVisibility(View.VISIBLE);
        } else {
            view.setVisibility(View.GONE);
        }
    }

    private void confirmDelete() {
        new AlertDialog.Builder(getContext())
                .setTitle("Slett hendelse")
                .setMessage("Er du sikker på at du vil slette '" + event.getTitle() + "'?")
                .setPositiveButton("Slett", (dialog, which) -> deleteEvent())
                .setNegativeButton("Avbryt", null)
                .show();
    }

    private void deleteEvent() {
        CreateEventRequest req = new CreateEventRequest(
                "", "", "", "", "", event.getCalendarName(), new ArrayList<>(), false, ""
        );
        req.id = event.getId();

        RetrofitClient.getApiService().deleteCalendarEvent(req).enqueue(new Callback<JsonElement>() {
            @Override
            public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {
                if (response.isSuccessful()) {
                    Toast.makeText(getContext(), "Slettet!", Toast.LENGTH_SHORT).show();
                    if (changeListener != null) {
                        changeListener.onEventChanged();
                    }
                    dismiss();
                } else {
                    Toast.makeText(getContext(), "Kunne ikke slette", Toast.LENGTH_LONG).show();
                }
            }

            @Override
            public void onFailure(Call<JsonElement> call, Throwable t) {
                Toast.makeText(getContext(), "Nettverksfeil", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\CalendarEvent.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class CalendarEvent implements Serializable  {
    @SerializedName("id")
    private String id;

    @SerializedName("title")
    private String title;

    @SerializedName("start_date")
    private String rawDate;

    @SerializedName("end_date")
    private String rawEndDate;

    @SerializedName("description")
    private String description;

    @SerializedName("location")
    private String location;

    @SerializedName("reminders")
    private List<Integer> reminders = new ArrayList<>();

    // NYE FELTER V12.2
    @SerializedName("calendar_name")
    private String calendarName;

    @SerializedName("calendar_color")
    private String calendarColor;

    // UI-hjelpefelter
    private String day;
    private String month;
    private String time;

    // Konstruktør
    public CalendarEvent(String title, String rawDate, String rawEndDate, String description, String location) {
        this.title = title;
        this.rawDate = rawDate;
        this.rawEndDate = rawEndDate;
        this.description = description;
        this.location = location;
    }

    public String getId() { return id; }
    public String getTitle() { return title; }
    public String getRawDate() { return rawDate; }
    public String getRawEndDate() { return rawEndDate; }
    public String getDescription() { return description != null ? description : ""; }
    public String getLocation() { return location != null ? location : ""; }

    public List<Integer> getReminders() {
        return reminders != null ? reminders : new ArrayList<>();
    }

    // NYE GETTERS/SETTERS
    public String getCalendarName() { return calendarName != null ? calendarName : "Ukjent"; }
    public void setCalendarName(String name) { this.calendarName = name; }

    public String getCalendarColor() { return calendarColor != null ? calendarColor : "#888888"; }
    public void setCalendarColor(String color) { this.calendarColor = color; }

    // --- KOMPATIBILITETS-METODER ---

    // Denne brukes for enkle varsler
    public void setReminderMinutes(int minutes) {
        this.reminders = new ArrayList<>();
        if (minutes > 0) {
            this.reminders.add(minutes);
        }
    }

    // NY METODE (Den som manglet og forårsaket krasj)
    // Lar oss sette hele listen med varsler på en gang
    public void setRemindersList(List<Integer> reminders) {
        this.reminders = reminders != null ? new ArrayList<>(reminders) : new ArrayList<>();
    }

    public int getReminderMinutes() {
        if (reminders != null && !reminders.isEmpty()) {
            return reminders.get(0);
        }
        return 0;
    }

    // --- UI SETTERS/GETTERS ---
    public String getDay() { return day; }
    public void setDay(String day) { this.day = day; }
    public String getMonth() { return month; }
    public void setMonth(String month) { this.month = month; }
    public String getTime() { return time; }
    public void setTime(String time) { this.time = time; }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\CalendarFullFragment.java
============================================================
package com.kbs.kbsintranett;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class CalendarFullFragment extends Fragment {

    private RecyclerView recyclerView;
    private ProgressBar progressBar;
    private TextView emptyView;
    private FloatingActionButton fabAddEvent;
    private LinearLayoutManager layoutManager;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_calendar_full, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        recyclerView = view.findViewById(R.id.recycler_full_calendar);
        progressBar = view.findViewById(R.id.loading_full_calendar);
        emptyView = view.findViewById(R.id.empty_view_calendar);
        fabAddEvent = view.findViewById(R.id.fab_add_calendar_event);

        layoutManager = new LinearLayoutManager(getContext());
        recyclerView.setLayoutManager(layoutManager);

        if (!UserManager.getInstance().getWriteableCalendars().isEmpty() || UserManager.getInstance().isEditorOrAbove()) {
            fabAddEvent.setVisibility(View.VISIBLE);
            fabAddEvent.setOnClickListener(v -> Navigation.findNavController(view).navigate(R.id.navigation_create_event));
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        fetchAllEvents();
    }

    private void fetchAllEvents() {
        progressBar.setVisibility(View.VISIBLE);
        new Thread(() -> {
            List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext(), false);
            new Handler(Looper.getMainLooper()).post(() -> fetchApiEvents(deviceEvents));
        }).start();
    }

    private void fetchApiEvents(List<CalendarEvent> deviceEvents) {
        RetrofitClient.getApiService().getCalendarEvents().enqueue(new Callback<List<CalendarEvent>>() {
            @Override
            public void onResponse(Call<List<CalendarEvent>> call, Response<List<CalendarEvent>> response) {
                if (!isAdded()) return;
                progressBar.setVisibility(View.GONE);

                List<CalendarEvent> apiEvents = new ArrayList<>();
                if (response.isSuccessful() && response.body() != null) {
                    apiEvents = response.body();
                    for (CalendarEvent e : apiEvents) CalendarManager.formatEventForUI(e);
                }

                List<CalendarEvent> allEvents = CalendarManager.mergeAndSort(apiEvents, deviceEvents);
                displaySortedList(allEvents);
            }

            @Override
            public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {
                if (!isAdded()) return;
                progressBar.setVisibility(View.GONE);
                displaySortedList(deviceEvents);
            }
        });
    }

    private void displaySortedList(List<CalendarEvent> events) {
        if (events.isEmpty()) {
            emptyView.setVisibility(View.VISIBLE);
            recyclerView.setVisibility(View.GONE);
            return;
        }

        emptyView.setVisibility(View.GONE);
        recyclerView.setVisibility(View.VISIBLE);

        List<Object> itemsWithHeaders = new ArrayList<>();
        String currentYear = "";

        for (CalendarEvent e : events) {
            String year = "Ukjent år";
            if (e.getRawDate() != null && e.getRawDate().length() >= 4) {
                year = e.getRawDate().substring(0, 4);
            }

            if (!year.equals(currentYear)) {
                itemsWithHeaders.add(year);
                currentYear = year;
            }
            itemsWithHeaders.add(e);
        }

        CalendarAdapter adapter = new CalendarAdapter(itemsWithHeaders, event -> {
            CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
            sheet.setOnEventChangeListener(CalendarFullFragment.this::fetchAllEvents);
            sheet.show(getParentFragmentManager(), "CalendarDetails");
        });
        recyclerView.setAdapter(adapter);
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\CalendarManager.java
============================================================
package com.kbs.kbsintranett;

import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.provider.CalendarContract;
import androidx.core.content.ContextCompat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;

public class CalendarManager {
    private static List<String> getKbsCalendarIds(Context context) {
        List<String> ids = new ArrayList<>();
        if (ContextCompat.checkSelfPermission(context, android.Manifest.permission.READ_CALENDAR) != PackageManager.PERMISSION_GRANTED) {
            return ids;
        }
        String[] projection = new String[] {
                CalendarContract.Calendars._ID,
                CalendarContract.Calendars.ACCOUNT_NAME
        };
        String selection = CalendarContract.Calendars.ACCOUNT_NAME + " LIKE ?";
        String[] selectionArgs = new String[] {"%@kbs.no"};

        try (Cursor cursor = context.getContentResolver().query(
                CalendarContract.Calendars.CONTENT_URI,
                projection,
                selection,
                selectionArgs,
                null
        )) {
            if (cursor != null) {
                while (cursor.moveToNext()) {
                    ids.add(cursor.getString(0));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ids;
    }

    /**
     * Henter hendelser fra enheten.
     * @param context App Context
     * @param isPreview Hvis true: Henter kun kommende måned (Raskt). Hvis false: Henter -1 til +6 mnd (Tregere).
     * @return Liste med events
     */
    public static List<CalendarEvent> getDeviceEvents(Context context, boolean isPreview) {
        List<CalendarEvent> deviceEvents = new ArrayList<>();
        if (ContextCompat.checkSelfPermission(context, android.Manifest.permission.READ_CALENDAR)
                != PackageManager.PERMISSION_GRANTED) {
            return deviceEvents;
        }

        List<String> kbsCalendarIds = getKbsCalendarIds(context);
        if (kbsCalendarIds.isEmpty()) {
            return deviceEvents;
        }

        long now = System.currentTimeMillis();
        long startMillis;
        long endMillis;

        if (isPreview) {
            // FORSIDEN: Optimalisert for hastighet.
            // Henter fra NÅ og 30 dager frem i tid.
            startMillis = now;
            endMillis = now + (30L * 24 * 60 * 60 * 1000);
        } else {
            // FULL VISNING: Bredere tidsvindu.
            // 1 mnd tilbake til 6 mnd frem.
            startMillis = now - (30L * 24 * 60 * 60 * 1000);
            endMillis = now + (180L * 24 * 60 * 60 * 1000);
        }

        String[] projection = new String[]{
                CalendarContract.Events.TITLE,
                CalendarContract.Events.DTSTART,
                CalendarContract.Events.DTEND,
                CalendarContract.Events.DESCRIPTION,
                CalendarContract.Events.EVENT_LOCATION,
                CalendarContract.Events.ALL_DAY
        };

        StringBuilder selection = new StringBuilder("(");
        List<String> selectionArgsList = new ArrayList<>();

        for (int i = 0; i < kbsCalendarIds.size(); i++) {
            selection.append(CalendarContract.Events.CALENDAR_ID).append(" = ?");
            selectionArgsList.add(kbsCalendarIds.get(i));
            if (i < kbsCalendarIds.size() - 1) {
                selection.append(" OR ");
            }
        }
        selection.append(") AND ");
        selection.append(CalendarContract.Events.DTSTART).append(" >= ? AND ");
        selection.append(CalendarContract.Events.DTSTART).append(" <= ?");

        selectionArgsList.add(String.valueOf(startMillis));
        selectionArgsList.add(String.valueOf(endMillis));

        String[] selectionArgs = selectionArgsList.toArray(new String[0]);

        try (Cursor cursor = context.getContentResolver().query(
                CalendarContract.Events.CONTENT_URI,
                projection,
                selection.toString(),
                selectionArgs,
                CalendarContract.Events.DTSTART + " ASC"
        )) {
            if (cursor != null) {
                SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
                while (cursor.moveToNext()) {
                    String title = cursor.getString(0);
                    long dtStart = cursor.getLong(1);
                    long dtEnd = cursor.getLong(2);
                    String desc = cursor.getString(3);
                    String loc = cursor.getString(4);
                    int allDay = cursor.getInt(5);

                    String rawStart;
                    String rawEnd;

                    if (allDay == 1) {
                        SimpleDateFormat shortFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
                        rawStart = shortFormat.format(new Date(dtStart));
                        rawEnd = shortFormat.format(new Date(dtEnd));
                    } else {
                        rawStart = isoFormat.format(new Date(dtStart));
                        rawEnd = isoFormat.format(new Date(dtEnd));
                    }

                    CalendarEvent event = new CalendarEvent(title, rawStart, rawEnd, desc, loc);
                    event.setReminderMinutes(0);

                    event.setCalendarColor("#888888");
                    event.setCalendarName("Min Kalender (Lokal)");

                    formatEventForUI(event);
                    deviceEvents.add(event);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return deviceEvents;
    }

    public static void formatEventForUI(CalendarEvent event) {
        if (event.getRawDate() == null) return;

        SimpleDateFormat outputDay = new SimpleDateFormat("dd", Locale.getDefault());
        SimpleDateFormat outputMonth = new SimpleDateFormat("MMM", new Locale("no", "NO"));
        SimpleDateFormat outputTime = new SimpleDateFormat("HH:mm", Locale.getDefault());

        outputMonth.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
        outputTime.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));

        try {
            Date date = null;
            Date endDate = null;
            boolean isAllDay = false;

            String raw = event.getRawDate();

            if (raw.length() == 10 && !raw.contains("T") && !raw.contains(" ")) {
                SimpleDateFormat shortFmt = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
                date = shortFmt.parse(raw);
                isAllDay = true;
                if (event.getRawEndDate() != null && event.getRawEndDate().length() == 10) {
                    endDate = shortFmt.parse(event.getRawEndDate());
                }
            }
            else if (raw.contains("T")) {
                SimpleDateFormat isoFmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
                isoFmt.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
                date = isoFmt.parse(raw);
                if (event.getRawEndDate() != null && event.getRawEndDate().contains("T")) {
                    endDate = isoFmt.parse(event.getRawEndDate());
                }
            }
            else {
                SimpleDateFormat sqlFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
                sqlFmt.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
                date = sqlFmt.parse(raw);
                if (event.getRawEndDate() != null) {
                    endDate = sqlFmt.parse(event.getRawEndDate());
                }
            }

            if (date != null) {
                event.setDay(outputDay.format(date));
                event.setMonth(outputMonth.format(date).toUpperCase());

                if (isAllDay) {
                    event.setTime("Hele dagen");
                } else {
                    String timeStr = outputTime.format(date);
                    if (endDate != null) {
                        timeStr += " - " + outputTime.format(endDate);
                    }
                    event.setTime("Kl. " + timeStr);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
            event.setDay("??");
            event.setMonth("???");
            event.setTime("Feil dato");
        }
    }

    public static List<CalendarEvent> mergeAndSort(List<CalendarEvent> apiEvents, List<CalendarEvent> deviceEvents) {
        List<CalendarEvent> all = new ArrayList<>();
        Set<String> uniqueKeys = new HashSet<>();

        // 1. Legg til alle API-events først (Prioritert)
        for (CalendarEvent apiEvent : apiEvents) {
            all.add(apiEvent);
            uniqueKeys.add(generateKey(apiEvent));
        }

        // 2. Legg til Device-events KUN hvis nøkkelen ikke finnes
        for (CalendarEvent deviceEvent : deviceEvents) {
            String key = generateKey(deviceEvent);
            if (!uniqueKeys.contains(key)) {
                all.add(deviceEvent);
            }
        }

        // 3. Sorter alt kronologisk
        Collections.sort(all, (e1, e2) -> {
            String d1 = e1.getRawDate() != null ? e1.getRawDate() : "";
            String d2 = e2.getRawDate() != null ? e2.getRawDate() : "";
            return d1.compareTo(d2);
        });

        return all;
    }

    private static String generateKey(CalendarEvent event) {
        String title = event.getTitle() != null ? event.getTitle().toLowerCase().trim() : "";
        String datePart = "";

        if (event.getRawDate() != null) {
            String digits = event.getRawDate().replaceAll("[^0-9]", "");
            if (digits.length() >= 12) {
                datePart = digits.substring(0, 12);
            } else if (digits.length() >= 8) {
                datePart = digits.substring(0, 8);
            } else {
                datePart = digits;
            }
        }
        return title + "_" + datePart;
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\CategoryAdapter.java
============================================================
package com.kbs.kbsintranett;

import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

public class CategoryAdapter extends RecyclerView.Adapter<CategoryAdapter.ViewHolder> {

    private List<String> categories;
    private String selectedCategory = "Alle"; // Standardvalg
    private OnCategoryClickListener listener;

    public interface OnCategoryClickListener {
        void onCategoryClick(String category);
    }

    public CategoryAdapter(List<String> categories, OnCategoryClickListener listener) {
        this.categories = categories;
        this.listener = listener;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_category, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        String category = categories.get(position);
        holder.name.setText(category);

        if (category.equals(selectedCategory)) {
            // Valgt stil (Blå bakgrunn, hvit tekst)
            holder.name.setBackgroundResource(R.drawable.bg_category_selected);
            holder.name.setTextColor(Color.WHITE);
        } else {
            // Ikke valgt stil (Hvit bakgrunn, mørk tekst)
            holder.name.setBackgroundResource(R.drawable.bg_category_unselected);
            holder.name.setTextColor(Color.parseColor("#333333"));
        }

        holder.itemView.setOnClickListener(v -> {
            selectedCategory = category;
            notifyDataSetChanged(); // Oppdater alle for å flytte markering
            listener.onCategoryClick(category);
        });
    }

    @Override
    public int getItemCount() {
        return categories.size();
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        TextView name;
        public ViewHolder(View view) {
            super(view);
            name = view.findViewById(R.id.category_name);
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\ChoicesAdapter.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

public class ChoicesAdapter implements JsonDeserializer<List<GravityField.Choice>> {
    @Override
    public List<GravityField.Choice> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        // Hvis feltet er "null" eller en tom tekststreng, returner en tom liste
        if (json.isJsonNull() || (json.isJsonPrimitive() && json.getAsString().isEmpty())) {
            return new ArrayList<>();
        }

        // Hvis det faktisk er en liste (Array), les den som vanlig
        if (json.isJsonArray()) {
            List<GravityField.Choice> list = new ArrayList<>();
            for (JsonElement e : json.getAsJsonArray()) {
                list.add(context.deserialize(e, GravityField.Choice.class));
            }
            return list;
        }

        // Hvis vi får noe annet rart (f.eks. en tekst som ikke er tom), ignorer det for å unngå krasj
        return new ArrayList<>();
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\ConditionalLogicAdapter.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import java.lang.reflect.Type;

public class ConditionalLogicAdapter implements JsonDeserializer<GravityField.ConditionalLogic> {
    @Override
    public GravityField.ConditionalLogic deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        // Hvis feltet er en streng (f.eks tom streng ""), returner null
        if (json.isJsonPrimitive() && json.getAsJsonPrimitive().isString()) {
            return null;
        }

        // Hvis det er et objekt, bruk standard deserialisering
        if (json.isJsonObject()) {
            // Vi må manuelt deserialisere for å unngå uendelig løkke hvis vi bare kaller context.deserialize på samme type
            // Enkleste måte er å la Gson gjøre jobben på innholdet
            return new com.google.gson.Gson().fromJson(json, GravityField.ConditionalLogic.class);
        }

        return null;
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\CreateEventFragment.java
============================================================
package com.kbs.kbsintranett;

import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Spinner;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;
import com.google.android.material.chip.Chip;
import com.google.android.material.chip.ChipGroup;
import com.google.gson.JsonElement;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class CreateEventFragment extends Fragment {
    private EditText etTitle, etDesc, etLocation;
    private Spinner spinnerCalendar, spinnerRecurrence;
    private ChipGroup chipGroupReminders;
    private Switch switchAllDay;
    private TextView txtPreview, txtSelectedParticipants;
    private Button btnStartDate, btnStartTime, btnEndDate, btnEndTime, btnSave, btnSelectParticipants;
    private RadioButton rbAll, rbSpecific;

    private Calendar startCal = Calendar.getInstance();
    private Calendar endCal = Calendar.getInstance();

    private String selectedRRule = null;
    private boolean isCustomRecurrence = false;

    // BRUKERLISTER
    private List<User> filteredUsers = new ArrayList<>();

    private String originalOrganizer = null;
    private CalendarEvent eventToEdit = null;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_create_event, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        // Initialisering av Views
        etTitle = view.findViewById(R.id.et_title);
        etDesc = view.findViewById(R.id.et_desc);
        etLocation = view.findViewById(R.id.et_location);
        switchAllDay = view.findViewById(R.id.switch_all_day);
        spinnerCalendar = view.findViewById(R.id.spinner_calendar);
        spinnerRecurrence = view.findViewById(R.id.spinner_recurrence);
        chipGroupReminders = view.findViewById(R.id.chip_group_reminders);
        txtPreview = view.findViewById(R.id.txt_time_preview);
        txtSelectedParticipants = view.findViewById(R.id.txt_selected_participants);
        btnStartDate = view.findViewById(R.id.btn_start_date);
        btnStartTime = view.findViewById(R.id.btn_start_time);
        btnEndDate = view.findViewById(R.id.btn_end_date);
        btnEndTime = view.findViewById(R.id.btn_end_time);
        btnSave = view.findViewById(R.id.btn_save_event);
        btnSelectParticipants = view.findViewById(R.id.btn_select_participants);
        rbAll = view.findViewById(R.id.rb_visibility_all);
        rbSpecific = view.findViewById(R.id.rb_visibility_specific);

        // Standardtidspunkt (neste time)
        startCal.add(Calendar.HOUR_OF_DAY, 1);
        startCal.set(Calendar.MINUTE, 0);
        endCal.setTime(startCal.getTime());
        endCal.add(Calendar.HOUR_OF_DAY, 1);

        setupCalendarSpinner();
        setupReminderChips();
        fetchUsers();

        // Sjekk om vi redigerer en eksisterende hendelse
        if (getArguments() != null && getArguments().containsKey("edit_event")) {
            eventToEdit = (CalendarEvent) getArguments().getSerializable("edit_event");
            prefillForm(eventToEdit);
            btnSave.setText("Oppdater Hendelse");
        }

        updateRecurrenceSpinner();
        updateUI();

        // Listeners
        switchAllDay.setOnCheckedChangeListener((btn, isChecked) -> updateUI());
        btnStartDate.setOnClickListener(v -> pickDate(startCal, true));
        btnEndDate.setOnClickListener(v -> pickDate(endCal, false));
        btnStartTime.setOnClickListener(v -> pickTime(startCal));
        btnEndTime.setOnClickListener(v -> pickTime(endCal));
        btnSave.setOnClickListener(v -> submitEvent());

        rbAll.setOnCheckedChangeListener((buttonView, isChecked) -> {
            if (isChecked) {
                btnSelectParticipants.setVisibility(View.GONE);
                txtSelectedParticipants.setVisibility(View.GONE);
            }
        });
        rbSpecific.setOnCheckedChangeListener((buttonView, isChecked) -> {
            if (isChecked) {
                btnSelectParticipants.setVisibility(View.VISIBLE);
                txtSelectedParticipants.setVisibility(View.VISIBLE);
            }
        });

        btnSelectParticipants.setOnClickListener(v -> showUserSelectionDialog());

        spinnerRecurrence.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                if (eventToEdit != null && position == 0 && selectedRRule != null) return;
                String selected = parent.getItemAtPosition(position).toString();
                if (selected.equals("Egendefinert...")) {
                    showCustomRecurrenceDialog();
                } else if (selected.startsWith("Ikke gjenta")) {
                    selectedRRule = null;
                } else {
                    generateStandardRRule(position);
                }
            }
            @Override public void onNothingSelected(AdapterView<?> parent) {}
        });
    }

    private void fetchUsers() {
        RetrofitClient.getApiService().getUsersList().enqueue(new Callback<List<User>>() {
            @Override
            public void onResponse(Call<List<User>> call, Response<List<User>> response) {
                if (!isAdded()) return;
                if (response.isSuccessful() && response.body() != null) {
                    // BRUKER DEN SENTRALE HJELPEKLASSEN FOR FILTRERING
                    filteredUsers = UserFilterHelper.getFilteredUsers(response.body());

                    if (eventToEdit != null) {
                        parseParticipantsFromDescription(eventToEdit.getDescription());
                    }
                }
            }
            @Override
            public void onFailure(Call<List<User>> call, Throwable t) {}
        });
    }

    private void showUserSelectionDialog() {
        if (filteredUsers.isEmpty()) {
            Toast.makeText(getContext(), "Ingen personer funnet.", Toast.LENGTH_SHORT).show();
            return;
        }

        String[] userNames = new String[filteredUsers.size()];
        boolean[] checkedItems = new boolean[filteredUsers.size()];

        for (int i = 0; i < filteredUsers.size(); i++) {
            userNames[i] = filteredUsers.get(i).getName();
            checkedItems[i] = filteredUsers.get(i).isSelected();
        }

        new AlertDialog.Builder(getContext())
                .setTitle("Velg deltakere")
                .setMultiChoiceItems(userNames, checkedItems, (dialog, which, isChecked) -> {
                    filteredUsers.get(which).setSelected(isChecked);
                })
                .setPositiveButton("OK", (dialog, which) -> updateParticipantPreview())
                .setNegativeButton("Avbryt", null)
                .show();
    }

    private void updateParticipantPreview() {
        StringBuilder sb = new StringBuilder("Valgte: ");
        int count = 0;
        for (User u : filteredUsers) {
            if (u.isSelected()) {
                if (count > 0) sb.append(", ");
                sb.append(u.getName());
                count++;
            }
        }
        if (count == 0) txtSelectedParticipants.setText("Ingen valgt");
        else txtSelectedParticipants.setText(sb.toString());
    }

    private void parseParticipantsFromDescription(String desc) {
        if (desc == null) return;
        Pattern p = Pattern.compile("#deltakere:([^\\s]+)");
        Matcher m = p.matcher(desc);

        if (m.find()) {
            rbSpecific.setChecked(true);
            String[] emails = m.group(1).split(",");
            for (String email : emails) {
                for (User u : filteredUsers) {
                    if (u.getEmail().equalsIgnoreCase(email.trim())) {
                        u.setSelected(true);
                    }
                }
            }
            updateParticipantPreview();
        } else {
            rbAll.setChecked(true);
        }
    }

    private void prefillForm(CalendarEvent event) {
        etTitle.setText(event.getTitle());
        if (event.getDescription() != null) {
            Matcher m = Pattern.compile("#arrangor:(.+)").matcher(event.getDescription());
            if (m.find()) originalOrganizer = m.group(1).trim();
        }

        String cleanDesc = event.getDescription()
                .replaceAll("#varsel:[\\d,]+", "")
                .replaceAll("#deltakere:[^\\s]+", "")
                .replaceAll("#arrangor:.+", "")
                .trim();
        etDesc.setText(cleanDesc);
        etLocation.setText(event.getLocation());

        ArrayAdapter<String> adapter = (ArrayAdapter<String>) spinnerCalendar.getAdapter();
        if (adapter != null) {
            int position = adapter.getPosition(event.getCalendarName());
            if (position >= 0) spinnerCalendar.setSelection(position);
        }
        spinnerCalendar.setEnabled(false);

        try {
            SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
            SimpleDateFormat simpleFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
            String start = event.getRawDate();
            if (start != null) {
                if (start.length() == 10) {
                    switchAllDay.setChecked(true);
                    startCal.setTime(simpleFormat.parse(start));
                    if (event.getRawEndDate() != null) {
                        Calendar c = Calendar.getInstance();
                        c.setTime(simpleFormat.parse(event.getRawEndDate()));
                        c.add(Calendar.DAY_OF_MONTH, -1);
                        endCal.setTime(c.getTime());
                    } else {
                        endCal.setTime(startCal.getTime());
                    }
                } else if (start.contains("T")) {
                    if (start.length() > 19) start = start.substring(0, 19);
                    startCal.setTime(isoFormat.parse(start));
                    String end = event.getRawEndDate();
                    if (end != null && end.contains("T")) {
                        if (end.length() > 19) end = end.substring(0, 19);
                        endCal.setTime(isoFormat.parse(end));
                    } else {
                        endCal.setTime(startCal.getTime());
                        endCal.add(Calendar.HOUR_OF_DAY, 1);
                    }
                }
            }
            List<Integer> existingReminders = event.getReminders();
            if (!existingReminders.isEmpty()) {
                for (int i = 0; i < chipGroupReminders.getChildCount(); i++) {
                    Chip chip = (Chip) chipGroupReminders.getChildAt(i);
                    chip.setChecked(existingReminders.contains((Integer) chip.getTag()));
                }
            }
        } catch (Exception e) {
            Log.e("KBS_EDIT", "Feil ved prefill", e);
        }
    }

    private void setupCalendarSpinner() {
        List<String> calendars = UserManager.getInstance().getWriteableCalendars();
        if (calendars.isEmpty()) {
            calendars = new ArrayList<>();
            calendars.add("Felles");
        }

        ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(), android.R.layout.simple_spinner_item, calendars) {
            @NonNull @Override public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
                TextView view = (TextView) super.getView(position, convertView, parent);
                view.setBackgroundColor(Color.parseColor(getCalendarColor(getItem(position))));
                view.setTextColor(Color.WHITE);
                view.setTypeface(null, Typeface.BOLD);
                return view;
            }
            @Override public View getDropDownView(int position, View convertView, ViewGroup parent) {
                TextView view = (TextView) super.getDropDownView(position, convertView, parent);
                view.setTextColor(Color.parseColor(getCalendarColor(getItem(position))));
                view.setTypeface(null, Typeface.BOLD);
                return view;
            }
        };
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinnerCalendar.setAdapter(adapter);
    }

    private String getCalendarColor(String name) {
        if (name == null) return "#888888";
        switch (name) {
            case "Felles": return "#0069B3"; // KBS Blå
            case "AMU/HMS/Miljø": return "#2E7D32"; // Mørk grønn (Miljø-profil)
            case "Administrasjonen": return "#607D8B";
            case "Serviceavdelingen": return "#E65100";
            case "Automasjonsavdelingen": return "#2E7D32"; // Merk: Denne er lik Miljø nå, du kan bytte til f.eks #1B5E20 hvis ønskelig
            case "Prosjektavdelingen": return "#7B1FA2";
            default: return "#888888";
        }
    }

    private void setupReminderChips() {
        chipGroupReminders.removeAllViews();
        addReminderChip("Ved start", 0);
        addReminderChip("5 min", 5);
        addReminderChip("10 min", 10);
        addReminderChip("15 min", 15);
        addReminderChip("30 min", 30);
        addReminderChip("1 t", 60);
        addReminderChip("2 t", 120);
        addReminderChip("1 d", 1440);
        addReminderChip("2 d", 2880);
        addReminderChip("1 u", 10080);

        if (eventToEdit == null) {
            for (int i=0; i<chipGroupReminders.getChildCount(); i++) {
                Chip c = (Chip) chipGroupReminders.getChildAt(i);
                if ((int)c.getTag() == 15) c.setChecked(true);
            }
        }
    }

    private void addReminderChip(String text, int minutes) {
        Chip chip = new Chip(getContext());
        chip.setText(text);
        chip.setTag(minutes);
        chip.setCheckable(true);
        chipGroupReminders.addView(chip);
    }

    private void updateRecurrenceSpinner() {
        String dayName = new SimpleDateFormat("EEEE", new Locale("no")).format(startCal.getTime());
        int dayOfMonth = startCal.get(Calendar.DAY_OF_MONTH);
        String monthName = new SimpleDateFormat("MMMM", new Locale("no")).format(startCal.getTime());
        int weekNo = (startCal.get(Calendar.DAY_OF_MONTH) - 1) / 7 + 1;
        List<String> options = new ArrayList<>();
        options.add("Ikke gjenta"); options.add("Daglig"); options.add("Ukentlig på " + dayName);
        options.add("Månedlig den " + dayOfMonth + "."); options.add("Månedlig den " + weekNo + ". " + dayName + "en");
        options.add("Årlig den " + dayOfMonth + ". " + monthName); options.add("Hver ukedag (man-fre)"); options.add("Egendefinert...");
        spinnerRecurrence.setAdapter(new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, options));
    }

    private void generateStandardRRule(int position) {
        switch (position) {
            case 1: selectedRRule = "RRULE:FREQ=DAILY"; break;
            case 2: selectedRRule = "RRULE:FREQ=WEEKLY"; break;
            case 3: selectedRRule = "RRULE:FREQ=MONTHLY"; break;
            case 4: selectedRRule = "RRULE:FREQ=MONTHLY;BYDAY=" + ((startCal.get(Calendar.DAY_OF_MONTH)-1)/7+1) + getDayCode(startCal.get(Calendar.DAY_OF_WEEK)); break;
            case 5: selectedRRule = "RRULE:FREQ=YEARLY"; break;
            case 6: selectedRRule = "RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR"; break;
            default: selectedRRule = null;
        }
    }

    private String getDayCode(int calendarDay) {
        switch (calendarDay) {
            case Calendar.MONDAY: return "MO"; case Calendar.TUESDAY: return "TU"; case Calendar.WEDNESDAY: return "WE";
            case Calendar.THURSDAY: return "TH"; case Calendar.FRIDAY: return "FR"; case Calendar.SATURDAY: return "SA";
            case Calendar.SUNDAY: return "SU"; default: return "MO";
        }
    }

    private void showCustomRecurrenceDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
        View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_custom_recurrence, null);
        EditText etInterval = view.findViewById(R.id.et_interval), etCount = view.findViewById(R.id.et_count);
        Spinner spinnerFreq = view.findViewById(R.id.spinner_freq);
        View layoutWeekdays = view.findViewById(R.id.layout_weekdays);
        ToggleButton[] toggles = {view.findViewById(R.id.tg_mon), view.findViewById(R.id.tg_tue), view.findViewById(R.id.tg_wed), view.findViewById(R.id.tg_thu), view.findViewById(R.id.tg_fri), view.findViewById(R.id.tg_sat), view.findViewById(R.id.tg_sun)};
        String[] labels = {"M", "T", "O", "T", "F", "L", "S"};
        for(int i=0; i<7; i++) { toggles[i].setText(labels[i]); toggles[i].setTextOn(labels[i]); toggles[i].setTextOff(labels[i]); }
        RadioGroup rgEnd = view.findViewById(R.id.rg_end); Button btnEndDatePicker = view.findViewById(R.id.btn_end_date_picker);
        Calendar customEndCal = Calendar.getInstance(); customEndCal.add(Calendar.MONTH, 1);
        btnEndDatePicker.setText(new SimpleDateFormat("d. MMM yyyy", Locale.getDefault()).format(customEndCal.getTime()));
        btnEndDatePicker.setOnClickListener(v -> {
            rgEnd.check(R.id.rb_date);
            new DatePickerDialog(getContext(), (p, y, m, d) -> { customEndCal.set(y, m, d); btnEndDatePicker.setText(new SimpleDateFormat("d. MMM yyyy", Locale.getDefault()).format(customEndCal.getTime())); }, customEndCal.get(Calendar.YEAR), customEndCal.get(Calendar.MONTH), customEndCal.get(Calendar.DAY_OF_MONTH)).show();
        });
        spinnerFreq.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override public void onItemSelected(AdapterView<?> p, View v, int pos, long id) { layoutWeekdays.setVisibility(pos == 1 ? View.VISIBLE : View.GONE); }
            @Override public void onNothingSelected(AdapterView<?> p) {}
        });
        builder.setView(view); AlertDialog dialog = builder.create();
        view.findViewById(R.id.btn_cancel).setOnClickListener(v -> { dialog.dismiss(); spinnerRecurrence.setSelection(0); });
        view.findViewById(R.id.btn_done).setOnClickListener(v -> {
            StringBuilder rrule = new StringBuilder("RRULE:FREQ=");
            rrule.append(new String[]{"DAILY", "WEEKLY", "MONTHLY", "YEARLY"}[spinnerFreq.getSelectedItemPosition()]);
            if (!etInterval.getText().toString().isEmpty() && !etInterval.getText().toString().equals("1")) rrule.append(";INTERVAL=").append(etInterval.getText().toString());
            if (spinnerFreq.getSelectedItemPosition() == 1) {
                List<String> days = new ArrayList<>(); String[] codes = {"MO", "TU", "WE", "TH", "FR", "SA", "SU"};
                for (int i=0; i<7; i++) if (toggles[i].isChecked()) days.add(codes[i]);
                if (!days.isEmpty()) rrule.append(";BYDAY=").append(String.join(",", days));
            }
            if (rgEnd.getCheckedRadioButtonId() == R.id.rb_date) {
                customEndCal.set(Calendar.HOUR_OF_DAY, 23); customEndCal.set(Calendar.MINUTE, 59);
                SimpleDateFormat utc = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'", Locale.US); utc.setTimeZone(java.util.TimeZone.getTimeZone("UTC"));
                rrule.append(";UNTIL=").append(utc.format(customEndCal.getTime()));
            } else if (rgEnd.getCheckedRadioButtonId() == R.id.rb_count) rrule.append(";COUNT=").append(etCount.getText().toString());
            selectedRRule = rrule.toString(); isCustomRecurrence = true; dialog.dismiss();
        });
        dialog.show();
    }

    private void pickDate(Calendar cal, boolean isStart) {
        new DatePickerDialog(getContext(), (view, y, m, d) -> {
            cal.set(y, m, d);
            if (isStart) {
                if (endCal.before(startCal)) { endCal.setTime(startCal.getTime()); if (!switchAllDay.isChecked()) endCal.add(Calendar.HOUR_OF_DAY, 1); }
                updateRecurrenceSpinner(); if (!isCustomRecurrence) spinnerRecurrence.setSelection(0);
            }
            updateUI();
        }, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)).show();
    }

    private void pickTime(Calendar cal) {
        new TimePickerDialog(getContext(), (view, h, m) -> {
            cal.set(Calendar.HOUR_OF_DAY, h); cal.set(Calendar.MINUTE, m);
            if (cal == startCal && endCal.before(startCal)) { endCal.setTime(startCal.getTime()); endCal.add(Calendar.HOUR_OF_DAY, 1); }
            updateUI();
        }, cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), true).show();
    }

    private void updateUI() {
        boolean isAllDay = switchAllDay.isChecked();
        btnStartTime.setVisibility(isAllDay ? View.GONE : View.VISIBLE);
        btnEndTime.setVisibility(isAllDay ? View.GONE : View.VISIBLE);
        SimpleDateFormat dFmt = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault()), tFmt = new SimpleDateFormat("HH:mm", Locale.getDefault());
        btnStartDate.setText(dFmt.format(startCal.getTime())); btnEndDate.setText(dFmt.format(endCal.getTime()));
        btnStartTime.setText(tFmt.format(startCal.getTime())); btnEndTime.setText(tFmt.format(endCal.getTime()));
        String p = dFmt.format(startCal.getTime()) + (isAllDay ? "" : " " + tFmt.format(startCal.getTime())) + " - ";
        if (startCal.get(Calendar.YEAR) == endCal.get(Calendar.YEAR) && startCal.get(Calendar.DAY_OF_YEAR) == endCal.get(Calendar.DAY_OF_YEAR)) {
            p += (isAllDay ? "(Samme dag)" : tFmt.format(endCal.getTime()));
        } else p += dFmt.format(endCal.getTime()) + (isAllDay ? "" : " " + tFmt.format(endCal.getTime()));
        txtPreview.setText(p);
    }

    private void submitEvent() {
        String title = etTitle.getText().toString().trim();
        if (title.isEmpty()) { etTitle.setError("Mangler tittel"); return; }
        SimpleDateFormat sdf = new SimpleDateFormat(switchAllDay.isChecked() ? "yyyy-MM-dd" : "yyyy-MM-dd'T'HH:mm", Locale.getDefault());
        List<Integer> reminders = new ArrayList<>();
        for (int i=0; i<chipGroupReminders.getChildCount(); i++) {
            Chip c = (Chip) chipGroupReminders.getChildAt(i);
            if (c.isChecked()) reminders.add((Integer) c.getTag());
        }
        String desc = etDesc.getText().toString();
        if (rbSpecific.isChecked()) {
            StringBuilder sb = new StringBuilder(); boolean first = true;
            for (User u : filteredUsers) if (u.isSelected()) { if (!first) sb.append(","); sb.append(u.getEmail()); first = false; }
            if (sb.length() > 0) desc += "\n\n#deltakere:" + sb.toString();
        }
        desc += "\n#arrangor:" + (originalOrganizer != null ? originalOrganizer : UserManager.getInstance().getUserDisplayName());

        String calSlug = spinnerCalendar.getSelectedItem() != null ? spinnerCalendar.getSelectedItem().toString() : "Felles";

        CreateEventRequest req = new CreateEventRequest(title, desc, etLocation.getText().toString(), sdf.format(startCal.getTime()), sdf.format(endCal.getTime()), calSlug, reminders, switchAllDay.isChecked(), selectedRRule);
        if (eventToEdit != null) req.id = eventToEdit.getId();

        Call<JsonElement> call = (eventToEdit != null) ? RetrofitClient.getApiService().updateCalendarEvent(req) : RetrofitClient.getApiService().createCalendarEvent(req);
        call.enqueue(new Callback<JsonElement>() {
            @Override public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {
                if (response.isSuccessful()) {
                    Toast.makeText(getContext(), "Lagret!", Toast.LENGTH_LONG).show();
                    Navigation.findNavController(getView()).navigateUp();
                }
            }
            @Override public void onFailure(Call<JsonElement> call, Throwable t) { Toast.makeText(getContext(), "Feil!", Toast.LENGTH_SHORT).show(); }
        });
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\CreateEventRequest.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.annotations.SerializedName;
import java.util.List;
public class CreateEventRequest {
    @SerializedName("id")
    public String id;
    @SerializedName("title")
    public String title;

    @SerializedName("description")
    public String description;

    @SerializedName("location")
    public String location;

    @SerializedName("start_time")
    public String startTime;

    @SerializedName("end_time")
    public String endTime;

    @SerializedName("calendar_type")
    public String calendarType;

    @SerializedName("reminders")
    public List<Integer> reminders; // Liste, ikke int

    @SerializedName("is_all_day")
    public boolean isAllDay;

    @SerializedName("recurrence")
    public String recurrence;

    // Oppdatert konstruktør som tar imot List<Integer>
    public CreateEventRequest(String title, String description, String location, String startTime, String endTime, String calendarType, List<Integer> reminders, boolean isAllDay, String recurrence) {
        this.title = title;
        this.description = description;
        this.location = location;
        this.startTime = startTime;
        this.endTime = endTime;
        this.calendarType = calendarType;
        this.reminders = reminders;
        this.isAllDay = isAllDay;
        this.recurrence = recurrence;
    }
}


============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\FormsFragment.java
============================================================
package com.kbs.kbsintranett;

import android.Manifest;
import android.animation.LayoutTransition;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.OpenableColumns;
import android.text.Editable;
import android.text.Html;
import android.text.InputType;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.ScrollView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonArray;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okio.BufferedSink;
import okio.Okio;
import okio.Source;

public class FormsFragment extends Fragment {

    private static final String TAG = "FormsFragment";
    private static final String BASE_URL_GF = "https://intranet.kbs.no/wp-json/gf/v2";

    // SKJEMA ID-er
    private static final int ID_ANSATTEOPPLYSNINGER = 1;
    private static final int ID_RUH = 4;
    private static final int ID_SIKKERHETSKURS = 9;
    private static final int ID_HMS_BEKREFTELSE = 10;
    private static final int ID_EGENMELDING = 11;
    private static final int ID_SJA = 14;
    private static final int ID_FRAVARSVARSEL = 15;
    private static final int ID_REFUSJON_UTLEGG = 16;

    private int formId = 1;

    private LinearLayout formContainer;
    private LinearLayout historyContainer;
    private View historyWrapper;
    private TextView txtStatus;
    private TextView lblHistory;
    private ProgressBar loadingSpinner;
    private ImageView btnToggleHistory;
    private Toolbar toolbar; // NYTT

    // --- HOVEDSKJEMA STATE ---
    private Map<String, View> fieldWrappers = new HashMap<>();
    private Map<String, View> inputViews = new HashMap<>();
    private Map<String, Boolean> requiredFieldsMap = new HashMap<>();
    private Map<String, Uri> fileUploads = new HashMap<>();

    // --- NESTED FORM (BARN) STATE ---
    private Map<String, View> childInputViews = new HashMap<>();
    private Map<String, Boolean> childRequiredFieldsMap = new HashMap<>();
    private Map<String, Uri> childFileUploads = new HashMap<>();

    private List<NestedEntry> nestedEntries = new ArrayList<>();
    private LinearLayout nestedEntriesContainer;
    private TextView totalAmountView;

    // --- FILOPPLASTING & KAMERA ---
    private String pendingFileFieldId = null;
    private boolean isSelectingForChild = false;
    private Uri currentPhotoUri = null;

    private ActivityResultLauncher<Intent> filePickerLauncher;
    private ActivityResultLauncher<Uri> takePictureLauncher;
    private ActivityResultLauncher<String> requestPermissionLauncher;

    private GravityForm currentForm;
    private final OkHttpClient client = new OkHttpClient();

    private static final Pattern TITLE_PATTERN = Pattern.compile("^(\\d+)[.\\s-]+\\s*(.*)");

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        filePickerLauncher = registerForActivityResult(
                new ActivityResultContracts.StartActivityForResult(),
                result -> {
                    if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
                        Uri uri = result.getData().getData();
                        if (uri != null && pendingFileFieldId != null) {
                            handleFileSelection(pendingFileFieldId, uri, isSelectingForChild);
                        }
                    }
                }
        );
        takePictureLauncher = registerForActivityResult(
                new ActivityResultContracts.TakePicture(),
                success -> {
                    if (success && currentPhotoUri != null && pendingFileFieldId != null) {
                        handleFileSelection(pendingFileFieldId, currentPhotoUri, isSelectingForChild);
                    } else if (!success) {
                        currentPhotoUri = null;
                    }
                }
        );
        requestPermissionLauncher = registerForActivityResult(
                new ActivityResultContracts.RequestPermission(),
                isGranted -> {
                    if (isGranted) {
                        openCamera();
                    } else {
                        Toast.makeText(getContext(), "Kameratillatelse er påkrevd for å ta bilde.", Toast.LENGTH_LONG).show();
                    }
                }
        );
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_forms, container, false);
        formContainer = view.findViewById(R.id.form_container);
        historyContainer = view.findViewById(R.id.historyContainer);
        historyWrapper = view.findViewById(R.id.history_wrapper);
        txtStatus = view.findViewById(R.id.txt_status);
        lblHistory = view.findViewById(R.id.lbl_history);
        loadingSpinner = view.findViewById(R.id.loading_spinner);

        // NYTT: Finn toolbar og sett listener
        toolbar = view.findViewById(R.id.forms_toolbar);
        if (toolbar != null) {
            toolbar.setNavigationOnClickListener(v -> Navigation.findNavController(view).navigateUp());
        }

        btnToggleHistory = view.findViewById(R.id.btn_toggle_history);
        if (btnToggleHistory != null) {
            btnToggleHistory.setOnClickListener(v -> toggleHistoryVisibility());
        }

        if (view instanceof ViewGroup) {
            LayoutTransition transition = ((ViewGroup) view).getLayoutTransition();
            if (transition == null) {
                transition = new LayoutTransition();
                ((ViewGroup) view).setLayoutTransition(transition);
            }
            transition.enableTransitionType(LayoutTransition.CHANGING);
        }

        if (formContainer == null) {
            formContainer = new LinearLayout(getContext());
        }

        if (getArguments() != null) {
            int argId = getArguments().getInt("formId", 0);
            if (argId != 0) formId = argId;
        }

        fetchFormStructure();

        return view;
    }

    // --- UI LOGIKK FOR DELT SKJERM ---

    private void expandFormModule() {
        if (historyWrapper != null && historyWrapper.getVisibility() == View.VISIBLE) {
            historyWrapper.setVisibility(View.GONE);
            if (btnToggleHistory != null) {
                btnToggleHistory.setImageResource(android.R.drawable.arrow_down_float);
            }
        }
    }

    private void toggleHistoryVisibility() {
        if (historyWrapper == null || btnToggleHistory == null) return;

        if (historyWrapper.getVisibility() == View.VISIBLE) {
            historyWrapper.setVisibility(View.GONE);
            btnToggleHistory.setImageResource(android.R.drawable.arrow_down_float);
        } else {
            historyWrapper.setVisibility(View.VISIBLE);
            btnToggleHistory.setImageResource(android.R.drawable.arrow_up_float);
        }
    }

    private void attachInteractionListener(View view) {
        if (view == null) return;
        view.setOnTouchListener((v, event) -> {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                expandFormModule();
            }
            return false;
        });
        view.setOnFocusChangeListener((v, hasFocus) -> {
            if (hasFocus) {
                expandFormModule();
            }
        });
        if (view.isClickable()) {
            view.setOnClickListener(v -> expandFormModule());
        }
    }

    // ----------------------------------

    private void fetchFormStructure() {
        if (loadingSpinner != null) loadingSpinner.setVisibility(View.VISIBLE);
        updateStatus("Laster skjema...");

        WordPressApiService api = RetrofitClient.getApiService();
        api.getForm(formId).enqueue(new retrofit2.Callback<GravityForm>() {
            @Override
            public void onResponse(retrofit2.Call<GravityForm> call, retrofit2.Response<GravityForm> response) {
                if (response.isSuccessful() && response.body() != null) {
                    currentForm = response.body();
                    if (getActivity() != null) {
                        getActivity().runOnUiThread(() -> {
                            if (loadingSpinner != null) loadingSpinner.setVisibility(View.GONE);
                            renderDynamicForm(currentForm);
                            fetchFormEntries();
                        });
                    }
                } else {
                    updateStatus("Feil ved lasting av skjema.");
                    if (loadingSpinner != null) loadingSpinner.setVisibility(View.GONE);
                }
            }

            @Override
            public void onFailure(retrofit2.Call<GravityForm> call, Throwable t) {
                updateStatus("Nettverksfeil (Skjema): " + t.getMessage());
                if (loadingSpinner != null) loadingSpinner.setVisibility(View.GONE);
            }
        });
    }

    private void renderDynamicForm(GravityForm form) {
        if (formContainer == null) return;
        formContainer.removeAllViews();
        fieldWrappers.clear();
        inputViews.clear();
        requiredFieldsMap.clear();
        fileUploads.clear();
        nestedEntries.clear();
        updateStatus("");

        // NYTT: Sett tittelen i Toolbaren i stedet for å legge til en TextView
        if (toolbar != null) {
            toolbar.setTitle(getCleanTitle(form.title));
        }

        if (historyWrapper != null) {
            historyWrapper.setVisibility(View.VISIBLE);
            if (btnToggleHistory != null) btnToggleHistory.setImageResource(android.R.drawable.arrow_up_float);
        }

        // Beskrivelse legges fortsatt inn som innhold
        if (form.description != null && !form.description.isEmpty()) {
            TextView formDesc = new TextView(getContext());
            String cleanDesc = form.description.replaceFirst("^\\d+\\.\\s*", "");
            formDesc.setText(cleanDesc);
            formDesc.setPadding(0, 0, 0, 40);
            formContainer.addView(formDesc);
        }

        if (form.fields == null) return;
        for (GravityField field : form.fields) {
            if ("hidden".equals(field.type) || field.isHidden || "hidden".equals(field.visibility)) {
                continue;
            }

            LinearLayout fieldWrapper = new LinearLayout(getContext());
            fieldWrapper.setOrientation(LinearLayout.VERTICAL);
            fieldWrapper.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
            fieldWrapper.setPadding(0, 10, 0, 20);

            fieldWrappers.put(String.valueOf(field.id), fieldWrapper);

            if ("section".equals(field.type)) {
                addSectionHeader(fieldWrapper, field.label, field.description);
                formContainer.addView(fieldWrapper);
                continue;
            }

            if ("html".equals(field.type)) {
                if (field.content != null && !field.content.isEmpty()) {
                    TextView htmlView = new TextView(getContext());
                    htmlView.setText(Html.fromHtml(field.content, Html.FROM_HTML_MODE_COMPACT));
                    fieldWrapper.addView(htmlView);
                }
                formContainer.addView(fieldWrapper);
                continue;
            }

            TextView label = new TextView(getContext());
            String labelText = field.label;
            if (field.isRequired) labelText += " *";
            label.setText(labelText);
            label.setTextColor(Color.DKGRAY);
            label.setTypeface(null, Typeface.BOLD);
            label.setPadding(0, 10, 0, 5);
            fieldWrapper.addView(label);

            if ("form".equals(field.type)) {
                renderNestedFormField(fieldWrapper, field);
            }
            else if ("product".equals(field.type) && "calculation".equals(field.inputType)) {
                renderTotalSumField(fieldWrapper, field);
            }
            else if ("time".equals(field.type)) {
                renderTimeField(fieldWrapper, field, inputViews, requiredFieldsMap);
            } else if ("fileupload".equals(field.type)) {
                renderFileUploadField(fieldWrapper, field, inputViews, requiredFieldsMap, false);
            } else if (field.inputs != null && !field.inputs.isEmpty()) {
                if ("consent".equals(field.type)) {
                    renderConsentField(fieldWrapper, field, inputViews, requiredFieldsMap);
                } else if ("checkbox".equals(field.type) || "multi_choice".equals(field.type)) {
                    renderCheckboxField(fieldWrapper, field, inputViews, requiredFieldsMap);
                } else {
                    renderCompositeField(fieldWrapper, field, inputViews, requiredFieldsMap);
                }
            } else if ("radio".equals(field.type)) {
                renderRadioField(fieldWrapper, field, inputViews, requiredFieldsMap);
            } else if ("select".equals(field.type)) {
                renderSelectField(fieldWrapper, field, inputViews, requiredFieldsMap);
            } else if ("textarea".equals(field.type)) {
                renderTextAreaField(fieldWrapper, field, inputViews, requiredFieldsMap);
            } else if ("date".equals(field.type)) {
                renderDateField(fieldWrapper, field, inputViews, requiredFieldsMap);
            } else if ("consent".equals(field.type)) {
                renderConsentField(fieldWrapper, field, inputViews, requiredFieldsMap);
            } else {
                renderTextField(fieldWrapper, field, inputViews, requiredFieldsMap);
            }

            if (field.description != null && !field.description.isEmpty()) {
                TextView desc = new TextView(getContext());
                desc.setText(Html.fromHtml(field.description, Html.FROM_HTML_MODE_COMPACT));
                desc.setTextSize(12);
                desc.setTextColor(Color.GRAY);
                fieldWrapper.addView(desc);
            }

            formContainer.addView(fieldWrapper);
        }

        Button dynamicSubmit = new Button(getContext());
        dynamicSubmit.setText("Send inn skjema");
        dynamicSubmit.setTextColor(Color.WHITE);
        dynamicSubmit.setBackgroundColor(Color.parseColor("#0069B3")); // KBS Blå
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        params.setMargins(0, 60, 0, 20);
        dynamicSubmit.setLayoutParams(params);
        dynamicSubmit.setOnClickListener(v -> submitDynamicForm());
        formContainer.addView(dynamicSubmit);

        evaluateAllConditionalLogic();
    }

    // --- NESTED FORM LOGIKK ---

    private void renderNestedFormField(LinearLayout container, GravityField field) {
        nestedEntriesContainer = new LinearLayout(getContext());
        nestedEntriesContainer.setOrientation(LinearLayout.VERTICAL);
        nestedEntriesContainer.setPadding(0, 10, 0, 10);
        container.addView(nestedEntriesContainer);

        Button btnAdd = new Button(getContext());
        btnAdd.setText("Legg til vedlegg");
        btnAdd.setBackgroundColor(Color.parseColor("#53AFE9"));
        btnAdd.setTextColor(Color.WHITE);
        btnAdd.setOnClickListener(v -> {
            expandFormModule();
            int childFormId = 18;
            if (field.gpnfForm != null) {
                try {
                    childFormId = Integer.parseInt(field.gpnfForm);
                } catch (NumberFormatException e) { e.printStackTrace(); }
            }
            openChildFormDialog(childFormId, field.id);
        });
        container.addView(btnAdd);

        EditText hiddenIds = new EditText(getContext());
        hiddenIds.setVisibility(View.GONE);
        inputViews.put(field.id, hiddenIds);
    }

    private void renderTotalSumField(LinearLayout container, GravityField field) {
        totalAmountView = new TextView(getContext());
        totalAmountView.setText("Kr 0,00");
        totalAmountView.setTextSize(18);
        totalAmountView.setTypeface(null, Typeface.BOLD);
        totalAmountView.setPadding(10, 10, 10, 10);
        container.addView(totalAmountView);
    }

    private void openChildFormDialog(int childFormId, String parentFieldId) {
        if (getActivity() == null) return;
        ProgressBar pBar = new ProgressBar(getContext());
        AlertDialog loadingDialog = new AlertDialog.Builder(getContext())
                .setView(pBar)
                .setMessage("Laster skjema...")
                .setCancelable(false)
                .show();
        RetrofitClient.getApiService().getForm(childFormId).enqueue(new retrofit2.Callback<GravityForm>() {
            @Override
            public void onResponse(retrofit2.Call<GravityForm> call, retrofit2.Response<GravityForm> response) {
                loadingDialog.dismiss();
                if (response.isSuccessful() && response.body() != null) {
                    showChildFormDialog(response.body(), parentFieldId);
                } else {
                    Toast.makeText(getContext(), "Kunne ikke hente underskjema", Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onFailure(retrofit2.Call<GravityForm> call, Throwable t) {
                loadingDialog.dismiss();
                Toast.makeText(getContext(), "Feil: " + t.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
    }

    private void showChildFormDialog(GravityForm childForm, String parentFieldId) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
        childInputViews.clear();
        childRequiredFieldsMap.clear();
        childFileUploads.clear();

        ScrollView scrollView = new ScrollView(getContext());
        LinearLayout layout = new LinearLayout(getContext());
        layout.setOrientation(LinearLayout.VERTICAL);
        layout.setPadding(30, 30, 30, 30);
        scrollView.addView(layout);
        for (GravityField field : childForm.fields) {
            if ("hidden".equals(field.type) || field.isHidden) continue;
            LinearLayout wrapper = new LinearLayout(getContext());
            wrapper.setOrientation(LinearLayout.VERTICAL);
            wrapper.setPadding(0, 10, 0, 20);

            TextView label = new TextView(getContext());
            String lText = field.label;
            if (field.isRequired) lText += " *";
            label.setText(lText);
            label.setTypeface(null, Typeface.BOLD);
            wrapper.addView(label);
            if ("fileupload".equals(field.type)) {
                renderFileUploadField(wrapper, field, childInputViews, childRequiredFieldsMap, true);
            } else if ("product".equals(field.type)) {
                EditText input = new EditText(getContext());
                input.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
                input.setHint("Kr 0.00");
                wrapper.addView(input);
                childInputViews.put(field.id, input);
                childRequiredFieldsMap.put(field.id, field.isRequired);
            } else {
                renderTextField(wrapper, field, childInputViews, childRequiredFieldsMap);
            }
            layout.addView(wrapper);
        }

        builder.setView(scrollView);
        builder.setPositiveButton("Legg til vedlegg", null);
        builder.setNegativeButton("Avbryt", (d, w) -> d.dismiss());
        AlertDialog dialog = builder.create();
        dialog.show();

        dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
            submitChildForm(childForm.id, dialog, parentFieldId);
        });
    }

    private void submitChildForm(int childFormId, AlertDialog dialog, String parentFieldId) {
        JSONObject inputValues = new JSONObject();
        for (Map.Entry<String, View> entry : childInputViews.entrySet()) {
            String val = getInputValueGeneric(entry.getValue());
            if (!val.isEmpty()) {
                try {
                    inputValues.put("input_" + entry.getKey(), val);
                } catch (JSONException e) { e.printStackTrace(); }
            }
        }

        if (!childFileUploads.isEmpty()) {
            List<MultipartBody.Part> fileParts = new ArrayList<>();
            Map<String, RequestBody> textParts = new HashMap<>();

            try {
                JSONArray names = inputValues.names();
                if (names != null) {
                    for (int i = 0; i < names.length(); i++) {
                        String key = names.getString(i);
                        String val = inputValues.getString(key);
                        textParts.put(key, RequestBody.create(MultipartBody.FORM, val));
                    }
                }

                if (parentFieldId != null) {
                    textParts.put("gpnf_entry_nested_form_field", RequestBody.create(MultipartBody.FORM, parentFieldId));
                }

                for (Map.Entry<String, Uri> fileEntry : childFileUploads.entrySet()) {
                    String fieldId = fileEntry.getKey();
                    Uri uri = fileEntry.getValue();
                    if (uri != null) {
                        MultipartBody.Part part = getFilePart("input_" + fieldId, uri);
                        if (part != null) fileParts.add(part);
                    }
                }

                Toast.makeText(getContext(), "Laster opp vedlegg...", Toast.LENGTH_SHORT).show();
                RetrofitClient.getApiService().submitMultipartForm(childFormId, textParts, fileParts).enqueue(new retrofit2.Callback<JsonElement>() {
                    @Override
                    public void onResponse(retrofit2.Call<JsonElement> call, retrofit2.Response<JsonElement> response) {
                        if (response.isSuccessful() && response.body() != null) {
                            try {
                                JsonObject json = response.body().getAsJsonObject();
                                if (json.has("is_valid") && json.get("is_valid").getAsBoolean()) {
                                    String entryId = json.has("entry_id") ? json.get("entry_id").getAsString() : "";
                                    String desc = getInputValueGeneric(childInputViews.get("3"));
                                    String price = getInputValueGeneric(childInputViews.get("4"));
                                    addNestedEntry(entryId, desc, price);
                                    dialog.dismiss();
                                } else {
                                    Toast.makeText(getContext(), "Ugyldig respons fra server", Toast.LENGTH_SHORT).show();
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                                Toast.makeText(getContext(), "Feil ved parsing av svar", Toast.LENGTH_SHORT).show();
                            }
                        } else {
                            Toast.makeText(getContext(), "Feil ved opplasting", Toast.LENGTH_SHORT).show();
                        }
                    }
                    @Override
                    public void onFailure(retrofit2.Call<JsonElement> call, Throwable t) {
                        Toast.makeText(getContext(), "Nettverksfeil", Toast.LENGTH_SHORT).show();
                    }
                });
            } catch (Exception e) { e.printStackTrace(); }
        }
    }

    private void addNestedEntry(String entryId, String description, String price) {
        nestedEntries.add(new NestedEntry(entryId, description, price));
        refreshNestedList();
    }

    private void refreshNestedList() {
        if (nestedEntriesContainer == null) return;
        nestedEntriesContainer.removeAllViews();

        double total = 0;
        List<String> ids = new ArrayList<>();
        for (NestedEntry entry : nestedEntries) {
            ids.add(entry.id);
            String cleanPrice = entry.price.replaceAll("[^0-9,.]", "").replace(",", ".");
            try {
                if (!cleanPrice.isEmpty()) total += Double.parseDouble(cleanPrice);
            } catch (NumberFormatException e) { }

            LinearLayout row = new LinearLayout(getContext());
            row.setOrientation(LinearLayout.HORIZONTAL);
            row.setPadding(10, 10, 10, 10);

            TextView txt = new TextView(getContext());
            txt.setText(entry.description + " (" + entry.price + ")");
            txt.setLayoutParams(new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1));

            row.addView(txt);
            nestedEntriesContainer.addView(row);
        }

        if (totalAmountView != null) {
            totalAmountView.setText("Totalt: Kr " + String.format("%.2f", total));
        }

        View hiddenField = inputViews.get("25");
        if (hiddenField instanceof EditText) {
            ((EditText)hiddenField).setText(TextUtils.join(",", ids));
        }
    }

    // --- FELLES METODER (FILE UPLOAD M/ CAMERA STØTTE) ---

    private void renderFileUploadField(LinearLayout container, GravityField field, Map<String, View> viewsMap, Map<String, Boolean> reqMap, boolean isChild) {
        LinearLayout fileLayout = new LinearLayout(getContext());
        fileLayout.setOrientation(LinearLayout.HORIZONTAL);

        Button btnUpload = new Button(getContext());
        btnUpload.setText("Velg fil / Ta bilde");
        btnUpload.setOnClickListener(v -> {
            pendingFileFieldId = field.id;
            isSelectingForChild = isChild;
            expandFormModule();
            showFileSourceDialog();
        });
        TextView txtFileName = new TextView(getContext());
        txtFileName.setText("Ingen fil valgt");
        txtFileName.setPadding(20, 0, 0, 0);
        txtFileName.setTextColor(Color.GRAY);
        btnUpload.setTag(txtFileName);

        fileLayout.addView(btnUpload);
        fileLayout.addView(txtFileName);
        container.addView(fileLayout);

        viewsMap.put(field.id, btnUpload);
        reqMap.put(field.id, field.isRequired);
    }

    private void showFileSourceDialog() {
        String[] options = {"Ta bilde", "Velg fil"};
        new AlertDialog.Builder(getContext())
                .setTitle("Last opp vedlegg")
                .setItems(options, (dialog, which) -> {
                    if (which == 0) {
                        if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA)
                                == PackageManager.PERMISSION_GRANTED) {
                            openCamera();
                        } else {
                            requestPermissionLauncher.launch(Manifest.permission.CAMERA);
                        }
                    } else {
                        if (filePickerLauncher != null) {
                            Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                            intent.setType("*/*");
                            String[] mimeTypes = {"image/jpeg", "image/png", "application/pdf"};
                            intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
                            filePickerLauncher.launch(intent);
                        }
                    }
                })
                .show();
    }

    private void openCamera() {
        currentPhotoUri = createImageUri();
        if (currentPhotoUri != null && takePictureLauncher != null) {
            try {
                takePictureLauncher.launch(currentPhotoUri);
            } catch (Exception e) {
                Toast.makeText(getContext(), "Kunne ikke starte kamera: " + e.getMessage(), Toast.LENGTH_SHORT).show();
                Log.e(TAG, "Camera launch failed", e);
            }
        } else {
            Toast.makeText(getContext(), "Kunne ikke opprette bildefil", Toast.LENGTH_SHORT).show();
        }
    }

    private Uri createImageUri() {
        try {
            String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
            String imageFileName = "JPEG_" + timeStamp + "_";
            File storageDir = requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
            File image = File.createTempFile(imageFileName, ".jpg", storageDir);
            return FileProvider.getUriForFile(requireContext(), "com.kbs.kbsintranett.fileprovider", image);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    private void handleFileSelection(String fieldId, Uri uri, boolean isChild) {
        if (isChild) {
            childFileUploads.put(fieldId, uri);
        } else {
            fileUploads.put(fieldId, uri);
        }

        Map<String, View> targetMap = isChild ? childInputViews : inputViews;
        View view = targetMap.get(fieldId);
        if (view instanceof Button) {
            TextView txtView = (TextView) view.getTag();
            if (txtView != null) {
                txtView.setText(getFileName(uri));
                txtView.setTextColor(Color.BLACK);
            }
        }
    }

    private String getFileName(Uri uri) {
        String result = null;
        if (uri.getScheme().equals("content")) {
            try (Cursor cursor = getContext().getContentResolver().query(uri, null, null, null, null)) {
                if (cursor != null && cursor.moveToFirst()) {
                    int index = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
                    if(index >= 0) result = cursor.getString(index);
                }
            } catch (Exception e) {}
        }
        if (result == null) {
            result = uri.getPath();
            int cut = result.lastIndexOf('/');
            if (cut != -1) result = result.substring(cut + 1);
        }
        return result;
    }

    private String getCleanTitle(String title) {
        if (title == null) return "";
        Matcher m = TITLE_PATTERN.matcher(title.trim());
        if (m.find()) {
            return m.group(2);
        }
        return title;
    }

    // --- STANDARD RENDER METODER ---

    private void renderTimeField(LinearLayout container, GravityField field, Map<String, View> views, Map<String, Boolean> req) {
        EditText timeInput = new EditText(getContext());
        timeInput.setFocusable(false);
        timeInput.setClickable(true);
        timeInput.setHint("00:00");
        timeInput.setOnClickListener(v -> {
            expandFormModule();
            Calendar mcurrentTime = Calendar.getInstance();
            int hour = mcurrentTime.get(Calendar.HOUR_OF_DAY);
            int minute = mcurrentTime.get(Calendar.MINUTE);
            new TimePickerDialog(getContext(), (timePicker, selectedHour, selectedMinute) -> {
                timeInput.setText(String.format("%02d:%02d", selectedHour, selectedMinute));
                evaluateAllConditionalLogic();
            }, hour, minute, true).show();
        });
        container.addView(timeInput);
        views.put(field.id, timeInput);
        req.put(field.id, field.isRequired);
    }

    private void renderTextField(LinearLayout container, GravityField field, Map<String, View> views, Map<String, Boolean> req) {
        EditText input = new EditText(getContext());
        input.setPadding(30, 30, 30, 30);
        input.setBackgroundResource(android.R.drawable.edit_text);

        if ("number".equals(field.type) || "phone".equals(field.type)) {
            input.setInputType(InputType.TYPE_CLASS_PHONE);
        } else if ("email".equals(field.type)) {
            input.setInputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
        } else {
            input.setInputType(InputType.TYPE_CLASS_TEXT);
        }

        if (views == inputViews) {
            UserManager user = UserManager.getInstance();
            String lowerLabel = field.label.toLowerCase();
            if (lowerLabel.contains("e-post")) input.setText(user.getUserEmail());
            if (lowerLabel.contains("navn") || lowerLabel.contains("melder")) input.setText(user.getUserDisplayName());
            if (lowerLabel.contains("stilling")) input.setText(user.getStilling());
            if (lowerLabel.contains("mobil")) input.setText(user.getMobiltelefon());
        }

        input.addTextChangedListener(new TextWatcher() {
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
            public void onTextChanged(CharSequence s, int start, int before, int count) {}
            public void afterTextChanged(Editable s) { evaluateAllConditionalLogic(); }
        });
        attachInteractionListener(input);

        container.addView(input);
        views.put(field.id, input);
        req.put(field.id, field.isRequired);
    }

    private void renderTextAreaField(LinearLayout container, GravityField field, Map<String, View> views, Map<String, Boolean> req) {
        EditText input = new EditText(getContext());
        input.setBackgroundResource(android.R.drawable.edit_text);
        input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
        input.setMinLines(3);
        input.setGravity(android.view.Gravity.TOP | android.view.Gravity.START);

        attachInteractionListener(input);

        container.addView(input);
        views.put(field.id, input);
        req.put(field.id, field.isRequired);
    }

    private void renderRadioField(LinearLayout container, GravityField field, Map<String, View> views, Map<String, Boolean> req) {
        RadioGroup group = new RadioGroup(getContext());
        if (field.choices != null) {
            for (GravityField.Choice choice : field.choices) {
                RadioButton rb = new RadioButton(getContext());
                rb.setText(choice.text);
                rb.setTag(choice.value);
                rb.setOnClickListener(v -> {
                    expandFormModule();
                    evaluateAllConditionalLogic();
                });
                group.addView(rb);
            }
        }
        group.setOnCheckedChangeListener((g, i) -> {
            expandFormModule();
            evaluateAllConditionalLogic();
        });
        container.addView(group);
        views.put(field.id, group);
        req.put(field.id, field.isRequired);
    }

    private void renderSelectField(LinearLayout container, GravityField field, Map<String, View> views, Map<String, Boolean> req) {
        Spinner spinner = new Spinner(getContext());
        spinner.setOnTouchListener((v, event) -> {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                expandFormModule();
            }
            return false;
        });
        List<String> labels = new ArrayList<>();
        labels.add("- Velg -");
        if (field.choices != null) {
            for (GravityField.Choice c : field.choices) labels.add(c.text);
        }
        ArrayAdapter<String> adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, labels);
        spinner.setAdapter(adapter);
        container.addView(spinner);
        views.put(field.id, spinner);
        req.put(field.id, field.isRequired);
    }

    private void renderConsentField(LinearLayout container, GravityField field, Map<String, View> views, Map<String, Boolean> req) {
        CheckBox checkBox = new CheckBox(getContext());
        String cbText = (field.checkboxLabel != null && !field.checkboxLabel.isEmpty()) ? field.checkboxLabel : field.label;
        checkBox.setText(cbText);
        String inputId = (field.inputs != null && !field.inputs.isEmpty()) ? field.inputs.get(0).id : field.id;

        checkBox.setTag("1");
        checkBox.setOnCheckedChangeListener((b, c) -> {
            expandFormModule();
            evaluateAllConditionalLogic();
        });
        container.addView(checkBox);
        views.put(inputId, checkBox);
        req.put(inputId, field.isRequired);
    }

    private void renderCheckboxField(LinearLayout container, GravityField field, Map<String, View> views, Map<String, Boolean> req) {
        if (field.inputs != null) {
            for (int i = 0; i < field.inputs.size(); i++) {
                GravityField inputDef = field.inputs.get(i);
                CheckBox checkBox = new CheckBox(getContext());
                checkBox.setText(inputDef.label);

                String value = "1";
                if (field.choices != null && i < field.choices.size()) {
                    value = field.choices.get(i).value;
                }
                checkBox.setTag(value);
                checkBox.setOnCheckedChangeListener((b, c) -> {
                    expandFormModule();
                    evaluateAllConditionalLogic();
                });
                container.addView(checkBox);
                views.put(inputDef.id, checkBox);
                req.put(inputDef.id, false);
            }
        }
    }

    private void renderDateField(LinearLayout container, GravityField field, Map<String, View> views, Map<String, Boolean> req) {
        EditText dateInput = new EditText(getContext());
        if (field.readOnly || (formId == ID_REFUSJON_UTLEGG && "28".equals(field.id))) {
            SimpleDateFormat df = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault());
            dateInput.setText(df.format(new Date()));

            dateInput.setFocusable(false);
            dateInput.setClickable(false);
            dateInput.setEnabled(false);
            dateInput.setTextColor(Color.BLACK);
        } else {
            dateInput.setFocusable(false);
            dateInput.setClickable(true);
            dateInput.setHint("dd.mm.yyyy");
            dateInput.setOnClickListener(v -> {
                expandFormModule();
                Calendar c = Calendar.getInstance();
                new DatePickerDialog(getContext(), (view, year, month, dayOfMonth) -> {
                    dateInput.setText(String.format("%02d.%02d.%d", dayOfMonth, month + 1, year));
                    evaluateAllConditionalLogic();
                }, c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH)).show();
            });
        }

        dateInput.setPadding(30, 30, 30, 30);
        dateInput.setBackgroundResource(android.R.drawable.edit_text);

        container.addView(dateInput);
        views.put(field.id, dateInput);
        req.put(field.id, field.isRequired);
    }

    private void renderCompositeField(LinearLayout container, GravityField parentField, Map<String, View> views, Map<String, Boolean> req) {
        UserManager user = UserManager.getInstance();
        boolean isPersonalia = (formId == ID_ANSATTEOPPLYSNINGER);

        List<GravityField> inputs = new ArrayList<>(parentField.inputs);
        if ("address".equals(parentField.type)) {
            Collections.sort(inputs, (f1, f2) -> Integer.compare(getAddressScore(f1.label), getAddressScore(f2.label)));
        }

        for (GravityField subField : inputs) {
            if (subField.isHidden || "hidden".equals(subField.visibility)) continue;
            TextView subLabel = new TextView(getContext());
            String subLabelText = subField.label;
            boolean isSubRequired = parentField.isRequired;
            if ("address".equals(parentField.type) && subField.id.endsWith(".2")) {
                isSubRequired = false;
            }
            if (isSubRequired) subLabelText += " *";

            subLabel.setText(subLabelText);
            subLabel.setTextColor(Color.GRAY);
            subLabel.setTextSize(12);
            subLabel.setPadding(0, 10, 0, 0);
            container.addView(subLabel);

            EditText subInput = new EditText(getContext());
            subInput.setPadding(30, 30, 30, 30);
            subInput.setBackgroundResource(android.R.drawable.edit_text);
            subInput.setInputType(InputType.TYPE_CLASS_TEXT);
            if (isPersonalia && parentField.label.toLowerCase().contains("navn") && !parentField.label.toLowerCase().contains("pårørende")) {
                String lowerSub = subField.label.toLowerCase();
                if (lowerSub.contains("fornavn")) subInput.setText(user.getFirstName());
                else if (lowerSub.contains("etternavn")) subInput.setText(user.getLastName());
            }

            attachInteractionListener(subInput);
            container.addView(subInput);
            views.put(subField.id, subInput);
            req.put(subField.id, isSubRequired);
        }
    }

    private void addSectionHeader(LinearLayout container, String title, String descText) {
        TextView sectionHeader = new TextView(getContext());
        sectionHeader.setText(title);
        sectionHeader.setTextSize(18);
        sectionHeader.setTypeface(null, Typeface.BOLD);
        sectionHeader.setTextColor(Color.parseColor("#0069B3"));
        sectionHeader.setPadding(0, 20, 0, 5);
        container.addView(sectionHeader);
        if (descText != null && !descText.isEmpty()) {
            TextView desc = new TextView(getContext());
            desc.setText(Html.fromHtml(descText, Html.FROM_HTML_MODE_COMPACT));
            desc.setTextSize(12);
            desc.setTextColor(Color.GRAY);
            container.addView(desc);
        }

        View line = new View(getContext());
        line.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 2));
        line.setBackgroundColor(Color.LTGRAY);
        line.setPadding(0,0,0,20);
        container.addView(line);
    }

    private int getAddressScore(String label) {
        if (label == null) return 99;
        String l = label.toLowerCase();
        if (l.contains("adresselinje 1")) return 1;
        if (l.contains("adresselinje 2")) return 2;
        if (l.contains("postnummer") || l.contains("zip")) return 3;
        if (l.contains("poststed") || l.contains("city")) return 4;
        if (l.contains("land") || l.contains("country")) return 5;
        return 99;
    }

    private void evaluateAllConditionalLogic() {
        if (currentForm == null || currentForm.fields == null) return;
        for (GravityField field : currentForm.fields) {
            if (field.conditionalLogic == null) {
                setViewVisibility(field.id, true);
                continue;
            }

            boolean isMatch = evaluateLogic(field.conditionalLogic);
            boolean show = "show".equalsIgnoreCase(field.conditionalLogic.actionType);
            boolean shouldBeVisible = (show && isMatch) || (!show && !isMatch);
            setViewVisibility(field.id, shouldBeVisible);
        }
    }

    private boolean evaluateLogic(GravityField.ConditionalLogic logic) {
        if (logic.rules == null || logic.rules.isEmpty()) return true;
        boolean isAll = "all".equalsIgnoreCase(logic.logicType);
        boolean aggregatedResult = isAll;

        for (GravityField.Rule rule : logic.rules) {
            String val = getInputValue(rule.fieldId);
            boolean ruleMatch = checkRule(val, rule.operator, rule.value);

            if (isAll) {
                aggregatedResult = aggregatedResult && ruleMatch;
                if (!aggregatedResult) break;
            } else {
                aggregatedResult = aggregatedResult ||
                        ruleMatch;
                if (aggregatedResult) break;
            }
        }
        return aggregatedResult;
    }

    private boolean checkRule(String actualValue, String operator, String targetValue) {
        if (actualValue == null) actualValue = "";
        if (targetValue == null) targetValue = "";

        switch (operator.toLowerCase()) {
            case "is": return actualValue.equalsIgnoreCase(targetValue);
            case "isnot": return !actualValue.equalsIgnoreCase(targetValue);
            case "contains": return actualValue.toLowerCase().contains(targetValue.toLowerCase());
            case "starts_with": return actualValue.toLowerCase().startsWith(targetValue.toLowerCase());
            case "ends_with": return actualValue.toLowerCase().endsWith(targetValue.toLowerCase());
            default: return false;
        }
    }

    private String getInputValue(String fieldId) {
        View view = inputViews.get(fieldId);
        return getInputValueGeneric(view);
    }

    private String getInputValueGeneric(View view) {
        if (view == null) return "";
        if (view instanceof EditText) return ((EditText) view).getText().toString();
        if (view instanceof RadioGroup) {
            int id = ((RadioGroup) view).getCheckedRadioButtonId();
            if (id != -1) {
                View rb = view.findViewById(id);
                if (rb != null && rb.getTag() != null) return rb.getTag().toString();
            }
        }
        if (view instanceof Spinner) {
            if (((Spinner) view).getSelectedItemPosition() == 0) return "";
            Object item = ((Spinner) view).getSelectedItem();
            return item != null ? item.toString() : "";
        }
        if (view instanceof CheckBox) {
            CheckBox cb = (CheckBox) view;
            if (cb.isChecked()) {
                return cb.getTag() != null ?
                        cb.getTag().toString() : "1";
            }
            return "";
        }
        return "";
    }

    private void setViewVisibility(String fieldId, boolean visible) {
        View wrapper = fieldWrappers.get(fieldId);
        if (wrapper != null) {
            wrapper.setVisibility(visible ? View.VISIBLE : View.GONE);
        }
    }

    // --- SUBMISSION ---

    private void submitDynamicForm() {
        JSONObject inputValues = new JSONObject();
        boolean hasValues = false;

        Log.d(TAG, "submitDynamicForm: Starting validation...");

        for (Map.Entry<String, View> entry : inputViews.entrySet()) {
            String fieldId = entry.getKey();
            View view = entry.getValue();

            View wrapper = fieldWrappers.get(fieldId);
            if (wrapper == null) {
                if (!view.isShown()) continue;
            } else {
                if (wrapper.getVisibility() != View.VISIBLE) continue;
            }

            String val = getInputValueGeneric(view);
            Boolean req = requiredFieldsMap.get(fieldId);
            if (req != null && req && val.isEmpty() && !(view instanceof Button)) {
                Log.d(TAG, "Validation failed for field " + fieldId);
                if (view instanceof EditText) {
                    ((EditText)view).setError("Må fylles ut");
                    view.requestFocus();
                } else {
                    Toast.makeText(getContext(), "Fyll ut alle felt", Toast.LENGTH_SHORT).show();
                }
                return;
            }
            if (!val.isEmpty()) {
                try {
                    GravityField fieldDef = getGravityFieldById(fieldId);
                    if (fieldDef != null && "date".equals(fieldDef.type)) {
                        val = formatDateForApi(val);
                    }

                    inputValues.put("input_" + fieldId, val);
                    hasValues = true;
                } catch (JSONException e) {}
            }
        }

        if (!hasValues && fileUploads.isEmpty()) {
            Log.d(TAG, "Submit aborted: Form is empty");
            Toast.makeText(getContext(), "Skjemaet er tomt", Toast.LENGTH_SHORT).show();
            return;
        }

        updateStatus("Sender inn...");
        String cookie = UserManager.getInstance().getCookie();
        Log.d(TAG, "Preparing submission payload: " + inputValues.toString());

        if (!fileUploads.isEmpty()) {
            Log.d(TAG, "Submitting as Multipart...");
            sendMultipart(inputValues);
        } else {
            Log.d(TAG, "Submitting as JSON...");
            RequestBody body = RequestBody.create(MediaType.parse("application/json"), inputValues.toString());
            String url = BASE_URL_GF + "/forms/" + formId + "/submissions";
            Request request = new Request.Builder().url(url).post(body).header("Cookie", cookie).build();
            client.newCall(request).enqueue(new okhttp3.Callback() {
                public void onFailure(okhttp3.Call call, IOException e) {
                    Log.e(TAG, "JSON submit failed", e);
                    updateStatus("Feil: " + e.getMessage());
                }
                public void onResponse(okhttp3.Call call, Response response) {
                    Log.d(TAG, "JSON response code: " + response.code());
                    if (response.isSuccessful()) {
                        if (getActivity() != null) getActivity().runOnUiThread(() -> {
                            Toast.makeText(getContext(), "Sendt!", Toast.LENGTH_LONG).show();
                            fetchFormEntries();
                            updateStatus("OK");
                            clearInputs();
                        });
                    } else {
                        try {
                            String errBody = response.body() != null ? response.body().string() : "No body";
                            Log.e(TAG, "Server error body: " + errBody);
                            updateStatus("Feil (" + response.code() + "): " + errBody);
                        } catch(Exception e){}
                    }
                }
            });
        }
    }

    private String formatDateForApi(String dateStr) {
        if (dateStr == null || dateStr.isEmpty()) return "";
        try {
            SimpleDateFormat displayFormat = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault());
            Date date = displayFormat.parse(dateStr);
            SimpleDateFormat apiFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
            return apiFormat.format(date);
        } catch (Exception e) {
            return dateStr;
        }
    }

    private GravityField getGravityFieldById(String id) {
        if (currentForm == null || currentForm.fields == null) return null;
        for (GravityField f : currentForm.fields) {
            if (f.id.equals(id)) return f;
            if (f.inputs != null) {
                for (GravityField sub : f.inputs) {
                    if (sub.id.equals(id)) return sub;
                }
            }
        }
        return null;
    }

    private void sendMultipart(JSONObject inputValues) {
        List<MultipartBody.Part> fileParts = new ArrayList<>();
        Map<String, RequestBody> textParts = new HashMap<>();
        try {
            JSONArray names = inputValues.names();
            if (names != null) {
                for(int i=0; i<names.length(); i++) {
                    String k = names.getString(i);
                    textParts.put(k, RequestBody.create(MultipartBody.FORM, inputValues.getString(k)));
                }
            }
            for (Map.Entry<String, Uri> entry : fileUploads.entrySet()) {
                MultipartBody.Part part = getFilePart("input_" + entry.getKey(), entry.getValue());
                if (part != null) fileParts.add(part);
            }
            RetrofitClient.getApiService().submitMultipartForm(formId, textParts, fileParts).enqueue(new retrofit2.Callback<JsonElement>() {
                public void onResponse(retrofit2.Call<JsonElement> call, retrofit2.Response<JsonElement> response) {
                    if (response.isSuccessful()) {
                        if (getActivity() != null) getActivity().runOnUiThread(() -> {
                            Toast.makeText(getContext(), "Sendt!", Toast.LENGTH_LONG).show();
                            fetchFormEntries();
                            updateStatus("OK");
                            clearInputs();
                        });
                    } else {
                        updateStatus("Feil: " + response.code());
                    }
                }
                public void onFailure(retrofit2.Call<JsonElement> call, Throwable t) { updateStatus("Feil: " + t.getMessage()); }
            });
        } catch (Exception e) {}
    }

    private MultipartBody.Part getFilePart(String partName, Uri uri) {
        try {
            InputStream inputStream = getContext().getContentResolver().openInputStream(uri);
            String fileName = getFileName(uri);
            RequestBody requestBody = new RequestBody() {
                @Override public MediaType contentType() { return MediaType.parse("application/octet-stream");
                }
                @Override public void writeTo(BufferedSink sink) throws IOException {
                    try (Source source = Okio.source(inputStream)) { sink.writeAll(source);
                    }
                }
            };
            return MultipartBody.Part.createFormData(partName, fileName, requestBody);
        } catch (Exception e) { return null;
        }
    }

    private void fetchFormEntries() {
        UserManager user = UserManager.getInstance();
        String cookie = user.getCookie();
        int userId = user.getUserId();

        if (cookie == null) return;
        String searchJson = "{\"field_filters\":[{\"key\":\"created_by\",\"value\":\"" + userId + "\"}]}";
        String encodedSearch = "";
        try {
            encodedSearch = URLEncoder.encode(searchJson, "UTF-8");
        } catch (UnsupportedEncodingException e) { e.printStackTrace(); }

        String url = BASE_URL_GF + "/entries?form_ids=" + formId + "&search=" + encodedSearch;
        Request request = new Request.Builder().url(url).header("Cookie", cookie).build();

        client.newCall(request).enqueue(new okhttp3.Callback() {
            @Override
            public void onFailure(@NonNull okhttp3.Call call, @NonNull IOException e) {
                Log.e(TAG, "Kunne ikke hente historikk", e);
            }

            @Override
            public void onResponse(@NonNull okhttp3.Call call, @NonNull Response response) throws IOException {
                if (response.isSuccessful()) {
                    String jsonStr = response.body().string();
                    try {
                        JSONObject json = new JSONObject(jsonStr);
                        if (json.has("entries")) {
                            JSONArray entries = json.getJSONArray("entries");
                            if (getActivity() != null) {
                                getActivity().runOnUiThread(() -> {
                                    showHistory(entries);
                                    if (formId == ID_ANSATTEOPPLYSNINGER && entries.length() > 0)
                                    {
                                        try {
                                            prefillFormFromHistory(entries.getJSONObject(0));
                                        } catch (JSONException e) { e.printStackTrace(); }
                                    }
                                });
                            }
                        }
                    } catch (JSONException e) { e.printStackTrace();
                    }
                }
            }
        });
    }

    private void showHistory(JSONArray entries) {
        if (historyContainer == null) return;
        historyContainer.removeAllViews();

        if (entries.length() == 0) {
            if (lblHistory != null) lblHistory.setVisibility(View.GONE);
            return;
        } else {
            if (lblHistory != null) lblHistory.setVisibility(View.VISIBLE);
        }

        try {
            int count = Math.min(entries.length(), 20);
            for (int i = 0; i < count; i++) {
                JSONObject entry = entries.getJSONObject(i);
                String date = entry.optString("date_created");

                String titleText = "Innsendt: " + date;
                TextView item = new TextView(getContext());
                item.setText(titleText);
                item.setPadding(10, 20, 10, 20);
                item.setBackgroundResource(android.R.drawable.list_selector_background);
                item.setTextSize(14);
                item.setOnClickListener(v -> showEntryDetails(entry));
                View line = new View(getContext());
                line.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1));
                line.setBackgroundColor(Color.LTGRAY);

                historyContainer.addView(item);
                historyContainer.addView(line);
            }
        } catch (JSONException e) { e.printStackTrace();
        }
    }

    private void showEntryDetails(JSONObject entry) {
        if (formId == ID_REFUSJON_UTLEGG) {
            Log.d(TAG, "Form 16 detected. Checking for child entries...");
            String nestedIds = entry.optString("25");

            if (!nestedIds.isEmpty()) {
                Log.d(TAG, "Nested IDs found: " + nestedIds);
                List<String> ids = new ArrayList<>();

                if (nestedIds.startsWith("[") && nestedIds.endsWith("]")) {
                    try {
                        JSONArray jsonArray = new JSONArray(nestedIds);
                        for(int i=0; i<jsonArray.length(); i++) {
                            ids.add(jsonArray.getString(i));
                        }
                    } catch(JSONException e) {
                        Log.e(TAG, "Failed to parse nested IDs as JSON array", e);
                    }
                } else {
                    for (String id : nestedIds.split(",")) {
                        ids.add(id.trim());
                    }
                }

                if (!ids.isEmpty()) {
                    AlertDialog loadingDialog = new AlertDialog.Builder(getContext())
                            .setMessage("Henter vedleggsdetaljer...")
                            .setCancelable(false)
                            .show();

                    StringBuilder accumulatedHtml = new StringBuilder();
                    StringBuilder accumulatedText = new StringBuilder();

                    appendBasicInfo(entry, accumulatedHtml, accumulatedText);

                    fetchChildEntriesRecursive(ids, 0, accumulatedHtml, accumulatedText, loadingDialog);
                    return;
                }
            }
        }

        StringBuilder htmlBuilder = new StringBuilder();
        StringBuilder textBuilder = new StringBuilder();
        appendBasicInfo(entry, htmlBuilder, textBuilder);
        showFinalDialog(htmlBuilder, textBuilder);
    }

    private void appendBasicInfo(JSONObject entry, StringBuilder html, StringBuilder text) {
        try {
            String date = entry.optString("date_created");
            html.append("<b>Innsendt:</b> ").append(date).append("<br><br>");
            text.append("Innsendt: ").append(date).append("\n\n");

            if (currentForm != null && currentForm.fields != null) {
                for (GravityField field : currentForm.fields) {
                    if ("section".equals(field.type) || "html".equals(field.type) || "captcha".equals(field.type)) continue;
                    if (formId == ID_REFUSJON_UTLEGG && "25".equals(field.id)) continue;

                    String value = "";
                    if (field.inputs != null && !field.inputs.isEmpty()) {
                        for (GravityField input : field.inputs) {
                            String subVal = entry.optString(input.id);
                            if (!subVal.isEmpty()) value += " " + subVal;
                        }
                    } else {
                        value = entry.optString(String.valueOf(field.id));
                    }

                    if (!value.trim().isEmpty()) {
                        if ("fileupload".equals(field.type)) {
                            String cleanUrl = extractUrl(value);
                            if (cleanUrl.startsWith("http")) {
                                html.append("<b>").append(field.label).append(":</b><br>")
                                        .append("<a href=\"").append(cleanUrl).append("\">Åpne fil</a><br><br>");
                                text.append(field.label).append(":\n").append(cleanUrl).append("\n\n");
                            } else {
                                html.append("<b>").append(field.label).append(":</b><br>").append(value).append("<br><br>");
                                text.append(field.label).append(":\n").append(value).append("\n\n");
                            }
                        } else {
                            html.append("<b>").append(field.label).append(":</b><br>").append(value).append("<br><br>");
                            text.append(field.label).append(":\n").append(value).append("\n\n");
                        }
                    }
                }
            }
        } catch (Exception e) {}
    }

    private void fetchChildEntriesRecursive(List<String> ids, int index, StringBuilder html, StringBuilder text, AlertDialog loader) {
        if (index >= ids.size()) {
            loader.dismiss();
            showFinalDialog(html, text);
            return;
        }

        String entryId = ids.get(index);
        RetrofitClient.getApiService().getSingleEntry(entryId).enqueue(new retrofit2.Callback<JsonElement>() {
            @Override
            public void onResponse(retrofit2.Call<JsonElement> call, retrofit2.Response<JsonElement> response) {
                if (response.isSuccessful() && response.body() != null) {
                    try {
                        JsonObject json = response.body().getAsJsonObject();
                        String desc = json.has("3") ? json.get("3").getAsString() : "Uten beskrivelse";
                        String price = json.has("4") ? json.get("4").getAsString() : "";

                        html.append("<b>Vedlegg ").append(index + 1).append(":</b><br>");
                        text.append("Vedlegg ").append(index + 1).append(":\n");

                        html.append(desc).append(" (").append(price).append(")<br>");
                        text.append(desc).append(" (").append(price).append(")\n");

                        if (json.has("1")) {
                            JsonElement fileEl = json.get("1");
                            if (fileEl.isJsonArray()) {
                                JsonArray arr = fileEl.getAsJsonArray();
                                for (int i = 0; i < arr.size(); i++) {
                                    String url = arr.get(i).getAsString().replace("\\/", "/");
                                    html.append("<a href=\"").append(url).append("\">Åpne fil ").append(i+1).append("</a><br>");
                                    text.append(url).append("\n");
                                }
                            } else {
                                String rawString = fileEl.getAsString();
                                if (rawString.startsWith("[") && rawString.endsWith("]")) {
                                    try {
                                        JSONArray arr = new JSONArray(rawString);
                                        for (int i = 0; i < arr.length(); i++) {
                                            String url = arr.getString(i).replace("\\/", "/");
                                            html.append("<a href=\"").append(url).append("\">Åpne fil ").append(i+1).append("</a><br>");
                                            text.append(url).append("\n");
                                        }
                                    } catch (JSONException ex) {
                                        String clean = extractUrl(rawString);
                                        html.append("<a href=\"").append(clean).append("\">Åpne fil</a><br>");
                                        text.append(clean).append("\n");
                                    }
                                } else {
                                    String clean = extractUrl(rawString);
                                    if(clean.startsWith("http")) {
                                        html.append("<a href=\"").append(clean).append("\">Åpne fil</a><br>");
                                        text.append(clean).append("\n");
                                    }
                                }
                            }
                        }
                        html.append("<br>");
                        text.append("\n");

                    } catch (Exception e) {
                        Log.e(TAG, "Error parsing child entry", e);
                    }
                }
                fetchChildEntriesRecursive(ids, index + 1, html, text, loader);
            }

            @Override
            public void onFailure(retrofit2.Call<JsonElement> call, Throwable t) {
                fetchChildEntriesRecursive(ids, index + 1, html, text, loader);
            }
        });
    }

    private void showFinalDialog(StringBuilder htmlBuilder, StringBuilder textBuilder) {
        ScrollView scroll = new ScrollView(getContext());
        TextView text = new TextView(getContext());
        text.setMovementMethod(android.text.method.LinkMovementMethod.getInstance());
        text.setText(Html.fromHtml(htmlBuilder.toString(), Html.FROM_HTML_MODE_COMPACT));
        text.setPadding(40, 40, 40, 40);
        scroll.addView(text);

        new AlertDialog.Builder(getContext())
                .setTitle("Detaljer")
                .setView(scroll)
                .setPositiveButton("Lukk", null)
                .setNeutralButton("Del", (d, w) -> shareEntryDetails(textBuilder.toString()))
                .create()
                .show();
    }

    private String extractUrl(String rawValue) {
        if (rawValue == null) return "";
        String clean = rawValue.replace("[", "")
                .replace("]", "")
                .replace("\"", "")
                .replace("\\/", "/");
        return clean.trim();
    }

    private void shareEntryDetails(String text) {
        Intent sendIntent = new Intent();
        sendIntent.setAction(Intent.ACTION_SEND);
        sendIntent.putExtra(Intent.EXTRA_TEXT, text);
        sendIntent.setType("text/plain");

        Intent shareIntent = Intent.createChooser(sendIntent, "Del innsending via...");
        startActivity(shareIntent);
    }

    private void prefillFormFromHistory(JSONObject latestEntry) {
        if (latestEntry == null) return;
        for (Map.Entry<String, View> entry : inputViews.entrySet()) {
            String fieldId = entry.getKey();
            View view = entry.getValue();

            if (latestEntry.has(fieldId)) {
                String value = latestEntry.optString(fieldId);
                if (value == null || value.isEmpty()) continue;

                if (view instanceof EditText) {
                    ((EditText) view).setText(value);
                } else if (view instanceof RadioGroup) {
                    RadioGroup group = (RadioGroup) view;
                    for (int i = 0; i < group.getChildCount(); i++) {
                        View child = group.getChildAt(i);
                        if (child instanceof RadioButton) {
                            Object tag = child.getTag();
                            if (tag != null && tag.toString().equalsIgnoreCase(value)) {
                                ((RadioButton) child).setChecked(true);
                                break;
                            }
                        }
                    }
                } else if (view instanceof CheckBox) {
                    if ("1".equals(value) || "true".equalsIgnoreCase(value) || ((CheckBox)view).getText().toString().equals(value)) {
                        ((CheckBox) view).setChecked(true);
                    }
                }
            }
        }
        updateStatus("Skjemaet er forhåndsutfylt fra din siste innsending.");
        evaluateAllConditionalLogic();
    }

    private void clearInputs() {
        for (View view : inputViews.values()) {
            if (view instanceof EditText) {
                ((EditText) view).setText("");
            } else if (view instanceof CheckBox) {
                ((CheckBox) view).setChecked(false);
            } else if (view instanceof RadioGroup) {
                ((RadioGroup) view).clearCheck();
            } else if (view instanceof Button) {
                Object tag = view.getTag();
                if (tag instanceof TextView) {
                    ((TextView) tag).setText("Ingen fil valgt");
                }
            }
        }
        fileUploads.clear();
        nestedEntries.clear();
        if (nestedEntriesContainer != null) nestedEntriesContainer.removeAllViews();
        if (totalAmountView != null) totalAmountView.setText("Kr 0,00");
    }

    private static class NestedEntry {
        String id;
        String description;
        String price;
        NestedEntry(String id, String d, String p) { this.id = id; this.description = d; this.price = p;
        }
    }

    private void updateStatus(String msg) {
        if (getActivity() != null && txtStatus != null) {
            getActivity().runOnUiThread(() -> txtStatus.setText(msg));
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\FormsListFragment.java
============================================================
package com.kbs.kbsintranett;

import android.graphics.Color;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class FormsListFragment extends Fragment {

    private LinearLayout formsContainer;
    private ProgressBar progressBar;
    private TextView errorText;
    private SwipeRefreshLayout swipeRefreshLayout;
    private static final Pattern TITLE_NUMBER_PATTERN = Pattern.compile("^(\\d+)[.\\s-]+\\s*(.*)");

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_forms_list, container, false);

        formsContainer = view.findViewById(R.id.forms_container);
        swipeRefreshLayout = view.findViewById(R.id.swipe_refresh);

        // Opprett en ProgressBar manuelt for første gangs lasting
        progressBar = new ProgressBar(getContext());
        LinearLayout.LayoutParams progressParams = new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        progressParams.gravity = Gravity.CENTER;
        progressBar.setLayoutParams(progressParams);

        formsContainer.addView(progressBar);

        errorText = new TextView(getContext());
        errorText.setTextColor(Color.RED);
        errorText.setVisibility(View.GONE);
        errorText.setPadding(20, 20, 20, 20);
        formsContainer.addView(errorText);

        // Sett opp listener for swipe
        swipeRefreshLayout.setOnRefreshListener(() -> {
            fetchFormsList();
        });

        // Hent data første gang
        fetchFormsList();

        return view;
    }

    private void fetchFormsList() {
        // Skjul feilmelding før ny henting
        if (errorText != null) errorText.setVisibility(View.GONE);

        RetrofitClient.getApiService().getFormsList().enqueue(new Callback<List<GravityForm>>() {
            @Override
            public void onResponse(Call<List<GravityForm>> call, Response<List<GravityForm>> response) {
                if (!isAdded()) return;

                // Stopp lasting-indikatorer
                progressBar.setVisibility(View.GONE);
                swipeRefreshLayout.setRefreshing(false);

                if (response.isSuccessful() && response.body() != null) {
                    List<GravityForm> allForms = response.body();
                    List<GravityForm> activeForms = new ArrayList<>();

                    for (GravityForm form : allForms) {
                        if (form.getIsActive()) {
                            // NYTT: Hardkodet filtrering av skjemaer som ikke skal vises i listen
                            // ID 10 = HMS-bekreftelse (Skal ligge i Håndbok/separat flyt)
                            // ID 18 = Refusjon-vedlegg (Er et underskjema som brukes av ID 16)
                            if (form.id == 10 || form.id == 18) {
                                continue;
                            }

                            activeForms.add(form);
                        }
                    }

                    Collections.sort(activeForms, new Comparator<GravityForm>() {
                        @Override
                        public int compare(GravityForm f1, GravityForm f2) {
                            int num1 = extractNumber(f1.title);
                            int num2 = extractNumber(f2.title);
                            return Integer.compare(num1, num2);
                        }
                    });
                    populateList(activeForms);

                } else {
                    String msg = "Kunne ikke hente skjemaer. Kode: " + response.code();
                    if (response.code() == 401 || response.code() == 403) {
                        msg += "\n(Mangler tilgang. Prøv å logge ut og inn igjen.)";
                    }
                    showError(msg);
                }
            }

            @Override
            public void onFailure(Call<List<GravityForm>> call, Throwable t) {
                if (!isAdded()) return;

                // Stopp lasting-indikatorer
                progressBar.setVisibility(View.GONE);
                swipeRefreshLayout.setRefreshing(false);

                showError("Nettverksfeil: " + t.getMessage());
            }
        });
    }

    private void populateList(List<GravityForm> forms) {
        formsContainer.removeAllViews();

        if (forms.isEmpty()) {
            showError("Ingen aktive skjemaer funnet.");
            return;
        }

        for (GravityForm form : forms) {
            int formId = form.id;
            String cleanTitle = cleanTitle(form.title);
            addFormButton(formsContainer, cleanTitle, formId);
        }
    }

    private void addFormButton(LinearLayout container, String title, int formId) {
        Button btn = new Button(getContext());
        btn.setText(title);
        btn.setBackgroundColor(Color.parseColor("#0069B3"));
        btn.setTextColor(Color.WHITE);
        btn.setPadding(30, 30, 30, 30);

        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        params.setMargins(0, 0, 0, 20);
        btn.setLayoutParams(params);

        btn.setOnClickListener(v -> {
            Bundle bundle = new Bundle();
            bundle.putInt("formId", formId);
            Navigation.findNavController(v).navigate(R.id.action_formsListFragment_to_formsDetailFragment, bundle);
        });
        container.addView(btn);
    }

    private void showError(String message) {
        if (formsContainer == null) return;
        formsContainer.removeAllViews();

        TextView tv = new TextView(getContext());
        tv.setText(message);
        tv.setTextColor(Color.RED);
        tv.setTextSize(16);
        formsContainer.addView(tv);
    }

    private int extractNumber(String title) {
        if (title == null) return 9999;
        Matcher m = TITLE_NUMBER_PATTERN.matcher(title.trim());
        if (m.find()) {
            try {
                return Integer.parseInt(m.group(1));
            } catch (NumberFormatException e) {
                return 9999;
            }
        }
        return 9999;
    }

    private String cleanTitle(String title) {
        if (title == null) return "";
        Matcher m = TITLE_NUMBER_PATTERN.matcher(title.trim());
        if (m.find()) {
            return m.group(2);
        }
        return title;
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\FormSubmission.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.annotations.SerializedName;
import java.util.Map;

public class FormSubmission {
    // Gravity Forms krever at dataene ligger inni "input_values"
    @SerializedName("input_values")
    public Map<String, String> inputValues;

    public FormSubmission(Map<String, String> inputValues) {
        this.inputValues = inputValues;
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\GoogleCalendarModels.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.annotations.SerializedName;
import java.util.List;

/**
 * Hjelpeklasser for å parse JSON direkte fra Google Calendar API v3.
 */
public class GoogleCalendarModels {

    public static class Response {
        @SerializedName("items")
        public List<Item> items;
    }

    public static class Item {
        @SerializedName("summary")
        public String summary;

        @SerializedName("description")
        public String description;

        @SerializedName("location")
        public String location;

        @SerializedName("start")
        public TimePoint start;

        @SerializedName("end")
        public TimePoint end;

        @SerializedName("reminders")
        public Reminders reminders;
    }

    public static class TimePoint {
        @SerializedName("dateTime")
        public String dateTime; // Format: 2025-12-15T10:00:00+01:00

        @SerializedName("date")
        public String date; // Format: 2025-12-15 (for heldags)
    }

    public static class Reminders {
        @SerializedName("useDefault")
        public boolean useDefault;

        @SerializedName("overrides")
        public List<Override> overrides;
    }

    public static class Override {
        @SerializedName("method")
        public String method; // f.eks "popup"

        @SerializedName("minutes")
        public int minutes;
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\GravityEntryResponse.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.annotations.SerializedName;
import java.util.List;
import java.util.Map;

public class GravityEntryResponse {
    @SerializedName("total_count")
    public int totalCount;

    @SerializedName("entries")
    public List<Map<String, String>> entries;
    // Vi bruker Map<String, String> fordi Gravity Forms returnerer alle feltverdier som nøkkel/verdi par i roten av objektet.
    // F.eks: { "id": "100", "form_id": "1", "1.3": "Ola", "1.6": "Nordmann" }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\GravityField.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.annotations.JsonAdapter;
import com.google.gson.annotations.SerializedName;
import java.util.List;

public class GravityField {
    @SerializedName("id")
    public String id;

    @SerializedName("type")
    public String type;

    @SerializedName("inputType")
    public String inputType;

    @SerializedName("label")
    public String label;

    @SerializedName("adminLabel")
    public String adminLabel;

    @SerializedName("description")
    public String description;

    @SerializedName("defaultValue")
    public String defaultValue;

    @SerializedName("isRequired")
    public boolean isRequired;

    @SerializedName("checkboxLabel")
    public String checkboxLabel;

    @SerializedName("visibility")
    public String visibility;

    @JsonAdapter(ChoicesAdapter.class)
    @SerializedName("choices")
    public List<Choice> choices;

    @SerializedName("content")
    public String content;

    // --- BRUKER ADAPTEREN HER ---
    @JsonAdapter(InputsAdapter.class)
    @SerializedName("inputs")
    public List<GravityField> inputs;
    // ---------------------------

    @SerializedName("isHidden")
    public boolean isHidden;

    @SerializedName("gwreadonly_enable")
    public boolean readOnly;

    @JsonAdapter(ConditionalLogicAdapter.class)
    @SerializedName("conditionalLogic")
    public ConditionalLogic conditionalLogic;

    @SerializedName("gppa-values-templates")
    public java.util.Map<String, String> gppaTemplates;

    @SerializedName("gpnfForm")
    public String gpnfForm;

    public static class Choice {
        @SerializedName("text") public String text;
        @SerializedName("value") public String value;
    }

    public static class ConditionalLogic {
        @SerializedName("actionType") public String actionType;
        @SerializedName("logicType") public String logicType;
        @SerializedName("rules") public List<Rule> rules;
    }

    public static class Rule {
        @SerializedName("fieldId") public String fieldId;
        @SerializedName("operator") public String operator;
        @SerializedName("value") public String value;
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\GravityForm.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.annotations.SerializedName;
import java.util.List;

public class GravityForm {
    @SerializedName("id")
    public int id;

    @SerializedName("title")
    public String title;

    @SerializedName("description")
    public String description;

    // Endret til Object for å være robust mot både "1" (String) og 1 (Int) fra API
    @SerializedName("is_active")
    public Object isActive;

    @SerializedName("fields")
    public List<GravityField> fields;

    // Hjelpemetode for å sjekke om skjemaet er aktivt
    public boolean getIsActive() {
        if (isActive == null) return false;
        String s = isActive.toString();
        return "1".equals(s) || "true".equalsIgnoreCase(s);
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\HandbookAdapter.java
============================================================
// FILSTI: app\src\main\java\com\kbs\kbsintranett\HandbookAdapter.java
package com.kbs.kbsintranett;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;

public class HandbookAdapter extends RecyclerView.Adapter<HandbookAdapter.ViewHolder> {

    private List<HandbookItem> fullList;
    private List<HandbookItem> filteredList;
    private OnItemClickListener listener;

    public interface OnItemClickListener {
        void onItemClick(HandbookItem item);
    }

    public HandbookAdapter(List<HandbookItem> items, OnItemClickListener listener) {
        this.fullList = items;
        this.filteredList = new ArrayList<>(items);
        this.listener = listener;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_handbook, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        HandbookItem item = filteredList.get(position);
        holder.title.setText(item.getTitle());

        // Håndter ingress: Kutt etter 80 tegn hvis den er veldig lang
        String desc = item.getDescription();
        if (desc == null) desc = "";
        if (desc.length() > 100) {
            desc = desc.substring(0, 100) + "...";
        }
        holder.desc.setText(desc);

        // Mapp ikon-type til ressurs (Utvidet liste fra PHP v3.0)
        int iconRes = R.drawable.ic_handbook_general; // Fallback
        String type = item.getIconType() != null ? item.getIconType() : "";

        switch (type) {
            case "car":         iconRes = R.drawable.ic_handbook_car; break;
            case "health":      iconRes = R.drawable.ic_handbook_health; break;
            case "people":      iconRes = R.drawable.ic_handbook_people; break;
            case "warning":     iconRes = R.drawable.ic_handbook_warning; break;
            case "doc":         iconRes = R.drawable.ic_handbook_doc; break;
            case "card":        iconRes = R.drawable.ic_handbook_doc; break; // Bruker doc inntil videre
            case "computer":    iconRes = R.drawable.ic_handbook_general; break;
            case "calendar":    iconRes = R.drawable.ic_handbook_general; break;
            case "money":       iconRes = R.drawable.ic_handbook_doc; break;
            case "helmet":      iconRes = R.drawable.ic_handbook_warning; break;
            case "trash":       iconRes = R.drawable.ic_handbook_general; break;
            case "book":        iconRes = R.drawable.ic_book; break; // Gjenbruk eksisterende ic_book
            case "chat":        iconRes = R.drawable.ic_handbook_people; break;
            default:            iconRes = R.drawable.ic_handbook_general; break;
        }
        holder.icon.setImageResource(iconRes);

        holder.itemView.setOnClickListener(v -> listener.onItemClick(item));
    }

    @Override
    public int getItemCount() {
        return filteredList.size();
    }

    public void filter(String query) {
        filteredList.clear();
        if (query.isEmpty()) {
            filteredList.addAll(fullList);
        } else {
            String q = query.toLowerCase();
            for (HandbookItem item : fullList) {
                if (item.getTitle().toLowerCase().contains(q) ||
                        (item.getDescription() != null && item.getDescription().toLowerCase().contains(q))) {
                    filteredList.add(item);
                }
            }
        }
        notifyDataSetChanged();
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        TextView title, desc;
        ImageView icon;

        public ViewHolder(View view) {
            super(view);
            title = view.findViewById(R.id.title);
            desc = view.findViewById(R.id.desc);
            icon = view.findViewById(R.id.icon);
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\HandbookDetailFragment.java
============================================================
package com.kbs.kbsintranett;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebResourceRequest;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;

import com.google.gson.JsonObject;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class HandbookDetailFragment extends Fragment {

    private int pageId;
    private String pageTitle;
    private WebView webView;
    private ProgressBar progressBar;

    // --- CSS: DESIGN MED MER LUFT ---
    private static final String CSS_STYLE =
            "<style>" +
                    "body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #333333; line-height: 1.6; padding: 16px; margin: 0; }" +

                    "h1 { color: #0069B3; font-size: 24px; border-bottom: 2px solid #0069B3; padding-bottom: 10px; margin-top: 0; }" +
                    "p, ul, li { margin-bottom: 12px; }" +
                    "a { color: #0069B3; font-weight: bold; text-decoration: none; }" +

                    // --- BOKSEN ---
                    ".trekkspill { " +
                    "   background-color: #fff; " +
                    "   border: 1px solid #ddd; " +
                    "   border-radius: 4px; " +
                    "   max-height: 58px; " + // LUKKET
                    "   overflow: hidden; " +
                    "   transition: max-height 0.4s ease; " +

                    // LUFT OG AVSTAND
                    "   margin-top: 32px; " +
                    "   margin-bottom: 16px; " +
                    "}" +

                    // --- KNAPPEN ---
                    ".trekkspill > a[id^='fl-accordion--label-'] { " +
                    "   display: flex; " +
                    "   justify-content: space-between; " +
                    "   align-items: center; " +
                    "   background-color: #f2f2f2; " +
                    "   color: #0069B3; " +
                    "   padding: 15px; " +
                    "   font-weight: bold; " +
                    "   cursor: pointer; " +
                    "   width: 100%; " +
                    "   height: 58px; " +
                    "   box-sizing: border-box; " +
                    "   pointer-events: auto; " +
                    "}" +

                    // --- PLUSS-TEGN ---
                    ".trekkspill > a[id^='fl-accordion--label-']::after { " +
                    "   content: '+'; " +
                    "   font-size: 24px; " +
                    "   font-weight: bold; " +
                    "   color: #0069B3; " +
                    "}" +

                    // --- ÅPEN TILSTAND ---
                    ".trekkspill.open { " +
                    "   max-height: 4000px; " + // ÅPEN
                    "   overflow: visible; " +
                    "   border-color: #0069B3; " +
                    "}" +

                    ".trekkspill.open > a[id^='fl-accordion--label-'] { " +
                    "   background-color: #0069B3; " +
                    "   color: #ffffff; " +
                    "}" +

                    ".trekkspill.open > a[id^='fl-accordion--label-']::after { " +
                    "   content: '-'; " +
                    "   color: #ffffff; " +
                    "}" +

                    // --- RYDDEJOBB ---
                    ".trekkspill > a[id^='fl-accordion--icon-'] { display: none !important; }" +
                    ".trekkspill > h2 { display: none; }" +
                    ".trekkspill > p, .trekkspill > ul, .trekkspill > div { padding: 10px 15px; }" +

                    "</style>";

    // --- JAVASCRIPT: AUTOSCROLL ---
    private static final String JS_SCRIPT =
            "<script>" +
                    "document.onclick = function(e) {\n" +
                    "    var target = e.target.closest('a[id^=\"fl-accordion--label-\"]');\n" +
                    "    \n" +
                    "    if (target) {\n" +
                    "        e.preventDefault();\n" +
                    "        \n" +
                    "        var currentBox = target.closest('.trekkspill');\n" +
                    "        \n" +
                    "        if (currentBox) {\n" +
                    "            var wasOpen = currentBox.classList.contains('open');\n" +
                    "            \n" +
                    "            // LUKK ALLE ANDRE\n" +
                    "            var allOpenBoxes = document.querySelectorAll('.trekkspill.open');\n" +
                    "            allOpenBoxes.forEach(function(box) {\n" +
                    "                box.classList.remove('open');\n" +
                    "            });\n" +
                    "            \n" +
                    "            // ÅPNE DEN VALGTE\n" +
                    "            if (!wasOpen) {\n" +
                    "                currentBox.classList.add('open');\n" +
                    "                \n" +
                    "                // AUTOSCROLL: Økt til 300ms for å vente på CSS-animasjonen\n" +
                    "                setTimeout(function() {\n" +
                    "                    currentBox.scrollIntoView({behavior: 'smooth', block: 'start'});\n" +
                    "                }, 300);\n" +
                    "            }\n" +
                    "        }\n" +
                    "        return false;\n" +
                    "    }\n" +
                    "};" +
                    "</script>";

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_handbook_detail, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        if (getArguments() != null) {
            pageId = getArguments().getInt("page_id");
            pageTitle = getArguments().getString("page_title");
        }

        Toolbar toolbar = view.findViewById(R.id.detail_toolbar);
        toolbar.setTitle(pageTitle != null ? pageTitle : "Håndbok");
        toolbar.setNavigationOnClickListener(v -> Navigation.findNavController(view).navigateUp());

        webView = view.findViewById(R.id.detail_webview);
        progressBar = view.findViewById(R.id.detail_loading);

        setupWebView();
        fetchContent();
    }

    private void setupWebView() {
        WebSettings settings = webView.getSettings();
        settings.setJavaScriptEnabled(true);
        settings.setDomStorageEnabled(true);
        settings.setCacheMode(WebSettings.LOAD_NO_CACHE);

        webView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
                return handleLinkClick(request.getUrl().toString());
            }

            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                return handleLinkClick(url);
            }
        });
    }

    private boolean handleLinkClick(String url) {
        // Ignorer klikk på accordion-lenker
        if (url.endsWith("#")) return true;

        String lowerUrl = url.toLowerCase();
        int formIdToOpen = 0;

        // --- SPESIALHÅNDTERING: Link til Skjemaer basert på URL-nøkkelord ---

        // ID 1: Ansatteopplysninger
        if (lowerUrl.contains("ansatteopplysninger")) {
            formIdToOpen = 1;
        }
        // ID 2: Vernerunde
        else if (lowerUrl.contains("vernerunde")) {
            formIdToOpen = 2;
        }
        // ID 4: RUH (Rapport om uønsket hendelse)
        else if (lowerUrl.contains("uonsket-hendelse") || lowerUrl.contains("/ruh")) {
            formIdToOpen = 4;
        }
        // ID 5: Lån av verktøy/henger
        else if (lowerUrl.contains("lan-av") || lowerUrl.contains("verktoy")) {
            formIdToOpen = 5;
        }
        // ID 6: Avviksmelding
        else if (lowerUrl.contains("avviksmelding") || lowerUrl.contains("/avvik")) {
            formIdToOpen = 6;
        }
        // ID 9: Sikkerhetskurs / Kompetansebevis
        else if (lowerUrl.contains("sikkerhetskurs") || lowerUrl.contains("kompetansebevis")) {
            formIdToOpen = 9;
        }
        // ID 10: HMS Bekreftelse
        else if (lowerUrl.contains("hms-bekreftelse") || lowerUrl.contains("hms-policy")) {
            formIdToOpen = 10;
        }
        // ID 11: Egenmelding
        else if (lowerUrl.contains("egenmelding")) {
            formIdToOpen = 11;
        }
        // ID 12: Sjekkliste firmabil
        else if (lowerUrl.contains("sjekkliste") && (lowerUrl.contains("bil") || lowerUrl.contains("kjoretoy"))) {
            formIdToOpen = 12;
        }
        // ID 14: SJA (Sikker Jobbanalyse)
        else if (lowerUrl.contains("sja") || lowerUrl.contains("jobbanalyse")) {
            formIdToOpen = 14;
        }
        // ID 15: Fraværsvarsel
        else if (lowerUrl.contains("fravaersvarsel") || lowerUrl.contains("fravarsvarsel")) {
            formIdToOpen = 15;
        }
        // ID 16: Refusjon utlegg
        else if (lowerUrl.contains("refusjon") || lowerUrl.contains("utlegg")) {
            formIdToOpen = 16;
        }
        // ID 21: Medarbeidersamtale
        else if (lowerUrl.contains("medarbeidersamtale")) {
            formIdToOpen = 21;
        }
        // ID 22: Medarbeiderundersøkelse
        else if (lowerUrl.contains("medarbeiderundersokelse")) {
            formIdToOpen = 22;
        }

        // Hvis vi fant et skjema, naviger dit internt
        if (formIdToOpen > 0) {
            Bundle bundle = new Bundle();
            bundle.putInt("formId", formIdToOpen);
            Navigation.findNavController(getView()).navigate(R.id.action_handbook_to_form, bundle);
            return true;
        }

        // --- STANDARD INTERN NAVIGASJON ---
        if (url.contains("intranet.kbs.no") || url.startsWith("/")) {
            int targetId = extractIdFromUrl(url);
            if (targetId > 0) {
                navigateToPage(targetId, "Laster...");
                return true;
            }

            progressBar.setVisibility(View.VISIBLE);
            RetrofitClient.getApiService().lookupPageId(url).enqueue(new Callback<JsonObject>() {
                @Override
                public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
                    if (!isAdded()) return;
                    progressBar.setVisibility(View.GONE);

                    if (response.isSuccessful() && response.body() != null) {
                        int id = response.body().get("id").getAsInt();
                        if (id > 0) {
                            navigateToPage(id, "Laster...");
                        } else {
                            openExternal(url);
                        }
                    } else {
                        openExternal(url);
                    }
                }

                @Override
                public void onFailure(Call<JsonObject> call, Throwable t) {
                    if (!isAdded()) return;
                    progressBar.setVisibility(View.GONE);
                    openExternal(url);
                }
            });
            return true;
        } else {
            // Ekstern lenke
            Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
            startActivity(browserIntent);
            return true;
        }
    }

    private void navigateToPage(int id, String title) {
        Bundle bundle = new Bundle();
        bundle.putInt("page_id", id);
        bundle.putString("page_title", title);
        Navigation.findNavController(getView()).navigate(R.id.action_handbook_to_detail, bundle);
    }

    private void openExternal(String url) {
        Intent intent = new Intent(getContext(), WebViewActivity.class);
        intent.putExtra(WebViewActivity.EXTRA_URL, url);
        intent.putExtra(WebViewActivity.EXTRA_TITLE, "KBS Intranett");
        startActivity(intent);
    }

    private int extractIdFromUrl(String url) {
        Pattern p = Pattern.compile("[?&](p|page_id|post)=([0-9]+)");
        Matcher m = p.matcher(url);
        if (m.find()) {
            try {
                return Integer.parseInt(m.group(2));
            } catch (NumberFormatException e) {
                return 0;
            }
        }
        return 0;
    }

    private void fetchContent() {
        progressBar.setVisibility(View.VISIBLE);
        RetrofitClient.getApiService().getHandbookPage(pageId).enqueue(new Callback<HandbookPage>() {
            @Override
            public void onResponse(Call<HandbookPage> call, Response<HandbookPage> response) {
                if (!isAdded()) return;
                progressBar.setVisibility(View.GONE);

                if (response.isSuccessful() && response.body() != null) {
                    HandbookPage page = response.body();

                    if (getView() != null) {
                        Toolbar toolbar = getView().findViewById(R.id.detail_toolbar);
                        if (toolbar != null) toolbar.setTitle(page.title);
                    }

                    String htmlContent = "<!DOCTYPE html><html><head>" +
                            "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">" +
                            CSS_STYLE +
                            JS_SCRIPT +
                            "</head><body>";

                    htmlContent += "<h1>" + page.title + "</h1>";

                    if (page.content != null) {
                        htmlContent += page.content;
                    } else {
                        htmlContent += "<p>Ingen innhold funnet.</p>";
                    }
                    htmlContent += "</body></html>";

                    webView.loadDataWithBaseURL("https://intranet.kbs.no", htmlContent, "text/html", "UTF-8", null);

                } else {
                    Toast.makeText(getContext(), "Kunne ikke laste innhold.", Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onFailure(Call<HandbookPage> call, Throwable t) {
                if (!isAdded()) return;
                progressBar.setVisibility(View.GONE);
                Toast.makeText(getContext(), "Nettverksfeil", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\HandbookFragment.java
============================================================
// FILSTI: app\src\main\java\com\kbs\kbsintranett\HandbookFragment.java
package com.kbs.kbsintranett;

import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation; // Viktig
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class HandbookFragment extends Fragment {

    private RecyclerView recyclerView;
    private ProgressBar progressBar;
    private EditText searchField;
    private HandbookAdapter adapter;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_handbook, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        recyclerView = view.findViewById(R.id.recycler_handbook);
        progressBar = view.findViewById(R.id.progressBar);
        searchField = view.findViewById(R.id.search_field);

        recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));

        searchField.addTextChangedListener(new TextWatcher() {
            @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
            @Override public void onTextChanged(CharSequence s, int start, int before, int count) {}
            @Override public void afterTextChanged(Editable s) {
                if (adapter != null) adapter.filter(s.toString());
            }
        });

        fetchHandbook();
    }

    private void fetchHandbook() {
        RetrofitClient.getApiService().getHandbookItems().enqueue(new Callback<List<HandbookItem>>() {
            @Override
            public void onResponse(Call<List<HandbookItem>> call, Response<List<HandbookItem>> response) {
                if (!isAdded()) return;
                progressBar.setVisibility(View.GONE);

                if (response.isSuccessful() && response.body() != null) {
                    adapter = new HandbookAdapter(response.body(), item -> {
                        // NYTT: Naviger til Native Detail Fragment
                        Bundle bundle = new Bundle();
                        bundle.putInt("page_id", item.getId());
                        bundle.putString("page_title", item.getTitle());

                        Navigation.findNavController(getView())
                                .navigate(R.id.action_handbook_to_detail, bundle);
                    });
                    recyclerView.setAdapter(adapter);
                } else {
                    String msg = "Kunne ikke laste håndboken. Kode: " + response.code();
                    if (response.code() == 404) msg += "\n(Fant ikke foreldresiden 'interninstruks-hms')";
                    Toast.makeText(getContext(), msg, Toast.LENGTH_LONG).show();
                }
            }

            @Override
            public void onFailure(Call<List<HandbookItem>> call, Throwable t) {
                if (!isAdded()) return;
                progressBar.setVisibility(View.GONE);
                Toast.makeText(getContext(), "Nettverksfeil", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\HandbookItem.java
============================================================
// FILSTI: app\src\main\java\com\kbs\kbsintranett\HandbookItem.java
package com.kbs.kbsintranett;

import com.google.gson.annotations.SerializedName;
import java.io.Serializable;

public class HandbookItem implements Serializable {
    @SerializedName("id")
    private int id; // NYTT

    @SerializedName("title")
    private String title;

    @SerializedName("desc")
    private String description;

    @SerializedName("icon_type")
    private String iconType;

    @SerializedName("url")
    private String url;

    public int getId() { return id; }
    public String getTitle() { return title; }
    public String getDescription() { return description; }
    public String getIconType() { return iconType; }
    public String getUrl() { return url; }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\HandbookPage.java
============================================================
// FILSTI: app\src\main\java\com\kbs\kbsintranett\HandbookPage.java
package com.kbs.kbsintranett;

import com.google.gson.annotations.SerializedName;

public class HandbookPage {
    @SerializedName("id")
    public int id;

    @SerializedName("title")
    public String title;

    @SerializedName("content")
    public String content; // Dette er HTML-strengen
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\HomeAdapter.java
============================================================
package com.kbs.kbsintranett;

import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;

public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    public static final int TYPE_CREATE_BUTTON = 1;
    public static final int TYPE_SECTION_TITLE = 2;
    public static final int TYPE_CALENDAR_ITEM = 3;
    public static final int TYPE_NEWS_ITEM = 4;
    public static final int TYPE_TASK_ITEM = 5;
    public static final int TYPE_EMPTY_TASKS = 6;

    private final List<Object> items;
    private final OnHomeClickListener listener;

    public interface OnHomeClickListener {
        void onCreateEventClick();
        void onViewAllCalendarClick();
        void onViewAllNewsClick();
        void onViewAllTasksClick();
        void onCalendarItemClick(CalendarEvent event);
        void onNewsItemClick(WpPost post);
        void onTaskItemClick(TaskItem task);
        void onTaskStatusChanged(TaskItem task, boolean isDone);
    }

    public HomeAdapter(List<Object> items, OnHomeClickListener listener) {
        this.items = items;
        this.listener = listener;
    }

    @Override
    public int getItemViewType(int position) {
        Object item = items.get(position);
        if (item instanceof CreateButtonItem) return TYPE_CREATE_BUTTON;
        if (item instanceof SectionTitleItem) return TYPE_SECTION_TITLE;
        if (item instanceof CalendarEvent) return TYPE_CALENDAR_ITEM;
        if (item instanceof WpPost) return TYPE_NEWS_ITEM;
        if (item instanceof TaskItem) return TYPE_TASK_ITEM;
        if (item instanceof EmptyTasksItem) return TYPE_EMPTY_TASKS;
        return -1;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        switch (viewType) {
            case TYPE_CREATE_BUTTON:
                return new CreateButtonViewHolder(inflater.inflate(R.layout.item_home_create_btn, parent, false));
            case TYPE_SECTION_TITLE:
                return new SectionTitleViewHolder(inflater.inflate(R.layout.item_home_section_title, parent, false));
            case TYPE_CALENDAR_ITEM:
                return new CalendarViewHolder(inflater.inflate(R.layout.item_calendar, parent, false));
            case TYPE_TASK_ITEM:
                return new TaskViewHolder(inflater.inflate(R.layout.item_task, parent, false));
            case TYPE_EMPTY_TASKS:
                return new EmptyViewHolder(inflater.inflate(R.layout.item_home_empty_tasks, parent, false));
            case TYPE_NEWS_ITEM:
                return new NewsViewHolder(inflater.inflate(R.layout.item_news, parent, false));
            default:
                throw new IllegalArgumentException("Ugyldig viewType");
        }
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        Object item = items.get(position);

        if (holder instanceof CreateButtonViewHolder) {
            ((CreateButtonViewHolder) holder).btnCreate.setOnClickListener(v -> listener.onCreateEventClick());
        }
        else if (holder instanceof SectionTitleViewHolder) {
            SectionTitleItem section = (SectionTitleItem) item;
            SectionTitleViewHolder vh = (SectionTitleViewHolder) holder;
            vh.title.setText(section.title);
            vh.btnViewAll.setOnClickListener(v -> {
                if (section.type == SectionTitleItem.TYPE_CALENDAR) listener.onViewAllCalendarClick();
                else if (section.type == SectionTitleItem.TYPE_TASKS) listener.onViewAllTasksClick();
                else listener.onViewAllNewsClick();
            });
        }
        else if (holder instanceof CalendarViewHolder) {
            CalendarEvent event = (CalendarEvent) item;
            CalendarViewHolder vh = (CalendarViewHolder) holder;
            vh.day.setText(event.getDay());
            vh.month.setText(event.getMonth());
            vh.time.setText(event.getTime());
            vh.title.setText(event.getTitle());
            boolean isPrivate = event.getDescription() != null && event.getDescription().contains("#deltakere:");
            try {
                int color = Color.parseColor(isPrivate ? "#673AB7" : event.getCalendarColor());
                vh.dateBox.setBackgroundTintList(ColorStateList.valueOf(color));
            } catch (Exception e) {
                vh.dateBox.setBackgroundTintList(ColorStateList.valueOf(Color.parseColor("#0069B3")));
            }
            vh.itemView.setOnClickListener(v -> listener.onCalendarItemClick(event));
        }
        else if (holder instanceof TaskViewHolder) {
            TaskItem task = (TaskItem) item;
            TaskViewHolder vh = (TaskViewHolder) holder;
            vh.title.setText(task.getTitle());

            if (task.getDueDate() > 0) {
                SimpleDateFormat sdf = new SimpleDateFormat("dd. MMM", Locale.getDefault());
                vh.date.setText("Frist: " + sdf.format(new Date(task.getDueDate())));
            } else {
                vh.date.setText("Ingen frist");
            }

            String myEmail = UserManager.getInstance().getUserEmail();
            boolean myStatus = task.getParticipantStatus(myEmail);
            vh.checkBox.setChecked(myStatus);

            long now = System.currentTimeMillis();
            boolean isOverdue = task.getDueDate() > 0 && task.getDueDate() < now && !task.isFullyCompleted() && !myStatus;

            if (isOverdue) {
                vh.cardView.setCardBackgroundColor(ContextCompat.getColor(vh.itemView.getContext(), R.color.kbs_soft_light_pink_beige));
                vh.date.setTextColor(ContextCompat.getColor(vh.itemView.getContext(), R.color.kbs_logo_accent_red));
            } else {
                vh.cardView.setCardBackgroundColor(Color.WHITE);
                vh.date.setTextColor(ContextCompat.getColor(vh.itemView.getContext(), R.color.kbs_muted_blue_gray));
            }

            // --- REINTRODUSERT FREMDRIFTSBEREGNING ---
            int total = task.getAssigneeStatus().size();
            int done = 0;
            for (Boolean isFinished : task.getAssigneeStatus().values()) {
                if (isFinished) done++;
            }
            vh.progress.setText("Fremdrift: " + done + "/" + total);
            // ------------------------------------------

            vh.checkBox.setOnClickListener(v -> listener.onTaskStatusChanged(task, vh.checkBox.isChecked()));
            vh.itemView.setOnClickListener(v -> listener.onTaskItemClick(task));
        }
        else if (holder instanceof NewsViewHolder) {
            WpPost post = (WpPost) item;
            NewsViewHolder vh = (NewsViewHolder) holder;
            vh.title.setText(post.getTitleStr());
            vh.excerpt.setText(post.getExcerptStr());
            vh.date.setText(post.date);
            String cat = post.getCategoryName();
            vh.category.setText(cat);
            vh.category.setVisibility(cat.isEmpty() ? View.GONE : View.VISIBLE);
            String imgUrl = post.getFeaturedImageUrl();
            if (imgUrl != null) {
                vh.image.setVisibility(View.VISIBLE);
                Glide.with(vh.image.getContext()).load(imgUrl).transition(DrawableTransitionOptions.withCrossFade()).centerCrop().into(vh.image);
            } else {
                vh.image.setVisibility(View.GONE);
            }
            vh.itemView.setOnClickListener(v -> listener.onNewsItemClick(post));
        }
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    // --- VIEW HOLDERS ---
    static class CreateButtonViewHolder extends RecyclerView.ViewHolder {
        Button btnCreate;
        CreateButtonViewHolder(View v) { super(v); btnCreate = v.findViewById(R.id.btn_create_event); }
    }

    static class SectionTitleViewHolder extends RecyclerView.ViewHolder {
        TextView title, btnViewAll;
        SectionTitleViewHolder(View v) { super(v); title = v.findViewById(R.id.txt_section_title); btnViewAll = v.findViewById(R.id.btn_view_all); }
    }

    static class CalendarViewHolder extends RecyclerView.ViewHolder {
        TextView day, month, title, time;
        LinearLayout dateBox;
        CalendarViewHolder(View view) {
            super(view);
            day = view.findViewById(R.id.cal_day);
            month = view.findViewById(R.id.cal_month);
            title = view.findViewById(R.id.cal_title);
            time = view.findViewById(R.id.cal_time);
            dateBox = view.findViewById(R.id.date_box_background);
        }
    }

    static class TaskViewHolder extends RecyclerView.ViewHolder {
        TextView title, date, progress;
        CheckBox checkBox;
        CardView cardView;
        TaskViewHolder(View v) {
            super(v);
            title = v.findViewById(R.id.task_title);
            date = v.findViewById(R.id.task_date);
            progress = v.findViewById(R.id.task_progress); // Husk denne
            checkBox = v.findViewById(R.id.task_checkbox);
            cardView = (CardView) v;
        }
    }

    static class NewsViewHolder extends RecyclerView.ViewHolder {
        TextView title, excerpt, date, category;
        ImageView image;
        NewsViewHolder(View v) {
            super(v);
            title = v.findViewById(R.id.news_title);
            excerpt = v.findViewById(R.id.news_excerpt);
            date = v.findViewById(R.id.news_date);
            category = v.findViewById(R.id.news_category);
            image = v.findViewById(R.id.news_image);
        }
    }

    static class EmptyViewHolder extends RecyclerView.ViewHolder {
        EmptyViewHolder(View v) { super(v); }
    }

    public static class CreateButtonItem {}
    public static class EmptyTasksItem {}
    public static class SectionTitleItem {
        public static final int TYPE_CALENDAR = 0;
        public static final int TYPE_NEWS = 1;
        public static final int TYPE_TASKS = 2;
        String title; int type;
        public SectionTitleItem(String t, int type) { this.title = t; this.type = type; }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\HomeFragment.java
============================================================
package com.kbs.kbsintranett;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import com.google.gson.JsonElement;

public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickListener {

    private RecyclerView recyclerView;
    private HomeAdapter adapter;
    private ProgressBar mainProgressBar;
    private SwipeRefreshLayout swipeRefreshLayout;

    private List<CalendarEvent> currentEvents = new ArrayList<>();
    private List<WpPost> currentNews = new ArrayList<>();
    private List<TaskItem> currentTasks = new ArrayList<>();

    private int activeNetworkCalls = 0;
    // Cache lever i 60 minutter. PUSH vil ugyldiggjøre den før tiden ved behov.
    private static final int CACHE_TTL_MINUTES = 60;

    private Handler timeoutHandler = new Handler(Looper.getMainLooper());
    private Runnable timeoutRunnable = this::forceStopLoading;

    private ActivityResultLauncher<String> requestPermissionLauncher;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestPermissionLauncher = registerForActivityResult(
                new ActivityResultContracts.RequestPermission(),
                isGranted -> refreshData(true)
        );
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_home, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        mainProgressBar = view.findViewById(R.id.main_loading_spinner);
        swipeRefreshLayout = view.findViewById(R.id.swipe_refresh_home);
        recyclerView = view.findViewById(R.id.main_recycler_view);

        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

        // Ved manuell swipe: Tving nettverksoppdatering
        swipeRefreshLayout.setOnRefreshListener(() -> refreshData(true));

        // Ved oppstart: Bruk cache (false = ikke tving nettverk)
        refreshData(false);
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        timeoutHandler.removeCallbacks(timeoutRunnable);
    }

    private void refreshData(boolean forceNetwork) {
        // 1. Last cache i bakgrunnen
        new Thread(() -> {
            if (getContext() == null) return;

            // Hent data fra disk
            List<CalendarEvent> cachedApiEvents = CacheManager.getCachedCalendarEvents(getContext());
            List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext(), true);

            for (CalendarEvent e : cachedApiEvents) CalendarManager.formatEventForUI(e);
            List<CalendarEvent> mergedEvents = CalendarManager.mergeAndSort(cachedApiEvents, deviceEvents);

            List<WpPost> cachedNews = CacheManager.getCachedNewsPosts(getContext());
            formatNewsDates(cachedNews);

            List<TaskItem> cachedTasks = CacheManager.getTasks(getContext());

            // 2. Oppdater UI på hovedtråden
            new Handler(Looper.getMainLooper()).post(() -> {
                if (!isAdded()) return;

                // Oppdater variablene
                currentEvents = mergedEvents;
                currentNews = cachedNews;
                currentTasks = cachedTasks;

                // Vis dataene vi fant umiddelbart (fjerner spinner hvis den var der)
                buildAndDisplayList();

                // 3. Vurder om vi skal hente nytt fra nettet
                boolean cacheValidCal = CacheManager.isCacheValid(getContext(), "calendar", CACHE_TTL_MINUTES);
                boolean cacheValidNews = CacheManager.isCacheValid(getContext(), "news", CACHE_TTL_MINUTES);
                boolean cacheValidTasks = CacheManager.isCacheValid(getContext(), "tasks", CACHE_TTL_MINUTES);

                // Vi henter KUN hvis brukeren swiper (force) ELLER cachen er utgått på dato.
                // Vi henter IKKE bare fordi listene er tomme (da stoler vi på at cachen er korrekt tom).
                boolean needNetwork = forceNetwork || !cacheValidCal || !cacheValidNews || !cacheValidTasks;

                if (needNetwork) {
                    // Vis spinner KUN hvis det er helt tomt fra før, eller ved manuell swipe
                    boolean isEverythingEmpty = currentNews.isEmpty() && currentTasks.isEmpty() && currentEvents.isEmpty();

                    if (isEverythingEmpty && mainProgressBar != null) {
                        mainProgressBar.setVisibility(View.VISIBLE);
                    } else if (forceNetwork && swipeRefreshLayout != null) {
                        swipeRefreshLayout.setRefreshing(true);
                    }
                    // Ellers: Silent update (ingen spinner, men vi henter i bakgrunnen)

                    activeNetworkCalls = 3;
                    timeoutHandler.removeCallbacks(timeoutRunnable);
                    timeoutHandler.postDelayed(timeoutRunnable, 10000); // 10 sek timeout

                    fetchCalendarData();
                    fetchNewsData();
                    fetchTaskData();
                } else {
                    // Cachen er god nok! Skjul evt spinnere.
                    stopLoaders();
                }
            });
        }).start();
    }

    private void stopLoaders() {
        if (mainProgressBar != null) mainProgressBar.setVisibility(View.GONE);
        if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false);
    }

    private void forceStopLoading() {
        if (activeNetworkCalls > 0) {
            activeNetworkCalls = 0;
            stopLoaders();
            buildAndDisplayList();
        }
    }

    private void fetchCalendarData() {
        if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CALENDAR) != PackageManager.PERMISSION_GRANTED) {
            requestPermissionLauncher.launch(Manifest.permission.READ_CALENDAR);
            checkLoadingComplete();
            return;
        }

        RetrofitClient.getApiService().getCalendarEvents().enqueue(new Callback<List<CalendarEvent>>() {
            @Override
            public void onResponse(Call<List<CalendarEvent>> call, Response<List<CalendarEvent>> response) {
                if (response.isSuccessful() && response.body() != null) {
                    List<CalendarEvent> apiEvents = response.body();
                    CacheManager.saveCalendarEvents(getContext(), apiEvents);

                    new Thread(() -> {
                        List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext(), true);
                        for (CalendarEvent e : apiEvents) CalendarManager.formatEventForUI(e);
                        List<CalendarEvent> merged = CalendarManager.mergeAndSort(apiEvents, deviceEvents);
                        new Handler(Looper.getMainLooper()).post(() -> {
                            currentEvents = merged;
                            checkLoadingComplete();
                        });
                    }).start();
                } else {
                    checkLoadingComplete();
                }
            }
            @Override
            public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {
                checkLoadingComplete();
            }
        });
    }

    private void fetchNewsData() {
        RetrofitClient.getApiService().getPosts().enqueue(new Callback<List<WpPost>>() {
            @Override
            public void onResponse(Call<List<WpPost>> call, Response<List<WpPost>> response) {
                if (response.isSuccessful() && response.body() != null) {
                    currentNews = response.body();
                    CacheManager.saveNewsPosts(getContext(), currentNews);
                    formatNewsDates(currentNews);
                }
                checkLoadingComplete();
            }
            @Override
            public void onFailure(Call<List<WpPost>> call, Throwable t) {
                checkLoadingComplete();
            }
        });
    }

    private void fetchTaskData() {
        RetrofitClient.getApiService().getTasks().enqueue(new Callback<List<TaskItem>>() {
            @Override
            public void onResponse(Call<List<TaskItem>> call, Response<List<TaskItem>> response) {
                if (response.isSuccessful() && response.body() != null) {
                    currentTasks = response.body();
                    CacheManager.saveTasks(getContext(), currentTasks);
                }
                checkLoadingComplete();
            }
            @Override
            public void onFailure(Call<List<TaskItem>> call, Throwable t) {
                checkLoadingComplete();
            }
        });
    }

    private void formatNewsDates(List<WpPost> posts) {
        if (posts == null) return;
        SimpleDateFormat rawFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
        SimpleDateFormat targetFormat = new SimpleDateFormat("dd. MMM yyyy", Locale.getDefault());
        for (WpPost post : posts) {
            try {
                if (post.date != null && post.date.contains("T")) {
                    Date date = rawFormat.parse(post.date);
                    post.date = targetFormat.format(date);
                }
            } catch (Exception ignored) {}
        }
    }

    private synchronized void checkLoadingComplete() {
        activeNetworkCalls--;
        // Oppdater visningen fortløpende så snart data kommer inn
        buildAndDisplayList();

        if (activeNetworkCalls <= 0) {
            activeNetworkCalls = 0;
            timeoutHandler.removeCallbacks(timeoutRunnable);
            stopLoaders();
        }
    }

    private void buildAndDisplayList() {
        if (!isAdded()) return;

        List<Object> items = new ArrayList<>();
        String myEmail = UserManager.getInstance().getUserEmail();

        // 1. KALENDER
        items.add(new HomeAdapter.SectionTitleItem("Kommende hendelser", HomeAdapter.SectionTitleItem.TYPE_CALENDAR));
        String today = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date());
        int calCount = 0;
        if (currentEvents != null) {
            for (CalendarEvent e : currentEvents) {
                if (e.getRawDate() != null && e.getRawDate().compareTo(today) >= 0) {
                    items.add(e);
                    calCount++;
                }
                if (calCount >= 3) break;
            }
        }

        // 2. OPPGAVER
        items.add(new HomeAdapter.SectionTitleItem("Mine oppgaver", HomeAdapter.SectionTitleItem.TYPE_TASKS));
        List<TaskItem> myActiveTasks = new ArrayList<>();
        if (currentTasks != null) {
            for (TaskItem t : currentTasks) {
                // Vis oppgaver jeg er deltaker i, som ikke er fullført av meg, og ikke helt lukket
                if (t.isUserParticipant(myEmail) && !t.getParticipantStatus(myEmail) && !t.isFullyCompleted()) {
                    myActiveTasks.add(t);
                }
            }
        }

        if (myActiveTasks.isEmpty()) {
            items.add(new HomeAdapter.EmptyTasksItem());
        } else {
            // Sortering: Dato (stigende), deretter de uten dato (alfabetisk)
            Collections.sort(myActiveTasks, (t1, t2) -> {
                boolean t1HasDate = t1.getDueDate() > 0;
                boolean t2HasDate = t2.getDueDate() > 0;

                if (t1HasDate && !t2HasDate) return -1;
                if (!t1HasDate && t2HasDate) return 1;
                if (!t1HasDate && !t2HasDate) return t1.getTitle().compareToIgnoreCase(t2.getTitle());

                return Long.compare(t1.getDueDate(), t2.getDueDate());
            });

            for (int i = 0; i < Math.min(myActiveTasks.size(), 3); i++) {
                items.add(myActiveTasks.get(i));
            }
        }

        // 3. NYHETER
        items.add(new HomeAdapter.SectionTitleItem("Siste nytt", HomeAdapter.SectionTitleItem.TYPE_NEWS));
        if (currentNews != null) {
            items.addAll(currentNews);
        }

        adapter = new HomeAdapter(items, this);
        recyclerView.setAdapter(adapter);
    }

    // --- NAVIGASJON & KLIKK ---

    @Override public void onCreateEventClick() { Navigation.findNavController(getView()).navigate(R.id.navigation_create_event); }
    @Override public void onViewAllCalendarClick() { Navigation.findNavController(getView()).navigate(R.id.navigation_calendar_full); }
    @Override public void onViewAllNewsClick() { Navigation.findNavController(getView()).navigate(R.id.navigation_news_full); }
    @Override public void onViewAllTasksClick() { Navigation.findNavController(getView()).navigate(R.id.navigation_tasks); }

    @Override public void onCalendarItemClick(CalendarEvent event) {
        CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
        sheet.setOnEventChangeListener(() -> refreshData(true));
        sheet.show(getParentFragmentManager(), "CalendarDetails");
    }

    @Override public void onNewsItemClick(WpPost post) {
        Bundle bundle = new Bundle();
        bundle.putSerializable("post_data", post);
        Navigation.findNavController(getView()).navigate(R.id.navigation_news_detail, bundle);
    }

    @Override public void onTaskItemClick(TaskItem task) {
        TaskDetailsBottomSheet sheet = new TaskDetailsBottomSheet(task, new TaskDetailsBottomSheet.OnTaskChangeListener() {
            @Override public void onTaskChanged() { saveAndSyncTasks(); }
            @Override public void onTaskDeleted(TaskItem taskToDelete) {
                currentTasks.remove(taskToDelete);
                saveAndSyncTasks();
            }
            @Override public void onEditRequested(TaskItem taskToEdit) {
                AddTaskBottomSheet editDialog = new AddTaskBottomSheet();
                editDialog.setTaskToEdit(taskToEdit);
                editDialog.setOnTaskAddedListener(new AddTaskBottomSheet.OnTaskAddedListener() {
                    @Override public void onTaskAdded(TaskItem task) {}
                    @Override public void onTaskUpdated(TaskItem task) {
                        saveAndSyncTasks();
                    }
                });
                editDialog.show(getChildFragmentManager(), "EditTask");
            }
        });
        sheet.show(getChildFragmentManager(), "TaskDetails");
    }

    @Override public void onTaskStatusChanged(TaskItem task, boolean isDone) {
        String myEmail = UserManager.getInstance().getUserEmail();
        task.setParticipantStatus(myEmail, isDone);
        if (task.getCreatedByEmail().equalsIgnoreCase(myEmail) && isDone) {
            task.setFullyCompleted(true);
        }
        saveAndSyncTasks();
    }

    private void saveAndSyncTasks() {
        CacheManager.saveTasks(getContext(), currentTasks);
        buildAndDisplayList();
        RetrofitClient.getApiService().syncTasks(currentTasks).enqueue(new Callback<JsonElement>() {
            @Override public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {}
            @Override public void onFailure(Call<JsonElement> call, Throwable t) {}
        });
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\ImageDialogFragment.java
============================================================
package com.kbs.kbsintranett;

import android.app.Dialog;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;

public class ImageDialogFragment extends DialogFragment {

    private static final String ARG_URL = "image_url";

    public static ImageDialogFragment newInstance(String imageUrl) {
        ImageDialogFragment fragment = new ImageDialogFragment();
        Bundle args = new Bundle();
        args.putString(ARG_URL, imageUrl);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onStart() {
        super.onStart();
        // Gjør dialogen fullskjerm
        Dialog dialog = getDialog();
        if (dialog != null) {
            int width = ViewGroup.LayoutParams.MATCH_PARENT;
            int height = ViewGroup.LayoutParams.MATCH_PARENT;
            dialog.getWindow().setLayout(width, height);
            dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.BLACK));
        }
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_image_dialog, container, false);

        ImageView imageView = view.findViewById(R.id.full_screen_image);
        ImageButton closeBtn = view.findViewById(R.id.btn_close_image);
        ProgressBar progressBar = view.findViewById(R.id.loading_image);

        String url = getArguments() != null ? getArguments().getString(ARG_URL) : null;

        if (url != null) {
            Glide.with(this)
                    .load(url)
                    .transition(DrawableTransitionOptions.withCrossFade())
                    .listener(new com.bumptech.glide.request.RequestListener<android.graphics.drawable.Drawable>() {
                        @Override
                        public boolean onLoadFailed(@Nullable com.bumptech.glide.load.engine.GlideException e, Object model, com.bumptech.glide.request.target.Target<android.graphics.drawable.Drawable> target, boolean isFirstResource) {
                            progressBar.setVisibility(View.GONE);
                            return false;
                        }

                        @Override
                        public boolean onResourceReady(android.graphics.drawable.Drawable resource, Object model, com.bumptech.glide.request.target.Target<android.graphics.drawable.Drawable> target, com.bumptech.glide.load.DataSource dataSource, boolean isFirstResource) {
                            progressBar.setVisibility(View.GONE);
                            return false;
                        }
                    })
                    .into(imageView);
        }

        closeBtn.setOnClickListener(v -> dismiss());
        // Lukk også hvis man trykker på selve bildet
        imageView.setOnClickListener(v -> dismiss());

        return view;
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        Dialog dialog = super.onCreateDialog(savedInstanceState);
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        return dialog;
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\InputsAdapter.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

public class InputsAdapter implements JsonDeserializer<List<GravityField>> {
    @Override
    public List<GravityField> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        if (json.isJsonNull()) {
            return new ArrayList<>();
        }
        // Fikser krasjen: Hvis Gravity Forms sender "" i stedet for [], returner tom liste
        if (json.isJsonPrimitive() && json.getAsJsonPrimitive().isString()) {
            return new ArrayList<>();
        }
        if (json.isJsonArray()) {
            List<GravityField> list = new ArrayList<>();
            for (JsonElement e : json.getAsJsonArray()) {
                list.add(context.deserialize(e, GravityField.class));
            }
            return list;
        }
        return new ArrayList<>();
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\InternalLinkMovementMethod.java
============================================================
// FILSTI: app\src\main\java\com\kbs\kbsintranett\InternalLinkMovementMethod.java
package com.kbs.kbsintranett;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; // <-- Sjekk at denne er med!
import android.text.Spannable;
import android.text.method.LinkMovementMethod;
import android.text.style.URLSpan;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import androidx.navigation.Navigation;

import com.google.gson.JsonObject;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class InternalLinkMovementMethod extends LinkMovementMethod {
    private static InternalLinkMovementMethod instance;
    private static final String TAG = "InternalLinkMethod";

    public static InternalLinkMovementMethod getInstance() {
        if (instance == null) instance = new InternalLinkMovementMethod();
        return instance;
    }

    @Override
    public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
        int action = event.getAction();

        if (action == MotionEvent.ACTION_UP) {
            int x = (int) event.getX();
            int y = (int) event.getY();

            x -= widget.getTotalPaddingLeft();
            y -= widget.getTotalPaddingTop();

            x += widget.getScrollX();
            y += widget.getScrollY();

            int line = widget.getLayout().getLineForVertical(y);
            int off = widget.getLayout().getOffsetForHorizontal(line, x);

            URLSpan[] link = buffer.getSpans(off, off, URLSpan.class);
            if (link.length != 0) {
                String url = link[0].getURL();
                handleLink(widget.getContext(), url, widget);
                return true;
            }
        }
        return super.onTouchEvent(widget, buffer, event);
    }

    private void handleLink(Context context, String url, View view) {
        Log.d(TAG, "Link clicked: " + url);

        // 1. Sjekk om det er en intern lenke
        if (url.contains("intranet.kbs.no") || url.startsWith("/")) {

            // a) Prøv å finne ID hvis den finnes i URLen (?p=123)
            int pageId = extractIdFromUrl(url);

            if (pageId > 0) {
                navigateToInternalPage(view, pageId, "Laster...");
            } else {
                // b) Det er en "pen" URL. Vi må spørre APIet hva IDen er.
                // Vi bruker Toast for å gi feedback om at noe skjer
                Toast.makeText(context, "Åpner side...", Toast.LENGTH_SHORT).show();

                RetrofitClient.getApiService().lookupPageId(url).enqueue(new Callback<JsonObject>() {
                    @Override
                    public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
                        if (response.isSuccessful() && response.body() != null) {
                            int id = response.body().get("id").getAsInt();
                            if (id > 0) {
                                // Suksess! Naviger internt
                                navigateToInternalPage(view, id, "Laster...");
                            } else {
                                // Fant ikke ID, åpne i WebView som fallback
                                openInWebView(context, url);
                            }
                        } else {
                            openInWebView(context, url);
                        }
                    }

                    @Override
                    public void onFailure(Call<JsonObject> call, Throwable t) {
                        openInWebView(context, url);
                    }
                });
            }
        } else {
            // Ekstern lenke - åpne i nettleser
            Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
            context.startActivity(browserIntent);
        }
    }

    private int extractIdFromUrl(String url) {
        Pattern p = Pattern.compile("[?&](p|page_id|post)=([0-9]+)");
        Matcher m = p.matcher(url);
        if (m.find()) {
            try {
                return Integer.parseInt(m.group(2));
            } catch (NumberFormatException e) {
                return 0;
            }
        }
        return 0;
    }

    private void navigateToInternalPage(View view, int pageId, String title) {
        try {
            Bundle bundle = new Bundle();
            bundle.putInt("page_id", pageId);
            bundle.putString("page_title", title);
            Navigation.findNavController(view).navigate(R.id.action_handbook_to_detail, bundle);
        } catch (Exception e) {
            Log.e(TAG, "Kunne ikke navigere", e);
        }
    }

    private void openInWebView(Context context, String url) {
        Intent intent = new Intent(context, WebViewActivity.class);
        intent.putExtra(WebViewActivity.EXTRA_URL, url);
        intent.putExtra(WebViewActivity.EXTRA_TITLE, "KBS Intranett");
        context.startActivity(intent);
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\KbsApplication.java
============================================================
package com.kbs.kbsintranett;

import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.os.Build;

public class KbsApplication extends Application {

    public static final String CHANNEL_ID = "kbs_calendar_channel";

    @Override
    public void onCreate() {
        super.onCreate();
        createNotificationChannel();
    }

    private void createNotificationChannel() {
        // Vi oppretter kanalen her ved oppstart, så den er klar uansett om
        // det er MainActivity eller en bakgrunnsjobb som trenger den.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    CHANNEL_ID,
                    "KBS Kalendervarsler",
                    NotificationManager.IMPORTANCE_HIGH
            );
            channel.setDescription("Varsler for kalenderhendelser");

            NotificationManager manager = getSystemService(NotificationManager.class);
            if (manager != null) {
                manager.createNotificationChannel(channel);
            }
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\LoginFragment.java
============================================================
package com.kbs.kbsintranett;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;

import com.google.android.gms.auth.api.signin.GoogleSignIn;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.android.gms.auth.api.signin.GoogleSignInClient;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.common.SignInButton;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.tasks.Task;

public class LoginFragment extends Fragment {

    private static final String TAG = "LoginFragment";
    private GoogleSignInClient mGoogleSignInClient;
    private TextView statusText;
    private SignInButton signInButton;

    // Håndterer resultatet fra Google-vinduet
    private final ActivityResultLauncher<Intent> signInLauncher = registerForActivityResult(
            new ActivityResultContracts.StartActivityForResult(),
            result -> {
                Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(result.getData());
                handleGoogleResult(task);
            }
    );

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_login, container, false);
        statusText = view.findViewById(R.id.status_text);
        signInButton = view.findViewById(R.id.sign_in_button);
        signInButton.setSize(SignInButton.SIZE_WIDE);

        // Hent ID fra MainActivity
        String clientId = MainActivity.GOOGLE_WEB_CLIENT_ID;
        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestIdToken(clientId)
                .requestEmail()
                .build();
        mGoogleSignInClient = GoogleSignIn.getClient(requireActivity(), gso);

        signInButton.setOnClickListener(v -> {
            statusText.setText("Starter Google innlogging...");
            Intent signInIntent = mGoogleSignInClient.getSignInIntent();
            signInLauncher.launch(signInIntent);
        });
        return view;
    }

    private void handleGoogleResult(Task<GoogleSignInAccount> completedTask) {
        try {
            GoogleSignInAccount account = completedTask.getResult(ApiException.class);
            // 1. Google er OK. Nå logger vi inn på WordPress.
            statusText.setText("Google OK. Kobler til KBS Intranett...");
            signInButton.setEnabled(false); // Hindre dobbeltklikk

            String photoUrl = (account.getPhotoUrl() != null) ? account.getPhotoUrl().toString() : null;

            AuthRepository.loginToWordPress(
                    account.getIdToken(),
                    account.getDisplayName(),
                    account.getEmail(),
                    photoUrl,
                    new AuthRepository.AuthCallback() {
                        @Override
                        public void onSuccess(String role) {
                            // 2. Alt er OK! Naviger til Hjem.
                            if (isAdded()) {
                                statusText.setText("Innlogging OK!");
                                NavController navController = Navigation.findNavController(requireActivity(), R.id.nav_host_fragment);
                                navController.navigate(R.id.action_login_to_home);
                            }
                        }

                        @Override
                        public void onError(String message) {
                            if (isAdded()) {
                                statusText.setText(message);
                                signInButton.setEnabled(true);
                            }
                        }
                    }
            );
        } catch (ApiException e) {
            // --- KORRIGERT FEILMELDING ---
            Log.w(TAG, "signInResult:failed code=" + e.getStatusCode());
            String message;
            if (e.getStatusCode() == 12500) {
                message = "Konto ikke funnet, eller konto uten rettigheter.";
            } else {
                message = "Google-feil: " + e.getStatusCode();
            }
            statusText.setText(message);
            // --- SLUTT PÅ KORRIGERING ---
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\LoginRequest.java
============================================================
package com.kbs.kbsintranett;

public class LoginRequest {
    public String token;

    public LoginRequest(String token) {
        this.token = token;
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\LoginResponse.java
============================================================
// FILSTI: app\src\main\java\com\kbs\kbsintranett\LoginResponse.java
package com.kbs.kbsintranett;

import com.google.gson.annotations.SerializedName;
import java.util.List;

public class LoginResponse {
    public boolean success;
    @SerializedName("full_cookie")
    public String fullCookie;

    public String role;

    @SerializedName("user_id")
    public int userId;

    @SerializedName("first_name")
    public String firstName;

    @SerializedName("last_name")
    public String lastName;

    @SerializedName("stilling")
    public String stilling;

    @SerializedName("mobiltelefon")
    public String mobiltelefon;

    // NYTT FELT: Liste over kalendere brukeren kan skrive til
    @SerializedName("writeable_calendars")
    public List<String> writeableCalendars;

    public String message;
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\MainActivity.java
============================================================
package com.kbs.kbsintranett;

import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.navigation.NavController;
import androidx.navigation.fragment.NavHostFragment;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import androidx.work.ExistingPeriodicWorkPolicy;
import androidx.work.PeriodicWorkRequest;
import androidx.work.WorkManager;

import com.google.android.gms.auth.api.signin.GoogleSignIn;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.android.gms.auth.api.signin.GoogleSignInClient;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.material.navigation.NavigationView;

import java.util.concurrent.TimeUnit;

public class MainActivity extends AppCompatActivity {

    public static final String GOOGLE_WEB_CLIENT_ID = BuildConfig.WEB_CLIENT_ID;
    private NavController navController;
    private DrawerLayout drawerLayout;
    private AppBarConfiguration appBarConfiguration;
    private ActivityResultLauncher<String> requestPermissionLauncher;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        drawerLayout = findViewById(R.id.drawer_layout);
        NavigationView navigationView = findViewById(R.id.nav_view);

        NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager()
                .findFragmentById(R.id.nav_host_fragment);

        if (navHostFragment != null) {
            navController = navHostFragment.getNavController();

            appBarConfiguration = new AppBarConfiguration.Builder(
                    R.id.navigation_home, R.id.navigation_calendar_full, R.id.navigation_tasks,
                    R.id.navigation_forms, R.id.navigation_news_full, R.id.navigation_handbook)
                    .setOpenableLayout(drawerLayout)
                    .build();

            NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);

            // Vi bruker en tilpasset listener for å sikre at Hjem og andre punkter alltid fungerer
            navigationView.setNavigationItemSelectedListener(item -> {
                boolean handled = NavigationUI.onNavDestinationSelected(item, navController);
                if (handled || item.getItemId() == R.id.navigation_home) {
                    if (item.getItemId() == R.id.navigation_home) {
                        navController.navigate(R.id.navigation_home);
                    }
                    drawerLayout.closeDrawer(GravityCompat.START);
                }
                return handled;
            });

            navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
                if (destination.getId() == R.id.navigation_login) {
                    toolbar.setVisibility(View.GONE);
                    drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
                } else {
                    toolbar.setVisibility(View.VISIBLE);
                    drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
                    updateNavHeader(navigationView);
                }
            });
        }

        setupTaskReminders();
        createNotificationChannel();
        requestPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {});
        checkNotificationPermission();
        checkExactAlarmPermission();
        checkLoginState();
    }

    private void updateNavHeader(NavigationView navigationView) {
        View headerView = navigationView.getHeaderView(0);
        TextView name = headerView.findViewById(R.id.nav_header_name);
        TextView email = headerView.findViewById(R.id.nav_header_email);
        UserManager user = UserManager.getInstance();
        if (user.isLoggedIn()) {
            name.setText(user.getUserDisplayName());
            email.setText(user.getUserEmail());
        }
    }

    private void setupTaskReminders() {
        PeriodicWorkRequest taskCheck = new PeriodicWorkRequest.Builder(
                TaskReminderWorker.class, 2, TimeUnit.DAYS).build();
        WorkManager.getInstance(this).enqueueUniquePeriodicWork(
                "TaskReminder", ExistingPeriodicWorkPolicy.KEEP, taskCheck);
    }

    @Override
    public boolean onSupportNavigateUp() {
        return NavigationUI.navigateUp(navController, appBarConfiguration) || super.onSupportNavigateUp();
    }

    @Override
    public void onBackPressed() {
        if (drawerLayout != null && drawerLayout.isDrawerOpen(GravityCompat.START)) {
            drawerLayout.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    private void checkLoginState() {
        GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);
        if (account == null) navigateToLogin();
        else refreshGoogleToken();
    }

    private void refreshGoogleToken() {
        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestIdToken(GOOGLE_WEB_CLIENT_ID).requestEmail().build();
        GoogleSignInClient client = GoogleSignIn.getClient(this, gso);
        client.silentSignIn().addOnSuccessListener(account -> {
            String photoUrl = (account.getPhotoUrl() != null) ? account.getPhotoUrl().toString() : null;
            AuthRepository.loginToWordPress(account.getIdToken(), account.getDisplayName(), account.getEmail(), photoUrl,
                    new AuthRepository.AuthCallback() {
                        @Override public void onSuccess(String role) {
                            if (navController.getCurrentDestination().getId() == R.id.navigation_login) {
                                navController.navigate(R.id.action_login_to_home);
                            }
                        }
                        @Override public void onError(String message) { navigateToLogin(); }
                    });
        }).addOnFailureListener(e -> navigateToLogin());
    }

    private void navigateToLogin() {
        if (navController != null && navController.getCurrentDestination() != null &&
                navController.getCurrentDestination().getId() != R.id.navigation_login) {
            navController.navigate(R.id.navigation_login);
        }
    }

    private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel("kbs_calendar_channel", "Varsler", NotificationManager.IMPORTANCE_HIGH);
            getSystemService(NotificationManager.class).createNotificationChannel(channel);
        }
    }

    private void checkNotificationPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
                requestPermissionLauncher.launch(android.Manifest.permission.POST_NOTIFICATIONS);
            }
        }
    }

    private void checkExactAlarmPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
            if (alarmManager != null && !alarmManager.canScheduleExactAlarms()) {
                Intent intent = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM);
                intent.setData(Uri.parse("package:" + getPackageName()));
                startActivity(intent);
            }
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\MyFirebaseMessagingService.java
============================================================
package com.kbs.kbsintranett;

import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class MyFirebaseMessagingService extends FirebaseMessagingService {

    private static final String TAG = "FCMService";
    private static final String CHANNEL_ID = "kbs_calendar_channel";

    @Override
    public void onNewToken(@NonNull String token) {
        super.onNewToken(token);
        AuthRepository.updateDeviceToken(token);
    }

    @Override
    public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);

        // 1. DATA PAYLOAD (Bakgrunnsoppdatering)
        if (remoteMessage.getData().size() > 0) {
            String forceRefresh = remoteMessage.getData().get("force_refresh");
            String type = remoteMessage.getData().get("type"); // "tasks_update", "news_update", "calendar_update"

            if ("true".equalsIgnoreCase(forceRefresh)) {
                Log.d(TAG, "Mottok force_refresh. Type: " + type);

                // Uansett hva det er, ugyldiggjør cachen slik at neste gang brukeren åpner appen, hentes ferske data.
                if ("news_update".equals(type)) {
                    CacheManager.invalidateCache(getApplicationContext(), "news");
                } else if ("tasks_update".equals(type)) {
                    CacheManager.invalidateCache(getApplicationContext(), "tasks");
                    updateTasksInBackground(); // Hent oppgaver med en gang i bakgrunnen
                } else {
                    // Kalender eller generelt
                    CacheManager.invalidateCache(getApplicationContext(), "calendar");
                    updateCalendarAndAlarms();
                }
            }
        }

        // 2. NOTIFICATION PAYLOAD (Synlig varsel - Nyheter etc)
        // Android viser disse automatisk når appen er i bakgrunnen.
        // Denne koden kjører hvis appen er i forgrunnen, eller hvis meldingen kun er "Data" og vi vil lage varsel manuelt.
        if (remoteMessage.getNotification() != null) {
            showNotification(
                    remoteMessage.getNotification().getTitle(),
                    remoteMessage.getNotification().getBody()
            );
        }
    }

    private void updateCalendarAndAlarms() {
        RetrofitClient.getApiService().getCalendarEvents().enqueue(new Callback<List<CalendarEvent>>() {
            @Override
            public void onResponse(Call<List<CalendarEvent>> call, Response<List<CalendarEvent>> response) {
                if (response.isSuccessful() && response.body() != null) {
                    CacheManager.saveCalendarEvents(getApplicationContext(), response.body());
                    AlarmScheduler.scheduleAlarmsForEvents(getApplicationContext(), response.body());
                }
            }
            @Override public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {}
        });
    }

    private void updateTasksInBackground() {
        // Henter oppgaver stille i bakgrunnen slik at de er klare når brukeren åpner appen
        RetrofitClient.getApiService().getTasks().enqueue(new Callback<List<TaskItem>>() {
            @Override
            public void onResponse(Call<List<TaskItem>> call, Response<List<TaskItem>> response) {
                if (response.isSuccessful() && response.body() != null) {
                    CacheManager.saveTasks(getApplicationContext(), response.body());
                }
            }
            @Override public void onFailure(Call<List<TaskItem>> call, Throwable t) {}
        });
    }

    private void showNotification(String title, String message) {
        createNotificationChannel();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
        }

        Intent tapIntent = new Intent(this, MainActivity.class);
        tapIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        PendingIntent pendingIntent = PendingIntent.getActivity(
                this, 0, tapIntent, PendingIntent.FLAG_IMMUTABLE
        );

        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_stat_kbs)
                .setColor(ContextCompat.getColor(this, R.color.kbs_logo_blue))
                .setContentTitle(title)
                .setContentText(message)
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setContentIntent(pendingIntent)
                .setAutoCancel(true);

        NotificationManagerCompat.from(this).notify((int) System.currentTimeMillis(), builder.build());
    }

    private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    CHANNEL_ID, "KBS Varsler", NotificationManager.IMPORTANCE_HIGH
            );
            getSystemService(NotificationManager.class).createNotificationChannel(channel);
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\NewsAdapter.java
============================================================
package com.kbs.kbsintranett;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
import java.util.List;

public class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> {

    private List<WpPost> posts;
    private OnItemClickListener listener; // NYTT

    // Interface for klikk
    public interface OnItemClickListener {
        void onItemClick(WpPost post);
    }

    // Oppdatert konstruktør
    public NewsAdapter(List<WpPost> posts, OnItemClickListener listener) {
        this.posts = posts;
        this.listener = listener;
    }

    // Overload for bakoverkompatibilitet (hvis du ikke sender listener)
    public NewsAdapter(List<WpPost> posts) {
        this.posts = posts;
        this.listener = null;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        WpPost post = posts.get(position);

        holder.title.setText(post.getTitleStr());
        holder.excerpt.setText(post.getExcerptStr());
        holder.date.setText(post.date);

        String cat = post.getCategoryName();
        if (!cat.isEmpty()) {
            holder.category.setText(cat);
            holder.category.setVisibility(View.VISIBLE);
        } else {
            holder.category.setVisibility(View.GONE);
        }

        String imageUrl = post.getFeaturedImageUrl();
        if (imageUrl != null && !imageUrl.isEmpty()) {
            holder.image.setVisibility(View.VISIBLE);
            Glide.with(holder.itemView.getContext())
                    .load(imageUrl)
                    .transition(DrawableTransitionOptions.withCrossFade())
                    .centerCrop()
                    .into(holder.image);
        } else {
            holder.image.setVisibility(View.GONE);
        }

        // NYTT: Håndter klikk
        holder.itemView.setOnClickListener(v -> {
            if (listener != null) {
                listener.onItemClick(post);
            }
        });
    }

    @Override
    public int getItemCount() {
        return posts.size();
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        TextView title, excerpt, date, category;
        ImageView image;

        public ViewHolder(View view) {
            super(view);
            title = view.findViewById(R.id.news_title);
            excerpt = view.findViewById(R.id.news_excerpt);
            date = view.findViewById(R.id.news_date);
            category = view.findViewById(R.id.news_category);
            image = view.findViewById(R.id.news_image);
        }
    }

    // NYTT: Metode for å oppdatere listen etter filtrering
    public void updateList(List<WpPost> newPosts) {
        this.posts = newPosts;
        notifyDataSetChanged();
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\NewsDetailFragment.java
============================================================
package com.kbs.kbsintranett;

import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;
import com.bumptech.glide.Glide;

public class NewsDetailFragment extends Fragment {

    // CSS Styling (Samme stil som håndboken, pluss bildehåndtering)
    private static final String CSS_STYLE =
            "<style>" +
                    "body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #333333; line-height: 1.6; padding: 0; margin: 0; }" +
                    "p, ul, li { margin-bottom: 12px; font-size: 16px; }" +
                    "a { color: #0069B3; font-weight: bold; text-decoration: none; }" +
                    "h1, h2, h3 { color: #0069B3; margin-top: 20px; margin-bottom: 10px; }" +

                    // Bilde-styling
                    "img { " +
                    "  max-width: 100% !important; " +
                    "  height: auto !important; " +
                    "  border-radius: 4px; " +
                    "  margin: 16px 0; " +
                    "  box-shadow: 0 2px 5px rgba(0,0,0,0.1); " +
                    "  display: block;" +
                    "}" +

                    // Fjerner unødvendig whitespace fra WP-galleri
                    ".gallery-item { margin: 0; padding: 0; }" +
                    "</style>";

    // JavaScript for å fange opp bildeklikk
    private static final String JS_SCRIPT =
            "<script>" +
                    "document.addEventListener('DOMContentLoaded', function() {" +
                    "   var images = document.getElementsByTagName('img');" +
                    "   for (var i = 0; i < images.length; i++) {" +
                    "       images[i].onclick = function() {" +
                    "           Android.showImage(this.src);" +
                    "       }" +
                    "   }" +
                    "});" +
                    "</script>";

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_news_detail, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        if (getArguments() != null) {
            WpPost post = (WpPost) getArguments().getSerializable("post_data");
            if (post != null) {
                setupViews(view, post);
            }
        }
    }

    private void setupViews(View view, WpPost post) {
        Toolbar toolbar = view.findViewById(R.id.detail_toolbar);
        toolbar.setNavigationOnClickListener(v -> Navigation.findNavController(view).navigateUp());

        ImageView image = view.findViewById(R.id.detail_image);
        TextView title = view.findViewById(R.id.detail_title);
        TextView category = view.findViewById(R.id.detail_category);
        TextView date = view.findViewById(R.id.detail_date);
        TextView author = view.findViewById(R.id.detail_author);
        WebView webView = view.findViewById(R.id.detail_webview);

        // Header bilde
        String imgUrl = post.getFeaturedImageUrl();
        if (imgUrl != null) {
            Glide.with(this).load(imgUrl).centerCrop().into(image);
        } else {
            image.setBackgroundColor(getResources().getColor(android.R.color.darker_gray));
        }

        title.setText(post.getTitleStr());
        category.setText(post.getCategoryName());
        date.setText("Publisert: " + post.date);
        author.setText("Av: " + post.getAuthorName());

        // Konfigurer WebView
        WebSettings settings = webView.getSettings();
        settings.setJavaScriptEnabled(true);
        settings.setDomStorageEnabled(true);

        // Legg til Interface for å snakke med Java
        webView.addJavascriptInterface(new WebAppInterface(getContext()), "Android");

        // Håndter linker internt (som i Håndboken)
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                // Bruk samme link-logikk som i Håndboken hvis nødvendig,
                // men her lar vi linker åpnes i nettleser for enkelhets skyld foreløpig
                return false;
            }
        });

        // Bygg HTML
        String rawContent = post.getContentStr();

        // Vask innholdet litt hvis nødvendig (f.eks fjerne inline styles som ødelegger)
        // Her legger vi bare til vår CSS og JS
        String htmlData = "<!DOCTYPE html><html><head>" +
                "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">" +
                CSS_STYLE +
                JS_SCRIPT +
                "</head><body>" +
                rawContent +
                "</body></html>";

        webView.loadDataWithBaseURL("https://intranet.kbs.no", htmlData, "text/html", "UTF-8", null);
    }

    // Bridge-klasse for å ta imot klikk fra JavaScript
    public class WebAppInterface {
        Context mContext;

        WebAppInterface(Context c) {
            mContext = c;
        }

        @JavascriptInterface
        public void showImage(String url) {
            // Må kjøres på UI-tråden
            if (getActivity() != null) {
                getActivity().runOnUiThread(() -> {
                    ImageDialogFragment dialog = ImageDialogFragment.newInstance(url);
                    dialog.show(getParentFragmentManager(), "image_lightbox");
                });
            }
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\NewsFullFragment.java
============================================================
package com.kbs.kbsintranett;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class NewsFullFragment extends Fragment {

    private RecyclerView recyclerViewNews;
    private RecyclerView recyclerViewCategories;
    private ProgressBar progressBar;
    private NewsAdapter newsAdapter;
    private List<WpPost> allPosts = new ArrayList<>();

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_news_full, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        recyclerViewNews = view.findViewById(R.id.recycler_news_full);
        recyclerViewCategories = view.findViewById(R.id.recycler_categories);
        progressBar = view.findViewById(R.id.loading_news_full);

        // Setup Nyhetsliste
        recyclerViewNews.setLayoutManager(new LinearLayoutManager(getContext()));

        // Setup Kategorier (Horisontal)
        recyclerViewCategories.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
        setupCategories();

        fetchAllNews();
    }

    private void setupCategories() {
        List<String> categories = Arrays.asList(
                "Alle",
                "Avtaler og invitasjoner", "BHT", "Bilhold", "Cordel",
                "Ferieavvikling", "Fest og moro", "Generell drift",
                "HMS", "IT og sikkerhet", "Miljøfyrtårn", "Møtereferat", "SMX"
        );

        CategoryAdapter catAdapter = new CategoryAdapter(categories, selectedCategory -> {
            filterNews(selectedCategory);
        });
        recyclerViewCategories.setAdapter(catAdapter);
    }

    private void fetchAllNews() {
        progressBar.setVisibility(View.VISIBLE);
        RetrofitClient.getApiService().getAllPosts().enqueue(new Callback<List<WpPost>>() {
            @Override
            public void onResponse(Call<List<WpPost>> call, Response<List<WpPost>> response) {
                if (!isAdded()) return;
                progressBar.setVisibility(View.GONE);

                if (response.isSuccessful() && response.body() != null) {
                    allPosts = response.body();
                    formatDates(allPosts);

                    newsAdapter = new NewsAdapter(new ArrayList<>(allPosts), post -> {
                        Bundle bundle = new Bundle();
                        bundle.putSerializable("post_data", post);
                        Navigation.findNavController(getView()).navigate(R.id.action_newsFull_to_newsDetail, bundle);
                    });
                    recyclerViewNews.setAdapter(newsAdapter);
                } else {
                    Toast.makeText(getContext(), "Klarte ikke laste nyheter", Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onFailure(Call<List<WpPost>> call, Throwable t) {
                if (!isAdded()) return;
                progressBar.setVisibility(View.GONE);
                Toast.makeText(getContext(), "Nettverksfeil", Toast.LENGTH_SHORT).show();
            }
        });
    }

    private void filterNews(String category) {
        if (newsAdapter == null) return;
        List<WpPost> filteredList = new ArrayList<>();

        if (category.equals("Alle")) {
            filteredList.addAll(allPosts);
        } else {
            for (WpPost post : allPosts) {
                if (post.getCategoryName().equals(category)) {
                    filteredList.add(post);
                }
            }
        }
        newsAdapter.updateList(filteredList);
    }

    private void formatDates(List<WpPost> posts) {
        SimpleDateFormat rawFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
        SimpleDateFormat targetFormat = new SimpleDateFormat("dd. MMM yyyy", Locale.getDefault());

        for (WpPost post : posts) {
            try {
                Date date = rawFormat.parse(post.date);
                post.date = targetFormat.format(date);
            } catch (Exception e) {}
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\NewsItem.java
============================================================
package com.kbs.kbsintranett;

public class NewsItem {
    private String title;
    private String excerpt; // Kort tekst/ingress
    private String author;

    public NewsItem(String title, String excerpt, String author) {
        this.title = title;
        this.excerpt = excerpt;
        this.author = author;
    }

    public String getTitle() { return title; }
    public String getExcerpt() { return excerpt; }
    public String getAuthor() { return author; }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\NotificationHelper.java
============================================================
package com.kbs.kbsintranett;

import android.Manifest;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;

public class NotificationHelper {

    private static final String CHANNEL_ID = "kbs_calendar_channel";

    /**
     * En enkel metode for å vise et standard KBS-varsel.
     */
    public static void showNotification(Context context, String title, String message) {
        // Sjekk tillatelse for Android 13+
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            if (ActivityCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
        }

        // Intent for å åpne appen når man trykker på varselet
        Intent intent = new Intent(context, MainActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        PendingIntent pendingIntent = PendingIntent.getActivity(
                context,
                (int) System.currentTimeMillis(),
                intent,
                PendingIntent.FLAG_IMMUTABLE
        );

        NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_stat_kbs) // Bruker samme ikon som kalender
                .setColor(ContextCompat.getColor(context, R.color.kbs_logo_blue))
                .setContentTitle(title)
                .setContentText(message)
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setContentIntent(pendingIntent)
                .setAutoCancel(true);

        NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
        notificationManager.notify((int) System.currentTimeMillis(), builder.build());
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\ProfileFragment.java
============================================================
package com.kbs.kbsintranett;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;

import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.google.android.gms.auth.api.signin.GoogleSignIn;
import com.google.android.gms.auth.api.signin.GoogleSignInClient;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;

public class ProfileFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_profile, container, false);

        ImageView closeBtn = view.findViewById(R.id.btn_close_profile);
        ImageView profileImage = view.findViewById(R.id.profile_image);
        TextView nameText = view.findViewById(R.id.profile_name);
        TextView emailText = view.findViewById(R.id.profile_email);
        TextView roleText = view.findViewById(R.id.profile_role);
        Button logoutBtn = view.findViewById(R.id.btn_logout);
        Button updateInfoBtn = view.findViewById(R.id.btn_update_info);
        TextView versionText = view.findViewById(R.id.tv_version_info); // NYTT

        UserManager user = UserManager.getInstance();
        nameText.setText(user.getUserDisplayName());
        emailText.setText(user.getUserEmail());
        roleText.setText("Rolle: " + user.getUserRole());

        // NYTT: Sett versjonstekst
        String versionInfo = "Versjon " + BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")";
        versionText.setText(versionInfo);

        if (user.getPhotoUrl() != null) {
            Glide.with(this)
                    .load(user.getPhotoUrl())
                    .apply(RequestOptions.circleCropTransform())
                    .into(profileImage);
        }

        closeBtn.setOnClickListener(v -> {
            Navigation.findNavController(view).navigateUp();
        });

        updateInfoBtn.setOnClickListener(v -> {
            Bundle bundle = new Bundle();
            bundle.putInt("formId", 1);
            Navigation.findNavController(view).navigate(R.id.action_profile_to_form, bundle);
        });

        logoutBtn.setOnClickListener(v -> performLogout());

        return view;
    }

    private void performLogout() {
        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestIdToken(MainActivity.GOOGLE_WEB_CLIENT_ID)
                .requestEmail()
                .build();
        GoogleSignInClient client = GoogleSignIn.getClient(requireActivity(), gso);

        client.signOut().addOnCompleteListener(task -> {
            UserManager.getInstance().logout();
            RetrofitClient.clearClient();
            NavController navController = Navigation.findNavController(requireActivity(), R.id.nav_host_fragment);
            navController.navigate(R.id.action_profile_to_login);
            Toast.makeText(getContext(), "Du er nå logget ut", Toast.LENGTH_SHORT).show();
        });
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\RegisterDeviceRequest.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.annotations.SerializedName;

public class RegisterDeviceRequest {
    @SerializedName("fcm_token")
    public String fcmToken;

    @SerializedName("platform")
    public String platform;

    public RegisterDeviceRequest(String fcmToken) {
        this.fcmToken = fcmToken;
        this.platform = "android";
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\RetrofitClient.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;

import java.io.IOException;
import java.util.List;

import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class RetrofitClient {
    private static final String BASE_URL = "https://intranet.kbs.no/";
    private static Retrofit retrofit = null;
    public static WordPressApiService getApiService() {
        if (retrofit == null) {

            HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
            if (BuildConfig.DEBUG) {
                // I debug-modus logger vi det mest nødvendige
                logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
            } else {
                // I release er vi stille for ytelse og sikkerhet
                logging.setLevel(HttpLoggingInterceptor.Level.NONE);
            }

            OkHttpClient client = new OkHttpClient.Builder()
                    .addInterceptor(logging)
                    .addInterceptor(new Interceptor() {
                        @Override
                        public Response intercept(Chain chain) throws IOException {
                            Request originalRequest = chain.request();
                            Request.Builder builder = originalRequest.newBuilder();

                            String dynamicCookie = UserManager.getInstance().getCookie();
                            if (dynamicCookie != null && !dynamicCookie.isEmpty()) {
                                builder.header("Cookie", dynamicCookie);
                            }

                            return chain.proceed(builder.build());
                        }
                    })
                    .build();

            Gson gson = new GsonBuilder()
                    .registerTypeAdapter(new TypeToken<List<GravityField.Choice>>(){}.getType(), new ChoicesAdapter())
                    .setLenient()
                    .create();

            retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .client(client)
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .build();
        }
        return retrofit.create(WordPressApiService.class);
    }

    public static void clearClient() {
        retrofit = null;
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\TaskAdapter.java
============================================================
package com.kbs.kbsintranett;

import android.graphics.Color;
import android.graphics.Paint;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;

public class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.ViewHolder> {

    private List<TaskItem> tasks;
    private OnTaskClickListener listener;
    private String currentUserEmail;

    public interface OnTaskClickListener {
        void onTaskClick(TaskItem task);
        void onStatusChanged(TaskItem task, boolean isDone);
    }

    public TaskAdapter(List<TaskItem> tasks, OnTaskClickListener listener) {
        this.tasks = tasks;
        this.listener = listener;
        this.currentUserEmail = UserManager.getInstance().getUserEmail();
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_task, parent, false);
        return new ViewHolder(v);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        TaskItem task = tasks.get(position);
        holder.title.setText(task.getTitle());

        if (task.getDueDate() > 0) {
            SimpleDateFormat sdf = new SimpleDateFormat("dd. MMM", Locale.getDefault());
            holder.date.setText("Frist: " + sdf.format(new Date(task.getDueDate())));
        } else {
            holder.date.setText("Ingen frist");
        }

        if (task.getCreatedByEmail() != null && !task.getCreatedByEmail().equalsIgnoreCase(currentUserEmail)) {
            holder.creator.setText("Tildelt av: " + task.getCreatedByName());
            holder.creator.setVisibility(View.VISIBLE);
        } else {
            holder.creator.setVisibility(View.GONE);
        }

        // SJEKK: Er jeg en deltaker?
        boolean isParticipant = task.isUserParticipant(currentUserEmail);

        if (isParticipant) {
            // Hvis jeg er deltaker: Vis checkbox og la meg endre MIN status
            holder.checkBox.setVisibility(View.VISIBLE);
            boolean myStatus = task.getParticipantStatus(currentUserEmail);
            holder.checkBox.setOnCheckedChangeListener(null); // Hindre trigging ved resirkulering
            holder.checkBox.setChecked(myStatus);
            holder.checkBox.setOnClickListener(v -> listener.onStatusChanged(task, holder.checkBox.isChecked()));
        } else {
            // Hvis jeg IKKE er deltaker (f.eks. Admin som ser på "Alle"):
            // Skjul checkboxen i listen. Admin må åpne detaljer for å endre andres status.
            holder.checkBox.setVisibility(View.INVISIBLE);
            holder.checkBox.setOnClickListener(null);
        }

        long now = System.currentTimeMillis();
        // Sjekk overtid (kun hvis frist er satt)
        // Er jeg ikke deltaker, sjekker vi om hele oppgaven er ferdig
        boolean isDoneToCheck = isParticipant ? task.getParticipantStatus(currentUserEmail) : task.isFullyCompleted();
        boolean isOverdue = task.getDueDate() > 0 && task.getDueDate() < now && !task.isFullyCompleted() && !isDoneToCheck;

        if (isOverdue) {
            holder.cardView.setCardBackgroundColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.kbs_soft_light_pink_beige));
            holder.date.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.kbs_logo_accent_red));
            SimpleDateFormat sdf = new SimpleDateFormat("dd. MMM", Locale.getDefault());
            holder.date.setText("FORFALT: " + sdf.format(new Date(task.getDueDate())));
        } else {
            holder.cardView.setCardBackgroundColor(Color.WHITE);
            holder.date.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.kbs_muted_blue_gray));
        }

        if (isDoneToCheck || task.isFullyCompleted()) {
            holder.title.setPaintFlags(holder.title.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
            holder.title.setTextColor(Color.GRAY);
            holder.cardView.setCardBackgroundColor(Color.parseColor("#F5F5F5"));
        } else if (!isOverdue) {
            holder.title.setPaintFlags(holder.title.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
            holder.title.setTextColor(Color.BLACK);
        }

        int total = task.getAssigneeStatus().size();
        int done = 0;
        for (Boolean b : task.getAssigneeStatus().values()) if (b) done++;
        holder.progress.setText("Fremdrift: " + done + "/" + total);

        holder.itemView.setOnClickListener(v -> listener.onTaskClick(task));
    }

    @Override
    public int getItemCount() { return tasks.size(); }

    static class ViewHolder extends RecyclerView.ViewHolder {
        TextView title, date, creator, progress;
        CheckBox checkBox;
        CardView cardView;
        ViewHolder(View v) {
            super(v);
            title = v.findViewById(R.id.task_title);
            date = v.findViewById(R.id.task_date);
            creator = v.findViewById(R.id.task_creator);
            progress = v.findViewById(R.id.task_progress);
            checkBox = v.findViewById(R.id.task_checkbox);
            cardView = (CardView) v;
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\TaskDetailsBottomSheet.java
============================================================
package com.kbs.kbsintranett;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.android.material.switchmaterial.SwitchMaterial;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Map;

public class TaskDetailsBottomSheet extends BottomSheetDialogFragment {

    private TaskItem task;
    private OnTaskChangeListener listener;

    public interface OnTaskChangeListener {
        void onTaskChanged();
        void onTaskDeleted(TaskItem task);
        void onEditRequested(TaskItem task);
    }

    public TaskDetailsBottomSheet(TaskItem task, OnTaskChangeListener listener) {
        this.task = task;
        this.listener = listener;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.bottom_sheet_task_details, container, false);

        TextView title = v.findViewById(R.id.detail_task_title);
        TextView date = v.findViewById(R.id.detail_task_date);
        TextView desc = v.findViewById(R.id.detail_task_desc);
        LinearLayout participantsContainer = v.findViewById(R.id.container_participants_status);
        SwitchMaterial switchNotify = v.findViewById(R.id.switch_notifications);
        LinearLayout ownerActions = v.findViewById(R.id.layout_owner_actions);
        Button btnDelete = v.findViewById(R.id.btn_delete_task);
        Button btnEdit = v.findViewById(R.id.btn_edit_task);
        Button btnClose = v.findViewById(R.id.btn_close_details); // NYTT

        title.setText(task.getTitle());
        if (task.getDueDate() > 0) {
            SimpleDateFormat sdf = new SimpleDateFormat("EEEE dd. MMMM yyyy", Locale.getDefault());
            date.setText("Frist: " + sdf.format(new Date(task.getDueDate())));
        } else {
            date.setText("Ingen frist");
        }

        if (task.getDescription() != null && !task.getDescription().isEmpty()) {
            desc.setText(task.getDescription());
            desc.setVisibility(View.VISIBLE);
        }

        participantsContainer.removeAllViews();
        UserManager userManager = UserManager.getInstance();
        String myEmail = userManager.getUserEmail();

        boolean canManageOthers = task.getCreatedByEmail().equalsIgnoreCase(myEmail) || userManager.isAdmin();

        for (Map.Entry<String, Boolean> entry : task.getAssigneeStatus().entrySet()) {
            String email = entry.getKey();
            boolean isDone = entry.getValue();

            if (canManageOthers) {
                CheckBox cb = new CheckBox(getContext());
                cb.setText(email + (isDone ? " (Fullført)" : ""));
                cb.setChecked(isDone);

                if (isDone) cb.setTextColor(getResources().getColor(android.R.color.darker_gray));
                else cb.setTextColor(getResources().getColor(R.color.black));

                cb.setOnCheckedChangeListener((buttonView, isChecked) -> {
                    task.setParticipantStatus(email, isChecked);

                    cb.setText(email + (isChecked ? " (Fullført)" : ""));
                    if (isChecked) cb.setTextColor(getResources().getColor(android.R.color.darker_gray));
                    else cb.setTextColor(getResources().getColor(R.color.black));

                    if (listener != null) listener.onTaskChanged();

                    // Sjekk om alle er ferdige, i så fall lukk vinduet
                    if (isChecked && areAllParticipantsFinished()) {
                        dismiss();
                    }
                });
                participantsContainer.addView(cb);

            } else {
                TextView t = new TextView(getContext());
                String status = isDone ? "✅ Fullført" : "⏳ Pågår";
                t.setText("• " + email + ": " + status);
                t.setPadding(0, 8, 0, 8);
                t.setTextSize(14);
                participantsContainer.addView(t);
            }
        }

        switchNotify.setChecked(task.isNotificationsEnabled());
        switchNotify.setOnCheckedChangeListener((btn, isChecked) -> {
            task.setNotificationsEnabled(isChecked);
            if (listener != null) listener.onTaskChanged();
        });

        if (canManageOthers) {
            ownerActions.setVisibility(View.VISIBLE);
            btnDelete.setOnClickListener(view -> {
                if (listener != null) listener.onTaskDeleted(task);
                dismiss();
            });
            btnEdit.setOnClickListener(view -> {
                if (listener != null) listener.onEditRequested(task);
                dismiss();
            });
        } else {
            ownerActions.setVisibility(View.GONE);
        }

        // NYTT: Lukk-knapp funksjonalitet
        btnClose.setOnClickListener(view -> dismiss());

        return v;
    }

    private boolean areAllParticipantsFinished() {
        for (Boolean isDone : task.getAssigneeStatus().values()) {
            if (!isDone) return false;
        }
        return true;
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\TaskItem.java
============================================================
package com.kbs.kbsintranett;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class TaskItem implements Serializable {
    private String id;
    private String title;
    private String description;
    private long dueDate;
    private String createdByEmail;
    private String createdByName;

    private Map<String, Boolean> assigneeStatus = new HashMap<>();

    private boolean notificationsEnabled = true;
    private boolean isFullyCompleted = false;

    public TaskItem(String title, String description, long dueDate) {
        this.id = UUID.randomUUID().toString();
        this.title = title;
        this.description = description;
        this.dueDate = dueDate;
        this.createdByEmail = UserManager.getInstance().getUserEmail();
        this.createdByName = UserManager.getInstance().getUserDisplayName();
    }

    // Getters
    public String getId() { return id; }
    public String getTitle() { return title; }
    public String getDescription() { return description; }
    public long getDueDate() { return dueDate; }
    public String getCreatedByEmail() { return createdByEmail; }
    public String getCreatedByName() { return createdByName; }
    public boolean isNotificationsEnabled() { return notificationsEnabled; }
    public boolean isFullyCompleted() { return isFullyCompleted; }

    // Setters (NYTT FOR REDIGERING)
    public void setTitle(String title) { this.title = title; }
    public void setDescription(String description) { this.description = description; }
    public void setDueDate(long dueDate) { this.dueDate = dueDate; }
    public void setNotificationsEnabled(boolean enabled) { this.notificationsEnabled = enabled; }
    public void setFullyCompleted(boolean fullyCompleted) { this.isFullyCompleted = fullyCompleted; }

    // Participant logikk
    public void addAssignee(String email) { assigneeStatus.put(email, false); }
    public void setParticipantStatus(String email, boolean done) { assigneeStatus.put(email, done); }
    public boolean getParticipantStatus(String email) { return assigneeStatus.getOrDefault(email, false); }
    public boolean isUserParticipant(String email) { return assigneeStatus.containsKey(email); }
    public Map<String, Boolean> getAssigneeStatus() { return assigneeStatus; }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\TaskReminderWorker.java
============================================================
package com.kbs.kbsintranett;

import android.content.Context;
import androidx.annotation.NonNull;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
import java.util.List;

public class TaskReminderWorker extends Worker {

    public TaskReminderWorker(@NonNull Context context, @NonNull WorkerParameters params) {
        super(context, params);
    }

    @NonNull
    @Override
    public Result doWork() {
        Context context = getApplicationContext();
        List<TaskItem> tasks = CacheManager.getTasks(context);
        String myEmail = UserManager.getInstance().getUserEmail();
        long now = System.currentTimeMillis();

        for (TaskItem task : tasks) {
            if (!task.isNotificationsEnabled() || task.isFullyCompleted() || task.getParticipantStatus(myEmail)) continue;

            long diff = task.getDueDate() - now;

            // Varsle hvis fristen er i dag (innenfor 24 timer)
            if (diff > 0 && diff < 86400000L) {
                NotificationHelper.showNotification(context, "Frist i dag!", "Oppgaven '" + task.getTitle() + "' forfaller snart.");
            }
            // Varsle hvis over frist (hver gang worker kjører, f.eks annenhver dag)
            else if (diff < 0) {
                NotificationHelper.showNotification(context, "Over frist!", "Oppgaven '" + task.getTitle() + "' er forsinket.");
            }
        }
        return Result.success();
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\TasksFragment.java
============================================================
package com.kbs.kbsintranett;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.tabs.TabLayout;
import com.google.gson.JsonElement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class TasksFragment extends Fragment implements TaskAdapter.OnTaskClickListener {

    private RecyclerView recyclerView;
    private TaskAdapter adapter;
    private TabLayout tabLayout;
    private SwipeRefreshLayout swipeRefresh;
    private CheckBox cbShowCompleted;
    private List<TaskItem> allTasks = new ArrayList<>();
    private String myEmail;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_tasks, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        myEmail = UserManager.getInstance().getUserEmail();
        tabLayout = view.findViewById(R.id.task_tabs);
        recyclerView = view.findViewById(R.id.recycler_tasks);
        swipeRefresh = view.findViewById(R.id.swipe_refresh_tasks);
        cbShowCompleted = view.findViewById(R.id.cb_show_completed);

        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

        setupTabs();

        FloatingActionButton fab = view.findViewById(R.id.fab_add_task);
        fab.setOnClickListener(v -> {
            AddTaskBottomSheet dialog = new AddTaskBottomSheet();
            dialog.setOnTaskAddedListener(new AddTaskBottomSheet.OnTaskAddedListener() {
                @Override
                public void onTaskAdded(TaskItem task) {
                    allTasks.add(0, task);
                    saveAndSync();
                }

                @Override
                public void onTaskUpdated(TaskItem task) {
                    saveAndSync();
                }
            });
            dialog.show(getChildFragmentManager(), "AddTask");
        });

        tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override public void onTabSelected(TabLayout.Tab tab) {
                updateFilterUI(tab.getPosition());
                filterAndDisplay();
            }
            @Override public void onTabUnselected(TabLayout.Tab tab) {}
            @Override public void onTabReselected(TabLayout.Tab tab) {}
        });

        cbShowCompleted.setOnCheckedChangeListener((buttonView, isChecked) -> filterAndDisplay());

        swipeRefresh.setOnRefreshListener(this::fetchTasksFromServer);

        allTasks = CacheManager.getTasks(getContext());
        updateFilterUI(tabLayout.getSelectedTabPosition());
        filterAndDisplay();
        fetchTasksFromServer();
    }

    private void setupTabs() {
        tabLayout.removeAllTabs();
        tabLayout.addTab(tabLayout.newTab().setText("Mine"));
        tabLayout.addTab(tabLayout.newTab().setText("Fullførte"));
        tabLayout.addTab(tabLayout.newTab().setText("Tildelt andre"));
        if (UserManager.getInstance().isEditorOrAbove()) {
            tabLayout.addTab(tabLayout.newTab().setText("Alle"));
        }
    }

    private void updateFilterUI(int tabIndex) {
        if (tabIndex == 3) {
            cbShowCompleted.setVisibility(View.VISIBLE);
        } else {
            cbShowCompleted.setVisibility(View.GONE);
        }
    }

    private void fetchTasksFromServer() {
        swipeRefresh.setRefreshing(true);
        RetrofitClient.getApiService().getTasks().enqueue(new Callback<List<TaskItem>>() {
            @Override
            public void onResponse(Call<List<TaskItem>> call, Response<List<TaskItem>> response) {
                swipeRefresh.setRefreshing(false);
                if (response.isSuccessful() && response.body() != null) {
                    allTasks = response.body();
                    CacheManager.saveTasks(getContext(), allTasks);
                    filterAndDisplay();
                }
            }
            @Override
            public void onFailure(Call<List<TaskItem>> call, Throwable t) {
                swipeRefresh.setRefreshing(false);
                Toast.makeText(getContext(), "Kunne ikke synkronisere oppgaver", Toast.LENGTH_SHORT).show();
            }
        });
    }

    private void filterAndDisplay() {
        List<TaskItem> filtered = new ArrayList<>();
        int selectedTab = tabLayout.getSelectedTabPosition();

        for (TaskItem t : allTasks) {
            boolean isParticipant = t.isUserParticipant(myEmail);
            boolean isCreator = t.getCreatedByEmail().equalsIgnoreCase(myEmail);
            boolean iHaveDoneIt = t.getParticipantStatus(myEmail);
            boolean hasOtherParticipants = false;
            for (String email : t.getAssigneeStatus().keySet()) {
                if (!email.equalsIgnoreCase(myEmail)) { hasOtherParticipants = true; break; }
            }

            // NYTT: Sjekk dynamisk om ALLE deltakerne har fullført oppgaven
            boolean allParticipantsFinished = true;
            if (t.getAssigneeStatus().isEmpty()) {
                allParticipantsFinished = false;
            } else {
                for (Boolean isDone : t.getAssigneeStatus().values()) {
                    if (!isDone) {
                        allParticipantsFinished = false;
                        break;
                    }
                }
            }

            // En oppgave er "helt ferdig" hvis flagget er satt ELLER alle har krysset av
            boolean effectivelyFinished = t.isFullyCompleted() || allParticipantsFinished;

            // Oppdater objektet slik at det lagres riktig neste gang (valgfritt men lurt)
            if (effectivelyFinished && !t.isFullyCompleted()) {
                t.setFullyCompleted(true);
            }

            switch (selectedTab) {
                case 0: // MINE
                    if (isParticipant && !iHaveDoneIt && !t.isFullyCompleted()) filtered.add(t);
                    break;
                case 1: // FULLFØRTE
                    if (t.isFullyCompleted() || (isParticipant && iHaveDoneIt)) filtered.add(t);
                    break;
                case 2: // TILDELT ANDRE
                    if (isCreator && !t.isFullyCompleted() && hasOtherParticipants) filtered.add(t);
                    break;
                case 3: // ALLE (Admin)
                    if (cbShowCompleted.isChecked()) {
                        // Vis alt
                        filtered.add(t);
                    } else {
                        // Vis kun hvis IKKE ferdig
                        if (!effectivelyFinished) filtered.add(t);
                    }
                    break;
            }
        }

        // SORTERING
        Collections.sort(filtered, (t1, t2) -> {
            boolean t1HasDate = t1.getDueDate() > 0;
            boolean t2HasDate = t2.getDueDate() > 0;

            if (selectedTab == 1) {
                if (t1HasDate && !t2HasDate) return -1;
                if (!t1HasDate && t2HasDate) return 1;
                if (!t1HasDate && !t2HasDate) return t1.getTitle().compareToIgnoreCase(t2.getTitle());
                return Long.compare(t2.getDueDate(), t1.getDueDate());
            }
            else {
                if (t1HasDate && !t2HasDate) return -1;
                if (!t1HasDate && t2HasDate) return 1;
                if (!t1HasDate && !t2HasDate) return t1.getTitle().compareToIgnoreCase(t2.getTitle());
                return Long.compare(t1.getDueDate(), t2.getDueDate());
            }
        });

        adapter = new TaskAdapter(filtered, this);
        recyclerView.setAdapter(adapter);
    }

    @Override
    public void onTaskClick(TaskItem task) {
        TaskDetailsBottomSheet sheet = new TaskDetailsBottomSheet(task, new TaskDetailsBottomSheet.OnTaskChangeListener() {
            @Override public void onTaskChanged() { saveAndSync(); }
            @Override public void onTaskDeleted(TaskItem taskToDelete) {
                allTasks.remove(taskToDelete);
                saveAndSync();
            }
            @Override public void onEditRequested(TaskItem taskToEdit) {
                AddTaskBottomSheet editDialog = new AddTaskBottomSheet();
                editDialog.setTaskToEdit(taskToEdit);
                editDialog.setOnTaskAddedListener(new AddTaskBottomSheet.OnTaskAddedListener() {
                    @Override public void onTaskAdded(TaskItem task) {}
                    @Override public void onTaskUpdated(TaskItem task) {
                        saveAndSync();
                    }
                });
                editDialog.show(getChildFragmentManager(), "EditTask");
            }
        });
        sheet.show(getChildFragmentManager(), "TaskDetails");
    }

    @Override
    public void onStatusChanged(TaskItem task, boolean isDone) {
        task.setParticipantStatus(myEmail, isDone);
        // Her trenger vi ikke logikk for "hasOthers" osv, fordi saveAndSync()
        // kaller filterAndDisplay() som nå regner ut status dynamisk.
        saveAndSync();
    }

    private void saveAndSync() {
        CacheManager.saveTasks(getContext(), allTasks);
        filterAndDisplay(); // Oppdaterer visningen umiddelbart
        RetrofitClient.getApiService().syncTasks(allTasks).enqueue(new Callback<JsonElement>() {
            @Override public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {}
            @Override public void onFailure(Call<JsonElement> call, Throwable t) {}
        });
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\User.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class User implements Serializable {
    @SerializedName("id")
    private int id;

    @SerializedName("name")
    private String name;

    @SerializedName("email")
    private String email;

    @SerializedName("roles")
    private List<String> roles; // NYTT: Liste over roller

    // For bruk i UI (sjekkbokser)
    private boolean isSelected = false;

    public int getId() { return id; }
    public String getName() { return name; }
    public String getEmail() { return email; }
    public List<String> getRoles() { return roles != null ? roles : new ArrayList<>(); }

    public boolean isSelected() { return isSelected; }
    public void setSelected(boolean selected) { isSelected = selected; }

    @Override
    public String toString() {
        return name;
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\UserFilterHelper.java
============================================================
package com.kbs.kbsintranett;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class UserFilterHelper {

    private static final List<Integer> EXCLUDED_IDS = Arrays.asList(50, 51); // felles@kbs.no og kbs@kbs.no
    private static final String REQUIRED_DOMAIN = "@kbs.no";

    public static List<User> getFilteredUsers(List<User> allUsers) {
        if (allUsers == null) return new ArrayList<>();

        UserManager me = UserManager.getInstance();
        String myEmail = me.getUserEmail();

        List<User> sanitizedList = new ArrayList<>();
        for (User u : allUsers) {
            String email = u.getEmail() != null ? u.getEmail().toLowerCase() : "";
            if (!email.endsWith(REQUIRED_DOMAIN)) continue;
            if (EXCLUDED_IDS.contains(u.getId())) continue;
            if (u.getRoles() == null || u.getRoles().isEmpty()) continue;

            sanitizedList.add(u);
        }

        if (me.isEditorOrAbove()) {
            return sanitizedList;
        }

        List<User> finalResult = new ArrayList<>();
        List<String> myRoles = getRolesForEmail(sanitizedList, myEmail);

        // NY ROLLE LAGT TIL I LISTEN: amuhmsmiljogruppa
        List<String> deptRoles = Arrays.asList(
                "serviceavdelingen",
                "automasjonsavdelingen",
                "prosjektavdelingen",
                "administrasjonen",
                "kbs_alle",
                "amuhmsmiljogruppa"
        );

        for (User u : sanitizedList) {
            if (u.getEmail().equalsIgnoreCase(myEmail)) {
                finalResult.add(u);
                continue;
            }

            boolean sharesDepartment = false;
            for (String role : u.getRoles()) {
                String r = role.toLowerCase();
                if (deptRoles.contains(r) && myRoles.contains(r)) {
                    sharesDepartment = true;
                    break;
                }
            }

            if (sharesDepartment) {
                finalResult.add(u);
            }
        }

        return finalResult;
    }

    private static List<String> getRolesForEmail(List<User> users, String email) {
        List<String> roles = new ArrayList<>();
        for (User u : users) {
            if (u.getEmail().equalsIgnoreCase(email)) {
                if (u.getRoles() != null) {
                    for (String r : u.getRoles()) roles.add(r.toLowerCase());
                }
                break;
            }
        }
        return roles;
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\UserManager.java
============================================================
// FILSTI: app\src\main\java\com\kbs\kbsintranett\UserManager.java
package com.kbs.kbsintranett;

import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;

/**
 * UserManager fungerer som en global sesjon for appen.
 * Den holder på informasjon om innlogget bruker, rettigheter og autentiserings-cookie.
 */
public class UserManager {
    private static UserManager instance;

    // Google Data
    private String userDisplayName;
    private String userEmail;
    private String googleIdToken;
    private String photoUrl;

    // WordPress Data
    private int userId;
    private String userRole;
    private String currentCookie;

    // Extended Info
    private String firstName;
    private String lastName;
    private String stilling;
    private String mobiltelefon;

    // FCM Token (Push)
    private String fcmToken;

    // NYTT:
    private List<String> writeableCalendars = new ArrayList<>();

    private UserManager() {}

    public static synchronized UserManager getInstance() {
        if (instance == null) {
            instance = new UserManager();
        }
        return instance;
    }

    public void setUserData(String name, String email, String token, @Nullable String photoUrl) {
        this.userDisplayName = name;
        this.userEmail = email;
        this.googleIdToken = token;
        this.photoUrl = photoUrl;
    }

    public void setExtendedUserInfo(String firstName, String lastName, String stilling, String mobiltelefon) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.stilling = stilling;
        this.mobiltelefon = mobiltelefon;
    }

    public void setWriteableCalendars(List<String> calendars) {
        this.writeableCalendars = calendars != null ? calendars : new ArrayList<>();
    }

    public List<String> getWriteableCalendars() {
        return writeableCalendars;
    }

    public void setCookie(String cookie) { this.currentCookie = cookie; }
    public void setUserRole(String role) { this.userRole = role; }
    public void setUserId(int id) { this.userId = id; }

    public String getUserDisplayName() { return userDisplayName != null ? userDisplayName : ""; }
    public String getUserEmail() { return userEmail != null ? userEmail : ""; }
    public String getGoogleIdToken() { return googleIdToken; }
    public String getPhotoUrl() { return photoUrl; }
    public String getCookie() { return currentCookie; }
    public String getUserRole() { return userRole != null ? userRole : "subscriber"; }
    public int getUserId() { return userId; }
    public String getFirstName() { return firstName != null ? firstName : ""; }
    public String getLastName() { return lastName != null ? lastName : ""; }
    public String getStilling() { return stilling != null ? stilling : ""; }
    public String getMobiltelefon() { return mobiltelefon != null ? mobiltelefon : ""; }

    public void setFcmToken(String token) { this.fcmToken = token; }
    public String getFcmToken() { return fcmToken; }

    public boolean isLoggedIn() { return userEmail != null && !userEmail.isEmpty(); }
    public boolean isAdmin() { return "administrator".equalsIgnoreCase(userRole); }
    public boolean isEditorOrAbove() {
        return userRole != null && (userRole.equalsIgnoreCase("administrator") || userRole.equalsIgnoreCase("editor"));
    }

    public void logout() {
        userDisplayName = null;
        userEmail = null;
        googleIdToken = null;
        photoUrl = null;
        userRole = null;
        currentCookie = null;
        userId = 0;
        firstName = null;
        lastName = null;
        stilling = null;
        mobiltelefon = null;
        writeableCalendars.clear();
        // Vi sletter ikke fcmToken ved logout, da enheten fortsatt er den samme
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\WebViewActivity.java
============================================================
package com.kbs.kbsintranett;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.webkit.CookieManager;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;

public class WebViewActivity extends AppCompatActivity {

    public static final String EXTRA_URL = "extra_url";
    public static final String EXTRA_TITLE = "extra_title";

    @SuppressLint("SetJavaScriptEnabled")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_webview);

        String url = getIntent().getStringExtra(EXTRA_URL);
        String title = getIntent().getStringExtra(EXTRA_TITLE);

        Toolbar toolbar = findViewById(R.id.toolbar);
        toolbar.setTitle(title != null ? title : "Håndbok");
        toolbar.setNavigationIcon(android.R.drawable.ic_menu_revert);
        toolbar.setNavigationOnClickListener(v -> finish());

        WebView webView = findViewById(R.id.webview);
        WebSettings settings = webView.getSettings();
        settings.setJavaScriptEnabled(true);
        settings.setDomStorageEnabled(true);
        settings.setBuiltInZoomControls(true);
        settings.setDisplayZoomControls(false);

        // --- MAGIEN SKJER HER: INJISER COOKIE ---
        String cookie = UserManager.getInstance().getCookie();
        if (cookie != null && !cookie.isEmpty()) {
            CookieManager cookieManager = CookieManager.getInstance();
            cookieManager.setAcceptCookie(true);
            // Vi antar at domenet er intranet.kbs.no basert på APIet
            cookieManager.setCookie("https://intranet.kbs.no", cookie);
        }
        // ----------------------------------------

        webView.setWebViewClient(new WebViewClient()); // Åpne linker i samme WebView

        if (url != null) {
            webView.loadUrl(url);
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\WordPressApiService.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.util.List;
import java.util.Map;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Path;
import retrofit2.http.Multipart;
import retrofit2.http.Part;
import retrofit2.http.PartMap;
import retrofit2.http.Query;

public interface WordPressApiService {

    // --- NYHETER ---
    @GET("wp-json/wp/v2/posts?per_page=10&_embed")
    Call<List<WpPost>> getPosts();

    @GET("wp-json/wp/v2/posts?per_page=50&_embed")
    Call<List<WpPost>> getAllPosts();


    // --- AUTENTISERING & ENHET ---
    @POST("wp-json/kbs/v1/login")
    Call<LoginResponse> googleLogin(@Body LoginRequest request);

    @POST("wp-json/kbs/v1/device/register")
    Call<JsonElement> registerDevice(@Body RegisterDeviceRequest request);

    @GET("wp-json/kbs/v1/users")
    Call<List<User>> getUsersList();


    // --- KALENDER ---
    @GET("wp-json/kbs/v1/calendar/events")
    Call<List<CalendarEvent>> getCalendarEvents();

    @POST("wp-json/kbs/v1/calendar/create")
    Call<JsonElement> createCalendarEvent(@Body CreateEventRequest request);

    @POST("wp-json/kbs/v1/calendar/update")
    Call<JsonElement> updateCalendarEvent(@Body CreateEventRequest request);

    @POST("wp-json/kbs/v1/calendar/delete")
    Call<JsonElement> deleteCalendarEvent(@Body CreateEventRequest request);


    // --- SKJEMAER (GRAVITY FORMS) ---
    @GET("wp-json/kbs/v1/forms")
    Call<List<GravityForm>> getFormsList();

    @GET("wp-json/kbs/v1/forms/{id}")
    Call<GravityForm> getForm(@Path("id") int formId);

    @Multipart
    @POST("wp-json/gf/v2/forms/{id}/submissions")
    Call<JsonElement> submitMultipartForm(
            @Path("id") int formId,
            @PartMap Map<String, RequestBody> textFields,
            @Part List<MultipartBody.Part> files
    );

    @GET("wp-json/gf/v2/entries")
    Call<GravityEntryResponse> getEntries(
            @Query("form_ids") int formId,
            @Query("search") String searchJson,
            @Query("paging[page_size]") int pageSize
    );

    @GET("wp-json/gf/v2/entries/{entry_id}")
    Call<JsonElement> getSingleEntry(@Path("entry_id") String entryId);


    // --- HÅNDBOK ---
    @GET("wp-json/kbs/v1/handbook")
    Call<List<HandbookItem>> getHandbookItems();

    @GET("wp-json/kbs/v1/handbook/{id}")
    Call<HandbookPage> getHandbookPage(@Path("id") int id);

    @GET("wp-json/kbs/v1/lookup-id")
    Call<JsonObject> lookupPageId(@Query("url") String url);


    // --- OPPGAVER (SYNKRONISERING) ---
    @GET("wp-json/kbs/v1/tasks")
    Call<List<TaskItem>> getTasks();

    @POST("wp-json/kbs/v1/tasks/sync")
    Call<JsonElement> syncTasks(@Body List<TaskItem> tasks);
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\WpPost.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;

public class WpPost implements Serializable {
    @SerializedName("title")
    public Rendered title;

    @SerializedName("excerpt")
    public Rendered excerpt;

    @SerializedName("content")
    public Rendered content;

    @SerializedName("date")
    public String date;

    @SerializedName("_embedded")
    public Embedded embedded;

    public static class Rendered implements Serializable {
        @SerializedName("rendered")
        public String renderedString;
    }

    public static class Embedded implements Serializable {
        @SerializedName("wp:featuredmedia")
        public List<Media> mediaList;

        @SerializedName("wp:term")
        public List<List<Term>> termList;

        // NYTT: Forfatter-liste
        @SerializedName("author")
        public List<Author> authorList;
    }

    public static class Media implements Serializable {
        @SerializedName("source_url")
        public String sourceUrl;
    }

    public static class Term implements Serializable {
        @SerializedName("name")
        public String name;
    }

    // NYTT: Forfatter-klasse
    public static class Author implements Serializable {
        @SerializedName("name")
        public String name;
    }

    public String getTitleStr() {
        return title != null ? title.renderedString : "Uten tittel";
    }

    public String getExcerptStr() {
        return excerpt != null ?
                android.text.Html.fromHtml(excerpt.renderedString, android.text.Html.FROM_HTML_MODE_COMPACT).toString().trim() : "";
    }

    public String getContentStr() {
        return content != null ? content.renderedString : "";
    }

    public String getFeaturedImageUrl() {
        if (embedded != null && embedded.mediaList != null && !embedded.mediaList.isEmpty()) {
            return embedded.mediaList.get(0).sourceUrl;
        }
        return null;
    }

    // NYTT: Hent forfatternavn
    public String getAuthorName() {
        if (embedded != null && embedded.authorList != null && !embedded.authorList.isEmpty()) {
            return embedded.authorList.get(0).name;
        }
        return "Ukjent"; // Fallback
    }

    public String getCategoryName() {
        if (embedded != null && embedded.termList != null && !embedded.termList.isEmpty()) {
            List<Term> categories = embedded.termList.get(0);
            if (categories == null || categories.isEmpty()) return "";

            List<String> priorityCategories = Arrays.asList(
                    "Avtaler og invitasjoner", "BHT", "Bilhold", "Cordel",
                    "Ferieavvikling", "Fest og moro", "Generell drift",
                    "HMS", "IT og sikkerhet", "Miljøfyrtårn", "Møtereferat", "SMX"
            );

            for (Term term : categories) {
                if (priorityCategories.contains(term.name)) return term.name;
            }
            for (Term term : categories) {
                if (term.name.contains("Alle ansatte")) return "Til info";
            }
            return categories.get(0).name;
        }
        return "";
    }
}

============================================================
FILSTI: app\src\main\res\color\selector_day_text.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_checked="true" android:color="#FFFFFF"/>
    <item android:color="#333333"/>
</selector>

============================================================
FILSTI: app\src\main\res\drawable\bg_category_selected.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/kbs_logo_blue" />
    <corners android:radius="20dp" />
</shape>

============================================================
FILSTI: app\src\main\res\drawable\bg_category_unselected.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#FFFFFF" />
    <stroke android:width="1dp" android:color="#DDDDDD" />
    <corners android:radius="20dp" />
</shape>

============================================================
FILSTI: app\src\main\res\drawable\bg_date_box.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
    <solid android:color="@color/kbs_logo_light_blue"/>
    <corners android:radius="8dp"/>
</shape>

============================================================
FILSTI: app\src\main\res\drawable\ic_book.xml
============================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
      
    <path android:fillColor="@android:color/white" android:pathData="M240,880Q207,880 183.5,856.5Q160,833 160,800L160,160Q160,127 183.5,103.5Q207,80 240,80L720,80Q753,80 776.5,103.5Q800,127 800,160L800,800Q800,833 776.5,856.5Q753,880 720,880L240,880ZM240,800L720,800Q720,800 720,800Q720,800 720,800L720,160Q720,160 720,160Q720,160 720,160L640,160L640,440L540,380L440,440L440,160L240,160Q240,160 240,160Q240,160 240,160L240,800Q240,800 240,800Q240,800 240,800ZM240,800Q240,800 240,800Q240,800 240,800L240,160Q240,160 240,160Q240,160 240,160L240,160Q240,160 240,160Q240,160 240,160L240,800Q240,800 240,800Q240,800 240,800ZM440,440L540,380L640,440L640,440L540,380L440,440Z"/>
    
</vector>


============================================================
FILSTI: app\src\main\res\drawable\ic_form.xml
============================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
      
    <path android:fillColor="@android:color/white" android:pathData="M22,7h-9v2h9V7zM22,15h-9v2h9V15zM5.54,11L2,7.46l1.41,-1.41l2.12,2.12l4.24,-4.24l1.41,1.41L5.54,11zM5.54,19L2,15.46l1.41,-1.41l2.12,2.12l4.24,-4.24l1.41,1.41L5.54,19z"/>
    
</vector>


============================================================
FILSTI: app\src\main\res\drawable\ic_handbook_car.xml
============================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="#0069B3">
    <path android:fillColor="@android:color/white" android:pathData="M18.92,6.01C18.72,5.42 18.16,5 17.5,5h-11c-0.66,0 -1.21,0.42 -1.42,1.01L3,12v8c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1h12v1c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-8l-2.08,-5.99zM6.5,16c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,13 6.5,13s1.5,0.67 1.5,1.5S7.33,16 6.5,16zM17.5,16c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM5,11l1.5,-4.5h11L19,11H5z"/>
</vector>

============================================================
FILSTI: app\src\main\res\drawable\ic_handbook_doc.xml
============================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="#0069B3">
    <path android:fillColor="@android:color/white" android:pathData="M14,2H6c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2H18c1.1,0 2,-0.9 2,-2V8l-6,-6zm2,16H8v-2h8v2zm0,-4H8v-2h8v2zm-3,-5V3.5L18.5,9H13z"/>
</vector>

============================================================
FILSTI: app\src\main\res\drawable\ic_handbook_general.xml
============================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="#0069B3">
    <path android:fillColor="@android:color/white" android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2V7h-2v2z"/>
</vector>

============================================================
FILSTI: app\src\main\res\drawable\ic_handbook_health.xml
============================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="#0069B3">
    <path android:fillColor="@android:color/white" android:pathData="M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5C21,3.9 20.1,3 19,3zM17,13h-4v4h-2v-4H7v-2h4V7h2v4h4V13z"/>
</vector>

============================================================
FILSTI: app\src\main\res\drawable\ic_handbook_people.xml
============================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="#0069B3">
    <path android:fillColor="@android:color/white" android:pathData="M16,11c1.66,0 2.99,-1.34 2.99,-3S17.66,5 16,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zm-8,0c1.66,0 2.99,-1.34 2.99,-3S9.66,5 8,5C6.34,5 5,6.34 5,8s1.34,3 3,3zm0,2c-2.33,0 -7,1.17 -7,3.5V19h14v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5zm8,0c-0.29,0 -0.62,0.02 -0.97,0.05 1.16,0.84 1.97,1.97 1.97,3.45V19h6v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5z"/>
</vector>

============================================================
FILSTI: app\src\main\res\drawable\ic_handbook_warning.xml
============================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="#0069B3">
    <path android:fillColor="@android:color/white" android:pathData="M1,21h22L12,2 1,21zm12,-3h-2v-2h2v2zm0,-4h-2v-4h2v4z"/>
</vector>

============================================================
FILSTI: app\src\main\res\drawable\ic_home.xml
============================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
      
    <path android:fillColor="@android:color/white" android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
    
</vector>


============================================================
FILSTI: app\src\main\res\drawable\ic_launcher_background.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<vector
    android:height="108dp"
    android:width="108dp"
    android:viewportHeight="108"
    android:viewportWidth="108"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="#3DDC84"
          android:pathData="M0,0h108v108h-108z"/>
    <path android:fillColor="#00000000" android:pathData="M9,0L9,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,0L19,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M29,0L29,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M39,0L39,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M49,0L49,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M59,0L59,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M69,0L69,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M79,0L79,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M89,0L89,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M99,0L99,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,9L108,9"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,19L108,19"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,29L108,29"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,39L108,39"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,49L108,49"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,59L108,59"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,69L108,69"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,79L108,79"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,89L108,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,99L108,99"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,29L89,29"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,39L89,39"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,49L89,49"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,59L89,59"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,69L89,69"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,79L89,79"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M29,19L29,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M39,19L39,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M49,19L49,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M59,19L59,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M69,19L69,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M79,19L79,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>


============================================================
FILSTI: app\src\main\res\drawable\ic_launcher_foreground.xml
============================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="108dp"
    android:height="108dp"
    android:viewportWidth="108"
    android:viewportHeight="108">
    <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
        <aapt:attr name="android:fillColor">
            <gradient
                android:endX="85.84757"
                android:endY="92.4963"
                android:startX="42.9492"
                android:startY="49.59793"
                android:type="linear">
                <item
                    android:color="#44000000"
                    android:offset="0.0" />
                <item
                    android:color="#00000000"
                    android:offset="1.0" />
            </gradient>
        </aapt:attr>
    </path>
    <path
        android:fillColor="#FFFFFF"
        android:fillType="nonZero"
        android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
        android:strokeWidth="1"
        android:strokeColor="#00000000" />
</vector>

============================================================
FILSTI: app\src\main\res\drawable\selector_day_toggle.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_checked="true">
        <shape android:shape="oval">
            <solid android:color="#0069B3"/> <!-- KBS Blå -->
        </shape>
    </item>
    <item>
        <shape android:shape="oval">
            <solid android:color="#EEEEEE"/> <!-- Lys grå -->
        </shape>
    </item>

</selector>

============================================================
FILSTI: app\src\main\res\layout\activity_main.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@color/white"
            android:elevation="4dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent" />

        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:defaultNavHost="true"
            app:navGraph="@navigation/mobile_navigation"
            app:layout_constraintTop_toBottomOf="@id/toolbar"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main"
        app:menu="@menu/drawer_menu" />

</androidx.drawerlayout.widget.DrawerLayout>

============================================================
FILSTI: app\src\main\res\layout\activity_webview.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/white"
        android:elevation="4dp"
        android:theme="@style/ThemeOverlay.AppCompat.Light" />

    <WebView
        android:id="@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\bottom_sheet_add_task.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:fillViewport="true">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="24dp"
        android:background="@color/white">

        <TextView
            android:id="@+id/txt_sheet_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Ny Oppgave"
            android:textSize="20sp"
            android:textStyle="bold"
            android:textColor="@color/black"
            android:layout_marginBottom="16dp"/>

        <EditText
            android:id="@+id/et_task_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Hva skal gjøres?"
            android:inputType="textCapSentences"
            android:layout_marginBottom="8dp"/>

        <EditText
            android:id="@+id/et_task_desc"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Beskrivelse (valgfritt)"
            android:inputType="textMultiLine"
            android:minLines="2"
            android:gravity="top"
            android:layout_marginBottom="16dp"/>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="center_vertical"
            android:layout_marginBottom="12dp">

            <Button
                android:id="@+id/btn_task_date"
                style="@style/Widget.MaterialComponents.Button.TextButton"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Sett frist" />

            <TextView
                android:id="@+id/txt_date_preview"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Frist: -"
                android:layout_marginStart="8dp"/>

            <!-- NYTT: Fjern frist knapp -->
            <Button
                android:id="@+id/btn_clear_date"
                style="@style/Widget.MaterialComponents.Button.TextButton"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="X"
                android:textColor="#999999"
                android:visibility="gone"/>
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="center_vertical"
            android:layout_marginBottom="24dp">
            <Button
                android:id="@+id/btn_task_users"
                style="@style/Widget.MaterialComponents.Button.TextButton"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Velg deltakere" />
            <TextView
                android:id="@+id/txt_users_preview"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Kun meg"
                android:layout_marginStart="8dp"/>
        </LinearLayout>

        <Button
            android:id="@+id/btn_save_task"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Lagre Oppgave"
            android:backgroundTint="@color/kbs_logo_blue"
            android:textColor="@color/white"
            android:layout_marginBottom="16dp"/>

    </LinearLayout>
</androidx.core.widget.NestedScrollView>

============================================================
FILSTI: app\src\main\res\layout\bottom_sheet_calendar_details.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="24dp"
    android:background="@android:color/white">

    <!-- Kalendernavn (Lite merke øverst) -->
    <TextView
        android:id="@+id/sheet_calendar_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="KALENDERNAVN"
        android:textSize="12sp"
        android:textStyle="bold"
        android:textColor="@color/white"
        android:background="@drawable/bg_date_box"
        android:paddingHorizontal="8dp"
        android:paddingVertical="4dp"
        android:layout_marginBottom="12dp"/>

    <TextView
        android:id="@+id/sheet_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Tittel"
        android:textSize="22sp"
        android:textStyle="bold"
        android:textColor="@color/black"
        android:layout_marginBottom="8dp"/>

    <TextView
        android:id="@+id/sheet_time"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Tidspunkt"
        android:textSize="16sp"
        android:textColor="@color/kbs_muted_blue_gray"
        android:layout_marginBottom="12dp"
        android:drawablePadding="8dp"
        app:drawableStartCompat="@android:drawable/ic_menu_recent_history"/>

    <TextView
        android:id="@+id/sheet_location"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Sted"
        android:textSize="16sp"
        android:textColor="@color/kbs_logo_blue"
        android:textStyle="bold"
        android:layout_marginBottom="16dp"
        android:visibility="gone"
        android:drawablePadding="8dp"
        android:gravity="center_vertical"
        android:background="?attr/selectableItemBackground"
        android:paddingVertical="8dp"
        app:drawableStartCompat="@android:drawable/ic_dialog_map"/>

    <!-- NYTT: Arrangør -->
    <TextView
        android:id="@+id/sheet_organizer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Invitert av: ..."
        android:textSize="14sp"
        android:textColor="#333"
        android:background="#F5F5F5"
        android:padding="12dp"
        android:layout_marginBottom="4dp"
        android:visibility="gone"/>

    <!-- Deltakere -->
    <TextView
        android:id="@+id/sheet_participants"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Synlig for: ..."
        android:textSize="14sp"
        android:textColor="#333"
        android:background="#F5F5F5"
        android:padding="12dp"
        android:layout_marginBottom="16dp"
        android:visibility="gone"/>

    <TextView
        android:id="@+id/sheet_desc"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Beskrivelse..."
        android:textSize="14sp"
        android:textColor="#555"
        android:layout_marginBottom="24dp"
        android:autoLink="web|email|phone"
        android:linksClickable="true"/>

    <!-- ADMIN KNAPPER (Vises kun for admin) -->
    <LinearLayout
        android:id="@+id/layout_admin_buttons"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:visibility="gone"
        android:layout_marginTop="16dp">

        <Button
            android:id="@+id/btn_delete"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Slett"
            android:backgroundTint="#D32F2F"
            android:textColor="#FFF"
            android:layout_marginEnd="8dp"/>

        <Button
            android:id="@+id/btn_edit"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Endre"
            android:backgroundTint="@color/kbs_logo_blue"
            android:textColor="#FFF"
            android:layout_marginStart="8dp"/>
    </LinearLayout>

    <Button
        android:id="@+id/btn_add_to_calendar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:visibility="gone"/>

</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\bottom_sheet_task_details.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="24dp"
    android:background="@color/white">

    <TextView
        android:id="@+id/detail_task_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Oppgavetittel"
        android:textSize="22sp"
        android:textStyle="bold"
        android:textColor="@color/black"
        android:layout_marginBottom="8dp"/>

    <TextView
        android:id="@+id/detail_task_date"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Frist: -"
        android:textSize="14sp"
        android:textColor="@color/kbs_muted_blue_gray"
        android:layout_marginBottom="16dp"/>

    <TextView
        android:id="@+id/detail_task_desc"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Beskrivelse kommer her..."
        android:textSize="16sp"
        android:textColor="#333"
        android:layout_marginBottom="24dp"
        android:visibility="gone"/>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#EEEEEE"
        android:layout_marginBottom="16dp"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Status deltakere:"
        android:textStyle="bold"
        android:textSize="14sp"
        android:layout_marginBottom="8dp"/>

    <LinearLayout
        android:id="@+id/container_participants_status"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_marginBottom="24dp"/>

    <com.google.android.material.switchmaterial.SwitchMaterial
        android:id="@+id/switch_notifications"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Aktiver påminnelser til deltakere"
        android:checked="true"
        android:layout_marginBottom="24dp"/>

    <LinearLayout
        android:id="@+id/layout_owner_actions"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginBottom="16dp"
        android:visibility="gone">

        <Button
            android:id="@+id/btn_delete_task"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Slett"
            android:backgroundTint="@color/kbs_logo_accent_red"
            android:textColor="@color/white"
            android:layout_marginEnd="8dp"/>

        <Button
            android:id="@+id/btn_edit_task"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Endre"
            android:backgroundTint="@color/kbs_logo_blue"
            android:textColor="@color/white"
            android:layout_marginStart="8dp"/>
    </LinearLayout>

    <!-- NY LUKK KNAPP -->
    <Button
        android:id="@+id/btn_close_details"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Lukk / Ferdig"
        style="@style/Widget.MaterialComponents.Button.OutlinedButton"
        android:textColor="@color/kbs_muted_blue_gray"/>

</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\dialog_custom_recurrence.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="24dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Egendefinert gjentakelse"
            android:textSize="20sp"
            android:textStyle="bold"
            android:textColor="#333"
            android:layout_marginBottom="24dp"/>

        <!-- FREKVENS -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="center_vertical"
            android:layout_marginBottom="24dp">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Gjenta hvert: "
                android:textSize="16sp"/>

            <EditText
                android:id="@+id/et_interval"
                android:layout_width="60dp"
                android:layout_height="wrap_content"
                android:inputType="number"
                android:text="1"
                android:gravity="center"
                android:layout_marginHorizontal="8dp"/>

            <Spinner
                android:id="@+id/spinner_freq"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:entries="@array/recurrence_freq_array"/>
        </LinearLayout>

        <!-- UKEDAGER (Vises kun hvis Uke er valgt) -->
        <LinearLayout
            android:id="@+id/layout_weekdays"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:layout_marginBottom="24dp"
            android:visibility="visible">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Gjenta på"
                android:layout_marginBottom="8dp"/>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
                android:weightSum="7">

                <ToggleButton android:id="@+id/tg_mon" android:textOn="M" android:textOff="M" style="@style/DayToggle"/>
                <ToggleButton android:id="@+id/tg_tue" android:textOn="T" android:textOff="T" style="@style/DayToggle"/>
                <ToggleButton android:id="@+id/tg_wed" android:textOn="O" android:textOff="O" style="@style/DayToggle"/>
                <ToggleButton android:id="@+id/tg_thu" android:textOn="T" android:textOff="T" style="@style/DayToggle"/>
                <ToggleButton android:id="@+id/tg_fri" android:textOn="F" android:textOff="F" style="@style/DayToggle"/>
                <ToggleButton android:id="@+id/tg_sat" android:textOn="L" android:textOff="L" style="@style/DayToggle"/>
                <ToggleButton android:id="@+id/tg_sun" android:textOn="S" android:textOff="S" style="@style/DayToggle"/>
            </LinearLayout>
        </LinearLayout>

        <!-- SLUTTER -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Slutter"
            android:layout_marginBottom="8dp"/>

        <RadioGroup
            android:id="@+id/rg_end"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <RadioButton
                android:id="@+id/rb_never"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Aldri"
                android:checked="true"/>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
                android:gravity="center_vertical">

                <RadioButton
                    android:id="@+id/rb_date"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Den"/>

                <Button
                    android:id="@+id/btn_end_date_picker"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Velg dato"
                    android:layout_marginStart="16dp"
                    style="@style/Widget.MaterialComponents.Button.TextButton"/>
            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
                android:gravity="center_vertical">

                <RadioButton
                    android:id="@+id/rb_count"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Etter"/>

                <EditText
                    android:id="@+id/et_count"
                    android:layout_width="60dp"
                    android:layout_height="wrap_content"
                    android:inputType="number"
                    android:text="13"
                    android:gravity="center"
                    android:layout_marginHorizontal="16dp"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="ganger"/>
            </LinearLayout>
        </RadioGroup>

        <!-- KNAPPER -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="end"
            android:layout_marginTop="32dp">

            <Button
                android:id="@+id/btn_cancel"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Avbryt"
                style="@style/Widget.MaterialComponents.Button.TextButton"
                android:textColor="#666"/>

            <Button
                android:id="@+id/btn_done"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Ferdig"
                android:backgroundTint="@color/kbs_logo_blue"
                android:textColor="#FFF"
                android:layout_marginStart="16dp"/>
        </LinearLayout>

    </LinearLayout>
</ScrollView>

============================================================
FILSTI: app\src\main\res\layout\fragment_calendar_full.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_full_calendar"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipToPadding="false"
        android:paddingBottom="80dp" />

    <ProgressBar
        android:id="@+id/loading_full_calendar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"/>

    <TextView
        android:id="@+id/empty_view_calendar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Ingen hendelser funnet"
        android:layout_gravity="center"
        android:visibility="gone"/>

    <!-- NY FAB: Flytende knapp nederst til høyre -->
    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab_add_calendar_event"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="24dp"
        android:src="@android:drawable/ic_input_add"
        app:backgroundTint="@color/kbs_logo_blue"
        app:tint="@color/white"
        android:visibility="gone"
        android:contentDescription="Ny hendelse" />

</FrameLayout>

============================================================
FILSTI: app\src\main\res\layout\fragment_create_event.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="16dp">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Opprett ny hendelse"
            android:textSize="24sp"
            android:textStyle="bold"
            android:layout_marginBottom="24dp"
            android:textColor="#333"/>

        <!-- TITTEL -->
        <EditText
            android:id="@+id/et_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Tittel"
            android:inputType="textCapSentences"
            android:padding="12dp"
            android:background="@android:drawable/edit_text"
            android:layout_marginBottom="16dp"/>

        <!-- BESKRIVELSE -->
        <EditText
            android:id="@+id/et_desc"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Beskrivelse / Notater"
            android:inputType="textMultiLine"
            android:minLines="3"
            android:padding="12dp"
            android:gravity="top"
            android:background="@android:drawable/edit_text"
            android:layout_marginBottom="16dp"/>

        <!-- STED -->
        <EditText
            android:id="@+id/et_location"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Hvor (Sted)"
            android:inputType="textCapWords"
            android:padding="12dp"
            android:background="@android:drawable/edit_text"
            android:layout_marginBottom="16dp"/>

        <!-- KALENDER VALG -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Velg Kalender:"
            android:textSize="14sp"
            android:textColor="#666"/>

        <Spinner
            android:id="@+id/spinner_calendar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="16dp"
            android:padding="12dp"/>

        <!-- SYNLIGHET / DELTAKERE (NYTT) -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Synlighet / Deltakere:"
            android:textSize="14sp"
            android:textColor="#666"
            android:layout_marginTop="8dp"/>

        <RadioGroup
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:layout_marginBottom="8dp">

            <RadioButton
                android:id="@+id/rb_visibility_all"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Alle i kalenderen (Standard)"
                android:checked="true"/>

            <RadioButton
                android:id="@+id/rb_visibility_specific"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Begrens til valgte personer..."/>
        </RadioGroup>

        <Button
            android:id="@+id/btn_select_participants"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Velg personer"
            android:visibility="gone"
            style="@style/Widget.MaterialComponents.Button.OutlinedButton"
            android:layout_marginBottom="16dp"/>

        <TextView
            android:id="@+id/txt_selected_participants"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Ingen valgt"
            android:textSize="12sp"
            android:textStyle="italic"
            android:visibility="gone"
            android:layout_marginBottom="16dp"/>

        <!-- HELE DAGEN -->
        <Switch
            android:id="@+id/switch_all_day"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Hele dagen"
            android:layout_marginBottom="16dp"/>

        <!-- START DATO/TID -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginBottom="8dp">

            <Button
                android:id="@+id/btn_start_date"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Startdato"
                android:layout_marginEnd="4dp"/>

            <Button
                android:id="@+id/btn_start_time"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Starttid"
                android:layout_marginStart="4dp"/>
        </LinearLayout>

        <!-- SLUTT DATO/TID -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginBottom="16dp">

            <Button
                android:id="@+id/btn_end_date"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Sluttdato"
                android:layout_marginEnd="4dp"/>

            <Button
                android:id="@+id/btn_end_time"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Slutttid"
                android:layout_marginStart="4dp"/>
        </LinearLayout>

        <TextView
            android:id="@+id/txt_time_preview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Valgt: -"
            android:gravity="center"
            android:textStyle="bold"
            android:layout_marginBottom="24dp"/>

        <!-- GJENTAKELSE -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Gjentakelse:"
            android:textSize="14sp"
            android:textColor="#666"/>

        <Spinner
            android:id="@+id/spinner_recurrence"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="16dp"
            android:padding="12dp"/>

        <!-- VARSLING MED CHIPS -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Varsling (Velg en eller flere):"
            android:textSize="14sp"
            android:textColor="#666"
            android:layout_marginBottom="8dp"/>

        <HorizontalScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scrollbars="none"
            android:layout_marginBottom="32dp">

            <com.google.android.material.chip.ChipGroup
                android:id="@+id/chip_group_reminders"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:singleLine="true"
                app:selectionRequired="false">
            </com.google.android.material.chip.ChipGroup>

        </HorizontalScrollView>

        <Button
            android:id="@+id/btn_save_event"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Lagre i Kalender"
            android:backgroundTint="@color/kbs_logo_blue"
            android:textColor="#FFFFFF"/>

    </LinearLayout>

</ScrollView>

============================================================
FILSTI: app\src\main\res\layout\fragment_forms.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#F5F5F5"
    tools:context=".FormsFragment">

    <!-- NYTT: Toolbar med tilbake-knapp -->
    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Light">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/forms_toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@color/white"
            app:navigationIcon="@android:drawable/ic_menu_revert"
            app:titleTextColor="@color/black"
            app:title="Laster..." />

    </com.google.android.material.appbar.AppBarLayout>

    <!-- Resten er likt, men nå under toolbaren -->

    <LinearLayout
        android:id="@+id/history_wrapper"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="3"
        android:orientation="vertical"
        android:background="#FFFFFF"
        android:elevation="4dp"
        android:layout_marginBottom="8dp"
        android:padding="16dp">

        <TextView
            android:id="@+id/lbl_history"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Tidligere innsendinger"
            android:textSize="18sp"
            android:textStyle="bold"
            android:textColor="#333333"
            android:layout_marginBottom="10dp"/>

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:id="@+id/historyContainer"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical" />
        </ScrollView>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="7"
        android:orientation="vertical"
        android:background="#FFFFFF"
        android:elevation="4dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="center_vertical"
            android:padding="8dp"
            android:background="#FAFAFA">

            <ProgressBar
                android:id="@+id/loading_spinner"
                android:layout_width="24dp"
                android:layout_height="24dp"
                android:visibility="gone"
                android:layout_marginEnd="8dp"/>

            <TextView
                android:id="@+id/txt_status"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:textColor="#666666"
                android:textSize="12sp"
                android:text="" />

            <ImageView
                android:id="@+id/btn_toggle_history"
                android:layout_width="32dp"
                android:layout_height="32dp"
                android:src="@android:drawable/arrow_up_float"
                android:background="?attr/selectableItemBackgroundBorderless"
                android:padding="4dp"
                android:contentDescription="Vis/Skjul historikk"
                app:tint="#666666" />
        </LinearLayout>

        <ScrollView
            android:id="@+id/form_scroll_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="16dp"
            android:fillViewport="true">

            <LinearLayout
                android:id="@+id/form_container"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:paddingBottom="40dp">
            </LinearLayout>
        </ScrollView>
    </LinearLayout>

</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\fragment_forms_list.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    android:background="#F5F5F5">

    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
        android:id="@+id/swipe_refresh"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:id="@+id/forms_container"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical" />
        </ScrollView>

    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\fragment_handbook.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#F5F5F5">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="16dp"
        android:background="@color/white"
        android:elevation="4dp">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="KBS Klima og Byggservice ønsker å ta vare på miljø og helse, og har derfor utarbeidet en håndbok som omhandler disse temaene.\n\nDet forventes at alle ansatte skal ha lest hele håndboken minst én gang, og at de har lest og forstått bedriftens HMS-målsetting senest 1. april hvert år."
            android:textSize="14sp"
            android:textColor="#666666"
            android:lineSpacingExtra="4dp"
            android:layout_marginBottom="16dp"/>

        <EditText
            android:id="@+id/search_field"
            android:layout_width="match_parent"
            android:layout_height="48dp"
            android:background="@drawable/bg_category_unselected"
            android:hint="Søk i håndboken..."
            android:paddingHorizontal="16dp"
            android:drawableStart="@android:drawable/ic_menu_search"
            android:drawablePadding="8dp"
            android:textColor="#333"
            android:textSize="14sp"
            android:inputType="text"/>
    </LinearLayout>

    <ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="32dp"/>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_handbook"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="8dp"
        android:clipToPadding="false"/>

</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\fragment_handbook_detail.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@color/white">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/detail_toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/kbs_very_light_blue"
        android:elevation="4dp"
        app:navigationIcon="@android:drawable/ic_menu_revert"
        app:titleTextColor="@color/black" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <WebView
            android:id="@+id/detail_webview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="vertical" />

        <ProgressBar
            android:id="@+id/detail_loading"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:visibility="gone"/>

    </RelativeLayout>

</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\fragment_home.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/kbs_very_light_blue">

    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
        android:id="@+id/swipe_refresh_home"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!-- Én enkel RecyclerView som inneholder alt -->
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/main_recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clipToPadding="false"
            android:paddingBottom="16dp" />

    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

    <ProgressBar
        android:id="@+id/main_loading_spinner"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:visibility="visible" />

</FrameLayout>

============================================================
FILSTI: app\src\main\res\layout\fragment_image_dialog.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/black">

    <!-- Lukkeknapp -->
    <ImageButton
        android:id="@+id/btn_close_image"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:src="@android:drawable/ic_menu_close_clear_cancel"
        android:background="?attr/selectableItemBackgroundBorderless"
        android:layout_alignParentEnd="true"
        android:layout_alignParentTop="true"
        android:layout_margin="16dp"
        app:tint="@android:color/white"
        android:contentDescription="Lukk bildevisning"
        android:elevation="10dp"/>

    <!-- Selve bildet -->
    <ImageView
        android:id="@+id/full_screen_image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        android:scaleType="fitCenter"
        android:adjustViewBounds="true"
        android:contentDescription="Fullskjermbilde" />

    <!-- Loading spinner -->
    <ProgressBar
        android:id="@+id/loading_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"/>

</RelativeLayout>

============================================================
FILSTI: app\src\main\res\layout\fragment_login.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    android:padding="32dp"
    android:background="#FFFFFF">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="KBS Intranett"
        android:textSize="28sp"
        android:textStyle="bold"
        android:textColor="#333333"
        android:layout_marginBottom="8dp"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Vennligst logg inn"
        android:textSize="16sp"
        android:textColor="#666666"
        android:layout_marginBottom="48dp"/>

    <com.google.android.gms.common.SignInButton
        android:id="@+id/sign_in_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/status_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:gravity="center"
        android:text=""
        android:textColor="#D32F2F"/>

</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\fragment_news_detail.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="250dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:contentScrim="@color/kbs_logo_blue">

            <ImageView
                android:id="@+id/detail_image"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop"
                app:layout_collapseMode="parallax" />

            <androidx.appcompat.widget.Toolbar
                android:id="@+id/detail_toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:navigationIcon="@android:drawable/ic_menu_revert" />

        </com.google.android.material.appbar.CollapsingToolbarLayout>
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="16dp">

            <!-- Kategori -->
            <TextView
                android:id="@+id/detail_category"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="@color/kbs_logo_accent_red"
                android:textStyle="bold"
                android:textAllCaps="true"
                android:textSize="12sp"
                android:layout_marginBottom="8dp"/>

            <!-- Tittel -->
            <TextView
                android:id="@+id/detail_title"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textSize="24sp"
                android:textStyle="bold"
                android:textColor="@android:color/black"
                android:layout_marginBottom="8dp"/>

            <!-- Dato og Forfatter -->
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
                android:layout_marginBottom="24dp">

                <TextView
                    android:id="@+id/detail_date"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="#888888"
                    android:textSize="12sp"
                    android:layout_marginEnd="16dp"/>

                <TextView
                    android:id="@+id/detail_author"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="#888888"
                    android:textStyle="italic"
                    android:textSize="12sp"
                    android:text="Av: Forfatter"/>
            </LinearLayout>

            <!--
                 WebView for innhold.
                 tools:ignore="WebViewLayout" hindrer feilmeldingen om wrap_content,
                 da dette er ønsket oppførsel inne i en NestedScrollView.
            -->
            <WebView
                android:id="@+id/detail_webview"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:scrollbars="none"
                tools:ignore="WebViewLayout" />

        </LinearLayout>
    </androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

============================================================
FILSTI: app\src\main\res\layout\fragment_news_full.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@color/kbs_very_light_blue">

    <!-- Redundant header er fjernet. Kun kategorilisten og nyhetslisten er igjen. -->

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_categories"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="8dp"
        android:clipToPadding="false"
        android:scrollbars="none"/>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler_news_full"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:paddingTop="8dp"
            android:paddingHorizontal="4dp"
            android:clipToPadding="false"/>

        <ProgressBar
            android:id="@+id/loading_news_full"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"/>
    </FrameLayout>
</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\fragment_profile.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    android:background="@color/kbs_very_light_blue">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="32dp"
        android:paddingHorizontal="10dp"
        android:background="@color/kbs_logo_blue" >

        <ImageView
            android:id="@+id/btn_close_profile"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:src="@android:drawable/ic_menu_close_clear_cancel"
            android:background="?attr/selectableItemBackgroundBorderless"
            android:padding="4dp"
            android:layout_alignParentStart="true"
            android:layout_centerVertical="true"
            app:tint="@color/white"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Min Profil"
            android:textSize="20sp"
            android:textStyle="bold"
            android:textColor="@color/white"
            android:layout_centerInParent="true"/>
    </RelativeLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:gravity="center_horizontal"
        android:paddingTop="32dp">

        <ImageView
            android:id="@+id/profile_image"
            android:layout_width="120dp"
            android:layout_height="120dp"
            android:src="@android:drawable/sym_def_app_icon"
            android:background="@android:color/transparent"
            android:layout_marginBottom="24dp"/>

        <TextView
            android:id="@+id/profile_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Laster navn..."
            android:textSize="24sp"
            android:textStyle="bold"
            android:textColor="@color/kbs_muted_blue_gray"/>

        <TextView
            android:id="@+id/profile_email"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="..."
            android:textSize="16sp"
            android:textColor="@color/kbs_muted_blue_gray"
            android:layout_marginBottom="8dp"/>

        <TextView
            android:id="@+id/profile_role"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Rolle: ..."
            android:textSize="14sp"
            android:textStyle="italic"
            android:textColor="@color/kbs_logo_blue"
            android:background="@color/kbs_very_light_blue"
            android:paddingHorizontal="12dp"
            android:paddingVertical="4dp"
            android:layout_marginTop="8dp"
            android:layout_marginBottom="32dp"/>

        <Button
            android:id="@+id/btn_update_info"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Oppdater mine opplysninger"
            android:backgroundTint="@color/white"
            android:textColor="@color/kbs_logo_blue"
            android:layout_marginBottom="16dp"/>

        <Button
            android:id="@+id/btn_logout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Logg ut"
            android:backgroundTint="@color/kbs_logo_accent_red"
            android:textColor="@color/white"
            android:layout_marginBottom="32dp"/>

        <!-- NYTT: VERSJONSINFO -->
        <TextView
            android:id="@+id/tv_version_info"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Versjon 1.0"
            android:textColor="#999999"
            android:textSize="12sp"/>

    </LinearLayout>

</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\fragment_tasks.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@color/kbs_very_light_blue">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/task_tabs"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/white"
        app:tabSelectedTextColor="@color/kbs_logo_blue"
        app:tabIndicatorColor="@color/kbs_logo_blue">
    </com.google.android.material.tabs.TabLayout>

    <!-- NY SJEKKBOKS: Vises kun under "Alle" -->
    <CheckBox
        android:id="@+id/cb_show_completed"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Vis også fullførte oppgaver"
        android:textColor="@color/kbs_muted_blue_gray"
        android:textSize="12sp"
        android:layout_gravity="end"
        android:layout_marginEnd="16dp"
        android:layout_marginTop="4dp"
        android:visibility="gone"/>

    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
        android:id="@+id/swipe_refresh_tasks"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler_tasks"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="8dp"
            android:clipToPadding="false" />

    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab_add_task"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end"
        android:layout_margin="24dp"
        android:src="@android:drawable/ic_input_add"
        app:backgroundTint="@color/kbs_logo_blue"
        app:tint="@color/white"
        android:contentDescription="Ny oppgave" />

</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\item_calendar.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="8dp"
    android:layout_marginHorizontal="8dp"
    app:cardCornerRadius="8dp"
    app:cardElevation="2dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="12dp"
        android:gravity="center_vertical">

        <LinearLayout
            android:id="@+id/date_box_background"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:orientation="vertical"
            android:gravity="center"
            android:background="@drawable/bg_date_box"
            android:layout_marginEnd="16dp">

            <TextView
                android:id="@+id/cal_day"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="09"
                android:textSize="20sp"
                android:textStyle="bold"
                android:textColor="@color/white"/>

            <TextView
                android:id="@+id/cal_month"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="DES"
                android:textSize="10sp"
                android:textAllCaps="true"
                android:textColor="@color/white"/>
        </LinearLayout>

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="vertical">

            <TextView
                android:id="@+id/cal_title"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Møtetittel"
                android:textSize="16sp"
                android:textStyle="bold"
                android:textColor="@color/kbs_muted_blue_gray"/>

            <TextView
                android:id="@+id/cal_time"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Kl. 10:00 - 11:00"
                android:textSize="12sp"
                android:textColor="@android:color/darker_gray"/>
        </LinearLayout>

    </LinearLayout>
</androidx.cardview.widget.CardView>

============================================================
FILSTI: app\src\main\res\layout\item_calendar_year_header.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/txt_calendar_year"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#4F5B66"
    android:paddingHorizontal="16dp"
    android:paddingVertical="8dp"
    android:text="2026"
    android:textColor="@color/white"
    android:textSize="18sp"
    android:textStyle="bold" />

============================================================
FILSTI: app\src\main\res\layout\item_category.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/category_name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Kategori"
    android:paddingHorizontal="16dp"
    android:paddingVertical="8dp"
    android:layout_marginEnd="8dp"
    android:background="@drawable/bg_category_unselected"
    android:textColor="#333333"
    android:textSize="14sp"
    android:textStyle="bold" />

============================================================
FILSTI: app\src\main\res\layout\item_handbook.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="6dp"
    app:cardCornerRadius="8dp"
    app:cardElevation="2dp"
    app:cardBackgroundColor="@color/white">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="16dp"
        android:gravity="center_horizontal">

        <ImageView
            android:id="@+id/icon"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:src="@drawable/ic_handbook_general"
            app:tint="@color/kbs_logo_blue"
            android:layout_marginBottom="12dp"/>

        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Tittel"
            android:textStyle="bold"
            android:textColor="#333333"
            android:textSize="14sp"
            android:gravity="center"
            android:layout_marginBottom="4dp"/>

        <TextView
            android:id="@+id/desc"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Beskrivelse..."
            android:textColor="#666666"
            android:textSize="12sp"
            android:gravity="center"
            android:maxLines="2"
            android:ellipsize="end"/>

    </LinearLayout>
</androidx.cardview.widget.CardView>

============================================================
FILSTI: app\src\main\res\layout\item_home_create_btn.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/btn_create_event"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="+ Ny Kalenderhendelse"
    android:backgroundTint="@color/kbs_logo_blue"
    android:textColor="#FFFFFF"
    android:layout_marginHorizontal="16dp"
    android:layout_marginBottom="16dp"/>

============================================================
FILSTI: app\src\main\res\layout\item_home_empty_tasks.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Ingen oppgaver tildelt"
    android:textColor="@color/kbs_muted_blue_gray"
    android:textSize="14sp"
    android:textStyle="italic"
    android:paddingHorizontal="16dp"
    android:paddingVertical="8dp" />

============================================================
FILSTI: app\src\main\res\layout\item_home_header.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="KBS Intranett"
        android:textSize="24sp"
        android:textStyle="bold"
        android:textColor="@color/kbs_muted_blue_gray"
        android:layout_centerVertical="true"/>

    <ImageView
        android:id="@+id/btn_profile"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:src="@android:drawable/ic_menu_my_calendar"
        android:background="?attr/selectableItemBackgroundBorderless"
        android:layout_alignParentEnd="true"
        app:tint="@color/kbs_logo_blue"/>
</RelativeLayout>

============================================================
FILSTI: app\src\main\res\layout\item_home_section_title.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:gravity="center_vertical"
    android:paddingHorizontal="16dp"
    android:paddingVertical="8dp">

    <TextView
        android:id="@+id/txt_section_title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="Seksjon"
        android:textSize="18sp"
        android:textStyle="bold"
        android:textColor="@color/black"/>

    <TextView
        android:id="@+id/btn_view_all"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Se alle >"
        android:textColor="@color/kbs_logo_blue"
        android:textStyle="bold"
        android:padding="8dp"
        android:background="?attr/selectableItemBackground"/>
</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\item_news.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="24dp"
    android:layout_marginHorizontal="8dp"
    app:cardCornerRadius="12dp"
    app:cardElevation="4dp"
    app:cardBackgroundColor="@android:color/white">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <ImageView
            android:id="@+id/news_image"
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:scaleType="centerCrop"
            android:src="@android:drawable/ic_menu_gallery"
            android:background="#EEEEEE" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="16dp">

            <TextView
                android:id="@+id/news_category"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="KATEGORI"
                android:textSize="12sp"
                android:textStyle="bold"
                android:textColor="@color/kbs_logo_accent_red"
                android:textAllCaps="true"
                android:letterSpacing="0.05"
                android:layout_marginBottom="4dp"/>

            <TextView
                android:id="@+id/news_title"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Overskrift på nyhetssaken kommer her"
                android:textSize="20sp"
                android:textStyle="bold"
                android:textColor="@android:color/black"
                android:layout_marginBottom="8dp"
                android:lineSpacingExtra="2dp"/>

            <TextView
                android:id="@+id/news_excerpt"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Her kommer en kort ingress som beskriver saken litt nærmere før man klikker seg inn..."
                android:textColor="#555555"
                android:textSize="14sp"
                android:maxLines="3"
                android:ellipsize="end"
                android:layout_marginBottom="12dp"/>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
                android:gravity="center_vertical">

                <ImageView
                    android:layout_width="14dp"
                    android:layout_height="14dp"
                    android:src="@android:drawable/ic_menu_my_calendar"
                    app:tint="#999999"
                    android:layout_marginEnd="6dp"/>

                <TextView
                    android:id="@+id/news_date"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="23. Nov 2025"
                    android:textSize="12sp"
                    android:textColor="#999999"/>
            </LinearLayout>

        </LinearLayout>
    </LinearLayout>
</androidx.cardview.widget.CardView>

============================================================
FILSTI: app\src\main\res\layout\item_task.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginHorizontal="8dp"
    android:layout_marginVertical="4dp"
    app:cardCornerRadius="8dp"
    app:cardElevation="2dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="12dp"
        android:gravity="center_vertical">

        <CheckBox
            android:id="@+id/task_checkbox"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="8dp" />

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="vertical">

            <TextView
                android:id="@+id/task_title"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Oppgavetittel"
                android:textSize="16sp"
                android:textStyle="bold"
                android:textColor="@color/black" />

            <TextView
                android:id="@+id/task_date"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Frist: -"
                android:textSize="12sp"
                android:textColor="@color/kbs_muted_blue_gray" />

            <TextView
                android:id="@+id/task_creator"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Tildelt av: -"
                android:textSize="11sp"
                android:textColor="@color/kbs_logo_blue"
                android:visibility="gone" />

            <TextView
                android:id="@+id/task_progress"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Fremdrift: 0/0"
                android:textSize="11sp"
                android:textStyle="italic"
                android:textColor="@color/kbs_muted_blue_gray"
                android:layout_marginTop="2dp"/>
        </LinearLayout>

    </LinearLayout>
</androidx.cardview.widget.CardView>

============================================================
FILSTI: app\src\main\res\layout\nav_header_main.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="176dp"
    android:background="@color/kbs_logo_blue"
    android:gravity="bottom"
    android:orientation="vertical"
    android:padding="16dp"
    android:theme="@style/ThemeOverlay.AppCompat.Dark">

    <ImageView
        android:id="@+id/nav_header_image"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:paddingTop="8dp"
        android:src="@mipmap/ic_launcher_round" />

    <TextView
        android:id="@+id/nav_header_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="8dp"
        android:text="KBS Intranett"
        android:textAppearance="@style/TextAppearance.AppCompat.Body1"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/nav_header_email"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="intranett@kbs.no" />

</LinearLayout>

============================================================
FILSTI: app\src\main\res\menu\bottom_nav_menu.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/navigation_home"
        android:icon="@android:drawable/ic_menu_today"
        android:title="Hjem" />

    <item
        android:id="@+id/navigation_forms"
        android:icon="@android:drawable/ic_menu_edit"
        android:title="Skjema" />

    <item
        android:id="@+id/navigation_handbook"
        android:icon="@android:drawable/ic_menu_info_details"
        android:title="Håndbok" />
</menu>

============================================================
FILSTI: app\src\main\res\menu\drawer_menu.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <item
            android:id="@+id/navigation_home"
            android:icon="@android:drawable/ic_menu_today"
            android:title="Hjem" />
        <item
            android:id="@+id/navigation_calendar_full"
            android:icon="@android:drawable/ic_menu_my_calendar"
            android:title="Kalender" />
        <item
            android:id="@+id/navigation_tasks"
            android:icon="@android:drawable/ic_menu_agenda"
            android:title="Oppgaver" />
        <item
            android:id="@+id/navigation_forms"
            android:icon="@android:drawable/ic_menu_edit"
            android:title="Skjemaer" />
        <item
            android:id="@+id/navigation_news_full"
            android:icon="@android:drawable/ic_menu_gallery"
            android:title="Nyheter" />
        <item
            android:id="@+id/navigation_handbook"
            android:icon="@android:drawable/ic_menu_info_details"
            android:title="Håndbok" />
    </group>

    <item android:title="Bruker">
        <menu>
            <item
                android:id="@+id/navigation_profile"
                android:icon="@android:drawable/ic_menu_manage"
                android:title="Min Profil" />
        </menu>
    </item>
</menu>

============================================================
FILSTI: app\src\main\res\mipmap-anydpi-v26\ic_launcher.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@color/ic_launcher_background"/>
    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

============================================================
FILSTI: app\src\main\res\mipmap-anydpi-v26\ic_launcher_round.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@color/ic_launcher_background"/>
    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

============================================================
FILSTI: app\src\main\res\navigation\mobile_navigation.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mobile_navigation"
    app:startDestination="@+id/navigation_home">

    <!-- LOGIN -->
    <fragment
        android:id="@+id/navigation_login"
        android:name="com.kbs.kbsintranett.LoginFragment"
        android:label="Logg inn"
        tools:layout="@layout/fragment_login">
        <action
            android:id="@+id/action_login_to_home"
            app:destination="@id/navigation_home"
            app:popUpTo="@id/navigation_home"
            app:popUpToInclusive="true" />
    </fragment>

    <!-- HJEM -->
    <fragment
        android:id="@+id/navigation_home"
        android:name="com.kbs.kbsintranett.HomeFragment"
        android:label="KBS Intranett"
        tools:layout="@layout/fragment_home">
        <action
            android:id="@+id/action_home_to_calendarFull"
            app:destination="@id/navigation_calendar_full" />
        <action
            android:id="@+id/action_home_to_newsFull"
            app:destination="@id/navigation_news_full" />
        <action
            android:id="@+id/action_home_to_newsDetail"
            app:destination="@id/navigation_news_detail" />
        <action
            android:id="@+id/action_home_to_create_event"
            app:destination="@id/navigation_create_event" />
        <action
            android:id="@+id/action_home_to_tasks"
            app:destination="@id/navigation_tasks" />
    </fragment>

    <!-- OPPGAVER (Navn endret for Toolbar) -->
    <fragment
        android:id="@+id/navigation_tasks"
        android:name="com.kbs.kbsintranett.TasksFragment"
        android:label="KBS Oppgaver"
        tools:layout="@layout/fragment_tasks" />

    <!-- KALENDER (Navn endret for Toolbar) -->
    <fragment
        android:id="@+id/navigation_calendar_full"
        android:name="com.kbs.kbsintranett.CalendarFullFragment"
        android:label="KBS Kalender"
        tools:layout="@layout/fragment_calendar_full" />

    <fragment
        android:id="@+id/navigation_create_event"
        android:name="com.kbs.kbsintranett.CreateEventFragment"
        android:label="Ny Hendelse"
        tools:layout="@layout/fragment_create_event" />

    <!-- NYHETER -->
    <fragment
        android:id="@+id/navigation_news_full"
        android:name="com.kbs.kbsintranett.NewsFullFragment"
        android:label="Siste nytt"
        tools:layout="@layout/fragment_news_full">
        <action
            android:id="@+id/action_newsFull_to_newsDetail"
            app:destination="@id/navigation_news_detail" />
    </fragment>

    <fragment
        android:id="@+id/navigation_news_detail"
        android:name="com.kbs.kbsintranett.NewsDetailFragment"
        android:label="Nyhet"
        tools:layout="@layout/fragment_news_detail" />

    <!-- SKJEMAER -->
    <fragment
        android:id="@+id/navigation_forms"
        android:name="com.kbs.kbsintranett.FormsListFragment"
        android:label="KBS Skjemaer"
        tools:layout="@layout/fragment_forms_list">
        <action
            android:id="@+id/action_formsListFragment_to_formsDetailFragment"
            app:destination="@id/navigation_forms_detail" />
    </fragment>

    <fragment
        android:id="@+id/navigation_forms_detail"
        android:name="com.kbs.kbsintranett.FormsFragment"
        android:label="Fyll ut skjema"
        tools:layout="@layout/fragment_forms">
        <argument
            android:name="formId"
            android:defaultValue="0"
            app:argType="integer" />
    </fragment>

    <!-- HÅNDBOK -->
    <fragment
        android:id="@+id/navigation_handbook"
        android:name="com.kbs.kbsintranett.HandbookFragment"
        android:label="KBS Håndbok"
        tools:layout="@layout/fragment_handbook">
        <action
            android:id="@+id/action_handbook_to_detail"
            app:destination="@id/navigation_handbook_detail" />
    </fragment>

    <fragment
        android:id="@+id/navigation_handbook_detail"
        android:name="com.kbs.kbsintranett.HandbookDetailFragment"
        android:label="Detaljer"
        tools:layout="@layout/fragment_handbook_detail">
        <argument
            android:name="page_id"
            app:argType="integer" />
        <argument
            android:name="page_title"
            app:argType="string" />
        <action
            android:id="@+id/action_handbook_to_detail"
            app:destination="@id/navigation_handbook_detail" />
        <action
            android:id="@+id/action_handbook_to_form"
            app:destination="@id/navigation_forms_detail" />
    </fragment>

    <!-- PROFIL -->
    <fragment
        android:id="@+id/navigation_profile"
        android:name="com.kbs.kbsintranett.ProfileFragment"
        android:label="Min Profil"
        tools:layout="@layout/fragment_profile">
        <action
            android:id="@+id/action_profile_to_login"
            app:destination="@id/navigation_login"
            app:popUpTo="@id/navigation_home"
            app:popUpToInclusive="true" />
        <action
            android:id="@+id/action_profile_to_form"
            app:destination="@id/navigation_forms_detail" />
    </fragment>

</navigation>

============================================================
FILSTI: app\src\main\res\values\colors.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="black">#FF000000</color>
    <color name="white">#FFFFFFFF</color>

    <color name="kbs_logo_blue">#0069B3</color>
    <color name="kbs_logo_light_blue">#53AFE9</color>
    <color name="kbs_logo_accent_red">#C40426</color>

    <color name="kbs_very_light_blue">#F5F7FA</color>
    <color name="kbs_muted_blue_gray">#4F5B66</color>
    <color name="kbs_soft_light_pink_beige">#F8E5E8</color>
</resources>

============================================================
FILSTI: app\src\main\res\values\ic_launcher_background.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="ic_launcher_background">#FFFFFF</color>
</resources>

============================================================
FILSTI: app\src\main\res\values\strings.xml
============================================================
<resources>
    <string name="app_name">KBS Intranett</string>
    <!-- NYTT: Array for gjentakelse-spinneren -->
    <string-array name="recurrence_freq_array">
        <item>dag</item>
        <item>uke</item>
        <item>måned</item>
        <item>år</item>
    </string-array>
</resources>

============================================================
FILSTI: app\src\main\res\values\themes.xml
============================================================
<resources xmlns:tools="http://schemas.android.com/tools">
    <style name="Base.Theme.KBSIntranett" parent="Theme.Material3.Light.NoActionBar">
        <item name="colorPrimary">#0056b3</item> <item name="colorPrimaryVariant">#004494</item>
        <item name="colorOnPrimary">#FFFFFF</item>

        <item name="android:windowLightStatusBar">true</item>
    </style>

    <style name="Theme.KBSIntranett" parent="Base.Theme.KBSIntranett" />

    <!-- NYTT: Stil for runde ukedag-knapper (M T O T F L S) -->
    <style name="DayToggle">
        <item name="android:layout_width">0dp</item>
        <item name="android:layout_height">40dp</item>
        <item name="android:layout_weight">1</item>
        <item name="android:background">@drawable/selector_day_toggle</item>
        <item name="android:textColor">@color/selector_day_text</item>
        <item name="android:textOff">M</item>
        <item name="android:textOn">M</item>
        <item name="android:textSize">12sp</item>
        <item name="android:layout_margin">2dp</item>
    </style>
</resources>

============================================================
FILSTI: app\src\main\res\values-night\themes.xml
============================================================
<resources xmlns:tools="http://schemas.android.com/tools">
    <style name="Base.Theme.KBSIntranett" parent="Theme.Material3.Light.NoActionBar">
        <item name="colorPrimary">#0056b3</item> <item name="colorPrimaryVariant">#004494</item>
        <item name="colorOnPrimary">#FFFFFF</item>

        <item name="android:windowLightStatusBar">true</item>
    </style>

    <style name="Theme.KBSIntranett" parent="Base.Theme.KBSIntranett" />
</resources>

============================================================
FILSTI: app\src\main\res\xml\backup_rules.xml
============================================================
<?xml version="1.0" encoding="utf-8"?><!--
   Sample backup rules file; uncomment and customize as necessary.
   See https://developer.android.com/guide/topics/data/autobackup
   for details.
   Note: This file is ignored for devices older than API 31
   See https://developer.android.com/about/versions/12/backup-restore
-->
<full-backup-content>
    <!--
   <include domain="sharedpref" path="."/>
   <exclude domain="sharedpref" path="device.xml"/>
-->
</full-backup-content>

============================================================
FILSTI: app\src\main\res\xml\data_extraction_rules.xml
============================================================
<?xml version="1.0" encoding="utf-8"?><!--
   Sample data extraction rules file; uncomment and customize as necessary.
   See https://developer.android.com/about/versions/12/backup-restore#xml-changes
   for details.
-->
<data-extraction-rules>
    <cloud-backup>
        <!-- TODO: Use <include> and <exclude> to control what is backed up.
        <include .../>
        <exclude .../>
        -->
    </cloud-backup>
    <!--
    <device-transfer>
        <include .../>
        <exclude .../>
    </device-transfer>
    -->
</data-extraction-rules>

============================================================
FILSTI: app\src\main\res\xml\file_paths.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-files-path name="my_images" path="Pictures" />
</paths>

============================================================
FILSTI: app\src\test\java\com\kbs\kbsintranett\ExampleUnitTest.java
============================================================
package com.kbs.kbsintranett;

import org.junit.Test;

import static org.junit.Assert.*;

/**
 * Example local unit test, which will execute on the development machine (host).
 *
 * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
 */
public class ExampleUnitTest {
    @Test
    public void addition_isCorrect() {
        assertEquals(4, 2 + 2);
    }
}
